]> arthur.barton.de Git - bup.git/commitdiff
vfs: try_lresolve() was a bad idea. Create try_resolve() instead.
authorAvery Pennarun <apenwarr@gmail.com>
Mon, 26 Jul 2010 04:30:29 +0000 (00:30 -0400)
committerAvery Pennarun <apenwarr@gmail.com>
Mon, 26 Jul 2010 04:30:29 +0000 (00:30 -0400)
Also add some comments to describe the actual differences between resolve()
and lresolve(), and clean things up a bit so that they actually work as
they're supposed to.

Basically, all of lresolve(), resolve(), and try_resolve() depend on
*intermediate* paths being resolvable; all of them will throw an exception
if not.  They only differ in the very last node in the path, when that node
is a symlink:

  resolve() will dereference it or throw an exception if it can't;
  try_resolve() will try to dereference it, but return self if it can't;
  lresolve() will not dereference it at all, like lstat() doesn't.

With that in mind, we can fix up cmd/ftp and cmd/web to use the right calls,
thus fixing an unexpected error in ftp's tab completion reported by Gabriel
Filion, which would happen if you tried to tab complete inside a directory
that contained a broken symlink.  We only care what the symlink points to so
we can decide whether or not to append '/' to the tab completion, so we want
it to fail silently if it's going to fail.

Signed-off-by: Avery Pennarun <apenwarr@gmail.com>
cmd/ftp-cmd.py
cmd/web-cmd.py
lib/bup/vfs.py

index 0dd825ab3568f7c38712d290be26987fbe7d11c4..87194aa5655b31509b156de69b4123c095fd542a 100755 (executable)
@@ -95,7 +95,7 @@ def completer(text, state):
         (dir, name, qtype, lastword, subs) = _last_res
         if state < len(subs):
             sn = subs[state]
-            sn1 = sn.resolve('')  # deref symlinks
+            sn1 = sn.try_resolve()  # find the type of any symlink target
             fullname = os.path.join(dir, sn.name)
             if stat.S_ISDIR(sn1.mode):
                 ret = shquote.what_to_add(qtype, lastword, fullname+'/',
@@ -105,7 +105,14 @@ def completer(text, state):
                                           terminate=True) + ' '
             return text + ret
     except Exception, e:
-        log('\nerror in completion: %s\n' % e)
+        if 0:
+            log('\n')
+            try:
+                import traceback
+                traceback.print_tb(sys.exc_traceback)
+            except Exception, e2:
+                log('Error printing traceback: %s\n' % e2)
+        log('\nError in completion: %s\n' % e)
 
 
 optspec = """
@@ -145,7 +152,7 @@ for line in lines:
     try:
         if cmd == 'ls':
             for parm in (words[1:] or ['.']):
-                do_ls(parm, pwd.resolve(parm))
+                do_ls(parm, pwd.try_resolve(parm))
         elif cmd == 'cd':
             for parm in words[1:]:
                 pwd = pwd.resolve(parm)
index 2ea8c73683137c5d4d29c6c18a1163750cf27ef6..aca9f87a5f21a35c73defeeb56d756c899363469 100755 (executable)
@@ -30,7 +30,7 @@ def _compute_dir_contents(n):
 
         # link should be based on fully resolved type to avoid extra
         # HTTP redirect.
-        if stat.S_ISDIR(sub.try_lresolve('').mode):
+        if stat.S_ISDIR(sub.try_resolve().mode):
             link = sub.name + "/"
 
         size = None
index e8f29489310edf8d9855492d380e7667864f3ae6..596a8b6910335f84a7cc72a33f7c78422b2ae91d 100644 (file)
@@ -205,8 +205,12 @@ class Node:
         else:
             return self.sub(first)
 
+    # walk into a given sub-path of this node.  If the last element is
+    # a symlink, leave it as a symlink, don't resolve it.  (like lstat())
     def lresolve(self, path):
         start = self
+        if not path:
+            return start
         if path.startswith('/'):
             start = self.top()
             path = path[1:]
@@ -216,15 +220,21 @@ class Node:
         #log('parts: %r %r\n' % (path, parts))
         return start._lresolve(parts)
 
-    def try_lresolve(self, path):
+    # walk into the given sub-path of this node, and dereference it if it
+    # was a symlink.
+    def resolve(self, path = ''):
+        return self.lresolve(path).lresolve('.')
+
+    # like resolve(), but don't worry if the last symlink points at an
+    # invalid path.
+    # (still returns an error if any intermediate nodes were invalid)
+    def try_resolve(self, path = ''):
+        n = self.lresolve(path)
         try:
-            return self.lresolve(path)
+            n = n.lresolve('.')
         except NoSuchFile:
-            # some symlinks don't actually point at a file that exists!
-            return self
-
-    def resolve(self, path):
-        return self.lresolve(path).lresolve('')
+            pass
+        return n
     
     def nlinks(self):
         if self._subs == None:
@@ -286,6 +296,9 @@ class Symlink(File):
         _symrefs += 1
         try:
             return self.parent.lresolve(self.readlink())
+        except NoSuchFile:
+            raise NoSuchFile("%s: broken symlink to %r"
+                             % (self.fullname(), self.readlink()))
         finally:
             _symrefs -= 1