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:
parent
4fad6a4f0e
commit
2bcf2878f2
5 changed files with 163 additions and 68 deletions
11
ChangeLog
11
ChangeLog
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
-----------------------
|
||||
|
||||
|
|
|
|||
|
|
@ -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
38
fuse.py
|
|
@ -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
101
xmp.py
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Reference in a new issue