diff --git a/src/borg/archive.py b/src/borg/archive.py index f9fdfc129..0fc411d3f 100644 --- a/src/borg/archive.py +++ b/src/borg/archive.py @@ -1353,12 +1353,16 @@ class FilesystemObjectProcessors: def create_helper(self, path, st, status=None, hardlinkable=True, strip_prefix=None): if strip_prefix is not None: assert not path.endswith(os.sep) - if strip_prefix.startswith(path + os.sep): + if path + os.sep == strip_prefix: + # this is the directory the slashdot hack points to - archive it as the root. + path = "." + elif strip_prefix.startswith(path + os.sep): # still on a directory level that shall be stripped - do not create an item for this! yield None, 'x', False, False return - # adjust path, remove stripped directory levels - path = path.removeprefix(strip_prefix) + else: + # adjust path, remove stripped directory levels + path = path.removeprefix(strip_prefix) safe_path = make_path_safe(path) item = Item(path=safe_path) diff --git a/src/borg/testsuite/archiver.py b/src/borg/testsuite/archiver.py index 47815fa31..4f4f39028 100644 --- a/src/borg/testsuite/archiver.py +++ b/src/borg/testsuite/archiver.py @@ -2297,6 +2297,24 @@ class ArchiverTestCase(ArchiverTestCaseBase): assert 'secondB' in output assert 'secondB/thirdB' in output + def test_create_dotslash_hack_root_metadata(self): + """Test that the slashdot hack archives the source directory metadata as the archive root.""" + os.makedirs(os.path.join(self.input_path, "first", "subdir")) + self.create_regular_file("first/file1", contents=b"hello") + self.cmd('init', '--encryption=none', self.repository_location) + archive = self.repository_location + '::test' + self.cmd('create', archive, 'input/first/./') # slashdot hack + output = self.cmd('list', archive) + # the root directory "." must be in the archive (this was the bug in #9534). + lines = output.splitlines() + assert lines[0].endswith(" .") + # children of the slashdot target must be archived. + assert "subdir" in output + assert "file1" in output + # parent directories must NOT be in the archive. + assert "input" not in output + assert "first" not in output + # def test_cmdline_compatibility(self): # self.create_regular_file('file1', size=1024 * 80) # self.cmd('init', '--encryption=repokey', self.repository_location)