Skip to content

How it works

You don't need this page to use archivey. It's for when you want to know what's underneath. Maybe you're deciding whether to trust archivey with archives from strangers and want to know which code reads them. Maybe an error names a package or program you didn't expect, or a file was recognized as a different format than you thought. The page follows what happens when you open an archive and read from it: how archivey recognizes the format, reads the archive's structure, and decodes the data. For why the defaults are the way they are, see Philosophy.

Recognizing the format

Archivey decides the format from the file's contents first. Most formats start with a fixed signature. Some keep it further in, as ISO images do at 32 KiB, or behind a program stub, as self-extracting archives do. A single compressed file such as a .gz is also checked for a TAR header inside, so a tarball named .gz opens as a .tar.gz. The name is used only when the contents don't settle it.

A few formats, such as raw zlib, LZMA alone and Brotli, have no signature, or one too weak to identify them. Archivey recognizes them by trying to decode the start of the data, which is called a probe. A probe can mistake other data for one of these formats, because the start of an unrelated file sometimes decodes without an error. So a probe runs only when the file's extension names that format, when you set always_probe_content=True, or when you call open_stream(). detect_format() runs the same steps without opening the archive.

Reading the archive's structure

Once the format is known, a parser reads the archive's structure: the list of members, their names, sizes and times, and where each one's data starts. Archivey has its own parsers for ZIP, TAR, 7z and RAR, and uses pycdlib for ISO images. All of them are written in Python, so parsing a malicious archive can result in wrong data but can't corrupt memory.

Its own parsers mean every format follows the same rules for metadata, and any archive, solid ones included, can be read in a single forward pass with stream_members().

In archivey's tests, the ZIP, TAR, 7z and RAR parsers are checked against zipfile, tarfile, py7zr, rarfile and unrar, comparing both metadata and bytes. Archivey never imports zipfile, tarfile, py7zr or rarfile at run time. For each format's details, see the format handbooks and the library analysis, both written for people working on archivey itself.

Decoding member data

Many formats share the same compression methods. LZMA appears in 7z, .xz and .tar.xz, and DEFLATE in ZIP, 7z and gzip. Archivey implements each method once, as a stream that decodes another stream, and each format reader stacks the streams a member needs. A method therefore behaves the same, with the same limits and checks, whichever format it comes in. As the data passes, the stack also computes the checksum the format recorded, where it records one, and compares it when the member's last byte comes out.

The decoders come from three places. Python's standard library covers zlib, bzip2 and LZMA, and Zstandard on Python 3.14 and later. The [recommended] extra includes third-party packages that add support for PPMd, Deflate64, Brotli, LZ4, Zstandard on older Pythons, and AES encryption. Archivey's own Python code handles .Z files, the 7z BCJ2, ARM64 and RISC-V filters, the legacy LZ4 frame, and the wrapping around each compressed stream in formats such as gzip and xz. Some decoders written in C can crash on bad data, so archivey runs those in a child process, and can run every decoder in one. Security says which.

RAR is the exception. No good library decodes it, so archivey decompresses RAR data by running an external program, unrar, rar or unar, and reading its output through a pipe, as the rarfile library does. Archivey still reads the RAR structure with its own parser, and reads members stored without compression or encryption itself. The decision record explains the choice.

Compressed data can't be read starting from the middle, so a seek inside a member takes one of three routes. Without help, archivey decompresses the member again from its start, or from where it already is if the target is ahead. Some formats record where they can be restarted: xz keeps an index of its blocks, so a seek starts decoding at the nearest block before the target. For bzip2 data, and large DEFLATE data such as gzip files, the seekable extra adds an accelerator that marks restart points as it reads, and later seeks resume from the nearest one. Seeking inside a member says what decoding again from the start and the accelerator mean for the checksum.