mirror of
https://github.com/tahoe-lafs/tahoe-lafs.git
synced 2025-01-31 08:25:35 +00:00
fuse: impl_b: Add impl_b to the contrib directory.
This commit is contained in:
parent
e538651947
commit
2fa5785960
84
contrib/fuse/impl_b/pyfuse/OrderedDict.py
Normal file
84
contrib/fuse/impl_b/pyfuse/OrderedDict.py
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
from UserDict import DictMixin
|
||||||
|
|
||||||
|
|
||||||
|
DELETED = object()
|
||||||
|
|
||||||
|
|
||||||
|
class OrderedDict(DictMixin):
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwds):
|
||||||
|
self.clear()
|
||||||
|
self.update(*args, **kwds)
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
self._keys = []
|
||||||
|
self._content = {} # {key: (index, value)}
|
||||||
|
self._deleted = 0
|
||||||
|
|
||||||
|
def copy(self):
|
||||||
|
return OrderedDict(self)
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
for key in self._keys:
|
||||||
|
if key is not DELETED:
|
||||||
|
yield key
|
||||||
|
|
||||||
|
def keys(self):
|
||||||
|
return [key for key in self._keys if key is not DELETED]
|
||||||
|
|
||||||
|
def popitem(self):
|
||||||
|
while 1:
|
||||||
|
try:
|
||||||
|
k = self._keys.pop()
|
||||||
|
except IndexError:
|
||||||
|
raise KeyError, 'OrderedDict is empty'
|
||||||
|
if k is not DELETED:
|
||||||
|
return k, self._content.pop(k)[1]
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
index, value = self._content[key]
|
||||||
|
return value
|
||||||
|
|
||||||
|
def __setitem__(self, key, value):
|
||||||
|
try:
|
||||||
|
index, oldvalue = self._content[key]
|
||||||
|
except KeyError:
|
||||||
|
index = len(self._keys)
|
||||||
|
self._keys.append(key)
|
||||||
|
self._content[key] = index, value
|
||||||
|
|
||||||
|
def __delitem__(self, key):
|
||||||
|
index, oldvalue = self._content.pop(key)
|
||||||
|
self._keys[index] = DELETED
|
||||||
|
if self._deleted <= len(self._content):
|
||||||
|
self._deleted += 1
|
||||||
|
else:
|
||||||
|
# compress
|
||||||
|
newkeys = []
|
||||||
|
for k in self._keys:
|
||||||
|
if k is not DELETED:
|
||||||
|
i, value = self._content[k]
|
||||||
|
self._content[k] = len(newkeys), value
|
||||||
|
newkeys.append(k)
|
||||||
|
self._keys = newkeys
|
||||||
|
self._deleted = 0
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self._content)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
res = ['%r: %r' % (key, self._content[key][1]) for key in self]
|
||||||
|
return 'OrderedDict(%s)' % (', '.join(res),)
|
||||||
|
|
||||||
|
def __cmp__(self, other):
|
||||||
|
if not isinstance(other, OrderedDict):
|
||||||
|
return NotImplemented
|
||||||
|
keys = self.keys()
|
||||||
|
r = cmp(keys, other.keys())
|
||||||
|
if r:
|
||||||
|
return r
|
||||||
|
for k in keys:
|
||||||
|
r = cmp(self[k], other[k])
|
||||||
|
if r:
|
||||||
|
return r
|
||||||
|
return 0
|
0
contrib/fuse/impl_b/pyfuse/__init__.py
Normal file
0
contrib/fuse/impl_b/pyfuse/__init__.py
Normal file
281
contrib/fuse/impl_b/pyfuse/cachefs.py
Normal file
281
contrib/fuse/impl_b/pyfuse/cachefs.py
Normal file
@ -0,0 +1,281 @@
|
|||||||
|
import os, stat, py, select
|
||||||
|
import inspect
|
||||||
|
from objectfs import ObjectFs
|
||||||
|
|
||||||
|
|
||||||
|
BLOCKSIZE = 8192
|
||||||
|
|
||||||
|
|
||||||
|
def remote_runner(BLOCKSIZE):
|
||||||
|
import sys, select, os, struct
|
||||||
|
stream = None
|
||||||
|
while True:
|
||||||
|
while stream is not None:
|
||||||
|
iwtd, owtd, ewtd = select.select([0], [1], [])
|
||||||
|
if iwtd:
|
||||||
|
break
|
||||||
|
pos = stream.tell()
|
||||||
|
data = stream.read(BLOCKSIZE)
|
||||||
|
res = ('R', path, pos, len(data))
|
||||||
|
sys.stdout.write('%r\n%s' % (res, data))
|
||||||
|
if len(data) < BLOCKSIZE:
|
||||||
|
stream = None
|
||||||
|
|
||||||
|
stream = None
|
||||||
|
msg = eval(sys.stdin.readline())
|
||||||
|
if msg[0] == 'L':
|
||||||
|
path = msg[1]
|
||||||
|
names = os.listdir(path)
|
||||||
|
res = []
|
||||||
|
for name in names:
|
||||||
|
try:
|
||||||
|
st = os.stat(os.path.join(path, name))
|
||||||
|
except OSError:
|
||||||
|
continue
|
||||||
|
res.append((name, st.st_mode, st.st_size))
|
||||||
|
res = msg + (res,)
|
||||||
|
sys.stdout.write('%s\n' % (res,))
|
||||||
|
elif msg[0] == 'R':
|
||||||
|
path, pos = msg[1:]
|
||||||
|
f = open(path, 'rb')
|
||||||
|
f.seek(pos)
|
||||||
|
data = f.read(BLOCKSIZE)
|
||||||
|
res = msg + (len(data),)
|
||||||
|
sys.stdout.write('%r\n%s' % (res, data))
|
||||||
|
elif msg[0] == 'S':
|
||||||
|
path, pos = msg[1:]
|
||||||
|
stream = open(path, 'rb')
|
||||||
|
stream.seek(pos)
|
||||||
|
#elif msg[0] == 'C':
|
||||||
|
# stream = None
|
||||||
|
|
||||||
|
|
||||||
|
class CacheFs(ObjectFs):
|
||||||
|
MOUNT_OPTIONS = {'max_read': BLOCKSIZE}
|
||||||
|
|
||||||
|
def __init__(self, localdir, remotehost, remotedir):
|
||||||
|
src = inspect.getsource(remote_runner)
|
||||||
|
src += '\n\nremote_runner(%d)\n' % BLOCKSIZE
|
||||||
|
|
||||||
|
remotecmd = 'python -u -c "exec input()"'
|
||||||
|
cmdline = [remotehost, remotecmd]
|
||||||
|
# XXX Unix style quoting
|
||||||
|
for i in range(len(cmdline)):
|
||||||
|
cmdline[i] = "'" + cmdline[i].replace("'", "'\\''") + "'"
|
||||||
|
cmd = 'ssh -C'
|
||||||
|
cmdline.insert(0, cmd)
|
||||||
|
|
||||||
|
child_in, child_out = os.popen2(' '.join(cmdline), bufsize=0)
|
||||||
|
child_in.write('%r\n' % (src,))
|
||||||
|
|
||||||
|
control = Controller(child_in, child_out)
|
||||||
|
ObjectFs.__init__(self, CacheDir(localdir, remotedir, control))
|
||||||
|
|
||||||
|
|
||||||
|
class Controller:
|
||||||
|
def __init__(self, child_in, child_out):
|
||||||
|
self.child_in = child_in
|
||||||
|
self.child_out = child_out
|
||||||
|
self.cache = {}
|
||||||
|
self.streaming = None
|
||||||
|
|
||||||
|
def next_answer(self):
|
||||||
|
answer = eval(self.child_out.readline())
|
||||||
|
#print 'A', answer
|
||||||
|
if answer[0] == 'R':
|
||||||
|
remotefn, pos, length = answer[1:]
|
||||||
|
data = self.child_out.read(length)
|
||||||
|
self.cache[remotefn, pos] = data
|
||||||
|
return answer
|
||||||
|
|
||||||
|
def wait_answer(self, query):
|
||||||
|
self.streaming = None
|
||||||
|
#print 'Q', query
|
||||||
|
self.child_in.write('%r\n' % (query,))
|
||||||
|
while True:
|
||||||
|
answer = self.next_answer()
|
||||||
|
if answer[:len(query)] == query:
|
||||||
|
return answer[len(query):]
|
||||||
|
|
||||||
|
def listdir(self, remotedir):
|
||||||
|
query = ('L', remotedir)
|
||||||
|
res, = self.wait_answer(query)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def wait_for_block(self, remotefn, pos):
|
||||||
|
key = remotefn, pos
|
||||||
|
while key not in self.cache:
|
||||||
|
self.next_answer()
|
||||||
|
return self.cache[key]
|
||||||
|
|
||||||
|
def peek_for_block(self, remotefn, pos):
|
||||||
|
key = remotefn, pos
|
||||||
|
while key not in self.cache:
|
||||||
|
iwtd, owtd, ewtd = select.select([self.child_out], [], [], 0)
|
||||||
|
if not iwtd:
|
||||||
|
return None
|
||||||
|
self.next_answer()
|
||||||
|
return self.cache[key]
|
||||||
|
|
||||||
|
def cached_block(self, remotefn, pos):
|
||||||
|
key = remotefn, pos
|
||||||
|
return self.cache.get(key)
|
||||||
|
|
||||||
|
def start_streaming(self, remotefn, pos):
|
||||||
|
if remotefn != self.streaming:
|
||||||
|
while (remotefn, pos) in self.cache:
|
||||||
|
pos += BLOCKSIZE
|
||||||
|
query = ('S', remotefn, pos)
|
||||||
|
#print 'Q', query
|
||||||
|
self.child_in.write('%r\n' % (query,))
|
||||||
|
self.streaming = remotefn
|
||||||
|
|
||||||
|
def read_blocks(self, remotefn, poslist):
|
||||||
|
lst = ['%r\n' % (('R', remotefn, pos),)
|
||||||
|
for pos in poslist if (remotefn, pos) not in self.cache]
|
||||||
|
if lst:
|
||||||
|
self.streaming = None
|
||||||
|
#print 'Q', '+ '.join(lst)
|
||||||
|
self.child_in.write(''.join(lst))
|
||||||
|
|
||||||
|
def clear_cache(self, remotefn):
|
||||||
|
for key in self.cache.keys():
|
||||||
|
if key[0] == remotefn:
|
||||||
|
del self.cache[key]
|
||||||
|
|
||||||
|
|
||||||
|
class CacheDir:
|
||||||
|
def __init__(self, localdir, remotedir, control, size=0):
|
||||||
|
self.localdir = localdir
|
||||||
|
self.remotedir = remotedir
|
||||||
|
self.control = control
|
||||||
|
self.entries = None
|
||||||
|
def listdir(self):
|
||||||
|
if self.entries is None:
|
||||||
|
self.entries = []
|
||||||
|
for name, st_mode, st_size in self.control.listdir(self.remotedir):
|
||||||
|
if stat.S_ISDIR(st_mode):
|
||||||
|
cls = CacheDir
|
||||||
|
else:
|
||||||
|
cls = CacheFile
|
||||||
|
obj = cls(os.path.join(self.localdir, name),
|
||||||
|
os.path.join(self.remotedir, name),
|
||||||
|
self.control,
|
||||||
|
st_size)
|
||||||
|
self.entries.append((name, obj))
|
||||||
|
return self.entries
|
||||||
|
|
||||||
|
class CacheFile:
|
||||||
|
def __init__(self, localfn, remotefn, control, size):
|
||||||
|
self.localfn = localfn
|
||||||
|
self.remotefn = remotefn
|
||||||
|
self.control = control
|
||||||
|
self.st_size = size
|
||||||
|
|
||||||
|
def size(self):
|
||||||
|
return self.st_size
|
||||||
|
|
||||||
|
def read(self):
|
||||||
|
try:
|
||||||
|
st = os.stat(self.localfn)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
if st.st_size == self.st_size: # fully cached
|
||||||
|
return open(self.localfn, 'rb')
|
||||||
|
os.unlink(self.localfn)
|
||||||
|
lpath = py.path.local(self.partial())
|
||||||
|
lpath.ensure(file=1)
|
||||||
|
f = open(self.partial(), 'r+b')
|
||||||
|
return DumpFile(self, f)
|
||||||
|
|
||||||
|
def partial(self):
|
||||||
|
return self.localfn + '.partial~'
|
||||||
|
|
||||||
|
def complete(self):
|
||||||
|
try:
|
||||||
|
os.rename(self.partial(), self.localfn)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class DumpFile:
|
||||||
|
|
||||||
|
def __init__(self, cf, f):
|
||||||
|
self.cf = cf
|
||||||
|
self.f = f
|
||||||
|
self.pos = 0
|
||||||
|
|
||||||
|
def seek(self, npos):
|
||||||
|
self.pos = npos
|
||||||
|
|
||||||
|
def read(self, count):
|
||||||
|
control = self.cf.control
|
||||||
|
self.f.seek(self.pos)
|
||||||
|
buffer = self.f.read(count)
|
||||||
|
self.pos += len(buffer)
|
||||||
|
count -= len(buffer)
|
||||||
|
|
||||||
|
self.f.seek(0, 2)
|
||||||
|
curend = self.f.tell()
|
||||||
|
|
||||||
|
if count > 0:
|
||||||
|
|
||||||
|
while self.pos > curend:
|
||||||
|
curend &= -BLOCKSIZE
|
||||||
|
data = control.peek_for_block(self.cf.remotefn, curend)
|
||||||
|
if data is None:
|
||||||
|
break
|
||||||
|
self.f.seek(curend)
|
||||||
|
self.f.write(data)
|
||||||
|
curend += len(data)
|
||||||
|
if len(data) < BLOCKSIZE:
|
||||||
|
break
|
||||||
|
|
||||||
|
start = max(self.pos, curend) & (-BLOCKSIZE)
|
||||||
|
end = (self.pos + count + BLOCKSIZE-1) & (-BLOCKSIZE)
|
||||||
|
poslist = range(start, end, BLOCKSIZE)
|
||||||
|
|
||||||
|
if self.pos <= curend:
|
||||||
|
control.start_streaming(self.cf.remotefn, start)
|
||||||
|
self.f.seek(start)
|
||||||
|
for p in poslist:
|
||||||
|
data = control.wait_for_block(self.cf.remotefn, p)
|
||||||
|
assert self.f.tell() == p
|
||||||
|
self.f.write(data)
|
||||||
|
if len(data) < BLOCKSIZE:
|
||||||
|
break
|
||||||
|
|
||||||
|
curend = self.f.tell()
|
||||||
|
while curend < self.cf.st_size:
|
||||||
|
curend &= -BLOCKSIZE
|
||||||
|
data = control.cached_block(self.cf.remotefn, curend)
|
||||||
|
if data is None:
|
||||||
|
break
|
||||||
|
assert self.f.tell() == curend
|
||||||
|
self.f.write(data)
|
||||||
|
curend += len(data)
|
||||||
|
else:
|
||||||
|
self.cf.complete()
|
||||||
|
control.clear_cache(self.cf.remotefn)
|
||||||
|
|
||||||
|
self.f.seek(self.pos)
|
||||||
|
buffer += self.f.read(count)
|
||||||
|
|
||||||
|
else:
|
||||||
|
control.read_blocks(self.cf.remotefn, poslist)
|
||||||
|
result = []
|
||||||
|
for p in poslist:
|
||||||
|
data = control.wait_for_block(self.cf.remotefn, p)
|
||||||
|
result.append(data)
|
||||||
|
if len(data) < BLOCKSIZE:
|
||||||
|
break
|
||||||
|
data = ''.join(result)
|
||||||
|
buffer += data[self.pos-start:self.pos-start+count]
|
||||||
|
|
||||||
|
else:
|
||||||
|
if self.pos + 60000 > curend:
|
||||||
|
curend &= -BLOCKSIZE
|
||||||
|
control.start_streaming(self.cf.remotefn, curend)
|
||||||
|
|
||||||
|
return buffer
|
71
contrib/fuse/impl_b/pyfuse/greenhandler.py
Normal file
71
contrib/fuse/impl_b/pyfuse/greenhandler.py
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import sys, os, Queue, atexit
|
||||||
|
|
||||||
|
dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
dir = os.path.join(dir, 'pypeers')
|
||||||
|
if dir not in sys.path:
|
||||||
|
sys.path.append(dir)
|
||||||
|
del dir
|
||||||
|
|
||||||
|
from greensock import *
|
||||||
|
import threadchannel
|
||||||
|
|
||||||
|
|
||||||
|
def _read_from_kernel(handler):
|
||||||
|
while True:
|
||||||
|
msg = read(handler.fd, handler.MAX_READ)
|
||||||
|
if not msg:
|
||||||
|
print >> sys.stderr, "out-kernel connexion closed"
|
||||||
|
break
|
||||||
|
autogreenlet(handler.handle_message, msg)
|
||||||
|
|
||||||
|
def add_handler(handler):
|
||||||
|
autogreenlet(_read_from_kernel, handler)
|
||||||
|
atexit.register(handler.close)
|
||||||
|
|
||||||
|
# ____________________________________________________________
|
||||||
|
|
||||||
|
THREAD_QUEUE = None
|
||||||
|
|
||||||
|
def thread_runner(n):
|
||||||
|
while True:
|
||||||
|
#print 'thread runner %d waiting' % n
|
||||||
|
operation, answer = THREAD_QUEUE.get()
|
||||||
|
#print 'thread_runner %d: %r' % (n, operation)
|
||||||
|
try:
|
||||||
|
res = True, operation()
|
||||||
|
except Exception:
|
||||||
|
res = False, sys.exc_info()
|
||||||
|
#print 'thread_runner %d: got %d bytes' % (n, len(res or ''))
|
||||||
|
answer.send(res)
|
||||||
|
|
||||||
|
|
||||||
|
def start_bkgnd_thread():
|
||||||
|
global THREAD_QUEUE, THREAD_LOCK
|
||||||
|
import thread
|
||||||
|
threadchannel.startup()
|
||||||
|
THREAD_LOCK = thread.allocate_lock()
|
||||||
|
THREAD_QUEUE = Queue.Queue()
|
||||||
|
for i in range(4):
|
||||||
|
thread.start_new_thread(thread_runner, (i,))
|
||||||
|
|
||||||
|
def wget(*args, **kwds):
|
||||||
|
from wget import wget
|
||||||
|
|
||||||
|
def operation():
|
||||||
|
kwds['unlock'] = THREAD_LOCK
|
||||||
|
THREAD_LOCK.acquire()
|
||||||
|
try:
|
||||||
|
return wget(*args, **kwds)
|
||||||
|
finally:
|
||||||
|
THREAD_LOCK.release()
|
||||||
|
|
||||||
|
if THREAD_QUEUE is None:
|
||||||
|
start_bkgnd_thread()
|
||||||
|
answer = threadchannel.ThreadChannel()
|
||||||
|
THREAD_QUEUE.put((operation, answer))
|
||||||
|
ok, res = answer.receive()
|
||||||
|
if not ok:
|
||||||
|
typ, value, tb = res
|
||||||
|
raise typ, value, tb
|
||||||
|
#print 'wget returns %d bytes' % (len(res or ''),)
|
||||||
|
return res
|
377
contrib/fuse/impl_b/pyfuse/handler.py
Normal file
377
contrib/fuse/impl_b/pyfuse/handler.py
Normal file
@ -0,0 +1,377 @@
|
|||||||
|
from kernel import *
|
||||||
|
import os, errno, sys, stat
|
||||||
|
|
||||||
|
def fuse_mount(mountpoint, opts=None):
|
||||||
|
if not isinstance(mountpoint, str):
|
||||||
|
raise TypeError
|
||||||
|
if opts is not None and not isinstance(opts, str):
|
||||||
|
raise TypeError
|
||||||
|
import dl
|
||||||
|
fuse = dl.open('libfuse.so')
|
||||||
|
if fuse.sym('fuse_mount_compat22'):
|
||||||
|
fnname = 'fuse_mount_compat22'
|
||||||
|
else:
|
||||||
|
fnname = 'fuse_mount' # older versions of libfuse.so
|
||||||
|
return fuse.call(fnname, mountpoint, opts)
|
||||||
|
|
||||||
|
class Handler(object):
|
||||||
|
__system = os.system
|
||||||
|
mountpoint = fd = None
|
||||||
|
__in_header_size = fuse_in_header.calcsize()
|
||||||
|
__out_header_size = fuse_out_header.calcsize()
|
||||||
|
MAX_READ = FUSE_MAX_IN
|
||||||
|
|
||||||
|
def __init__(self, mountpoint, filesystem, logfile='STDERR', **opts1):
|
||||||
|
opts = getattr(filesystem, 'MOUNT_OPTIONS', {}).copy()
|
||||||
|
opts.update(opts1)
|
||||||
|
if opts:
|
||||||
|
opts = opts.items()
|
||||||
|
opts.sort()
|
||||||
|
opts = ' '.join(['%s=%s' % item for item in opts])
|
||||||
|
else:
|
||||||
|
opts = None
|
||||||
|
fd = fuse_mount(mountpoint, opts)
|
||||||
|
if fd < 0:
|
||||||
|
raise IOError("mount failed")
|
||||||
|
self.fd = fd
|
||||||
|
if logfile == 'STDERR':
|
||||||
|
logfile = sys.stderr
|
||||||
|
self.logfile = logfile
|
||||||
|
if self.logfile:
|
||||||
|
print >> self.logfile, '* mounted at', mountpoint
|
||||||
|
self.mountpoint = mountpoint
|
||||||
|
self.filesystem = filesystem
|
||||||
|
self.handles = {}
|
||||||
|
self.nexth = 1
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
if self.fd is not None:
|
||||||
|
os.close(self.fd)
|
||||||
|
self.fd = None
|
||||||
|
if self.mountpoint:
|
||||||
|
cmd = "fusermount -u '%s'" % self.mountpoint.replace("'", r"'\''")
|
||||||
|
self.mountpoint = None
|
||||||
|
if self.logfile:
|
||||||
|
print >> self.logfile, '*', cmd
|
||||||
|
self.__system(cmd)
|
||||||
|
|
||||||
|
close = __del__
|
||||||
|
|
||||||
|
def loop_forever(self):
|
||||||
|
while True:
|
||||||
|
msg = os.read(self.fd, FUSE_MAX_IN)
|
||||||
|
if not msg:
|
||||||
|
raise EOFError("out-kernel connection closed")
|
||||||
|
self.handle_message(msg)
|
||||||
|
|
||||||
|
def handle_message(self, msg):
|
||||||
|
headersize = self.__in_header_size
|
||||||
|
req = fuse_in_header(msg[:headersize])
|
||||||
|
assert req.len == len(msg)
|
||||||
|
name = req.opcode
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
name = fuse_opcode2name[req.opcode]
|
||||||
|
meth = getattr(self, name)
|
||||||
|
except (IndexError, AttributeError):
|
||||||
|
raise NotImplementedError
|
||||||
|
#if self.logfile:
|
||||||
|
# print >> self.logfile, '%s(%d)' % (name, req.nodeid)
|
||||||
|
reply = meth(req, msg[headersize:])
|
||||||
|
#if self.logfile:
|
||||||
|
# print >> self.logfile, ' >>', repr(reply)
|
||||||
|
except NotImplementedError:
|
||||||
|
if self.logfile:
|
||||||
|
print >> self.logfile, '%s: not implemented' % (name,)
|
||||||
|
self.send_reply(req, err=errno.ENOSYS)
|
||||||
|
except EnvironmentError, e:
|
||||||
|
if self.logfile:
|
||||||
|
print >> self.logfile, '%s: %s' % (name, e)
|
||||||
|
self.send_reply(req, err = e.errno or errno.ESTALE)
|
||||||
|
except NoReply:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
self.send_reply(req, reply)
|
||||||
|
|
||||||
|
def send_reply(self, req, reply=None, err=0):
|
||||||
|
assert 0 <= err < 1000
|
||||||
|
if reply is None:
|
||||||
|
reply = ''
|
||||||
|
elif not isinstance(reply, str):
|
||||||
|
reply = reply.pack()
|
||||||
|
f = fuse_out_header(unique = req.unique,
|
||||||
|
error = -err,
|
||||||
|
len = self.__out_header_size + len(reply))
|
||||||
|
data = f.pack() + reply
|
||||||
|
while data:
|
||||||
|
count = os.write(self.fd, data)
|
||||||
|
if not count:
|
||||||
|
raise EOFError("in-kernel connection closed")
|
||||||
|
data = data[count:]
|
||||||
|
|
||||||
|
def notsupp_or_ro(self):
|
||||||
|
if hasattr(self.filesystem, "modified"):
|
||||||
|
raise IOError(errno.ENOSYS, "not supported")
|
||||||
|
else:
|
||||||
|
raise IOError(errno.EROFS, "read-only file system")
|
||||||
|
|
||||||
|
# ____________________________________________________________
|
||||||
|
|
||||||
|
def FUSE_INIT(self, req, msg):
|
||||||
|
msg = fuse_init_in_out(msg[:8])
|
||||||
|
if self.logfile:
|
||||||
|
print >> self.logfile, 'INIT: %d.%d' % (msg.major, msg.minor)
|
||||||
|
return fuse_init_in_out(major = FUSE_KERNEL_VERSION,
|
||||||
|
minor = FUSE_KERNEL_MINOR_VERSION)
|
||||||
|
|
||||||
|
def FUSE_GETATTR(self, req, msg):
|
||||||
|
node = self.filesystem.getnode(req.nodeid)
|
||||||
|
attr, valid = self.filesystem.getattr(node)
|
||||||
|
return fuse_attr_out(attr_valid = valid,
|
||||||
|
attr = attr)
|
||||||
|
|
||||||
|
def FUSE_SETATTR(self, req, msg):
|
||||||
|
if not hasattr(self.filesystem, 'setattr'):
|
||||||
|
self.notsupp_or_ro()
|
||||||
|
msg = fuse_setattr_in(msg)
|
||||||
|
if msg.valid & FATTR_MODE: mode = msg.attr.mode & 0777
|
||||||
|
else: mode = None
|
||||||
|
if msg.valid & FATTR_UID: uid = msg.attr.uid
|
||||||
|
else: uid = None
|
||||||
|
if msg.valid & FATTR_GID: gid = msg.attr.gid
|
||||||
|
else: gid = None
|
||||||
|
if msg.valid & FATTR_SIZE: size = msg.attr.size
|
||||||
|
else: size = None
|
||||||
|
if msg.valid & FATTR_ATIME: atime = msg.attr.atime
|
||||||
|
else: atime = None
|
||||||
|
if msg.valid & FATTR_MTIME: mtime = msg.attr.mtime
|
||||||
|
else: mtime = None
|
||||||
|
node = self.filesystem.getnode(req.nodeid)
|
||||||
|
self.filesystem.setattr(node, mode, uid, gid,
|
||||||
|
size, atime, mtime)
|
||||||
|
attr, valid = self.filesystem.getattr(node)
|
||||||
|
return fuse_attr_out(attr_valid = valid,
|
||||||
|
attr = attr)
|
||||||
|
|
||||||
|
def FUSE_RELEASE(self, req, msg):
|
||||||
|
msg = fuse_release_in(msg, truncate=True)
|
||||||
|
try:
|
||||||
|
del self.handles[msg.fh]
|
||||||
|
except KeyError:
|
||||||
|
raise IOError(errno.EBADF, msg.fh)
|
||||||
|
FUSE_RELEASEDIR = FUSE_RELEASE
|
||||||
|
|
||||||
|
def FUSE_OPENDIR(self, req, msg):
|
||||||
|
#msg = fuse_open_in(msg)
|
||||||
|
node = self.filesystem.getnode(req.nodeid)
|
||||||
|
attr, valid = self.filesystem.getattr(node)
|
||||||
|
if mode2type(attr.mode) != TYPE_DIR:
|
||||||
|
raise IOError(errno.ENOTDIR, node)
|
||||||
|
fh = self.nexth
|
||||||
|
self.nexth += 1
|
||||||
|
self.handles[fh] = True, '', node
|
||||||
|
return fuse_open_out(fh = fh)
|
||||||
|
|
||||||
|
def FUSE_READDIR(self, req, msg):
|
||||||
|
msg = fuse_read_in(msg)
|
||||||
|
try:
|
||||||
|
isdir, data, node = self.handles[msg.fh]
|
||||||
|
if not isdir:
|
||||||
|
raise KeyError # not a dir handle
|
||||||
|
except KeyError:
|
||||||
|
raise IOError(errno.EBADF, msg.fh)
|
||||||
|
if msg.offset == 0:
|
||||||
|
# start or rewind
|
||||||
|
d_entries = []
|
||||||
|
off = 0
|
||||||
|
for name, type in self.filesystem.listdir(node):
|
||||||
|
off += fuse_dirent.calcsize(len(name))
|
||||||
|
d_entry = fuse_dirent(ino = INVALID_INO,
|
||||||
|
off = off,
|
||||||
|
type = type,
|
||||||
|
name = name)
|
||||||
|
d_entries.append(d_entry)
|
||||||
|
data = ''.join([d.pack() for d in d_entries])
|
||||||
|
self.handles[msg.fh] = True, data, node
|
||||||
|
return data[msg.offset:msg.offset+msg.size]
|
||||||
|
|
||||||
|
def replyentry(self, (subnodeid, valid1)):
|
||||||
|
subnode = self.filesystem.getnode(subnodeid)
|
||||||
|
attr, valid2 = self.filesystem.getattr(subnode)
|
||||||
|
return fuse_entry_out(nodeid = subnodeid,
|
||||||
|
entry_valid = valid1,
|
||||||
|
attr_valid = valid2,
|
||||||
|
attr = attr)
|
||||||
|
|
||||||
|
def FUSE_LOOKUP(self, req, msg):
|
||||||
|
filename = c2pystr(msg)
|
||||||
|
dirnode = self.filesystem.getnode(req.nodeid)
|
||||||
|
return self.replyentry(self.filesystem.lookup(dirnode, filename))
|
||||||
|
|
||||||
|
def FUSE_OPEN(self, req, msg, mask=os.O_RDONLY|os.O_WRONLY|os.O_RDWR):
|
||||||
|
msg = fuse_open_in(msg)
|
||||||
|
node = self.filesystem.getnode(req.nodeid)
|
||||||
|
attr, valid = self.filesystem.getattr(node)
|
||||||
|
if mode2type(attr.mode) != TYPE_REG:
|
||||||
|
raise IOError(errno.EPERM, node)
|
||||||
|
f = self.filesystem.open(node, msg.flags & mask)
|
||||||
|
if isinstance(f, tuple):
|
||||||
|
f, open_flags = f
|
||||||
|
else:
|
||||||
|
open_flags = 0
|
||||||
|
fh = self.nexth
|
||||||
|
self.nexth += 1
|
||||||
|
self.handles[fh] = False, f, node
|
||||||
|
return fuse_open_out(fh = fh, open_flags = open_flags)
|
||||||
|
|
||||||
|
def FUSE_READ(self, req, msg):
|
||||||
|
msg = fuse_read_in(msg)
|
||||||
|
try:
|
||||||
|
isdir, f, node = self.handles[msg.fh]
|
||||||
|
if isdir:
|
||||||
|
raise KeyError
|
||||||
|
except KeyError:
|
||||||
|
raise IOError(errno.EBADF, msg.fh)
|
||||||
|
f.seek(msg.offset)
|
||||||
|
return f.read(msg.size)
|
||||||
|
|
||||||
|
def FUSE_WRITE(self, req, msg):
|
||||||
|
if not hasattr(self.filesystem, 'modified'):
|
||||||
|
raise IOError(errno.EROFS, "read-only file system")
|
||||||
|
msg, data = fuse_write_in.from_head(msg)
|
||||||
|
try:
|
||||||
|
isdir, f, node = self.handles[msg.fh]
|
||||||
|
if isdir:
|
||||||
|
raise KeyError
|
||||||
|
except KeyError:
|
||||||
|
raise IOError(errno.EBADF, msg.fh)
|
||||||
|
f.seek(msg.offset)
|
||||||
|
f.write(data)
|
||||||
|
self.filesystem.modified(node)
|
||||||
|
return fuse_write_out(size = len(data))
|
||||||
|
|
||||||
|
def FUSE_MKNOD(self, req, msg):
|
||||||
|
if not hasattr(self.filesystem, 'mknod'):
|
||||||
|
self.notsupp_or_ro()
|
||||||
|
msg, filename = fuse_mknod_in.from_param(msg)
|
||||||
|
node = self.filesystem.getnode(req.nodeid)
|
||||||
|
return self.replyentry(self.filesystem.mknod(node, filename, msg.mode))
|
||||||
|
|
||||||
|
def FUSE_MKDIR(self, req, msg):
|
||||||
|
if not hasattr(self.filesystem, 'mkdir'):
|
||||||
|
self.notsupp_or_ro()
|
||||||
|
msg, filename = fuse_mkdir_in.from_param(msg)
|
||||||
|
node = self.filesystem.getnode(req.nodeid)
|
||||||
|
return self.replyentry(self.filesystem.mkdir(node, filename, msg.mode))
|
||||||
|
|
||||||
|
def FUSE_SYMLINK(self, req, msg):
|
||||||
|
if not hasattr(self.filesystem, 'symlink'):
|
||||||
|
self.notsupp_or_ro()
|
||||||
|
linkname, target = c2pystr2(msg)
|
||||||
|
node = self.filesystem.getnode(req.nodeid)
|
||||||
|
return self.replyentry(self.filesystem.symlink(node, linkname, target))
|
||||||
|
|
||||||
|
#def FUSE_LINK(self, req, msg):
|
||||||
|
# ...
|
||||||
|
|
||||||
|
def FUSE_UNLINK(self, req, msg):
|
||||||
|
if not hasattr(self.filesystem, 'unlink'):
|
||||||
|
self.notsupp_or_ro()
|
||||||
|
filename = c2pystr(msg)
|
||||||
|
node = self.filesystem.getnode(req.nodeid)
|
||||||
|
self.filesystem.unlink(node, filename)
|
||||||
|
|
||||||
|
def FUSE_RMDIR(self, req, msg):
|
||||||
|
if not hasattr(self.filesystem, 'rmdir'):
|
||||||
|
self.notsupp_or_ro()
|
||||||
|
dirname = c2pystr(msg)
|
||||||
|
node = self.filesystem.getnode(req.nodeid)
|
||||||
|
self.filesystem.rmdir(node, dirname)
|
||||||
|
|
||||||
|
def FUSE_FORGET(self, req, msg):
|
||||||
|
if hasattr(self.filesystem, 'forget'):
|
||||||
|
self.filesystem.forget(req.nodeid)
|
||||||
|
raise NoReply
|
||||||
|
|
||||||
|
def FUSE_READLINK(self, req, msg):
|
||||||
|
if not hasattr(self.filesystem, 'readlink'):
|
||||||
|
raise IOError(errno.ENOSYS, "readlink not supported")
|
||||||
|
node = self.filesystem.getnode(req.nodeid)
|
||||||
|
target = self.filesystem.readlink(node)
|
||||||
|
return target
|
||||||
|
|
||||||
|
def FUSE_RENAME(self, req, msg):
|
||||||
|
if not hasattr(self.filesystem, 'rename'):
|
||||||
|
self.notsupp_or_ro()
|
||||||
|
msg, oldname, newname = fuse_rename_in.from_param2(msg)
|
||||||
|
oldnode = self.filesystem.getnode(req.nodeid)
|
||||||
|
newnode = self.filesystem.getnode(msg.newdir)
|
||||||
|
self.filesystem.rename(oldnode, oldname, newnode, newname)
|
||||||
|
|
||||||
|
def getxattrs(self, nodeid):
|
||||||
|
if not hasattr(self.filesystem, 'getxattrs'):
|
||||||
|
raise IOError(errno.ENOSYS, "xattrs not supported")
|
||||||
|
node = self.filesystem.getnode(nodeid)
|
||||||
|
return self.filesystem.getxattrs(node)
|
||||||
|
|
||||||
|
def FUSE_LISTXATTR(self, req, msg):
|
||||||
|
names = self.getxattrs(req.nodeid).keys()
|
||||||
|
names = ['user.' + name for name in names]
|
||||||
|
totalsize = 0
|
||||||
|
for name in names:
|
||||||
|
totalsize += len(name)+1
|
||||||
|
msg = fuse_getxattr_in(msg)
|
||||||
|
if msg.size > 0:
|
||||||
|
if msg.size < totalsize:
|
||||||
|
raise IOError(errno.ERANGE, "buffer too small")
|
||||||
|
names.append('')
|
||||||
|
return '\x00'.join(names)
|
||||||
|
else:
|
||||||
|
return fuse_getxattr_out(size=totalsize)
|
||||||
|
|
||||||
|
def FUSE_GETXATTR(self, req, msg):
|
||||||
|
xattrs = self.getxattrs(req.nodeid)
|
||||||
|
msg, name = fuse_getxattr_in.from_param(msg)
|
||||||
|
if not name.startswith('user.'): # ENODATA == ENOATTR
|
||||||
|
raise IOError(errno.ENODATA, "only supports 'user.' xattrs, "
|
||||||
|
"got %r" % (name,))
|
||||||
|
name = name[5:]
|
||||||
|
try:
|
||||||
|
value = xattrs[name]
|
||||||
|
except KeyError:
|
||||||
|
raise IOError(errno.ENODATA, "no such xattr") # == ENOATTR
|
||||||
|
value = str(value)
|
||||||
|
if msg.size > 0:
|
||||||
|
if msg.size < len(value):
|
||||||
|
raise IOError(errno.ERANGE, "buffer too small")
|
||||||
|
return value
|
||||||
|
else:
|
||||||
|
return fuse_getxattr_out(size=len(value))
|
||||||
|
|
||||||
|
def FUSE_SETXATTR(self, req, msg):
|
||||||
|
xattrs = self.getxattrs(req.nodeid)
|
||||||
|
msg, name, value = fuse_setxattr_in.from_param_head(msg)
|
||||||
|
assert len(value) == msg.size
|
||||||
|
# XXX msg.flags ignored
|
||||||
|
if not name.startswith('user.'): # ENODATA == ENOATTR
|
||||||
|
raise IOError(errno.ENODATA, "only supports 'user.' xattrs")
|
||||||
|
name = name[5:]
|
||||||
|
try:
|
||||||
|
xattrs[name] = value
|
||||||
|
except KeyError:
|
||||||
|
raise IOError(errno.ENODATA, "cannot set xattr") # == ENOATTR
|
||||||
|
|
||||||
|
def FUSE_REMOVEXATTR(self, req, msg):
|
||||||
|
xattrs = self.getxattrs(req.nodeid)
|
||||||
|
name = c2pystr(msg)
|
||||||
|
if not name.startswith('user.'): # ENODATA == ENOATTR
|
||||||
|
raise IOError(errno.ENODATA, "only supports 'user.' xattrs")
|
||||||
|
name = name[5:]
|
||||||
|
try:
|
||||||
|
del xattrs[name]
|
||||||
|
except KeyError:
|
||||||
|
raise IOError(errno.ENODATA, "cannot delete xattr") # == ENOATTR
|
||||||
|
|
||||||
|
|
||||||
|
class NoReply(Exception):
|
||||||
|
pass
|
107
contrib/fuse/impl_b/pyfuse/httpfs.py
Normal file
107
contrib/fuse/impl_b/pyfuse/httpfs.py
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
import os, re, urlparse
|
||||||
|
from handler import Handler
|
||||||
|
from objectfs import ObjectFs
|
||||||
|
|
||||||
|
|
||||||
|
class Root:
|
||||||
|
def __init__(self):
|
||||||
|
self.entries = {'gg': GoogleRoot()}
|
||||||
|
def listdir(self):
|
||||||
|
return self.entries.keys()
|
||||||
|
def join(self, hostname):
|
||||||
|
if hostname in self.entries:
|
||||||
|
return self.entries[hostname]
|
||||||
|
if '.' not in hostname:
|
||||||
|
raise KeyError
|
||||||
|
result = HtmlNode('http://%s/' % (hostname,))
|
||||||
|
self.entries[hostname] = result
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
class UrlNode:
|
||||||
|
data = None
|
||||||
|
|
||||||
|
def __init__(self, url):
|
||||||
|
self.url = url
|
||||||
|
|
||||||
|
def getdata(self):
|
||||||
|
if self.data is None:
|
||||||
|
print self.url
|
||||||
|
g = os.popen("lynx -source %r" % (self.url,), 'r')
|
||||||
|
self.data = g.read()
|
||||||
|
g.close()
|
||||||
|
return self.data
|
||||||
|
|
||||||
|
|
||||||
|
class HtmlNode(UrlNode):
|
||||||
|
r_links = re.compile(r'<a\s[^>]*href="([^"]+)"[^>]*>(.*?)</a>',
|
||||||
|
re.IGNORECASE | re.DOTALL)
|
||||||
|
r_images = re.compile(r'<img\s[^>]*src="([^"]+[.]jpg)"', re.IGNORECASE)
|
||||||
|
|
||||||
|
def format(self, text, index,
|
||||||
|
TRANSTBL = ''.join([(32<=c<127 and c!=ord('/'))
|
||||||
|
and chr(c) or '_'
|
||||||
|
for c in range(256)])):
|
||||||
|
return text.translate(TRANSTBL)
|
||||||
|
|
||||||
|
def listdir(self):
|
||||||
|
data = self.getdata()
|
||||||
|
|
||||||
|
seen = {}
|
||||||
|
def uniquename(name):
|
||||||
|
name = self.format(name, len(seen))
|
||||||
|
if name == '' or name.startswith('.'):
|
||||||
|
name = '_' + name
|
||||||
|
basename = name
|
||||||
|
i = 1
|
||||||
|
while name in seen:
|
||||||
|
i += 1
|
||||||
|
name = '%s_%d' % (basename, i)
|
||||||
|
seen[name] = True
|
||||||
|
return name
|
||||||
|
|
||||||
|
for link, text in self.r_links.findall(data):
|
||||||
|
url = urlparse.urljoin(self.url, link)
|
||||||
|
yield uniquename(text), HtmlNode(url)
|
||||||
|
|
||||||
|
for link in self.r_images.findall(data):
|
||||||
|
text = os.path.basename(link)
|
||||||
|
url = urlparse.urljoin(self.url, link)
|
||||||
|
yield uniquename(text), RawNode(url)
|
||||||
|
|
||||||
|
yield '.source', RawNode(self.url)
|
||||||
|
|
||||||
|
|
||||||
|
class RawNode(UrlNode):
|
||||||
|
|
||||||
|
def read(self):
|
||||||
|
return self.getdata()
|
||||||
|
|
||||||
|
def size(self):
|
||||||
|
if self.data:
|
||||||
|
return len(self.data)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class GoogleRoot:
|
||||||
|
def join(self, query):
|
||||||
|
return GoogleSearch(query)
|
||||||
|
|
||||||
|
class GoogleSearch(HtmlNode):
|
||||||
|
r_links = re.compile(r'<a\sclass=l\s[^>]*href="([^"]+)"[^>]*>(.*?)</a>',
|
||||||
|
re.IGNORECASE | re.DOTALL)
|
||||||
|
|
||||||
|
def __init__(self, query):
|
||||||
|
self.url = 'http://www.google.com/search?q=' + query
|
||||||
|
|
||||||
|
def format(self, text, index):
|
||||||
|
text = text.replace('<b>', '').replace('</b>', '')
|
||||||
|
text = HtmlNode.format(self, text, index)
|
||||||
|
return '%d. %s' % (index, text)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
root = Root()
|
||||||
|
handler = Handler('/home/arigo/mnt', ObjectFs(root))
|
||||||
|
handler.loop_forever()
|
405
contrib/fuse/impl_b/pyfuse/kernel.py
Normal file
405
contrib/fuse/impl_b/pyfuse/kernel.py
Normal file
@ -0,0 +1,405 @@
|
|||||||
|
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)
|
155
contrib/fuse/impl_b/pyfuse/memoryfs.py
Normal file
155
contrib/fuse/impl_b/pyfuse/memoryfs.py
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
from kernel import *
|
||||||
|
from handler import Handler
|
||||||
|
import stat, time, os, weakref, errno
|
||||||
|
from cStringIO import StringIO
|
||||||
|
|
||||||
|
|
||||||
|
class MemoryFS(object):
|
||||||
|
INFINITE = 86400.0
|
||||||
|
|
||||||
|
|
||||||
|
class Dir(object):
|
||||||
|
type = TYPE_DIR
|
||||||
|
def __init__(self, attr):
|
||||||
|
self.attr = attr
|
||||||
|
self.contents = {} # { 'filename': Dir()/File()/SymLink() }
|
||||||
|
|
||||||
|
class File(object):
|
||||||
|
type = TYPE_REG
|
||||||
|
def __init__(self, attr):
|
||||||
|
self.attr = attr
|
||||||
|
self.data = StringIO()
|
||||||
|
|
||||||
|
class SymLink(object):
|
||||||
|
type = TYPE_LNK
|
||||||
|
def __init__(self, attr, target):
|
||||||
|
self.attr = attr
|
||||||
|
self.target = target
|
||||||
|
|
||||||
|
|
||||||
|
def __init__(self, root=None):
|
||||||
|
self.uid = os.getuid()
|
||||||
|
self.gid = os.getgid()
|
||||||
|
self.umask = os.umask(0); os.umask(self.umask)
|
||||||
|
self.root = root or self.Dir(self.newattr(stat.S_IFDIR))
|
||||||
|
self.root.id = FUSE_ROOT_ID
|
||||||
|
self.nodes = weakref.WeakValueDictionary()
|
||||||
|
self.nodes[FUSE_ROOT_ID] = self.root
|
||||||
|
self.nextid = FUSE_ROOT_ID + 1
|
||||||
|
|
||||||
|
def newattr(self, s, ino=None, mode=0666):
|
||||||
|
now = time.time()
|
||||||
|
attr = fuse_attr(size = 0,
|
||||||
|
mode = s | (mode & ~self.umask),
|
||||||
|
nlink = 1, # even on dirs! this confuses 'find' in
|
||||||
|
# a good way :-)
|
||||||
|
atime = now,
|
||||||
|
mtime = now,
|
||||||
|
ctime = now,
|
||||||
|
uid = self.uid,
|
||||||
|
gid = self.gid)
|
||||||
|
if ino is None:
|
||||||
|
ino = id(attr)
|
||||||
|
if ino < 0:
|
||||||
|
ino = ~ino
|
||||||
|
attr.ino = ino
|
||||||
|
return attr
|
||||||
|
|
||||||
|
def getnode(self, id):
|
||||||
|
return self.nodes[id]
|
||||||
|
|
||||||
|
def modified(self, node):
|
||||||
|
node.attr.mtime = node.attr.atime = time.time()
|
||||||
|
if isinstance(node, self.File):
|
||||||
|
node.data.seek(0, 2)
|
||||||
|
node.attr.size = node.data.tell()
|
||||||
|
|
||||||
|
def getattr(self, node):
|
||||||
|
return node.attr, self.INFINITE
|
||||||
|
|
||||||
|
def setattr(self, node, mode, uid, gid, size, atime, mtime):
|
||||||
|
if mode is not None:
|
||||||
|
node.attr.mode = (node.attr.mode & ~0777) | (mode & 0777)
|
||||||
|
if uid is not None:
|
||||||
|
node.attr.uid = uid
|
||||||
|
if gid is not None:
|
||||||
|
node.attr.gid = gid
|
||||||
|
if size is not None:
|
||||||
|
assert isinstance(node, self.File)
|
||||||
|
node.data.seek(0, 2)
|
||||||
|
oldsize = node.data.tell()
|
||||||
|
if size < oldsize:
|
||||||
|
node.data.seek(size)
|
||||||
|
node.data.truncate()
|
||||||
|
self.modified(node)
|
||||||
|
elif size > oldsize:
|
||||||
|
node.data.write('\x00' * (size - oldsize))
|
||||||
|
self.modified(node)
|
||||||
|
if atime is not None:
|
||||||
|
node.attr.atime = atime
|
||||||
|
if mtime is not None:
|
||||||
|
node.attr.mtime = mtime
|
||||||
|
|
||||||
|
def listdir(self, node):
|
||||||
|
assert isinstance(node, self.Dir)
|
||||||
|
for name, subobj in node.contents.items():
|
||||||
|
yield name, subobj.type
|
||||||
|
|
||||||
|
def lookup(self, dirnode, filename):
|
||||||
|
try:
|
||||||
|
return dirnode.contents[filename].id, self.INFINITE
|
||||||
|
except KeyError:
|
||||||
|
raise IOError(errno.ENOENT, filename)
|
||||||
|
|
||||||
|
def open(self, filenode, flags):
|
||||||
|
return filenode.data
|
||||||
|
|
||||||
|
def newnodeid(self, newnode):
|
||||||
|
id = self.nextid
|
||||||
|
self.nextid += 1
|
||||||
|
newnode.id = id
|
||||||
|
self.nodes[id] = newnode
|
||||||
|
return id
|
||||||
|
|
||||||
|
def mknod(self, dirnode, filename, mode):
|
||||||
|
node = self.File(self.newattr(stat.S_IFREG, mode=mode))
|
||||||
|
dirnode.contents[filename] = node
|
||||||
|
return self.newnodeid(node), self.INFINITE
|
||||||
|
|
||||||
|
def mkdir(self, dirnode, subdirname, mode):
|
||||||
|
node = self.Dir(self.newattr(stat.S_IFDIR, mode=mode))
|
||||||
|
dirnode.contents[subdirname] = node
|
||||||
|
return self.newnodeid(node), self.INFINITE
|
||||||
|
|
||||||
|
def symlink(self, dirnode, linkname, target):
|
||||||
|
node = self.SymLink(self.newattr(stat.S_IFLNK), target)
|
||||||
|
dirnode.contents[linkname] = node
|
||||||
|
return self.newnodeid(node), self.INFINITE
|
||||||
|
|
||||||
|
def unlink(self, dirnode, filename):
|
||||||
|
del dirnode.contents[filename]
|
||||||
|
|
||||||
|
rmdir = unlink
|
||||||
|
|
||||||
|
def readlink(self, symlinknode):
|
||||||
|
return symlinknode.target
|
||||||
|
|
||||||
|
def rename(self, olddirnode, oldname, newdirnode, newname):
|
||||||
|
node = olddirnode.contents[oldname]
|
||||||
|
newdirnode.contents[newname] = node
|
||||||
|
del olddirnode.contents[oldname]
|
||||||
|
|
||||||
|
def getxattrs(self, node):
|
||||||
|
try:
|
||||||
|
return node.xattrs
|
||||||
|
except AttributeError:
|
||||||
|
node.xattrs = {}
|
||||||
|
return node.xattrs
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
import sys
|
||||||
|
mountpoint = sys.argv[1]
|
||||||
|
memoryfs = MemoryFS()
|
||||||
|
handler = Handler(mountpoint, memoryfs)
|
||||||
|
handler.loop_forever()
|
191
contrib/fuse/impl_b/pyfuse/mirrorfs.py
Normal file
191
contrib/fuse/impl_b/pyfuse/mirrorfs.py
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
"""
|
||||||
|
For reading and caching from slow file system (e.g. DVDs or network).
|
||||||
|
|
||||||
|
python mirrorfs.py <sourcedir> <cachedir> <mountpoint>
|
||||||
|
|
||||||
|
Makes <mountpoint> show a read-only copy of the files in <sourcedir>,
|
||||||
|
caching all data ever read in the <cachedir> to avoid reading it
|
||||||
|
twice. This script also features optimistic read-ahead: once a
|
||||||
|
file is accessed, and as long as no other file is accessed, the
|
||||||
|
whole file is read and cached as fast as the <sourcedir> will
|
||||||
|
provide it.
|
||||||
|
|
||||||
|
You have to clean up <cachedir> manually before mounting a modified
|
||||||
|
or different <sourcedir>.
|
||||||
|
"""
|
||||||
|
import sys, os, posixpath, stat
|
||||||
|
|
||||||
|
try:
|
||||||
|
__file__
|
||||||
|
except NameError:
|
||||||
|
__file__ = sys.argv[0]
|
||||||
|
this_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
# ____________________________________________________________
|
||||||
|
|
||||||
|
sys.path.append(os.path.dirname(this_dir))
|
||||||
|
from blockfs import valuetree
|
||||||
|
from handler import Handler
|
||||||
|
import greenhandler, greensock
|
||||||
|
from objectfs import ObjectFs
|
||||||
|
|
||||||
|
BLOCKSIZE = 65536
|
||||||
|
|
||||||
|
class MirrorFS(ObjectFs):
|
||||||
|
rawfd = None
|
||||||
|
|
||||||
|
def __init__(self, srcdir, cachedir):
|
||||||
|
self.srcdir = srcdir
|
||||||
|
self.cachedir = cachedir
|
||||||
|
self.table = valuetree.ValueTree(os.path.join(cachedir, 'table'), 'q')
|
||||||
|
if '' not in self.table:
|
||||||
|
self.initial_read_dir('')
|
||||||
|
self.table[''] = -1,
|
||||||
|
try:
|
||||||
|
self.rawfile = open(os.path.join(cachedir, 'raw'), 'r+b')
|
||||||
|
except IOError:
|
||||||
|
self.rawfile = open(os.path.join(cachedir, 'raw'), 'w+b')
|
||||||
|
ObjectFs.__init__(self, DirNode(self, ''))
|
||||||
|
self.readahead_at = None
|
||||||
|
greenhandler.autogreenlet(self.readahead)
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
self.table.close()
|
||||||
|
|
||||||
|
def readahead(self):
|
||||||
|
while True:
|
||||||
|
greensock.sleep(0.001)
|
||||||
|
while not self.readahead_at:
|
||||||
|
greensock.sleep(1)
|
||||||
|
path, blocknum = self.readahead_at
|
||||||
|
self.readahead_at = None
|
||||||
|
try:
|
||||||
|
self.readblock(path, blocknum, really=False)
|
||||||
|
except EOFError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def initial_read_dir(self, path):
|
||||||
|
print 'Reading initial directory structure...', path
|
||||||
|
dirname = os.path.join(self.srcdir, path)
|
||||||
|
for name in os.listdir(dirname):
|
||||||
|
filename = os.path.join(dirname, name)
|
||||||
|
st = os.stat(filename)
|
||||||
|
if stat.S_ISDIR(st.st_mode):
|
||||||
|
self.initial_read_dir(posixpath.join(path, name))
|
||||||
|
q = -1
|
||||||
|
else:
|
||||||
|
q = st.st_size
|
||||||
|
self.table[posixpath.join(path, name)] = q,
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
self.tablelock.acquire()
|
||||||
|
try:
|
||||||
|
return self.table[key]
|
||||||
|
finally:
|
||||||
|
self.tablelock.release()
|
||||||
|
|
||||||
|
def readblock(self, path, blocknum, really=True):
|
||||||
|
s = '%s/%d' % (path, blocknum)
|
||||||
|
try:
|
||||||
|
q, = self.table[s]
|
||||||
|
except KeyError:
|
||||||
|
print s
|
||||||
|
self.readahead_at = None
|
||||||
|
f = open(os.path.join(self.srcdir, path), 'rb')
|
||||||
|
f.seek(blocknum * BLOCKSIZE)
|
||||||
|
data = f.read(BLOCKSIZE)
|
||||||
|
f.close()
|
||||||
|
if not data:
|
||||||
|
q = -2
|
||||||
|
else:
|
||||||
|
data += '\x00' * (BLOCKSIZE - len(data))
|
||||||
|
self.rawfile.seek(0, 2)
|
||||||
|
q = self.rawfile.tell()
|
||||||
|
self.rawfile.write(data)
|
||||||
|
self.table[s] = q,
|
||||||
|
if q == -2:
|
||||||
|
raise EOFError
|
||||||
|
else:
|
||||||
|
if q == -2:
|
||||||
|
raise EOFError
|
||||||
|
if really:
|
||||||
|
self.rawfile.seek(q, 0)
|
||||||
|
data = self.rawfile.read(BLOCKSIZE)
|
||||||
|
else:
|
||||||
|
data = None
|
||||||
|
if self.readahead_at is None:
|
||||||
|
self.readahead_at = path, blocknum + 1
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
class Node(object):
|
||||||
|
|
||||||
|
def __init__(self, mfs, path):
|
||||||
|
self.mfs = mfs
|
||||||
|
self.path = path
|
||||||
|
|
||||||
|
class DirNode(Node):
|
||||||
|
|
||||||
|
def join(self, name):
|
||||||
|
path = posixpath.join(self.path, name)
|
||||||
|
q, = self.mfs.table[path]
|
||||||
|
if q == -1:
|
||||||
|
return DirNode(self.mfs, path)
|
||||||
|
else:
|
||||||
|
return FileNode(self.mfs, path)
|
||||||
|
|
||||||
|
def listdir(self):
|
||||||
|
result = []
|
||||||
|
for key, value in self.mfs.table.iteritemsfrom(self.path):
|
||||||
|
if not key.startswith(self.path):
|
||||||
|
break
|
||||||
|
tail = key[len(self.path):].lstrip('/')
|
||||||
|
if tail and '/' not in tail:
|
||||||
|
result.append(tail)
|
||||||
|
return result
|
||||||
|
|
||||||
|
class FileNode(Node):
|
||||||
|
|
||||||
|
def size(self):
|
||||||
|
q, = self.mfs.table[self.path]
|
||||||
|
return q
|
||||||
|
|
||||||
|
def read(self):
|
||||||
|
return FileStream(self.mfs, self.path)
|
||||||
|
|
||||||
|
class FileStream(object):
|
||||||
|
|
||||||
|
def __init__(self, mfs, path):
|
||||||
|
self.mfs = mfs
|
||||||
|
self.path = path
|
||||||
|
self.pos = 0
|
||||||
|
self.size, = self.mfs.table[path]
|
||||||
|
|
||||||
|
def seek(self, p):
|
||||||
|
self.pos = p
|
||||||
|
|
||||||
|
def read(self, count):
|
||||||
|
result = []
|
||||||
|
end = min(self.pos + count, self.size)
|
||||||
|
while self.pos < end:
|
||||||
|
blocknum, offset = divmod(self.pos, BLOCKSIZE)
|
||||||
|
data = self.mfs.readblock(self.path, blocknum)
|
||||||
|
data = data[offset:]
|
||||||
|
data = data[:end - self.pos]
|
||||||
|
assert len(data) > 0
|
||||||
|
result.append(data)
|
||||||
|
self.pos += len(data)
|
||||||
|
return ''.join(result)
|
||||||
|
|
||||||
|
# ____________________________________________________________
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
import sys
|
||||||
|
srcdir, cachedir, mountpoint = sys.argv[1:]
|
||||||
|
mirrorfs = MirrorFS(srcdir, cachedir)
|
||||||
|
try:
|
||||||
|
handler = Handler(mountpoint, mirrorfs)
|
||||||
|
greenhandler.add_handler(handler)
|
||||||
|
greenhandler.mainloop()
|
||||||
|
finally:
|
||||||
|
mirrorfs.close()
|
174
contrib/fuse/impl_b/pyfuse/objectfs.py
Normal file
174
contrib/fuse/impl_b/pyfuse/objectfs.py
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
from kernel import *
|
||||||
|
import stat, errno, os, time
|
||||||
|
from cStringIO import StringIO
|
||||||
|
from OrderedDict import OrderedDict
|
||||||
|
|
||||||
|
|
||||||
|
class ObjectFs:
|
||||||
|
"""A simple read-only file system based on Python objects.
|
||||||
|
|
||||||
|
Interface of Directory objects:
|
||||||
|
* join(name) returns a file or subdirectory object
|
||||||
|
* listdir() returns a list of names, or a list of (name, object)
|
||||||
|
|
||||||
|
join() is optional if listdir() returns a list of (name, object).
|
||||||
|
Alternatively, Directory objects can be plain dictionaries {name: object}.
|
||||||
|
|
||||||
|
Interface of File objects:
|
||||||
|
* size() returns the size
|
||||||
|
* read() returns the data
|
||||||
|
|
||||||
|
Alternatively, File objects can be plain strings.
|
||||||
|
|
||||||
|
Interface of SymLink objects:
|
||||||
|
* readlink() returns the symlink's target, as a string
|
||||||
|
"""
|
||||||
|
|
||||||
|
INFINITE = 86400.0
|
||||||
|
USE_DIR_CACHE = True
|
||||||
|
|
||||||
|
def __init__(self, rootnode):
|
||||||
|
self.nodes = {FUSE_ROOT_ID: rootnode}
|
||||||
|
if self.USE_DIR_CACHE:
|
||||||
|
self.dircache = {}
|
||||||
|
self.starttime = time.time()
|
||||||
|
self.uid = os.getuid()
|
||||||
|
self.gid = os.getgid()
|
||||||
|
self.umask = os.umask(0); os.umask(self.umask)
|
||||||
|
|
||||||
|
def newattr(self, s, ino, mode=0666):
|
||||||
|
if ino < 0:
|
||||||
|
ino = ~ino
|
||||||
|
return fuse_attr(ino = ino,
|
||||||
|
size = 0,
|
||||||
|
mode = s | (mode & ~self.umask),
|
||||||
|
nlink = 1, # even on dirs! this confuses 'find' in
|
||||||
|
# a good way :-)
|
||||||
|
atime = self.starttime,
|
||||||
|
mtime = self.starttime,
|
||||||
|
ctime = self.starttime,
|
||||||
|
uid = self.uid,
|
||||||
|
gid = self.gid)
|
||||||
|
|
||||||
|
def getnode(self, nodeid):
|
||||||
|
try:
|
||||||
|
return self.nodes[nodeid]
|
||||||
|
except KeyError:
|
||||||
|
raise IOError(errno.ESTALE, nodeid)
|
||||||
|
|
||||||
|
def getattr(self, node):
|
||||||
|
timeout = self.INFINITE
|
||||||
|
if isinstance(node, str):
|
||||||
|
attr = self.newattr(stat.S_IFREG, id(node))
|
||||||
|
attr.size = len(node)
|
||||||
|
elif hasattr(node, 'readlink'):
|
||||||
|
target = node.readlink()
|
||||||
|
attr = self.newattr(stat.S_IFLNK, id(node))
|
||||||
|
attr.size = len(target)
|
||||||
|
attr.mode |= 0777
|
||||||
|
elif hasattr(node, 'size'):
|
||||||
|
sz = node.size()
|
||||||
|
attr = self.newattr(stat.S_IFREG, id(node))
|
||||||
|
if sz is None:
|
||||||
|
timeout = 0
|
||||||
|
else:
|
||||||
|
attr.size = sz
|
||||||
|
else:
|
||||||
|
attr = self.newattr(stat.S_IFDIR, id(node), mode=0777)
|
||||||
|
#print 'getattr(%s) -> %s, %s' % (node, attr, timeout)
|
||||||
|
return attr, timeout
|
||||||
|
|
||||||
|
def getentries(self, node):
|
||||||
|
if isinstance(node, dict):
|
||||||
|
return node
|
||||||
|
try:
|
||||||
|
if not self.USE_DIR_CACHE:
|
||||||
|
raise KeyError
|
||||||
|
return self.dircache[node]
|
||||||
|
except KeyError:
|
||||||
|
entries = OrderedDict()
|
||||||
|
if hasattr(node, 'listdir'):
|
||||||
|
for name in node.listdir():
|
||||||
|
if isinstance(name, tuple):
|
||||||
|
name, subnode = name
|
||||||
|
else:
|
||||||
|
subnode = None
|
||||||
|
entries[name] = subnode
|
||||||
|
if self.USE_DIR_CACHE:
|
||||||
|
self.dircache[node] = entries
|
||||||
|
return entries
|
||||||
|
|
||||||
|
def listdir(self, node):
|
||||||
|
entries = self.getentries(node)
|
||||||
|
for name, subnode in entries.items():
|
||||||
|
if subnode is None:
|
||||||
|
subnode = node.join(name)
|
||||||
|
self.nodes[uid(subnode)] = subnode
|
||||||
|
entries[name] = subnode
|
||||||
|
if isinstance(subnode, str):
|
||||||
|
yield name, TYPE_REG
|
||||||
|
elif hasattr(subnode, 'readlink'):
|
||||||
|
yield name, TYPE_LNK
|
||||||
|
elif hasattr(subnode, 'size'):
|
||||||
|
yield name, TYPE_REG
|
||||||
|
else:
|
||||||
|
yield name, TYPE_DIR
|
||||||
|
|
||||||
|
def lookup(self, node, name):
|
||||||
|
entries = self.getentries(node)
|
||||||
|
try:
|
||||||
|
subnode = entries.get(name)
|
||||||
|
if subnode is None:
|
||||||
|
if hasattr(node, 'join'):
|
||||||
|
subnode = node.join(name)
|
||||||
|
entries[name] = subnode
|
||||||
|
else:
|
||||||
|
raise KeyError
|
||||||
|
except KeyError:
|
||||||
|
raise IOError(errno.ENOENT, name)
|
||||||
|
else:
|
||||||
|
return self.reply(subnode)
|
||||||
|
|
||||||
|
def reply(self, node):
|
||||||
|
res = uid(node)
|
||||||
|
self.nodes[res] = node
|
||||||
|
return res, self.INFINITE
|
||||||
|
|
||||||
|
def open(self, node, mode):
|
||||||
|
if not isinstance(node, str):
|
||||||
|
node = node.read()
|
||||||
|
if not hasattr(node, 'read'):
|
||||||
|
node = StringIO(node)
|
||||||
|
return node
|
||||||
|
|
||||||
|
def readlink(self, node):
|
||||||
|
return node.readlink()
|
||||||
|
|
||||||
|
def getxattrs(self, node):
|
||||||
|
return getattr(node, '__dict__', {})
|
||||||
|
|
||||||
|
# ____________________________________________________________
|
||||||
|
|
||||||
|
import struct
|
||||||
|
try:
|
||||||
|
HUGEVAL = 256 ** struct.calcsize('P')
|
||||||
|
except struct.error:
|
||||||
|
HUGEVAL = 0
|
||||||
|
|
||||||
|
def fixid(result):
|
||||||
|
if result < 0:
|
||||||
|
result += HUGEVAL
|
||||||
|
return result
|
||||||
|
|
||||||
|
def uid(obj):
|
||||||
|
"""
|
||||||
|
Return the id of an object as an unsigned number so that its hex
|
||||||
|
representation makes sense
|
||||||
|
"""
|
||||||
|
return fixid(id(obj))
|
||||||
|
|
||||||
|
class SymLink(object):
|
||||||
|
def __init__(self, target):
|
||||||
|
self.target = target
|
||||||
|
def readlink(self):
|
||||||
|
return self.target
|
63
contrib/fuse/impl_b/pyfuse/pairtype.py
Normal file
63
contrib/fuse/impl_b/pyfuse/pairtype.py
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
"""
|
||||||
|
Two magic tricks for classes:
|
||||||
|
|
||||||
|
class X:
|
||||||
|
__metaclass__ = extendabletype
|
||||||
|
...
|
||||||
|
|
||||||
|
# in some other file...
|
||||||
|
class __extend__(X):
|
||||||
|
... # and here you can add new methods and class attributes to X
|
||||||
|
|
||||||
|
Mostly useful together with the second trick, which lets you build
|
||||||
|
methods whose 'self' is a pair of objects instead of just one:
|
||||||
|
|
||||||
|
class __extend__(pairtype(X, Y)):
|
||||||
|
attribute = 42
|
||||||
|
def method((x, y), other, arguments):
|
||||||
|
...
|
||||||
|
|
||||||
|
pair(x, y).attribute
|
||||||
|
pair(x, y).method(other, arguments)
|
||||||
|
|
||||||
|
This finds methods and class attributes based on the actual
|
||||||
|
class of both objects that go into the pair(), with the usual
|
||||||
|
rules of method/attribute overriding in (pairs of) subclasses.
|
||||||
|
|
||||||
|
For more information, see test_pairtype.
|
||||||
|
"""
|
||||||
|
|
||||||
|
class extendabletype(type):
|
||||||
|
"""A type with a syntax trick: 'class __extend__(t)' actually extends
|
||||||
|
the definition of 't' instead of creating a new subclass."""
|
||||||
|
def __new__(cls, name, bases, dict):
|
||||||
|
if name == '__extend__':
|
||||||
|
for cls in bases:
|
||||||
|
for key, value in dict.items():
|
||||||
|
if key == '__module__':
|
||||||
|
continue
|
||||||
|
# XXX do we need to provide something more for pickling?
|
||||||
|
setattr(cls, key, value)
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
return super(extendabletype, cls).__new__(cls, name, bases, dict)
|
||||||
|
|
||||||
|
|
||||||
|
def pair(a, b):
|
||||||
|
"""Return a pair object."""
|
||||||
|
tp = pairtype(a.__class__, b.__class__)
|
||||||
|
return tp((a, b)) # tp is a subclass of tuple
|
||||||
|
|
||||||
|
pairtypecache = {}
|
||||||
|
|
||||||
|
def pairtype(cls1, cls2):
|
||||||
|
"""type(pair(a,b)) is pairtype(a.__class__, b.__class__)."""
|
||||||
|
try:
|
||||||
|
pair = pairtypecache[cls1, cls2]
|
||||||
|
except KeyError:
|
||||||
|
name = 'pairtype(%s, %s)' % (cls1.__name__, cls2.__name__)
|
||||||
|
bases1 = [pairtype(base1, cls2) for base1 in cls1.__bases__]
|
||||||
|
bases2 = [pairtype(cls1, base2) for base2 in cls2.__bases__]
|
||||||
|
bases = tuple(bases1 + bases2) or (tuple,) # 'tuple': ultimate base
|
||||||
|
pair = pairtypecache[cls1, cls2] = extendabletype(name, bases, {})
|
||||||
|
return pair
|
92
contrib/fuse/impl_b/pyfuse/pathfs.py
Normal file
92
contrib/fuse/impl_b/pyfuse/pathfs.py
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
from kernel import *
|
||||||
|
import errno, posixpath, os
|
||||||
|
|
||||||
|
|
||||||
|
class PathFs(object):
|
||||||
|
"""Base class for a read-write FUSE file system interface
|
||||||
|
whose underlying content is best accessed with '/'-separated
|
||||||
|
string paths.
|
||||||
|
"""
|
||||||
|
uid = os.getuid()
|
||||||
|
gid = os.getgid()
|
||||||
|
umask = os.umask(0); os.umask(umask)
|
||||||
|
timeout = 86400.0
|
||||||
|
|
||||||
|
def __init__(self, root=''):
|
||||||
|
self._paths = {FUSE_ROOT_ID: root}
|
||||||
|
self._path2id = {root: FUSE_ROOT_ID}
|
||||||
|
self._nextid = FUSE_ROOT_ID + 1
|
||||||
|
|
||||||
|
def getnode(self, nodeid):
|
||||||
|
try:
|
||||||
|
return self._paths[nodeid]
|
||||||
|
except KeyError:
|
||||||
|
raise IOError(errno.ESTALE, nodeid)
|
||||||
|
|
||||||
|
def forget(self, nodeid):
|
||||||
|
try:
|
||||||
|
p = self._paths.pop(nodeid)
|
||||||
|
del self._path2id[p]
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def cachepath(self, path):
|
||||||
|
if path in self._path2id:
|
||||||
|
return self._path2id[path]
|
||||||
|
id = self._nextid
|
||||||
|
self._nextid += 1
|
||||||
|
self._paths[id] = path
|
||||||
|
self._path2id[path] = id
|
||||||
|
return id
|
||||||
|
|
||||||
|
def mkattr(self, path, size, st_kind, mode, time):
|
||||||
|
attr = fuse_attr(ino = self._path2id[path],
|
||||||
|
size = size,
|
||||||
|
mode = st_kind | (mode & ~self.umask),
|
||||||
|
nlink = 1, # even on dirs! this confuses 'find' in
|
||||||
|
# a good way :-)
|
||||||
|
atime = time,
|
||||||
|
mtime = time,
|
||||||
|
ctime = time,
|
||||||
|
uid = self.uid,
|
||||||
|
gid = self.gid)
|
||||||
|
return attr, self.timeout
|
||||||
|
|
||||||
|
def lookup(self, path, name):
|
||||||
|
npath = posixpath.join(path, name)
|
||||||
|
if not self.check_path(npath):
|
||||||
|
raise IOError(errno.ENOENT, name)
|
||||||
|
return self.cachepath(npath), self.timeout
|
||||||
|
|
||||||
|
def mknod(self, path, name, mode):
|
||||||
|
npath = posixpath.join(path, name)
|
||||||
|
self.mknod_path(npath, mode)
|
||||||
|
return self.cachepath(npath), self.timeout
|
||||||
|
|
||||||
|
def mkdir(self, path, name, mode):
|
||||||
|
npath = posixpath.join(path, name)
|
||||||
|
self.mkdir_path(npath, mode)
|
||||||
|
return self.cachepath(npath), self.timeout
|
||||||
|
|
||||||
|
def unlink(self, path, name):
|
||||||
|
npath = posixpath.join(path, name)
|
||||||
|
self.unlink_path(npath)
|
||||||
|
|
||||||
|
def rmdir(self, path, name):
|
||||||
|
npath = posixpath.join(path, name)
|
||||||
|
self.rmdir_path(npath)
|
||||||
|
|
||||||
|
def rename(self, oldpath, oldname, newpath, newname):
|
||||||
|
noldpath = posixpath.join(oldpath, oldname)
|
||||||
|
nnewpath = posixpath.join(newpath, newname)
|
||||||
|
if not self.rename_path(noldpath, nnewpath):
|
||||||
|
raise IOError(errno.ENOENT, oldname)
|
||||||
|
# fix all paths in the cache
|
||||||
|
N = len(noldpath)
|
||||||
|
for id, path in self._paths.items():
|
||||||
|
if path.startswith(noldpath):
|
||||||
|
if len(path) == N or path[N] == '/':
|
||||||
|
del self._path2id[path]
|
||||||
|
path = nnewpath + path[N:]
|
||||||
|
self._paths[id] = path
|
||||||
|
self._path2id[path] = id
|
181
contrib/fuse/impl_b/pyfuse/pysvnfs.py
Normal file
181
contrib/fuse/impl_b/pyfuse/pysvnfs.py
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
from kernel import *
|
||||||
|
import errno, posixpath, weakref
|
||||||
|
from time import time as now
|
||||||
|
from stat import S_IFDIR, S_IFREG, S_IFMT
|
||||||
|
from cStringIO import StringIO
|
||||||
|
from handler import Handler
|
||||||
|
from pathfs import PathFs
|
||||||
|
from pysvn.ra_filesystem import SvnRepositoryFilesystem
|
||||||
|
import pysvn.date
|
||||||
|
|
||||||
|
|
||||||
|
class SvnFS(PathFs):
|
||||||
|
|
||||||
|
def __init__(self, svnurl, root=''):
|
||||||
|
super(SvnFS, self).__init__(root)
|
||||||
|
self.svnurl = svnurl
|
||||||
|
self.openfiles = weakref.WeakValueDictionary()
|
||||||
|
self.creationtimes = {}
|
||||||
|
self.do_open()
|
||||||
|
|
||||||
|
def do_open(self, rev='HEAD'):
|
||||||
|
self.fs = SvnRepositoryFilesystem(svnurl, rev)
|
||||||
|
|
||||||
|
def do_commit(self, msg):
|
||||||
|
rev = self.fs.commit(msg)
|
||||||
|
if rev is None:
|
||||||
|
print '* no changes.'
|
||||||
|
else:
|
||||||
|
print '* checked in revision %d.' % (rev,)
|
||||||
|
self.do_open()
|
||||||
|
|
||||||
|
def do_status(self, path=''):
|
||||||
|
print '* status'
|
||||||
|
result = []
|
||||||
|
if path and not path.endswith('/'):
|
||||||
|
path += '/'
|
||||||
|
for delta in self.fs._compute_deltas():
|
||||||
|
if delta.path.startswith(path):
|
||||||
|
if delta.oldrev is None:
|
||||||
|
c = 'A'
|
||||||
|
elif delta.newrev is None:
|
||||||
|
c = 'D'
|
||||||
|
else:
|
||||||
|
c = 'M'
|
||||||
|
result.append(' %s %s\n' % (c, delta.path[len(path):]))
|
||||||
|
return ''.join(result)
|
||||||
|
|
||||||
|
def getattr(self, path):
|
||||||
|
stat = self.fs.stat(path)
|
||||||
|
if stat['svn:entry:kind'] == 'dir':
|
||||||
|
s = S_IFDIR
|
||||||
|
mode = 0777
|
||||||
|
else:
|
||||||
|
s = S_IFREG
|
||||||
|
mode = 0666
|
||||||
|
try:
|
||||||
|
time = pysvn.date.decode(stat['svn:entry:committed-date'])
|
||||||
|
except KeyError:
|
||||||
|
try:
|
||||||
|
time = self.creationtimes[path]
|
||||||
|
except KeyError:
|
||||||
|
time = self.creationtimes[path] = now()
|
||||||
|
return self.mkattr(path,
|
||||||
|
size = stat.get('svn:entry:size', 0),
|
||||||
|
st_kind = s,
|
||||||
|
mode = mode,
|
||||||
|
time = time)
|
||||||
|
|
||||||
|
def setattr(self, path, mode, uid, gid, size, atime, mtime):
|
||||||
|
if size is not None:
|
||||||
|
data = self.fs.read(path)
|
||||||
|
if size < len(data):
|
||||||
|
self.fs.write(path, data[:size])
|
||||||
|
elif size > len(data):
|
||||||
|
self.fs.write(path, data + '\x00' * (size - len(data)))
|
||||||
|
|
||||||
|
def listdir(self, path):
|
||||||
|
for name in self.fs.listdir(path):
|
||||||
|
kind = self.fs.check_path(posixpath.join(path, name))
|
||||||
|
if kind == 'dir':
|
||||||
|
yield name, TYPE_DIR
|
||||||
|
else:
|
||||||
|
yield name, TYPE_REG
|
||||||
|
|
||||||
|
def check_path(self, path):
|
||||||
|
kind = self.fs.check_path(path)
|
||||||
|
return kind is not None
|
||||||
|
|
||||||
|
def open(self, path, mode):
|
||||||
|
try:
|
||||||
|
of = self.openfiles[path]
|
||||||
|
except KeyError:
|
||||||
|
of = self.openfiles[path] = OpenFile(self.fs.read(path))
|
||||||
|
return of, FOPEN_KEEP_CACHE
|
||||||
|
|
||||||
|
def modified(self, path):
|
||||||
|
try:
|
||||||
|
of = self.openfiles[path]
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
self.fs.write(path, of.f.getvalue())
|
||||||
|
|
||||||
|
def mknod_path(self, path, mode):
|
||||||
|
self.fs.add(path)
|
||||||
|
|
||||||
|
def mkdir_path(self, path, mode):
|
||||||
|
self.fs.mkdir(path)
|
||||||
|
|
||||||
|
def unlink_path(self, path):
|
||||||
|
self.fs.unlink(path)
|
||||||
|
|
||||||
|
def rmdir_path(self, path):
|
||||||
|
self.fs.rmdir(path)
|
||||||
|
|
||||||
|
def rename_path(self, oldpath, newpath):
|
||||||
|
kind = self.fs.check_path(oldpath)
|
||||||
|
if kind is None:
|
||||||
|
return False
|
||||||
|
self.fs.move(oldpath, newpath, kind)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def getxattrs(self, path):
|
||||||
|
return XAttrs(self, path)
|
||||||
|
|
||||||
|
|
||||||
|
class OpenFile:
|
||||||
|
def __init__(self, data=''):
|
||||||
|
self.f = StringIO()
|
||||||
|
self.f.write(data)
|
||||||
|
self.f.seek(0)
|
||||||
|
|
||||||
|
def seek(self, pos):
|
||||||
|
self.f.seek(pos)
|
||||||
|
|
||||||
|
def read(self, sz):
|
||||||
|
return self.f.read(sz)
|
||||||
|
|
||||||
|
def write(self, buf):
|
||||||
|
self.f.write(buf)
|
||||||
|
|
||||||
|
|
||||||
|
class XAttrs:
|
||||||
|
def __init__(self, svnfs, path):
|
||||||
|
self.svnfs = svnfs
|
||||||
|
self.path = path
|
||||||
|
|
||||||
|
def keys(self):
|
||||||
|
return []
|
||||||
|
|
||||||
|
def __getitem__(self, key):
|
||||||
|
if key == 'status':
|
||||||
|
return self.svnfs.do_status(self.path)
|
||||||
|
raise KeyError(key)
|
||||||
|
|
||||||
|
def __setitem__(self, key, value):
|
||||||
|
if key == 'commit' and self.path == '':
|
||||||
|
self.svnfs.do_commit(value)
|
||||||
|
elif key == 'update' and self.path == '':
|
||||||
|
if self.svnfs.fs.modified():
|
||||||
|
raise IOError(errno.EPERM, "there are local changes")
|
||||||
|
if value == '':
|
||||||
|
rev = 'HEAD'
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
rev = int(value)
|
||||||
|
except ValueError:
|
||||||
|
raise IOError(errno.EPERM, "invalid revision number")
|
||||||
|
self.svnfs.do_open(rev)
|
||||||
|
else:
|
||||||
|
raise KeyError(key)
|
||||||
|
|
||||||
|
def __delitem__(self, key):
|
||||||
|
raise KeyError(key)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
import sys
|
||||||
|
svnurl, mountpoint = sys.argv[1:]
|
||||||
|
handler = Handler(mountpoint, SvnFS(svnurl))
|
||||||
|
handler.loop_forever()
|
142
contrib/fuse/impl_b/pyfuse/r_svnfs.py
Normal file
142
contrib/fuse/impl_b/pyfuse/r_svnfs.py
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
"""
|
||||||
|
A read-only svn fs showing all the revisions in subdirectories.
|
||||||
|
"""
|
||||||
|
from objectfs import ObjectFs, SymLink
|
||||||
|
from handler import Handler
|
||||||
|
from pysvn.ra import connect
|
||||||
|
from pysvn.date import decode
|
||||||
|
import errno, posixpath, time
|
||||||
|
|
||||||
|
|
||||||
|
#USE_SYMLINKS = 0 # they are wrong if the original file had another path
|
||||||
|
|
||||||
|
# use getfattr -d filename to see the node's attributes, which include
|
||||||
|
# information like the revision at which the file was last modified
|
||||||
|
|
||||||
|
|
||||||
|
class Root:
|
||||||
|
def __init__(self, svnurl):
|
||||||
|
self.svnurl = svnurl
|
||||||
|
self.ra = connect(svnurl)
|
||||||
|
self.head = self.ra.get_latest_rev()
|
||||||
|
|
||||||
|
def listdir(self):
|
||||||
|
for rev in range(1, self.head+1):
|
||||||
|
yield str(rev)
|
||||||
|
yield 'HEAD'
|
||||||
|
|
||||||
|
def join(self, name):
|
||||||
|
try:
|
||||||
|
rev = int(name)
|
||||||
|
except ValueError:
|
||||||
|
if name == 'HEAD':
|
||||||
|
return SymLink(str(self.head))
|
||||||
|
else:
|
||||||
|
raise KeyError(name)
|
||||||
|
return TopLevelDir(self.ra, rev, rev, '')
|
||||||
|
|
||||||
|
|
||||||
|
class Node:
|
||||||
|
def __init__(self, ra, rev, last_changed_rev, path):
|
||||||
|
self.ra = ra
|
||||||
|
self.rev = rev
|
||||||
|
self.last_changed_rev = last_changed_rev
|
||||||
|
self.path = path
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<%s %d/%s>' % (self.__class__.__name__, self.rev, self.path)
|
||||||
|
|
||||||
|
class Dir(Node):
|
||||||
|
def listdir(self):
|
||||||
|
rev, props, entries = self.ra.get_dir(self.path, self.rev,
|
||||||
|
want_props = False)
|
||||||
|
for key, stats in entries.items():
|
||||||
|
yield key, getnode(self.ra, self.rev,
|
||||||
|
posixpath.join(self.path, key), stats)
|
||||||
|
|
||||||
|
class File(Node):
|
||||||
|
def __init__(self, ra, rev, last_changed_rev, path, size):
|
||||||
|
Node.__init__(self, ra, rev, last_changed_rev, path)
|
||||||
|
self.filesize = size
|
||||||
|
|
||||||
|
def size(self):
|
||||||
|
return self.filesize
|
||||||
|
|
||||||
|
def read(self):
|
||||||
|
checksum, rev, props, data = self.ra.get_file(self.path, self.rev,
|
||||||
|
want_props = False)
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
class TopLevelDir(Dir):
|
||||||
|
def listdir(self):
|
||||||
|
for item in Dir.listdir(self):
|
||||||
|
yield item
|
||||||
|
yield 'svn:log', Log(self.ra, self.rev)
|
||||||
|
|
||||||
|
class Log:
|
||||||
|
|
||||||
|
def __init__(self, ra, rev):
|
||||||
|
self.ra = ra
|
||||||
|
self.rev = rev
|
||||||
|
|
||||||
|
def getlogentry(self):
|
||||||
|
try:
|
||||||
|
return self.logentry
|
||||||
|
except AttributeError:
|
||||||
|
logentries = self.ra.log('', startrev=self.rev, endrev=self.rev)
|
||||||
|
try:
|
||||||
|
[self.logentry] = logentries
|
||||||
|
except ValueError:
|
||||||
|
self.logentry = None
|
||||||
|
return self.logentry
|
||||||
|
|
||||||
|
def size(self):
|
||||||
|
return len(self.read())
|
||||||
|
|
||||||
|
def read(self):
|
||||||
|
logentry = self.getlogentry()
|
||||||
|
if logentry is None:
|
||||||
|
return 'r%d | (no change here)\n' % (self.rev,)
|
||||||
|
datetuple = time.gmtime(decode(logentry.date))
|
||||||
|
date = time.strftime("%c", datetuple)
|
||||||
|
return 'r%d | %s | %s\n\n%s' % (self.rev,
|
||||||
|
logentry.author,
|
||||||
|
date,
|
||||||
|
logentry.message)
|
||||||
|
|
||||||
|
|
||||||
|
if 0:
|
||||||
|
pass
|
||||||
|
##if USE_SYMLINKS:
|
||||||
|
## def getnode(ra, rev, path, stats):
|
||||||
|
## committed_rev = stats['svn:entry:committed-rev']
|
||||||
|
## if committed_rev == rev:
|
||||||
|
## kind = stats['svn:entry:kind']
|
||||||
|
## if kind == 'file':
|
||||||
|
## return File(ra, rev, path, stats['svn:entry:size'])
|
||||||
|
## elif kind == 'dir':
|
||||||
|
## return Dir(ra, rev, path)
|
||||||
|
## else:
|
||||||
|
## raise IOError(errno.EINVAL, "kind %r" % (kind,))
|
||||||
|
## else:
|
||||||
|
## depth = path.count('/')
|
||||||
|
## return SymLink('../' * depth + '../%d/%s' % (committed_rev, path))
|
||||||
|
else:
|
||||||
|
def getnode(ra, rev, path, stats):
|
||||||
|
last_changed_rev = stats['svn:entry:committed-rev']
|
||||||
|
kind = stats['svn:entry:kind']
|
||||||
|
if kind == 'file':
|
||||||
|
return File(ra, rev, last_changed_rev, path,
|
||||||
|
stats['svn:entry:size'])
|
||||||
|
elif kind == 'dir':
|
||||||
|
return Dir(ra, rev, last_changed_rev, path)
|
||||||
|
else:
|
||||||
|
raise IOError(errno.EINVAL, "kind %r" % (kind,))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
import sys
|
||||||
|
svnurl, mountpoint = sys.argv[1:]
|
||||||
|
handler = Handler(mountpoint, ObjectFs(Root(svnurl)))
|
||||||
|
handler.loop_forever()
|
305
contrib/fuse/impl_b/pyfuse/rwobjectfs.py
Normal file
305
contrib/fuse/impl_b/pyfuse/rwobjectfs.py
Normal file
@ -0,0 +1,305 @@
|
|||||||
|
from kernel import *
|
||||||
|
import stat, errno, os, time
|
||||||
|
from cStringIO import StringIO
|
||||||
|
from OrderedDict import OrderedDict
|
||||||
|
|
||||||
|
INFINITE = 86400.0
|
||||||
|
|
||||||
|
|
||||||
|
class Wrapper(object):
|
||||||
|
def __init__(self, obj):
|
||||||
|
self.obj = obj
|
||||||
|
|
||||||
|
def getuid(self):
|
||||||
|
return uid(self.obj)
|
||||||
|
|
||||||
|
def __hash__(self):
|
||||||
|
return hash(self.obj)
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return self.obj == other
|
||||||
|
|
||||||
|
def __ne__(self, other):
|
||||||
|
return self.obj != other
|
||||||
|
|
||||||
|
|
||||||
|
class BaseDir(object):
|
||||||
|
|
||||||
|
def join(self, name):
|
||||||
|
"Return a file or subdirectory object"
|
||||||
|
for item in self.listdir():
|
||||||
|
if isinstance(item, tuple):
|
||||||
|
subname, subnode = item
|
||||||
|
if subname == name:
|
||||||
|
return subnode
|
||||||
|
raise KeyError(name)
|
||||||
|
|
||||||
|
def listdir(self):
|
||||||
|
"Return a list of names, or a list of (name, object)"
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def create(self, name):
|
||||||
|
"Create a file"
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def mkdir(self, name):
|
||||||
|
"Create a subdirectory"
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def symlink(self, name, target):
|
||||||
|
"Create a symbolic link"
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def unlink(self, name):
|
||||||
|
"Remove a file or subdirectory."
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def rename(self, newname, olddirnode, oldname):
|
||||||
|
"Move another node into this directory."
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def getuid(self):
|
||||||
|
return uid(self)
|
||||||
|
|
||||||
|
def getattr(self, fs):
|
||||||
|
return fs.newattr(stat.S_IFDIR, self.getuid(), mode=0777), INFINITE
|
||||||
|
|
||||||
|
def setattr(self, **kwds):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def getentries(self):
|
||||||
|
entries = OrderedDict()
|
||||||
|
for name in self.listdir():
|
||||||
|
if isinstance(name, tuple):
|
||||||
|
name, subnode = name
|
||||||
|
else:
|
||||||
|
subnode = None
|
||||||
|
entries[name] = subnode
|
||||||
|
return entries
|
||||||
|
|
||||||
|
|
||||||
|
class BaseFile(object):
|
||||||
|
|
||||||
|
def size(self):
|
||||||
|
"Return the size of the file, or None if not known yet"
|
||||||
|
f = self.open()
|
||||||
|
if isinstance(f, str):
|
||||||
|
return len(f)
|
||||||
|
f.seek(0, 2)
|
||||||
|
return f.tell()
|
||||||
|
|
||||||
|
def open(self):
|
||||||
|
"Return the content as a string or a file-like object"
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def getuid(self):
|
||||||
|
return uid(self)
|
||||||
|
|
||||||
|
def getattr(self, fs):
|
||||||
|
sz = self.size()
|
||||||
|
attr = fs.newattr(stat.S_IFREG, self.getuid())
|
||||||
|
if sz is None:
|
||||||
|
timeout = 0
|
||||||
|
else:
|
||||||
|
attr.size = sz
|
||||||
|
timeout = INFINITE
|
||||||
|
return attr, timeout
|
||||||
|
|
||||||
|
def setattr(self, size, **kwds):
|
||||||
|
f = self.open()
|
||||||
|
if self.size() == size:
|
||||||
|
return
|
||||||
|
if isinstance(f, str):
|
||||||
|
raise IOError(errno.EPERM)
|
||||||
|
f.seek(size)
|
||||||
|
f.truncate()
|
||||||
|
|
||||||
|
|
||||||
|
class BaseSymLink(object):
|
||||||
|
|
||||||
|
def readlink(self):
|
||||||
|
"Return the symlink's target, as a string"
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def getuid(self):
|
||||||
|
return uid(self)
|
||||||
|
|
||||||
|
def getattr(self, fs):
|
||||||
|
target = self.readlink()
|
||||||
|
attr = fs.newattr(stat.S_IFLNK, self.getuid())
|
||||||
|
attr.size = len(target)
|
||||||
|
attr.mode |= 0777
|
||||||
|
return attr, INFINITE
|
||||||
|
|
||||||
|
def setattr(self, **kwds):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# ____________________________________________________________
|
||||||
|
|
||||||
|
class Dir(BaseDir):
|
||||||
|
def __init__(self, **contents):
|
||||||
|
self.contents = contents
|
||||||
|
def listdir(self):
|
||||||
|
return self.contents.items()
|
||||||
|
def join(self, name):
|
||||||
|
return self.contents[name]
|
||||||
|
def create(self, fs, name):
|
||||||
|
node = fs.File()
|
||||||
|
self.contents[name] = node
|
||||||
|
return node
|
||||||
|
def mkdir(self, fs, name):
|
||||||
|
node = fs.Dir()
|
||||||
|
self.contents[name] = node
|
||||||
|
return node
|
||||||
|
def symlink(self, fs, name, target):
|
||||||
|
node = fs.SymLink(target)
|
||||||
|
self.contents[name] = node
|
||||||
|
return node
|
||||||
|
def unlink(self, name):
|
||||||
|
del self.contents[name]
|
||||||
|
def rename(self, newname, olddirnode, oldname):
|
||||||
|
oldnode = olddirnode.join(oldname)
|
||||||
|
olddirnode.unlink(oldname)
|
||||||
|
self.contents[newname] = oldnode
|
||||||
|
|
||||||
|
class File(BaseFile):
|
||||||
|
def __init__(self):
|
||||||
|
self.data = StringIO()
|
||||||
|
def size(self):
|
||||||
|
self.data.seek(0, 2)
|
||||||
|
return self.data.tell()
|
||||||
|
def open(self):
|
||||||
|
return self.data
|
||||||
|
|
||||||
|
class SymLink(BaseFile):
|
||||||
|
def __init__(self, target):
|
||||||
|
self.target = target
|
||||||
|
def readlink(self):
|
||||||
|
return self.target
|
||||||
|
|
||||||
|
# ____________________________________________________________
|
||||||
|
|
||||||
|
|
||||||
|
class RWObjectFs(object):
|
||||||
|
"""A simple read-write file system based on Python objects."""
|
||||||
|
|
||||||
|
UID = os.getuid()
|
||||||
|
GID = os.getgid()
|
||||||
|
UMASK = os.umask(0); os.umask(UMASK)
|
||||||
|
|
||||||
|
Dir = Dir
|
||||||
|
File = File
|
||||||
|
SymLink = SymLink
|
||||||
|
|
||||||
|
def __init__(self, rootnode):
|
||||||
|
self.nodes = {FUSE_ROOT_ID: rootnode}
|
||||||
|
self.starttime = time.time()
|
||||||
|
|
||||||
|
def newattr(self, s, ino, mode=0666):
|
||||||
|
return fuse_attr(ino = ino,
|
||||||
|
size = 0,
|
||||||
|
mode = s | (mode & ~self.UMASK),
|
||||||
|
nlink = 1, # even on dirs! this confuses 'find' in
|
||||||
|
# a good way :-)
|
||||||
|
atime = self.starttime,
|
||||||
|
mtime = self.starttime,
|
||||||
|
ctime = self.starttime,
|
||||||
|
uid = self.UID,
|
||||||
|
gid = self.GID)
|
||||||
|
|
||||||
|
def getnode(self, nodeid):
|
||||||
|
try:
|
||||||
|
return self.nodes[nodeid]
|
||||||
|
except KeyError:
|
||||||
|
raise IOError(errno.ESTALE, nodeid)
|
||||||
|
|
||||||
|
def getattr(self, node):
|
||||||
|
return node.getattr(self)
|
||||||
|
|
||||||
|
def setattr(self, node, mode, uid, gid, size, atime, mtime):
|
||||||
|
node.setattr(mode=mode, uid=uid, gid=gid, size=size,
|
||||||
|
atime=atime, mtime=mtime)
|
||||||
|
|
||||||
|
def listdir(self, node):
|
||||||
|
entries = node.getentries()
|
||||||
|
for name, subnode in entries.items():
|
||||||
|
if subnode is None:
|
||||||
|
subnode = node.join(name)
|
||||||
|
self.nodes[uid(subnode)] = subnode
|
||||||
|
entries[name] = subnode
|
||||||
|
if isinstance(subnode, str):
|
||||||
|
yield name, TYPE_REG
|
||||||
|
elif hasattr(subnode, 'readlink'):
|
||||||
|
yield name, TYPE_LNK
|
||||||
|
elif hasattr(subnode, 'size'):
|
||||||
|
yield name, TYPE_REG
|
||||||
|
else:
|
||||||
|
yield name, TYPE_DIR
|
||||||
|
|
||||||
|
def lookup(self, node, name):
|
||||||
|
try:
|
||||||
|
subnode = node.join(name)
|
||||||
|
except KeyError:
|
||||||
|
raise IOError(errno.ENOENT, name)
|
||||||
|
else:
|
||||||
|
res = uid(subnode)
|
||||||
|
self.nodes[res] = subnode
|
||||||
|
return res, INFINITE
|
||||||
|
|
||||||
|
def mknod(self, dirnode, filename, mode):
|
||||||
|
node = dirnode.create(filename)
|
||||||
|
return self.newnodeid(node), INFINITE
|
||||||
|
|
||||||
|
def mkdir(self, dirnode, subdirname, mode):
|
||||||
|
node = dirnode.mkdir(subdirname)
|
||||||
|
return self.newnodeid(node), INFINITE
|
||||||
|
|
||||||
|
def symlink(self, dirnode, linkname, target):
|
||||||
|
node = dirnode.symlink(linkname, target)
|
||||||
|
return self.newnodeid(node), INFINITE
|
||||||
|
|
||||||
|
def unlink(self, dirnode, filename):
|
||||||
|
try:
|
||||||
|
dirnode.unlink(filename)
|
||||||
|
except KeyError:
|
||||||
|
raise IOError(errno.ENOENT, filename)
|
||||||
|
|
||||||
|
rmdir = unlink
|
||||||
|
|
||||||
|
def open(self, node, mode):
|
||||||
|
f = node.open()
|
||||||
|
if isinstance(f, str):
|
||||||
|
f = StringIO(f)
|
||||||
|
return f
|
||||||
|
|
||||||
|
def readlink(self, node):
|
||||||
|
return node.readlink()
|
||||||
|
|
||||||
|
def rename(self, olddirnode, oldname, newdirnode, newname):
|
||||||
|
try:
|
||||||
|
newdirnode.rename(newname, olddirnode, oldname)
|
||||||
|
except KeyError:
|
||||||
|
raise IOError(errno.ENOENT, oldname)
|
||||||
|
|
||||||
|
def getxattrs(self, node):
|
||||||
|
return getattr(node, '__dict__', {})
|
||||||
|
|
||||||
|
# ____________________________________________________________
|
||||||
|
|
||||||
|
import struct
|
||||||
|
try:
|
||||||
|
HUGEVAL = 256 ** struct.calcsize('P')
|
||||||
|
except struct.error:
|
||||||
|
HUGEVAL = 0
|
||||||
|
|
||||||
|
def fixid(result):
|
||||||
|
if result < 0:
|
||||||
|
result += HUGEVAL
|
||||||
|
return result
|
||||||
|
|
||||||
|
def uid(obj):
|
||||||
|
"""
|
||||||
|
Return the id of an object as an unsigned number so that its hex
|
||||||
|
representation makes sense
|
||||||
|
"""
|
||||||
|
return fixid(id(obj))
|
42
contrib/fuse/impl_b/pyfuse/svnfs.py
Normal file
42
contrib/fuse/impl_b/pyfuse/svnfs.py
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import py
|
||||||
|
from handler import Handler
|
||||||
|
from objectfs import ObjectFs
|
||||||
|
|
||||||
|
|
||||||
|
class SvnDir:
|
||||||
|
def __init__(self, path):
|
||||||
|
self.path = path
|
||||||
|
|
||||||
|
def listdir(self):
|
||||||
|
for p in self.path.listdir():
|
||||||
|
if p.check(dir=1):
|
||||||
|
cls = SvnDir
|
||||||
|
else:
|
||||||
|
cls = SvnFile
|
||||||
|
yield p.basename, cls(p)
|
||||||
|
|
||||||
|
|
||||||
|
class SvnFile:
|
||||||
|
data = None
|
||||||
|
|
||||||
|
def __init__(self, path):
|
||||||
|
self.path = path
|
||||||
|
|
||||||
|
def size(self):
|
||||||
|
if self.data is None:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
return len(self.data)
|
||||||
|
|
||||||
|
def read(self):
|
||||||
|
if self.data is None:
|
||||||
|
self.data = self.path.read()
|
||||||
|
return self.data
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
import sys
|
||||||
|
svnurl, mountpoint = sys.argv[1:]
|
||||||
|
root = SvnDir(py.path.svnurl(svnurl))
|
||||||
|
handler = Handler(mountpoint, ObjectFs(root))
|
||||||
|
handler.loop_forever()
|
106
contrib/fuse/impl_b/pyfuse/tahoe.py
Normal file
106
contrib/fuse/impl_b/pyfuse/tahoe.py
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
"""
|
||||||
|
PyFuse client for the Tahoe distributed file system.
|
||||||
|
See http://allmydata.org/
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Read-only for now.
|
||||||
|
|
||||||
|
# Portions copied from the file contrib/fuse/tahoe_fuse.py distributed
|
||||||
|
# with Tahoe 1.0.0.
|
||||||
|
|
||||||
|
import os, sys
|
||||||
|
from objectfs import ObjectFs
|
||||||
|
from handler import Handler
|
||||||
|
import simplejson
|
||||||
|
import urllib
|
||||||
|
|
||||||
|
|
||||||
|
### Config:
|
||||||
|
TahoeConfigDir = '~/.tahoe'
|
||||||
|
|
||||||
|
|
||||||
|
### Utilities for debug:
|
||||||
|
def log(msg, *args):
|
||||||
|
print msg % args
|
||||||
|
|
||||||
|
|
||||||
|
class TahoeConnection:
|
||||||
|
def __init__(self, confdir):
|
||||||
|
self.confdir = confdir
|
||||||
|
self._init_url()
|
||||||
|
|
||||||
|
def _init_url(self):
|
||||||
|
f = open(os.path.join(self.confdir, 'webport'), 'r')
|
||||||
|
contents = f.read()
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
fields = contents.split(':')
|
||||||
|
proto, port = fields[:2]
|
||||||
|
assert proto == 'tcp'
|
||||||
|
port = int(port)
|
||||||
|
self.url = 'http://localhost:%d' % (port,)
|
||||||
|
|
||||||
|
def get_root(self):
|
||||||
|
# For now we just use the same default as the CLI:
|
||||||
|
rootdirfn = os.path.join(self.confdir, 'private', 'root_dir.cap')
|
||||||
|
f = open(rootdirfn, 'r')
|
||||||
|
cap = f.read().strip()
|
||||||
|
f.close()
|
||||||
|
return TahoeDir(self, canonicalize_cap(cap))
|
||||||
|
|
||||||
|
|
||||||
|
class TahoeNode:
|
||||||
|
def __init__(self, conn, uri):
|
||||||
|
self.conn = conn
|
||||||
|
self.uri = uri
|
||||||
|
|
||||||
|
def get_metadata(self):
|
||||||
|
f = self._open('?t=json')
|
||||||
|
json = f.read()
|
||||||
|
f.close()
|
||||||
|
return simplejson.loads(json)
|
||||||
|
|
||||||
|
def _open(self, postfix=''):
|
||||||
|
url = '%s/uri/%s%s' % (self.conn.url, self.uri, postfix)
|
||||||
|
log('*** Fetching: %r', url)
|
||||||
|
return urllib.urlopen(url)
|
||||||
|
|
||||||
|
|
||||||
|
class TahoeDir(TahoeNode):
|
||||||
|
def listdir(self):
|
||||||
|
flag, md = self.get_metadata()
|
||||||
|
assert flag == 'dirnode'
|
||||||
|
result = []
|
||||||
|
for name, (childflag, childmd) in md['children'].items():
|
||||||
|
if childflag == 'dirnode':
|
||||||
|
cls = TahoeDir
|
||||||
|
else:
|
||||||
|
cls = TahoeFile
|
||||||
|
result.append((str(name), cls(self.conn, childmd['ro_uri'])))
|
||||||
|
return result
|
||||||
|
|
||||||
|
class TahoeFile(TahoeNode):
|
||||||
|
def size(self):
|
||||||
|
rawsize = self.get_metadata()[1]['size']
|
||||||
|
return rawsize
|
||||||
|
|
||||||
|
def read(self):
|
||||||
|
return self._open().read()
|
||||||
|
|
||||||
|
|
||||||
|
def canonicalize_cap(cap):
|
||||||
|
cap = urllib.unquote(cap)
|
||||||
|
i = cap.find('URI:')
|
||||||
|
assert i != -1, 'A cap must contain "URI:...", but this does not: ' + cap
|
||||||
|
return cap[i:]
|
||||||
|
|
||||||
|
def main(mountpoint, basedir):
|
||||||
|
conn = TahoeConnection(basedir)
|
||||||
|
root = conn.get_root()
|
||||||
|
handler = Handler(mountpoint, ObjectFs(root))
|
||||||
|
handler.loop_forever()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
[mountpoint] = sys.argv[1:]
|
||||||
|
basedir = os.path.expanduser(TahoeConfigDir)
|
||||||
|
main(mountpoint, basedir)
|
172
contrib/fuse/impl_b/pyfuse/test.py
Normal file
172
contrib/fuse/impl_b/pyfuse/test.py
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
from handler import Handler
|
||||||
|
import stat, errno, os, time
|
||||||
|
from cStringIO import StringIO
|
||||||
|
from kernel import *
|
||||||
|
|
||||||
|
|
||||||
|
UID = os.getuid()
|
||||||
|
GID = os.getgid()
|
||||||
|
UMASK = os.umask(0); os.umask(UMASK)
|
||||||
|
INFINITE = 86400.0
|
||||||
|
|
||||||
|
|
||||||
|
class Node(object):
|
||||||
|
__slots__ = ['attr', 'data']
|
||||||
|
|
||||||
|
def __init__(self, attr, data=None):
|
||||||
|
self.attr = attr
|
||||||
|
self.data = data
|
||||||
|
|
||||||
|
def type(self):
|
||||||
|
return mode2type(self.attr.mode)
|
||||||
|
|
||||||
|
def modified(self):
|
||||||
|
self.attr.mtime = self.attr.atime = time.time()
|
||||||
|
t = self.type()
|
||||||
|
if t == TYPE_REG:
|
||||||
|
f = self.data
|
||||||
|
pos = f.tell()
|
||||||
|
f.seek(0, 2)
|
||||||
|
self.attr.size = f.tell()
|
||||||
|
f.seek(pos)
|
||||||
|
elif t == TYPE_DIR:
|
||||||
|
nsubdirs = 0
|
||||||
|
for nodeid in self.data.values():
|
||||||
|
nsubdirs += nodeid & 1
|
||||||
|
self.attr.nlink = 2 + nsubdirs
|
||||||
|
|
||||||
|
|
||||||
|
def newattr(s, mode=0666):
|
||||||
|
now = time.time()
|
||||||
|
return fuse_attr(ino = INVALID_INO,
|
||||||
|
size = 0,
|
||||||
|
mode = s | (mode & ~UMASK),
|
||||||
|
nlink = 1 + (s == stat.S_IFDIR),
|
||||||
|
atime = now,
|
||||||
|
mtime = now,
|
||||||
|
ctime = now,
|
||||||
|
uid = UID,
|
||||||
|
gid = GID)
|
||||||
|
|
||||||
|
# ____________________________________________________________
|
||||||
|
|
||||||
|
class Filesystem:
|
||||||
|
|
||||||
|
def __init__(self, rootnode):
|
||||||
|
self.nodes = {FUSE_ROOT_ID: rootnode}
|
||||||
|
self.nextid = 2
|
||||||
|
assert self.nextid > FUSE_ROOT_ID
|
||||||
|
|
||||||
|
def getnode(self, nodeid):
|
||||||
|
try:
|
||||||
|
return self.nodes[nodeid]
|
||||||
|
except KeyError:
|
||||||
|
raise IOError(errno.ESTALE, nodeid)
|
||||||
|
|
||||||
|
def forget(self, nodeid):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def cachenode(self, node):
|
||||||
|
id = self.nextid
|
||||||
|
self.nextid += 2
|
||||||
|
if node.type() == TYPE_DIR:
|
||||||
|
id += 1
|
||||||
|
self.nodes[id] = node
|
||||||
|
return id
|
||||||
|
|
||||||
|
def getattr(self, node):
|
||||||
|
return node.attr, INFINITE
|
||||||
|
|
||||||
|
def setattr(self, node, mode=None, uid=None, gid=None,
|
||||||
|
size=None, atime=None, mtime=None):
|
||||||
|
if mode is not None: node.attr.mode = (node.attr.mode&~0777) | mode
|
||||||
|
if uid is not None: node.attr.uid = uid
|
||||||
|
if gid is not None: node.attr.gid = gid
|
||||||
|
if atime is not None: node.attr.atime = atime
|
||||||
|
if mtime is not None: node.attr.mtime = mtime
|
||||||
|
if size is not None and node.type() == TYPE_REG:
|
||||||
|
node.data.seek(size)
|
||||||
|
node.data.truncate()
|
||||||
|
|
||||||
|
def listdir(self, node):
|
||||||
|
for name, subnodeid in node.data.items():
|
||||||
|
subnode = self.nodes[subnodeid]
|
||||||
|
yield name, subnode.type()
|
||||||
|
|
||||||
|
def lookup(self, node, name):
|
||||||
|
try:
|
||||||
|
return node.data[name], INFINITE
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
if hasattr(node, 'findnode'):
|
||||||
|
try:
|
||||||
|
subnode = node.findnode(name)
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
id = self.cachenode(subnode)
|
||||||
|
node.data[name] = id
|
||||||
|
return id, INFINITE
|
||||||
|
raise IOError(errno.ENOENT, name)
|
||||||
|
|
||||||
|
def open(self, node, mode):
|
||||||
|
return node.data
|
||||||
|
|
||||||
|
def mknod(self, node, name, mode):
|
||||||
|
subnode = Node(newattr(mode & 0170000, mode & 0777))
|
||||||
|
if subnode.type() == TYPE_REG:
|
||||||
|
subnode.data = StringIO()
|
||||||
|
else:
|
||||||
|
raise NotImplementedError
|
||||||
|
id = self.cachenode(subnode)
|
||||||
|
node.data[name] = id
|
||||||
|
node.modified()
|
||||||
|
return id, INFINITE
|
||||||
|
|
||||||
|
def mkdir(self, node, name, mode):
|
||||||
|
subnode = Node(newattr(stat.S_IFDIR, mode & 0777), {})
|
||||||
|
id = self.cachenode(subnode)
|
||||||
|
node.data[name] = id
|
||||||
|
node.modified()
|
||||||
|
return id, INFINITE
|
||||||
|
|
||||||
|
def symlink(self, node, linkname, target):
|
||||||
|
subnode = Node(newattr(stat.S_IFLNK, 0777), target)
|
||||||
|
id = self.cachenode(subnode)
|
||||||
|
node.data[linkname] = id
|
||||||
|
node.modified()
|
||||||
|
return id, INFINITE
|
||||||
|
|
||||||
|
def readlink(self, node):
|
||||||
|
assert node.type() == TYPE_LNK
|
||||||
|
return node.data
|
||||||
|
|
||||||
|
def unlink(self, node, name):
|
||||||
|
try:
|
||||||
|
del node.data[name]
|
||||||
|
except KeyError:
|
||||||
|
raise IOError(errno.ENOENT, name)
|
||||||
|
node.modified()
|
||||||
|
|
||||||
|
rmdir = unlink
|
||||||
|
|
||||||
|
def rename(self, oldnode, oldname, newnode, newname):
|
||||||
|
if newnode.type() != TYPE_DIR:
|
||||||
|
raise IOError(errno.ENOTDIR, newnode)
|
||||||
|
try:
|
||||||
|
nodeid = oldnode.data.pop(oldname)
|
||||||
|
except KeyError:
|
||||||
|
raise IOError(errno.ENOENT, oldname)
|
||||||
|
oldnode.modified()
|
||||||
|
newnode.data[newname] = nodeid
|
||||||
|
newnode.modified()
|
||||||
|
|
||||||
|
def modified(self, node):
|
||||||
|
node.modified()
|
||||||
|
|
||||||
|
# ____________________________________________________________
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
root = Node(newattr(stat.S_IFDIR), {})
|
||||||
|
handler = Handler('/home/arigo/mnt', Filesystem(root))
|
||||||
|
handler.loop_forever()
|
Loading…
x
Reference in New Issue
Block a user