All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.peircean.glusterfs.GlusterFileSystemProvider Maven / Gradle / Ivy

The newest version!
package com.peircean.glusterfs;

import com.peircean.libgfapi_jni.internal.GLFS;
import com.peircean.libgfapi_jni.internal.structs.stat;
import com.peircean.libgfapi_jni.internal.structs.statvfs;
import lombok.AccessLevel;
import lombok.Getter;

import java.io.IOException;
import java.net.URI;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.*;
import java.nio.file.attribute.*;
import java.nio.file.spi.FileSystemProvider;
import java.util.*;

import static com.peircean.libgfapi_jni.internal.GLFS.*;

/**
 * @author Louis Zuckerman
 */
public class GlusterFileSystemProvider extends FileSystemProvider {

    public static final String GLUSTER = "gluster";
    public static final int GLUSTERD_PORT = 24007;
    public static final String TCP = "tcp";
    @Getter(AccessLevel.PACKAGE)
    private static Map cache = new HashMap();

    @Override
    public String getScheme() {
        return GLUSTER;
    }

    @Override
    public FileSystem newFileSystem(URI uri, Map stringMap) throws IOException {
        String authorityString = uri.getAuthority();
        String[] authority = parseAuthority(authorityString);

        String volname = authority[1];
        long volptr = glfsNew(volname);

        glfsSetVolfileServer(authority[0], volptr);

        glfsInit(authorityString, volptr);

//        GLFS.glfs_set_logging(volptr, "/tmp/gluster-java.log", 9);

        GlusterFileSystem fileSystem = new GlusterFileSystem(this, authority[0], volname, volptr);
        cache.put(authorityString, fileSystem);

        return fileSystem;
    }

    String[] parseAuthority(String authority) {
        if (!authority.contains(":")) {
            throw new IllegalArgumentException("URI must be of the form 'gluster://server:volume/path");
        }
        String[] aarr = authority.split(":");
        if (aarr.length != 2 || aarr[0].isEmpty() || aarr[1].isEmpty()) {
            throw new IllegalArgumentException("URI must be of the form 'gluster://server:volume/path");
        }

        return aarr;
    }

    long glfsNew(String volname) {
        long volptr = glfs_new(volname);
        if (0 == volptr) {
            throw new IllegalArgumentException("Failed to create new client for volume: " + volname);
        }
        return volptr;
    }

    void glfsSetVolfileServer(String host, long volptr) {
        int setServer = glfs_set_volfile_server(volptr, TCP, host, GLUSTERD_PORT);
        if (0 != setServer) {
            throw new IllegalArgumentException("Failed to set server address: " + host);
        }
    }

    void glfsInit(String authorityString, long volptr) {
        int init = glfs_init(volptr);
        if (0 != init) {
            throw new IllegalArgumentException("Failed to initialize glusterfs client: " + authorityString);
        }
    }

    @Override
    public FileSystem getFileSystem(URI uri) {
        if (!cache.containsKey(uri.getAuthority())) {
            throw new FileSystemNotFoundException("No cached filesystem for: " + uri.getAuthority());
        }
        return cache.get(uri.getAuthority());
    }

    @Override
    public Path getPath(URI uri) {
        if (!uri.getScheme().equals(getScheme())) {
            throw new IllegalArgumentException("No support for scheme: " + uri.getScheme());
        }
        try {
            FileSystem fileSystem = getFileSystem(uri);
            return fileSystem.getPath(uri.getPath());
        } catch (FileSystemNotFoundException e) {
        }

        try {
            return newFileSystem(uri, null).getPath(uri.getPath());
        } catch (IOException e) {
            throw new FileSystemNotFoundException("Unable to open a connection to " + uri.getAuthority());
        }
    }

    @Override
    public SeekableByteChannel newByteChannel(Path path, Set openOptions, FileAttribute... fileAttributes) throws IOException {
        return newFileChannelHelper(path, openOptions, fileAttributes);
    }

    @Override
    public FileChannel newFileChannel(Path path, Set options, FileAttribute... attrs) throws IOException {
        return newFileChannelHelper(path, options, attrs);
    }

    FileChannel newFileChannelHelper(Path path, Set options, FileAttribute[] attrs) throws IOException {
        GlusterFileChannel channel = new GlusterFileChannel();
        channel.init((GlusterFileSystem) getFileSystem(path.toUri()), path, options, attrs);
        return channel;
    }

