18 KiB
交接文档 — ps5-web-file-manager 工作进度
交接时间:2026-09-15 · 分支
main· 最新提交0d036a7· tagv1.9.1指向1fa2f09(需重打到0d036a7)主线(用户 2026-09-12 指令):「先从 zip 分卷开始吧,然后把六种组合打齐,并把密码通道补齐,注意一些报错信息提示的时候尽量详细准确」 状态:主线全部闭合。 六种组合(ZIP/RAR/7z × 单卷/分卷)+ 密码通道(RAR 加密 + 7zAES)+ 报错详细信息,全部落地、测试全绿、PS5 ELF 构建成功。 剩余:① 推送
0d036a7;② tag 重打;③ PS5 真机端到端验证;④-mhe=on加密头(唯一功能缺口)。
一、当前状态速览
| 维度 | 状态 |
|---|---|
| 解压引擎 | ZIP / RAR / 7z × 单卷/分卷(6 组合)+ 7zAES / RAR 加密 |
| 主机测试 | ZIP 108 + RAR 27 + 7z 28 = 163 checks,0 失败(MinGW gcc 16.2.0) |
| PS5 构建 | ✅ WSL prospero-clang 18.1.8,一键脚本可复现 |
| ELF 产物 | web-file-mgr-v1.9.1.elf · 1,017,864 B · sha256 05952ac05fb937e8758846cbd8d495a427670e3cfb7f4020d42d4c6fe8c21631 · e_machine=0x003e |
| GitHub | main 已推到 1fa2f09;0d036a7 待推;v1.9.1 tag 待重打 |
| 唯一功能缺口 | 7z -mhe=on(加密头) |
待用户在自己终端执行
cd "/c/Users/songl/Desktop/Web File Manager/ps5-web-file-manager"
# 1) 推送本轮的 /api/version 改动
git push origin main
# 2) v1.9.1 重打到最新(1fa2f09 -> 0d036a7,让 tag 内含 /api/version + 新 ELF)
git tag -f -a v1.9.1 -m "v1.9.1 -- 7z + multi-volume + 7zAES + PS5 build + version API" 0d036a7
git push -f origin v1.9.1
# 3) 可选:发 Release(附带 ELF)
gh release create v1.9.1 --title "v1.9.1" --notes-file .git/tag-v191.txt web-file-mgr-v1.9.1.elf
沙箱内 git 出站 HTTPS 被拦(502/403),推送只能用户侧执行。
二、本轮(2026-09-15)变更
2.1 一键构建脚本(a54f34b / 5229cd5)
| 文件 | 跑在哪 | 作用 |
|---|---|---|
.build/build-win.sh |
Windows Git Bash | 入口:把 WSL 脚本经 stdin 喂给 wsl.exe,透传退出码 |
.build/build-elf-wsl.sh |
WSL Ubuntu-22.04 | 5 阶段:环境检查 → rsync 同步 → make all → 验证 → 拷回 Windows |
.build/build-elf.sh |
WSL 内 | 仅首次搭环境用(libmicrohttpd staging 安装 + sudo) |
# Windows 端(注意:必须 /usr/bin/bash,裸 bash 会解析成 WSL 启动器)
cd "/c/Users/songl/Desktop/Web File Manager/ps5-web-file-manager"
/usr/bin/bash .build/build-win.sh
# 或 WSL 内
bash /home/song/build.sh
两个关键实现点(改脚本前必读):
wsl.exe -- bash -c '...'遇到含空格路径会被拆断 → 必须wsl.exe -- bash < script.sh(stdin 重定向)make ... | tail会吞掉退出码 → 用${PIPESTATUS[0]};否则编译失败还会继续跑验证,输出假成功
2.2 版本号体系统一(f4fd464 / 1fa2f09 / 0d036a7)
原本版本号有两个真相来源,已经漂移过:Makefile 写 v1.9.1,assets/main.js 硬编 "v1.9",UI 右下角在整个 v1.9.1 发布期都显示旧值。
现在收敛到 Makefile 一处:
VERSION_TAG ?= v1.9.1 # 可用 make VERSION_TAG=v1.9.2 临时覆盖
BIN := web-file-mgr-$(VERSION_TAG).elf
改这一行会同时影响 四处:ELF 内嵌版本串、PS5 启动通知、输出文件名、UI 右下角。
| 提交 | 内容 |
|---|---|
f4fd464 |
VERSION_TAG v1.9 → v1.9.1;.gitignore 改白名单式(.build/* 全忽略 + ! 放行 6 个脚本),.workbuddy/ 也忽略 |
1fa2f09 |
输出文件名派生自 VERSION_TAG;build-elf-wsl.sh 从 Makefile 反读版本(不硬编);build-win.sh 每次都推 WSL 脚本(原来只在缺失时推,导致改了脚本 WSL 侧仍跑旧版) |
0d036a7 |
新增 /api/version,前端右下角改从后端取值(见 2.3) |
2.3 UI 版本号改由后端提供(0d036a7)
问题:assets/main.js:38 的 const APP_VERSION = "v1.9" 与 Makefile 无关,必然漂移。
修法:
- 新
src/version.c—GET /api/version→{"ok":true,"version":"v1.9.1","titleId":"FMGR88888"},直接来自 Makefile 已传的-DVERSION_TAG/-DTITLE_ID宏,没有第二处要记得改 src/filemgr.c路由表加一行(紧邻/api/space)+filemgr_internal.h声明 +MakefileCOMMON_SRCS- 前端:字面量降级为
APP_VERSION_FALLBACK(先渲染,保证页脚不空),loadVersion()后台刷新。请求失败静默吞掉 —— 版本号显示错属于装饰性问题,不该弹错误 toast
踩坑:新文件漏了 #include "json_util.h" → strbuf_append / json_escape 隐式声明报错。space.c 是模板,照抄时别漏。
三、功能矩阵与测试
3.1 六种组合 + 密码通道
| # | 引擎 | 单卷 | 分卷 | 密码 |
|---|---|---|---|---|
| ① | ZIP | ✅ | ✅ 三种命名约定 | ✅ |
| ② | RAR | ✅ | ✅(vendor unrar 7.20.1) | ✅ RARSetPassword |
| ③ | 7z | ✅ | ✅ .7z.001 |
✅ 7zAES |
3.2 测试
export PATH="/c/mingw64/bin:/c/Users/songl/.workbuddy/binaries/PortableGit/versions/1.2.0/mingw64/bin:/c/Users/songl/.workbuddy/binaries/python/versions/3.13.12:/usr/bin:/bin:/c/Windows/System32:/c/Windows"
cd "/c/Users/songl/Desktop/Web File Manager/ps5-web-file-manager"
/usr/bin/bash tests/run-tests.sh # ZIP 108 + RAR 27
/usr/bin/bash tests/run-sevenz-tests.sh # 7z 28(含 KNOWN_GAPS 检查)
⚠️ 绝不写裸 bash —— 可能解析到 C:\Windows\System32\bash.exe(WSL 启动器),脚本跑进 Linux,gcc/python 全变 Linux 版,报莫名错误。必须 /usr/bin/bash。
run-sevenz-tests.sh 带 KNOWN_GAPS 列表(当前仅 aeshe),缺口修好后脚本会主动报错,防止列表腐烂。脚本不做任何删除(safe-delete 钩子会拦 rm -rf)。
四、构建(PS5 ELF)
4.1 日常构建
cd "/c/Users/songl/Desktop/Web File Manager/ps5-web-file-manager"
/usr/bin/bash .build/build-win.sh
增量有效(ps5-obj/ 缓存保留)→ 二次构建 30s–2min。别 make clean(全量重编第三方 3–5min)。
产物:项目根 web-file-mgr-<VERSION_TAG>.elf,同时留在 WSL /home/song/ps5-web-file-manager/。
4.2 验证清单
ls -lh web-file-mgr-v1.9.1.elf
sha256sum web-file-mgr-v1.9.1.elf
od -An -tx2 -j18 -N2 web-file-mgr-v1.9.1.elf # 期望 3e00
strings -a web-file-mgr-v1.9.1.elf | grep -m1 '^v1\.'
⚠️ od -An -tx2 打印的是小端 short 的值(003e),不是字节序(3e00)。脚本里比对用 $((16#$EM)) 转数值(62 = x86-64 ✅ / 183 = aarch64 ❌)。
⚠️ sha256 只在源码不变时可复现。核对版本请用 strings ... | grep ^v1.9,比 sha256 直观且抗噪。
4.3 构建坑(已修,改 Makefile 前必读)
third_party/7z/AesOpt.c 用编译器版本宏判断是否启用 AES-NI / AVX / VAES,clang 18 直接进 VAES 分支,但 prospero-clang 默认 target 是 generic x86_64 → _mm256_aesenc_epi128 未声明,20 报错。
- ❌ 不能
filter-out AesOpt.c——Aes.c通过AesGenTables引用AesCbc_Encode_HW等符号,会链接失败 - ✅ 正解:路径过滤
SEVENZ_C_FLAGS := -maes -mavx2 -mvaes,仅third_party/7z/*.c用。PS5 是 Zen 2,硬件全支持,运行时无差异
五、7z 引擎设计要点(改代码前必读)
5.1 为什么不走 SDK 的解码器
LZMA SDK 26.03(public domain,已 vendor 到 third_party/7z/,解码子集 60 文件)有两个硬限制,实测复现过:
CSzFolder上限 4 coder / 3 bond —— 7-Zip 默认-m0=bcj2链 = BCJ2 + 4×LZMA2 = 5 coder,SzAr_DecodeFolder()返回SZ_ERROR_UNSUPPORTED。注意SzArEx_Open()用的是另一套宽松扫描器(k_Scan_NumCoders_MAX 64),所以文件列表和解压尺寸仍然全对,失败只在解压时按条目暴露- C 解码器完全没有 7zAES coder ——
-p与-mhe=on全被拒
→ 因此引擎自解析 folder blob + 自己驱动 codec 链(src/sevenz_chain.c/.h,pull pipeline:node_pull(n, dst, want, &got),不够就 node_refill() 拉上游)。
5.2 最关键的坑
【必记】每个 coder 节点的 out_size 必须取 coder_unpack_sizes[index],绝不能用 folder 的 unpack size。 BCJ2 folder 里 MAIN 常大于 folder 最终尺寸(实测 300066 > 300000)。用错的症状:每层 LZMA2 静默短 21 字节,只在特定包上暴露。
其他:
- 编译必须
-DZ7_PPMD_SUPPORT,否则7zDec.c直接丢掉 PPMd CoderUnpackSizes是扁平数组(每条 = 对应 coder 输出流大小),非累计;FoToCoderUnpackSizes[f]..[f+1]是该 folder 的切片- main coder = 第一个未被 bond 消费的 coder
SzArEx_Extract失败后会把半成品留在 block cache → 后续条目报假 CRC,要重置blockIndex- 造夹具用 Extra 包的
7za.exe(有 PPMd);7zr.exe没有 PPMd 编码器 - 调试三件套在
.build/:chainprobe.c(摸内部图)、chainprobe2.c -r <coder>(强制 root 逐层二分)、chaincheck.py(Python liblzma 独立复现同一条链,秒判"图错"还是"循环错")
5.3 7zAES KDF
numCyclesPower = b0 & 0x3F;saltSize = ((b0>>7)&1) + (b1>>4);ivSize = ((b0>>6)&1) + (b1&0x0F),随后依次 salt → iv。
numCyclesPower == 0x3F 时 key = salt||password 补齐/截断到 32 字节;否则 key = SHA256(salt || password_utf16le || counter_le64) 迭代 1<<numCyclesPower 次。之后 AES-256-CBC。
限额 max_aes_cycles = 24(约 8s)。
改引擎前先在 .build/aesprobe.c 独立验证 KDF(用 vendor 的 Sha256.c + Aes.c 解 aes.7z coder0,与 cus[0]=638314 比对),确认后再集成。
5.4 分卷流抽象
src/zipx_volstream.c/.h(ZIP,包成mz_stream)·src/sevenz_volstream.c/.h(7z,包成 SDKISeekInStream)- 结构体首成员必须是
mz_stream stream;/ISeekInStream vt;(回调把void*强转) vol_is_open()必须返回MZ_OK/MZ_OPEN_ERROR(不是 1/0)- vtbl 必须注册
destroy,否则mz_stream_delete()不回调 → 泄漏 - CONCAT 模式对
DISK_NUMBER/DISK_SIZE返回MZ_PARAM_ERROR→ 让 minizip 不切盘 - DISK 模式
set_prop(DISK_NUMBER, -1)必须切到最后一卷(minizip 路径mz_zip.c:2252-2275) remove_source_archives()要删所有卷,避免孤儿卷
5.5 提取门面
src/sevenz_extract.c(1753 行)完全仿 zip_extract.c / rar_extract.c:scan → extract(staging, 每 entry fsync) → publish(整 rename) → cleanup。
- OVERWRITE 与 MERGE 对目录-目录碰撞都递归下钻(仅叶子文件不同)
- 三方共用
src/zipx_common.c(限额 profile +zipx_status_string()) - scan 阶段每 256 entries 报一次进度(曾用 4096,小包扫描期 UI 静默),扫描末 force-report;
precheck_folders入口也强制报一次 - 密码错时 detail 必须带 archive 名(曾是 NULL → i18n
{arg}展开成空 → 用户看到「密码错误: 」后面光秃秃)
六、限额体系(ZIP 与 RAR 共用;7z 同源)
| 限额字段 | default | large | 160GB/9万文件场景 |
|---|---|---|---|
max_entries |
200,000 | 500,000 | 9 万 ✅ |
max_total_bytes |
2 TiB | 4 TiB | 160 GiB ✅ |
max_file_bytes |
512 GiB | 1 TiB | 20 GiB ✅ |
max_ratio |
500 | 1000 | 仅 ≥1GiB 条目受检 |
ratio_min_bytes |
1 GiB | 1 GiB | 小文件豁免 |
- 切 large 档的触发条件:压缩包文件本身 >480 GiB(
assets/main.jsLARGE_FILE_THRESHOLD_BYTES),160GB 包走 default - ratio 有尺寸下限(
ratio_min_bytes= 1GiB):小文件高压缩率合法常见(零填充/稀疏),且写出字节受"声明上限 +check_space()"双重约束,无害 - 唯一真实失败点是磁盘空间:
check_space()按解压后总量查statvfs,峰值 =zip 体积 + 解出体积。分卷场景"传一卷解一卷删一卷"可降峰值 - 32 位安全:引擎内部 size 全
uint64_t;minizipmz_zip.h:34-35的compressed/uncompressed_size是int64_t→ >4GiB 不截断 - 已知 UX 缺陷(未修):进度条 % 用字节(
main.js:1985)、文字进度解压时用条目数(main.js:2014)、ETA 用字节速度(task.c:129-177)。混合大包上割裂,建议统一为字节
七、环境要点(新人必读)
- PS5 是 x86-64 Zen 2(不是 aarch64!),target triple
x86_64-sie-ps5 - PS5 SDK C++ runtime = LLVM libc++(FreeBSD 系 sysroot,无 libstdc++)→ C++ 必须
-stdlib=libc++,链接-lc++ -lc++abi(Makefile 已处理:unrar 用 prospero-clang++ 编) - PS5 SDK libc 的
*at()族(mkdirat/openat/renameat/unlinkat)能链接但运行时损坏:返回 -1 且errno=0(2026-09-06 Frostpunk 2 真机确诊)。zip_extract.c已有"*at() 失败回退全路径调用"兼容层;写新引擎代码时直接用全路径或沿用回退模式。报错要带(errno=%d),errno=0时strerror会骗人 - WSL:
export PS5_PAYLOAD_SDK=/opt/ps5-payload-sdk后才能 make target/user/homebrew/由 songl(197609) 拥有,WSL 身份 song(1000) 写不进 → staging 模式(make install 到 /tmp →sudo cp -r)//wsl$/Ubuntu-22.04/是 SMB 只读视图,改 WSL 文件必须走 Windows 路径- libmicrohttpd 必须
--disable-https --disable-openssl - minizip-ng 4.2.2 补丁(升级会丢):
src/mz_strm_os_posix.cL25 后插#ifndef O_BINARY / #define O_BINARY 0 / #endif - 主机 POSIX shim(CP936 主机必需):
tests/posix_compat.h把lstat/stat → wfm_stat、opendir → _wopendir(UTF-8 转换)、fopen → _wfopen。MinGW ANSI 入口看不见 UTF-8 文件名 - 复杂 commit / tag message 用
-F 文件,不要-m长文本(bash quoting 会挂)
八、唯一功能缺口
-mhe=on 加密头 7z
-mhe=on 时整个 header(含 folder 表)也被加密,引擎必须在解析 folder 之前先用密码解密第二份 header,才能知道有哪些 folder / 用什么 coder。工作量约为标准 7zAES 的 2 倍(两次 AES 解密路径)。
当前 SZ_ERROR_UNSUPPORTED,已在 tests/run-sevenz-tests.sh 的 KNOWN_GAPS(aeshe)标注 —— 缺口修好后脚本会主动报错提醒移除。
真机端到端待验证
ELF 已构建,但需装 PS5 实测:
- ZIP / RAR / 7z 三类分卷真机解压
- 加密 7z(7zAES) 真机解压
- 160GB / 9.5 万文件大 ZIP
- 分卷 RAR 进度条实时走动
- UI 右下角版本号显示
v1.9.1
可选项(非阻塞)
- 性能:实测上游(7-Zip 本体)在 7z 格式上快 1.9×(单线程)/ 3.4×(8 线程);ZIP 无显著差异。差距不在我们的架构(我们比 SDK 自己的
SzArEx路径还快 1.02×)。汇编解码器已于 2026-09-16 启用(Asm/x86/LzmaDecOpt.asm+ jwasm +-DZ7_LZMA_DEC_OPT,1.26×,无 jwasm 自动退纯 C),剩余差距是多线程 LZMA2(未做)。完整数据与复现方式见docs/EXTRACTION-PERF.md,基准工具tests/bench_driver.py - fsync 批量化(每 64MB/N 条刷一次)—— 9.5 万文件级可省 20–30 分钟
- 解压失败保留 staging 支持续解(中等改动)
- 进度条 % / 文字进度 / ETA 三处口径统一为字节
九、仓库许可与代码归属(2026-09-15 核查)
用户曾担心「项目源自他人代码、没有许可」——前提不成立:
- 上游
owendswang/ps5-web-file-manager经 GitHub API 确认 = GPL-3.0(78 stars,last push 2026-09-08) - 本项目
LICENSE(GPL-3.0 全文)在 root commit5cb0b76即存在,与上游一致 - 授权链完整:
ps5-payload-dev/websrv(John Törnblom, GPLv3+,其 Copyright 头仍保留在asset.c/asset.h/mime.h/websrv.h)→owendswang→ 本项目
代码量构成:
- 第三方 vendored 71,528 行(unrar7 27,710 / zlib 20,106 / LZMA SDK 17,248 / minizip-ng 6,464)——重写时原样复用,零成本
- 第一方 20,844 行 = 上游 v1.7 遗产 13,860 + 自有 6,984
- 自有代码中 3,661 行零耦合(
sevenz_chain2094 +zipx_volume658 +zipx_volstream494 +sevenz_volstream415,只依赖 public domain / zlib)→ 可单独抽成 MIT 库
完整评估见 docs/REWRITE-FEASIBILITY.md(三路径:补合规 0.5 天 / 架构重构 12–18 天 / clean-room 重写 35–50 天)。结论:建议补合规而非重写 —— GPL-3.0 保护 7z 引擎成果不被闭源白嫖。
十、工作区状态
工作树已干净(git status 仅剩有意保留的未跟踪文档)。
已清理(2026-09-15):
| 文件 | 说明 | 去向 |
|---|---|---|
erssonglDesktopWeb File Managerps5-web-file-manager¬(2543 B) |
早期 shell 转义事故:一次 git log --oneline --color 的输出被重定向进了文件名。末尾是 U+F022(私用区码位,mojibake 残留),各工具渲染不一 —— git 显示成八进制转义、ls -b 印成 ASCII 引号 |
回收站($R…,2543 B,可还原) |
web-file-mgr-unpack 1.9.1.elf(898 KiB) |
陷阱:文件名写 1.9.1,内嵌却是 9-07 的 v1.9(无 7z 引擎) | 已不在仓库根 |
⚠️ 清理这类特殊文件名时:
SHFileOperationW(带FOF_ALLOWUNDO走回收站)对含私用区码位的路径会返回ERROR_FILE_NOT_FOUND (2),但动作实际已生效。删完务必查C:\$Recycle.Bin\<SID>\$I*记录确认落在回收站($I存原路径 UTF-16,$R是内容)。本沙箱里Add-Type与rm都被拦(后者有 safe-delete 钩子),只能用 Pythonctypes调 shell32。
.build/ 下的探针/调试产物已被 .gitignore 白名单覆盖,不再污染 git status。