Skip to content

Commit

Permalink
Fix error when configured source directories are not present on the f…
Browse files Browse the repository at this point in the history
…ilesystem at the time of backup (#387).
  • Loading branch information
witten committed Oct 11, 2021
1 parent 1004500 commit 449896f
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 4 deletions.
2 changes: 2 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
1.5.19.dev0
* #387: Fix error when configured source directories are not present on the filesystem at the time
of backup. Now, Borg will complain, but the backup will still continue.
* Update sample systemd service file with more granular read-only filesystem settings.
* Move Gitea and GitHub hosting from a personal namespace to an organization for better
collaboration with related projects.
Expand Down
14 changes: 10 additions & 4 deletions borgmatic/borg/create.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,18 @@ def _expand_home_directories(directories):
return tuple(os.path.expanduser(directory) for directory in directories)


def map_directories_to_devices(directories): # pragma: no cover
def map_directories_to_devices(directories):
'''
Given a sequence of directories, return a map from directory to an identifier for the device on
which that directory resides. This is handy for determining whether two different directories
are on the same filesystem (have the same device identifier).
which that directory resides or None if the path doesn't exist.
This is handy for determining whether two different directories are on the same filesystem (have
the same device identifier).
'''
return {directory: os.stat(directory).st_dev for directory in directories}
return {
directory: os.stat(directory).st_dev if os.path.exists(directory) else None
for directory in directories
}


def deduplicate_directories(directory_devices):
Expand Down Expand Up @@ -82,6 +87,7 @@ def deduplicate_directories(directory_devices):
for parent in parents:
if (
pathlib.PurePath(other_directory) == parent
and directory_devices[directory] is not None
and directory_devices[other_directory] == directory_devices[directory]
):
if directory in deduplicated:
Expand Down
25 changes: 25 additions & 0 deletions tests/unit/borg/test_create.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,30 @@ def test_expand_home_directories_considers_none_as_no_directories():
assert paths == ()


def test_map_directories_to_devices_gives_device_id_per_path():
flexmock(module.os).should_receive('stat').with_args('/foo').and_return(flexmock(st_dev=55))
flexmock(module.os).should_receive('stat').with_args('/bar').and_return(flexmock(st_dev=66))

device_map = module.map_directories_to_devices(('/foo', '/bar'))

assert device_map == {
'/foo': 55,
'/bar': 66,
}


def test_map_directories_to_devices_with_missing_path_does_not_error():
flexmock(module.os).should_receive('stat').with_args('/foo').and_return(flexmock(st_dev=55))
flexmock(module.os).should_receive('stat').with_args('/bar').and_raise(FileNotFoundError)

device_map = module.map_directories_to_devices(('/foo', '/bar'))

assert device_map == {
'/foo': 55,
'/bar': None,
}


@pytest.mark.parametrize(
'directories,expected_directories',
(
Expand All @@ -72,6 +96,7 @@ def test_expand_home_directories_considers_none_as_no_directories():
({'/root': 1, '/root/foo/': 1}, ('/root',)),
({'/root': 1, '/root/foo': 2}, ('/root', '/root/foo')),
({'/root/foo': 1, '/root': 1}, ('/root',)),
({'/root': None, '/root/foo': None}, ('/root', '/root/foo')),
({'/root': 1, '/etc': 1, '/root/foo/bar': 1}, ('/etc', '/root')),
({'/root': 1, '/root/foo': 1, '/root/foo/bar': 1}, ('/root',)),
({'/dup': 1, '/dup': 1}, ('/dup',)),
Expand Down

0 comments on commit 449896f

Please sign in to comment.