Library / SDK
composefs/tar-rs avatar
composefs/tar-rs

tar-rs, a tar reader and writer for Rust that streams

Tar file reading/writing for Rust

742 stars255 forksRustApache-2.0

At a glance

What is it?
composefs/tar-rs is the tar crate on crates.io, a Rust implementation of the tar format that is abstract over its input and output and never requires the whole archive to be resident in memory. It handles the format itself and leaves compression to something else, and the project carries a small Unix extension for extended attributes.
Who is it for?
Adopt tar-rs when you need to read or write tar archives in Rust and want streaming rather than loading an archive into memory, especially for layered images and container tooling. Skip it if you need compression, because the library deliberately does not handle it.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 32 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What the crate does and what it deliberately leaves out

tar-rs is a tar archive reading and writing library for Rust, published on crates.io as the tar crate.

The package description in the repository is more informative than the README on this point, and it states two constraints plainly. The library does not currently handle compression. And it is abstract over all I/O readers and writers. Additionally, great lengths are taken to ensure that the entire contents are never required to be entirely resident in memory all at once.

That last point is the design centre of the crate. A tar archive is a sequence of headers each followed by a file's contents, and the format is designed to be processed as a stream. A library that reads an entire archive into a vector before handing you anything is simple to write and useless for the case tar is actually used for, which is large images and large file sets that you want to copy through without a temporary copy of everything.

Leaving compression out is a deliberate scope decision rather than a gap. The tar format itself does not compress; compressed variants such as tar.gz and tar.zst wrap the same container in a compression layer, so a library that handled compression would be doing two jobs and would have to be a codec rather than a format implementation. The consequence is that you compose it with a compression stream, which is exactly how the pipeline ends up working in practice.

The licence is Apache-2.0 or MIT at your option, the crate is on docs.rs as tar, and the version is 0.4.46 with the last push on 2026-09-21.

The authorship detail is worth noting for anyone who has used the crate before it moved. The Cargo.toml lists Alex Crichton as the author, and the repository is now under the composefs organisation, which is the composefs organisation.

Reading an archive

The README's reading example is short and shows the shape of the API.

```rust,no_run extern crate tar;

use std::io::prelude::*; use std::fs::File; use tar::Archive;

fn main() { let file = File::open("foo.tar").unwrap(); let mut a = Archive::new(file);

for file in a.entries().unwrap() { // Make sure there wasn't an I/O error let mut file = file.unwrap();

// Inspect metadata about the file println!("{:?}", file.header().path().unwrap()); println!("{}", file.header().size().unwrap());

// files implement the Read trait let mut s = String::new(); file.read_to_string(&mut s).unwrap(); println!("{}", s); } } ```

Three things in that snippet carry the design.

The Archive is constructed over a File, and the type parameter is the reader. That is the abstraction over I/O in practice: the same Archive works over a File, a Cursor over a byte slice, or a network stream.

The entries method returns an iterator, and each item is a Result. The comment in the example points at the reason, which is that you have to make sure there was not been an I/O error, and the unwrap on the entry is the step that surfaces it. A tar archive is untrusted input, and the iterator that stops on the first read error rather than yielding a truncated entry is what keeps a truncated file from being mistaken for a complete one.

The entry itself implements Read, and the file contents are read through the standard trait. That is what makes the streaming property real: you read one entry's contents into memory, or better you hand the entry to something that copies it, and the rest of the archive never exists in memory at all.

The header is reached through a separate method from the contents, so metadata and payload are accessed independently, which is what you want when you are listing an archive without extracting it.

Writing an archive, the xattr feature and the project layout

Writing is the mirror image and uses the Builder.

```rust,no_run extern crate tar;

use std::io::prelude::*; use std::fs::File; use tar::Builder;

fn main() { let file = File::create("foo.tar").unwrap(); let mut a = Builder::new(file);

a.append_path("file1.txt").unwrap(); a.append_file("file2.txt", &mut File::open("file3.txt").unwrap()).unwrap(); } ```

The two append methods differ in where the content comes from. append_path takes the path and the builder opens the file itself, while append_file takes a name and a reader, so you can put content under a different name than the file it came from, which is what you need when you are constructing a layer.

The feature list in the Cargo.toml is short. The default feature is xattr, and the target-specific dependencies show that the xattr crate and libc are pulled in on unix targets only, with the xattr crate marked optional. So extended attributes are enabled by default on Linux and macOS and simply absent on Windows, which is the correct behaviour for a format extension that those platforms do not have.

