mirror of https://github.com/perkeep/perkeep.git
619 lines
19 KiB
Python
619 lines
19 KiB
Python
# Copyright (c) 2010 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, wraps
|
|
from inspect import getmembers, ismethod
|
|
from platform import machine, system
|
|
from stat import S_IFDIR, S_IFREG
|
|
|
|
|
|
_system = system()
|
|
_machine = machine()
|
|
|
|
class LibFUSE(CDLL):
|
|
def __init__(self):
|
|
if _system == 'Darwin':
|
|
self.libiconv = CDLL(find_library('iconv'), RTLD_GLOBAL)
|
|
super(LibFUSE, self).__init__(find_library('fuse'))
|
|
|
|
self.fuse_mount.argtypes = (c_char_p, POINTER(fuse_args))
|
|
self.fuse_mount.restype = c_void_p
|
|
self.fuse_lowlevel_new.argtypes = (POINTER(fuse_args), POINTER(fuse_lowlevel_ops),
|
|
c_size_t, c_void_p)
|
|
self.fuse_lowlevel_new.restype = c_void_p
|
|
self.fuse_set_signal_handlers.argtypes = (c_void_p,)
|
|
self.fuse_session_add_chan.argtypes = (c_void_p, c_void_p)
|
|
self.fuse_session_loop.argtypes = (c_void_p,)
|
|
self.fuse_remove_signal_handlers.argtypes = (c_void_p,)
|
|
self.fuse_session_remove_chan.argtypes = (c_void_p,)
|
|
self.fuse_session_destroy.argtypes = (c_void_p,)
|
|
self.fuse_unmount.argtypes = (c_char_p, c_void_p)
|
|
|
|
self.fuse_req_ctx.restype = POINTER(fuse_ctx)
|
|
self.fuse_req_ctx.argtypes = (fuse_req_t,)
|
|
|
|
self.fuse_reply_err.argtypes = (fuse_req_t, c_int)
|
|
self.fuse_reply_attr.argtypes = (fuse_req_t, c_void_p, c_double)
|
|
self.fuse_reply_entry.argtypes = (fuse_req_t, c_void_p)
|
|
self.fuse_reply_open.argtypes = (fuse_req_t, c_void_p)
|
|
self.fuse_reply_buf.argtypes = (fuse_req_t, c_char_p, c_size_t)
|
|
self.fuse_reply_write.argtypes = (fuse_req_t, c_size_t)
|
|
|
|
self.fuse_add_direntry.argtypes = (c_void_p, c_char_p, c_size_t, c_char_p,
|
|
c_stat_p, c_off_t)
|
|
|
|
class fuse_args(Structure):
|
|
_fields_ = [('argc', c_int), ('argv', POINTER(c_char_p)), ('allocated', c_int)]
|
|
|
|
class c_timespec(Structure):
|
|
_fields_ = [('tv_sec', c_long), ('tv_nsec', c_long)]
|
|
|
|
class c_stat(Structure):
|
|
pass # Platform dependent
|
|
|
|
if _system == 'Darwin':
|
|
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
|
|
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
|
|
|
|
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)]
|
|
|
|
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_ctx(Structure):
|
|
_fields_ = [('uid', c_uid_t), ('gid', c_gid_t), ('pid', c_pid_t)]
|
|
|
|
fuse_ino_t = c_ulong
|
|
fuse_req_t = c_void_p
|
|
c_stat_p = POINTER(c_stat)
|
|
fuse_file_info_p = POINTER(fuse_file_info)
|
|
|
|
FUSE_SET_ATTR = ('st_mode', 'st_uid', 'st_gid', 'st_size', 'st_atime', 'st_mtime')
|
|
|
|
class fuse_entry_param(Structure):
|
|
_fields_ = [
|
|
('ino', fuse_ino_t),
|
|
('generation', c_ulong),
|
|
('attr', c_stat),
|
|
('attr_timeout', c_double),
|
|
('entry_timeout', c_double)]
|
|
|
|
class fuse_lowlevel_ops(Structure):
|
|
_fields_ = [
|
|
('init', CFUNCTYPE(None, c_void_p, c_void_p)),
|
|
('destroy', CFUNCTYPE(None, c_void_p)),
|
|
('lookup', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, c_char_p)),
|
|
('forget', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, c_ulong)),
|
|
('getattr', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, fuse_file_info_p)),
|
|
('setattr', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, c_stat_p, c_int, fuse_file_info_p)),
|
|
('readlink', CFUNCTYPE(None, fuse_req_t, fuse_ino_t)),
|
|
('mknod', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, c_char_p, c_mode_t, c_dev_t)),
|
|
('mkdir', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, c_char_p, c_mode_t)),
|
|
('unlink', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, c_char_p)),
|
|
('rmdir', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, c_char_p)),
|
|
('symlink', CFUNCTYPE(None, fuse_req_t, c_char_p, fuse_ino_t, c_char_p)),
|
|
('rename', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, c_char_p, fuse_ino_t, c_char_p)),
|
|
('link', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, fuse_ino_t, c_char_p)),
|
|
('open', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, fuse_file_info_p)),
|
|
('read', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, c_size_t, c_off_t, fuse_file_info_p)),
|
|
('write', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, c_char_p, c_size_t, c_off_t,
|
|
fuse_file_info_p)),
|
|
('flush', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, fuse_file_info_p)),
|
|
('release', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, fuse_file_info_p)),
|
|
('fsync', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, c_int, fuse_file_info_p)),
|
|
('opendir', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, fuse_file_info_p)),
|
|
('readdir', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, c_size_t, c_off_t, fuse_file_info_p)),
|
|
('releasedir', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, fuse_file_info_p)),
|
|
('fsyncdir', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, c_int, fuse_file_info_p))]
|
|
|
|
|
|
def struct_to_dict(p):
|
|
try:
|
|
x = p.contents
|
|
return dict((key, getattr(x, key)) for key, type in x._fields_)
|
|
except ValueError:
|
|
return {}
|
|
|
|
def stat_to_dict(p):
|
|
try:
|
|
d = {}
|
|
x = p.contents
|
|
for key, type in x._fields_:
|
|
if key in ('st_atimespec', 'st_mtimespec', 'st_ctimespec'):
|
|
ts = getattr(x, key)
|
|
key = key[:-4] # Lose the "spec"
|
|
d[key] = ts.tv_sec + ts.tv_nsec / 10 ** 9
|
|
else:
|
|
d[key] = getattr(x, key)
|
|
return d
|
|
except ValueError:
|
|
return {}
|
|
|
|
def dict_to_stat(d):
|
|
for key in ('st_atime', 'st_mtime', 'st_ctime'):
|
|
if key in d:
|
|
val = d[key]
|
|
sec = int(val)
|
|
nsec = int((val - sec) * 10 ** 9)
|
|
d[key + 'spec'] = c_timespec(sec, nsec)
|
|
return c_stat(**d)
|
|
|
|
def setattr_mask_to_list(mask):
|
|
return [FUSE_SET_ATTR[i] for i in range(len(FUSE_SET_ATTR)) if mask & (1 << i)]
|
|
|
|
class FUSELL(object):
|
|
def __init__(self, mountpoint):
|
|
self.libfuse = LibFUSE()
|
|
|
|
fuse_ops = fuse_lowlevel_ops()
|
|
|
|
for name, prototype in fuse_lowlevel_ops._fields_:
|
|
method = getattr(self, 'fuse_' + name, None) or getattr(self, name, None)
|
|
if method:
|
|
setattr(fuse_ops, name, prototype(method))
|
|
|
|
args = ['fuse']
|
|
argv = fuse_args(len(args), (c_char_p * len(args))(*args), 0)
|
|
|
|
# TODO: handle initialization errors
|
|
|
|
chan = self.libfuse.fuse_mount(mountpoint, argv)
|
|
assert chan
|
|
|
|
session = self.libfuse.fuse_lowlevel_new(argv, byref(fuse_ops), sizeof(fuse_ops), None)
|
|
assert session
|
|
|
|
err = self.libfuse.fuse_set_signal_handlers(session)
|
|
assert err == 0
|
|
|
|
self.libfuse.fuse_session_add_chan(session, chan)
|
|
|
|
err = self.libfuse.fuse_session_loop(session)
|
|
assert err == 0
|
|
|
|
err = self.libfuse.fuse_remove_signal_handlers(session)
|
|
assert err == 0
|
|
|
|
self.libfuse.fuse_session_remove_chan(chan)
|
|
self.libfuse.fuse_session_destroy(session)
|
|
self.libfuse.fuse_unmount(mountpoint, chan)
|
|
|
|
def reply_err(self, req, err):
|
|
return self.libfuse.fuse_reply_err(req, err)
|
|
|
|
def reply_none(self, req):
|
|
self.libfuse.fuse_reply_none(req)
|
|
|
|
def reply_entry(self, req, entry):
|
|
entry['attr'] = c_stat(**entry['attr'])
|
|
e = fuse_entry_param(**entry)
|
|
self.libfuse.fuse_reply_entry(req, byref(e))
|
|
|
|
def reply_create(self, req, *args):
|
|
pass # XXX
|
|
|
|
def reply_attr(self, req, attr, attr_timeout):
|
|
st = dict_to_stat(attr)
|
|
return self.libfuse.fuse_reply_attr(req, byref(st), c_double(attr_timeout))
|
|
|
|
def reply_readlink(self, req, *args):
|
|
pass # XXX
|
|
|
|
def reply_open(self, req, d):
|
|
fi = fuse_file_info(**d)
|
|
return self.libfuse.fuse_reply_open(req, byref(fi))
|
|
|
|
def reply_write(self, req, count):
|
|
return self.libfuse.fuse_reply_write(req, count)
|
|
|
|
def reply_buf(self, req, buf):
|
|
return self.libfuse.fuse_reply_buf(req, buf, len(buf))
|
|
|
|
def reply_readdir(self, req, size, off, entries):
|
|
bufsize = 0
|
|
sized_entries = []
|
|
for name, attr in entries:
|
|
entsize = self.libfuse.fuse_add_direntry(req, None, 0, name, None, 0)
|
|
sized_entries.append((name, attr, entsize))
|
|
bufsize += entsize
|
|
|
|
next = 0
|
|
buf = create_string_buffer(bufsize)
|
|
for name, attr, entsize in sized_entries:
|
|
entbuf = cast(addressof(buf) + next, c_char_p)
|
|
st = c_stat(**attr)
|
|
next += entsize
|
|
self.libfuse.fuse_add_direntry(req, entbuf, entsize, name, byref(st), next)
|
|
|
|
if off < bufsize:
|
|
buf = cast(addressof(buf) + off, c_char_p) if off else buf
|
|
return self.libfuse.fuse_reply_buf(req, buf, min(bufsize - off, size))
|
|
else:
|
|
return self.libfuse.fuse_reply_buf(req, None, 0)
|
|
|
|
|
|
# If you override the following methods you should reply directly
|
|
# with the self.libfuse.fuse_reply_* methods.
|
|
|
|
def fuse_getattr(self, req, ino, fi):
|
|
self.getattr(req, ino, struct_to_dict(fi))
|
|
|
|
def fuse_setattr(self, req, ino, attr, to_set, fi):
|
|
attr_dict = stat_to_dict(attr)
|
|
to_set_list = setattr_mask_to_list(to_set)
|
|
fi_dict = struct_to_dict(fi)
|
|
self.setattr(req, ino, attr_dict, to_set_list, fi_dict)
|
|
|
|
def fuse_open(self, req, ino, fi):
|
|
self.open(req, ino, struct_to_dict(fi))
|
|
|
|
def fuse_read(self, req, ino, size, off, fi):
|
|
self.read(req, ino, size, off, fi)
|
|
|
|
def fuse_write(self, req, ino, buf, size, off, fi):
|
|
buf_str = string_at(buf, size)
|
|
fi_dict = struct_to_dict(fi)
|
|
self.write(req, ino, buf_str, off, fi_dict)
|
|
|
|
def fuse_flush(self, req, ino, fi):
|
|
self.flush(req, ino, struct_to_dict(fi))
|
|
|
|
def fuse_release(self, req, ino, fi):
|
|
self.release(req, ino, struct_to_dict(fi))
|
|
|
|
def fuse_fsync(self, req, ino, datasync, fi):
|
|
self.fsyncdir(req, ino, datasync, struct_to_dict(fi))
|
|
|
|
def fuse_opendir(self, req, ino, fi):
|
|
self.opendir(req, ino, struct_to_dict(fi))
|
|
|
|
def fuse_readdir(self, req, ino, size, off, fi):
|
|
self.readdir(req, ino, size, off, struct_to_dict(fi))
|
|
|
|
def fuse_releasedir(self, req, ino, fi):
|
|
self.releasedir(req, ino, struct_to_dict(fi))
|
|
|
|
def fuse_fsyncdir(self, req, ino, datasync, fi):
|
|
self.fsyncdir(req, ino, datasync, struct_to_dict(fi))
|
|
|
|
|
|
# Utility methods
|
|
|
|
def req_ctx(self, req):
|
|
ctx = self.libfuse.fuse_req_ctx(req)
|
|
return struct_to_dict(ctx)
|
|
|
|
|
|
# Methods to be overridden in subclasses.
|
|
# Reply with the self.reply_* methods.
|
|
|
|
def init(self, userdata, conn):
|
|
"""Initialize filesystem
|
|
|
|
There's no reply to this method
|
|
"""
|
|
pass
|
|
|
|
def destroy(self, userdata):
|
|
"""Clean up filesystem
|
|
|
|
There's no reply to this method
|
|
"""
|
|
pass
|
|
|
|
def lookup(self, req, parent, name):
|
|
"""Look up a directory entry by name and get its attributes.
|
|
|
|
Valid replies:
|
|
reply_entry
|
|
reply_err
|
|
"""
|
|
self.reply_err(req, ENOENT)
|
|
|
|
def forget(self, req, ino, nlookup):
|
|
"""Forget about an inode
|
|
|
|
Valid replies:
|
|
reply_none
|
|
"""
|
|
self.reply_none(req)
|
|
|
|
def getattr(self, req, ino, fi):
|
|
"""Get file attributes
|
|
|
|
Valid replies:
|
|
reply_attr
|
|
reply_err
|
|
"""
|
|
if ino == 1:
|
|
attr = {'st_ino': 1, 'st_mode': S_IFDIR | 0755, 'st_nlink': 2}
|
|
self.reply_attr(req, attr, 1.0)
|
|
else:
|
|
self.reply_err(req, ENOENT)
|
|
|
|
def setattr(self, req, ino, attr, to_set, fi):
|
|
"""Set file attributes
|
|
|
|
Valid replies:
|
|
reply_attr
|
|
reply_err
|
|
"""
|
|
self.reply_err(req, EROFS)
|
|
|
|
def readlink(self, req, ino):
|
|
"""Read symbolic link
|
|
|
|
Valid replies:
|
|
reply_readlink
|
|
reply_err
|
|
"""
|
|
self.reply_err(req, ENOENT)
|
|
|
|
def mknod(self, req, parent, name, mode, rdev):
|
|
"""Create file node
|
|
|
|
Valid replies:
|
|
reply_entry
|
|
reply_err
|
|
"""
|
|
self.reply_err(req, EROFS)
|
|
|
|
def mkdir(self, req, parent, name, mode):
|
|
"""Create a directory
|
|
|
|
Valid replies:
|
|
reply_entry
|
|
reply_err
|
|
"""
|
|
self.reply_err(req, EROFS)
|
|
|
|
def unlink(self, req, parent, name):
|
|
"""Remove a file
|
|
|
|
Valid replies:
|
|
reply_err
|
|
"""
|
|
self.reply_err(req, EROFS)
|
|
|
|
def rmdir(self, req, parent, name):
|
|
"""Remove a directory
|
|
|
|
Valid replies:
|
|
reply_err
|
|
"""
|
|
self.reply_err(req, EROFS)
|
|
|
|
def symlink(self, req, link, parent, name):
|
|
"""Create a symbolic link
|
|
|
|
Valid replies:
|
|
reply_entry
|
|
reply_err
|
|
"""
|
|
self.reply_err(req, EROFS)
|
|
|
|
def rename(self, req, parent, name, newparent, newname):
|
|
"""Rename a file
|
|
|
|
Valid replies:
|
|
reply_err
|
|
"""
|
|
self.reply_err(req, EROFS)
|
|
|
|
def link(self, req, ino, newparent, newname):
|
|
"""Create a hard link
|
|
|
|
Valid replies:
|
|
reply_entry
|
|
reply_err
|
|
"""
|
|
self.reply_err(req, EROFS)
|
|
|
|
def open(self, req, ino, fi):
|
|
"""Open a file
|
|
|
|
Valid replies:
|
|
reply_open
|
|
reply_err
|
|
"""
|
|
self.reply_open(req, fi)
|
|
|
|
def read(self, req, ino, size, off, fi):
|
|
"""Read data
|
|
|
|
Valid replies:
|
|
reply_buf
|
|
reply_err
|
|
"""
|
|
self.reply_err(req, EIO)
|
|
|
|
def write(self, req, ino, buf, off, fi):
|
|
"""Write data
|
|
|
|
Valid replies:
|
|
reply_write
|
|
reply_err
|
|
"""
|
|
self.reply_err(req, EROFS)
|
|
|
|
def flush(self, req, ino, fi):
|
|
"""Flush method
|
|
|
|
Valid replies:
|
|
reply_err
|
|
"""
|
|
self.reply_err(req, 0)
|
|
|
|
def release(self, req, ino, fi):
|
|
"""Release an open file
|
|
|
|
Valid replies:
|
|
reply_err
|
|
"""
|
|
self.reply_err(req, 0)
|
|
|
|
def fsync(self, req, ino, datasync, fi):
|
|
"""Synchronize file contents
|
|
|
|
Valid replies:
|
|
reply_err
|
|
"""
|
|
self.reply_err(req, 0)
|
|
|
|
def opendir(self, req, ino, fi):
|
|
"""Open a directory
|
|
|
|
Valid replies:
|
|
reply_open
|
|
reply_err
|
|
"""
|
|
self.reply_open(req, fi)
|
|
|
|
def readdir(self, req, ino, size, off, fi):
|
|
"""Read directory
|
|
|
|
Valid replies:
|
|
reply_readdir
|
|
reply_err
|
|
"""
|
|
if ino == 1:
|
|
attr = {'st_ino': 1, 'st_mode': S_IFDIR}
|
|
entries = [('.', attr), ('..', attr)]
|
|
self.reply_readdir(req, size, off, entries)
|
|
else:
|
|
self.reply_err(req, ENOENT)
|
|
|
|
def releasedir(self, req, ino, fi):
|
|
"""Release an open directory
|
|
|
|
Valid replies:
|
|
reply_err
|
|
"""
|
|
self.reply_err(req, 0)
|
|
|
|
def fsyncdir(self, req, ino, datasync, fi):
|
|
"""Synchronize directory contents
|
|
|
|
Valid replies:
|
|
reply_err
|
|
"""
|
|
self.reply_err(req, 0) |