#!/usr/bin/env python # July 2008: Bryan Newbold # Based on xmp.py import os, sys from errno import * from stat import * import fcntl # pull in some spaghetti to make this stuff work without fuse-py being installed try: import _find_fuse_parts except ImportError: pass import fuse from fuse import Fuse if not hasattr(fuse, '__version__'): raise RuntimeError, \ "your fuse-py doesn't know of fuse.__version__, probably it's too old." fuse.fuse_python_api = (0, 2) fuse.feature_assert('stateful_files', 'has_init') def flag2mode(flags): md = {os.O_RDONLY: 'r', os.O_WRONLY: 'w', os.O_RDWR: 'w+'} m = md[flags & (os.O_RDONLY | os.O_WRONLY | os.O_RDWR)] if flags | os.O_APPEND: m = m.replace('w', 'a', 1) return m class PyNamespace(Fuse): def __init__(self, *args, **kw): Fuse.__init__(self, *args, **kw) # do stuff to set up your filesystem here, if you want #import thread #thread.start_new_thread(self.mythread, ()) #self.root = '/' # def mythread(self): # # """ # The beauty of the FUSE python implementation is that with the python interp # running in foreground, you can have threads # """ # print "mythread: started" # while 1: # time.sleep(120) # print "mythread: ticking" def getattr(self, path): return locals()[path].__repr__ def readlink(self, path): return str(path) def readdir(self, path, offset): for e in os.listdir("." + path): yield fuse.Direntry(e) def unlink(self, path): del locals()[path] def rmdir(self, path): del locals()[path] def symlink(self, path, path1): locals()[path1] = locals()[path] def rename(self, path, path1): locals()[path1] = locals()[path] del locals()[path] def link(self, path, path1): locals()[path1] = locals()[path] def chmod(self, path, mode): return -EPFNOSUPPORT def chown(self, path, user, group): return -EPFNOSUPPORT def truncate(self, path, len): return -EPFNOSUPPORT def mknod(self, path, mode, dev): return -EPFNOSUPPORT def mkdir(self, path, mode): return -EPFNOSUPPORT def utime(self, path, times): return -EPFNOSUPPORT # The following utimens method would do the same as the above utime method. # We can't make it better though as the Python stdlib doesn't know of # subsecond preciseness in acces/modify times. # # def utimens(self, path, ts_acc, ts_mod): # os.utime("." + path, (ts_acc.tv_sec, ts_mod.tv_sec)) def access(self, path, mode): if not os.access("." + path, mode): return -EACCES # This is how we could add stub extended attribute handlers... # (We can't have ones which aptly delegate requests to the underlying fs # because Python lacks a standard xattr interface.) # # def getxattr(self, path, name, size): # val = name.swapcase() + '@' + path # if size == 0: # # We are asked for size of the value. # return len(val) # return val # # def listxattr(self, path, size): # # We use the "user" namespace to please XFS utils # aa = ["user." + a for a in ("foo", "bar")] # if size == 0: # # We are asked for size of the attr list, ie. joint size of attrs # # plus null separators. # return len("".join(aa)) + len(aa) # return aa def statfs(self): """ Should return an object with statvfs attributes (f_bsize, f_frsize...). Eg., the return value of os.statvfs() is such a thing (since py 2.2). If you are not reusing an existing statvfs object, start with fuse.StatVFS(), and define the attributes. To provide usable information (ie., you want sensible df(1) output, you are suggested to specify the following attributes: - f_bsize - preferred size of file blocks, in bytes - f_frsize - fundamental size of file blcoks, in bytes [if you have no idea, use the same as blocksize] - f_blocks - total number of blocks in the filesystem - f_bfree - number of free blocks - f_files - total number of file inodes - f_ffree - nunber of free file inodes """ rval = os.statvfs(".") rval[3] = len(locals()) rval[5] = len(locals()) return rval def fsinit(self): #os.chdir(self.root) pass class PyNamespaceFile(object): def __init__(self, path, flags, *mode): self.fobject = locals()[path] self.fstr = StringIO(str(self.fobject)) def read(self, length, offset): return self.fstr.read(length) def write(self, buf, offset): self.fstr.seek(offset) self.fstr.write(buf) return len(buf) def release(self, flags): self.fstr.close() def _fflush(self): if 'w' in self.fstr.mode or 'a' in self.fstr.mode: self.fstr.flush() def fsync(self, isfsyncfile): pass def flush(self): pass def fgetattr(self): pass def ftruncate(self, len): self.fstr.truncate(len) def lock(self, cmd, owner, **kw): # The code here is much rather just a demonstration of the locking # API than something which actually was seen to be useful. # Advisory file locking is pretty messy in Unix, and the Python # interface to this doesn't make it better. # We can't do fcntl(2)/F_GETLK from Python in a platfrom independent # way. The following implementation *might* work under Linux. # # if cmd == fcntl.F_GETLK: # import struct # # lockdata = struct.pack('hhQQi', kw['l_type'], os.SEEK_SET, # kw['l_start'], kw['l_len'], kw['l_pid']) # ld2 = fcntl.fcntl(self.fd, fcntl.F_GETLK, lockdata) # flockfields = ('l_type', 'l_whence', 'l_start', 'l_len', 'l_pid') # uld2 = struct.unpack('hhQQi', ld2) # res = {} # for i in xrange(len(uld2)): # res[flockfields[i]] = uld2[i] # # return fuse.Flock(**res) # Convert fcntl-ish lock parameters to Python's weird # lockf(3)/flock(2) medley locking API... return -EOPNOTSUPP #op = { fcntl.F_UNLCK : fcntl.LOCK_UN, # fcntl.F_RDLCK : fcntl.LOCK_SH, # fcntl.F_WRLCK : fcntl.LOCK_EX }[kw['l_type']] #if cmd == fcntl.F_GETLK: # return -EOPNOTSUPP #elif cmd == fcntl.F_SETLK: # if op != fcntl.LOCK_UN: # op |= fcntl.LOCK_NB #elif cmd == fcntl.F_SETLKW: # pass #else: # return -EINVAL #fcntl.lockf(self.fd, op, kw['l_start'], kw['l_len']) def main(self, *a, **kw): self.file_class = self.PyNamespaceFile return Fuse.main(self, *a, **kw) def main(): usage = """ Presents a python namespace as a filesystem for aesthetic reasons """ + Fuse.fusage server = PyNamespace(version="%prog " + fuse.__version__, usage=usage, dash_s_do='setsingle') #server.parser.add_option(mountopt="root", metavar="PATH", default='/', # help="mirror filesystem from under PATH [default: %default]") #server.parse(values=server, errex=1) #try: # if server.fuse_args.mount_expected(): # os.chdir(server.root) #except OSError: # print >> sys.stderr, "can't enter root of underlying filesystem" # sys.exit(1) server.main() if __name__ == '__main__': main()