Skip to content

Fix ZIP member selection - #19256

Merged
LibretroAdmin merged 2 commits into
libretro:masterfrom
SaveEditors:fix-zip-member-selection
Jul 28, 2026
Merged

Fix ZIP member selection#19256
LibretroAdmin merged 2 commits into
libretro:masterfrom
SaveEditors:fix-zip-member-selection

Conversation

@SaveEditors

@SaveEditors SaveEditors commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Description

ZIP file lists can expose macOS AppleDouble metadata before the actual content file. Explicit ZIP member reads also use substring matching, so a request for game.nes can select __MACOSX/._game.nes.

This change ignores __MACOSX and ._ metadata entries during ZIP-compatible archive listing. Buffered and incremental zip#member reads now require the complete entry name, consistent with the 7z backend.

Malformed archives that fail backend initialization now pass through the existing error cleanup before returning. This closes the opened archive stream and clears transfer ownership without changing the iterator's failure result.

The archive sample generates its ZIP inputs and verifies malformed archives, filtered and unfiltered listings, buffered reads, incremental entry-source reads, and initialization-failure cleanup without adding binary fixtures.

Testing

Windows:

  • make clean all
  • archive_zip_test.exe
  • MinGW Makefile.win build with -std=gnu89
  • retroarch-cleanup-test.exe --version

CI:

  • Build and run libretro-common/samples with ASan/UBSan

Related Issues

Fixes #19242.

Related Pull Requests

None.

Reviewers

@SaveEditors
SaveEditors force-pushed the fix-zip-member-selection branch from d0bf9ce to ff5e3da Compare July 25, 2026 14:05
@SaveEditors
SaveEditors force-pushed the fix-zip-member-selection branch from ff5e3da to 50b288a Compare July 25, 2026 17:10
The sidecar filter sat in file_archive_get_file_list_cb, which meant it
ran once per central-directory entry and only helped the one consumer
that goes through that callback.  Move it into the ZIP backend's
directory walk instead.

The callback had to re-derive the backend from the path on every member
to decide whether the archive was a ZIP - a PATH_MAX_LENGTH strlcpy, an
archive-delimiter scan, an extension parse and up to three case-folded
comparisons per entry - and then walk the entry name byte by byte to
find its segment boundaries.  Inside zip_parse_file_iterate_step_internal
the format is known and the central-directory name length has already
been read, so the check needs no strlen, no scan of the whole name, and
no backend lookup; the entry is dropped before its name is even copied
out of the directory block.

Doing it in the walk also covers every consumer rather than the listing
callback alone.  file_archive_extract_cb selects on extension, so a
"._game.nes" sidecar matched it and cores requiring uncompressed content
were still handed the metadata file:

  before   file_archive_extract_file(mac.zip, "nes") -> ._game.nes
  after    file_archive_extract_file(mac.zip, "nes") -> game.nes

An archive holding nothing but sidecars now reports no extractable
content instead of extracting one.  The end-of-central-directory record
counts the skipped entries, so step_total is decremented as they are
dropped, keeping the decompress progress denominator honest; a Mac-made
two-entry archive reported 50% at completion before and reports 100% now.

Listing 100,000 entries, -O2, best of 60 per run, 9 interleaved runs:

  master               12.39 ms
  filter in callback   17.69 ms   (+42.8%)
  filter in the walk   12.71 ms   (+2.6%)

Listing results are unchanged on every fixture tried, including
lookalikes ("My__MACOSX/rom.nes", "__MACOSXX/rom2.nes", "_.nes",
"a._b.nes"), nested sidecars, an explicit "__MACOSX/" directory entry,
a sidecar-only archive and an empty archive.  Explicit member reads are
unchanged except that a sidecar can no longer be requested by name,
which follows from the entry no longer being enumerated.

Verified against zlib and against the built-in inflate, with
ARCHIVE_HAVE_ZLIB and ARCHIVE_USE_BUILTIN_DEFLATE forced both ways, and
with HAVE_ZLIB alone.  The sample suite passes unmodified, plain and
under ASan/UBSan.
@LibretroAdmin
LibretroAdmin merged commit 31e63a1 into libretro:master Jul 28, 2026
44 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Archive auto-selection loads macOS AppleDouble metadata instead of valid ROM

2 participants