Build the rsync command as an argument list

The rsync command was built as a string and split with shlex.split,
so paths containing quotes broke it, and the key file path in the -e
command was split on spaces. Pass each path as its own argument, and
quote the key file and user name inside the -e command, which rsync
splits itself.
This commit is contained in:
Fuxino
2026-10-08 20:24:53 +02:00
parent baf5b07d23
commit 73de4f8d25
+15 -12
View File
@@ -552,24 +552,27 @@ class Backup:
logger.info('Copying files. This may take a long time...') logger.info('Copying files. This may take a long time...')
if self._last_backup == '': # Build the argument list directly, so paths with spaces or quotes are passed unchanged
rsync = f'/usr/bin/rsync {self.options} --exclude-from={self._exclude_path} ' +\ args = ['/usr/bin/rsync', *self.options.split()]
f'--files-from={self._inputs_path} / "{self._server}{self._output_dir}"'
else:
rsync = f'/usr/bin/rsync {self.options} --link-dest="{self._last_backup}" --exclude-from=' +\
f'{self._exclude_path} --files-from={self._inputs_path} / "{self._server}{self._output_dir}"'
if self._last_backup != '':
args.append(f'--link-dest={self._last_backup}')
args.extend([f'--exclude-from={self._exclude_path}', f'--files-from={self._inputs_path}',
'/', f'{self._server}{self._output_dir}'])
# rsync splits the -e command itself, so quote the parts that may contain spaces
if self.ssh_keyfile is not None: if self.ssh_keyfile is not None:
rsync = f'{rsync} -e \'ssh -i {self.ssh_keyfile} -o StrictHostKeyChecking=accept-new\'' ssh = f'ssh -i {shlex.quote(self.ssh_keyfile)} -o StrictHostKeyChecking=accept-new'
elif self._password_auth and which('sshpass'): elif self._password_auth and which('sshpass'):
rsync = f'{rsync} -e \'sshpass -e ssh -l {self.ssh_user} -o StrictHostKeyChecking=accept-new\'' ssh = f'sshpass -e ssh -l {shlex.quote(self.ssh_user)} -o StrictHostKeyChecking=accept-new'
else: else:
rsync = f'{rsync} -e \'ssh -o StrictHostKeyChecking=accept-new\'' ssh = 'ssh -o StrictHostKeyChecking=accept-new'
args.extend(['-e', ssh])
if self._remote and self.remote_sudo: if self._remote and self.remote_sudo:
rsync = f'{rsync} --rsync-path="sudo rsync"' args.append('--rsync-path=sudo rsync')
args = shlex.split(rsync)
with Popen(args, stdin=PIPE, stdout=PIPE, stderr=STDOUT, shell=False) as p: with Popen(args, stdin=PIPE, stdout=PIPE, stderr=STDOUT, shell=False) as p:
output: Union[bytes, List[str]] output: Union[bytes, List[str]]