From 7214f2f8b87549bee466f4d425d3f581d38db95d Mon Sep 17 00:00:00 2001
From: nejucomo <nejucomo@gmail.com>
Date: Sat, 12 Jan 2008 17:06:39 -0700
Subject: [PATCH] A patch to make tahoe-fuse.py work with 0.7.0 plus a howto
 README.

---
 contrib/fuse/README              | 90 ++++++++++++++++++++++++++++++++
 contrib/{ => fuse}/tahoe-fuse.py | 37 +++++++++----
 2 files changed, 118 insertions(+), 9 deletions(-)
 create mode 100644 contrib/fuse/README
 rename contrib/{ => fuse}/tahoe-fuse.py (89%)

diff --git a/contrib/fuse/README b/contrib/fuse/README
new file mode 100644
index 00000000..f79107cc
--- /dev/null
+++ b/contrib/fuse/README
@@ -0,0 +1,90 @@
+
+Welcome to the tahoe fuse interface prototype!
+
+
+Dependencies:
+
+In addition to a working tahoe installation, this interface depends
+on the python-fuse interface.  This package is available on Ubuntu
+systems as "python-fuse".  It is only known to work with ubuntu
+package version "2.5-5build1".  The latest ubuntu package (version
+"1:0.2-pre3-3") appears to not work currently.
+
+Unfortunately this package appears poorly maintained (notice the wildy
+different version strings and changing API semantics), so if you know
+of a good replacement pythonic fuse interface, please let tahoe-dev know
+about it!
+
+
+Configuration:
+
+Currently tahoe-fuse.py uses the same ~/.tahoe/private/root_dir.cap
+file (which is also the CLI default).  This is not configurable yet.
+Place a directory cap in this file.  (Hint: If you can run "tahoe ls"
+and see a directory listing, this file is properly configured.)
+
+
+Commandline:
+
+The usage is "tahoe-fuse.py <mountpoint>".  The mount point needs to
+be an existing directory which should be empty.  (If it's not empty
+the contents will be safe, but unavailable while the tahoe-fuse.py
+process is mounted there.)
+
+
+Usage:
+
+To use the interface, use other programs to poke around the
+mountpoint.  You should be able to see the same contents as you would
+by using the CLI or WUI for the same directory cap.
+
+
+Runtime Behavior Notes:
+
+Read-only:
+Only reading a tahoe grid is supported, which is reflected in
+the permission modes.  With Tahoe 0.7.0, write access should be easier
+to implement, but is not yet present.
+
+In-Memory File Caching:
+Currently requesting a particular file for read causes the entire file to
+be retrieved into tahoe-fuse.py memory before the read operation returns!
+This caching is reused for subsequent reads.  Beware large files.
+When transitioning to a finer-grained fuse api, this caching should be
+replaced with straight-forward calls to the wapi.  In my opinion, the
+Tahoe node should do all the caching tricks, so that extensions such as
+tahoe-fuse.py can be simple and thin.
+
+Backgrounding Behavior:
+When using the 2.5-5build1 ubuntu package, and no other arguments
+besides a mountpoint to tahoe-fuse.py, the process should remain in
+the foreground and print debug information.   Other python-fuse
+versions appear to alter this behavior and may fork the process to
+the background and obscure the log output.  Bonus points to whomever
+discovers the fate of these poor log messages in this case.
+
+"Investigative Logging":
+This prototype is designed to aide in further fuse development, so
+currently *every* fuse interface call figures out the process from
+which the file system request originates, then it figures out that
+processes commandline (this uses the /proc file system).  This is handy
+for interactive inspection of what kinds of behavior invokes which
+file system operations, but may not work for you.  To disable this
+inspection, edit the source and comment out all of the "@debugcall"
+[FIXME: double check python ref name] method decorators by inserting a
+'#' so it looks like "#@debugcall" (without quotes).
+
+Not-to-spec:
+The current version was not implemented according to any spec and
+makes quite a few dubious "guesses" for what data to pass the fuse
+interface.  You may see bizarre values, which may potentialy confuse
+any processes visiting the files under the mount point.
+
+Serial, blocking operations:
+Most fuse operations result in one or more http calls to the WAPI.
+These are serial and blocking (at least for the tested python-fuse
+version 2.5-5build1), so access to this file system is quite
+inefficient.
+
+
+Good luck!
diff --git a/contrib/tahoe-fuse.py b/contrib/fuse/tahoe-fuse.py
similarity index 89%
rename from contrib/tahoe-fuse.py
rename to contrib/fuse/tahoe-fuse.py
index 400e7a15..8e352917 100644
--- a/contrib/tahoe-fuse.py
+++ b/contrib/fuse/tahoe-fuse.py
@@ -58,6 +58,7 @@ fuse.fuse_python_api = (0, 1) # Use the silly path-based api for now.
 ### Config:
 TahoeConfigDir = '~/.tahoe'
 MagicDevNumber = 42
+UnknownSize = -1
 
 
 def main(args = sys.argv[1:]):
@@ -136,12 +137,21 @@ class TahoeFS (fuse.Fuse):
         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.rootdir = TahoeDir(self.url, uri)
+    def _init_rootdir(self):
+        # For now we just use the same default as the CLI:
+        rootdirfn = os.path.join(self.confdir, 'private', 'root_dir.cap')
+        try:
+            f = open(rootdirfn, 'r')
+            cap = f.read().strip()
+            f.close()
+        except EnvironmentError, le:
+            # FIXME: This user-friendly help message may be platform-dependent because it checks the exception description.
+            if le.args[1].find('No such file or directory') != -1:
+                raise SystemExit('%s requires a directory capability in %s, but it was not found.\nPlease see "The CLI" in "docs/using.html".\n' % (sys.argv[0], rootdirfn))
+            else:
+                raise le
+
+        self.rootdir = TahoeDir(self.url, canonicalize_cap(cap))
 
     def _get_node(self, path):
         assert path.startswith('/')
@@ -335,7 +345,7 @@ class TahoeNode (object):
 
 class TahoeFile (TahoeNode):
     def __init__(self, baseurl, uri):
-        assert uri.split(':', 2)[1] in ('CHK', 'LIT'), `uri`
+        #assert uri.split(':', 2)[1] in ('CHK', 'LIT'), `uri` # fails as of 0.7.0
         TahoeNode.__init__(self, baseurl, uri)
 
     # nonfuse:
@@ -346,7 +356,12 @@ class TahoeFile (TahoeNode):
         return 1
     
     def get_size(self):
-        return self.get_metadata()[1]['size']
+        rawsize = self.get_metadata()[1]['size']
+        if type(rawsize) is not int: # FIXME: What about sizes which do not fit in python int?
+            assert rawsize == u'?', `rawsize`
+            return UnknownSize
+        else:
+            return rawsize
     
     def resolve_path(self, path):
         assert type(path) is list
@@ -356,7 +371,6 @@ class TahoeFile (TahoeNode):
 
 class TahoeDir (TahoeNode):
     def __init__(self, baseurl, uri):
-        #assert uri.split(':', 2)[1] in ('DIR2', 'DIR2-RO'), `uri`
         TahoeNode.__init__(self, baseurl, uri)
 
         self.mode = stat.S_IFDIR | 0500 # Read only directory.
@@ -408,6 +422,11 @@ class TahoeDir (TahoeNode):
         return c
         
         
+def canonicalize_cap(cap):
+    i = cap.find('URI:')
+    assert i != -1, 'A cap must contain "URI:...", but this does not: ' + cap
+    return cap[i:]
+    
 
 if __name__ == '__main__':
     main()
-- 
2.45.2