返回资讯中心
外部精选
软件工程
#开发者实时流

研究者构造出最难解压的 tar 包,8 种工具解出 8 种结果

来源标题:The Hardest Tarball

一位研究者利用 GNU tar 的 pax 格式特性构造出 hardest.tar,同一归档在不同工具下会解出 8 种不同结果。该归档通过重复文件生成硬链接、借用 pax 头覆写 size=512,使硬链接后紧跟的 512 字节既可被视作数据也可被视作下一个文件的头。POSIX 对 pax 归档中硬链接是否可携带数据块表述模糊,正是这一歧义造成了解析分歧。

Hacker News 最新stabbles2026/11/7 · 今天9 分钟阅读

以下正文由 AI 翻译自 Hacker News 最新(原文版权归原站所有),可能存在偏差,重要内容请以原文为准。

有个有趣的例子:一个归档文件可以用一次 GNU tar 调用构造出来,而根据你使用的工具不同,它有 8 种不同的解压方式。 $ printf hello > file $ touch empty $ tar --format=pax --pax-option='delete=?time,size:=512'
--transform='s,^./file$,é,'
-cf hardest.tar file ./file empty 它依次从三个文件 file、./file、empty 创建一个归档文件 hardest.tar,其中 file 被重复了一次,因此 GNU tar 会把第二个存为硬链接,而 empty 是空文件。利用第二个条目的唯一路径,这个硬链接被重命名为 é。这会触发 tar 创建一个 pax header,以 UTF-8 存储文件名。我们向 pax header 添加一条 size=512 记录,它会覆盖硬链接默认的 size=0。512 这个值并非巧合:它恰好是一个 tar header 的大小。 从视觉上看,这个归档具有以下结构,基于硬链接是否可以拥有大小,有两种基本解读。 现在,POSIX 规定 pax 归档中硬链接的数据块“可以包含”,但其含义并不完全清楚。那么硬链接之后的 512 字节是它的数据,而不是我们归档时 empty 的 header?一个硬链接怎么可能与其前一个对应文件具有不同的内容? 在用知名的 tar 工具和库解压时,这个归档给出了我迄今遇到的最丰富、最令人惊讶的结果。以下结果均使用撰写本文时所有软件包的最新版本产生。 这一组在原则上合理:它们忽略硬链接上的 size,解压出 file、它的硬链接 é 以及 empty。 这是 macOS 的系统 tar 工具,它在硬链接上 size 概念的 POSIX 精神上更接近,会把那 512 字节写入硬链接。最终结果是 file 现在包含 empty 的 tar header。 这一组同意 bsdtar 的观点,认为那 512 字节是硬链接的数据,但不会把它们写入硬链接。相反,它们向前跳过这些数据,你最终只得到 file 和 é,而没有 empty。 这里你开始需要更多想象力了。因为 é 包含数据,所以会创建一个单独的文件,其中包含 empty 的 header 作为数据,而 file 保留其原始内容。 把 é 变成一个单独的、空文件,并且也解压出 empty。为什么不呢? 完全不解压 é,但作为补偿,它在 file 和 empty 旁边创建一个空目录 PaxHeaders。 R 采用了完全不同的方法:它把 empty 解压为 512 个空字节。它错误地把 size 应用到了 empty 而不是硬链接上,而那 512 字节来自归档结束标记。 Haskell 的 tar 本来会和 GNU tar 完全一样,只可惜它有字符串解码问题,会把磁盘上的文件 é 变成 é(在 2026 年,这不是你浏览器的渲染问题)。 这些实现拒绝了这个 tarball。我认为这是你能做的最好的事情,即使它在技术上并不符合 POSIX。 这个 tarball 是我在测试套件 haampie/tar-minefield 中收集并分类的 134 个棘手 tarball 之一,该套件自上一篇文章以来一直在开发。其网站 https://stoppels.ch/tar 有一个大表格,展示各实现如何处理所有这些情况,以及每个测试用例的详细信息。本文 tarball 的一个变体已作为测试用例包含在内。 如果你想讨论这个问题,请给我发邮件:firstname[at]lastname.ch。

正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。

打开原文
去社区讨论 →