implement /./relpath hack, fixes #1655

This commit is contained in:
Thomas Waldmann 2016-10-13 00:38:04 +02:00
parent 12a127ace1
commit e829e8372d
3 changed files with 23 additions and 7 deletions

View File

@ -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 '',

View File

@ -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.

View File

@ -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)