summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnton Khirnov <anton@khirnov.net>2020-10-20 12:14:36 +0200
committerAnton Khirnov <anton@khirnov.net>2020-10-20 12:26:22 +0200
commit38370e7c144620fe0e2b9d435d0d8b8bbb9dbae6 (patch)
treed05ffb03bca62a2ae5b3f62ee1882dd492e65a97
parent58a09ee3d34439ddce8391d375c0028bd242e3f2 (diff)
TargetSSHLVM: resolve remote dirs
Should handle remote symlinks properly.
-rw-r--r--lbup/targets.py40
1 files changed, 19 insertions, 21 deletions
diff --git a/lbup/targets.py b/lbup/targets.py
index f6f7e82..64dd80f 100644
--- a/lbup/targets.py
+++ b/lbup/targets.py
@@ -50,8 +50,8 @@ class Target(ABC):
raise ValueError('One or more dirs to backup required')
self.name = name
- self.dirs = list(map(AbsPath, dirs))
- self.excludes = list(map(AbsPath, excludes))
+ self.dirs = dirs
+ self.excludes = excludes
if logger is None:
self._logger = logging.getLogger('%s.%s' % (self.__class__.__name__, self.name))
@@ -182,16 +182,15 @@ class TargetSSH(Target):
return out
- def _resolve_remote_bupdir(self, ssh):
- bupdir = self._paramiko_exec_cmd(ssh, 'realpath -e ' + self._remote_bupdir).splitlines()
- if (len(bupdir) != 1 or len(bupdir[0]) <= 1 or bupdir[0][0] != '/' or
- re.search(r'\s', bupdir[0])):
- raise BackupException('Invalid BUP_DIR on the remote target: %s' % str(bupdir))
- return bupdir[0]
+ def _resolve_remote_path(self, ssh, path):
+ path = self._paramiko_exec_cmd(ssh, 'realpath -e %s' % path).splitlines()
+ if len(path) != 1:
+ raise BackupException('Expected exactly one path from realpath', path)
+ return AbsPath(path[0])
def save(self, dry_run = False):
with _ssh_client.SSHConnection(self._remote) as ssh:
- remote_bupdir = self._resolve_remote_bupdir(ssh)
+ remote_bupdir = self._resolve_remote_path(ssh, self._remote_bupdir)
bup_exec = ['bup', 'on', '%s@%s' % (self._remote.username, self._remote.host),
'-d', remote_bupdir]
@@ -236,12 +235,12 @@ class TargetSSHLVM(TargetSSH):
"""
return 1
- def _resolve_mntdev(self, mntinfo):
+ def _resolve_mntdev(self, mntinfo, dirs):
"""
Find out which LV to snapshot.
This also checks that all the dirs are on the same LV and no non-trivial
- topologies (such as symlinks or bind mounts) are involved,
+ topologies (like bind mounts) are involved,
otherwise a BackupException is raised.
Return a tuple of (devnum, mountpoint)
@@ -249,7 +248,7 @@ class TargetSSHLVM(TargetSSH):
devnum = None
mountpoint = None
fstype = None
- for d in self.dirs:
+ for d in dirs:
mp = mntinfo.mountpoint_for_path(d)
e = list(mntinfo.entries_for_mountpoint(mp))
@@ -269,9 +268,6 @@ class TargetSSHLVM(TargetSSH):
raise BackupException('Mismatching device numbers/mountpoints',
dn, devnum, mp, mountpoint)
- # TODO? check that there are no symlinks?
- # by running stat maybe?
-
return (devnum, mountpoint, fstype)
def _resolve_lv(self, ssh, devnum):
@@ -349,8 +345,10 @@ class TargetSSHLVM(TargetSSH):
conn_tgt = stack.enter_context(_ssh_client.SSHConnection(self._remote))
conn_root = stack.enter_context(_ssh_client.SSHConnection(self._root_remote()))
- # resolve the path to BUP_DIR on the remote
- bupdir = self._resolve_remote_bupdir(conn_tgt)
+ # resolve the remote paths
+ bupdir = self._resolve_remote_path(conn_tgt, self._remote_bupdir)
+ dirs = [self._resolve_remote_path(conn_tgt, d) for d in self.dirs]
+ excludes = [self._resolve_remote_path(conn_tgt, d) for d in self.excludes]
# make sure the mount directory exists
# due to how bup index works, the mount directory has to stay the
@@ -366,7 +364,7 @@ class TargetSSHLVM(TargetSSH):
self._paramiko_exec_cmd(conn_root, 'cat /proc/%d/mountinfo' % self._mntns_pid,
decode = False))
- devnum, mountpoint, fstype = self._resolve_mntdev(mntinfo)
+ devnum, mountpoint, fstype = self._resolve_mntdev(mntinfo, dirs)
# make sure we have a valid fstype
fstype = fstype.decode('ascii')
@@ -377,12 +375,12 @@ class TargetSSHLVM(TargetSSH):
"%d:%d" % (devnum >> 8, devnum & 255), fstype, mountpoint)
bup_exec = ['bup', 'on', '%s@%s' % (self._remote.username, self._remote.host),
- '-d', bupdir]
+ '-d', str(bupdir)]
save_opts = ['--graft=%s=%s' % (snapshot_mount, mountpoint)]
index_opts = ['--no-check-device']
reparent = (mountpoint, AbsPath(snapshot_mount))
- dirs = [d.reparent(*reparent) for d in self.dirs]
- excludes = [d.reparent(*reparent) for d in self.excludes]
+ dirs = [d.reparent(*reparent) for d in dirs]
+ excludes = [d.reparent(*reparent) for d in excludes]
stack.enter_context(self._mount_snapshot(conn_root, devnum, snapshot_mount, fstype))