diff options
author | Terence Honles <terence@honles.com> | 2012-04-24 01:00:33 -0700 |
---|---|---|
committer | Terence Honles <terence@honles.com> | 2012-04-24 11:25:44 -0700 |
commit | 5954f3035331a43d41bcaab276d70af5b14c992e (patch) | |
tree | 1484ed3c5e94303b923ee7ad5be811cd5e052bca | |
parent | cae6da00074e4a01aaed73b727ce7e7b8d93e593 (diff) | |
download | fusepy-5954f3035331a43d41bcaab276d70af5b14c992e.tar.gz |
bringing fuse2x.py and fuse3x.py together
-rw-r--r-- | fuse/fuse2x.py | 13 | ||||
-rw-r--r-- | fuse/fuse3x.py | 262 |
2 files changed, 201 insertions, 74 deletions
diff --git a/fuse/fuse2x.py b/fuse/fuse2x.py index b3a964a..a590966 100644 --- a/fuse/fuse2x.py +++ b/fuse/fuse2x.py @@ -23,6 +23,8 @@ from signal import signal, SIGINT, SIG_DFL from stat import S_IFDIR from traceback import print_exc +import logging + try: from functools import partial except ImportError: @@ -333,7 +335,7 @@ class FUSE(object): fuse_ops = fuse_operations() for name, prototype in fuse_operations._fields_: if prototype != c_voidp and getattr(operations, name, None): - op = partial(self._wrapper_, getattr(self, name)) + op = partial(self._wrapper, getattr(self, name)) setattr(fuse_ops, name, prototype(op)) old_handler = signal(SIGINT, SIG_DFL) @@ -355,7 +357,8 @@ class FUSE(object): else: yield '%s=%s' % (key, value) - def _wrapper_(self, func, *args, **kwargs): + @staticmethod + def _wrapper(func, *args, **kwargs): """Decorator for the methods that follow""" try: return func(*args, **kwargs) or 0 @@ -743,8 +746,10 @@ class Operations(object): class LoggingMixIn: + log = logging.getLogger('fuse.log-mixin') + def __call__(self, op, path, *args): - print '->', op, path, repr(args) + self.log.debug('-> %s %s %s', op, path, repr(args)) ret = '[Unhandled Exception]' try: ret = getattr(self, op)(path, *args) @@ -753,4 +758,4 @@ class LoggingMixIn: ret = str(e) raise finally: - print '<-', op, repr(ret) + self.log.debug('<- %s %s', op, repr(ret)) diff --git a/fuse/fuse3x.py b/fuse/fuse3x.py index bcb9c4c..fb98341 100644 --- a/fuse/fuse3x.py +++ b/fuse/fuse3x.py @@ -12,16 +12,33 @@ # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +from __future__ import division + from ctypes import * from ctypes.util import find_library from errno import * -from functools import partial +from os import strerror from platform import machine, system +from signal import signal, SIGINT, SIG_DFL from stat import S_IFDIR from traceback import print_exc import logging +try: + from functools import partial +except ImportError: + # http://docs.python.org/library/functools.html#functools.partial + def partial(func, *args, **keywords): + def newfunc(*fargs, **fkeywords): + newkeywords = keywords.copy() + newkeywords.update(fkeywords) + return func(*(args + fargs), **newkeywords) + + newfunc.func = func + newfunc.args = args + newfunc.keywords = keywords + return newfunc class c_timespec(Structure): _fields_ = [('tv_sec', c_long), ('tv_nsec', c_long)] @@ -33,8 +50,25 @@ class c_stat(Structure): pass # Platform dependent _system = system() -if _system in ('Darwin', 'FreeBSD'): - _libiconv = CDLL(find_library("iconv"), RTLD_GLOBAL) # libfuse dependency +_machine = machine() + +if _system == 'Darwin': + _libiconv = CDLL(find_library('iconv'), RTLD_GLOBAL) # libfuse dependency + _libfuse_path = (find_library('fuse4x') or find_library('osxfuse') or + find_library('fuse')) +else: + _libfuse_path = find_library('fuse') + +if not _libfuse_path: + raise EnvironmentError('Unable to find libfuse') +else: + _libfuse = CDLL(_libfuse_path) + +if _system == 'Darwin' and hasattr(_libfuse, 'macfuse_version'): + _system = 'Darwin-MacFuse' + + +if _system in ('Darwin', 'Darwin-MacFuse', 'FreeBSD'): ENOTSUP = 45 c_dev_t = c_int32 c_fsblkcnt_t = c_ulong @@ -48,20 +82,41 @@ if _system in ('Darwin', 'FreeBSD'): c_size_t, c_int, c_uint32) getxattr_t = CFUNCTYPE(c_int, c_char_p, c_char_p, POINTER(c_byte), c_size_t, c_uint32) - c_stat._fields_ = [ - ('st_dev', c_dev_t), - ('st_ino', c_uint32), - ('st_mode', c_mode_t), - ('st_nlink', c_uint16), - ('st_uid', c_uid_t), - ('st_gid', c_gid_t), - ('st_rdev', c_dev_t), - ('st_atimespec', c_timespec), - ('st_mtimespec', c_timespec), - ('st_ctimespec', c_timespec), - ('st_size', c_off_t), - ('st_blocks', c_int64), - ('st_blksize', c_int32)] + if _system == 'Darwin': + c_stat._fields_ = [ + ('st_dev', c_dev_t), + ('st_mode', c_mode_t), + ('st_nlink', c_uint16), + ('st_ino', c_uint64), + ('st_uid', c_uid_t), + ('st_gid', c_gid_t), + ('st_rdev', c_dev_t), + ('st_atimespec', c_timespec), + ('st_mtimespec', c_timespec), + ('st_ctimespec', c_timespec), + ('st_birthtimespec', c_timespec), + ('st_size', c_off_t), + ('st_blocks', c_int64), + ('st_blksize', c_int32), + ('st_flags', c_int32), + ('st_gen', c_int32), + ('st_lspare', c_int32), + ('st_qspare', c_int64)] + else: + c_stat._fields_ = [ + ('st_dev', c_dev_t), + ('st_ino', c_uint32), + ('st_mode', c_mode_t), + ('st_nlink', c_uint16), + ('st_uid', c_uid_t), + ('st_gid', c_gid_t), + ('st_rdev', c_dev_t), + ('st_atimespec', c_timespec), + ('st_mtimespec', c_timespec), + ('st_ctimespec', c_timespec), + ('st_size', c_off_t), + ('st_blocks', c_int64), + ('st_blksize', c_int32)] elif _system == 'Linux': ENOTSUP = 95 c_dev_t = c_ulonglong @@ -75,7 +130,6 @@ elif _system == 'Linux': setxattr_t = CFUNCTYPE(c_int, c_char_p, c_char_p, POINTER(c_byte), c_size_t, c_int) getxattr_t = CFUNCTYPE(c_int, c_char_p, c_char_p, POINTER(c_byte), c_size_t) - _machine = machine() if _machine == 'x86_64': c_stat._fields_ = [ ('st_dev', c_dev_t), @@ -179,6 +233,9 @@ class fuse_context(Structure): ('pid', c_pid_t), ('private_data', c_voidp)] +_libfuse.fuse_get_context.restype = POINTER(fuse_context) + + class fuse_operations(Structure): _fields_ = [ ('getattr', CFUNCTYPE(c_int, c_char_p, POINTER(c_stat))), @@ -238,13 +295,6 @@ def set_st_attrs(st, attrs): setattr(st, key, val) -_libfuse_path = find_library('fuse') -if not _libfuse_path: - raise EnvironmentError('Unable to find libfuse') -_libfuse = CDLL(_libfuse_path) -_libfuse.fuse_get_context.restype = POINTER(fuse_context) - - def fuse_get_context(): """Returns a (uid, gid, pid) tuple""" ctxp = _libfuse.fuse_get_context() @@ -252,6 +302,11 @@ def fuse_get_context(): return ctx.uid, ctx.gid, ctx.pid +class FuseOSError(OSError): + def __init__(self, errno): + super(FuseOSError, self).__init__(errno, strerror(errno)) + + class FUSE(object): """This class is the lower level interface and should not be subclassed under normal use. Its methods are called by fuse. @@ -273,21 +328,37 @@ class FUSE(object): args.append('-s') kwargs.setdefault('fsname', operations.__class__.__name__) args.append('-o') - args.append(','.join(key if val == True else '%s=%s' % (key, val) - for key, val in kwargs.items())) + args.append(','.join(self._normalize_fuse_options(**kwargs))) args.append(mountpoint) argv = (c_char_p * len(args))(*args) fuse_ops = fuse_operations() for name, prototype in fuse_operations._fields_: if prototype != c_voidp and getattr(operations, name, None): - op = partial(self._wrapper_, getattr(self, name)) + op = partial(self._wrapper, getattr(self, name)) setattr(fuse_ops, name, prototype(op)) - _libfuse.fuse_main_real(len(args), argv, pointer(fuse_ops), - sizeof(fuse_ops), None) + + old_handler = signal(SIGINT, SIG_DFL) + + err = _libfuse.fuse_main_real(len(args), argv, pointer(fuse_ops), + sizeof(fuse_ops), None) + + signal(SIGINT, old_handler) + del self.operations # Invoke the destructor + if err: + raise RuntimeError(err) + + @staticmethod + def _normalize_fuse_options(**kargs): + for key, value in kargs.items(): + if isinstance(value, bool): + if value is True: yield key + else: + yield '%s=%s' % (key, value) - def _wrapper_(self, func, *args, **kwargs): + @staticmethod + def _wrapper(func, *args, **kwargs): """Decorator for the methods that follow""" try: return func(*args, **kwargs) or 0 @@ -331,6 +402,11 @@ class FUSE(object): return self.operations('chmod', path, mode) def chown(self, path, uid, gid): + # Check if any of the arguments is a -1 that has overflowed + if c_uid_t(uid + 1).value == 0: + uid = -1 + if c_gid_t(gid + 1).value == 0: + gid = -1 return self.operations('chown', path, uid, gid) def truncate(self, path, length): @@ -345,17 +421,26 @@ class FUSE(object): return 0 def read(self, path, buf, size, offset, fip): - fh = fip.contents if self.raw_fi else fip.contents.fh + if self.raw_fi: + fh = fip.contents + else: + fh = fip.contents.fh + ret = self.operations('read', path, size, offset, fh) - if not ret: - return 0 + if not ret: return 0 + data = create_string_buffer(ret[:size], size) memmove(buf, data, size) return size def write(self, path, buf, size, offset, fip): data = string_at(buf, size) - fh = fip.contents if self.raw_fi else fip.contents.fh + + if self.raw_fi: + fh = fip.contents + else: + fh = fip.contents.fh + return self.operations('write', path, data, offset, fh) def statfs(self, path, buf): @@ -364,18 +449,31 @@ class FUSE(object): for key, val in attrs.items(): if hasattr(stv, key): setattr(stv, key, val) + return 0 def flush(self, path, fip): - fh = fip.contents if self.raw_fi else fip.contents.fh + if self.raw_fi: + fh = fip.contents + else: + fh = fip.contents.fh + return self.operations('flush', path, fh) def release(self, path, fip): - fh = fip.contents if self.raw_fi else fip.contents.fh + if self.raw_fi: + fh = fip.contents + else: + fh = fip.contents.fh + return self.operations('release', path, fh) def fsync(self, path, datasync, fip): - fh = fip.contents if self.raw_fi else fip.contents.fh + if self.raw_fi: + fh = fip.contents + else: + fh = fip.contents.fh + return self.operations('fsync', path, datasync, fh) def setxattr(self, path, name, value, size, options, *args): @@ -386,20 +484,28 @@ class FUSE(object): ret = self.operations('getxattr', path, name, *args) retsize = len(ret) buf = create_string_buffer(ret, retsize) # Does not add trailing 0 - if bool(value): - if retsize > size: - return -ERANGE + + if value: + if retsize > size: return -ERANGE + memmove(value, buf, retsize) + return retsize def listxattr(self, path, namebuf, size): ret = self.operations('listxattr', path) - buf = create_string_buffer('\x00'.join(ret)) if ret else '' + + if ret: + buf = create_string_buffer('\x00'.join(ret)) + else: + buf = '' + bufsize = len(buf) - if bool(namebuf): - if bufsize > size: - return -ERANGE + if namebuf: + if bufsize > size: return -ERANGE + memmove(namebuf, buf, bufsize) + return bufsize def removexattr(self, path, name): @@ -452,19 +558,34 @@ class FUSE(object): return 0 def ftruncate(self, path, length, fip): - fh = fip.contents if self.raw_fi else fip.contents.fh + if self.raw_fi: + fh = fip.contents + else: + fh = fip.contents.fh + return self.operations('truncate', path, length, fh) def fgetattr(self, path, buf, fip): memset(buf, 0, sizeof(c_stat)) + st = buf.contents - fh = fip and (fip.contents if self.raw_fi else fip.contents.fh) + if not fip: + fh = fip + elif self.raw_fi: + fh = fip.contents + else: + fh = fip.contents.fh + attrs = self.operations('getattr', path, fh) set_st_attrs(st, attrs) return 0 def lock(self, path, fip, cmd, lock): - fh = fip.contents if self.raw_fi else fip.contents.fh + if self.raw_fi: + fh = fip.contents + else: + fh = fip.contents.fh + return self.operations('lock', path, fh, cmd, lock) def utimens(self, path, buf): @@ -474,6 +595,7 @@ class FUSE(object): times = (atime, mtime) else: times = None + return self.operations('utimens', path, times) def bmap(self, path, blocksize, idx): @@ -482,15 +604,15 @@ class FUSE(object): class Operations(object): """This class should be subclassed and passed as an argument to FUSE on - initialization. All operations should raise an OSError exception on - error. + initialization. All operations should raise a FuseOSError exception + on error. When in doubt of what an operation should do, check the FUSE header file or the corresponding system call man page.""" def __call__(self, op, *args): if not hasattr(self, op): - raise OSError(EFAULT, '') + raise FuseOSError(EFAULT) return getattr(self, op)(*args) def access(self, path, amode): @@ -499,17 +621,17 @@ class Operations(object): bmap = None def chmod(self, path, mode): - raise OSError(EROFS, '') + raise FuseOSError(EROFS) def chown(self, path, uid, gid): - raise OSError(EROFS, '') + raise FuseOSError(EROFS) def create(self, path, mode, fi=None): """When raw_fi is False (default case), fi is None and create should return a numerical file handle. When raw_fi is True the file handle should be set directly by create and return 0.""" - raise OSError(EROFS, '') + raise FuseOSError(EROFS) def destroy(self, path): """Called on filesystem destruction. Path is always /""" @@ -533,11 +655,11 @@ class Operations(object): while Linux counts only the subdirectories.""" if path != '/': - raise OSError(ENOENT, '') + raise FuseOSError(ENOENT) return dict(st_mode=(S_IFDIR | 0o755), st_nlink=2) def getxattr(self, path, name, position=0): - raise OSError(ENOTSUP, '') + raise FuseOSError(ENOTSUP) def init(self, path): """Called on filesystem initialization. Path is always / @@ -545,7 +667,7 @@ class Operations(object): pass def link(self, target, source): - raise OSError(EROFS, '') + raise FuseOSError(EROFS) def listxattr(self, path): return [] @@ -553,10 +675,10 @@ class Operations(object): lock = None def mkdir(self, path, mode): - raise OSError(EROFS, '') + raise FuseOSError(EROFS) def mknod(self, path, mode, dev): - raise OSError(EROFS, '') + raise FuseOSError(EROFS) def open(self, path, flags): """When raw_fi is False (default case), open should return a numerical @@ -572,7 +694,7 @@ class Operations(object): def read(self, path, size, offset, fh): """Returns a string containing the data requested.""" - raise OSError(ENOENT, '') + raise FuseOSError(EIO) def readdir(self, path, fh): """Can return either a list of names, or a list of (name, attrs, offset) @@ -580,7 +702,7 @@ class Operations(object): return ['.', '..'] def readlink(self, path): - raise OSError(ENOENT, '') + raise FuseOSError(ENOENT) def release(self, path, fh): return 0 @@ -589,16 +711,16 @@ class Operations(object): return 0 def removexattr(self, path, name): - raise OSError(ENOTSUP, '') + raise FuseOSError(ENOTSUP) def rename(self, old, new): - raise OSError(EROFS, '') + raise FuseOSError(EROFS) def rmdir(self, path): - raise OSError(EROFS, '') + raise FuseOSError(EROFS) def setxattr(self, path, name, value, options, position=0): - raise OSError(ENOTSUP, '') + raise FuseOSError(ENOTSUP) def statfs(self, path): """Returns a dictionary with keys identical to the statvfs C structure @@ -607,26 +729,26 @@ class Operations(object): return {} def symlink(self, target, source): - raise OSError(EROFS, '') + raise FuseOSError(EROFS) def truncate(self, path, length, fh=None): - raise OSError(EROFS, '') + raise FuseOSError(EROFS) def unlink(self, path): - raise OSError(EROFS, '') + raise FuseOSError(EROFS) def utimens(self, path, times=None): """Times is a (atime, mtime) tuple. If None use current time.""" return 0 def write(self, path, data, offset, fh): - raise OSError(EROFS, '') + raise FuseOSError(EROFS) class LoggingMixIn: def __call__(self, op, path, *args): logging.debug('-> %s %s %s', op, path, repr(args)) - ret = '[Unknown Error]' + ret = '[Unhandled Exception]' try: ret = getattr(self, op)(path, *args) return ret |