Files
LisherSong--ps5-web-file-ma…/HANDOVER.md
T

18 KiB
Raw Blame History

交接文档 — ps5-web-file-manager 工作进度

交接时间:2026-09-15 · 分支 main · 最新提交 0d036a7 · tag v1.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 声明 + Makefile COMMON_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 文件)有两个硬限制,实测复现过:

  1. 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),所以文件列表和解压尺寸仍然全对,失败只在解压时按条目暴露
  2. 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,包成 SDK ISeekInStream)
  • 结构体首成员必须是 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.js LARGE_FILE_THRESHOLD_BYTES),160GB 包走 default
  • ratio 有尺寸下限(ratio_min_bytes = 1GiB):小文件高压缩率合法常见(零填充/稀疏),且写出字节受"声明上限 + check_space()"双重约束,无害
  • 唯一真实失败点是磁盘空间:check_space() 按解压后总量查 statvfs,峰值 = zip 体积 + 解出体积。分卷场景"传一卷解一卷删一卷"可降峰值
  • 32 位安全:引擎内部 size 全 uint64_t;minizip mz_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.c L25 后插 #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 实测:

  1. ZIP / RAR / 7z 三类分卷真机解压
  2. 加密 7z(7zAES) 真机解压
  3. 160GB / 9.5 万文件大 ZIP
  4. 分卷 RAR 进度条实时走动
  5. UI 右下角版本号显示 v1.9.1

可选项(非阻塞)

  • 性能:实测上游(7-Zip 本体)在 7z 格式上快 1.9×(单线程)/ 3.4×(8 线程);ZIP 无显著差异。差距不在我们的架构(我们比 SDK 自己的 SzArEx 路径还快 1.02×)。已全部落地(2026-09-16):①汇编解码器(LzmaDecOpt.asm+jwasm,1.26×,无 jwasm 自动退纯 C)②多线程 LZMA2(Lzma2DecMt,8 线程,1.37×,线程失败自动降级 chain;BCJ2/加密布局仍走 chain)③ZIP 逐条目 fsync 移除(8000 文件 ≥14×)。7z 现与 7-Zip 单线程打平、ZIP 已压过官方(本机受 Defender 拖累不可比,PS5 无该因素)。RAR 与官方 UnRAR 同速(unrar 自带 target("aes") SIMD 已启用,无逐条目 fsync)。完整数据见 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 commit 5cb0b76 即存在,与上游一致
  • 授权链完整: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_chain 2094 + zipx_volume 658 + zipx_volstream 494 + sevenz_volstream 415,只依赖 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 钩子),只能用 Python ctypes 调 shell32。

.build/ 下的探针/调试产物已被 .gitignore 白名单覆盖,不再污染 git status。