    @Override
    public DirectoryStream newDirectoryStream(Path path, DirectoryStream.Filter filter) throws IOException {
        if (!Files.isDirectory(path)) {
            throw new NotDirectoryException("Not a directory! " + path.toString());
        }
        GlusterPath glusterPath = (GlusterPath) path;
        GlusterDirectoryStream stream = new GlusterDirectoryStream();
        stream.setFileSystem(glusterPath.getFileSystem());
        stream.open(glusterPath);
        stream.setFilter(filter);

        return stream;
    }

    @Override
    public void createDirectory(Path path, FileAttribute... fileAttributes) throws IOException {
        if (Files.exists(path)) {
            throw new FileAlreadyExistsException(path.toString());
        }
        if (!Files.exists(path.getParent())) {
            throw new IOException();
        }

        int mode = 0775;

        if (fileAttributes.length > 0) {
            mode = GlusterFileAttributes.parseAttrs(fileAttributes);
        }

        int ret = GLFS.glfs_mkdir(((GlusterFileSystem) path.getFileSystem()).getVolptr(), path.toString(), mode);

        if (ret < 0) {
            throw new IOException(path.toString());
        }
    }

    @Override
    public void delete(Path path) throws IOException {
        if (!Files.exists(path)) {
            throw new NoSuchFileException(path.toString());
        }
        if (Files.isDirectory(path)) {
            if(!directoryIsEmpty(path)) {
                throw new DirectoryNotEmptyException(path.toString());
            }

            int ret = GLFS.glfs_rmdir(((GlusterFileSystem)path.getFileSystem()).getVolptr(), path.toString());

            if (ret < 0) {
                throw new IOException(path.toString());
            }
        } else {
            int ret = GLFS.glfs_unlink(((GlusterFileSystem) path.getFileSystem()).getVolptr(), path.toString());

            if (ret < 0) {
                throw new IOException(path.toString());
            }
        }
    }

