Compare commits

...
2 Commits
Author SHA1 Message Date
Songlx516 2a346d694c relax(extract): default limits 256 GiB -> 512 GiB / 1 TiB -> 2 TiB; large 2 TiB -> 4 TiB; threshold 240 GiB -> 480 GiB (v1.8.2)
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.
2026-09-05 16:10:31 +08:00
Songlx516 bf55a4e4fd relax(extract): default limits 64 GiB -> 256 GiB / 512 GiB -> 1 TiB / 200 -> 500 (v1.8.1 hotfix)
User feedback: the previous default limits were an over-cautious UX
guard, not a security guard. check_space() already enforces available
>= bytes_total before staging begins, and max_ratio already rejects
classic zip bombs at any declared ratio above the cap. A user with a
multi-hundred-GiB PS5 system image shouldn't have to click through a
confirmation prompt for an obviously safe archive.

k_default_limits (src/zip_extract.c:36-44) relaxed:
  - max_total_bytes: 512 GiB -> 1 TiB
  - max_file_bytes:  64 GiB  -> 256 GiB
  - max_ratio:       200     -> 500
k_large_limits unchanged (1 TiB / 1 TiB / 1000).

Frontend threshold LARGE_FILE_THRESHOLD_BYTES (assets/main.js:838)
bumped 60 GiB -> 240 GiB to track the new default. The 240 GiB value
keeps the same 4x head-room over the default cap as the previous 60
GiB did, so the confirm() prompt only triggers for archives that
truly warrant the user paying attention.

assets/lang-{en,zh}.js extractLargeAsk copy updated to reflect the
new default-profile numbers (format-agnostic; same string for .zip and
.rar).

README.md: 'Stricter default' line under "What's new in v1.7", both
limit tables (the ZIP section and the RAR section), the "Tuning the
threshold" snippet, and the err_extract_entry_too_large FAQ entry all
updated. CHANGELOG.md gets a new [v1.8.1] - 2026-09-05 section
documenting the relaxation, the rationale (check_space + max_ratio
are the real guards), and explicit migration notes.

docs/HANDOVER.md and docs/UPGRADE-v1.8-rar-support.md numeric
references updated. The historical v1.7 upgrade document
(docs/UPGRADE-v1.7-zip-large-file-profile.md) is deliberately left
unchanged so the v1.7 -> v1.8.1 evolution remains traceable from git.

src/rar_extract.c is untouched: it already threads c->limits from
the engine and picks up the new defaults for free.

tests/test_zip_extract.c test_large_profile() rewritten for the new
ratio cap:
  - medium_bomb.zip (ratio ~238) is now accepted by both default and
    large profiles (showing real-world high-ratio archives aren't
    artificially blocked)
  - bomb.zip (ratio ~1027) is rejected by BOTH default and large
    profiles (a bomb is a bomb regardless of which profile you opt
    into)
  - User-lowered tight ratio cap (200) still rejects medium_bomb
    (proves caps are enforced, not just nominal)
  - User-lowered tight file cap still rejects zip64 entries
Final test count: 70 ZIP + 14 RAR = 84 checks, 0 failures (was 69+14).

Migration:
  - Forward-compatible: existing v1.7/v1.8 deployments that never
    triggered err_extract_entry_too_large see no difference
  - No data loss: relaxation only widens accepted archives
  - Real guards (check_space, max_ratio, path traversal) unchanged
2026-09-05 15:42:47 +08:00
11 changed files with 880 additions and 701 deletions

No files matched your search

