/** * FUSE-J: Java bindings for FUSE (Filesystem in Userspace by Miklos Szeredi (mszeredi@inf.bme.hu)) * * Copyright (C) 2003 Peter Levart (peter@select-tech.si) * * This program can be distributed under the terms of the GNU LGPL. * See the file COPYING.LIB */ package fuse; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.nio.BufferOverflowException; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; @SuppressWarnings({"OctalInteger"}) public class FakeFilesystem implements Filesystem3, XattrSupport, LifecycleSupport { private static final Log log = LogFactory.getLog(FakeFilesystem.class); private static final int BLOCK_SIZE = 512; private static final int NAME_LENGTH = 1024; private static class Node { static int nfiles = 0; String name; int mode; Map<String, byte[]> xattrs = new HashMap<String, byte[]>(); Node(String name, int mode, String... xattrs) { this.name = name; this.mode = mode; for (int i = 0; i < xattrs.length - 1; i += 2) { this.xattrs.put(xattrs[i], xattrs[i + 1].getBytes()); } nfiles++; } public String toString() { String cn = getClass().getName(); return cn.substring(cn.indexOf("$")) + "[ name=" + name + ", mode=" + Integer.toOctalString(mode) + "(OCT) ]"; } } private static class Directory extends Node { Map<String, Node> files = new LinkedHashMap<String, Node>(); Directory(String name, int mode, String... xattrs) { super(name, mode, xattrs); } void add(Node node) { files.put(node.name, node); } public String toString() { return super.toString() + " with " + files.size() + " files"; } } private static class File extends Node { byte[] content; File(String name, int mode, String content, String... xattrs) { super(name, mode, xattrs); this.content = content.getBytes(); } } private static class Link extends Node { String link; Link(String name, int mode, String link, String... xattrs) { super(name, mode, xattrs); this.link = link; } } private static class FileHandle { Node node; FileHandle(Node node) { this.node = node; log.debug(" " + this + " created"); } void release() { log.debug(" " + this + " released"); } protected void finalize() { log.debug(" " + this + " finalized"); } public String toString() { return "FileHandle[" + node + ", hashCode=" + hashCode() + "]"; } } // a root directory private Directory root; // lookup node private Node lookup(String path) { if (path.equals("/")) { return root; } java.io.File f = new java.io.File(path); Node parent = lookup(f.getParent()); Node node = (parent instanceof Directory) ? ((Directory) parent).files.get(f.getName()) : null; if (log.isDebugEnabled()) { log.debug(" lookup(\"" + path + "\") returning: " + node); } return node; } public FakeFilesystem() { root = new Directory("", 0755, "description", "ROOT directory"); root.add(new File("README", 0644, "You have read me\n", "mimetype", "text/plain", "description", "a README file")); root.add(new File("execute_me.sh", 0755, "#!/bin/sh\n\necho \"You executed me\"\n", "mimetype", "text/plain", "description", "a BASH script")); Directory subdir = new Directory("subdir", 0755, "description", "a subdirectory"); root.add(subdir); subdir.add(new Link("README.link", 0666, "../README", "description", "a symbolic link")); subdir.add(new Link("execute_me.link.sh", 0666, "../execute_me.sh", "description", "another symbolic link")); log.info("created"); } public int chmod(String path, int mode) throws FuseException { Node node = lookup(path); if (node != null) { node.mode = (node.mode & FuseStatConstants.TYPE_MASK) | (mode & FuseStatConstants.MODE_MASK); return 0; } return Errno.ENOENT; } public int chown(String path, int uid, int gid) throws FuseException { return 0; } public int getattr(String path, FuseGetattrSetter getattrSetter) throws FuseException { Node node = lookup(path); int time = (int) (System.currentTimeMillis() / 1000L); if (node instanceof Directory) { Directory directory = (Directory) node; getattrSetter.set(directory.hashCode(), FuseFtypeConstants.TYPE_DIR | directory.mode, 1, 0, 0, 0, directory.files.size() * NAME_LENGTH, (directory.files.size() * NAME_LENGTH + BLOCK_SIZE - 1) / BLOCK_SIZE, time, time, time); return 0; } else if (node instanceof File) { File file = (File) node; getattrSetter.set(file.hashCode(), FuseFtypeConstants.TYPE_FILE | file.mode, 1, 0, 0, 0, file.content.length, (file.content.length + BLOCK_SIZE - 1) / BLOCK_SIZE, time, time, time); return 0; } else if (node instanceof Link) { Link link = (Link) node; getattrSetter.set(link.hashCode(), FuseFtypeConstants.TYPE_SYMLINK | link.mode, 1, 0, 0, 0, link.link.length(), (link.link.length() + BLOCK_SIZE - 1) / BLOCK_SIZE, time, time, time); return 0; } return Errno.ENOENT; } public int getdir(String path, FuseDirFiller filler) throws FuseException { Node node = lookup(path); if (node instanceof Directory) { for (Node child : ((Directory) node).files.values()) { int ftype = (child instanceof Directory) ? FuseFtypeConstants.TYPE_DIR : ((child instanceof File) ? FuseFtypeConstants.TYPE_FILE : ((child instanceof Link) ? FuseFtypeConstants.TYPE_SYMLINK : 0)); if (ftype > 0) { filler.add(child.name, child.hashCode(), ftype | child.mode); } } return 0; } return Errno.ENOTDIR; } public int link(String from, String to) throws FuseException { return Errno.EROFS; } public int mkdir(String path, int mode) throws FuseException { return Errno.EROFS; } public int mknod(String path, int mode, int rdev) throws FuseException { return Errno.EROFS; } public int rename(String from, String to) throws FuseException { return Errno.EROFS; } public int rmdir(String path) throws FuseException { return Errno.EROFS; } public int statfs(FuseStatfsSetter statfsSetter) throws FuseException { statfsSetter.set(BLOCK_SIZE, 1000, 200, 180, Node.nfiles, 0, NAME_LENGTH); return 0; } public int symlink(String from, String to) throws FuseException { return Errno.EROFS; } public int truncate(String path, long size) throws FuseException { return Errno.EROFS; } public int unlink(String path) throws FuseException { return Errno.EROFS; } public int utime(String path, int atime, int mtime) throws FuseException { return 0; } public int readlink(String path, CharBuffer link) throws FuseException { Node node = lookup(path); if (node instanceof Link) { link.append(((Link) node).link); return 0; } return Errno.ENOENT; } // if open returns a filehandle by calling FuseOpenSetter.setFh() method, it will be passed to every method that supports 'fh' argument public int open(String path, int flags, FuseOpenSetter openSetter) throws FuseException { Node node = lookup(path); if (node != null) { openSetter.setFh(new FileHandle(node)); return 0; } return Errno.ENOENT; } // fh is filehandle passed from open, // isWritepage indicates that write was caused by a writepage public int write(String path, Object fh, boolean isWritepage, ByteBuffer buf, long offset) throws FuseException { return Errno.EROFS; } // fh is filehandle passed from open public int read(String path, Object fh, ByteBuffer buf, long offset) throws FuseException { if (fh instanceof FileHandle) { File file = (File) ((FileHandle) fh).node; buf.put(file.content, (int) offset, Math.min(buf.remaining(), file.content.length - (int) offset)); return 0; } return Errno.EBADF; } // new operation (called on every filehandle close), fh is filehandle passed from open public int flush(String path, Object fh) throws FuseException { if (fh instanceof FileHandle) { return 0; } return Errno.EBADF; } // new operation (Synchronize file contents), fh is filehandle passed from open, // isDatasync indicates that only the user data should be flushed, not the meta data public int fsync(String path, Object fh, boolean isDatasync) throws FuseException { if (fh instanceof FileHandle) { return 0; } return Errno.EBADF; } // (called when last filehandle is closed), fh is filehandle passed from open public int release(String path, Object fh, int flags) throws FuseException { if (fh instanceof FileHandle) { ((FileHandle) fh).release(); System.runFinalization(); return 0; } return Errno.EBADF; } // // XattrSupport implementation /** * This method will be called to get the value of the extended attribute * * @param path the path to file or directory containing extended attribute * @param name the name of the extended attribute * @param dst a ByteBuffer that should be filled with the value of the extended attribute * @param position specifies the offset within the extended attribute. * @return 0 if Ok or errno when error * @throws fuse.FuseException an alternative to returning errno is to throw this exception with errno initialized * @throws java.nio.BufferOverflowException * should be thrown to indicate that the given <code>dst</code> ByteBuffer * is not large enough to hold the attribute's value. After that <code>getxattr()</code> method will * be called again with a larger buffer. */ public int getxattr(String path, String name, ByteBuffer dst, int position) throws FuseException, BufferOverflowException { Node node = lookup(path); if (node == null) { return Errno.ENOENT; } byte[] value = node.xattrs.get(name); if (value == null) { return Errno.ENOATTR; } dst.put(value); return 0; } /** * This method can be called to query for the size of the extended attribute * * @param path the path to file or directory containing extended attribute * @param name the name of the extended attribute * @param sizeSetter a callback interface that should be used to set the attribute's size * @return 0 if Ok or errno when error * @throws fuse.FuseException an alternative to returning errno is to throw this exception with errno initialized */ public int getxattrsize(String path, String name, FuseSizeSetter sizeSetter) throws FuseException { Node node = lookup(path); if (node == null) { return Errno.ENOENT; } byte[] value = node.xattrs.get(name); if (value == null) { return Errno.ENOATTR; } sizeSetter.setSize(value.length); return 0; } /** * This method will be called to get the list of extended attribute names * * @param path the path to file or directory containing extended attributes * @param lister a callback interface that should be used to list the attribute names * @return 0 if Ok or errno when error * @throws fuse.FuseException an alternative to returning errno is to throw this exception with errno initialized */ public int listxattr(String path, XattrLister lister) throws FuseException { Node node = lookup(path); if (node == null) { return Errno.ENOENT; } for (String xattrName : node.xattrs.keySet()) { lister.add(xattrName); } return 0; } /** * This method will be called to remove the extended attribute * * @param path the path to file or directory containing extended attributes * @param name the name of the extended attribute * @return 0 if Ok or errno when error * @throws fuse.FuseException an alternative to returning errno is to throw this exception with errno initialized */ public int removexattr(String path, String name) throws FuseException { return Errno.EROFS; } /** * This method will be called to set the value of an extended attribute * * @param path the path to file or directory containing extended attributes * @param name the name of the extended attribute * @param value the value of the extended attribute * @param flags parameter can be used to refine the semantics of the operation.<p> * <code>XATTR_CREATE</code> specifies a pure create, which should fail with <code>Errno.EEXIST</code> if the named attribute exists already.<p> * <code>XATTR_REPLACE</code> specifies a pure replace operation, which should fail with <code>Errno.ENOATTR</code> if the named attribute does not already exist.<p> * By default (no flags), the extended attribute will be created if need be, or will simply replace the value if the attribute exists. * @param position specifies the offset within the extended attribute. * @return 0 if Ok or errno when error * @throws fuse.FuseException an alternative to returning errno is to throw this exception with errno initialized */ public int setxattr(String path, String name, ByteBuffer value, int flags, int position) throws FuseException { return Errno.EROFS; } // // LifeCycleSupport public int init() { log.info("Initializing Filesystem"); return 0; } public int destroy() { log.info("Destroying Filesystem"); return 0; } // // Java entry point public static void main(String[] args) { log.info("entering"); try { FuseMount.mount(args, new FakeFilesystem(), log); } catch (Exception e) { e.printStackTrace(); } finally { log.info("exiting"); } } }