mirror of
https://github.com/tahoe-lafs/tahoe-lafs.git
synced 2024-12-23 06:42:26 +00:00
406 lines
9.3 KiB
Python
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)
|