Skip to content

Commit d25465c

Browse files
Merge pull request #7460 from dotdoom/master-ignorezeros
Add --ignore-zeros flag to import-tar
2 parents 46f1cda + 335efbf commit d25465c

File tree

2 files changed

+61
-1
lines changed

2 files changed

+61
-1
lines changed

src/borg/archiver/tar_cmds.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@ def _import_tar(self, args, repository, manifest, key, cache, tarstream):
289289
file_status_printer=self.print_file_status,
290290
)
291291

292-
tar = tarfile.open(fileobj=tarstream, mode="r|")
292+
tar = tarfile.open(fileobj=tarstream, mode="r|", ignore_zeros=args.ignore_zeros)
293293

294294
while True:
295295
tarinfo = tar.next()
@@ -445,6 +445,9 @@ def build_parser_tar(self, subparsers, common_parser, mid_common_parser):
445445
- UNIX V7 tar
446446
- SunOS tar with extended attributes
447447
448+
To import multiple tarballs into a single archive, they can be simply
449+
concatenated (e.g. using "cat") into a single file, and imported with an
450+
``--ignore-zeros`` option to skip through the stop markers between them.
448451
"""
449452
)
450453
subparser = subparsers.add_parser(
@@ -487,6 +490,12 @@ def build_parser_tar(self, subparsers, common_parser, mid_common_parser):
487490
help="only display items with the given status characters",
488491
)
489492
subparser.add_argument("--json", action="store_true", help="output stats as JSON (implies --stats)")
493+
subparser.add_argument(
494+
"--ignore-zeros",
495+
dest="ignore_zeros",
496+
action="store_true",
497+
help="ignore zero-filled blocks in the input tarball",
498+
)
490499

491500
archive_group = subparser.add_argument_group("Archive options")
492501
archive_group.add_argument(

src/borg/testsuite/archiver/tar_cmds.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,57 @@ def test_import_tar_gz(self, tar_format="GNU"):
143143
self.cmd(f"--repo={self.repository_location}", "extract", "dst")
144144
self.assert_dirs_equal("input", "output/input", ignore_ns=True, ignore_xattrs=True)
145145

146+
@requires_gnutar
147+
def test_import_concatenated_tar_with_ignore_zeros(self):
148+
self.create_test_files(create_hardlinks=False) # hardlinks become separate files
149+
os.unlink("input/flagfile")
150+
with changedir("input"):
151+
subprocess.check_call(["tar", "cf", "file1.tar", "file1"])
152+
subprocess.check_call(["tar", "cf", "the_rest.tar", "--exclude", "file1*", "."])
153+
with open("concatenated.tar", "wb") as concatenated:
154+
with open("file1.tar", "rb") as file1:
155+
concatenated.write(file1.read())
156+
# Clean up for assert_dirs_equal.
157+
os.unlink("file1.tar")
158+
159+
with open("the_rest.tar", "rb") as the_rest:
160+
concatenated.write(the_rest.read())
161+
# Clean up for assert_dirs_equal.
162+
os.unlink("the_rest.tar")
163+
164+
self.cmd(f"--repo={self.repository_location}", "rcreate", "--encryption=none")
165+
self.cmd(f"--repo={self.repository_location}", "import-tar", "--ignore-zeros", "dst", "input/concatenated.tar")
166+
# Clean up for assert_dirs_equal.
167+
os.unlink("input/concatenated.tar")
168+
169+
with changedir(self.output_path):
170+
self.cmd(f"--repo={self.repository_location}", "extract", "dst")
171+
self.assert_dirs_equal("input", "output", ignore_ns=True, ignore_xattrs=True)
172+
173+
@requires_gnutar
174+
def test_import_concatenated_tar_without_ignore_zeros(self):
175+
self.create_test_files(create_hardlinks=False) # hardlinks become separate files
176+
os.unlink("input/flagfile")
177+
with changedir("input"):
178+
subprocess.check_call(["tar", "cf", "file1.tar", "file1"])
179+
subprocess.check_call(["tar", "cf", "the_rest.tar", "--exclude", "file1*", "."])
180+
with open("concatenated.tar", "wb") as concatenated:
181+
with open("file1.tar", "rb") as file1:
182+
concatenated.write(file1.read())
183+
with open("the_rest.tar", "rb") as the_rest:
184+
concatenated.write(the_rest.read())
185+
os.unlink("the_rest.tar")
186+
187+
self.cmd(f"--repo={self.repository_location}", "rcreate", "--encryption=none")
188+
self.cmd(f"--repo={self.repository_location}", "import-tar", "dst", "input/concatenated.tar")
189+
190+
with changedir(self.output_path):
191+
self.cmd(f"--repo={self.repository_location}", "extract", "dst")
192+
193+
# Negative test -- assert that only file1 has been extracted, and the_rest has been ignored
194+
# due to zero-filled block marker.
195+
self.assert_equal(os.listdir("output"), ["file1"])
196+
146197
def test_roundtrip_pax_borg(self):
147198
self.create_test_files()
148199
self.cmd(f"--repo={self.repository_location}", "rcreate", "--encryption=none")

0 commit comments

Comments
 (0)