tahoe-lafs/contrib/fuse/impl_b/pyfuse/kernel.py

406 lines
9.3 KiB
Python

from struct import pack, unpack, calcsize
import stat
class Struct(object):
__slots__ = []
def __init__(self, data=None, truncate=False, **fields):
if data is not None:
if truncate:
data = data[:self.calcsize()]
self.unpack(data)
for key, value in fields.items():
setattr(self, key, value)
def unpack(self, data):
data = unpack(self.__types__, data)
for key, value in zip(self.__slots__, data):
setattr(self, key, value)
def pack(self):
return pack(self.__types__, *[getattr(self, k, 0)
for k in self.__slots__])
def calcsize(cls):
return calcsize(cls.__types__)
calcsize = classmethod(calcsize)
def __repr__(self):
result = ['%s=%r' % (name, getattr(self, name, None))
for name in self.__slots__]
return '<%s %s>' % (self.__class__.__name__, ', '.join(result))
def from_param(cls, msg):
limit = cls.calcsize()
zero = msg.find('\x00', limit)
assert zero >= 0
return cls(msg[:limit]), msg[limit:zero]
from_param = classmethod(from_param)
def from_param2(cls, msg):
limit = cls.calcsize()
zero1 = msg.find('\x00', limit)
assert zero1 >= 0
zero2 = msg.find('\x00', zero1+1)
assert zero2 >= 0
return cls(msg[:limit]), msg[limit:zero1], msg[zero1+1:zero2]
from_param2 = classmethod(from_param2)
def from_head(cls, msg):
limit = cls.calcsize()
return cls(msg[:limit]), msg[limit:]
from_head = classmethod(from_head)
def from_param_head(cls, msg):
limit = cls.calcsize()
zero = msg.find('\x00', limit)
assert zero >= 0
return cls(msg[:limit]), msg[limit:zero], msg[zero+1:]
from_param_head = classmethod(from_param_head)
class StructWithAttr(Struct):
def unpack(self, data):
limit = -fuse_attr.calcsize()
super(StructWithAttr, self).unpack(data[:limit])
self.attr = fuse_attr(data[limit:])
def pack(self):
return super(StructWithAttr, self).pack() + self.attr.pack()
def calcsize(cls):
return super(StructWithAttr, cls).calcsize() + fuse_attr.calcsize()
calcsize = classmethod(calcsize)
def _mkstruct(name, c, base=Struct):
typ2code = {
'__u32': 'I',
'__s32': 'i',
'__u64': 'Q',
'__s64': 'q'}
slots = []
types = ['=']
for line in c.split('\n'):
line = line.strip()
if line:
line, tail = line.split(';', 1)
typ, nam = line.split()
slots.append(nam)
types.append(typ2code[typ])
cls = type(name, (base,), {'__slots__': slots,
'__types__': ''.join(types)})
globals()[name] = cls
class timeval(object):
def __init__(self, attr1, attr2):
self.sec = attr1
self.nsec = attr2
def __get__(self, obj, typ=None):
if obj is None:
return self
else:
return (getattr(obj, self.sec) +
getattr(obj, self.nsec) * 0.000000001)
def __set__(self, obj, val):
val = int(val * 1000000000)
sec, nsec = divmod(val, 1000000000)
setattr(obj, self.sec, sec)
setattr(obj, self.nsec, nsec)
def __delete__(self, obj):
delattr(obj, self.sec)
delattr(obj, self.nsec)
def _mktimeval(cls, attr1, attr2):
assert attr1.startswith('_')
assert attr2.startswith('_')
tv = timeval(attr1, attr2)
setattr(cls, attr1[1:], tv)
INVALID_INO = 0xFFFFFFFFFFFFFFFF
def mode2type(mode):
return (mode & 0170000) >> 12
TYPE_REG = mode2type(stat.S_IFREG)
TYPE_DIR = mode2type(stat.S_IFDIR)
TYPE_LNK = mode2type(stat.S_IFLNK)
def c2pystr(s):
n = s.find('\x00')
assert n >= 0
return s[:n]
def c2pystr2(s):
first = c2pystr(s)
second = c2pystr(s[len(first)+1:])
return first, second
# ____________________________________________________________
# Version number of this interface
FUSE_KERNEL_VERSION = 7
# Minor version number of this interface
FUSE_KERNEL_MINOR_VERSION = 2
# The node ID of the root inode
FUSE_ROOT_ID = 1
# The major number of the fuse character device
FUSE_MAJOR = 10
# The minor number of the fuse character device
FUSE_MINOR = 229
# Make sure all structures are padded to 64bit boundary, so 32bit
# userspace works under 64bit kernels
_mkstruct('fuse_attr', '''
__u64 ino;
__u64 size;
__u64 blocks;
__u64 _atime;
__u64 _mtime;
__u64 _ctime;
__u32 _atimensec;
__u32 _mtimensec;
__u32 _ctimensec;
__u32 mode;
__u32 nlink;
__u32 uid;
__u32 gid;
__u32 rdev;
''')
_mktimeval(fuse_attr, '_atime', '_atimensec')
_mktimeval(fuse_attr, '_mtime', '_mtimensec')
_mktimeval(fuse_attr, '_ctime', '_ctimensec')
_mkstruct('fuse_kstatfs', '''
__u64 blocks;
__u64 bfree;
__u64 bavail;
__u64 files;
__u64 ffree;
__u32 bsize;
__u32 namelen;
''')
FATTR_MODE = 1 << 0
FATTR_UID = 1 << 1
FATTR_GID = 1 << 2
FATTR_SIZE = 1 << 3
FATTR_ATIME = 1 << 4
FATTR_MTIME = 1 << 5
#
# Flags returned by the OPEN request
#
# FOPEN_DIRECT_IO: bypass page cache for this open file
# FOPEN_KEEP_CACHE: don't invalidate the data cache on open
#
FOPEN_DIRECT_IO = 1 << 0
FOPEN_KEEP_CACHE = 1 << 1
fuse_opcode = {
'FUSE_LOOKUP' : 1,
'FUSE_FORGET' : 2, # no reply
'FUSE_GETATTR' : 3,
'FUSE_SETATTR' : 4,
'FUSE_READLINK' : 5,
'FUSE_SYMLINK' : 6,
'FUSE_MKNOD' : 8,
'FUSE_MKDIR' : 9,
'FUSE_UNLINK' : 10,
'FUSE_RMDIR' : 11,
'FUSE_RENAME' : 12,
'FUSE_LINK' : 13,
'FUSE_OPEN' : 14,
'FUSE_READ' : 15,
'FUSE_WRITE' : 16,
'FUSE_STATFS' : 17,
'FUSE_RELEASE' : 18,
'FUSE_FSYNC' : 20,
'FUSE_SETXATTR' : 21,
'FUSE_GETXATTR' : 22,
'FUSE_LISTXATTR' : 23,
'FUSE_REMOVEXATTR' : 24,
'FUSE_FLUSH' : 25,
'FUSE_INIT' : 26,
'FUSE_OPENDIR' : 27,
'FUSE_READDIR' : 28,
'FUSE_RELEASEDIR' : 29,
'FUSE_FSYNCDIR' : 30,
}
fuse_opcode2name = []
def setup():
for key, value in fuse_opcode.items():
fuse_opcode2name.extend([None] * (value+1 - len(fuse_opcode2name)))
fuse_opcode2name[value] = key
setup()
del setup
# Conservative buffer size for the client
FUSE_MAX_IN = 8192
FUSE_NAME_MAX = 1024
FUSE_SYMLINK_MAX = 4096
FUSE_XATTR_SIZE_MAX = 4096
_mkstruct('fuse_entry_out', """
__u64 nodeid; /* Inode ID */
__u64 generation; /* Inode generation: nodeid:gen must \
be unique for the fs's lifetime */
__u64 _entry_valid; /* Cache timeout for the name */
__u64 _attr_valid; /* Cache timeout for the attributes */
__u32 _entry_valid_nsec;
__u32 _attr_valid_nsec;
""", base=StructWithAttr)
_mktimeval(fuse_entry_out, '_entry_valid', '_entry_valid_nsec')
_mktimeval(fuse_entry_out, '_attr_valid', '_attr_valid_nsec')
_mkstruct('fuse_forget_in', '''
__u64 nlookup;
''')
_mkstruct('fuse_attr_out', '''
__u64 _attr_valid; /* Cache timeout for the attributes */
__u32 _attr_valid_nsec;
__u32 dummy;
''', base=StructWithAttr)
_mktimeval(fuse_attr_out, '_attr_valid', '_attr_valid_nsec')
_mkstruct('fuse_mknod_in', '''
__u32 mode;
__u32 rdev;
''')
_mkstruct('fuse_mkdir_in', '''
__u32 mode;
__u32 padding;
''')
_mkstruct('fuse_rename_in', '''
__u64 newdir;
''')
_mkstruct('fuse_link_in', '''
__u64 oldnodeid;
''')
_mkstruct('fuse_setattr_in', '''
__u32 valid;
__u32 padding;
''', base=StructWithAttr)
_mkstruct('fuse_open_in', '''
__u32 flags;
__u32 padding;
''')
_mkstruct('fuse_open_out', '''
__u64 fh;
__u32 open_flags;
__u32 padding;
''')
_mkstruct('fuse_release_in', '''
__u64 fh;
__u32 flags;
__u32 padding;
''')
_mkstruct('fuse_flush_in', '''
__u64 fh;
__u32 flush_flags;
__u32 padding;
''')
_mkstruct('fuse_read_in', '''
__u64 fh;
__u64 offset;
__u32 size;
__u32 padding;
''')
_mkstruct('fuse_write_in', '''
__u64 fh;
__u64 offset;
__u32 size;
__u32 write_flags;
''')
_mkstruct('fuse_write_out', '''
__u32 size;
__u32 padding;
''')
fuse_statfs_out = fuse_kstatfs
_mkstruct('fuse_fsync_in', '''
__u64 fh;
__u32 fsync_flags;
__u32 padding;
''')
_mkstruct('fuse_setxattr_in', '''
__u32 size;
__u32 flags;
''')
_mkstruct('fuse_getxattr_in', '''
__u32 size;
__u32 padding;
''')
_mkstruct('fuse_getxattr_out', '''
__u32 size;
__u32 padding;
''')
_mkstruct('fuse_init_in_out', '''
__u32 major;
__u32 minor;
''')
_mkstruct('fuse_in_header', '''
__u32 len;
__u32 opcode;
__u64 unique;
__u64 nodeid;
__u32 uid;
__u32 gid;
__u32 pid;
__u32 padding;
''')
_mkstruct('fuse_out_header', '''
__u32 len;
__s32 error;
__u64 unique;
''')
class fuse_dirent(Struct):
__slots__ = ['ino', 'off', 'type', 'name']
def unpack(self, data):
self.ino, self.off, namelen, self.type = struct.unpack('QQII',
data[:24])
self.name = data[24:24+namelen]
assert len(self.name) == namelen
def pack(self):
namelen = len(self.name)
return pack('QQII%ds' % ((namelen+7)&~7,),
self.ino, getattr(self, 'off', 0), namelen,
self.type, self.name)
def calcsize(cls, namelen):
return 24 + ((namelen+7)&~7)
calcsize = classmethod(calcsize)