package com.blubb.gyingpan; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import net.fusejna.DirectoryFiller; import net.fusejna.ErrorCodes; import net.fusejna.FlockCommand; import net.fusejna.FuseFilesystem; import net.fusejna.StructFlock.FlockWrapper; import net.fusejna.StructFuseFileInfo.FileInfoWrapper; import net.fusejna.StructStat.StatWrapper; import net.fusejna.StructStatvfs.StatvfsWrapper; import net.fusejna.StructTimeBuffer.TimeBufferWrapper; import net.fusejna.XattrFiller; import net.fusejna.XattrListFiller; import net.fusejna.types.TypeMode.ModeWrapper; import net.fusejna.types.TypeMode.NodeType; import com.sun.jna.Platform; public class FuseFS extends FuseFilesystem { GDrive gdrive; public FuseFS(GDrive g) { this.gdrive = g; //log(true); } @Override public int access(String path, int access) { return 0; } @Override public void afterUnmount(File mountPoint) { gdrive.doPersist(); } @Override public void beforeMount(File mountPoint) { // empty } @Override public int bmap(String path, FileInfoWrapper info) { return -ErrorCodes.firstNonNull(ErrorCodes.ENOTSUP(), ErrorCodes.ENOSYS()); } @Override public int chmod(String path, ModeWrapper mode) { return 0; } @Override public int chown(String path, long uid, long gid) { return 0; } @Override public int create(String path, ModeWrapper mode, FileInfoWrapper info) { if(gdrive.findPath(path, null) != null) return -ErrorCodes.EEXIST(); String parentname = path.substring(0, path.lastIndexOf('/')); String name = path.substring(path.lastIndexOf('/') + 1); Node parent = gdrive.findPath(parentname, null); if (parent == null) return -ErrorCodes.ENOENT(); gdrive.createFile(parent, name); return 0; } @Override public void destroy() { } @Override public int fgetattr(String path, StatWrapper stat, FileInfoWrapper info) { return getattr(path, stat); } @Override public int flush(String path, FileInfoWrapper info) { return 0; } @Override public int fsync(String path, int datasync, FileInfoWrapper info) { return 0; } @Override public int fsyncdir(String path, int datasync, FileInfoWrapper info) { return 0; } @Override public int ftruncate(String path, long offset, FileInfoWrapper info) { return truncate(path, offset); } @Override public int getattr(String path, StatWrapper stat) { Node n = gdrive.findPath(path, null); if (n == null) return -ErrorCodes.ENOENT(); stat.setAllTimesMillis(n.lastModified); stat.size(n.getSize()); if (n.isDirectory()) stat.setMode(NodeType.DIRECTORY); else { if (n.isGoogleFile()) stat.setMode(NodeType.FILE, true, false, false); else stat.setMode(NodeType.FILE, true, true, false); } return 0; } @Override protected String getName() { return "jdrive"; } @Override protected String[] getOptions() { if(Platform.isMac()) { String opts[] = { "-o", "noappledouble,noapplexattr,iosize=4096" }; return opts; } String opts[] = {}; return opts; } @Override public int getxattr(String path, String xattr, XattrFiller filler, long size, long position) { return -ErrorCodes.firstNonNull(ErrorCodes.ENOSYS(), ErrorCodes.ENOTSUP()); } @Override public void init() { } @Override public int link(String path, String target) { return -ErrorCodes.firstNonNull(ErrorCodes.ENOSYS(), ErrorCodes.ENOTSUP()); } @Override public int listxattr(String path, XattrListFiller filler) { return -ErrorCodes.firstNonNull(ErrorCodes.ENOSYS(), ErrorCodes.ENOTSUP()); } @Override public int lock(String path, FileInfoWrapper info, FlockCommand command, FlockWrapper flock) { return 0; } @Override public int mkdir(String path, ModeWrapper mode) { System.out.println("mkdir " + path); if(gdrive.findPath(path, null) != null) return -ErrorCodes.EEXIST(); String parentname = path.substring(0, path.lastIndexOf('/')); String name = path.substring(path.lastIndexOf('/') + 1); Node parent = gdrive.findPath(parentname, null); if (parent == null) return -ErrorCodes.ENOENT(); gdrive.createDir(parent, name); return 0; } @Override public int mknod(String path, ModeWrapper mode, long dev) { System.out.println("mknod: should not be called"); return -ErrorCodes.ENOSYS(); } @Override public int open(String path, FileInfoWrapper info) { GYMain.setStatus("open " + path); Node n = gdrive.findPath(path, info); if (n != null) { if (n.isDirectory()) return 0; // start to cache gdrive.startCaching(n); } else { return -ErrorCodes.ENOENT(); } return 0; } @Override public int opendir(String path, FileInfoWrapper info) { return 0; } @Override public int read(String path, ByteBuffer buffer, long size, long offset, FileInfoWrapper info) { //System.out.println("read " + path + " " + offset + " " + size); Node n = gdrive.findPath(path, info); if (n.isDirectory()) return -ErrorCodes.EISDIR(); try { synchronized (n) { File f = n.cache(); RandomAccessFile aFile = new RandomAccessFile(f, "r"); FileChannel inChannel = aFile.getChannel(); buffer.limit((int) size); int len = inChannel.read(buffer, offset); aFile.close(); if(len < 0) len = 0; return len; } } catch (IOException e) { e.printStackTrace(); return -ErrorCodes.EAGAIN(); } } @Override public int readdir(String path, DirectoryFiller filler) { // System.out.println("readdir(" + path + ")"); Node n = gdrive.findPath(path, null); // filler.add(".", ".."); for (Node c : n.children) { filler.add(c.getFileName()); } return 0; } @Override public int readlink(String path, ByteBuffer buffer, long size) { return 0; } @Override public int release(String path, FileInfoWrapper info) { return 0; } @Override public int releasedir(String path, FileInfoWrapper info) { return 0; } @Override public int removexattr(String path, String xattr) { return -ErrorCodes.firstNonNull(ErrorCodes.ENOSYS(), ErrorCodes.ENOTSUP()); } @Override public int rename(String path, String newName) { System.out.println("rename " + path + " " + newName); Node n = gdrive.findPath(path, null); if (n == null) return -ErrorCodes.ENOENT(); String fromDir = path.substring(0, path.lastIndexOf('/')); String fromName = path.substring(path.lastIndexOf('/') + 1); String toDir = newName.substring(0, newName.lastIndexOf('/')); String toName = newName.substring(newName.lastIndexOf('/') + 1); if (fromDir.equals(toDir)) { n.rename(toName); } else { if (!fromName.equals(toName)) { n.rename(toName); } Node toNode = gdrive.findPath(toDir, null); if (toNode == null) return -ErrorCodes.ENOENT(); n.move(gdrive.findPath(fromDir, null), toNode); } return 0; } @Override public int rmdir(String path) { Node n = gdrive.findPath(path, null); if (n == null) return -ErrorCodes.ENOENT(); if (n.children != null && n.children.size() > 0) return -ErrorCodes.ENOTEMPTY(); if (!n.isDirectory()) return -ErrorCodes.ENOTDIR(); String parentname = path.substring(0, path.lastIndexOf('/')); n.delete(gdrive.findPath(parentname, null)); return 0; } @Override public int setxattr(String path, String xattr, ByteBuffer value, long size, int flags, int position) { return -ErrorCodes.firstNonNull(ErrorCodes.ENOSYS(), ErrorCodes.ENOTSUP()); } @Override public int statfs(String path, StatvfsWrapper wrapper) { wrapper.set(1024, 1024, 1024 * 1024 * 1024, 1024 * 1024 * 1024, 1024 * 1024 * 1024, 100 * 1024, 100 * 1024, 100 * 1024); return 0; } @Override public int symlink(String path, String target) { return -ErrorCodes.ENOSYS(); } @Override public int truncate(String path, long offset) { System.out.println("truncate " + path + " " + offset); Node n = gdrive.findPath(path, null); if (n == null) return -ErrorCodes.ENOENT(); try { n.truncate(offset); } catch (IOException e) { e.printStackTrace(); return -ErrorCodes.EAGAIN(); } return 0; } @Override public int unlink(String path) { Node n = gdrive.findPath(path, null); if (n == null) return -ErrorCodes.ENOENT(); if (n.children != null && n.children.size() > 0) return -ErrorCodes.ENOTEMPTY(); if (n.isDirectory()) return -ErrorCodes.EISDIR(); String parentname = path.substring(0, path.lastIndexOf('/')); n.delete(gdrive.findPath(parentname, null)); return 0; } @Override public int utimens(String path, TimeBufferWrapper wrapper) { Node n = gdrive.findPath(path, null); if (n == null) return -ErrorCodes.ENOENT(); wrapper.ac_setMillis(n.lastModified); return 0; } @Override public int write(String path, ByteBuffer buf, long bufSize, long writeOffset, FileInfoWrapper info) { Node n = gdrive.findPath(path, info); if(n == null) return -ErrorCodes.ENOENT(); if (n.isDirectory()) return -ErrorCodes.EISDIR(); try { synchronized (n) { File f = n.cache(); RandomAccessFile aFile = new RandomAccessFile(f, "rw"); FileChannel inChannel = aFile.getChannel(); buf.limit((int)bufSize); int len = inChannel.write(buf, writeOffset); n.markDirty(); n.size = aFile.length(); aFile.close(); return len; } } catch (IOException e) { e.printStackTrace(); return -ErrorCodes.EAGAIN(); } } }