fuse: adjust for python 3 and test there
authorRob Browning <rlb@defaultvalue.org>
Thu, 2 Jan 2020 21:30:28 +0000 (15:30 -0600)
committerRob Browning <rlb@defaultvalue.org>
Sun, 2 Feb 2020 19:30:12 +0000 (13:30 -0600)
The python 3 version could have issues until the fuse module supports
binary data more completely (e.g. bytes paths), or until we switch to
some other foundation, but it may be OK even so (with some
inefficiency) given our bup-python iso-8859-1 hack.

Signed-off-by: Rob Browning <rlb@defaultvalue.org>
Tested-by: Rob Browning <rlb@defaultvalue.org>
Makefile
cmd/fuse-cmd.py
lib/bup/compat.py
t/test-fuse.sh

index 4c232f4..50a34a9 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -175,6 +175,7 @@ cmdline_tests := \
   t/test-compression.sh \
   t/test-drecurse.sh \
   t/test-fsck.sh \
+  t/test-fuse.sh \
   t/test-ftp \
   t/test-gc.sh \
   t/test-import-duplicity.sh \
@@ -205,7 +206,6 @@ cmdline_tests := \
 ifeq "2" "$(bup_python_majver)"
   cmdline_tests += \
     t/test-web.sh \
-    t/test-fuse.sh \
     t/test-index-check-device.sh \
     t/test-restore-map-owner.sh \
     t/test-xdev.sh
index 8e57ab6..dbc0cd1 100755 (executable)
@@ -5,24 +5,44 @@ exec "$bup_python" "$0" ${1+"$@"}
 """
 # end of bup preamble
 
-from __future__ import absolute_import
+from __future__ import absolute_import, print_function
 import sys, os, errno
 
 try:
     import fuse
 except ImportError:
-    log('error: cannot find the python "fuse" module; please install it\n')
-    sys.exit(1)
+    print('error: cannot find the python "fuse" module; please install it',
+          file=sys.stderr)
+    sys.exit(2)
 if not hasattr(fuse, '__version__'):
-    raise RuntimeError('your fuse module is too old for fuse.__version__')
+    print('error: fuse module is too old for fuse.__version__', file=sys.stderr)
+    sys.exit(2)
 fuse.fuse_python_api = (0, 2)
 
+if sys.version_info[0] > 2:
+    try:
+        fuse_ver = fuse.__version__.split('.')
+        fuse_ver_maj = int(fuse_ver[0])
+    except:
+        log('error: cannot determine the fuse major version; please report',
+            file=sys.stderr)
+        sys.exit(2)
+    if len(fuse_ver) < 3 or fuse_ver_maj < 1:
+        print("error: fuse module can't handle binary data; please upgrade to 1.0+\n",
+              file=sys.stderr)
+        sys.exit(2)
+
 from bup import options, git, vfs, xstat
+from bup.compat import argv_bytes, fsdecode, py_maj
 from bup.helpers import log
 from bup.repo import LocalRepo
 
+
 # FIXME: self.meta and want_meta?
 
+# The path handling is just wrong, but the current fuse module can't
+# handle bytes paths.
+
 class BupFs(fuse.Fuse):
     def __init__(self, repo, verbose=0, fake_metadata=False):
         fuse.Fuse.__init__(self)
@@ -31,6 +51,7 @@ class BupFs(fuse.Fuse):
         self.fake_metadata = fake_metadata
     
     def getattr(self, path):
+        path = argv_bytes(path)
         global opt
         if self.verbose > 0:
             log('--getattr(%r)\n' % path)
@@ -56,6 +77,7 @@ class BupFs(fuse.Fuse):
         return st
 
     def readdir(self, path, offset):
+        path = argv_bytes(path)
         assert not offset  # We don't return offsets, so offset should be unused
         res = vfs.resolve(self.repo, path, follow=False)
         dir_name, dir_item = res[-1]
@@ -64,18 +86,21 @@ class BupFs(fuse.Fuse):
         yield fuse.Direntry('..')
         # FIXME: make sure want_meta=False is being completely respected
         for ent_name, ent_item in vfs.contents(repo, dir_item, want_meta=False):
-            yield fuse.Direntry(ent_name.replace('/', '-'))
+            fusename = fsdecode(ent_name.replace(b'/', b'-'))
+            yield fuse.Direntry(fusename)
 
     def readlink(self, path):
+        path = argv_bytes(path)
         if self.verbose > 0:
             log('--readlink(%r)\n' % path)
         res = vfs.resolve(self.repo, path, follow=False)
         name, item = res[-1]
         if not item:
             return -errno.ENOENT
-        return vfs.readlink(repo, item)
+        return fsdecode(vfs.readlink(repo, item))
 
     def open(self, path, flags):
+        path = argv_bytes(path)
         if self.verbose > 0:
             log('--open(%r)\n' % path)
         res = vfs.resolve(self.repo, path, follow=False)
@@ -90,6 +115,7 @@ class BupFs(fuse.Fuse):
         #return vfs.fopen(repo, item)
 
     def read(self, path, size, offset):
+        path = argv_bytes(path)
         if self.verbose > 0:
             log('--read(%r)\n' % path)
         res = vfs.resolve(self.repo, path, follow=False)
@@ -100,6 +126,7 @@ class BupFs(fuse.Fuse):
             f.seek(offset)
             return f.read(size)
 
+
 optspec = """
 bup fuse [-d] [-f] <mountpoint>
 --
