--- /dev/null
+#! /usr/bin/env python
+'''
+Tahoe thin-client fuse module.
+
+Goals:
+- Delegate to Tahoe webapi as much as possible.
+- Thin as possible.
+- This is a proof-of-concept, not a usable product.
+'''
+
+
+#import bindann
+#bindann.install_exception_handler()
+
+import sys, stat, os, errno, urllib
+
+import simplejson
+
+# FIXME: Currently uses the old, silly path-based (non-stateful) interface:
+import fuse
+fuse.fuse_python_api = (0, 1) # Use the silly path-based api for now.
+
+
+### Config:
+TahoeConfigDir = '~/.tahoe'
+MagicDevNumber = 42
+
+
+def main(args = sys.argv[1:]):
+ fs = TahoeFS(os.path.expanduser(TahoeConfigDir))
+ fs.main()
+
+
+### Utilities just for debug:
+def debugdeco(m):
+ def dbmeth(self, *a, **kw):
+ pid = self.GetContext()['pid']
+ print '[%d %r]\n%s%r%r' % (pid, get_cmdline(pid), m.__name__, a, kw)
+ try:
+ r = m(self, *a, **kw)
+ if (type(r) is int) and (r < 0):
+ print '-> -%s\n' % (errno.errorcode[-r],)
+ else:
+ repstr = repr(r)[:256]
+ print '-> %s\n' % (repstr,)
+ return r
+ except:
+ sys.excepthook(*sys.exc_info())
+
+ return dbmeth
+
+
+def get_cmdline(pid):
+ f = open('/proc/%d/cmdline' % pid, 'r')
+ args = f.read().split('\0')
+ f.close()
+ assert args[-1] == ''
+ return args[:-1]
+
+
+class ErrnoExc (Exception):
+ def __init__(self, eno):
+ self.eno = eno
+ Exception.__init__(self, errno.errorcode[eno])
+
+ @staticmethod
+ def wrapped(meth):
+ def wrapper(*args, **kw):
+ try:
+ return meth(*args, **kw)
+ except ErrnoExc, e:
+ return -e.eno
+ wrapper.__name__ = meth.__name__
+ return wrapper
+
+
+### Heart of the Matter:
+class TahoeFS (fuse.Fuse):
+ def __init__(self, confdir):
+ fuse.Fuse.__init__(self)
+ self.confdir = confdir
+
+ self.flags = 0 # FIXME: What goes here?
+ self.multithreaded = 0
+
+ # silly path-based file handles.
+ self.filecontents = {} # {path -> contents}
+
+ self._init_url()
+ self._init_bookmarks()
+
+ 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 _init_bookmarks(self):
+ f = open(os.path.join(self.confdir, 'fuse-bookmarks.uri'), 'r')
+ uri = f.read().strip()
+ f.close()
+
+ self.bookmarks = TahoeDir(self.url, uri)
+
+ def _get_node(self, path):
+ assert path.startswith('/')
+ if path == '/':
+ return self.bookmarks.resolve_path([])
+ else:
+ parts = path.split('/')[1:]
+ return self.bookmarks.resolve_path(parts)
+
+ def _get_contents(self, path):
+ node = self._get_node(path)
+ contents = node.open().read()
+ self.filecontents[path] = contents
+ return contents
+
+ @debugdeco
+ @ErrnoExc.wrapped
+ def getattr(self, path):
+ node = self._get_node(path)
+ return node.getattr()
+
+ @debugdeco
+ @ErrnoExc.wrapped
+ def getdir(self, path):
+ """
+ return: [(name, typeflag), ... ]
+ """
+ node = self._get_node(path)
+ return node.getdir()
+
+ @debugdeco
+ @ErrnoExc.wrapped
+ def mythread(self):
+ return -errno.ENOSYS
+
+ @debugdeco
+ @ErrnoExc.wrapped
+ def chmod(self, path, mode):
+ return -errno.ENOSYS
+
+ @debugdeco
+ @ErrnoExc.wrapped
+ def chown(self, path, uid, gid):
+ return -errno.ENOSYS
+
+ @debugdeco
+ @ErrnoExc.wrapped
+ def fsync(self, path, isFsyncFile):
+ return -errno.ENOSYS
+
+ @debugdeco
+ @ErrnoExc.wrapped
+ def link(self, target, link):
+ return -errno.ENOSYS
+
+ @debugdeco
+ @ErrnoExc.wrapped
+ def mkdir(self, path, mode):
+ return -errno.ENOSYS
+
+ @debugdeco
+ @ErrnoExc.wrapped
+ def mknod(self, path, mode, dev_ignored):
+ return -errno.ENOSYS
+
+ @debugdeco
+ @ErrnoExc.wrapped
+ def open(self, path, mode):
+ IgnoredFlags = os.O_RDONLY | os.O_NONBLOCK | os.O_SYNC | os.O_LARGEFILE
+ # Note: IgnoredFlags are all ignored!
+ for fname in dir(os):
+ if fname.startswith('O_'):
+ flag = getattr(os, fname)
+ if flag & IgnoredFlags:
+ continue
+ elif mode & flag:
+ print 'Flag not supported:', fname
+ raise ErrnoExc(errno.ENOSYS)
+
+ self._get_contents(path)
+ return 0
+
+ @debugdeco
+ @ErrnoExc.wrapped
+ def read(self, path, length, offset):
+ return self._get_contents(path)[offset:length]
+
+ @debugdeco
+ @ErrnoExc.wrapped
+ def release(self, path):
+ del self.filecontents[path]
+ return 0
+
+ @debugdeco
+ @ErrnoExc.wrapped
+ def readlink(self, path):
+ return -errno.ENOSYS
+
+ @debugdeco
+ @ErrnoExc.wrapped
+ def rename(self, oldpath, newpath):
+ return -errno.ENOSYS
+
+ @debugdeco
+ @ErrnoExc.wrapped
+ def rmdir(self, path):
+ return -errno.ENOSYS
+
+ #@debugdeco
+ @ErrnoExc.wrapped
+ def statfs(self):
+ return -errno.ENOSYS
+
+ @debugdeco
+ @ErrnoExc.wrapped
+ def symlink ( self, targetPath, linkPath ):
+ return -errno.ENOSYS
+
+ @debugdeco
+ @ErrnoExc.wrapped
+ def truncate(self, path, size):
+ return -errno.ENOSYS
+
+ @debugdeco
+ @ErrnoExc.wrapped
+ def unlink(self, path):
+ return -errno.ENOSYS
+
+ @debugdeco
+ @ErrnoExc.wrapped
+ def utime(self, path, times):
+ return -errno.ENOSYS
+
+
+class TahoeNode (object):
+ NextInode = 0
+
+ @staticmethod
+ def make(baseurl, uri):
+ typefield = uri.split(':', 2)[1]
+ if typefield.startswith('DIR'):
+ return TahoeDir(baseurl, uri)
+ else:
+ return TahoeFile(baseurl, uri)
+
+ def __init__(self, baseurl, uri):
+ self.burl = baseurl
+ self.uri = uri
+ self.fullurl = '%s/uri/%s' % (self.burl, self.uri)
+ self.inode = TahoeNode.NextInode
+ TahoeNode.NextInode += 1
+
+ def getattr(self):
+ """
+ - st_mode (protection bits)
+ - st_ino (inode number)
+ - st_dev (device)
+ - st_nlink (number of hard links)
+ - st_uid (user ID of owner)
+ - st_gid (group ID of owner)
+ - st_size (size of file, in bytes)
+ - st_atime (time of most recent access)
+ - st_mtime (time of most recent content modification)
+ - st_ctime (platform dependent; time of most recent metadata change on Unix,
+ or the time of creation on Windows).
+ """
+ # FIXME: Return metadata that isn't completely fabricated.
+ return (self.get_mode(),
+ self.inode,
+ MagicDevNumber,
+ self.get_linkcount(),
+ os.getuid(),
+ os.getgid(),
+ self.get_size(),
+ 0,
+ 0,
+ 0)
+
+ def get_metadata(self):
+ f = self.open('?t=json')
+ json = f.read()
+ f.close()
+ return simplejson.loads(json)
+
+ def open(self, postfix=''):
+ url = self.fullurl + postfix
+ print '*** Fetching:', `url`
+ return urllib.urlopen(url)
+
+
+class TahoeFile (TahoeNode):
+ def __init__(self, baseurl, uri):
+ assert uri.split(':', 2)[1] in ('CHK', 'LIT'), `uri`
+ TahoeNode.__init__(self, baseurl, uri)
+
+ # nonfuse:
+ def get_mode(self):
+ return stat.S_IFREG | 0400 # Read only regular file.
+
+ def get_linkcount(self):
+ return 1
+
+ def get_size(self):
+ return self.get_metadata()[1]['size']
+
+ def resolve_path(self, path):
+ assert type(path) is list
+ assert path == []
+ return self
+
+
+class TahoeDir (TahoeNode):
+ def __init__(self, baseurl, uri):
+ assert uri.split(':', 2)[1] in ('DIR', 'DIR-RO'), `uri`
+ TahoeNode.__init__(self, baseurl, uri)
+
+ self.mode = stat.S_IFDIR | 0500 # Read only directory.
+
+ # FUSE:
+ def getdir(self):
+ d = [('.', self.get_mode()), ('..', self.get_mode())]
+ for name, child in self.get_children().items():
+ if name: # Just ignore this crazy case!
+ d.append((name, child.get_mode()))
+ return d
+
+ # nonfuse:
+ def get_mode(self):
+ return stat.S_IFDIR | 0500 # Read only directory.
+
+ def get_linkcount(self):
+ return len(self.getdir())
+
+ def get_size(self):
+ return 2 ** 12 # FIXME: What do we return here? len(self.get_metadata())
+
+ def resolve_path(self, path):
+ assert type(path) is list
+
+ if path:
+ head = path[0]
+ child = self.get_child(head)
+ return child.resolve_path(path[1:])
+ else:
+ return self
+
+ def get_child(self, name):
+ c = self.get_children()
+ return c[name]
+
+ def get_children(self):
+ flag, md = self.get_metadata()
+ assert flag == 'dirnode'
+
+ c = {}
+ for name, (childflag, childmd) in md['children'].items():
+ if childflag == 'dirnode':
+ cls = TahoeDir
+ else:
+ cls = TahoeFile
+
+ c[str(name)] = cls(self.burl, childmd['ro_uri'])
+ return c
+
+
+
+if __name__ == '__main__':
+ main()
+
+++ /dev/null
-#! /usr/bin/env python
-'''
-Tahoe thin-client fuse module.
-
-Goals:
-- Delegate to Tahoe webapi as much as possible.
-- Thin as possible.
-- This is a proof-of-concept, not a usable product.
-'''
-
-
-#import bindann
-#bindann.install_exception_handler()
-
-import sys, stat, os, errno, urllib
-
-import simplejson
-
-# FIXME: Currently uses the old, silly path-based (non-stateful) interface:
-import fuse
-fuse.fuse_python_api = (0, 1) # Use the silly path-based api for now.
-
-
-### Config:
-TahoeConfigDir = '~/.tahoe'
-MagicDevNumber = 42
-
-
-def main(args = sys.argv[1:]):
- fs = TahoeFS(os.path.expanduser(TahoeConfigDir))
- fs.main()
-
-
-### Utilities just for debug:
-def debugdeco(m):
- def dbmeth(self, *a, **kw):
- pid = self.GetContext()['pid']
- print '[%d %r]\n%s%r%r' % (pid, get_cmdline(pid), m.__name__, a, kw)
- try:
- r = m(self, *a, **kw)
- if (type(r) is int) and (r < 0):
- print '-> -%s\n' % (errno.errorcode[-r],)
- else:
- repstr = repr(r)[:256]
- print '-> %s\n' % (repstr,)
- return r
- except:
- sys.excepthook(*sys.exc_info())
-
- return dbmeth
-
-
-def get_cmdline(pid):
- f = open('/proc/%d/cmdline' % pid, 'r')
- args = f.read().split('\0')
- f.close()
- assert args[-1] == ''
- return args[:-1]
-
-
-class ErrnoExc (Exception):
- def __init__(self, eno):
- self.eno = eno
- Exception.__init__(self, errno.errorcode[eno])
-
- @staticmethod
- def wrapped(meth):
- def wrapper(*args, **kw):
- try:
- return meth(*args, **kw)
- except ErrnoExc, e:
- return -e.eno
- wrapper.__name__ = meth.__name__
- return wrapper
-
-
-### Heart of the Matter:
-class TahoeFS (fuse.Fuse):
- def __init__(self, confdir):
- fuse.Fuse.__init__(self)
- self.confdir = confdir
-
- self.flags = 0 # FIXME: What goes here?
- self.multithreaded = 0
-
- # silly path-based file handles.
- self.filecontents = {} # {path -> contents}
-
- self._init_url()
- self._init_bookmarks()
-
- 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 _init_bookmarks(self):
- f = open(os.path.join(self.confdir, 'fuse-bookmarks.uri'), 'r')
- uri = f.read().strip()
- f.close()
-
- self.bookmarks = TahoeDir(self.url, uri)
-
- def _get_node(self, path):
- assert path.startswith('/')
- if path == '/':
- return self.bookmarks.resolve_path([])
- else:
- parts = path.split('/')[1:]
- return self.bookmarks.resolve_path(parts)
-
- def _get_contents(self, path):
- node = self._get_node(path)
- contents = node.open().read()
- self.filecontents[path] = contents
- return contents
-
- @debugdeco
- @ErrnoExc.wrapped
- def getattr(self, path):
- node = self._get_node(path)
- return node.getattr()
-
- @debugdeco
- @ErrnoExc.wrapped
- def getdir(self, path):
- """
- return: [(name, typeflag), ... ]
- """
- node = self._get_node(path)
- return node.getdir()
-
- @debugdeco
- @ErrnoExc.wrapped
- def mythread(self):
- return -errno.ENOSYS
-
- @debugdeco
- @ErrnoExc.wrapped
- def chmod(self, path, mode):
- return -errno.ENOSYS
-
- @debugdeco
- @ErrnoExc.wrapped
- def chown(self, path, uid, gid):
- return -errno.ENOSYS
-
- @debugdeco
- @ErrnoExc.wrapped
- def fsync(self, path, isFsyncFile):
- return -errno.ENOSYS
-
- @debugdeco
- @ErrnoExc.wrapped
- def link(self, target, link):
- return -errno.ENOSYS
-
- @debugdeco
- @ErrnoExc.wrapped
- def mkdir(self, path, mode):
- return -errno.ENOSYS
-
- @debugdeco
- @ErrnoExc.wrapped
- def mknod(self, path, mode, dev_ignored):
- return -errno.ENOSYS
-
- @debugdeco
- @ErrnoExc.wrapped
- def open(self, path, mode):
- IgnoredFlags = os.O_RDONLY | os.O_NONBLOCK | os.O_SYNC | os.O_LARGEFILE
- # Note: IgnoredFlags are all ignored!
- for fname in dir(os):
- if fname.startswith('O_'):
- flag = getattr(os, fname)
- if flag & IgnoredFlags:
- continue
- elif mode & flag:
- print 'Flag not supported:', fname
- raise ErrnoExc(errno.ENOSYS)
-
- self._get_contents(path)
- return 0
-
- @debugdeco
- @ErrnoExc.wrapped
- def read(self, path, length, offset):
- return self._get_contents(path)[offset:length]
-
- @debugdeco
- @ErrnoExc.wrapped
- def release(self, path):
- del self.filecontents[path]
- return 0
-
- @debugdeco
- @ErrnoExc.wrapped
- def readlink(self, path):
- return -errno.ENOSYS
-
- @debugdeco
- @ErrnoExc.wrapped
- def rename(self, oldpath, newpath):
- return -errno.ENOSYS
-
- @debugdeco
- @ErrnoExc.wrapped
- def rmdir(self, path):
- return -errno.ENOSYS
-
- #@debugdeco
- @ErrnoExc.wrapped
- def statfs(self):
- return -errno.ENOSYS
-
- @debugdeco
- @ErrnoExc.wrapped
- def symlink ( self, targetPath, linkPath ):
- return -errno.ENOSYS
-
- @debugdeco
- @ErrnoExc.wrapped
- def truncate(self, path, size):
- return -errno.ENOSYS
-
- @debugdeco
- @ErrnoExc.wrapped
- def unlink(self, path):
- return -errno.ENOSYS
-
- @debugdeco
- @ErrnoExc.wrapped
- def utime(self, path, times):
- return -errno.ENOSYS
-
-
-class TahoeNode (object):
- NextInode = 0
-
- @staticmethod
- def make(baseurl, uri):
- typefield = uri.split(':', 2)[1]
- if typefield.startswith('DIR'):
- return TahoeDir(baseurl, uri)
- else:
- return TahoeFile(baseurl, uri)
-
- def __init__(self, baseurl, uri):
- self.burl = baseurl
- self.uri = uri
- self.fullurl = '%s/uri/%s' % (self.burl, self.uri)
- self.inode = TahoeNode.NextInode
- TahoeNode.NextInode += 1
-
- def getattr(self):
- """
- - st_mode (protection bits)
- - st_ino (inode number)
- - st_dev (device)
- - st_nlink (number of hard links)
- - st_uid (user ID of owner)
- - st_gid (group ID of owner)
- - st_size (size of file, in bytes)
- - st_atime (time of most recent access)
- - st_mtime (time of most recent content modification)
- - st_ctime (platform dependent; time of most recent metadata change on Unix,
- or the time of creation on Windows).
- """
- # FIXME: Return metadata that isn't completely fabricated.
- return (self.get_mode(),
- self.inode,
- MagicDevNumber,
- self.get_linkcount(),
- os.getuid(),
- os.getgid(),
- self.get_size(),
- 0,
- 0,
- 0)
-
- def get_metadata(self):
- f = self.open('?t=json')
- json = f.read()
- f.close()
- return simplejson.loads(json)
-
- def open(self, postfix=''):
- url = self.fullurl + postfix
- print '*** Fetching:', `url`
- return urllib.urlopen(url)
-
-
-class TahoeFile (TahoeNode):
- def __init__(self, baseurl, uri):
- assert uri.split(':', 2)[1] in ('CHK', 'LIT'), `uri`
- TahoeNode.__init__(self, baseurl, uri)
-
- # nonfuse:
- def get_mode(self):
- return stat.S_IFREG | 0400 # Read only regular file.
-
- def get_linkcount(self):
- return 1
-
- def get_size(self):
- return self.get_metadata()[1]['size']
-
- def resolve_path(self, path):
- assert type(path) is list
- assert path == []
- return self
-
-
-class TahoeDir (TahoeNode):
- def __init__(self, baseurl, uri):
- assert uri.split(':', 2)[1] in ('DIR', 'DIR-RO'), `uri`
- TahoeNode.__init__(self, baseurl, uri)
-
- self.mode = stat.S_IFDIR | 0500 # Read only directory.
-
- # FUSE:
- def getdir(self):
- d = [('.', self.get_mode()), ('..', self.get_mode())]
- for name, child in self.get_children().items():
- if name: # Just ignore this crazy case!
- d.append((name, child.get_mode()))
- return d
-
- # nonfuse:
- def get_mode(self):
- return stat.S_IFDIR | 0500 # Read only directory.
-
- def get_linkcount(self):
- return len(self.getdir())
-
- def get_size(self):
- return 2 ** 12 # FIXME: What do we return here? len(self.get_metadata())
-
- def resolve_path(self, path):
- assert type(path) is list
-
- if path:
- head = path[0]
- child = self.get_child(head)
- return child.resolve_path(path[1:])
- else:
- return self
-
- def get_child(self, name):
- c = self.get_children()
- return c[name]
-
- def get_children(self):
- flag, md = self.get_metadata()
- assert flag == 'dirnode'
-
- c = {}
- for name, (childflag, childmd) in md['children'].items():
- if childflag == 'dirnode':
- cls = TahoeDir
- else:
- cls = TahoeFile
-
- c[str(name)] = cls(self.burl, childmd['ro_uri'])
- return c
-
-
-
-if __name__ == '__main__':
- main()
-