fuse python bindings @file _fusemodule.c includes globals PROLOGUE EPILOGUE getattr_func readlink_func getdir_add_entry getdir_func mknod_func mkdir_func unlink_func rmdir_func symlink_func rename_func link_func chmod_func chown_func truncate_func utime_func read_func write_func open_func release_func statfs_func fsync_func process_cmd pyfuse_loop_mt Fuse_main DL_EXPORT @file fuse.py imports class ErrnoWrapper __init__ __call__ class Fuse attribs __init__ main @file Makefile @file xmp.py imports class Xmp __init__ mythread attribs getattr readlink getdir unlink rmdir symlink rename link chmod chown truncate mknod mkdir utime open read write release statfs fsync mainline @file setup.py @file README @file mount.fuse @file fuse.py << fuse declarations >> class ErrnoWrapper << class ErrnoWrapper declarations >> __init__ __call__ class Fuse << class Fuse declarations >> __init__ main @language c /* Copyright (C) 2001 Jeff Epler <jepler@unpythonic.dhs.org> This program can be distributed under the terms of the GNU GPL. See the file COPYING. */ @others #include <Python.h> #include <fuse.h> #include <time.h> static PyObject *getattr_cb=NULL, *readlink_cb=NULL, *getdir_cb=NULL, *mknod_cb=NULL, *mkdir_cb=NULL, *unlink_cb=NULL, *rmdir_cb=NULL, *symlink_cb=NULL, *rename_cb=NULL, *link_cb=NULL, *chmod_cb=NULL, *chown_cb=NULL, *truncate_cb=NULL, *utime_cb=NULL, *open_cb=NULL, *read_cb=NULL, *write_cb=NULL, *release_cb=NULL, *statfs_cb=NULL, *fsync_cb=NULL ; #define PROLOGUE \ int ret = -EINVAL; \ if (!v) { PyErr_Print(); goto OUT; } \ if(v == Py_None) { ret = 0; goto OUT_DECREF; } \ if(PyInt_Check(v)) { ret = PyInt_AsLong(v); goto OUT_DECREF; } #define EPILOGUE \ OUT_DECREF: \ Py_DECREF(v); \ OUT: \ return ret; /* * Local Variables: * indent-tabs-mode: t * c-basic-offset: 8 * End: * Changed by David McNab (david@rebirthing.co.nz) to work with recent pythons. * Namely, replacing PyTuple_* with PySequence_*, and checking numerical values * with both PyInt_Check and PyLong_Check. */ static int getattr_func(const char *path, struct stat *st) { int i; PyObject *v = PyObject_CallFunction(getattr_cb, "s", path); PROLOGUE if(!PySequence_Check(v)) { goto OUT_DECREF; } if(PySequence_Size(v) < 10) { goto OUT_DECREF; } for(i=0; i<10; i++) { PyObject *tmp = PySequence_GetItem(v, i); if (!(PyInt_Check(tmp) || PyLong_Check(tmp))) goto OUT_DECREF; } st->st_mode = PyInt_AsLong(PySequence_GetItem(v, 0)); st->st_ino = PyInt_AsLong(PySequence_GetItem(v, 1)); st->st_dev = PyInt_AsLong(PySequence_GetItem(v, 2)); st->st_nlink= PyInt_AsLong(PySequence_GetItem(v, 3)); st->st_uid = PyInt_AsLong(PySequence_GetItem(v, 4)); st->st_gid = PyInt_AsLong(PySequence_GetItem(v, 5)); st->st_size = PyInt_AsLong(PySequence_GetItem(v, 6)); st->st_atime= PyInt_AsLong(PySequence_GetItem(v, 7)); st->st_mtime= PyInt_AsLong(PySequence_GetItem(v, 8)); st->st_ctime= PyInt_AsLong(PySequence_GetItem(v, 9)); /* Fill in fields not provided by Python lstat() */ st->st_blksize= 4096; st->st_blocks= (st->st_size + 511)/512; st->st_ino = 0; ret = 0; EPILOGUE } static int readlink_func(const char *path, char *link, size_t size) { PyObject *v = PyObject_CallFunction(readlink_cb, "s", path); char *s; PROLOGUE if(!PyString_Check(v)) { ret = -EINVAL; goto OUT_DECREF; } s = PyString_AsString(v); strncpy(link, s, size); link[size-1] = '\0'; ret = 0; EPILOGUE } static int getdir_add_entry(PyObject *w, fuse_dirh_t dh, fuse_dirfil_t df) { PyObject *o0; PyObject *o1; int ret = -EINVAL; if(!PySequence_Check(w)) { printf("getdir item not sequence\n"); goto out; } if(PySequence_Length(w) != 2) { printf("getdir item not len 2\n"); goto out; } o0 = PySequence_GetItem(w, 0); o1 = PySequence_GetItem(w, 1); if(!PyString_Check(o0)) { printf("getdir item[0] not string\n"); goto out_decref; } if(!PyInt_Check(o1)) { printf("getdir item[1] not int\n"); goto out_decref; } ret = df(dh, PyString_AsString(o0), PyInt_AsLong(o1)); out_decref: Py_DECREF(o0); Py_DECREF(o1); out: return ret; } static int getdir_func(const char *path, fuse_dirh_t dh, fuse_dirfil_t df) { PyObject *v = PyObject_CallFunction(getdir_cb, "s", path); int i; PROLOGUE if(!PySequence_Check(v)) { printf("getdir_func not sequence\n"); goto OUT_DECREF; } for(i=0; i < PySequence_Length(v); i++) { PyObject *w = PySequence_GetItem(v, i); ret = getdir_add_entry(w, dh, df); Py_DECREF(w); if(ret != 0) goto OUT_DECREF; } ret = 0; EPILOGUE } static int mknod_func(const char *path, mode_t m, dev_t d) { PyObject *v = PyObject_CallFunction(mknod_cb, "sii", path, m, d); PROLOGUE EPILOGUE } static int mkdir_func(const char *path, mode_t m) { PyObject *v = PyObject_CallFunction(mkdir_cb, "si", path, m); PROLOGUE EPILOGUE } static int unlink_func(const char *path) { PyObject *v = PyObject_CallFunction(unlink_cb, "s", path); PROLOGUE EPILOGUE } static int rmdir_func(const char *path) { PyObject *v = PyObject_CallFunction(rmdir_cb, "s", path); PROLOGUE EPILOGUE } static int symlink_func(const char *path, const char *path1) { PyObject *v = PyObject_CallFunction(symlink_cb, "ss", path, path1); PROLOGUE EPILOGUE } static int rename_func(const char *path, const char *path1) { PyObject *v = PyObject_CallFunction(rename_cb, "ss", path, path1); PROLOGUE EPILOGUE } static int link_func(const char *path, const char *path1) { PyObject *v = PyObject_CallFunction(link_cb, "ss", path, path1); PROLOGUE EPILOGUE } static int chmod_func(const char *path, mode_t m) { PyObject *v = PyObject_CallFunction(chmod_cb, "si", path, m); PROLOGUE EPILOGUE } static int chown_func(const char *path, uid_t u, gid_t g) { PyObject *v = PyObject_CallFunction(chown_cb, "sii", path, u, g); PROLOGUE EPILOGUE } static int truncate_func(const char *path, off_t o) { PyObject *v = PyObject_CallFunction(truncate_cb, "si", path, o); PROLOGUE EPILOGUE } static int utime_func(const char *path, struct utimbuf *u) { int actime = u ? u->actime : time(NULL); int modtime = u ? u->modtime : actime; PyObject *v = PyObject_CallFunction(utime_cb, "s(ii)", path, actime, modtime); PROLOGUE EPILOGUE } static int read_func(const char *path, char *buf, size_t s, off_t off) { PyObject *v = PyObject_CallFunction(read_cb, "sii", path, s, off); PROLOGUE if(PyString_Check(v)) { if(PyString_Size(v) > s) goto OUT_DECREF; memcpy(buf, PyString_AsString(v), PyString_Size(v)); ret = PyString_Size(v); } EPILOGUE } static int write_func(const char *path, const char *buf, size_t t, off_t off) { PyObject *v = PyObject_CallFunction(write_cb,"ss#i", path, buf, t, off); PROLOGUE EPILOGUE } static int open_func(const char *path, int mode) { PyObject *v = PyObject_CallFunction(open_cb, "si", path, mode); PROLOGUE printf("open_func: path=%s\n", path); EPILOGUE } static int release_func(const char *path, int flags) { PyObject *v = PyObject_CallFunction(release_cb, "si", path, flags); PROLOGUE //printf("release_func: path=%s flags=%d\n", path, flags); EPILOGUE } static void process_cmd(struct fuse *f, struct fuse_cmd *cmd, void *data) { PyInterpreterState *interp = (PyInterpreterState *) data; PyThreadState *state; PyEval_AcquireLock(); state = PyThreadState_New(interp); PyThreadState_Swap(state); __fuse_process_cmd(f, cmd); PyThreadState_Clear(state); PyThreadState_Swap(NULL); PyThreadState_Delete(state); PyEval_ReleaseLock(); } static void pyfuse_loop_mt(struct fuse *f) { PyInterpreterState *interp; PyThreadState *save; PyEval_InitThreads(); interp = PyThreadState_Get()->interp; save = PyEval_SaveThread(); __fuse_loop_mt(f, process_cmd, interp); /* Not yet reached: */ PyEval_RestoreThread(save); } static PyObject * Fuse_main(PyObject *self, PyObject *args, PyObject *kw) { int flags=0; int multithreaded=0; static struct fuse *fuse=NULL; struct fuse_operations op; static char *kwlist[] = { "getattr", "readlink", "getdir", "mknod", "mkdir", "unlink", "rmdir", "symlink", "rename", "link", "chmod", "chown", "truncate", "utime", "open", "read", "write", "release", "statfs", "fsync", "flags", "multithreaded", NULL}; memset(&op, 0, sizeof(op)); if (!PyArg_ParseTupleAndKeywords(args, kw, "|OOOOOOOOOOOOOOOOOOOOii", kwlist, &getattr_cb, &readlink_cb, &getdir_cb, &mknod_cb, &mkdir_cb, &unlink_cb, &rmdir_cb, &symlink_cb, &rename_cb, &link_cb, &chmod_cb, &chown_cb, &truncate_cb, &utime_cb, &open_cb, &read_cb, &write_cb, &release_cb, &statfs_cb, &fsync_cb, &flags, &multithreaded)) return NULL; #define DO_ONE_ATTR(name) if(name ## _cb) { Py_INCREF(name ## _cb); op.name = name ## _func; } else { op.name = NULL; } DO_ONE_ATTR(getattr); DO_ONE_ATTR(readlink); DO_ONE_ATTR(getdir); DO_ONE_ATTR(mknod); DO_ONE_ATTR(mkdir); DO_ONE_ATTR(unlink); DO_ONE_ATTR(rmdir); DO_ONE_ATTR(symlink); DO_ONE_ATTR(rename); DO_ONE_ATTR(link); DO_ONE_ATTR(chmod); DO_ONE_ATTR(chown); DO_ONE_ATTR(truncate); DO_ONE_ATTR(utime); DO_ONE_ATTR(open); DO_ONE_ATTR(read); DO_ONE_ATTR(write); DO_ONE_ATTR(release); DO_ONE_ATTR(statfs); DO_ONE_ATTR(fsync); fuse = fuse_new(0, flags, &op); if(multithreaded) pyfuse_loop_mt(fuse); else fuse_loop(fuse); //printf("Fuse_main: called\n"); Py_INCREF(Py_None); return Py_None; } @ List of functions defined in the module @c static PyMethodDef Fuse_methods[] = { {"main", (PyCFunction)Fuse_main, METH_VARARGS|METH_KEYWORDS}, {NULL, NULL} /* sentinel */ }; /* Initialization function for the module (*must* be called init_fuse) */ DL_EXPORT(void) init_fuse(void) { PyObject *m, *d; static PyObject *ErrorObject; /* Create the module and add the functions */ m = Py_InitModule("_fuse", Fuse_methods); /* Add some symbolic constants to the module */ d = PyModule_GetDict(m); ErrorObject = PyErr_NewException("fuse.error", NULL, NULL); PyDict_SetItemString(d, "error", ErrorObject); PyDict_SetItemString(d, "DEBUG", PyInt_FromLong(FUSE_DEBUG)); } # # Copyright (C) 2001 Jeff Epler <jepler@unpythonic.dhs.org> # # This program can be distributed under the terms of the GNU GPL. # See the file COPYING. # @language python @others # suppress version mismatch warnings try: import warnings warnings.filterwarnings('ignore', 'Python C API version mismatch', RuntimeWarning, ) except: pass from _fuse import main, DEBUG import os, sys from errno import * class ErrnoWrapper: @others def __init__(self, func): self.func = func def __call__(self, *args, **kw): try: return apply(self.func, args, kw) except (IOError, OSError), detail: # Sometimes this is an int, sometimes an instance... if hasattr(detail, "errno"): detail = detail.errno return -detail class Fuse: @others _attrs = ['getattr', 'readlink', 'getdir', 'mknod', 'mkdir', 'unlink', 'rmdir', 'symlink', 'rename', 'link', 'chmod', 'chown', 'truncate', 'utime', 'open', 'read', 'write', 'release', 'statfs', 'fsync'] flags = 0 multithreaded = 0 def __init__(self, *args, **kw): # default attributes self.optlist = [] self.optdict = {} self.mountpoint = None # grab arguments, if any argv = sys.argv argc = len(argv) if argc > 1: # we've been given the mountpoint self.mountpoint = argv[1] if argc > 2: # we've received mount args optstr = argv[2] opts = optstr.split(",") for o in opts: try: k, v = o.split("=", 1) self.optdict[k] = v except: self.optlist.append(o) def main(self): d = {'flags': self.flags} d['multithreaded'] = self.multithreaded for a in self._attrs: if hasattr(self,a): d[a] = ErrnoWrapper(getattr(self, a)) apply(main, (), d) # Makefile now uses distutils _fusemodule.so: _fusemodule.c #gcc -g3 -I/usr/include/python2.1 _fusemodule.c -Wl,-shared -o _fusemodule.so -Wimplicit -lfuse && python -c 'import _fuse' python setup.py build_ext --inplace install: _fusemodule.so python setup.py install clean: rm -rf _fusemodule.so *.pyc *.pyo *~ build @first #!/usr/bin/env python # # Copyright (C) 2001 Jeff Epler <jepler@unpythonic.dhs.org> # # This program can be distributed under the terms of the GNU GPL. # See the file COPYING. # @others from fuse import Fuse import os from errno import * from stat import * import thread class Xmp(Fuse): @others def __init__(self, *args, **kw): Fuse.__init__(self, *args, **kw) if 0: print "xmp.py:Xmp:mountpoint: %s" % repr(self.mountpoint) print "xmp.py:Xmp:unnamed mount options: %s" % self.optlist print "xmp.py:Xmp:named mount options: %s" % self.optdict # do stuff to set up your filesystem here, if you want #thread.start_new_thread(self.mythread, ()) pass 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" flags = 1 def getattr(self, path): return os.lstat(path) def readlink(self, path): return os.readlink(path) def getdir(self, path): return map(lambda x: (x,0), os.listdir(path)) def unlink(self, path): return os.unlink(path) def rmdir(self, path): return os.rmdir(path) def symlink(self, path, path1): return os.symlink(path, path1) def rename(self, path, path1): return os.rename(path, path1) def link(self, path, path1): return os.link(path, path1) def chmod(self, path, mode): return os.chmod(path, mode) def chown(self, path, user, group): return os.chown(path, user, group) def truncate(self, path, size): f = open(path, "w+") return f.truncate(size) def mknod(self, path, mode, dev): """ Python has no os.mknod, so we can only do some things """ if S_ISREG(mode): open(path, "w") else: return -EINVAL def mkdir(self, path, mode): return os.mkdir(path, mode) def utime(self, path, times): return os.utime(path, times) def open(self, path, flags): #print "xmp.py:Xmp:open: %s" % path os.close(os.open(path, flags)) return 0 def read(self, path, len, offset): #print "xmp.py:Xmp:read: %s" % path f = open(path, "r") f.seek(offset) return f.read(len) def write(self, path, buf, off): #print "xmp.py:Xmp:write: %s" % path f = open(path, "r+") f.seek(off) f.write(buf) return len(buf) def release(self, path, flags): print "xmp.py:Xmp:release: %s %s" % (path, flags) return 0 if __name__ == '__main__': server = Xmp() server.flags = 0 server.multithreaded = 1; server.main() """ distutils script for FUSE python module """ from distutils.core import setup, Extension setup(name="fuse", version="0.1", ext_modules=[Extension("_fusemodule", ["_fusemodule.c"], library_dirs=["../lib",], libraries=["fuse",], ), ], py_modules=["fuse"], ) @language Refer to the INSTALL file for build/install instructions General Information =================== This is a Python[1] interface to FUSE[2]. FUSE (Filesystem in USErspace) is a simple interface for userspace programs to export a virtual filesystem to the linux kernel. FUSE also aims to provide a secure method for non privileged users to create and mount their own filesystem implementations. When run from the commandline, "fuse.py" simply reexports the root filesystem within the mount point as example/fusexmp does in the main FUSE distribution. It also offers a class, fuse.Fuse, which can be subclassed to create a filesystem. fuse.Xmp is the example filesystem implementation. In your subclass of fuse, add attributes with the expected names ("getattr", "readlink", etc) and call signatures (refer to fuse.Xmp) then call main(). Make it runnable as a #! script, and mount with fusermount <mount point> <script name> for some reason, fusermount <mount point> python <script name> does not seem to work. (why?) Update ====== Updated 13-Dec-2003 by David McNab <david@rebirthing.co.nz> - changed Makefile to use Pyton distutils - added setup.py for distutils - added 'code.leo' file for convenience of those who use the Leo code editor (leo.sf.net) - added support for 'statfs' and 'fsync' methods (refer xmp.py) Updated Dec 2003 by David McNab <david@rebirthing.co.nz>: - added support for 'release' events (ie when file gets closed) - added __init__ to base class, which picks off parameters and stores them as instance attributes: - self.mountpoint - the mountpoint as given in the mount command - self.optlist - unnamed options (eg 'rw', 'exec' etc) - self.optdict - named options (eg, '-o arg1=val1,arg2=val2...' from mount cmd) - fixed incompatibility issues with recent pythons (original was broken under python2.3) Limitations =========== This is minimally tested, though I think I have exercised each function. There's no documentation, docstrings, or tests. Python's lstat() does not return some fields which must be filled in (st_blksize, st_blocks, st_ino), and _fusemodule assumes that the return value from the lstat() method is identical to Python's lstat(). This limitation should be lifted, and some standard order chosen for these three values. For now, though, default values are chosen and du returns a number similar to the "real" one. The Python Global Interpreter Lock is not handled, so using fuse.MULTITHREAD will not work. Modifying the PROLOGUE and EPILOGUE functions may take care of this. For now, just run without fuse.MULTITHREAD in flags. Author ====== I'm Jeff Epler <jepler@unpythonic.dhs.org>. I've been dabbling in Python for nearly 7 years now, and interested (despite the lack of a real practical use) in userspace filesystems ever since I couldn't get userfs to compile way back in '93 or so. FUSE is cool, but i'm still not sure what it's good for in practical terms. I don't know how high a level of interest I'll maintain in this project, so if you want to do something with it feel free to do so. Like FUSE, this software is distributed under the terms of the GNU General Public License, Version 2. Future versions, if any, will be available at [3]. [1] http://www.python.org [2] http://sourceforge.net/projects/avf/ [3] http://unpythonic.dhs.org/~jepler/fuse/ @first #!/usr/bin/env python """ This utility allows FUSE filesystems to be mounted with the regular *nix 'mount' command, or even be listed in /etc/fstab To enable this, you need to: 1. set execute-permission on this script 2. symlink this script into /sbin/mount.fuse Usage: You can use this in 3 ways: 1. mount -t fuse /path/to/script/or/program /path/of/mount/point [options] 2. mount -t fuse none /path/of/mount/point -o fs=/path/to/script/or/prog[,opt=val...] 3. in /etc/fstab, add: /path/to/script/or/prog /path/of/mount/point fuse noauto[,...] """ import sys, os, time progname = sys.argv[0] def usage(ret): print "Usage: %s /path/to/fuse/fs /path/of/mountpoint [-o options]" % progname print "or: %s none /path/of/mountpoint [-o fs=/path/to/fuse/fs[,...]]" % progname sys.exit(ret) def main(): # initial sanity check argc = len(sys.argv) if argc < 3 or sys.argv[3] != "-o": usage(1) dev = sys.argv[1] mountpoint = sys.argv[2] # grab options, if any optdict = {} optlist = [] if argc > 4: odata = sys.argv[4] opts = odata.split(",") #print opts for o in opts: try: k, v = o.split("=", 1) optdict[k] = v except: optlist.append(o) else: odata = "" #print sys.argv if dev == 'none': if not optdict.has_key("fs"): print "%s: Must specify python file with 'fs' option\n" % progname usage(1) pyfile = optdict['fs'] else: pyfile = dev if not os.path.isfile(pyfile): print "%s: file %s doesn't exist, or is not a file" % (progname, pyfile) sys.exit(1) pypath = os.path.abspath(pyfile) #print optdict, optlist # all seems ok - run our fuse fs as a child if os.fork() == 0: os.system("fusermount -c -x %s %s %s %s" % (mountpoint, pypath, mountpoint, odata)) else: #print "parent exiting..." pass if __name__ == '__main__': main() def statfs(self): """ Should return a tuple with the following 6 elements: - blocksize - size of file blocks, in bytes - totalblocks - total number of blocks in the filesystem - freeblocks - number of free blocks - totalfiles - total number of file inodes - freefiles - nunber of free file inodes Feel free to set any of the above values to 0, which tells the kernel that the info is not available. """ print "xmp.py:Xmp:statfs: returning fictitious values" blocks_size = 1024 blocks = 100000 blocks_free = 25000 files = 100000 files_free = 60000 namelen = 80 return (blocks_size, blocks, blocks_free, files, files_free, namelen) def fsync(self, path, isfsyncfile): print "xmp.py:Xmp:fsync: path=%s, isfsyncfile=%s" % (path, isfsyncfile) return 0 static int statfs_func(struct fuse_statfs *fst) { int i; long retvalues[6]; PyObject *v = PyObject_CallFunction(statfs_cb, ""); PROLOGUE if (!PySequence_Check(v)) { goto OUT_DECREF; } if (PySequence_Size(v) < 6) { goto OUT_DECREF; } for(i=0; i<6; i++) { PyObject *tmp = PySequence_GetItem(v, i); retvalues[i] = PyInt_Check(tmp) ? PyInt_AsLong(tmp) : (PyLong_Check(tmp) ? PyLong_AsLong(tmp) : 0); } fst->block_size = retvalues[0]; fst->blocks = retvalues[1]; fst->blocks_free = retvalues[2]; fst->files = retvalues[3]; fst->files_free = retvalues[4]; fst->namelen = retvalues[5]; ret = 0; #ifdef IGNORE_THIS printf("block_size=%ld, blocks=%ld, blocks_free=%ld, files=%ld, files_free=%ld, namelen=%ld\n", retvalues[0], retvalues[1], retvalues[2], retvalues[3], retvalues[4], retvalues[5]); #endif EPILOGUE } static int fsync_func(const char *path, int isfsyncfile) { PyObject *v = PyObject_CallFunction(fsync_cb, "si", path, isfsyncfile); PROLOGUE EPILOGUE } @ignore @language python << fuse declarations >> @others #@-node:main #@-others #@-node:class Fuse #@-others #@-node:@file fuse.py #@-leo #@+leo-ver=4 #@+node:@file fuse.py # # Copyright (C) 2001 Jeff Epler <jepler@unpythonic.dhs.org> # # This program can be distributed under the terms of the GNU GPL. # See the file COPYING. # #@@language python #@+others #@+node:imports # suppress version mismatch warnings try: import warnings warnings.filterwarnings('ignore', 'Python C API version mismatch', RuntimeWarning, ) except: pass from _fuse import main, DEBUG import os, sys from errno import * #@-node:imports #@+node:class ErrnoWrapper class ErrnoWrapper: << class ErrnoWrapper declarations >> @others #@ @+others #@+node:__init__ def __init__(self, func): self.func = func #@-node:__init__ #@+node:__call__ def __call__(self, *args, **kw): try: return apply(self.func, args, kw) except (IOError, OSError), detail: # Sometimes this is an int, sometimes an instance... if hasattr(detail, "errno"): detail = detail.errno return -detail #@-node:__call__ #@-others #@-node:class ErrnoWrapper #@+node:class Fuse class Fuse: << class Fuse declarations >> @others #@ @+others #@+node:attribs _attrs = ['getattr', 'readlink', 'getdir', 'mknod', 'mkdir', 'unlink', 'rmdir', 'symlink', 'rename', 'link', 'chmod', 'chown', 'truncate', 'utime', 'open', 'read', 'write', 'release', 'statfs', 'fsync'] flags = 0 multithreaded = 0 #@-node:attribs #@+node:__init__ def __init__(self, *args, **kw): # default attributes self.optlist = [] self.optdict = {} self.mountpoint = None # grab arguments, if any argv = sys.argv argc = len(argv) if argc > 1: # we've been given the mountpoint self.mountpoint = argv[1] if argc > 2: # we've received mount args optstr = argv[2] opts = optstr.split(",") for o in opts: try: k, v = o.split("=", 1) self.optdict[k] = v except: self.optlist.append(o) #@-node:__init__ #@+node:main def main(self): d = {'flags': self.flags} d['multithreaded'] = self.multithreaded for a in self._attrs: if hasattr(self,a): d[a] = ErrnoWrapper(getattr(self, a)) apply(main, (), d)