mirror of
https://github.com/LisherSong/ps5-web-file-manager.git
synced 2026-10-06 13:00:23 +02:00
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
This commit is contained in:
1 parent
848b774333
commit
bf55a4e4fd
9 files changed
+741
-673
No files matched your search
+69
-4
@@ -4,14 +4,79 @@ 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:
|
||||
> Release artifact for v1.8.1:
|
||||
> `web-file-mgr.elf` — size TBD (cross-compile runs in WSL — see `docs/HANDOVER.md`)
|
||||
> sha256 TBD
|
||||
> 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: 3 source files relaxed (`src/zip_extract.c` k_default_limits,
|
||||
> `assets/main.js` `LARGE_FILE_THRESHOLD_BYTES`, `assets/lang-{en,zh}.js` copy);
|
||||
> no vendored or engine changes.
|
||||
|
||||
## [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
|
||||
|
||||
|
||||
@@ -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 240 GiB frontend threshold lives in `assets/main.js`:
|
||||
|
||||
```js
|
||||
const LARGE_FILE_THRESHOLD_BYTES = 60 * 1024 * 1024 * 1024;
|
||||
const LARGE_FILE_THRESHOLD_BYTES = 240 * 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,9 +368,9 @@ 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`
|
||||
- **`err_extract_entry_too_large`** — default archive caps are 256 GiB per
|
||||
entry / 500:1 ratio. Confirm the large-file prompt (appears for
|
||||
archives > 240 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
|
||||
|
||||
+1
-1
@@ -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 2 TiB)\nCancel = default limits (single file 256 GiB, archive total 1 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
@@ -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 / 总解压最大 2 TiB)\n取消 = 默认限制(单文件 256 GiB / 总解压 1 TiB),可能拒绝此压缩包",
|
||||
extractLargeActive: "此任务已启用大文件模式",
|
||||
extractSelectMainVolume: "请改选主卷(如 .rar 或 .part01.rar)",
|
||||
extractArchivePending: "正在准备解压 {name}",
|
||||
|
||||
+1
-1
@@ -835,7 +835,7 @@ async function startExtractTask(path, dstDir, conflict, removeSource, name, larg
|
||||
}
|
||||
}
|
||||
|
||||
const LARGE_FILE_THRESHOLD_BYTES = 60 * 1024 * 1024 * 1024;
|
||||
const LARGE_FILE_THRESHOLD_BYTES = 240 * 1024 * 1024 * 1024;
|
||||
|
||||
function shouldPromptLargeMode(itemSize) {
|
||||
return Number(itemSize || 0) > LARGE_FILE_THRESHOLD_BYTES;
|
||||
|
||||
+6
-6
@@ -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 大于 240 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).
|
||||
```
|
||||
|
||||
|
||||
@@ -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`. |
|
||||
|
||||
+3
-3
@@ -35,9 +35,9 @@
|
||||
|
||||
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 = 1ULL * 1024 * 1024 * 1024 * 1024,
|
||||
.max_file_bytes = 256ULL * 1024 * 1024 * 1024,
|
||||
.max_ratio = 500,
|
||||
.max_depth = 32,
|
||||
.max_name_len = 255,
|
||||
.max_path_len = 1024
|
||||
|
||||
+642
-639
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user