服务:ark查看压缩包拖拽子目录出来复制失败

问题分析

1. 现象

  • 双击 /s500data/fileJob/1606atibm/2105oracle/x99/bak/x99_20260829145835.tar.gz,在 Ark 里打开该压缩包。
  • 在 Ark 界面把包内目录 www/frpc 拖到 Dolphin 的 /s500data/docker/,松手释放鼠标。
  • Ark 界面显示「正在解压 3 个文件」,全程无任何弹窗/交互提示;进度跑完后 /s500data/docker/ 里没有任何新增的 frpc 目录内容。
  • 同样的拖拽操作,在别的压缩包(成员名是相对路径的那种)上是正常的。

2. 结论

根因在上游 Ark(26.04.3),不是 NixOS、不是文件权限、不是 KIO/Dolphin、不是磁盘空间、也不是弹窗被挡住。

具体:当压缩包内的成员名带前导「/」(绝对路径,例如 /www/frpc/docker-compose.yml)时,「按选中条目解压」这一步永远匹配不到条目 ⇒ 静默解出 0 个文件,而进度条仍照报「正在解压 N 个文件」。

同一链路上「解压所有 / 整包拖」走的是另一分支,不受影响(实测同一个包整包解压:2771 个文件 / 600 MB 全部正确落地)。

3. 调用链路(谁在做什么)

  1. Ark 的拖拽不带文件 URL,只带两个私有 MIME:application/x-kde-ark-dndextract-service(发送方 D-Bus 服务名)与 application/x-kde-ark-dndextract-path(对象路径)—— ark/part/archivemodel.cpp::ArchiveModel::mimeData()。
  2. Ark 在 Part 构造时注册 D-Bus 对象 /DndExtract/<n>,接口 org.kde.ark.DndExtract,唯一方法 extractSelectedFilesTo(QString path)。
  3. Dolphin 自己处理这个 MIME:src/views/draganddrophelper.cpp::DragAndDropHelper::dropUrls() 直接发起同步 D-Bus 调用 extractSelectedFilesTo(目标目录),不经过 KIO 的 DropJob;非 Dolphin 的目标才走 KIO 兜底分支 kio/src/widgets/dropjob.cpp,逻辑相同。
  4. Ark 侧 Part::extractSelectedFilesTo() 用 ExtractionOptions::setDragAndDropEnabled(true) 起 ExtractJob。
  5. .tar.gz 由 kerfuffle 的 libarchive 插件处理。strace 实测:没有任何外部 tar/unar 子进程,只有 Ark 自己 chdir(目标目录),也没有 .ark-XXXX 临时目录 ⇒ 那条「临时目录 + moveDroppedFilesToDest」的路径只适用于 7z/rar/zip 等 CliInterface 插件,对 tar.* 不成立。

4. 代码上的不一致(根本成因)

文件:plugins/libarchive/libarchiveplugin.cpp(v26.04.3),两处对「列出」与「抽取」的路径处理不一致:

#位置行为
R01list() → emitEntryFromArchiveEntry():e->setProperty("fullPath", archive_entry_pathname(...))成员名原样进模型 ⇒ 绝对路径归档的模型路径是 /www/frpc、/www/frpc/docker-compose.yml
R02extractFiles():先 while (entryName.startsWith('/')) entryName.remove(0,1),再 remainingFiles.contains(entryName) / fullPaths.indexOf(entryName),不命中则 continue抽取侧剥掉开头「/」再与模型路径做全等匹配 ⇒ www/frpc ≠ /www/frpc ⇒ 每个条目都被跳过,0 个被解出且不报错
R03进度数字来源:totalEntriesCount = files.size()等于「所选条目数」(该目录 + 2 个文件 = 3),与实际写出多少无关 ⇒ 所以会一本正经显示「正在解压 3 个文件」然后目标里一个字节都没有
R04extractAll 分支(未选任何条目 / 整包拖)完全绕过 R02 那段匹配 ⇒ 整包解压永远正常,这也是「同一个包两种结果」的原因

5. 证据

5.1 真实拖拽的现场抓取(dbus-monitor + 50 ms 全树轮询 + Ark 的 fd 扫描)

#抓取项结果
R05Dolphin 实际传给 Ark 的目标目录字符串extractSelectedFilesTo "/s500data/docker" —— 完全正确(不是 URL、不是空、不是别的目录)
R06拖拽后 /s500data/docker 下的新增/删除(50 ms 粒度、含隐藏项)零(连临时目录都没有出现过)
R07/s500data/docker 的 mtime仍是 13:48,而拖拽发生在 14:16:35 ⇒ 那次一个条目都没落盘
R08全盘反向搜索 13:40 之后的 frpcv100.toml / .ark-*无(/s500data、/home/cat、/tmp 都查过)

