From 73de4f8d25fed7f8efe31eb71cad00521e151401 Mon Sep 17 00:00:00 2001 From: Fuxino Date: Thu, 8 Oct 2026 20:24:53 +0200 Subject: [PATCH] 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. --- simple_backup/simple_backup.py | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/simple_backup/simple_backup.py b/simple_backup/simple_backup.py index 7e99cc3..59a711f 100755 --- a/simple_backup/simple_backup.py +++ b/simple_backup/simple_backup.py @@ -552,24 +552,27 @@ class Backup: logger.info('Copying files. This may take a long time...') - if self._last_backup == '': - rsync = f'/usr/bin/rsync {self.options} --exclude-from={self._exclude_path} ' +\ - 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}"' + # Build the argument list directly, so paths with spaces or quotes are passed unchanged + args = ['/usr/bin/rsync', *self.options.split()] + 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: - 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'): - 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: - 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: - rsync = f'{rsync} --rsync-path="sudo rsync"' - - args = shlex.split(rsync) + args.append('--rsync-path=sudo rsync') with Popen(args, stdin=PIPE, stdout=PIPE, stderr=STDOUT, shell=False) as p: output: Union[bytes, List[str]]