mirror of https://github.com/perkeep/perkeep.git
651 lines
22 KiB
Python
651 lines
22 KiB
Python
# Copyright (c) 2008 Giorgos Verigakis <verigak@gmail.com>
|
|
#
|
|
# Permission to use, copy, modify, and distribute this software for any
|
|
# purpose with or without fee is hereby granted, provided that the above
|
|
# copyright notice and this permission notice appear in all copies.
|
|
#
|
|
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
# 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 stat import S_IFDIR
|
|
from traceback import print_exc
|
|
|
|
|
|
class c_timespec(Structure):
|
|
_fields_ = [('tv_sec', c_long), ('tv_nsec', c_long)]
|
|
|
|
class c_utimbuf(Structure):
|
|
_fields_ = [('actime', c_timespec), ('modtime', c_timespec)]
|
|
|
|
class c_stat(Structure):
|
|
pass # Platform dependent
|
|
|
|
_system = system()
|
|
if _system in ('Darwin', 'FreeBSD'):
|
|
_libiconv = CDLL(find_library("iconv"), RTLD_GLOBAL) # libfuse dependency
|
|
ENOTSUP = 45
|
|
c_dev_t = c_int32
|
|
c_fsblkcnt_t = c_ulong
|
|
c_fsfilcnt_t = c_ulong
|
|
c_gid_t = c_uint32
|
|
c_mode_t = c_uint16
|
|
c_off_t = c_int64
|
|
c_pid_t = c_int32
|
|
c_uid_t = c_uint32
|
|
setxattr_t = CFUNCTYPE(c_int, c_char_p, c_char_p, POINTER(c_byte),
|
|
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)]
|
|
elif _system == 'Linux':
|
|
ENOTSUP = 95
|
|
c_dev_t = c_ulonglong
|
|
c_fsblkcnt_t = c_ulonglong
|
|
c_fsfilcnt_t = c_ulonglong
|
|
c_gid_t = c_uint
|
|
c_mode_t = c_uint
|
|
c_off_t = c_longlong
|
|
c_pid_t = c_int
|
|
c_uid_t = c_uint
|
|
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),
|
|
('st_ino', c_ulong),
|
|
('st_nlink', c_ulong),
|
|
('st_mode', c_mode_t),
|
|
('st_uid', c_uid_t),
|
|
('st_gid', c_gid_t),
|
|
('__pad0', c_int),
|
|
('st_rdev', c_dev_t),
|
|
('st_size', c_off_t),
|
|
('st_blksize', c_long),
|
|
('st_blocks', c_long),
|
|
('st_atimespec', c_timespec),
|
|
('st_mtimespec', c_timespec),
|
|
('st_ctimespec', c_timespec)]
|
|
elif _machine == 'ppc':
|
|
c_stat._fields_ = [
|
|
('st_dev', c_dev_t),
|
|
('st_ino', c_ulonglong),
|
|
('st_mode', c_mode_t),
|
|
('st_nlink', c_uint),
|
|
('st_uid', c_uid_t),
|
|
('st_gid', c_gid_t),
|
|
('st_rdev', c_dev_t),
|
|
('__pad2', c_ushort),
|
|
('st_size', c_off_t),
|
|
('st_blksize', c_long),
|
|
('st_blocks', c_longlong),
|
|
('st_atimespec', c_timespec),
|
|
('st_mtimespec', c_timespec),
|
|
('st_ctimespec', c_timespec)]
|
|
else:
|
|
# i686, use as fallback for everything else
|
|
c_stat._fields_ = [
|
|
('st_dev', c_dev_t),
|
|
('__pad1', c_ushort),
|
|
('__st_ino', c_ulong),
|
|
('st_mode', c_mode_t),
|
|
('st_nlink', c_uint),
|
|
('st_uid', c_uid_t),
|
|
('st_gid', c_gid_t),
|
|
('st_rdev', c_dev_t),
|
|
('__pad2', c_ushort),
|
|
('st_size', c_off_t),
|
|
('st_blksize', c_long),
|
|
('st_blocks', c_longlong),
|
|
('st_atimespec', c_timespec),
|
|
('st_mtimespec', c_timespec),
|
|
('st_ctimespec', c_timespec),
|
|
('st_ino', c_ulonglong)]
|
|
else:
|
|
raise NotImplementedError('%s is not supported.' % _system)
|
|
|
|
|
|
class c_statvfs(Structure):
|
|
_fields_ = [
|
|
('f_bsize', c_ulong),
|
|
('f_frsize', c_ulong),
|
|
('f_blocks', c_fsblkcnt_t),
|
|
('f_bfree', c_fsblkcnt_t),
|
|
('f_bavail', c_fsblkcnt_t),
|
|
('f_files', c_fsfilcnt_t),
|
|
('f_ffree', c_fsfilcnt_t),
|
|
('f_favail', c_fsfilcnt_t)]
|
|
|
|
if _system == 'FreeBSD':
|
|
c_fsblkcnt_t = c_uint64
|
|
c_fsfilcnt_t = c_uint64
|
|
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)
|
|
class c_statvfs(Structure):
|
|
_fields_ = [
|
|
('f_bavail', c_fsblkcnt_t),
|
|
('f_bfree', c_fsblkcnt_t),
|
|
('f_blocks', c_fsblkcnt_t),
|
|
('f_favail', c_fsfilcnt_t),
|
|
('f_ffree', c_fsfilcnt_t),
|
|
('f_files', c_fsfilcnt_t),
|
|
('f_bsize', c_ulong),
|
|
('f_flag', c_ulong),
|
|
('f_frsize', c_ulong)]
|
|
|
|
class fuse_file_info(Structure):
|
|
_fields_ = [
|
|
('flags', c_int),
|
|
('fh_old', c_ulong),
|
|
('writepage', c_int),
|
|
('direct_io', c_uint, 1),
|
|
('keep_cache', c_uint, 1),
|
|
('flush', c_uint, 1),
|
|
('padding', c_uint, 29),
|
|
('fh', c_uint64),
|
|
('lock_owner', c_uint64)]
|
|
|
|
class fuse_context(Structure):
|
|
_fields_ = [
|
|
('fuse', c_voidp),
|
|
('uid', c_uid_t),
|
|
('gid', c_gid_t),
|
|
('pid', c_pid_t),
|
|
('private_data', c_voidp)]
|
|
|
|
class fuse_operations(Structure):
|
|
_fields_ = [
|
|
('getattr', CFUNCTYPE(c_int, c_char_p, POINTER(c_stat))),
|
|
('readlink', CFUNCTYPE(c_int, c_char_p, POINTER(c_byte), c_size_t)),
|
|
('getdir', c_voidp), # Deprecated, use readdir
|
|
('mknod', CFUNCTYPE(c_int, c_char_p, c_mode_t, c_dev_t)),
|
|
('mkdir', CFUNCTYPE(c_int, c_char_p, c_mode_t)),
|
|
('unlink', CFUNCTYPE(c_int, c_char_p)),
|
|
('rmdir', CFUNCTYPE(c_int, c_char_p)),
|
|
('symlink', CFUNCTYPE(c_int, c_char_p, c_char_p)),
|
|
('rename', CFUNCTYPE(c_int, c_char_p, c_char_p)),
|
|
('link', CFUNCTYPE(c_int, c_char_p, c_char_p)),
|
|
('chmod', CFUNCTYPE(c_int, c_char_p, c_mode_t)),
|
|
('chown', CFUNCTYPE(c_int, c_char_p, c_uid_t, c_gid_t)),
|
|
('truncate', CFUNCTYPE(c_int, c_char_p, c_off_t)),
|
|
('utime', c_voidp), # Deprecated, use utimens
|
|
('open', CFUNCTYPE(c_int, c_char_p, POINTER(fuse_file_info))),
|
|
('read', CFUNCTYPE(c_int, c_char_p, POINTER(c_byte), c_size_t, c_off_t,
|
|
POINTER(fuse_file_info))),
|
|
('write', CFUNCTYPE(c_int, c_char_p, POINTER(c_byte), c_size_t, c_off_t,
|
|
POINTER(fuse_file_info))),
|
|
('statfs', CFUNCTYPE(c_int, c_char_p, POINTER(c_statvfs))),
|
|
('flush', CFUNCTYPE(c_int, c_char_p, POINTER(fuse_file_info))),
|
|
('release', CFUNCTYPE(c_int, c_char_p, POINTER(fuse_file_info))),
|
|
('fsync', CFUNCTYPE(c_int, c_char_p, c_int, POINTER(fuse_file_info))),
|
|
('setxattr', setxattr_t),
|
|
('getxattr', getxattr_t),
|
|
('listxattr', CFUNCTYPE(c_int, c_char_p, POINTER(c_byte), c_size_t)),
|
|
('removexattr', CFUNCTYPE(c_int, c_char_p, c_char_p)),
|
|
('opendir', CFUNCTYPE(c_int, c_char_p, POINTER(fuse_file_info))),
|
|
('readdir', CFUNCTYPE(c_int, c_char_p, c_voidp, CFUNCTYPE(c_int, c_voidp,
|
|
c_char_p, POINTER(c_stat), c_off_t), c_off_t, POINTER(fuse_file_info))),
|
|
('releasedir', CFUNCTYPE(c_int, c_char_p, POINTER(fuse_file_info))),
|
|
('fsyncdir', CFUNCTYPE(c_int, c_char_p, c_int, POINTER(fuse_file_info))),
|
|
('init', CFUNCTYPE(c_voidp, c_voidp)),
|
|
('destroy', CFUNCTYPE(c_voidp, c_voidp)),
|
|
('access', CFUNCTYPE(c_int, c_char_p, c_int)),
|
|
('create', CFUNCTYPE(c_int, c_char_p, c_mode_t, POINTER(fuse_file_info))),
|
|
('ftruncate', CFUNCTYPE(c_int, c_char_p, c_off_t, POINTER(fuse_file_info))),
|
|
('fgetattr', CFUNCTYPE(c_int, c_char_p, POINTER(c_stat),
|
|
POINTER(fuse_file_info))),
|
|
('lock', CFUNCTYPE(c_int, c_char_p, POINTER(fuse_file_info), c_int, c_voidp)),
|
|
('utimens', CFUNCTYPE(c_int, c_char_p, POINTER(c_utimbuf))),
|
|
('bmap', CFUNCTYPE(c_int, c_char_p, c_size_t, POINTER(c_ulonglong)))]
|
|
|
|
|
|
def time_of_timespec(ts):
|
|
return ts.tv_sec + ts.tv_nsec / 10 ** 9
|
|
|
|
def set_st_attrs(st, attrs):
|
|
for key, val in attrs.items():
|
|
if key in ('st_atime', 'st_mtime', 'st_ctime'):
|
|
timespec = getattr(st, key + 'spec')
|
|
timespec.tv_sec = int(val)
|
|
timespec.tv_nsec = int((val - timespec.tv_sec) * 10 ** 9)
|
|
elif hasattr(st, key):
|
|
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()
|
|
ctx = ctxp.contents
|
|
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.
|
|
Assumes API version 2.6 or later."""
|
|
|
|
def __init__(self, operations, mountpoint, raw_fi=False, **kwargs):
|
|
"""Setting raw_fi to True will cause FUSE to pass the fuse_file_info
|
|
class as is to Operations, instead of just the fh field.
|
|
This gives you access to direct_io, keep_cache, etc."""
|
|
|
|
self.operations = operations
|
|
self.raw_fi = raw_fi
|
|
args = ['fuse']
|
|
if kwargs.pop('foreground', False):
|
|
args.append('-f')
|
|
if kwargs.pop('debug', False):
|
|
args.append('-d')
|
|
if kwargs.pop('nothreads', False):
|
|
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(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))
|
|
setattr(fuse_ops, name, prototype(op))
|
|
err = _libfuse.fuse_main_real(len(args), argv, pointer(fuse_ops),
|
|
sizeof(fuse_ops), None)
|
|
del self.operations # Invoke the destructor
|
|
if err:
|
|
raise RuntimeError(err)
|
|
|
|
def _wrapper_(self, func, *args, **kwargs):
|
|
"""Decorator for the methods that follow"""
|
|
try:
|
|
return func(*args, **kwargs) or 0
|
|
except OSError, e:
|
|
return -(e.errno or EFAULT)
|
|
except:
|
|
print_exc()
|
|
return -EFAULT
|
|
|
|
def getattr(self, path, buf):
|
|
return self.fgetattr(path, buf, None)
|
|
|
|
def readlink(self, path, buf, bufsize):
|
|
ret = self.operations('readlink', path)
|
|
data = create_string_buffer(ret[:bufsize - 1])
|
|
memmove(buf, data, len(data))
|
|
return 0
|
|
|
|
def mknod(self, path, mode, dev):
|
|
return self.operations('mknod', path, mode, dev)
|
|
|
|
def mkdir(self, path, mode):
|
|
return self.operations('mkdir', path, mode)
|
|
|
|
def unlink(self, path):
|
|
return self.operations('unlink', path)
|
|
|
|
def rmdir(self, path):
|
|
return self.operations('rmdir', path)
|
|
|
|
def symlink(self, source, target):
|
|
return self.operations('symlink', target, source)
|
|
|
|
def rename(self, old, new):
|
|
return self.operations('rename', old, new)
|
|
|
|
def link(self, source, target):
|
|
return self.operations('link', target, source)
|
|
|
|
def chmod(self, path, mode):
|
|
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):
|
|
return self.operations('truncate', path, length)
|
|
|
|
def open(self, path, fip):
|
|
fi = fip.contents
|
|
if self.raw_fi:
|
|
return self.operations('open', path, fi)
|
|
else:
|
|
fi.fh = self.operations('open', path, fi.flags)
|
|
return 0
|
|
|
|
def read(self, path, buf, size, offset, fip):
|
|
fh = fip.contents if self.raw_fi else fip.contents.fh
|
|
ret = self.operations('read', path, size, offset, fh)
|
|
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
|
|
return self.operations('write', path, data, offset, fh)
|
|
|
|
def statfs(self, path, buf):
|
|
stv = buf.contents
|
|
attrs = self.operations('statfs', path)
|
|
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
|
|
return self.operations('flush', path, fh)
|
|
|
|
def release(self, path, fip):
|
|
fh = fip.contents if self.raw_fi else 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
|
|
return self.operations('fsync', path, datasync, fh)
|
|
|
|
def setxattr(self, path, name, value, size, options, *args):
|
|
data = string_at(value, size)
|
|
return self.operations('setxattr', path, name, data, options, *args)
|
|
|
|
def getxattr(self, path, name, value, size, *args):
|
|
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
|
|
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 ''
|
|
bufsize = len(buf)
|
|
if bool(namebuf):
|
|
if bufsize > size:
|
|
return -ERANGE
|
|
memmove(namebuf, buf, bufsize)
|
|
return bufsize
|
|
|
|
def removexattr(self, path, name):
|
|
return self.operations('removexattr', path, name)
|
|
|
|
def opendir(self, path, fip):
|
|
# Ignore raw_fi
|
|
fip.contents.fh = self.operations('opendir', path)
|
|
return 0
|
|
|
|
def readdir(self, path, buf, filler, offset, fip):
|
|
# Ignore raw_fi
|
|
for item in self.operations('readdir', path, fip.contents.fh):
|
|
if isinstance(item, str):
|
|
name, st, offset = item, None, 0
|
|
else:
|
|
name, attrs, offset = item
|
|
if attrs:
|
|
st = c_stat()
|
|
set_st_attrs(st, attrs)
|
|
else:
|
|
st = None
|
|
if filler(buf, name, st, offset) != 0:
|
|
break
|
|
return 0
|
|
|
|
def releasedir(self, path, fip):
|
|
# Ignore raw_fi
|
|
return self.operations('releasedir', path, fip.contents.fh)
|
|
|
|
def fsyncdir(self, path, datasync, fip):
|
|
# Ignore raw_fi
|
|
return self.operations('fsyncdir', path, datasync, fip.contents.fh)
|
|
|
|
def init(self, conn):
|
|
return self.operations('init', '/')
|
|
|
|
def destroy(self, private_data):
|
|
return self.operations('destroy', '/')
|
|
|
|
def access(self, path, amode):
|
|
return self.operations('access', path, amode)
|
|
|
|
def create(self, path, mode, fip):
|
|
fi = fip.contents
|
|
if self.raw_fi:
|
|
return self.operations('create', path, mode, fi)
|
|
else:
|
|
fi.fh = self.operations('create', path, mode)
|
|
return 0
|
|
|
|
def ftruncate(self, path, length, fip):
|
|
fh = fip.contents if self.raw_fi else 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)
|
|
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
|
|
return self.operations('lock', path, fh, cmd, lock)
|
|
|
|
def utimens(self, path, buf):
|
|
if buf:
|
|
atime = time_of_timespec(buf.contents.actime)
|
|
mtime = time_of_timespec(buf.contents.modtime)
|
|
times = (atime, mtime)
|
|
else:
|
|
times = None
|
|
return self.operations('utimens', path, times)
|
|
|
|
def bmap(self, path, blocksize, idx):
|
|
return self.operations('bmap', path, blocksize, idx)
|
|
|
|
|
|
class Operations(object):
|
|
"""This class should be subclassed and passed as an argument to FUSE on
|
|
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 FuseOSError(EFAULT)
|
|
return getattr(self, op)(*args)
|
|
|
|
def access(self, path, amode):
|
|
return 0
|
|
|
|
bmap = None
|
|
|
|
def chmod(self, path, mode):
|
|
raise FuseOSError(EROFS)
|
|
|
|
def chown(self, path, uid, gid):
|
|
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 FuseOSError(EROFS)
|
|
|
|
def destroy(self, path):
|
|
"""Called on filesystem destruction. Path is always /"""
|
|
pass
|
|
|
|
def flush(self, path, fh):
|
|
return 0
|
|
|
|
def fsync(self, path, datasync, fh):
|
|
return 0
|
|
|
|
def fsyncdir(self, path, datasync, fh):
|
|
return 0
|
|
|
|
def getattr(self, path, fh=None):
|
|
"""Returns a dictionary with keys identical to the stat C structure
|
|
of stat(2).
|
|
st_atime, st_mtime and st_ctime should be floats.
|
|
NOTE: There is an incombatibility between Linux and Mac OS X concerning
|
|
st_nlink of directories. Mac OS X counts all files inside the directory,
|
|
while Linux counts only the subdirectories."""
|
|
|
|
if path != '/':
|
|
raise FuseOSError(ENOENT)
|
|
return dict(st_mode=(S_IFDIR | 0755), st_nlink=2)
|
|
|
|
def getxattr(self, path, name, position=0):
|
|
raise FuseOSError(ENOTSUP)
|
|
|
|
def init(self, path):
|
|
"""Called on filesystem initialization. Path is always /
|
|
Use it instead of __init__ if you start threads on initialization."""
|
|
pass
|
|
|
|
def link(self, target, source):
|
|
raise FuseOSError(EROFS)
|
|
|
|
def listxattr(self, path):
|
|
return []
|
|
|
|
lock = None
|
|
|
|
def mkdir(self, path, mode):
|
|
raise FuseOSError(EROFS)
|
|
|
|
def mknod(self, path, mode, dev):
|
|
raise FuseOSError(EROFS)
|
|
|
|
def open(self, path, flags):
|
|
"""When raw_fi is False (default case), open should return a numerical
|
|
file handle.
|
|
When raw_fi is True the signature of open becomes:
|
|
open(self, path, fi)
|
|
and the file handle should be set directly."""
|
|
return 0
|
|
|
|
def opendir(self, path):
|
|
"""Returns a numerical file handle."""
|
|
return 0
|
|
|
|
def read(self, path, size, offset, fh):
|
|
"""Returns a string containing the data requested."""
|
|
raise FuseOSError(EIO)
|
|
|
|
def readdir(self, path, fh):
|
|
"""Can return either a list of names, or a list of (name, attrs, offset)
|
|
tuples. attrs is a dict as in getattr."""
|
|
return ['.', '..']
|
|
|
|
def readlink(self, path):
|
|
raise FuseOSError(ENOENT)
|
|
|
|
def release(self, path, fh):
|
|
return 0
|
|
|
|
def releasedir(self, path, fh):
|
|
return 0
|
|
|
|
def removexattr(self, path, name):
|
|
raise FuseOSError(ENOTSUP)
|
|
|
|
def rename(self, old, new):
|
|
raise FuseOSError(EROFS)
|
|
|
|
def rmdir(self, path):
|
|
raise FuseOSError(EROFS)
|
|
|
|
def setxattr(self, path, name, value, options, position=0):
|
|
raise FuseOSError(ENOTSUP)
|
|
|
|
def statfs(self, path):
|
|
"""Returns a dictionary with keys identical to the statvfs C structure
|
|
of statvfs(3).
|
|
On Mac OS X f_bsize and f_frsize must be a power of 2 (minimum 512)."""
|
|
return {}
|
|
|
|
def symlink(self, target, source):
|
|
raise FuseOSError(EROFS)
|
|
|
|
def truncate(self, path, length, fh=None):
|
|
raise FuseOSError(EROFS)
|
|
|
|
def unlink(self, path):
|
|
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 FuseOSError(EROFS)
|
|
|
|
|
|
class LoggingMixIn:
|
|
def __call__(self, op, path, *args):
|
|
print '->', op, path, repr(args)
|
|
ret = '[Unhandled Exception]'
|
|
try:
|
|
ret = getattr(self, op)(path, *args)
|
|
return ret
|
|
except OSError, e:
|
|
ret = str(e)
|
|
raise
|
|
finally:
|
|
print '<-', op, repr(ret)
|