Skip to content

gh-156434: Restore entry offsets when repack() fails - #156435

Open
fedonman wants to merge 1 commit into
python:mainfrom
fedonman:fix-zipfile-repack-rollback
Open

gh-156434: Restore entry offsets when repack() fails#156435
fedonman wants to merge 1 commit into
python:mainfrom
fedonman:fix-zipfile-repack-rollback

Conversation

@fedonman

@fedonman fedonman commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

ZipFile.repack() now saves the header_offset of every entry it hands to the repacker, including the removed ones, and restores them if the repack raises. A failed move no longer leaves the in-memory offsets describing a layout that was never written, so a later close() no longer commits it.

$ ./python -m test test_zipfile -m test_repack_failed_write_restores_header_offsets -v
test_repack_failed_write_restores_header_offsets (test.test_zipfile.test_core.OtherRepackTests.test_repack_failed_write_restores_header_offsets) ... ok
Total tests: run=1 (filtered)
Result: SUCCESS

$ ./python -m test test_zipfile
Total tests: run=601 skipped=3
Result: SUCCESS

No changelog entry: remove() and repack() are new in 3.16 and have not been released, so this belongs with the original entry for gh-51067.

_ZipRepacker.repack() updates ZipInfo.header_offset for each member
before moving that member's bytes, so a failing _copy_bytes() left the
in-memory offsets describing a layout that was never written.  remove()
has already set _didModify, so a later close() committed a central
directory built from those offsets, and the archive became unreadable
even though the caller had handled the error.

ZipFile.repack() now snapshots the offsets of the surviving and the
removed entries and restores them if the repack raises.  The copy only
ever moves data to lower offsets, so a partial move never overwrites
its own source and the archive falls back to the state of a remove()
with no repack().

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, I've triggered Copilot review by mistake, but since it left a valid comment, just shortening the general review message. @m-aciek

Comment thread Lib/zipfile/__init__.py
Comment on lines 2436 to 2444
self._writing = True
header_offsets = [(zinfo, zinfo.header_offset)
for zinfo in (*self.filelist, *(removed or ()))]
try:
repacker = _ZipRepacker(
strict_descriptor=strict_descriptor,
chunk_size=chunk_size,
)
repacker.repack(self, removed)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ZipFile.close() writes wrong offsets after a failed repack()

3 participants