@@ -111,6 +138,14 @@ v,verbose     increase log output (can be used more than once)
 """
 o = options.Options(optspec)
 opt, flags, extra = o.parse(sys.argv[1:])
+if not opt.verbose:
+    opt.verbose = 0
+
+# Set stderr to be line buffered, even if it's not connected to the console
+# so that we'll be able to see diagnostics in a timely fashion.
+errfd = sys.stderr.fileno()
+sys.stderr.flush()
+sys.stderr = os.fdopen(errfd, 'w', 1)
 
 if len(extra) != 1:
     o.fatal('only one mount point argument expected')
@@ -118,7 +153,10 @@ if len(extra) != 1:
 git.check_repo_or_die()
 repo = LocalRepo()
 f = BupFs(repo=repo, verbose=opt.verbose, fake_metadata=(not opt.meta))
+
+# This is likely wrong, but the fuse module doesn't currently accept bytes
 f.fuse_args.mountpoint = extra[0]
+
 if opt.debug:
     f.fuse_args.add('debug')
 if opt.foreground:
index 03041f3..e59f4d3 100644 (file)
@@ -25,7 +25,7 @@ if py3:
               file=sys.stderr)
         sys.exit(2)
 
-    from os import fsencode
+    from os import fsdecode, fsencode
     from shlex import quote
     input = input
     range = range
@@ -76,6 +76,9 @@ if py3:
 
 else:  # Python 2
 
+    def fsdecode(x):
+        return x
+
     def fsencode(x):
         return x
 
index 1732d66..1ed3554 100755 (executable)
@@ -41,7 +41,7 @@ savename()
 {
     readonly secs="$1"
     WVPASS bup-python -c "from time import strftime, localtime; \
-       print strftime('%Y-%m-%d-%H%M%S', localtime($secs))"
+       print(strftime('%Y-%m-%d-%H%M%S', localtime($secs)))"
 }
 
 export TZ=UTC
@@ -49,7 +49,7 @@ export TZ=UTC
 WVPASS bup init
 WVPASS cd "$tmpdir"
 
-savestamp1=$(WVPASS bup-python -c 'import time; print int(time.time())') || exit $?
+savestamp1=$(WVPASS bup-python -c 'import time; print(int(time.time()))') || exit $?
 savestamp2=$(($savestamp1 + 1))
 
 savename1="$(savename "$savestamp1")" || exit $?