Update code so that it can use newer FUSE features / I.

Make file I/O stateful:
  - If `open' methods returns an object, that will
    be preserved and passed around to I/O requests
    on the file in question.
  - Added support for `create' method
  - Wrap this mechanism into OO: file I/O methods
    can be implemented via an user specified class.
  - Add warning to README.new_fusepy_api
This commit is contained in:
dzsekijo 2006-05-31 16:42:54 +00:00
parent 4fad6a4f0e
commit 2bcf2878f2
5 changed files with 163 additions and 68 deletions

View file

@ -1,3 +1,14 @@
2006-05-31 Csaba Henk <csaba.henk@creo.hu>
* Update code so that it can use newer FUSE features:
- Stage 1: make file I/O stateful.
+ If `open' methods returns an object, that will
be preserved and passed around to I/O requests
on the file in question.
+ Added support for `create' method
+ Wrap this mechanism into OO: file I/O methods
can be implemented via an user specified class.
+ Add warning to README.new_fusepy_api
2006-05-29 Csaba Henk <csaba.henk@creo.hu>
* Add some missing Py_DECREF-s.
* Make compat layer side effect free, fix bailing out upon

View file

@ -7,6 +7,15 @@ user and the developer POV.
.. contents::
.. Attention:: The code is being adjusted so that it can sanely use
newer FUSE features.
This document will not be updated until the end of this period.
Moreover, you can count on temporary breaks in compatibility
with oldish code (either C or Python).
Enforcing compatibility
-----------------------

View file