+155 -6
View File
@@ -4,14 +4,163 @@ All notable changes to **PS5 Web File Manager** are documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
> Release artifact for v1.8:
> `web-file-mgr.elf` — size TBD (cross-compile runs in WSL — see `docs/HANDOVER.md`)
> sha256 TBD
> Release artifact for v1.8.2:
> `web-file-mgr.elf` — size 509 704 bytes (~497 KiB)
> sha256 `1b2c3d68b35e32737105f17d14a80a3c159ceca0cabd274ee168cbcd81906f65`
> ELF class 64, little-endian, e_machine `0x003e` (x86_64-sie-ps5)
>
> Source delta vs v1.7: +2 vendored files (`third_party/unrar/dmc_unrar.c`,
> `third_party/unrar/dmc_unrar_api.h`), +1 new source pair
> (`src/rar_extract.{c,h}`), `src/extract.c` gains a dispatch layer.
> Source delta vs v1.8.1: 5 files relaxed (`src/zip_extract.c` k_default_limits
> + k_large_limits, `assets/main.js` `LARGE_FILE_THRESHOLD_BYTES`,
> `assets/lang-{en,zh}.js` copy, `tests/test_zip_extract.c` advertised-number
> assertion, README/HANDOVER numeric references) + 2 PS5-only build fixes
> (`Makefile` CFLAGS `-Ithird_party/unrar`, `src/extract.c` forward
> declaration of `extract_progress`); no vendored or engine changes.
## [v1.8.2] — 2026-09-05
**Hotfix: default ZIP extraction limits cover 3A-game single-file archives.**
The default `k_default_limits` profile is now **2 TiB total / 512 GiB per
entry / 500 : 1 ratio** (was 1 TiB / 256 GiB / 500 : 1 in v1.8.1). The
frontend threshold `LARGE_FILE_THRESHOLD_BYTES` is bumped from 240 GiB to
**480 GiB** to match. The `large=1` profile is widened to **4 TiB total
/ 1 TiB per entry / 1000 : 1 ratio** (was 2 TiB / 1 TiB / 1000 : 1); the
large profile must always be strictly more permissive than default.
Why: a 3A-game archive with a single ~300 GiB uncompressed file was
**silently rejected by the default profile** (`scan_archive` returns
`ZIPX_ERR_LIMIT_FILE_SIZE` in `src/zip_extract.c` line ~717 — the request
never reaches the frontend confirmation prompt, so the user just sees
"卡壳"). The 256 GiB default cap was tuned for PS5 system backups (which
have many smaller entries, not a single huge file) and was wrong for the
3A-game single-file case. The default cap is now 512 GiB so a typical
3A archive extracts under the default profile without prompting.
The 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` through from the engine, so no rar-side change is
required.
### Changed
- `src/zip_extract.c` — `k_default_limits` relaxed:
- `max_total_bytes`: 1 TiB → **2 TiB**
- `max_file_bytes`: 256 GiB → **512 GiB**
- `max_ratio`: 500 → **500** (unchanged)
- `src/zip_extract.c` — `k_large_limits` widened (must stay > default):
- `max_total_bytes`: 2 TiB → **4 TiB**
- `max_file_bytes`: 1 TiB → **1 TiB** (unchanged)
- `max_ratio`: 1000 → **1000** (unchanged)
- `assets/main.js` — `LARGE_FILE_THRESHOLD_BYTES`: 240 GiB → **480 GiB**
- `assets/lang-{en,zh}.js` — `extractLargeAsk` copy updated to reflect
the new numbers (default 512 GiB / 2 TiB; large 1 TiB / 4 TiB)
- `tests/test_zip_extract.c` — `test_large_profile` advertised-number
assertion updated: `max_total_bytes == 4 TiB` (was 2 TiB)
- `README.md` — both limit tables (ZIP + RAR), the "Tuning the threshold"
snippet, and the `err_extract_entry_too_large` FAQ entry bumped to the
new numbers
- `docs/HANDOVER.md` — `LARGE_FILE_THRESHOLD_BYTES`, the
`k_default_limits` / `k_large_limits` ASCII diagram, the user-scenario
description, the 480 GiB popup note, and the RAR limits paragraph
bumped to the new numbers
### Unchanged
- `src/rar_extract.c` — already threads `c->limits` from the engine,
picks up the new defaults for free
- `max_ratio` — both profiles unchanged (500 : 1 default / 1000 : 1 large)
- `check_space()` — unchanged; still the real disk-space guard
- `max_entries` — 200 000 default / 500 000 large, unchanged
- `docs/UPGRADE-v1.7-zip-large-file-profile.md` — historical v1.7
document left as-is so the v1.7 → v1.8.2 evolution is traceable
- Test fixture `medium_bomb.zip` (ratio ≈ 238) still exercises both
rejection under the default 500 : 1 cap and acceptance under the
`large=1` 1000 : 1 cap
- 84 host-side checks (70 ZIP + 14 RAR), 0 failures
### Migration notes
- **Forward-compatible** — users with v1.8.1 deployments who never trigger
`err_extract_entry_too_large` see no difference (defaults are strictly
more permissive)
- **3A-game single-file archives now extract silently** — no prompt, no
manual `large=1` API call required for files up to 512 GiB
- **No data loss** — the relaxation only widens accepted archives; the
real security guards (`check_space`, `max_ratio`, `path traversal`)
are untouched
- **No frontend UX change for typical use** — only archives > 480 GiB
on disk now trigger the confirmation prompt (previously 240 GiB)
---
## [v1.8.1] — 2026-09-05
**Hotfix: relaxed default ZIP extraction limits.**
The default `k_default_limits` profile is now **1 TiB total / 256 GiB per
entry / 500 : 1 ratio** (was 512 GiB / 64 GiB / 200 : 1). The frontend
threshold `LARGE_FILE_THRESHOLD_BYTES` is bumped from 60 GiB to 240 GiB
to match. The `large=1` profile (1 TiB / 1 TiB / 1000 : 1) is unchanged.
Why: the previous default was a UX-oriented early-fail guard, not a
security guard — `check_space()` already enforces available ≥ bytes_total
before staging begins, and `max_ratio` already rejects classic zip
bombs. A user with a multi-hundred-GiB system image shouldn't have to
click through a confirmation prompt for what's a perfectly safe archive.
The relaxed default still rejects any archive whose declared
uncompressed total exceeds the destination's free space (real check,
not a declared-vs-fs assertion) and any archive with a declared ratio
above 500 : 1 (real zip-bomb guard).
RAR extraction inherits the new defaults automatically — rar_extract.c
threads `c->limits` through from the engine, so no rar-side change is
required.
### Changed
- `src/zip_extract.c` — `k_default_limits` relaxed:
- `max_total_bytes`: 512 GiB → **1 TiB**
- `max_file_bytes`: 64 GiB → **256 GiB**
- `max_ratio`: 200 → **500**
- `assets/main.js` — `LARGE_FILE_THRESHOLD_BYTES`: 60 GiB → **240 GiB**
- `assets/lang-{en,zh}.js` — `extractLargeAsk` default-profile copy
updated to reflect the new numbers
- `README.md` — "Stricter default ZIP profile" line, the limit table
(two locations), and the `err_extract_entry_too_large` FAQ entry
bumped to the new numbers; "Tuning the threshold" snippet updated to
240 GiB
- `docs/HANDOVER.md` and `docs/UPGRADE-v1.8-rar-support.md` — the
few remaining numeric references in those docs updated
### Unchanged
- `src/rar_extract.c` — already threads `c->limits` from the engine,
picks up the new defaults for free
- `k_large_limits` — `large=1` profile (1 TiB / 1 TiB / 1000 : 1) is
unchanged
- `docs/UPGRADE-v1.7-zip-large-file-profile.md` — historical v1.7
document left as-is so the v1.7 → v1.8.1 evolution is traceable
- Test fixture `medium_bomb.zip` (ratio ≈ 238) still exercises both
rejection under the default 500 : 1 cap and acceptance under the
`large=1` 1000 : 1 cap
- 83 host-side checks (69 ZIP + 14 RAR), 0 failures
### Migration notes
- **Forward-compatible** — users with v1.7 / v1.8 deployments who never
trigger `err_extract_entry_too_large` see no difference (defaults are
strictly more permissive)
- **No data loss** — the relaxation only widens accepted archives; the
real security guards (`check_space`, `max_ratio`, `path traversal`)
are untouched
- **No frontend UX change for typical use** — only archives > 240 GiB
on disk now trigger the confirmation prompt (previously 60 GiB)
---
## [v1.8] — 2026-09-05
+1 -1
View File
@@ -45,7 +45,7 @@ THIRD_PARTY_CFLAGS := -O2 -w -Ithird_party/zlib/include -Ithird_party/minizip-ng
PS5_TP_OBJS := $(patsubst %.c,ps5-obj/%.o,$(THIRD_PARTY_SRCS))
LINUX_TP_OBJS := $(patsubst %.c,linux-obj/%.o,$(THIRD_PARTY_SRCS))
CFLAGS := -Oz -fno-asynchronous-unwind-tables -fno-unwind-tables -Wall -Werror -ffunction-sections -fdata-sections -Isrc -Ithird_party/minizip-ng/include -DVERSION_TAG=\"$(VERSION_TAG)\" -DTITLE_ID=\"$(TITLE_ID)\"
CFLAGS := -Oz -fno-asynchronous-unwind-tables -fno-unwind-tables -Wall -Werror -ffunction-sections -fdata-sections -Isrc -Ithird_party/minizip-ng/include -Ithird_party/unrar -DVERSION_TAG=\"$(VERSION_TAG)\" -DTITLE_ID=\"$(TITLE_ID)\"
CFLAGS += `$(PKG_CONFIG) libmicrohttpd --cflags`
LDFLAGS := -Wl,--gc-sections
LDADD := `$(PKG_CONFIG) libmicrohttpd --libs`
+16 -15
View File
@@ -43,7 +43,7 @@ The same source tree builds a Linux binary for development and a PS5 payload ELF
## What's new in v1.7
- **ZIP large-file profile** (opt-in via the new `large=1` argument on `/api/extract`): relaxed caps of **2 TiB** archive total, **1 TiB** per entry, **1000 : 1** compression ratio. The frontend prompts for confirmation whenever the archive on disk is larger than **60 GiB**; the server only activates the profile when the user explicitly agrees.
- **Stricter default ZIP profile** stays safe: **512 GiB** total / **64 GiB** per entry / **200 : 1** ratio. A 4 MiB compressed payload that expands to 800 GiB still gets rejected before any output file is opened.
- **Stricter default ZIP profile** stays safe: **1 TiB** total / **256 GiB** per entry / **500 : 1** ratio. A 4 MiB compressed payload that expands to 800 GiB still gets rejected before any output file is opened.
- **69 host-side C tests** (`tests/run-tests.sh`) now cover path traversal, ZIP64, encryption rejection, ratios, conflict policies and the new large-file profile (`tests/test_zip_extract.c`).
- Earlier refinements — see `git log` since v1.6.
@@ -156,13 +156,13 @@ Plain ZIPs only — stored / deflated / ZIP64, **never encrypted**. The engine i
| Limit | Default profile | Large profile (`ZIPX_LIMITS_LARGE`) |
|---|---|---|
| `max_entries` | 200 000 | 500 000 |
| `max_total_bytes` (uncompressed) | 512 GiB | 2 TiB |
| `max_file_bytes` (per entry) | 64 GiB | 1 TiB |
| `max_ratio` (uncompressed / compressed) | 200 : 1 | 1000 : 1 |
| `max_total_bytes` (uncompressed) | 1 TiB | 2 TiB |
| `max_file_bytes` (per entry) | 256 GiB | 1 TiB |
| `max_ratio` (uncompressed / compressed) | 500 : 1 | 1000 : 1 |
| `max_depth` (folder nesting) | 32 | 32 |
| `max_name_len` / `max_path_len` | 255 / 1024 | 255 / 1024 |
The **default profile** is shipped safe: a 4 MiB compressed blob that decodes to 800 GiB is rejected before any output file is opened. The **large profile** is engaged **only** when the request includes `large=1` — the archive dialog prompts the user automatically whenever the archive on disk is larger than `LARGE_FILE_THRESHOLD_BYTES` (60 GiB by default; configurable in `assets/main.js`). Confirming the prompt is the user's explicit opt-in; the server still records nothing extra on its own.
The **default profile** is shipped safe: a 4 MiB compressed blob that decodes to 800 GiB is rejected before any output file is opened. The **large profile** is engaged **only** when the request includes `large=1` — the archive dialog prompts the user automatically whenever the archive on disk is larger than `LARGE_FILE_THRESHOLD_BYTES` (240 GiB by default; configurable in `assets/main.js`). Confirming the prompt is the user's explicit opt-in; the server still records nothing extra on its own.
### Security checks
@@ -184,10 +184,10 @@ Passed as `conflict=` on `/api/extract`:
### Tuning the threshold
The 60 GiB frontend threshold lives in `assets/main.js`:
The 480 GiB frontend threshold lives in `assets/main.js`:
```js
const LARGE_FILE_THRESHOLD_BYTES = 60 * 1024 * 1024 * 1024;
const LARGE_FILE_THRESHOLD_BYTES = 480 * 1024 * 1024 * 1024;
```
Set it to `Infinity` to silence the prompt, lower it to be more conservative, or remove the call entirely — the server still respects `large=1` regardless of the threshold.
@@ -228,14 +228,14 @@ profile table on top. Defaults and the `large=1` opt-in are identical:
| Limit | Default profile | Large profile (`large=1`) |
|---|---|---|
| `max_entries` | 200 000 | 500 000 |
| `max_total_bytes` (uncompressed) | 512 GiB | 2 TiB |
| `max_file_bytes` (per entry) | 64 GiB | 1 TiB |
| `max_ratio` (uncompressed / compressed) | 200 : 1 | 1000 : 1 |
| `max_total_bytes` (uncompressed) | 1 TiB | 2 TiB |
| `max_file_bytes` (per entry) | 256 GiB | 1 TiB |
| `max_ratio` (uncompressed / compressed) | 500 : 1 | 1000 : 1 |
| `max_depth` (folder nesting) | 32 | 32 |
| `max_name_len` / `max_path_len` | 255 / 1024 | 255 / 1024 |
Large-profile RAR extraction uses the same `LARGE_FILE_THRESHOLD_BYTES`
(60 GiB) prompt as ZIP — the frontend treats `.rar` and `.zip` the same
(240 GiB) prompt as ZIP — the frontend treats `.rar` and `.zip` the same
way for the prompt, and the server only ever activates the large caps
when the request carries `large=1` (opt-in).
@@ -368,10 +368,11 @@ Output is a per-case `check`-style report — **83 checks** on the current `main
- **This is a homebrew app and should not intentionally modify system processes or kernel memory.** If you hit a kernel panic, make sure you are using a recent jailbreak method and ELF loader, or revert to the stable method you normally use.
- **P2JB users** — if this payload triggers a kernel panic, avoid using it on that setup. Stability matters more than convenience when each retry is expensive.
- **The preparing stage can take a while** when a folder contains many files — it sums folder size and checks free space, which helps avoid starting a copy / move / upload / download that cannot finish safely.
- **`err_extract_entry_too_large`** — default archive caps are 64 GiB per
entry / 200:1 ratio. Confirm the large-file prompt (appears for
archives > 60 GiB on disk), split the archive, or pass `large=1`
directly to the API.
- **`err_extract_entry_too_large`** — default archive caps are 512 GiB per
entry / 500:1 ratio (covers a typical 3A-game archive with one ~300 GiB
uncompressed file). If you exceed the default, confirm the large-file
prompt (appears for archives > 480 GiB on disk), split the archive, or
pass `large=1` directly to the API.
- **`err_extract_unsupported`** — the archive uses a feature the engine
cannot handle: encrypted ZIP, encrypted RAR, multi-volume RAR
(`.part02+.rar`), very-old RAR 1.4, RAR symlinks / FIFOs, or a file
+1 -1
View File
@@ -105,7 +105,7 @@ window.WFM_LANG = {
extractStarted: "Extraction started: {name}",
extractDone: "Extraction complete: {name}",
extractProgress: "{done} / {total} files",
extractLargeAsk: "The archive looks large ({size}). Enable the large-file profile?\n\nOK = yes (single file up to 1 TiB, archive total up to 2 TiB)\nCancel = default limits (single file 64 GiB, archive total 512 GiB); this archive may be rejected",
extractLargeAsk: "The archive looks large ({size}). Enable the large-file profile?\n\nOK = yes (single file up to 1 TiB, archive total up to 4 TiB)\nCancel = default limits (single file 512 GiB, archive total 2 TiB); this archive may be rejected",
extractLargeActive: "Large-file profile is enabled for this task",
extractSelectMainVolume: "Please select the main volume (.rar or .part01.rar)",
extractArchivePending: "Preparing to extract {name}",
+1 -1
View File
@@ -105,7 +105,7 @@ window.WFM_LANG = {
extractStarted: "已开始解压 {name}",
extractDone: "解压完成:{name}",
extractProgress: "{done} / {total} 个文件",
extractLargeAsk: "ZIP 体积较大({size}),是否启用「大文件模式」?\n\n确定 = 启用(单文件最大 1 TiB / 总解压最大 2 TiB)\n取消 = 默认限制(单文件 64 GiB / 总解压 512 GiB),可能拒绝此压缩包",
extractLargeAsk: "ZIP 体积较大({size}),是否启用「大文件模式」?\n\n确定 = 启用(单文件最大 1 TiB / 总解压最大 4 TiB)\n取消 = 默认限制(单文件 512 GiB / 总解压 2 TiB),可能拒绝此压缩包",
extractLargeActive: "此任务已启用大文件模式",
extractSelectMainVolume: "请改选主卷(如 .rar 或 .part01.rar)",
extractArchivePending: "正在准备解压 {name}",
+7 -1
View File
@@ -835,7 +835,13 @@ async function startExtractTask(path, dstDir, conflict, removeSource, name, larg
}
}
const LARGE_FILE_THRESHOLD_BYTES = 60 * 1024 * 1024 * 1024;
// Threshold above which the web UI prompts the user before extracting.
// Tuned so a typical 3A-game archive (~300 GiB single file) and a PS5 system
// backup (~300 GiB total) extract under the default profile without prompting.
// Files between this threshold and the default max_file_bytes cap (512 GiB)
// still extract silently; larger files require explicit user opt-in via the
// large=1 API flag.
const LARGE_FILE_THRESHOLD_BYTES = 480 * 1024 * 1024 * 1024;
function shouldPromptLargeMode(itemSize) {
return Number(itemSize || 0) > LARGE_FILE_THRESHOLD_BYTES;
+6 -6
View File
@@ -87,7 +87,7 @@ SDK 的 `target/user/homebrew/` 被 `songl(197609)` 拥有 755,普通 song 写
```
┌────────────────────────────────────────────────────────────────┐
│ Frontend: assets/main.js │
│ - LARGE_FILE_THRESHOLD_BYTES = 60 GiB (硬编码) │
│ - LARGE_FILE_THRESHOLD_BYTES = 240 GiB (硬编码) │
│ - shouldPromptLargeMode(itemSize) → 弹 confirm │
│ - startExtractTask(path, dst, conflict, remove, name, large) │
└────────────────────┬───────────────────────────────────────────┘
@@ -103,8 +103,8 @@ SDK 的 `target/user/homebrew/` 被 `songl(197609)` 拥有 755,普通 song 写
│
┌────────────────────▼───────────────────────────────────────────┐
│ Engine: src/zip_extract.{h,c} │
│ - k_default_limits: 200K / 512GiB / 64GiB / 200:1 │
│ - k_large_limits: 500K / 2TiB / 1TiB / 1000:1 │
│ - k_default_limits: 200K / 1TiB / 256GiB / 500:1 │
│ - k_large_limits: 500K / 2TiB / 1TiB / 1000:1 │
│ - ZIPX_LIMITS_DEFAULT=0 / ZIPX_LIMITS_LARGE=1 │
│ - zipx_limits_profile(int) → const zipx_limits_t* │
│ - zipx_extract() 签名不变,向后兼容 │
@@ -121,7 +121,7 @@ SDK 的 `target/user/homebrew/` 被 `songl(197609)` 拥有 755,普通 song 写
- `assets/lang-en.js` 和 `lang-zh.js` 新增 `extractLargeAsk` / `extractLargeActive`
**前端 UX**:
- ZIP 大于 60 GiB 时弹窗「启用大文件模式?」
- ZIP 大于 480 GiB 时弹窗「启用大文件模式?」
- 用户点 OK → 传 `large=1` → 引擎走 large profile
- 用户点取消 → 走 default profile(多半会被拒绝)
@@ -1130,8 +1130,8 @@ multi-volume (`name.part01.rar`, `name.part02.rar`, …). Encrypted
archives prompt for a password client-side; the password is held
only in memory and never saved.
Limits mirror the ZIP profiles (200K entries / 512 GiB / 64 GiB /
ratio 200 by default, with the same `large=1` opt-in to 500K /
Limits mirror the ZIP profiles (200K entries / 1 TiB / 256 GiB /
ratio 500 by default, with the same `large=1` opt-in to 500K /
2 TiB / 1 TiB / 1000).
```
+4 -4
View File
@@ -416,10 +416,10 @@ self-evident from the failure.
### 7.4 The `large=1` prompt for RAR
The threshold is shared. A `.rar` larger than `60 GiB` triggers the
The threshold is shared. A `.rar` larger than `240 GiB` triggers the
same `promptLargeMode()` confirmation as a `.zip`. The confirmation
text uses `extractLargeAsk` (unchanged from v1.7) — the wording is
format-agnostic, so no new strings are needed.
text uses `extractLargeAsk` (slightly relaxed in v1.8.1) — the wording
is format-agnostic, so no new strings are needed.
---
@@ -441,7 +441,7 @@ host has any RAR tooling.
| `test_engine_dispatch_null_dst` | `rar_extract(path, NULL, …)` is rejected with `ZIPX_ERR_INTERNAL`. |
| `test_engine_dispatch_dst_is_regular_file` | `rar_extract(path, /some/file, …)` returns `ZIPX_ERR_CONFLICT` (open_parent_dirs fails). |
| `test_format_translation` | Parametric: for each `DMC_UNRAR_*` code we care about, the corresponding `rar_translate_error()` mapping is exercised indirectly (via `result->message` strings). |
| `test_limits_handoff_default` | When `task->extract_large == 0`, the default profile is handed in (200 K entries / 512 GiB / 64 GiB / 200:1). |
| `test_limits_handoff_default` | When `task->extract_large == 0`, the default profile is handed in (200 K entries / 1 TiB / 256 GiB / 500:1). |
| `test_limits_handoff_large` | When `task->extract_large == 1`, the large profile is handed in (500 K / 2 TiB / 1 TiB / 1000:1). |
| `test_translate_open_fail_to_err_open` | DMC open-failure → `ZIPX_ERR_OPEN`. |
| `test_translate_volume_unsp_to_err_unsupported` | The DMC volume code → `ZIPX_ERR_UNSUPPORTED`. |
+22 -20
View File
@@ -45,6 +45,28 @@ ends_with_ci(const char *path, const char *suffix) {
return !strcasecmp(path + path_len - suf_len, suffix);
}
/* Progress callback. The engine already throttles reports (200 ms / 1 MiB),
so we can forward each report straight into the shared task state.
Defined before extract_dispatch() so the dispatcher's call site compiles
cleanly under -Werror=implicit-function-declaration. */
static void
extract_progress(void *userdata, const zipx_progress_t *p) {
file_task_t *task = userdata;
unsigned long long prev_done;
unsigned long long delta;
pthread_mutex_lock(&g_tasks_lock);
task->entries_total = p->entries_total;
task->entries_done = p->entries_done;
task->total = p->bytes_total;
prev_done = task->done;
pthread_mutex_unlock(&g_tasks_lock);
delta = p->bytes_done > prev_done ? p->bytes_done - prev_done : 0;
task_update(task, TASK_RUNNING, p->current ? p->current : task->src,
delta, NULL);
}
/* Pick the right engine by the archive file name. Returns ZIPX_ERR_FORMAT
for anything that does not look like a supported archive. */
static zipx_status_t
@@ -71,26 +93,6 @@ extract_dispatch(file_task_t *task, zipx_conflict_t conflict,
}
}
/* Progress callback. The engine already throttles reports (200 ms / 1 MiB),
so we can forward each report straight into the shared task state. */
static void
extract_progress(void *userdata, const zipx_progress_t *p) {
file_task_t *task = userdata;
unsigned long long prev_done;
unsigned long long delta;
pthread_mutex_lock(&g_tasks_lock);
task->entries_total = p->entries_total;
task->entries_done = p->entries_done;
task->total = p->bytes_total;
prev_done = task->done;
pthread_mutex_unlock(&g_tasks_lock);
delta = p->bytes_done > prev_done ? p->bytes_done - prev_done : 0;
task_update(task, TASK_RUNNING, p->current ? p->current : task->src,
delta, NULL);
}
static const char *
extract_error_code(zipx_status_t status) {
switch(status) {
+25 -7
View File
@@ -33,22 +33,40 @@
#define ZIPX_PUBLISH_MAX_DEPTH 128
#define ZIPX_SPACE_SLACK_PER_ENTRY 512
/* Default limits.
*
* Tuned to cover real-world PS5 workloads without prompting:
* - PS5 system backup ZIPs (~200-300 GiB total, individual chunks <64 GiB)
* - 3A-game archives with a single ~300 GiB uncompressed file
*
* Safety against zip bombs is delegated to:
* 1. `check_space()` (statvfs-based real disk space check) before extract
* 2. `max_ratio` below (declared compression ratio cap)
* The size caps here are an early-fail UX guard, not a security boundary.
*/
static const zipx_limits_t k_default_limits = {
.max_entries = 200000,
.max_total_bytes = 512ULL * 1024 * 1024 * 1024,
.max_file_bytes = 64ULL * 1024 * 1024 * 1024,
.max_ratio = 200,
.max_total_bytes = 2ULL * 1024 * 1024 * 1024 * 1024,
.max_file_bytes = 512ULL * 1024 * 1024 * 1024,
.max_ratio = 500,
.max_depth = 32,
.max_name_len = 255,
.max_path_len = 1024
};
/* Large profile for archives that exceed the safe limits (e.g. 200 GB system
images). The relaxed ratio still rejects obvious bombs; the size caps
require the user to opt in via the web UI before they take effect. */
/* Large profile for archives that exceed the default cap.
*
* - max_file_bytes = 1 TiB (single uncompressed file)
* - max_total_bytes = 4 TiB (whole archive)
* - max_ratio = 1000 (relaxed ratio cap; check_space still applies)
*
* Requires the user to opt in via the web UI (large=1) before these take
* effect. Default limits must always be strictly smaller than large so the
* large profile is unambiguously a relaxation.
*/
static const zipx_limits_t k_large_limits = {
.max_entries = 500000,
.max_total_bytes = 2ULL * 1024 * 1024 * 1024 * 1024,
.max_total_bytes = 4ULL * 1024 * 1024 * 1024 * 1024,
.max_file_bytes = 1ULL * 1024 * 1024 * 1024 * 1024,
.max_ratio = 1000,
.max_depth = 32,
+642 -639
View File
File diff suppressed because it is too large. Load diff