aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Miklos Szeredi <miklos@szeredi.hu>2001-11-14 08:16:20 +0000
committerGravatar Miklos Szeredi <miklos@szeredi.hu>2001-11-14 08:16:20 +0000
commit838c14f9d46cb613fba86d12210221f93b1acf0c (patch)
tree53b104174d5ab1183ac12b8cdf7ea59712de85e2
parent05033041285858d581b05617900e2921502bf434 (diff)
added python bindings
-rw-r--r--AUTHORS9
-rw-r--r--ChangeLog12
-rw-r--r--README.python60
-rw-r--r--python/_fusemodule.c266
-rw-r--r--python/fuse.py101
5 files changed, 448 insertions, 0 deletions
diff --git a/AUTHORS b/AUTHORS
index f74e6de..5d0a86a 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -1 +1,10 @@
+FUSE core
+---------
+
Miklos Szeredi <mszeredi@inf.bme.hu>
+
+
+Python bindings
+---------------
+
+Jeff Epler <jepler@unpythonic.dhs.org>
diff --git a/ChangeLog b/ChangeLog
index 48cc699..cfa7aef 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,15 @@
2001-11-09 Miklos Szeredi <mszeredi@inf.bme.hu>
* Started ChangeLog
+
+2001-11-13 Miklos Szeredi <mszeredi@inf.bme.hu>
+
+ * Fixed vfsmount reference leak in fuse_follow_link
+
+ * FS blocksize is set to PAGE_CACHE_SIZE, blksize attribute from
+ userspace is ignored
+
+2001-11-14 Miklos Szeredi <mszeredi@inf.bme.hu>
+
+ * Python bindings by Jeff Epler added
+
diff --git a/README.python b/README.python
new file mode 100644
index 0000000..451c56c
--- /dev/null
+++ b/README.python
@@ -0,0 +1,60 @@
+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?)
+
+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/
diff --git a/python/_fusemodule.c b/python/_fusemodule.c
new file mode 100644
index 0000000..eee4fda
--- /dev/null
+++ b/python/_fusemodule.c
@@ -0,0 +1,266 @@
+#include "Python.h"
+#include "fuse.h"
+#include <time.h>
+
+static PyObject *ErrorObject;
+
+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;
+struct fuse *fuse=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;
+static int getattr_func(const char *path, struct stat *st) {
+ int i;
+ PyObject *v = PyObject_CallFunction(getattr_cb, "s", path);
+ PROLOGUE
+
+ if(!PyTuple_Check(v)) { goto OUT_DECREF; }
+ if(PyTuple_Size(v) < 10) { goto OUT_DECREF; }
+ for(i=0; i<10; i++) {
+ if (!PyInt_Check(PyTuple_GetItem(v, 0))) goto OUT_DECREF;
+ }
+
+ st->st_mode = PyInt_AsLong(PyTuple_GetItem(v, 0));
+ st->st_ino = PyInt_AsLong(PyTuple_GetItem(v, 1));
+ st->st_dev = PyInt_AsLong(PyTuple_GetItem(v, 2));
+ st->st_nlink= PyInt_AsLong(PyTuple_GetItem(v, 3));
+ st->st_uid = PyInt_AsLong(PyTuple_GetItem(v, 4));
+ st->st_gid = PyInt_AsLong(PyTuple_GetItem(v, 5));
+ st->st_size = PyInt_AsLong(PyTuple_GetItem(v, 6));
+ st->st_atime= PyInt_AsLong(PyTuple_GetItem(v, 7));
+ st->st_mtime= PyInt_AsLong(PyTuple_GetItem(v, 8));
+ st->st_ctime= PyInt_AsLong(PyTuple_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_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);
+ printf("getdir_func validate %d\n", i);
+ if(!PySequence_Check(w)) { printf("getdir item not sequence\n"); goto OUT_DECREF; }
+ if(PySequence_Length(w) != 2) { printf("getdir item not len 2\n"); goto OUT_DECREF; }
+ if(!PyString_Check(PySequence_GetItem(w,0))){ printf("getdir item[0] not string"); goto OUT_DECREF; }
+ if(!PyInt_Check(PySequence_GetItem(w, 1))) { printf("getdir item[1] not int"); goto OUT_DECREF; }
+ }
+
+ for(i=0; i < PySequence_Length(v); i++) {
+ PyObject *w = PySequence_GetItem(v, i);
+ printf("getdir_func %d\n", i);
+ ret = df(dh, PyString_AsString(PySequence_GetItem(w, 0)),
+ PyInt_AsLong(PySequence_GetItem(w, 1)));
+ if(ret) goto OUT_DECREF;
+ }
+
+ ret = 0;
+
+ EPILOGUE
+}
+
+int mknod_func(const char *path, mode_t m, dev_t d) {
+ PyObject *v = PyObject_CallFunction(mknod_cb, "sii", path, m, d);
+ PROLOGUE
+ EPILOGUE
+}
+
+int mkdir_func(const char *path, mode_t m) {
+ PyObject *v = PyObject_CallFunction(mkdir_cb, "si", path, m);
+ PROLOGUE
+ EPILOGUE
+}
+
+int unlink_func(const char *path) {
+ PyObject *v = PyObject_CallFunction(unlink_cb, "s", path);
+ PROLOGUE
+ EPILOGUE
+}
+
+int rmdir_func(const char *path) {
+ PyObject *v = PyObject_CallFunction(rmdir_cb, "s", path);
+ PROLOGUE
+ EPILOGUE
+}
+
+int symlink_func(const char *path, const char *path1) {
+ PyObject *v = PyObject_CallFunction(symlink_cb, "ss", path, path1);
+ PROLOGUE
+ EPILOGUE
+}
+
+int rename_func(const char *path, const char *path1) {
+ PyObject *v = PyObject_CallFunction(rename_cb, "ss", path, path1);
+ PROLOGUE
+ EPILOGUE
+}
+
+int link_func(const char *path, const char *path1) {
+ PyObject *v = PyObject_CallFunction(link_cb, "ss", path, path1);
+ PROLOGUE
+ EPILOGUE
+}
+
+int chmod_func(const char *path, mode_t m) {
+ PyObject *v = PyObject_CallFunction(chmod_cb, "si", path, m);
+ PROLOGUE
+ EPILOGUE
+}
+
+int chown_func(const char *path, uid_t u, gid_t g) {
+ PyObject *v = PyObject_CallFunction(chown_cb, "sii", path, u, g);
+ PROLOGUE
+ EPILOGUE
+}
+
+int truncate_func(const char *path, off_t o) {
+ PyObject *v = PyObject_CallFunction(truncate_cb, "si", path, o);
+ PROLOGUE
+ EPILOGUE
+}
+
+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
+}
+
+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
+}
+
+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
+}
+
+int open_func(const char *path, int mode) {
+ PyObject *v = PyObject_CallFunction(open_cb, "si", path, mode);
+ PROLOGUE
+ EPILOGUE
+}
+
+static PyObject *
+Fuse_main(PyObject *self, PyObject *args, PyObject *kw)
+{
+ PyObject *list, *item;
+
+ int flags=0;
+
+ struct fuse_operations op;
+
+ static char *kwlist[] = {
+ "getattr", "readlink", "getdir", "mknod",
+ "mkdir", "unlink", "rmdir", "symlink", "rename",
+ "link", "chmod", "chown", "truncate", "utime",
+ "open", "read", "write", "flags", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "|OOOOOOOOOOOOOOOOOi",
+ 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, &flags))
+ 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);
+
+ fuse = fuse_new(0, flags);
+ fuse_set_operations(fuse, &op);
+ fuse_loop(fuse);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+/* List of functions defined in the module */
+
+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;
+
+ /* 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, "MULTITHREAD", PyInt_FromLong(FUSE_MULTITHREAD));
+ PyDict_SetItemString(d, "DEBUG", PyInt_FromLong(FUSE_DEBUG));
+
+}
diff --git a/python/fuse.py b/python/fuse.py
new file mode 100644
index 0000000..ec40262
--- /dev/null
+++ b/python/fuse.py
@@ -0,0 +1,101 @@
+#!/usr/bin/env python
+
+from _fuse import main, DEBUG
+import os
+from stat import *
+from errno import *
+
+class ErrnoWrapper:
+ 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:
+ _attrs = ['getattr', 'readlink', 'getdir', 'mknod', 'mkdir',
+ 'unlink', 'rmdir', 'symlink', 'rename', 'link', 'chmod',
+ 'chown', 'truncate', 'utime', 'open', 'read', 'write']
+
+ flags = 0
+ def main(self):
+ d = {'flags': self.flags}
+ for a in self._attrs:
+ if hasattr(self,a):
+ d[a] = ErrnoWrapper(getattr(self, a))
+ apply(main, (), d)
+
+class Xmp(Fuse):
+ 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.lchown(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):
+ os.close(os.open(path, flags))
+ return 0
+
+ def read(self, path, len, offset):
+ f = open(path, "r")
+ f.seek(offset)
+ return f.read(len)
+
+ def write(self, path, buf, off):
+ f = open(path, "r+")
+ f.seek(off)
+ f.write(buf)
+ return len(buf)
+
+if __name__ == '__main__':
+ server = Xmp()
+ server.flags = DEBUG
+ server.main()