/** * 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 java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.BufferOverflowException; import java.nio.charset.*; import java.util.Date; /** * This is an adapter that implements fuse.FuseFS byte level API and delegates * to the fuse.Filesystem3 String level API. You specify the encoding to be used * for file names and paths. */ public class Filesystem3ToFuseFSAdapter implements FuseFS { private Filesystem3 fs3; private XattrSupport xattrSupport; private LifecycleSupport lifecycleSupport; private Charset cs; private Log log; public Filesystem3ToFuseFSAdapter(Filesystem3 fs3, Log log) { this(fs3, System.getProperty("file.encoding", "UTF-8"), log); } public Filesystem3ToFuseFSAdapter(Filesystem3 fs3, String encoding, Log log) { this(fs3, Charset.forName(encoding), log); } public Filesystem3ToFuseFSAdapter(Filesystem3 fs3, Charset cs, Log log) { this.fs3 = fs3; // XattrSupport is optional if (fs3 instanceof XattrSupport) { xattrSupport = (XattrSupport) fs3; } // Lifecycle is optional if (fs3 instanceof LifecycleSupport) { lifecycleSupport = (LifecycleSupport) fs3; } this.cs = cs; this.log = log; } // // FuseFS implementation public int getattr(ByteBuffer path, FuseGetattrSetter getattrSetter) { String pathStr = cs.decode(path).toString(); if (log != null && log.isDebugEnabled()) { log.debug("getattr: path=" + pathStr); } try { return handleErrno(fs3.getattr(pathStr, getattrSetter), getattrSetter); } catch(Exception e) { return handleException(e); } } public int readlink(ByteBuffer path, ByteBuffer link) { String pathStr = cs.decode(path).toString(); if (log != null && log.isDebugEnabled()) { log.debug("readlink: path=" + pathStr); } CharBuffer linkCb = CharBuffer.allocate(link.capacity()); try { int errno = fs3.readlink(pathStr, linkCb); if (errno == 0) { linkCb.flip(); CharsetEncoder enc = cs.newEncoder() .onUnmappableCharacter(CodingErrorAction.REPLACE) .onMalformedInput(CodingErrorAction.REPLACE); CoderResult result = enc.encode(linkCb, link, true); if (result.isOverflow()) { throw new FuseException("Buffer owerflow while encoding result").initErrno(Errno.ENAMETOOLONG); } } return handleErrno(errno, linkCb.rewind()); } catch(Exception e) { return handleException(e); } } public int getdir(ByteBuffer path, FuseFSDirFiller dirFiller) { String pathStr = cs.decode(path).toString(); if (log != null && log.isDebugEnabled()) { log.debug("getdir: path=" + pathStr); } try { dirFiller.setCharset(cs); return handleErrno(fs3.getdir(pathStr, dirFiller), dirFiller); } catch(Exception e) { return handleException(e); } } public int mknod(ByteBuffer path, int mode, int rdev) { String pathStr = cs.decode(path).toString(); if (log != null && log.isDebugEnabled()) { log.debug("mknod: path=" + pathStr + ", mode=" + Integer.toOctalString(mode) + "(OCT), rdev=" + rdev); } try { return handleErrno(fs3.mknod(pathStr, mode, rdev)); } catch(Exception e) { return handleException(e); } } public int mkdir(ByteBuffer path, int mode) { String pathStr = cs.decode(path).toString(); if (log != null && log.isDebugEnabled()) { log.debug("mkdir: path=" + pathStr + ", mode=" + Integer.toOctalString(mode) + "(OCT)"); } try { return handleErrno(fs3.mkdir(pathStr, mode)); } catch(Exception e) { return handleException(e); } } public int unlink(ByteBuffer path) { String pathStr = cs.decode(path).toString(); if (log != null && log.isDebugEnabled()) { log.debug("unlink: path=" + pathStr); } try { return handleErrno(fs3.unlink(pathStr)); } catch(Exception e) { return handleException(e); } } public int rmdir(ByteBuffer path) { String pathStr = cs.decode(path).toString(); if (log != null && log.isDebugEnabled()) { log.debug("rmdir: path=" + pathStr); } try { return handleErrno(fs3.rmdir(pathStr)); } catch(Exception e) { return handleException(e); } } public int symlink(ByteBuffer from, ByteBuffer to) { String fromStr = cs.decode(from).toString(); String toStr = cs.decode(to).toString(); if (log != null && log.isDebugEnabled()) { log.debug("symlink: from=" + fromStr + " to=" + toStr); } try { return handleErrno(fs3.symlink(fromStr, toStr)); } catch(Exception e) { return handleException(e); } } public int rename(ByteBuffer from, ByteBuffer to) { String fromStr = cs.decode(from).toString(); String toStr = cs.decode(to).toString(); if (log != null && log.isDebugEnabled()) { log.debug("rename: from=" + fromStr + " to=" + toStr); } try { return handleErrno(fs3.rename(fromStr, toStr)); } catch(Exception e) { return handleException(e); } } public int link(ByteBuffer from, ByteBuffer to) { String fromStr = cs.decode(from).toString(); String toStr = cs.decode(to).toString(); if (log != null && log.isDebugEnabled()) { log.debug("link: from=" + fromStr + " to=" + toStr); } try { return handleErrno(fs3.link(fromStr, toStr)); } catch(Exception e) { return handleException(e); } } public int chmod(ByteBuffer path, int mode) { String pathStr = cs.decode(path).toString(); if (log != null && log.isDebugEnabled()) { log.debug("chmod: path=" + pathStr + ", mode=" + Integer.toOctalString(mode) + "(OCT)"); } try { return handleErrno(fs3.chmod(pathStr, mode)); } catch(Exception e) { return handleException(e); } } public int chown(ByteBuffer path, int uid, int gid) { String pathStr = cs.decode(path).toString(); if (log != null && log.isDebugEnabled()) { log.debug("chown: path=" + pathStr + ", uid=" + uid + ", gid=" + gid); } try { return handleErrno(fs3.chown(pathStr, uid, gid)); } catch(Exception e) { return handleException(e); } } public int truncate(ByteBuffer path, long size) { String pathStr = cs.decode(path).toString(); if (log != null && log.isDebugEnabled()) { log.debug("truncate: path=" + pathStr + ", size=" + size); } try { return handleErrno(fs3.truncate(pathStr, size)); } catch(Exception e) { return handleException(e); } } public int utime(ByteBuffer path, int atime, int mtime) { String pathStr = cs.decode(path).toString(); if (log != null && log.isDebugEnabled()) { log.debug("utime: path=" + pathStr + ", atime=" + atime + " (" + new Date((long) atime * 1000L) + "), mtime=" + mtime + " (" + new Date((long) mtime * 1000L) + ")"); } try { return handleErrno(fs3.utime(pathStr, atime, mtime)); } catch(Exception e) { return handleException(e); } } public int statfs(FuseStatfsSetter statfsSetter) { if (log != null && log.isDebugEnabled()) { log.debug("statfs"); } try { return handleErrno(fs3.statfs(statfsSetter), statfsSetter); } catch(Exception e) { return handleException(e); } } public int open(ByteBuffer path, int flags, FuseOpenSetter openSetter) { String pathStr = cs.decode(path).toString(); if (log != null && log.isDebugEnabled()) { log.debug("open: path=" + pathStr + ", flags=" + flags); } try { return handleErrno(fs3.open(pathStr, flags, openSetter), openSetter); } catch(Exception e) { return handleException(e); } } public int read(ByteBuffer path, Object fh, ByteBuffer buf, long offset) { String pathStr = cs.decode(path).toString(); if (log != null && log.isDebugEnabled()) { log.debug("read: path=" + pathStr + ", fh=" + fh + ", offset=" + offset); } try { return handleErrno(fs3.read(pathStr, fh, buf, offset), buf); } catch(Exception e) { return handleException(e); } } public int write(ByteBuffer path, Object fh, boolean isWritepage, ByteBuffer buf, long offset) { String pathStr = cs.decode(path).toString(); if (log != null && log.isDebugEnabled()) { log.debug("write: path=" + pathStr + ", fh=" + fh + ", isWritepage=" + isWritepage + ", offset=" + offset); } try { return handleErrno(fs3.write(pathStr, fh, isWritepage, buf, offset), buf); } catch(Exception e) { return handleException(e); } } public int flush(ByteBuffer path, Object fh) { String pathStr = cs.decode(path).toString(); if (log != null && log.isDebugEnabled()) { log.debug("flush: path=" + pathStr + ", fh=" + fh); } try { return handleErrno(fs3.flush(pathStr, fh)); } catch(Exception e) { return handleException(e); } } public int release(ByteBuffer path, Object fh, int flags) { String pathStr = cs.decode(path).toString(); if (log != null && log.isDebugEnabled()) { log.debug("release: path=" + pathStr + ", fh=" + fh + ", flags=" + flags); } try { return handleErrno(fs3.release(pathStr, fh, flags)); } catch(Exception e) { return handleException(e); } } public int fsync(ByteBuffer path, Object fh, boolean isDatasync) { String pathStr = cs.decode(path).toString(); if (log != null && log.isDebugEnabled()) { log.debug("fsync: path=" + pathStr + ", fh=" + fh + ", isDatasync=" + isDatasync); } try { return handleErrno(fs3.fsync(cs.decode(path).toString(), fh, isDatasync)); } catch(Exception e) { return handleException(e); } } // // extended attribute support is optional public int getxattrsize(ByteBuffer path, ByteBuffer name, FuseSizeSetter sizeSetter) { if (xattrSupport == null) { return handleErrno(Errno.ENOTSUPP); } String pathStr = cs.decode(path).toString(); String nameStr = cs.decode(name).toString(); if (log != null && log.isDebugEnabled()) { log.debug("getxattrsize: path=" + pathStr + ", name=" + nameStr); } try { return handleErrno(xattrSupport.getxattrsize(pathStr, nameStr, sizeSetter), sizeSetter); } catch(Exception e) { return handleException(e); } } public int getxattr(ByteBuffer path, ByteBuffer name, ByteBuffer value, int position) { if (xattrSupport == null) { return handleErrno(Errno.ENOTSUPP); } String pathStr = cs.decode(path).toString(); String nameStr = cs.decode(name).toString(); if (log != null && log.isDebugEnabled()) { log.debug("getxattr: path=" + pathStr + ", name=" + nameStr); } try { return handleErrno(xattrSupport.getxattr(pathStr, nameStr, value, position), value); } catch(Exception e) { return handleException(e); } } // // private implementation of XattrLister that estimates the byte size of the attribute names list // using Charset of the enclosing Filesystem3ToFuseFSAdapter class private class XattrSizeLister implements XattrLister { CharsetEncoder enc = cs.newEncoder(); int size = 0; public void add(String xattrName) { size += (int) ((float) xattrName.length() * enc.averageBytesPerChar()) + 1; } } // // estimate the byte size of attribute names list... public int listxattrsize(ByteBuffer path, FuseSizeSetter sizeSetter) { if (xattrSupport == null) { return handleErrno(Errno.ENOTSUPP); } String pathStr = cs.decode(path).toString(); if (log != null && log.isDebugEnabled()) { log.debug("listxattrsize: path=" + pathStr); } int errno; XattrSizeLister lister = new XattrSizeLister(); try { errno = xattrSupport.listxattr(pathStr, lister); } catch(Exception e) { return handleException(e); } sizeSetter.setSize(lister.size); return handleErrno(errno, sizeSetter); } // // private implementation of XattrLister that encodes list of attribute names into given ByteBuffer // using Charset of the enclosing Filesystem3ToFuseFSAdapter class private class XattrValueLister implements XattrLister { CharsetEncoder enc = cs.newEncoder() .onMalformedInput(CodingErrorAction.REPLACE) .onUnmappableCharacter(CodingErrorAction.REPLACE); ByteBuffer list; BufferOverflowException boe; XattrValueLister(ByteBuffer list) { this.list = list; } public void add(String xattrName) { if (boe == null) // don't need to bother any more if there was an exception already { try { enc.encode(CharBuffer.wrap(xattrName), list, true); list.put((byte) 0); // each attribute name is terminated by byte 0 } catch(BufferOverflowException e) { boe = e; } } } // // for debugging public String toString() { StringBuilder sb = new StringBuilder(); sb.append("["); boolean first = true; for(int i = 0; i < list.position(); i++) { int offset = i; int length = 0; while(offset + length < list.position() && list.get(offset + length) != 0) { length++; } byte[] nameBytes = new byte[length]; for(int j = 0; j < length; j++) { nameBytes[j] = list.get(offset + j); } if (first) { first = false; } else { sb.append(", "); } sb.append('"').append(cs.decode(ByteBuffer.wrap(nameBytes))).append('"'); i = offset + length; } sb.append("]"); return sb.toString(); } } // // list attributes into given ByteBuffer... public int listxattr(ByteBuffer path, final ByteBuffer list) { if (xattrSupport == null) { return handleErrno(Errno.ENOTSUPP); } String pathStr = cs.decode(path).toString(); if (log != null && log.isDebugEnabled()) { log.debug("listxattr: path=" + pathStr); } int errno; XattrValueLister lister = new XattrValueLister(list); try { errno = xattrSupport.listxattr(pathStr, lister); } catch(Exception e) { return handleException(e); } // was there a BufferOverflowException? if (lister.boe != null) { return handleException(lister.boe); } return handleErrno(errno, lister); } public int setxattr(ByteBuffer path, ByteBuffer name, ByteBuffer value, int flags, int position) { if (xattrSupport == null) { return handleErrno(Errno.ENOTSUPP); } String pathStr = cs.decode(path).toString(); String nameStr = cs.decode(name).toString(); if (log != null && log.isDebugEnabled()) { log.debug("setxattr: path=" + pathStr + ", name=" + nameStr + ", value=" + value + ", flags=" + flags); } try { return handleErrno(xattrSupport.setxattr(pathStr, nameStr, value, flags, position)); } catch(Exception e) { return handleException(e); } } public int removexattr(ByteBuffer path, ByteBuffer name) { if (xattrSupport == null) { return handleErrno(Errno.ENOTSUPP); } String pathStr = cs.decode(path).toString(); String nameStr = cs.decode(name).toString(); if (log != null && log.isDebugEnabled()) { log.debug("removexattr: path= " + pathStr + ", name=" + nameStr); } try { return handleErrno(xattrSupport.removexattr(pathStr, nameStr)); } catch(Exception e) { return handleException(e); } } // Lifecycle support is optional public int init() { if (lifecycleSupport == null) { return handleErrno(Errno.ENOTSUPP); } if (log != null && log.isDebugEnabled()) { log.debug("init: start filesystem"); } try { return handleErrno(lifecycleSupport.init()); } catch(Exception e) { return handleException(e); } } public int destroy() { if (lifecycleSupport == null) { return handleErrno(Errno.ENOTSUPP); } if (log != null && log.isDebugEnabled()) { log.debug("destroy: shutdown filesystem"); } try { return handleErrno(lifecycleSupport.destroy()); } catch(Exception e) { return handleException(e); } } // // private private int handleErrno(int errno) { if (log != null && log.isDebugEnabled()) { log.debug((errno == 0) ? " returning with success" : " returning errno: " + errno); } return errno; } private int handleErrno(int errno, Object v1) { if (errno != 0) { return handleErrno(errno); } if (log != null && log.isDebugEnabled()) { log.debug(" returning: " + v1); } return errno; } private int handleErrno(int errno, Object v1, Object v2) { if (errno != 0) { return handleErrno(errno); } if (log != null && log.isDebugEnabled()) { log.debug(" returning: " + v1 + ", " + v2); } return errno; } private int handleException(Exception e) { int errno; if (e instanceof FuseException) { errno = handleErrno(((FuseException) e).getErrno()); if (log != null && log.isDebugEnabled()) { log.debug(e); } } else if (e instanceof BufferOverflowException) { errno = handleErrno(Errno.ERANGE); if (log != null && log.isDebugEnabled()) { log.debug(e); } } else { errno = handleErrno(Errno.EFAULT); if (log != null) { log.error(e); } } return errno; } }