Newshot
Account

最难处理的 tarball

AI summary

原文只有标题「The hardest tarball」,没有提供摘要内容,无法提取更多事实。

Translation(model translation, not the source's own text)

最难的 tarball?

2026 年 11 月 7 日

这里有个稀奇的东西:一个归档,它可以用单次 GNU tar 调用构建出来,却会根据你使用的工具不同,以 8 种截然不同的方式被解压。


        $ printf hello > file
$ touch empty
$ tar --format=pax --pax-option='delete=?time,size:=512' \
      --transform='s,^\./file$,café,' \
      -cf hardest.tar file ./file empty
      

它依次从 file、./file、empty 三个文件创建归档 hardest.tar,其中 file 出现两次,以便 GNU tar 把第二个存成硬链接,而 empty 是空的。利用第二条目的唯一路径,这个硬链接被重命名为 café。这会触发 tar 创建一个 pax 头,以 UTF-8 存储文件名。我们往 pax 头里加一条 size=512 记录,覆盖硬链接默认的 size=0。512 这个值并非巧合:它恰好是一个 tar 头的大小。

从视觉上看,这个归档具有如下结构,并且基于硬链接能否拥有大小,存在两种基本解读。

hardest.tar 的结构,基于硬链接能否拥有大小,存在两种基本解读。
hardest.tar 的结构,基于硬链接能否拥有大小,存在两种基本解读。

现在,POSIX 说硬链接的数据块在 pax 归档中“可以被包含”,但其含义并不完全清楚。那么硬链接之后的这 512 字节就是它的数据,而不是我们归档时那个 empty 的头?一个硬链接怎么会有与其前面对应文件不同的内容?

解压它

用知名的 tar 工具和库解压这个归档,产生了迄今为止我遇到过的最丰富、最令人惊讶的结果。以下结果是用写作时所有软件包的最新版本产生的。

GNU tar、BusyBox、Python、Go 和 Perl

这一组原则上很合理:它们忽略硬链接上的大小,解压出 file、它的硬链接 café,以及 empty。

bsdtar

这是 macOS 的系统 tar 工具,在硬链接大小这一概念上更贴近 POSIX 的精神,它把这 512 字节写入硬链接。最终结果是 file 现在包含 empty 的 tar 头。

7-Zip、node-tar、tar-fs,以及 Rust 的 tar 和 astral-tokio-tar

这一组同意 bsdtar 的看法,认为这 512 字节是硬链接的数据,但并不把它们写入硬链接。相反,它们向后跳过这些数据,于是你只剩下 file 和 café,没有 empty。

Java 的 Commons Compress

从这里开始就需要更多想象力了。因为 café 含有数据,所以会创建一个单独的文件,其数据是 empty 的头,而 file 保留其原始内容。

PHP 的 PharData

把 café 变成一个单独的、空的文件,同时还解压出 empty。为什么不呢?

RubyGems

完全不提取 café,但作为弥补,它在 file 和 empty 旁边创建了一个空目录 PaxHeaders。

R 的思路完全不同:它把 empty 解压成 512 个空字节。它错误地把大小应用到了 empty 而不是硬链接上,而这 512 字节来自归档结束标记。

附加:Haskell

Haskell 的 tar 本会和 GNU tar 完全一样,只可惜它有字符串解码问题,会把文件 café 在磁盘上变成 café(在 2026 年,这不是你浏览器的渲染怪癖)。

.NET、Julia 和 uv 的 tar-codec

这些实现拒绝这个 tarball。我认为这是你能做的最好的事,即便从技术上讲它并不符合 POSIX。

更多内容

这个 tarball 是我在测试套件 haampie/tar-minefield 中收集并分类的 134 个刁钻 tarball 之一,该套件自上一篇文章以来一直在开发。它的网站 https://stoppels.ch/tar 有一张大表格,展示各实现如何处理所有这些情况,以及每个测试用例的详细信息。本文这个 tarball 的一个变体也作为一个测试用例被收录。

如果你想讨论这个,请给我发邮件:firstname[at]lastname.ch。

The translation is machine-generated and individual terms may be off. Nothing is rewritten or cut; the original is authoritative.

Published by Lobsters
This site only summarises and aggregates; copyright stays with the original author. Headlines and summaries follow the source — where they differ, the original wins.
Read the original ↗