diff --git a/NEWS b/NEWS index a63be326..341983b3 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,7 @@ variables available for explicit use in your commands. See the documentation for more information: https://torsion.org/borgmatic/docs/how-to/run-arbitrary-borg-commands/ * #719: Fix an error when running "borg key export" through borgmatic. + * #720: Fix an error when dumping a MySQL database and the "exclude_nodump" option is set. * When merging two configuration files, error gracefully if the two files do not adhere to the same format. diff --git a/borgmatic/borg/create.py b/borgmatic/borg/create.py index 618376a5..1ec54fdd 100644 --- a/borgmatic/borg/create.py +++ b/borgmatic/borg/create.py @@ -280,14 +280,17 @@ def collect_special_file_paths( create_command, local_path, working_directory, borg_environment, skip_directories ): ''' - Given a Borg create command as a tuple, a local Borg path, a working directory, and a dict of + Given a Borg create command as a tuple, a local Borg path, a working directory, a dict of environment variables to pass to Borg, and a sequence of parent directories to skip, collect the paths for any special files (character devices, block devices, and named pipes / FIFOs) that Borg would encounter during a create. These are all paths that could cause Borg to hang if its --read-special flag is used. ''' + # Omit "--exclude-nodump" from the Borg dry run command, because that flag causes Borg to open + # files including any named pipe we've created. paths_output = execute_command_and_capture_output( - create_command + ('--dry-run', '--list'), + tuple(argument for argument in create_command if argument != '--exclude-nodump') + + ('--dry-run', '--list'), capture_stderr=True, working_directory=working_directory, extra_environment=borg_environment, diff --git a/tests/unit/borg/test_create.py b/tests/unit/borg/test_create.py index c798c446..33e95607 100644 --- a/tests/unit/borg/test_create.py +++ b/tests/unit/borg/test_create.py @@ -449,6 +449,25 @@ def test_collect_special_file_paths_excludes_non_special_files(): ) == ('/foo', '/baz') +def test_collect_special_file_paths_omits_exclude_no_dump_flag_from_command(): + flexmock(module).should_receive('execute_command_and_capture_output').with_args( + ('borg', 'create', '--dry-run', '--list'), + capture_stderr=True, + working_directory=None, + extra_environment=None, + ).and_return('Processing files ...\n- /foo\n+ /bar\n- /baz').once() + flexmock(module).should_receive('special_file').and_return(True) + flexmock(module).should_receive('any_parent_directories').and_return(False) + + module.collect_special_file_paths( + ('borg', 'create', '--exclude-nodump'), + local_path=None, + working_directory=None, + borg_environment=None, + skip_directories=flexmock(), + ) + + DEFAULT_ARCHIVE_NAME = '{hostname}-{now:%Y-%m-%dT%H:%M:%S.%f}' # noqa: FS003 REPO_ARCHIVE_WITH_PATHS = (f'repo::{DEFAULT_ARCHIVE_NAME}', 'foo', 'bar')