Java tutorial
/***************************************************************************** * Copyright 2011-2012 INRIA * Copyright 2012 University of Kaiserslautern * Copyright 2011-2012 Universidade Nova de Lisboa * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *****************************************************************************/ package swift.application.filesystem.fuse; import java.io.File; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.util.Collection; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import swift.application.filesystem.Filesystem; import swift.application.filesystem.FilesystemBasic; import swift.application.filesystem.IFile; import swift.client.SwiftImpl; import swift.client.SwiftOptions; import swift.crdt.DirectoryCRDT; import swift.crdt.core.CachePolicy; import swift.crdt.core.IsolationLevel; import swift.crdt.core.SwiftSession; import swift.crdt.core.TxnHandle; import swift.dc.DCConstants; import swift.dc.DCSequencerServer; import swift.dc.DCServer; import swift.exceptions.NetworkException; import swift.exceptions.NoSuchObjectException; import swift.exceptions.VersionNotFoundException; import swift.exceptions.WrongTypeException; import swift.utils.Pair; import sys.Sys; import fuse.Errno; import fuse.Filesystem3; import fuse.FuseDirFiller; import fuse.FuseException; import fuse.FuseFtypeConstants; import fuse.FuseGetattrSetter; import fuse.FuseMount; import fuse.FuseOpenSetter; import fuse.FuseSizeSetter; import fuse.FuseStatfsSetter; import fuse.XattrLister; import fuse.XattrSupport; /** * FUSE-J: Java bindings for FUSE (Filesystem in Userspace by Miklos Szeredi * (mszeredi@inf.bme.hu)) * * The interface description of Fuse says: * * The file system operations: * * Most of these should work very similarly to the well known UNIX file system * operations. Exceptions are: * * - All operations should return the error value (errno) by either: - throwing * a fuse.FuseException with a errno field of the exception set to the desired * fuse.Errno.E* value. - returning an integer value taken from fuse.Errno.E* * constants. this is supposed to be less expensive in terms of CPU cycles and * should only be used for very frequent errors (for example ENOENT). * * - getdir() is the opendir(), readdir(), ..., closedir() sequence in one call. * * - There is no create() operation, mknod() will be called for creation of all * non directory, non symlink nodes. * * - open() No creation, or truncation flags (O_CREAT, O_EXCL, O_TRUNC) will be * passed to open(). Open should only check if the operation is permitted for * the given flags. * * - read(), write(), release() are are passed a filehandle that is returned * from open() in addition to a pathname. The offset of the read and write is * passed as the last argument, the number of bytes read/writen is returned * through the java.nio.ByteBuffer object * * - release() is called when an open file has: 1) all file descriptors closed * 2) all memory mappings unmapped This call need only be implemented if this * information is required. * * - flush() called when a file is closed (can be called multiple times for each * dup-ed filehandle) * * - fsync() called when file data should be synced (with a flag to sync only * data but not metadata) * */ public class FilesystemFuse implements Filesystem3, XattrSupport { private static final Log log = LogFactory.getLog(FilesystemFuse.class); protected static SwiftSession server; protected IsolationLevel isolationlevel = IsolationLevel.REPEATABLE_READS; protected CachePolicy cachepolicy = CachePolicy.CACHED; protected boolean commitAsync = true; protected Filesystem fs; private static final int MODE = 0777; private static final int BLOCK_SIZE = 512; private static final int NAME_LENGTH = 1024; protected static final String ROOT = "test"; private void commit(TxnHandle txn) { if (commitAsync) { txn.commitAsync(null); } else { txn.commit(); } } @Override public int chmod(String path, int mode) throws FuseException { // No model implemented log.info("chmod for " + path); return Errno.EROFS; } @Override public int chown(String path, int uid, int gid) throws FuseException { // No ownership model implemented log.info("chown for " + path); return Errno.EROFS; } @Override public int flush(String path, Object fileHandle) throws FuseException { String remotePath = getRemotePath(path); log.info("flush for " + path); synchronized (this) { TxnHandle txn = null; try { txn = server.beginTxn(isolationlevel, cachepolicy, false); IFile f = (IFile) fileHandle; File fstub = new File(remotePath); fs.updateFile(txn, fstub.getName(), fstub.getParent(), f); commit(txn); return 0; } catch (NetworkException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (WrongTypeException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (NoSuchObjectException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (VersionNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } txn.rollback(); } return Errno.EROFS; } @Override public int fsync(String path, Object fileHandle, boolean isDatasync) throws FuseException { String remotePath = getRemotePath(path); log.info("flush for " + remotePath); synchronized (this) { TxnHandle txn = null; try { txn = server.beginTxn(isolationlevel, cachepolicy, false); IFile f = (IFile) fileHandle; File fstub = new File(remotePath); fs.updateFile(txn, fstub.getName(), fstub.getParent(), f); commit(txn); // TODO Shouldn't this be one txn? txn = server.beginTxn(isolationlevel, cachepolicy, false); fileHandle = fs.readFile(txn, fstub.getName(), fstub.getParent()); commit(txn); return 0; } catch (NetworkException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (WrongTypeException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (NoSuchObjectException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (VersionNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } txn.rollback(); } return Errno.EROFS; } @Override public int getattr(String path, FuseGetattrSetter getattrSetter) throws FuseException { String remotePath = getRemotePath(path); File fstub = new File(remotePath); log.info("getattr for " + remotePath); int time = (int) (System.currentTimeMillis() / 1000L); synchronized (this) { TxnHandle txn = null; try { txn = server.beginTxn(isolationlevel, cachepolicy, true); if ("/".equals(path)) { DirectoryCRDT root = fs.getDirectory(txn, "/" + ROOT); getattrSetter.set(root.hashCode(), FuseFtypeConstants.TYPE_DIR | MODE, 1, 0, 0, 0, root.getValue().size() * NAME_LENGTH, (root.getValue().size() * NAME_LENGTH + BLOCK_SIZE - 1) / BLOCK_SIZE, time, time, time); } else if (fs.isDirectory(txn, fstub.getName(), fstub.getParent())) { DirectoryCRDT dir = fs.getDirectory(txn, remotePath); getattrSetter.set(dir.hashCode(), FuseFtypeConstants.TYPE_DIR | MODE, 1, 0, 0, 0, dir.getValue().size() * NAME_LENGTH, (dir.getValue().size() * NAME_LENGTH + BLOCK_SIZE - 1) / BLOCK_SIZE, time, time, time); } else if (fs.isFile(txn, fstub.getName(), fstub.getParent())) { IFile f = fs.readFile(txn, fstub.getName(), fstub.getParent()); getattrSetter.set(fstub.hashCode(), FuseFtypeConstants.TYPE_FILE | MODE, 1, 0, 0, 0, f.getSize(), (f.getSize() + BLOCK_SIZE - 1) / BLOCK_SIZE, time, time, time); } else { txn.rollback(); return Errno.ENOENT; } commit(txn); return 0; } catch (NetworkException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (WrongTypeException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (NoSuchObjectException e) { e.printStackTrace(); } catch (VersionNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } txn.rollback(); } return Errno.ENOENT; } @Override public int getdir(String path, FuseDirFiller filler) throws FuseException { String remotePath = getRemotePath(path); log.info("getdir for " + remotePath); synchronized (this) { TxnHandle txn = null; try { txn = server.beginTxn(isolationlevel, cachepolicy, true); DirectoryCRDT dir = fs.getDirectory(txn, remotePath); Collection<Pair<String, Class<?>>> c = dir.getValue(); for (Pair<String, Class<?>> entry : c) { String name = entry.getFirst(); // TODO This needs to be adapted for links and permissions int mode = MODE; int ftype = FuseFtypeConstants.TYPE_FILE; if (entry.getSecond().equals(DirectoryCRDT.class)) { ftype = FuseFtypeConstants.TYPE_DIR; } filler.add(name, entry.hashCode(), ftype | mode); } commit(txn); return 0; } catch (NetworkException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (WrongTypeException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (NoSuchObjectException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (VersionNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } txn.rollback(); } return Errno.EROFS; } @Override public int link(String from, String to) throws FuseException { // FIXME Links are future work... log.info("link from " + from + " to " + to); return Errno.EROFS; } @Override public int mkdir(String path, int mode) throws FuseException { String remotePath = getRemotePath(path); log.info("mkdir for " + remotePath); synchronized (this) { TxnHandle txn = null; try { txn = server.beginTxn(isolationlevel, cachepolicy, false); File f = new File(remotePath); log.info("creating dir " + f.getName() + " in parentdir " + f.getParent()); fs.createDirectory(txn, f.getName(), f.getParent()); commit(txn); return 0; } catch (NetworkException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (WrongTypeException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (NoSuchObjectException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (VersionNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } txn.rollback(); } return Errno.EROFS; } private String getRemotePath(String path) { if ("/".equals(path)) { return "/" + ROOT; } return "/" + ROOT + path; } @Override public int mknod(String path, int mode, int rdev) throws FuseException { String remotePath = getRemotePath(path); log.info("mknod for " + remotePath); synchronized (this) { TxnHandle txn = null; try { txn = server.beginTxn(isolationlevel, cachepolicy, false); File f = new File(remotePath); log.info("creating file " + f.getName() + " in parentdir " + f.getParent()); fs.createFile(txn, f.getName(), f.getParent()); commit(txn); return 0; } catch (NetworkException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (WrongTypeException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (NoSuchObjectException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (VersionNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } txn.rollback(); } return Errno.EROFS; } @Override public int open(String path, int flags, FuseOpenSetter openSetter) throws FuseException { String remotePath = getRemotePath(path); log.info("open for " + remotePath); synchronized (this) { TxnHandle txn = null; try { txn = server.beginTxn(isolationlevel, cachepolicy, true); File fstub = new File(remotePath); if (fstub.isDirectory()) { txn.rollback(); throw new FuseException().initErrno(FuseException.EISDIR); } log.info("opening file " + fstub.getName() + " in parentdir " + fstub.getParent()); IFile f = fs.readFile(txn, fstub.getName(), fstub.getParent()); commit(txn); openSetter.setFh(f); return 0; } catch (NetworkException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (WrongTypeException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (NoSuchObjectException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (VersionNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } txn.rollback(); } return Errno.EROFS; } @Override public int read(String path, Object fh, ByteBuffer buf, long offset) throws FuseException { String remotePath = getRemotePath(path); log.info("read for " + remotePath); if (fh instanceof IFile) { IFile f = (IFile) fh; f.read(buf, offset); return 0; } return Errno.EBADF; } @Override public int readlink(String path, CharBuffer link) throws FuseException { // TODO Auto-generated method stub log.info("readlink for " + path); return Errno.EROFS; } @Override public int release(String path, Object fileHandle, int flags) throws FuseException { // No action required here log.info("release for " + path); return 0; } @Override public int rename(String from, String to) throws FuseException { // TODO Auto-generated method stub log.info("rename from " + from + " to " + to); return Errno.EROFS; } @Override public int rmdir(String path) throws FuseException { String remotePath = getRemotePath(path); log.info("rmdir for " + remotePath); synchronized (this) { TxnHandle txn = null; try { txn = server.beginTxn(isolationlevel, cachepolicy, false); File f = new File(remotePath); fs.removeDirectory(txn, f.getName(), f.getParent()); commit(txn); return 0; } catch (NetworkException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (WrongTypeException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (NoSuchObjectException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (VersionNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } txn.rollback(); } return Errno.EROFS; } @Override public int statfs(FuseStatfsSetter arg0) throws FuseException { // TODO Auto-generated method stub log.info("statfs called"); return 0; } @Override public int symlink(String from, String to) throws FuseException { // FIXME Future work... log.info("symlink from " + from + " to " + to); return Errno.EROFS; } @Override public int truncate(String path, long mode) throws FuseException { // TODO Auto-generated method stub log.info("truncate for " + path); return 0; } @Override public int unlink(String path) throws FuseException { String remotePath = getRemotePath(path); log.info("Unlink for " + remotePath); synchronized (this) { TxnHandle txn = null; try { txn = server.beginTxn(isolationlevel, cachepolicy, false); File f = new File(remotePath); fs.removeFile(txn, f.getName(), f.getParent()); commit(txn); return 0; } catch (NetworkException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (WrongTypeException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (NoSuchObjectException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (VersionNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } txn.rollback(); } return Errno.EROFS; } @Override public int utime(String path, int atime, int mtime) throws FuseException { // Modification times are not supported yet.... log.info("Utime for " + path); return 0; } @Override public int write(String path, Object fh, boolean isWritepage, ByteBuffer buf, long offset) throws FuseException { String remotePath = getRemotePath(path); log.info("write for " + remotePath); if (fh instanceof IFile) { IFile f = (IFile) fh; f.update(buf, offset); } return 0; } // // 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 * @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 { log.info("getxattr " + name + " for " + path); return 0; // return Errno.ENOATTR; } /** * 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 { log.info("getxattrsize " + name + " for " + path); return 0; // return Errno.ENOATTR; } /** * 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 { log.info("listxattr for " + path); return 0; // return Errno.ENOATTR; } /** * 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 { log.info("removexattr " + name + " for " + path); return 0; // return Errno.ENOATTR; } /** * 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. * @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 { log.info("setxattr " + name + " for " + path); return 0; // return Errno.ENOATTR; } public static void initServerInfrastructure(String sequencerName, String scoutName) { DCSequencerServer.main(new String[] { "-name", sequencerName }); try { Thread.sleep(500); } catch (InterruptedException e) { // do nothing } DCServer.main(new String[] { sequencerName }); try { Thread.sleep(500); } catch (InterruptedException e) { // do nothing } Sys.getInstance(); server = SwiftImpl.newSingleSessionInstance(new SwiftOptions(scoutName, DCConstants.SURROGATE_PORT)); } public static void main(String[] args) { log.info("setting up servers"); initServerInfrastructure("localhost", "localhost"); try { log.info("getting root directory"); TxnHandle txn = server.beginTxn(IsolationLevel.SNAPSHOT_ISOLATION, CachePolicy.STRICTLY_MOST_RECENT, false); // create a root directory // FIXME make this part of arguments Filesystem fs = new FilesystemBasic(txn, ROOT, "DIR"); txn.commit(); log.info("mounting filesystem"); FilesystemFuse fuse = new FilesystemFuse(); fuse.fs = fs; FuseMount.mount(args, fuse, log); } catch (Exception e) { e.printStackTrace(); } finally { log.info("exiting"); } } }