    @Override
    public void copy(Path path, Path path2, CopyOption... copyOptions) throws IOException {
        guardAbsolutePath(path);
        guardAbsolutePath(path2);
        guardFileExists(path);

        boolean targetExists = Files.exists(path2);
        if (targetExists && isSameFile(path, path2)) {
            return;
        }

        boolean overwrite = false;
        boolean copyAttributes = false;
        for (CopyOption co : copyOptions) {
            if (StandardCopyOption.ATOMIC_MOVE.equals(co)) {
                throw new UnsupportedOperationException("Atomic move not supported");
            }
            if (StandardCopyOption.REPLACE_EXISTING.equals(co)) {
                overwrite = true;
            }
            if (StandardCopyOption.COPY_ATTRIBUTES.equals(co)) {
                copyAttributes = true;
            }
        }

        if (!overwrite && targetExists) {
            throw new FileAlreadyExistsException("Target " + path2 + " exists and REPLACE_EXISTING not specified");
        }
        if (Files.isDirectory(path2) && !directoryIsEmpty(path2)) {
            throw new DirectoryNotEmptyException("Target not empty: " + path2);
        }
        if (Files.isDirectory(path)) {
            Files.createDirectory(path2);
        } else {
            Files.createFile(path2, PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString("rw-rw-r--")));
            copyFileContent(path, path2);
            if (copyAttributes) {
                copyFileAttributes(path, path2);
            }
        }
    }

    void copyFileAttributes(Path path, Path path2) throws IOException {
        stat stat = new stat();
        long volptr = ((GlusterFileSystem) path.getFileSystem()).getVolptr();
        int retStat = glfs_stat(volptr, path.toString(), stat);
        int retChmod = 0;
        if (0664 != stat.st_mode) {
            retChmod = GLFS.glfs_chmod(volptr, path2.toString(), stat.st_mode);
        }
        if (retStat < 0 || retChmod < 0) {
            throw new IOException("Could not copy file attributes.");
        }
    }

    void copyFileContent(Path path, Path path2) throws IOException {
        Set options = new HashSet<>();
        options.add(StandardOpenOption.READ);

        //TODO: investigate 8kB byte array limitation
        byte[] readBytes = new byte[8192];
        FileChannel channel = newFileChannel(path, options);
        ByteBuffer readBuffer = ByteBuffer.wrap(readBytes);

        boolean writtenTo = false;
        int read = channel.read(readBuffer);

        while (read > 0) {
            byte[] writeBytes = Arrays.copyOf(readBytes, read);
            if (!writtenTo) {
                Files.write(path2, writeBytes, StandardOpenOption.TRUNCATE_EXISTING);
                writtenTo = true;
            } else {
                Files.write(path2, writeBytes, StandardOpenOption.APPEND);
            }
            read = channel.read(readBuffer);
        }
        channel.close();
    }

    boolean directoryIsEmpty(Path path) throws IOException {
        try (DirectoryStream stream = newDirectoryStream(path, null)) {
            if (stream.iterator().hasNext()) {
                return false;
            }
            return true;
        }
    }

    @Override
    public void move(Path path, Path path2, CopyOption... copyOptions) throws IOException {
        guardAbsolutePath(path);
        guardAbsolutePath(path2);
        guardFileExists(path);
        if (Files.exists(path2) && isSameFile(path, path2)) {
            return;
        }

        boolean overwrite = false;
        for (CopyOption co : copyOptions) {
            if (StandardCopyOption.ATOMIC_MOVE.equals(co)) {
                throw new AtomicMoveNotSupportedException(path.toString(), path2.toString(), "Atomic move not supported");
            }
            if (StandardCopyOption.REPLACE_EXISTING.equals(co)) {
                overwrite = true;
            }
        }

        FileSystem fileSystem = path.getFileSystem();

        if (!overwrite && Files.exists(path2)) {
            throw new FileAlreadyExistsException("Target " + path2 + " exists and REPLACE_EXISTING not specified");
        }
        if (Files.isDirectory(path2) && !directoryIsEmpty(path2)) {
            throw new DirectoryNotEmptyException("Target not empty: " + path2);
        }
        if (!fileSystem.equals(path2.getFileSystem())) {
            throw new UnsupportedOperationException("Can not move file to a different file system");
        }
        GLFS.glfs_rename(((GlusterFileSystem) fileSystem).getVolptr(), ((GlusterPath) path).getString(), ((GlusterPath) path2).getString());
    }

    void guardFileExists(Path path) throws NoSuchFileException {
        if (!Files.exists(path)) {
            throw new NoSuchFileException(path.toString());
        }
    }

    void guardAbsolutePath(Path p) {
        if (!p.isAbsolute()) {
            throw new UnsupportedOperationException("Relative paths not supported: " + p);
        }
    }

    @Override
    public boolean isSameFile(Path path, Path path2) throws IOException {
        if (path.equals(path2)) {
            return true;
        }
        if (!path.getFileSystem().equals(path2.getFileSystem())) { //if file system differs, then we don't need to check provider; we know the files differ
            return false;
        }
        guardFileExists(path);
        guardFileExists(path2);

        stat stat1 = statPath(path);
        stat stat2 = statPath(path2);

        return stat1.st_ino == stat2.st_ino;
    }

    stat statPath(Path path) throws IOException {
        stat stat = new stat();
        String pathString = ((GlusterPath) path).getString();
        int ret = GLFS.glfs_stat(((GlusterFileSystem) path.getFileSystem()).getVolptr(),
                pathString, stat);
        if (ret != 0) {
            throw new IOException("Stat failed for " + pathString);
        }
        return stat;
    }

    @Override
    public boolean isHidden(Path path) throws IOException {
        return ((GlusterPath) path.getFileName()).getParts()[0].startsWith(".");
    }

    @Override
    public FileStore getFileStore(Path path) throws IOException {
        if (Files.exists(path)) {
            return path.getFileSystem().getFileStores().iterator().next();
        } else {
            throw new NoSuchFileException(path.toString());
        }
    }

    @Override
    public void checkAccess(Path path, AccessMode... accessModes) throws IOException {
        long volptr = ((GlusterFileSystem) path.getFileSystem()).getVolptr();
        String pathString = ((GlusterPath) path).getString();

        stat stat = new stat();
        int ret = GLFS.glfs_lstat(volptr, pathString, stat);

        if (-1 == ret) {
            throw new NoSuchFileException("");
        }

        for (AccessMode m : accessModes) {
            int access = GLFS.glfs_access(volptr, pathString, modeInt(m));
            if (-1 == access) {
                throw new AccessDeniedException(pathString);
            }
        }

    }

    private int modeInt(AccessMode m) {
        switch (m) {
            case EXECUTE:
                return 1;
            case WRITE:
                return 2;
            case READ:
                return 4;
        }
        return -1;
    }

    @Override
    public  V getFileAttributeView(Path path, Class vClass, LinkOption... linkOptions) {
        return null;
    }

    @Override
    public  A readAttributes(Path path, Class type, LinkOption... linkOptions) throws IOException {
//        if (!type.isAssignableFrom(GlusterFileAttributes.class)) { // Why doesn't this work when type is GlusterFileAttributes.class?!
        if (type.equals(DosFileAttributes.class)) {
            throw new UnsupportedOperationException(type + " attribute type is not supported, only PosixFileAttributes & its superinterfaces");
        }
        stat stat = new stat();

        boolean followSymlinks = true;
        for (LinkOption lo : linkOptions) {
            if (lo.equals(LinkOption.NOFOLLOW_LINKS)) {
                followSymlinks = false;
                break;
            }
        }
        int ret;
        String pathString = ((GlusterPath) path).getString();
        if (followSymlinks) {
            ret = GLFS.glfs_stat(((GlusterFileSystem) path.getFileSystem()).getVolptr(), pathString, stat);
        } else {
            ret = GLFS.glfs_lstat(((GlusterFileSystem) path.getFileSystem()).getVolptr(), pathString, stat);
        }

        if (-1 == ret) {
            throw new NoSuchFileException("");
        }

        return (A) GlusterFileAttributes.fromStat(stat);
    }

    @Override
    public Map readAttributes(Path path, String s, LinkOption... linkOptions) throws IOException {
        return null;
    }

    @Override
    public void setAttribute(Path path, String s, Object o, LinkOption... linkOptions) throws IOException {

    }

    @Override
    public Path readSymbolicLink(Path link) throws IOException {
        String pathString = link.toString();
        if (!Files.isSymbolicLink(link)) {
            throw new NotLinkException(pathString);
        }

        stat stat = new stat();
        GlusterFileSystem fileSystem = (GlusterFileSystem) link.getFileSystem();
        long volptr = fileSystem.getVolptr();
        int statReturn = GLFS.glfs_lstat(volptr, pathString, stat);
        if (0 != statReturn) {
            throw new IOException("Unable to get size of symlink " + pathString);
        }

        long length = (int) stat.st_size;
        byte[] content = new byte[(int)length];
        int readReturn = GLFS.glfs_readlink(volptr, pathString, content, content.length);
        if (-1 == readReturn) {
            throw new IOException("Unable to read symlink " + pathString);
        }
        
        return new GlusterPath(fileSystem, new String(content));
    }

    @Override
    public void createSymbolicLink(Path link, Path target, FileAttribute... attrs) throws IOException {
        String linkPath = link.toString();
        if (Files.exists(link, LinkOption.NOFOLLOW_LINKS)) {
            throw new FileAlreadyExistsException(linkPath);
        }
        if (null != attrs && attrs.length > 0) {
            throw new UnsupportedOperationException("glfs_symlink does not support atomic mode/perms");
        }
        GlusterFileSystem fileSystem = (GlusterFileSystem) link.getFileSystem();
        long volptr = fileSystem.getVolptr();
        int ret = GLFS.glfs_symlink(volptr, target.toString(), linkPath);
        if (0 != ret) {
            throw new IOException("Unknown error creating symlink: " + linkPath);
        }
    }

    int close(long volptr) {
        return glfs_fini(volptr);
    }

    long getTotalSpace(long volptr) throws IOException {
        statvfs buf = new statvfs();
        GLFS.glfs_statvfs(volptr, "/", buf);
        return buf.f_bsize * buf.f_blocks;
    }

    long getUsableSpace(long volptr) throws IOException {
        statvfs buf = new statvfs();
        GLFS.glfs_statvfs(volptr, "/", buf);
        return buf.f_bsize * buf.f_bavail;
    }

    long getUnallocatedSpace(long volptr) throws IOException {
        statvfs buf = new statvfs();
        GLFS.glfs_statvfs(volptr, "/", buf);
        return buf.f_bsize * buf.f_bfree;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy