Merge pull request #6706 from ThomasWaldmann/revert-no-scp-in-master

Revert "Remove scp syntax for locations (#6697)"
This commit is contained in:
TW 2022-05-18 17:58:56 +02:00 committed by GitHub
commit b10d661185
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 80 additions and 47 deletions

View File

@ -81,7 +81,7 @@ The options which are added to the key will perform the following:
Due to the ``cd`` command we use, the server automatically changes the current
working directory. Then client doesn't need to have knowledge of the absolute
or relative remote repository path and can directly access the repositories at
``ssh://<user>@<host>/./<repo>``.
``<user>@<host>:<repo>``.
.. note:: The setup above ignores all client given commandline parameters
which are normally appended to the `borg serve` command.
@ -93,21 +93,21 @@ The client needs to initialize the `pictures` repository like this:
::
borg init ssh://backup@backup01.srv.local/./pictures
borg init backup@backup01.srv.local:pictures
Or with the full path (should actually never be used, as only for demonstrational purposes).
The server should automatically change the current working directory to the `<client fqdn>` folder.
::
borg init ssh://backup@backup01.srv.local/home/backup/repos/johndoe.clnt.local/pictures
borg init backup@backup01.srv.local:/home/backup/repos/johndoe.clnt.local/pictures
When `johndoe.clnt.local` tries to access a not restricted path the following error is raised.
John Doe tries to backup into the Web 01 path:
::
borg init ssh://backup@backup01.srv.local/home/backup/repos/web01.srv.local/pictures
borg init backup@backup01.srv.local:/home/backup/repos/web01.srv.local/pictures
::

View File

@ -405,7 +405,7 @@ Borg can initialize and access repositories on remote hosts if the
host is accessible using SSH. This is fastest and easiest when Borg
is installed on the remote host, in which case the following syntax is used::
$ borg init ssh://user@hostname/path/to/repo
$ borg init user@hostname:/path/to/repo
Note: please see the usage chapter for a full documentation of repo URLs.

View File

@ -14,16 +14,32 @@ Note: you may also prepend a ``file://`` to a filesystem path to get URL style.
**Remote repositories** accessed via ssh user@host:
``ssh://user@host:port/path/to/repo`` - remote repo, absolute path
``user@host:/path/to/repo`` - remote repo, absolute path
``ssh://user@host:port/path/to/repo`` - same, alternative syntax, port can be given
**Remote repositories with relative paths** can be given using this syntax:
``user@host:path/to/repo`` - path relative to current directory
``user@host:~/path/to/repo`` - path relative to user's home directory
``user@host:~other/path/to/repo`` - path relative to other's home directory
Note: giving ``user@host:/./path/to/repo`` or ``user@host:/~/path/to/repo`` or
``user@host:/~other/path/to/repo`` is also supported, but not required here.
**Remote repositories with relative paths, alternative syntax with port**:
``ssh://user@host:port/./path/to/repo`` - path relative to current directory
``ssh://user@host:port/~/path/to/repo`` - path relative to user's home directory
``ssh://user@host:port/~other/path/to/repo`` - path relative to other's home directory
If you frequently need the same repo URL, it is a good idea to set the
``BORG_REPO`` environment variable to set a default for the repo URL:
@ -36,31 +52,3 @@ to use the default - it will be read from BORG_REPO then.
Use ``::`` syntax to give the repo URL when syntax requires giving a positional
argument for the repo (e.g. ``borg mount :: /mnt``).
Converting from scp-style repo URLs
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Borg does not support scp-style repo URLs any more.
Here is how you can convert to URL style:
::
user@host:path/to/repo # relative to cwd
-->
ssh://user@host:22/./path/to/repo # relative to cwd
or (usually the cwd is the home dir after ssh login)
ssh://user@host:22/~/path/to/repo # relative to home dir
user@host:/path/to/repo # absolute repo path
-->
ssh://user@host:22/path/to/repo # absolute repo path
Notes:
Port 22 is the default, so you can omit the ``:22`` if you like.
If you used some hack to use a non-standard port (which was not directly
supported by the scp-style repo specification), you can now do it that way
with the ``ssh:`` URL and remove the hack (usually in ssh configuration or
given via ``--rsh`` borg option).

View File

@ -15,8 +15,8 @@ Examples
# Remote repository (accesses a remote borg via ssh)
# repokey: stores the (encrypted) key into <REPO_DIR>/config
$ borg init --encryption=repokey-aes-ocb ssh://user@hostname/./backup
$ borg init --encryption=repokey-aes-ocb user@hostname:backup
# Remote repository (accesses a remote borg via ssh)
# keyfile: stores the (encrypted) key into ~/.config/borg/keys/
$ borg init --encryption=keyfile-aes-ocb ssh://user@hostname/./backup
$ borg init --encryption=keyfile-aes-ocb user@hostname:backup