The rest of the manifest is careful in ways that matter for a library. The rust-version is 1.63, so the minimum supported Rust is reasonably old and the crate is usable in older toolchains. The lints section denies dead_code, with a comment inviting contributors to comment it out locally while working on a patch, which is a sensible accommodation for a codebase where the linter would otherwise fire on in-progress code.

The dev dependencies tell you about the test approach. There is astral-tokio-tar, alongside tokio and tokio-stream, which points at asynchronous code paths being exercised in tests. There is tempfile and rand, and the exclude field in the manifest excludes tests/archives from the published package, so the binary test fixtures do not bloat the crate.

The repository layout shows a mature project. There is a fuzz/ directory, which for an archive parser is the single most valuable thing to have, since malformed archives are the attack surface. There is an xtask/ directory for build automation, a.devcontainer/, an examples/ directory with extract_file, list, raw_list and write, a REVIEW.md, renovate.json for dependency updates, and a.cargo/ directory.

The examples list is itself a hint about the API surface, because raw_list and list suggest two levels of listing: the raw header fields, and the interpreted ones.

Where it fits against the alternatives

The comparison in Rust is with the tar crate's predecessors and with the system tar command.

The system tar is the tool most people reach for, and shelling out to it from a program is a common approach. The problem is that it means the archive handling happens in another process, with a serialisation boundary, and that you get the behaviour of whatever version is installed rather than the behaviour you coded. A library gives you the format semantics in your own process and lets you handle errors the way you want.

The other option some projects take is combining a compression crate with a hand-rolled tar parser, which is exactly the work this crate saves.

The important comparison is with the tar implementations in other ecosystems, and the reason this crate is well suited to container tooling is the streaming guarantee. A layered image format is a stack of tar archives, and building one means writing each layer in turn, reading the previous one to compute the diff. Doing that with a library that holds a whole archive in memory means the image build needs as much memory as the image is large, which defeats the point of layering.

The limitation to keep in mind is the compression boundary. Because the crate does not compress, a caller that wants a compressed tar has to compose it with a codec, and it is worth understanding where that composition goes wrong. The usual failure is a compressed stream that is read but not decompressed, or one that is decompressed but not verified, and the resulting archive is not readable by tar even though the code appears correct.

The xattr feature is the second limitation, and it is smaller. On a filesystem that does not support extended attributes, or on an archive format that does not carry them, the information is simply not there, and a tool that needs it has to detect that rather than assume it.

For a Rust program that needs to read or write tar, whether for container images, for release tarballs or for backup, this is the library to use, and the streaming design is the reason.

Editorial conclusion

Adopt tar-rs when you need to read or write tar archives in Rust and want streaming rather than loading an archive into memory, especially for layered images and container tooling. Skip it if you need compression, because the library deliberately does not handle it. Verify first by reading a large archive entry by entry and watching the memory use, and by checking that the xattr feature behaves on the filesystems you deploy to.

Frequently asked questions

Does the tar crate for Rust handle compression?

No. The package description states that the library does not currently handle compression, and that it is abstract over all I/O readers and writers, so you compose it with a compression stream separately.

How does tar-rs avoid loading a whole archive into memory?

The crate states that great lengths are taken to ensure the entire contents are never required to be entirely resident in memory at once. The entries method returns an iterator of entries, each of which implements Read, so you process one file at a time through the stream.

What are the two ways to add a file with the tar Builder?

append_path takes a path and opens the file itself, while append_file takes a name and a reader, so the content can be stored under a different name than the file it came from. The README's example uses a call to append_file with file2.txt as the name and file3.txt as the source.

What does the xattr feature do in tar-rs?

It enables extended attribute support, and it is on by default. The xattr crate and libc are target-specific dependencies for unix, with the xattr crate optional, so the feature is simply absent on Windows, which does not have those attributes.

What licence is tar-rs released under?

Apache-2.0 or MIT at your option, and the README states that unless a contribution says otherwise, any contribution submitted for inclusion is dual licensed under the same terms without any additional conditions. The crate is published on crates.io as tar and the version is 0.4.46.

Official sources

  1. composefs/tar-rs on GitHub
  2. License: Apache-2.0
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/composefs-tar-rs.svg)](https://hysenlabs.com/projects/composefs-tar-rs)

Was this useful?