5.2 对照实验(同一个压缩包、同一条 D-Bus 调用)

#做法结果
R09选中包内一个目录(如根级 home)→ 解到空临时目录❌ 什么都不写,返回码 0,无报错、无弹窗(复现用户症状)
R10不选任何条目(= 解压全部)→ 解到空临时目录✅ 2771 个文件 / 600 MB 全部落地,www/frpc/docker-compose.yml、www/frpc/frpcv100.toml 齐全
R11同一操作,小压缩包(成员名是相对路径 www/frpc/…),选中子目录✅ 正常落地为 <目标>/www/frpc/… ⇒ 说明「按选中条目解压」这条代码路径本身是通的,差别只在成员名是否带开头「/」
R12对 R10 那次解压做 strace -f -e trace=file无外部 tar/unar 子进程,只有 chdir(<目标目录>) ⇒ 确认走 libarchive 插件、无临时目录搬移环节

5.3 归档本身的形态

tar -tzf 与 python3 tarfile 逐条核对:全部 3846 个成员都是绝对路径(/home/x99/backupGhost.sh、/www、/www/frpc/docker-compose.yml 等,属主 uid/gid = 1000 / x99)。这类包一般由 tar czf x.tgz /www /home/x99(带前导「/」)打包产生。

附:本笔记第一版曾把成因判成「覆盖确认对话框弹在 Ark 窗口」——该结论已作废,原因是当时只测了「目标已存在同名文件」的小包场景;本次用同一个 336 MB 的包做「选中 vs 不选」对照,才把差异锁定到「成员名前导 / 」。

解决记录

6. 判据(10 秒自证)

tar -tzf /s500data/fileJob/1606atibm/2105oracle/x99/bak/x99_20260829145835.tar.gz | head
  • tar 会提示「从成员名中删除开头的“/”」⇒ 有这个提示就命中本 bug。
  • 或用「同一个包」两步对照:拖包内某个目录 → 零落地;解压所有 / 整包拖 → 正常。

7. 处置(按代价从低到高)

  1. 直接绕过(推荐):这个包要用「解压所有」或「解压到…」;或在终端 tar -xzf <包> -C <目标目录>(GNU tar 自己会剥掉开头「/」,行为正确)。
  2. 想保留拖拽习惯:先把这个包洗成相对路径成员名再拖:mkdir x && tar -xzf old.tgz -C x && tar -czf new.tgz -C x .(新包成员是 ./www/...,Ark 侧能匹配;代码里「./」有显式剥离逻辑,但该形态尚未在 Ark 里实测,用前先拿小包试一次)。
  3. 要根治拖拽:只能给 Ark 打补丁(extractFiles() 两侧做同样的前导「/」归一化,或用规范化后的路径比较)。未做:需重新编译 Ark;且本机 bugs.kde.org / invent.kde.org TLS 不可达、KDE/ark 在 GitHub 上没有 issue 库 ⇒ 无法确认上游是否已修。

8. 待实测

  • 该 bug 是 26.04.3 独有还是长期存在(需逐版本对比 libarchiveplugin.cpp 这段代码)。
  • 上游是否已修(bugs.kde.org / invent.kde.org 本机不可达)。
  • 触发条件是否只有「成员名以「/」开头」这一种(./www/… 形态未在 Ark 里实测)。

9. 取证手法(下次同类问题直接复用)

  • dbus-monitor "type='method_call',interface='org.kde.ark.DndExtract'" 一次就能钉死「Dolphin 到底传了什么目标目录」。
  • 目标目录用 50 ms 全树轮询 + /proc/<ark pid>/fd 双证据;只看 ls 会漏掉「建了又删」。
  • strace -f -e trace=file -s 300 -- .ark-wrapped <包> 能直接判「有没有起外部进程」「chdir 到哪」「写了哪些路径」。本机 KDE release 构建下 QT_LOGGING_RULES='ark.debug=true' 零输出,别指望 qCDebug。
  • 免鼠标验证「Ark 解压落地」这半边:qdbus org.kde.ark-<pid> /DndExtract/1 org.kde.ark.DndExtract.extractSelectedFilesTo <目录>;服务名必须按 pgrep -x ark 的真实 pid 取(qdbus | grep 取第一个会打到用户自己那个实例)。
  • 判「文件是不是这次写的」看 ctime:stat -c '%n mtime=%y ctime=%z'(KIO 复制与同盘 rename 都保留 mtime、只更新 ctime)。
  • 本机 xdotool 不存在;computer_use 往 Ark 发合成点击/按键经常不生效、甚至把 Ark 弄崩(本次两次 SIGSEGV)⇒ 别把「自动化选中失败」当成机制失效。
  • pkill -f <模式> 会连执行它的自己一起杀(本次又栽一次,整条命令被 SIGTERM)⇒ 用 pid kill。