View File

@ -315,7 +315,7 @@ class Location:
# path must not contain :: (it ends at :: or string end), but may contain single colons.
# to avoid ambiguities with other regexes, it must also not start with ":" nor with "//" nor with "ssh://".
local_path_re = r"""
scp_path_re = r"""
(?!(:|//|ssh://)) # not starting with ":" or // or ssh://
(?P<path>([^:]|(:(?!:)))+) # any chars, but no "::"
"""
@ -361,8 +361,13 @@ class Location:
(?P<proto>file):// # file://
""" + file_path_re + optional_archive_re, re.VERBOSE) # servername/path, path or path::archive
local_re = re.compile(
local_path_re + optional_archive_re, re.VERBOSE) # local path with optional archive
# note: scp_re is also used for local paths
scp_re = re.compile(r"""
(
""" + optional_user_re + host_re + r""" # user@ (optional), host name or address
: # : (required!)
)? # user@host: part is optional
""" + scp_path_re + optional_archive_re, re.VERBOSE) # path with optional archive
# get the repo from BORG_REPO env and the optional archive from param.
# if the syntax requires giving REPOSITORY (see "borg mount"),
@ -438,11 +443,13 @@ class Location:
self.path = normpath_special(m.group('path'))
self.archive = m.group('archive')
return True
m = self.local_re.match(text)
m = self.scp_re.match(text)
if m:
self.user = m.group('user')
self._host = m.group('host')
self.path = normpath_special(m.group('path'))
self.archive = m.group('archive')
self.proto = 'file'
self.proto = self._host and 'ssh' or 'file'
return True
return False

View File

@ -4014,7 +4014,7 @@ class ManifestAuthenticationTest(ArchiverTestCaseBase):
class RemoteArchiverTestCase(ArchiverTestCase):
prefix = 'ssh://__testsuite__'
prefix = '__testsuite__:'
def open_repository(self):
return RemoteRepository(Location(self.repository_location))

View File

@ -113,6 +113,32 @@ class TestLocationWithoutEnv:
"Location(proto='file', user=None, host=None, port=None, path='/some/path', archive=None)"
assert Location('file:///some/path').to_key_filename() == keys_dir + 'some_path'
def test_scp(self, monkeypatch, keys_dir):
monkeypatch.delenv('BORG_REPO', raising=False)
assert repr(Location('user@host:/some/path::archive')) == \
"Location(proto='ssh', user='user', host='host', port=None, path='/some/path', archive='archive')"
assert repr(Location('user@host:/some/path')) == \
"Location(proto='ssh', user='user', host='host', port=None, path='/some/path', archive=None)"
assert repr(Location('user@[::]:/some/path::archive')) == \
"Location(proto='ssh', user='user', host='::', port=None, path='/some/path', archive='archive')"
assert repr(Location('user@[::]:/some/path')) == \
"Location(proto='ssh', user='user', host='::', port=None, path='/some/path', archive=None)"
assert repr(Location('user@[2001:db8::]:/some/path::archive')) == \
"Location(proto='ssh', user='user', host='2001:db8::', port=None, path='/some/path', archive='archive')"
assert repr(Location('user@[2001:db8::]:/some/path')) == \
"Location(proto='ssh', user='user', host='2001:db8::', port=None, path='/some/path', archive=None)"
assert repr(Location('user@[2001:db8::c0:ffee]:/some/path::archive')) == \
"Location(proto='ssh', user='user', host='2001:db8::c0:ffee', port=None, path='/some/path', archive='archive')"
assert repr(Location('user@[2001:db8::c0:ffee]:/some/path')) == \
"Location(proto='ssh', user='user', host='2001:db8::c0:ffee', port=None, path='/some/path', archive=None)"
assert repr(Location('user@[2001:db8::192.0.2.1]:/some/path::archive')) == \
"Location(proto='ssh', user='user', host='2001:db8::192.0.2.1', port=None, path='/some/path', archive='archive')"
assert repr(Location('user@[2001:db8::192.0.2.1]:/some/path')) == \
"Location(proto='ssh', user='user', host='2001:db8::192.0.2.1', port=None, path='/some/path', archive=None)"
assert Location('user@[2001:db8::192.0.2.1]:/some/path').to_key_filename() == keys_dir + '2001_db8__192_0_2_1__some_path'
assert repr(Location('user@[2a02:0001:0002:0003:0004:0005:0006:0007]:/some/path')) == \
"Location(proto='ssh', user='user', host='2a02:0001:0002:0003:0004:0005:0006:0007', port=None, path='/some/path', archive=None)"
def test_smb(self, monkeypatch, keys_dir):
monkeypatch.delenv('BORG_REPO', raising=False)
assert repr(Location('file:////server/share/path::archive')) == \
@ -171,6 +197,8 @@ class TestLocationWithoutEnv:
def test_user_parsing(self):
# see issue #1930
assert repr(Location('host:path::2016-12-31@23:59:59')) == \
"Location(proto='ssh', user=None, host='host', port=None, path='path', archive='2016-12-31@23:59:59')"
assert repr(Location('ssh://host/path::2016-12-31@23:59:59')) == \
"Location(proto='ssh', user=None, host='host', port=None, path='/path', archive='2016-12-31@23:59:59')"
@ -254,6 +282,15 @@ class TestLocationWithEnv:
assert repr(Location()) == \
"Location(proto='file', user=None, host=None, port=None, path='/some/path', archive=None)"
def test_scp(self, monkeypatch):
monkeypatch.setenv('BORG_REPO', 'user@host:/some/path')
assert repr(Location('::archive')) == \
"Location(proto='ssh', user='user', host='host', port=None, path='/some/path', archive='archive')"
assert repr(Location('::')) == \
"Location(proto='ssh', user='user', host='host', port=None, path='/some/path', archive=None)"
assert repr(Location()) == \
"Location(proto='ssh', user='user', host='host', port=None, path='/some/path', archive=None)"
def test_folder(self, monkeypatch):
monkeypatch.setenv('BORG_REPO', 'path')
assert repr(Location('::archive')) == \

View File

@ -861,7 +861,7 @@ class RemoteRepositoryTestCase(RepositoryTestCase):
repository = None # type: RemoteRepository
def open(self, create=False):
return RemoteRepository(Location('ssh://__testsuite__' + os.path.join(self.tmppath, 'repository')),
return RemoteRepository(Location('__testsuite__:' + os.path.join(self.tmppath, 'repository')),
exclusive=True, create=create)
def _get_mock_args(self):
@ -937,11 +937,12 @@ class RemoteRepositoryTestCase(RepositoryTestCase):
def test_ssh_cmd(self):
args = self._get_mock_args()
self.repository._args = args
assert self.repository.ssh_cmd(Location('example.com:foo')) == ['ssh', 'example.com']
assert self.repository.ssh_cmd(Location('ssh://example.com/foo')) == ['ssh', 'example.com']
assert self.repository.ssh_cmd(Location('ssh://user@example.com/foo')) == ['ssh', 'user@example.com']
assert self.repository.ssh_cmd(Location('ssh://user@example.com:1234/foo')) == ['ssh', '-p', '1234', 'user@example.com']
os.environ['BORG_RSH'] = 'ssh --foo'
assert self.repository.ssh_cmd(Location('ssh://example.com/foo')) == ['ssh', '--foo', 'example.com']
assert self.repository.ssh_cmd(Location('example.com:foo')) == ['ssh', '--foo', 'example.com']
def test_borg_cmd(self):
assert self.repository.borg_cmd(None, testing=True) == [sys.executable, '-m', 'borg.archiver', 'serve']
@ -963,7 +964,7 @@ class RemoteRepositoryTestCase(RepositoryTestCase):
'--storage-quota=314159265']
args.rsh = 'ssh -i foo'
self.repository._args = args
assert self.repository.ssh_cmd(Location('ssh://example.com/foo')) == ['ssh', '-i', 'foo', 'example.com']
assert self.repository.ssh_cmd(Location('example.com:foo')) == ['ssh', '-i', 'foo', 'example.com']
class RemoteLegacyFree(RepositoryTestCaseBase):
@ -971,7 +972,7 @@ class RemoteLegacyFree(RepositoryTestCaseBase):
def open(self, create=False):
with patch.object(RemoteRepository, 'dictFormat', True):
return RemoteRepository(Location('ssh://__testsuite__' + os.path.join(self.tmppath, 'repository')),
return RemoteRepository(Location('__testsuite__:' + os.path.join(self.tmppath, 'repository')),
exclusive=True, create=create)
def test_legacy_free(self):
@ -994,7 +995,7 @@ class RemoteLegacyFree(RepositoryTestCaseBase):
class RemoteRepositoryCheckTestCase(RepositoryCheckTestCase):
def open(self, create=False):
return RemoteRepository(Location('ssh://__testsuite__' + os.path.join(self.tmppath, 'repository')),
return RemoteRepository(Location('__testsuite__:' + os.path.join(self.tmppath, 'repository')),
exclusive=True, create=create)
def test_crash_before_compact(self):