Skip to content

Commit 49b74bc

Browse files
committed
gh-149578: Fix tarfile.open failing on PAX archives with only global headers
When a PAX format tar archive contains only global headers and no regular members, tarfile.open() raised ReadError because the EOFHeaderError from reaching end-of-archive after the global header was being caught and converted to SubsequentHeaderError in TarInfo._proc_pax(). This prevented the caller from handling end-of-archive normally. Fix by letting EOFHeaderError propagate when processing a global header (XGLTYPE), while still treating it as a SubsequentHeaderError for extended headers (XHDTYPE) where a following file entry is mandatory.
1 parent bc1be4f commit 49b74bc

3 files changed

Lines changed: 40 additions & 0 deletions

File tree

Lib/tarfile.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1565,6 +1565,14 @@ def _proc_pax(self, tarfile):
15651565
# Fetch the next header.
15661566
try:
15671567
next = self._fromtarfile(tarfile, dircheck=False)
1568+
except EOFHeaderError:
1569+
if self.type == XGLTYPE:
1570+
# If this is a global header at the end of the archive
1571+
# (no regular members follow), let the EOFHeaderError
1572+
# propagate so the caller handles end-of-archive normally.
1573+
tarfile.offset = tarfile.fileobj.tell() - BLOCKSIZE
1574+
raise
1575+
raise SubsequentHeaderError("end of file header") from None
15681576
except HeaderError as e:
15691577
raise SubsequentHeaderError(str(e)) from None
15701578

Lib/test/test_tarfile.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2297,6 +2297,35 @@ def test_pax_global_header(self):
22972297
finally:
22982298
tar.close()
22992299

2300+
def test_pax_global_header_empty_archive(self):
2301+
# An archive that contains only a global header and no regular
2302+
# members should be opened successfully (gh-149578).
2303+
pax_headers = {"foo": "bar"}
2304+
2305+
# Create a PAX archive with global headers but no file entries.
2306+
with tarfile.open(tmpname, "w", format=tarfile.PAX_FORMAT,
2307+
pax_headers=pax_headers):
2308+
pass
2309+
2310+
# Reading the archive should work and preserve global headers.
2311+
with tarfile.open(tmpname) as tar:
2312+
self.assertEqual(tar.pax_headers, pax_headers)
2313+
self.assertEqual(tar.getmembers(), [])
2314+
2315+
# Appending to the archive should work.
2316+
with tarfile.open(tmpname, "a") as tar:
2317+
self.assertEqual(tar.pax_headers, pax_headers)
2318+
self.assertEqual(tar.getmembers(), [])
2319+
tar.addfile(tarfile.TarInfo("test"))
2320+
2321+
# Verify the appended member is present and global headers
2322+
# are preserved.
2323+
with tarfile.open(tmpname) as tar:
2324+
self.assertEqual(tar.pax_headers, pax_headers)
2325+
members = tar.getmembers()
2326+
self.assertEqual(len(members), 1)
2327+
self.assertEqual(members[0].name, "test")
2328+
23002329
def test_pax_extended_header(self):
23012330
# The fields from the pax header have priority over the
23022331
# TarInfo.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix :func:`tarfile.open` failing with :exc:`~tarfile.ReadError` when
2+
opening a PAX format tar archive that contains only global headers and
3+
no regular members.

0 commit comments

Comments
 (0)