From: Rob Browning Date: Tue, 27 Feb 2018 07:32:54 +0000 (-0800) Subject: Replace lresolve with resolve(..., follow=False) X-Git-Tag: 0.30~40 X-Git-Url: https://arthur.barton.de/gitweb/?p=bup.git;a=commitdiff_plain;h=5fc56d8e45065211119c781de0546e83c8270479 Replace lresolve with resolve(..., follow=False) Although there's the NOFOLLOW precedent, this is really just to narrow the API before we add it as a repo method, i.e. so we only have to handle one function instead of two. Signed-off-by: Rob Browning Tested-by: Rob Browning --- diff --git a/cmd/cat-file-cmd.py b/cmd/cat-file-cmd.py index 06e14f6..9cb6961 100755 --- a/cmd/cat-file-cmd.py +++ b/cmd/cat-file-cmd.py @@ -39,7 +39,7 @@ if not re.match(r'/*[^/]+/[^/]+', target): o.fatal("path %r doesn't include a branch and revision" % target) repo = LocalRepo() -resolved = vfs.lresolve(repo, target) +resolved = vfs.resolve(repo, target, follow=False) leaf_name, leaf_item = resolved[-1] if not leaf_item: log('error: cannot access %r in %r\n' diff --git a/cmd/fuse-cmd.py b/cmd/fuse-cmd.py index 747d25b..8e57ab6 100755 --- a/cmd/fuse-cmd.py +++ b/cmd/fuse-cmd.py @@ -34,7 +34,8 @@ class BupFs(fuse.Fuse): global opt if self.verbose > 0: log('--getattr(%r)\n' % path) - res = vfs.lresolve(self.repo, path, want_meta=(not self.fake_metadata)) + res = vfs.resolve(self.repo, path, want_meta=(not self.fake_metadata), + follow=False) name, item = res[-1] if not item: return -errno.ENOENT @@ -56,7 +57,7 @@ class BupFs(fuse.Fuse): def readdir(self, path, offset): assert not offset # We don't return offsets, so offset should be unused - res = vfs.lresolve(self.repo, path) + res = vfs.resolve(self.repo, path, follow=False) dir_name, dir_item = res[-1] if not dir_item: yield -errno.ENOENT @@ -68,7 +69,7 @@ class BupFs(fuse.Fuse): def readlink(self, path): if self.verbose > 0: log('--readlink(%r)\n' % path) - res = vfs.lresolve(self.repo, path) + res = vfs.resolve(self.repo, path, follow=False) name, item = res[-1] if not item: return -errno.ENOENT @@ -77,7 +78,7 @@ class BupFs(fuse.Fuse): def open(self, path, flags): if self.verbose > 0: log('--open(%r)\n' % path) - res = vfs.lresolve(self.repo, path) + res = vfs.resolve(self.repo, path, follow=False) name, item = res[-1] if not item: return -errno.ENOENT @@ -91,7 +92,7 @@ class BupFs(fuse.Fuse): def read(self, path, size, offset): if self.verbose > 0: log('--read(%r)\n' % path) - res = vfs.lresolve(self.repo, path) + res = vfs.resolve(self.repo, path, follow=False) name, item = res[-1] if not item: return -errno.ENOENT diff --git a/cmd/restore-cmd.py b/cmd/restore-cmd.py index 6544a24..0edbdef 100755 --- a/cmd/restore-cmd.py +++ b/cmd/restore-cmd.py @@ -246,7 +246,7 @@ def main(): add_error("path %r doesn't include a branch and revision" % path) continue try: - resolved = vfs.lresolve(repo, path, want_meta=True) + resolved = vfs.resolve(repo, path, want_meta=True, follow=False) except vfs.IOError as e: add_error(e) continue diff --git a/lib/bup/ls.py b/lib/bup/ls.py index 7b3d9ea..4a05b39 100644 --- a/lib/bup/ls.py +++ b/lib/bup/ls.py @@ -113,7 +113,7 @@ def within_repo(repo, opt): for path in opt.paths: try: if opt.directory: - resolved = vfs.lresolve(repo, path) + resolved = vfs.resolve(repo, path, follow=False) else: resolved = vfs.try_resolve(repo, path) diff --git a/lib/bup/rm.py b/lib/bup/rm.py index 5345114..d0e0551 100644 --- a/lib/bup/rm.py +++ b/lib/bup/rm.py @@ -68,7 +68,7 @@ def dead_items(repo, paths): # Scan for bad requests, and opportunities to optimize for path in paths: try: - resolved = vfs.lresolve(repo, path) + resolved = vfs.resolve(repo, path, follow=False) except vfs.IOError as e: add_error(e) continue diff --git a/lib/bup/t/tmetadata.py b/lib/bup/t/tmetadata.py index 347f874..2bf7780 100644 --- a/lib/bup/t/tmetadata.py +++ b/lib/bup/t/tmetadata.py @@ -146,8 +146,9 @@ def test_metadata_method(): ex(bup_path, '-d', bup_dir, 'save', '-tvvn', 'test', data_path) git.check_repo_or_die(bup_dir) repo = LocalRepo() - resolved = vfs.lresolve(repo, - '/test/latest' + resolve_parent(data_path)) + resolved = vfs.resolve(repo, + '/test/latest' + resolve_parent(data_path), + follow=False) leaf_name, leaf_item = resolved[-1] m = leaf_item.meta WVPASS(m.mtime == test_time2) diff --git a/lib/bup/t/tvfs.py b/lib/bup/t/tvfs.py index 7cfe2f0..127acb6 100644 --- a/lib/bup/t/tvfs.py +++ b/lib/bup/t/tvfs.py @@ -117,7 +117,7 @@ def run_augment_item_meta_tests(repo, file_path, file_size, link_path, link_target): _, file_item = vfs.resolve(repo, file_path)[-1] - _, link_item = vfs.lresolve(repo, link_path)[-1] + _, link_item = vfs.resolve(repo, link_path, follow=False)[-1] wvpass(isinstance(file_item.meta, Metadata)) wvpass(isinstance(link_item.meta, Metadata)) # Note: normally, modifying item.meta values is forbidden @@ -253,7 +253,6 @@ def test_resolve(): with no_lingering_errors(): with test_tempdir('bup-tvfs-') as tmpdir: resolve = vfs.resolve - lresolve = vfs.lresolve bup_dir = tmpdir + '/bup' environ['GIT_DIR'] = bup_dir environ['BUP_DIR'] = bup_dir @@ -391,9 +390,9 @@ def test_resolve(): ('not-there', None)) wvpasseq(expected, res) - wvstart('lresolve: /test/latest/bad-symlink') + wvstart('resolve nofollow: /test/latest/bad-symlink') vfs.clear_cache() - res = lresolve(repo, '/test/latest/bad-symlink') + res = resolve(repo, '/test/latest/bad-symlink', follow=False) wvpasseq(4, len(res)) bad_symlink_value = tip_tree['bad-symlink'] expected_bad_symlink_item_w_meta = vfs.Item(meta=bad_symlink_value.meta, @@ -414,9 +413,9 @@ def test_resolve(): ('file', expected_file_item_w_meta)) wvpasseq(expected, res) - wvstart('lresolve: /test/latest/file-symlink') + wvstart('resolve nofollow: /test/latest/file-symlink') vfs.clear_cache() - res = lresolve(repo, '/test/latest/file-symlink') + res = resolve(repo, '/test/latest/file-symlink', follow=False) wvpasseq(4, len(res)) file_symlink_value = tip_tree['file-symlink'] expected_file_symlink_item_w_meta = vfs.Item(meta=file_symlink_value.meta, @@ -457,10 +456,10 @@ def test_resolve(): '/test/latest/file-symlink/../', '/test/latest/file-symlink/../.', '/test/latest/file-symlink/../..'): - wvstart('lresolve: ' + path) + wvstart('resolve nofollow: ' + path) vfs.clear_cache() try: - lresolve(repo, path) + resolve(repo, path, follow=False) except vfs.IOError as res_ex: wvpasseq(ENOTDIR, res_ex.errno) wvpasseq(['', 'test', save_time_str, 'file'], @@ -475,9 +474,9 @@ def test_resolve(): wvpasseq(ENOTDIR, res_ex.errno) wvpasseq(None, res_ex.terminus) - wvstart('lresolve: /test/latest/dir-symlink') + wvstart('resolve nofollow: /test/latest/dir-symlink') vfs.clear_cache() - res = lresolve(repo, '/test/latest/dir-symlink') + res = resolve(repo, '/test/latest/dir-symlink', follow=False) wvpasseq(4, len(res)) dir_symlink_value = tip_tree['dir-symlink'] expected_dir_symlink_item_w_meta = vfs.Item(meta=dir_symlink_value.meta, @@ -495,8 +494,10 @@ def test_resolve(): ('test', test_revlist_w_meta), (save_time_str, expected_latest_item_w_meta), ('dir', expected_dir_item)) + def lresolve(*args, **keys): + return resolve(*args, **dict(keys, follow=False)) for resname, resolver in (('resolve', resolve), - ('lresolve', lresolve)): + ('resolve nofollow', lresolve)): for path in ('/test/latest/dir-symlink/', '/test/latest/dir-symlink/.'): wvstart(resname + ': ' + path) @@ -611,7 +612,6 @@ def test_resolve_loop(): with no_lingering_errors(): with test_tempdir('bup-tvfs-resloop-') as tmpdir: resolve = vfs.resolve - lresolve = vfs.lresolve bup_dir = tmpdir + '/bup' environ['GIT_DIR'] = bup_dir environ['BUP_DIR'] = bup_dir diff --git a/lib/bup/vfs.py b/lib/bup/vfs.py index e36df64..e3ea16f 100644 --- a/lib/bup/vfs.py +++ b/lib/bup/vfs.py @@ -60,7 +60,6 @@ from bup.compat import range from bup.git import BUP_CHUNKED, cp, get_commit_items, parse_commit, tree_decode from bup.helpers import debug2, last from bup.metadata import Metadata -from bup.repo import LocalRepo, RemoteRepo class IOError(exceptions.IOError): @@ -780,9 +779,9 @@ def contents(repo, item, names=None, want_meta=True): for x in item_gen: yield x -def _resolve_path(repo, path, parent=None, want_meta=True, deref=False): +def _resolve_path(repo, path, parent=None, want_meta=True, follow=True): cache_key = b'res:%d%d%d:%s\0%s' \ - % (bool(want_meta), bool(deref), repo.id(), + % (bool(want_meta), bool(follow), repo.id(), ('/'.join(x[0] for x in parent) if parent else ''), '/'.join(path)) resolution = cache_get(cache_key) @@ -815,7 +814,7 @@ def _resolve_path(repo, path, parent=None, want_meta=True, deref=False): % (parent,)) is_absolute, must_be_dir, future = _decompose_path(path) if must_be_dir: - deref = True + follow = True if not future: # path was effectively '.' or '/' if is_absolute: return notice_resolution((('', _root),)) @@ -874,7 +873,7 @@ def _resolve_path(repo, path, parent=None, want_meta=True, deref=False): item = item._replace(meta=dir_meta) past.append((segment, item)) else: # symlink - if not future and not deref: + if not future and not follow: past.append((segment, item),) continue if hops > 100: @@ -893,21 +892,15 @@ def _resolve_path(repo, path, parent=None, want_meta=True, deref=False): future.extend(target_future) hops += 1 -def lresolve(repo, path, parent=None, want_meta=True): - """Perform exactly the same function as resolve(), except if the final - path element is a symbolic link, don't follow it, just return it - in the result. - - """ - return _resolve_path(repo, path, parent=parent, want_meta=want_meta, - deref=False) - -def resolve(repo, path, parent=None, want_meta=True): +def resolve(repo, path, parent=None, want_meta=True, follow=True): """Follow the path in the virtual filesystem and return a tuple representing the location, if any, denoted by the path. Each element in the result tuple will be (name, info), where info will be a VFS item that can be passed to functions like item_mode(). + If follow is false, and if the final path element is a symbolic + link, don't follow it, just return it in the result. + If a path segment that does not exist is encountered during resolution, the result will represent the location of the missing item, and that item in the result will be None. @@ -948,28 +941,29 @@ def resolve(repo, path, parent=None, want_meta=True): """ result = _resolve_path(repo, path, parent=parent, want_meta=want_meta, - deref=True) + follow=follow) _, leaf_item = result[-1] - if leaf_item: + if leaf_item and follow: assert not S_ISLNK(item_mode(leaf_item)) return result def try_resolve(repo, path, parent=None, want_meta=True): """If path does not refer to a symlink, does not exist, or refers to a - valid symlink, behave exactly like resolve(). If path refers to - an invalid symlink, behave like lresolve. + valid symlink, behave exactly like resolve(..., follow=True). If + path refers to an invalid symlink, behave like resolve(..., + follow=False). """ - res = lresolve(repo, path, parent=parent, want_meta=want_meta) + res = resolve(repo, path, parent=parent, want_meta=want_meta, follow=False) leaf_name, leaf_item = res[-1] if not leaf_item: return res if not S_ISLNK(item_mode(leaf_item)): return res - deref = resolve(repo, leaf_name, parent=res[:-1], want_meta=want_meta) - deref_name, deref_item = deref[-1] - if deref_item: - return deref + follow = resolve(repo, leaf_name, parent=res[:-1], want_meta=want_meta) + follow_name, follow_item = follow[-1] + if follow_item: + return follow return res def augment_item_meta(repo, item, include_size=False):