The Hardest Tarball?
This post meticulously crafts a 'hardest tarball' – a single archive designed to extract in eight distinct ways depending on the utility used. It delves deep into the ambiguous POSIX specifications around hard links and pax headers, showcasing the surprising and often inconsistent behaviors of common archiving tools. This kind of low-level exploration of system utilities and their edge cases is a perennial favorite for the technically curious HN audience.
The Lowdown
The author presents a fascinating curiosity: a single tar archive, dubbed the 'hardest tarball', that extracts with wildly varying results across different tools. This highly engineered tarball exploits ambiguities in the POSIX standard regarding hard links and the PAX format, particularly concerning how size attributes are handled for hard-linked files. The article methodically demonstrates how these subtle differences lead to a diverse array of outcomes, highlighting the practical challenges of interoperability in fundamental system utilities.
- Tarball Construction: The
hardest.taris created with a singleGNU tarcommand, archivingfile, a hard link tofile(renamed tocafé), andempty. A PAX header is specifically crafted to includesize=512for thecaféentry, which is exactly the size of a tar header. - POSIX Ambiguity: The core issue stems from POSIX's vague wording that data blocks for a hard link 'may be included' in PAX archives, leading to different interpretations of whether a hard link can possess its own data payload separate from its source.
- Extraction Divergence: Various popular
tarimplementations interpret and extract this tarball in unique ways:- GNU tar, BusyBox, Python, Go, Perl: Generally ignore the hard link's
sizeand extractfile,café, andemptyas expected. - bsdtar (macOS): Interprets the 512 bytes as data for the hard link, resulting in
cafécontaining the header ofempty. - 7-Zip, node-tar, Rust's tar, etc.: Recognize the 512 bytes as data but skip them, leading to
emptybeing omitted from extraction. - Java's Commons Compress: Creates a separate file for the
emptyheader ascafénow has data. - PHP's PharData: Makes
caféa separate, empty file and extractsempty. - RubyGems: Does not extract
caféat all, but creates an emptyPaxHeadersdirectory. - R: Extracts
emptywith 512 null bytes, misapplying the size toemptyand using end-of-archive markers. - Haskell's tar: Similar to GNU tar but suffers from string decoding issues, rendering
caféascafé. - .NET, Julia, uv's tar-codec: These implementations simply reject the malformed tarball, which the author posits might be the most sensible, if not strictly POSIX-compliant, approach.
- GNU tar, BusyBox, Python, Go, Perl: Generally ignore the hard link's
- Related Project: This 'hardest tarball' is one of 134 tricky examples collected in the author's
haampie/tar-minefieldtest suite, which aims to systematically document and comparetarimplementation behaviors.
This demonstration serves as a stark reminder that even seemingly fundamental data formats can harbor deep ambiguities, leading to significant interoperability challenges across different software implementations. The author's tar-minefield project is an invaluable resource for understanding these edge cases.