@ -23,6 +23,7 @@
#include <sys/types.h>
#include <sys/param.h>
#include <sys/mount.h>
#include <stdint.h>
#include <time.h>
#include <Python.h>
#include "fuse.h"
@ -32,7 +33,7 @@ static PyObject *getattr_cb=NULL, *readlink_cb=NULL, *getdir_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
*statfs_cb=NULL, *fsync_cb=NULL, *create_cb=NULL
;
static PyObject *Py_FuseError;
@ -59,6 +60,23 @@ OUT_DECREF: \
OUT: \
return ret;
#if FUSE_VERSION >= 22
static __inline PyObject *
fi_to_py(struct fuse_file_info *fi)
{
return (PyObject *)(uintptr_t)fi->fh;
}
#define PYO_CALLWITHFI(fi, fnc, fmt, ...) \
fi_to_py(fi) ? \
PyObject_CallFunction(fnc, #fmt "O", ## __VA_ARGS__, fi_to_py(fi)) : \
PyObject_CallFunction(fnc, #fmt, ## __VA_ARGS__)
#else
#define PYO_CALLWITHFI(fi, fnc, fmt, ...) \
PyObject_CallFunction(fnc, #fmt, ## __VA_ARGS__)
#endif /* FUSE_VERSION >= 22 */
#define fetchattr_nam(st, attr, aname) \
if (!(tmp = PyObject_GetAttrString(v, aname))) \
goto OUT_DECREF; \
@ -336,7 +354,7 @@ static int
read_func(const char *path, char *buf, size_t s, off_t off)
#endif
{
PyObject *v = PyObject_CallFunction(read_cb, "siK", path, s, off);
PyObject *v = PYO_CALLWITHFI(fi, read_cb, siK, path, s, off);
PROLOGUE
@ -359,7 +377,7 @@ static int
write_func(const char *path, const char *buf, size_t t, off_t off)
#endif
{
PyObject *v = PyObject_CallFunction(write_cb,"ss#K", path, buf, t, off);
PyObject *v = PYO_CALLWITHFI(fi, write_cb, ss#K, path, buf, t, off);
PROLOGUE
EPILOGUE
@ -370,22 +388,48 @@ static int
open_func(const char *path, struct fuse_file_info *fi)
{
PyObject *v = PyObject_CallFunction(open_cb, "si", path, fi->flags);
PROLOGUE
fi->fh = (uintptr_t) v;
return 0;
EPILOGUE
}
#else
static int
open_func(const char *path, int mode)
{
PyObject *v = PyObject_CallFunction(open_cb, "si", path, mode);
#endif
PROLOGUE
EPILOGUE
}
#endif
#if FUSE_VERSION >= 25
static int
create_func(const char *path, mode_t mode, struct fuse_file_info *fi)
{
PyObject *v = PyObject_CallFunction(create_cb, "si", path, fi->flags, mode);
PROLOGUE
fi->fh = (uintptr_t) v;
return 0;
EPILOGUE
}
#endif
#if FUSE_VERSION >= 22
static int
release_func(const char *path, struct fuse_file_info *fi)
{
PyObject *v = PyObject_CallFunction(release_cb, "si", path, fi->flags);
PyObject *v = fi_to_py(fi) ?
PyObject_CallFunction(release_cb, "siN", path, fi->flags,
fi_to_py(fi)) :
PyObject_CallFunction(release_cb, "si", path, fi->flags);
#else
static int
release_func(const char *path, int flags)
@ -436,14 +480,13 @@ statfs_func(const char *dummy, struct statfs *fst)
#if FUSE_VERSION >= 22
static int
fsync_func(const char *path, int datasync, struct fuse_file_info *fi)
{
PyObject *v = PyObject_CallFunction(fsync_cb, "si", path, datasync);
#else
static int
fsync_func(const char *path, int isfsyncfile)
{
PyObject *v = PyObject_CallFunction(fsync_cb, "si", path, isfsyncfile);
fsync_func(const char *path, int datasync)
#endif
{
PyObject *v = PYO_CALLWITHFI(fi, fsync_cb, si, path, datasync);
PROLOGUE
EPILOGUE
}
@ -511,16 +554,16 @@ Fuse_main(PyObject *self, PyObject *args, PyObject *kw)
"mkdir", "unlink", "rmdir", "symlink", "rename",
"link", "chmod", "chown", "truncate", "utime",
"open", "read", "write", "release", "statfs", "fsync",
"fuse_args", "multithreaded", NULL};
"create", "fuse_args", "multithreaded", NULL};
memset(&op, 0, sizeof(op));
if (!PyArg_ParseTupleAndKeywords(args, kw, "|OOOOOOOOOOOOOOOOOOOOOi",
if (!PyArg_ParseTupleAndKeywords(args, kw, "|OOOOOOOOOOOOOOOOOOOOOOi",
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,
&fargseq, &multithreaded))
&create_cb, &fargseq, &multithreaded))
return NULL;
#define DO_ONE_ATTR(name) \
@ -550,6 +593,9 @@ Fuse_main(PyObject *self, PyObject *args, PyObject *kw)
DO_ONE_ATTR(release);
DO_ONE_ATTR(statfs);
DO_ONE_ATTR(fsync);
#if FUSE_VERSION >= 25
DO_ONE_ATTR(create);
#endif
#undef DO_ONE_ATTR

38
fuse.py
View file

@ -456,7 +456,7 @@ class Fuse(object):
_attrs = ['getattr', 'readlink', 'getdir', 'mknod', 'mkdir',
'unlink', 'rmdir', 'symlink', 'rename', 'link', 'chmod',
'chown', 'truncate', 'utime', 'open', 'read', 'write', 'release',
'statfs', 'fsync']
'statfs', 'fsync', 'create']
fusage = "%prog [mountpoint] [options]"
@ -504,6 +504,19 @@ class Fuse(object):
d = {'multithreaded': self.multithreaded and 1 or 0}
d['fuse_args'] = args or self.fuse_args.assemble()
if hasattr(self, 'file_class'):
self.methproxy = {}
class mpx(object):
def __init__(self, name):
self.name = name
def __call__(self, *a):
return getattr(a[-1], self.name)(*(a[1:-1]))
for meth in 'read', 'write', 'fsync', 'release': #, 'flush', 'fgetattr', 'ftruncate'
if hasattr(self.file_class, meth):
self.methproxy[meth] = mpx(meth)
for a in self._attrs:
if hasattr(self,a):
c = ''
@ -511,21 +524,30 @@ class Fuse(object):
c = '_compat_0_1'
d[a] = ErrnoWrapper(getattr(self, a + c))
domount = True
if not args:
domount = self.fuse_args.do_mount()
try:
main(**d)
except FuseError:
if domount: raise
if args or self.fuse_args.do_mount():
raise
def __getattr__(self, meth):
if not hasattr(self, 'file_class'):
raise AttributeError
if meth in ('open', 'create'):
return self.file_class
if hasattr(self, 'methproxy') and self.methproxy.has_key(meth):
return self.methproxy[meth]
raise AttributeError
def GetContext(self):
return FuseGetContext(self)
def Invalidate(self, path):
return FuseInvalidate(self, path)
def fuseoptref(cls):
"""
@ -612,7 +634,7 @@ class Fuse(object):
def main_0_1_preamble(self):
cfargs = FuseArgs()
cfargs = FuseArgs()
cfargs.mountpoint = self.mountpoint

101
xmp.py
View file

@ -25,7 +25,15 @@ if not hasattr(fuse, '__version__'):
raise RuntimeError, \
"your fuse-py doesn't know of fuse.__version__, probably it's too old."
import thread
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 Xmp(Fuse):
root = '/'
@ -34,25 +42,21 @@ class Xmp(Fuse):
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
#import thread
#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"
# 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 os.lstat(self.root + path)
@ -89,11 +93,7 @@ class Xmp(Fuse):
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(self.root + path, "w")
else:
return -EINVAL
os.mknod(self.root + path, mode, dev)
def mkdir(self, path, mode):
return os.mkdir(self.root + path, mode)
@ -101,28 +101,6 @@ class Xmp(Fuse):
def utime(self, path, times):
return os.utime(self.root + path, times)
def open(self, path, flags):
#print "xmp.py:Xmp:open: %s" % path
os.close(os.open(self.root + path, flags))
return 0
def read(self, path, length, offset):
#print "xmp.py:Xmp:read: %s" % path
f = open(self.root + path, "r")
f.seek(offset)
return f.read(length)
def write(self, path, buf, off):
#print "xmp.py:Xmp:write: %s" % path
f = open(self.root + 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
def statfs(self):
"""
Should return an object with statvfs attributes (f_bsize, f_frsize...).
@ -144,9 +122,38 @@ class Xmp(Fuse):
return os.statvfs(self.root)
def fsync(self, path, isfsyncfile):
print "xmp.py:Xmp:fsync: path=%s, isfsyncfile=%s" % (self.root + path, isfsyncfile)
return 0
def main(self, *a, **kw):
server = self
class XmpFile:
def __init__(self, path, flags, *mode):
self.file = os.fdopen(os.open(server.root + path, flags, *mode),
flag2mode(flags))
def read(self, length, offset):
self.file.seek(offset)
return self.file.read(length)
def write(self, buf, offset):
self.file.seek(offset)
self.file.write(buf)
return len(buf)
def release(self, flags):
self.file.close()
def fsync(self, isfsyncfile):
if isfsyncfile and hasattr(os, 'fdatasync'):
os.fdatasync(self.file.fileno())
else:
os.fsync(self.file.fileno())
self.file_class = XmpFile
return Fuse.main(self, *a, **kw)
if __name__ == '__main__':
@ -169,5 +176,5 @@ Userspace nullfs-alike: mirror the filesystem tree from some point on.
except OSError:
print >> sys.stderr, "can't stat root of underlying filesystem"
sys.exit(1)
server.main()