10. 落点

  • 项目台账(Hermes 维护):hermes-nixos-workspace/docs/ark-drag-drop-extract-conflict.md(第二版,含全部证据与对照实验);INDEX 检索行与变更日志已同步。
  • 可复用排障知识:skill nixos-desktop-troubleshooting 的 references/ark-dnd-extract.md(机制 / 静默失败分支 / 取证三件套)。

补充:为什么命令行 tar 没事、Ark 却翻车

一句话:tar 的「安全」发生在写盘那一刻;Ark 的失败发生在写盘之前的「按名单挑条目」。 两层不是同一个地方,所以「tar 能安全处理绝对路径」和「Ark 拖拽解不出来」可以同时成立,互不矛盾。

11.1 采信(已用上游一手材料在本机核实)

#外部说法核实结果
R13GNU tar 解压时默认剥掉成员名前导「/」,把 /etc/passwd 当 etc/passwd 解到当前目录,所以带绝对路径的包在命令行下完全正常且安全✅ 采信。本机对这个包的 tar -tzf 直接打出「从成员名中删除开头的“/”」,且 `tar -xzf` 行为正确
R14存在安全缺陷 CVE-2024-57966:24.12.0 之前的 Ark 会把条目解到绝对路径✅ 采信。NVD 原文:「libarchiveplugin.cpp in KDE ark before 24.12.0 can extract to an absolute path from an archive.」(published 2025-02-03)
R15修复提交 fe518d81 要求 libarchive 把绝对路径当相对路径,并设置 ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS✅ 采信。KDE/ark commit fe518d81(2024-11-07)标题即「Treat absolute paths as relative paths during extraction」,正文含该 flag
R16本机 Ark 26.04.3 已包含该修复✅ 采信。本机插件源码的写盘 flags 为 ARCHIVE_EXTRACT_TIME | …_SECURE_NOABSOLUTEPATHS | …_SECURE_NODOTDOT | …_SECURE_SYMLINKS ⇒ 写盘这一层不会再跑到绝对路径去

11.2 修正(外部说法与代码事实的差别)

  • 失败点不在写盘、也不只是「截断路径」那一步,而在抽取前的名单匹配:模型里合成出来的目录条目是相对路径(parentFor() 把 fullPath 去掉尾斜杠后按「/」分词、跳过空段再重建,根级得到 www/),而真实文件条目原样保留绝对路径(emitEntryFromArchiveEntry() 直接存 /www/frpc/a.txt)⇒ 同一棵树里两种写法混用。
  • extractFiles() 又先剥掉条目名开头的「/」,再拿去和带「/」的模型路径做全等比较 ⇒ 必然对不上 ⇒ 每条 continue。表现是静默 0 个文件,而不是「解到绝对路径去」。
  • 因此「已修复 CVE,但拖拽这条路径仍会以另一种形式翻车」这个方向是对的;但不能理解为安全漏洞复发:NOABSOLUTEPATHS 管的是写盘落点,管不到 Ark 自己那套「模型路径 ↔ 抽取名单」的比较逻辑。
  • 结论措辞上也不宜写成「已知的、已修复的缺陷」:本次能确证的是 CVE 与其修复,以及当前版本仍会静默失败这两件事;「同一 bug 号」没有证据。

11.3 待核 / 未核实

  • 「parentFor() 返回的路径不带尾部斜杠,导致非顶层条目截断后开头多出一个「/」」这一细节:我读到的 parentFor() 是 fullPath.endsWith('/') → chopped(1) 再分词重建相对路径,真正的字符串截断在 extractFiles() 按 rootNode 移除那一段。两者是否等价未逐一验证,暂记「方向一致、细节待核」。
  • 除 CVE-2024-57966 外是否存在单独的上游 issue 号专记「拖拽抽取静默失败」:本机 bugs.kde.org / invent.kde.org TLS 不可达,GitHub 上的 KDE/ark 只是镜像、没有 issue 库 ⇒ 未核实,对外材料里不要写「已知 bug #xxxx」。

复核命令(本机可直接跑,出网需带 http_proxy=127.0.0.1:7890):tar -tzf <包> | head;grep -n 'ARCHIVE_EXTRACT' <libarchiveplugin.cpp>;curl https://api.github.com/repos/KDE/ark/commits/fe518d81;curl 'https://services.nvd.nist.gov/rest/json/cves/2.0?cveId=CVE-2024-57966'。