Host-side verification that the ZIP engine can extract a >4GiB zip64
entry end to end (4.7 GiB fixture, content byte-compared):
- tests/compat/sys/statvfs.h: resolve the path before taking the drive
letter (relative paths used to fail) and scale f_bavail by 4096 since
`unsigned long` is 32-bit on Windows and silently truncated ~250GB
down to ~2.3GB; real 64-bit hosts and the PS5 (LP64) are unaffected.
- tests/bigfile_e2e.c: standalone driver to extract one large archive
and print the engine result (verification via cmp/sha256 by caller).
- tests/run-tests.sh: incremental clean instead of `rm -rf` of the whole
build tree.
- Makefile: force -DHAVE_FSEEKO so minizip uses fseeko/ftello instead of
falling back to fseek/ftell on libcs without the probe.
Result: 4.7 GiB zip64 extracts OK on host in ~6s, output identical to
source. Engine 64-bit write path confirmed clean; PS5 failure needs the
strerror passthrough build for real-machine errno capture.
unrar rijndael/system call __builtin_cpu_supports (AES-NI probe) which
clang lowers to __cpu_model + __cpu_indicator_init; the FreeBSD-style PS5
sysroot ships no libgcc, so the link failed. Add a PS5-only (x86_64, non
linux/win) stub TU; host builds keep the real libgcc-provided symbols.
The link driver is now the C++ compiler (needed for unrar objects), but
it treats the .c files on the command line as C++ and clang 18 rejects
that with -Werror=deprecated. Pass -x c before the C source list and
-x none before the object files.
isnt.cpp / motw.cpp need windows.h (OS version / Zone.Identifier checks)
and are not in the official unrar UNIX makefile; the _UNIX branch
#ifdef's their symbols out, so PS5 and linux builds must not compile
them (first PS5 build failed on isnt.cpp). run-tests.sh MinGW flow
keeps them.
build-elf.sh: make failure previously fell through to step 7 which
reported the STALE elf as OK (sha unchanged, user confused); make now
aborts the script (pipefail already set).
Replace the dmc_unrar 1.7.0 backend (third_party/unrar/) with the rarlab
UnRAR 7.20.1 source vendored in third_party/unrar7/ (commit c68c0de),
driven through its C-compatible DLL API.
Why: dmc_unrar only dispatches RAR5 compression version 5
(switch(file->version) case 0x5000). WinRAR 6.x/7.x archives (algorithm
"v6") land on the default branch -> DMC_UNRAR_FILE_UNSUPPORTED_VERSION
-> user-visible "corrupt archive", confirmed on a real v6:8M archive on
2026-09-05. unrar 7.20.1 natively handles v6, multi-volume (.partNN.rar
auto-merge) and encrypted archives.
Engine changes (src/rar_extract.c):
- Facade include swapped to third_party/unrar7/unrar_c_api.h; all calls
now go through RAROpenArchiveEx/RARReadHeaderEx/RARProcessFile/
RARCloseArchive. scan + extract each re-open the archive (the DLL API
is sequential, unlike dmc's index-based access).
- Error translation rewritten for the ERAR_* code set.
- extract no longer builds each staging path by hand (open_parent_dirs/
extract_one removed): unrar writes the whole tree under the staging
root; names were validated during scan.
- Bug fix: normalize_name() cleared the caller-provided *is_dir (it set
*is_dir = 0), turning directory entries into files and tripping the
duplicate detector whenever an explicit directory header followed
files beneath it (real v6 archive had exactly this). RHDF_DIRECTORY
is now honored end to end.
- Encrypted RAR still fails up front (ZIPX_ERR_UNSUPPORTED): unrar can
decrypt, but password plumbing (API/UI) lands in a later commit.
Build:
- Makefile: C++ compiler vars (CXX/HOST_CXX, prospero-clang++ defaults
to -stdlib=libc++), unrar7 RARDLL source set (49 files, mirrors
UnRARDll.vcxproj), .cpp pattern rules, link through the C++ driver.
- tests/run-tests.sh: compiles unrar7 objects with $CXX, links the RAR
test with g++ and -lpowrprof on Windows.
- third_party/unrar/ (dmc_unrar) removed; see git history.
Verified on host (MinGW): full engine run on the user's real v6 archive
(24.9 MB, 170 headers) -> ZIPX_OK, 170/170 entries, 133 files + 37 dirs,
71,514,931 bytes. Host suite: 70 ZIP + 14 RAR checks green.
Frontend password/multi-volume UX and release plumbing (CHANGELOG,
THIRD_PARTY_NOTICES, README, tag) follow in the v1.9 release commit.
A 3A-game archive with a single ~300 GiB uncompressed file was silently
rejected by the v1.8.1 default profile (scan_archive returns
ZIPX_ERR_LIMIT_FILE_SIZE before the request ever reaches the frontend
confirmation prompt, so the user just sees "卡壳"). The 256 GiB default
cap was tuned for PS5 system backups (many smaller entries) and was wrong
for the 3A-game single-file case.
Limits changes:
- src/zip_extract.c k_default_limits: 1 TiB / 256 GiB / 500 -> 2 TiB / 512 GiB / 500
- src/zip_extract.c k_large_limits: 2 TiB / 1 TiB / 1000 -> 4 TiB / 1 TiB / 1000 (must stay > default)
- assets/main.js LARGE_FILE_THRESHOLD_BYTES: 240 GiB -> 480 GiB
- assets/lang-{en,zh}.js extractLargeAsk copy reflects new numbers
- tests/test_zip_extract.c: large.max_total_bytes advertised-number assertion updated to 4 TiB
- README.md / docs/HANDOVER.md: limit tables, FAQ, threshold snippets updated
- CHANGELOG.md: v1.8.2 entry added above v1.8.1
PS5-only build fixes (caught by WSL cross-compile, host tests pass without):
- Makefile CFLAGS: add -Ithird_party/unrar so src/rar_extract.c can find
the dmc_unrar_api.h facade header (THIRD_PARTY_CFLAGS already had it
but the main CFLAGS for project sources did not).
- src/extract.c: move extract_progress() definition before
extract_dispatch() so the implicit function declaration is not flagged
by -Werror=implicit-function-declaration (clang 18 in the PS5 SDK is
stricter than the host gcc used by tests/run-tests.sh).
Release artifact: web-file-mgr.elf 509 704 bytes,
sha256 1b2c3d68b35e32737105f17d14a80a3c159ceca0cabd274ee168cbcd81906f65,
e_machine 0x003e (x86_64-sie-ps5).
Safety argument is unchanged from v1.8.1: zip-bomb defence is
check_space() (statvfs-based real disk-space check before staging) +
max_ratio (declared compression ratio cap). The size caps are a UX guard,
not a security boundary.
RAR extraction inherits the new defaults automatically (rar_extract.c
threads c->limits from the engine).
84 host-side checks (70 ZIP + 14 RAR), 0 failures. PS5 cross-compile
succeeds; ELF size grew from 427 656 (v1.8) to 509 704 (v1.8.2) due to
the dmc_unrar vendor TU being linked in.
src/rar_extract.{c,h} (1276 LOC) — new public engine:
rar_extract(rar_path, dst_dir, conflict, limits, cancel, progress, userdata, result)
Mirrors the zip_extract contract exactly: same three-phase template
(scan -> extract to .wfm-extract-*.tmp staging + fsync -> publish via
rename -> cleanup/rollback), same zipx_status_t / zipx_limits_t /
zipx_conflict_t / zipx_result_t / zipx_progress_t types, same callback
signatures. Reuses publish_dir/publish_entry/publish_staging so a RAR
extract behaves identically to a ZIP extract under conflict-policy
(overwrite / merge / fail), rollback, fsync, and per-entry fsync.
Internals:
- rarx_ctx_t carries limits, callback, staging dir, published paths,
detail-msg accumulator, and the nameset for path-conflict detection.
- rar_translate_error maps every dmc_unrar_return code to a
zipx_status_t (UNSUPPORTED volumes/encryption/method -> UNSUPPORTED,
unsupported_link -> SPECIAL, oversized entry -> LIMIT_FILE,
CRC fail -> CRC, etc.) so the frontend needs zero per-engine branching.
- normalize_name converts backslash -> forward slash (RAR names may use
either on Windows) and detects directory entries by trailing slash
rather than relying on external attributes alone.
- open_parent_dirs / check_space / remove_tree / cleanup_staging /
rollback_published reused from zip_extract's pattern.
src/extract.c — adds extract_dispatch(file_task_t*, conflict, limits,
result*) routing .zip -> zipx_extract(), .rar -> rar_extract(), else
ZIPX_ERR_UNSUPPORTED ('only .zip and .rar are accepted'). The existing
extract_worker now calls dispatch instead of zipx_extract directly.
ends_with_ci() helper for the .rar suffix match (case-insensitive, trims
trailing '/' from paths that the frontend might send).
Makefile:
- VERSION_TAG := v1.8 (was v1.7)
- COMMON_SRCS adds src/rar_extract.c
- THIRD_PARTY_SRCS adds third_party/unrar/dmc_unrar.c (compiled as its
own TU; never inlined into rar_extract.c)
- THIRD_PARTY_CFLAGS adds -Ithird_party/unrar and
-DDMC_UNRAR_DISABLE_BE32TOH_BE64TOH=1 to avoid <endian.h> conflicts
with the SDK's headers.
Limitations surfaced as ZIPX_ERR_UNSUPPORTED with frontend-visible
err_extract_unsupported messages:
- multi-volume RAR (.partNN.rar chains) -- dmc_unrar upstream
limitation; documented v1.9 plan is to vendor opello/unrar C++
- encrypted RAR (password= ignored) -- dmc_unrar upstream limitation
- symlinks / FIFOs / device nodes -- ZIPX_ERR_SPECIAL
- RAR1.4 / RAR1.5 -- ZIPX_ERR_UNSUPPORTED
No ELF rebuild attached in this commit (cross-compile is WSL-side and
deferred to the release commit).
Homebrew HTTP file manager payload for jailbroken PS5 consoles (x86_64-sie-ps5,
title id FMGR88888). Browse, edit, upload, download and extract ZIPs through
any browser on the LAN; single self-contained ELF, no external services.
Highlights (v1.7):
- ZIP large-file profile (large=1): 2 TiB total / 1 TiB per entry / 1000:1
ratio. Frontend prompts for confirmation whenever archive > 60 GiB on disk.
- Default ZIP profile stays safe: 512 GiB / 64 GiB / 200:1.
- 69 host-side C tests (tests/run-tests.sh) covering traversal, ZIP64,
encryption rejection, conflict policies and the large-file profile.
- Three-phase extraction engine (scan -> extract -> publish -> cleanup) with
atomic staging + rename, located in src/zip_extract.{c,h}.
Project layout:
src/ C payload sources
assets/ HTML / CSS / JS / icons / param.json
tests/ POSIX/host test suite + fixtures
third_party/ vendored zlib + minizip-ng (zlib license)
docs/screenshots/ README screenshot images
LICENSE GPLv3+
Build:
make # cross-compile with PS5_PAYLOAD_SDK
make linux # Linux host binary for UI dev