From e829e8372d0a9f862c4f048ac7fdc5dc8ad60366 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Thu, 13 Oct 2016 00:38:04 +0200 Subject: [PATCH] implement /./relpath hack, fixes #1655 --- borg/helpers.py | 16 +++++++++++----- borg/remote.py | 4 +++- borg/testsuite/helpers.py | 10 +++++++++- 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/borg/helpers.py b/borg/helpers.py index 14caeec3c..2cceb2af1 100644 --- a/borg/helpers.py +++ b/borg/helpers.py @@ -835,26 +835,32 @@ class Location: return True def _parse(self, text): + def normpath_special(p): + # avoid that normpath strips away our relative path hack and even makes p absolute + relative = p.startswith('/./') + p = os.path.normpath(p) + return ('/.' + p) if relative else p + m = self.ssh_re.match(text) if m: self.proto = m.group('proto') self.user = m.group('user') self.host = m.group('host') self.port = m.group('port') and int(m.group('port')) or None - self.path = os.path.normpath(m.group('path')) + self.path = normpath_special(m.group('path')) self.archive = m.group('archive') return True m = self.file_re.match(text) if m: self.proto = m.group('proto') - self.path = os.path.normpath(m.group('path')) + self.path = normpath_special(m.group('path')) self.archive = m.group('archive') return True m = self.scp_re.match(text) if m: self.user = m.group('user') self.host = m.group('host') - self.path = os.path.normpath(m.group('path')) + self.path = normpath_special(m.group('path')) self.archive = m.group('archive') self.proto = self.host and 'ssh' or 'file' return True @@ -885,9 +891,9 @@ class Location: return self.path else: if self.path and self.path.startswith('~'): - path = '/' + self.path + path = '/' + self.path # /~/x = path x relative to home dir elif self.path and not self.path.startswith('/'): - path = '/~/' + self.path + path = '/./' + self.path # /./x = path x relative to cwd else: path = self.path return 'ssh://{}{}{}{}'.format('{}@'.format(self.user) if self.user else '', diff --git a/borg/remote.py b/borg/remote.py index 20d0c1857..e04802314 100644 --- a/borg/remote.py +++ b/borg/remote.py @@ -129,8 +129,10 @@ class RepositoryServer: # pragma: no cover def open(self, path, create=False, lock_wait=None, lock=True, exclusive=None, append_only=False): path = os.fsdecode(path) - if path.startswith('/~'): + if path.startswith('/~'): # /~/x = path x relative to home dir, /~username/x = relative to "user" home dir path = path[1:] + elif path.startswith('/./'): # /./x = path x relative to cwd + path = path[3:] path = os.path.realpath(os.path.expanduser(path)) if self.restrict_to_paths: # if --restrict-to-path P is given, we make sure that we only operate in/below path P. diff --git a/borg/testsuite/helpers.py b/borg/testsuite/helpers.py index 10574f5a1..1087b552d 100644 --- a/borg/testsuite/helpers.py +++ b/borg/testsuite/helpers.py @@ -69,6 +69,8 @@ class TestLocationWithoutEnv: "Location(proto='file', user=None, host=None, port=None, path='/some/absolute/path', archive='archive')" assert repr(Location('/some/absolute/path')) == \ "Location(proto='file', user=None, host=None, port=None, path='/some/absolute/path', archive=None)" + assert repr(Location('ssh://user@host/some/path')) == \ + "Location(proto='ssh', user='user', host='host', port=None, path='/some/path', archive=None)" def test_relpath(self, monkeypatch): monkeypatch.delenv('BORG_REPO', raising=False) @@ -76,6 +78,12 @@ class TestLocationWithoutEnv: "Location(proto='file', user=None, host=None, port=None, path='some/relative/path', archive='archive')" assert repr(Location('some/relative/path')) == \ "Location(proto='file', user=None, host=None, port=None, path='some/relative/path', archive=None)" + assert repr(Location('ssh://user@host/./some/path')) == \ + "Location(proto='ssh', user='user', host='host', port=None, path='/./some/path', archive=None)" + assert repr(Location('ssh://user@host/~/some/path')) == \ + "Location(proto='ssh', user='user', host='host', port=None, path='/~/some/path', archive=None)" + assert repr(Location('ssh://user@host/~user/some/path')) == \ + "Location(proto='ssh', user='user', host='host', port=None, path='/~user/some/path', archive=None)" def test_with_colons(self, monkeypatch): monkeypatch.delenv('BORG_REPO', raising=False) @@ -107,7 +115,7 @@ class TestLocationWithoutEnv: 'ssh://user@host:1234/some/path::archive'] for location in locations: assert Location(location).canonical_path() == \ - Location(Location(location).canonical_path()).canonical_path() + Location(Location(location).canonical_path()).canonical_path(), "failed: %s" % location def test_format_path(self, monkeypatch): monkeypatch.delenv('BORG_REPO', raising=False)