All formats

System.Formats.Tar

LanguageC# (.NET 7 and later)
LicenseMIT (part of the .NET runtime)
Repositorygithub.com/dotnet/runtime
DocumentationSystem.Formats.Tar namespace
Depends onnothing (in the base class library); System.IO.Compression for gzip
Read / writeyes / yes; v7, ustar, pax and GNU formats; no compression of its own

System.Formats.Tar ships with .NET 7 and later. It has 3 levels:

It writes pax by default, so long names, large files and nanosecond times work. It doesn’t compress: combine it with GZipStream (or BrotliStream, ZLibStream) from System.IO.Compression. For .tar.xz or .tar.zst use SharpCompress or a zstd package.

Installation#

Nothing to install on .NET 7 or later: add using System.Formats.Tar;. It isn’t available for .NET Framework or .NET 6.

Basic usage: create and extract a .tar.gz#

C#
using System.Formats.Tar;
using System.IO.Compression;

// create backup.tar.gz from the "docs" folder
using (FileStream fs = File.Create("backup.tar.gz"))
using (var gz = new GZipStream(fs, CompressionLevel.Optimal))
{
    TarFile.CreateFromDirectory("docs", gz, includeBaseDirectory: true);
}

// extract it into "out"
Directory.CreateDirectory("out");
using (FileStream fs = File.OpenRead("backup.tar.gz"))
using (var gz = new GZipStream(fs, CompressionMode.Decompress))
{
    TarFile.ExtractToDirectory(gz, "out", overwriteFiles: true);
}

For an uncompressed .tar pass file paths directly: TarFile.CreateFromDirectory("docs", "docs.tar", false) and TarFile.ExtractToDirectory("docs.tar", "out", true). ExtractToDirectory throws if an entry would land outside the destination folder.

How to list the files in a tar archive#

C#
using System.Formats.Tar;

using FileStream fs = File.OpenRead("archive.tar");
using var reader = new TarReader(fs);
TarEntry? entry;
while ((entry = reader.GetNextEntry()) != null)
{
    Console.WriteLine($"{entry.EntryType,-16} {entry.Length,10} {entry.ModificationTime:u} {entry.Name}");
}

entry.Format tells you whether the entry is V7, Ustar, Pax or Gnu.

How to read one file from a tar into memory#

GetNextEntry(copyData: true) copies the data so it stays readable after the reader moves on; with the default false, read DataStream before the next call.

C#
using System.Formats.Tar;
using System.Text;

static string? ReadText(string tarPath, string name)
{
    using FileStream fs = File.OpenRead(tarPath);
    using var reader = new TarReader(fs);
    while (reader.GetNextEntry() is TarEntry entry)
    {
        if (entry.Name == name && entry.DataStream != null)
        {
            using var sr = new StreamReader(entry.DataStream, Encoding.UTF8);
            return sr.ReadToEnd();
        }
    }
    return null;
}

How to write entries from memory and from files#

C#
using System.Formats.Tar;
using System.Text;

using FileStream fs = File.Create("out.tar");
using var writer = new TarWriter(fs, TarEntryFormat.Pax, leaveOpen: false);

// a file from disk, stored as "data/report.csv"
writer.WriteEntry("report.csv", "data/report.csv");

// a directory entry
writer.WriteEntry(new PaxTarEntry(TarEntryType.Directory, "notes/"));

// a file from memory, with mode and time
byte[] bytes = Encoding.UTF8.GetBytes("hello\n");
var entry = new PaxTarEntry(TarEntryType.RegularFile, "notes/hello.txt")
{
    DataStream = new MemoryStream(bytes),
    Mode = UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.GroupRead | UnixFileMode.OtherRead,
    ModificationTime = DateTimeOffset.UtcNow,
};
writer.WriteEntry(entry);
// disposing the writer writes the end-of-archive blocks

How to extract entries one by one#

C#
using System.Formats.Tar;

string dest = Path.GetFullPath("out");
using FileStream fs = File.OpenRead("archive.tar");
using var reader = new TarReader(fs);
while (reader.GetNextEntry() is TarEntry entry)
{
    string target = Path.GetFullPath(Path.Combine(dest, entry.Name));
    if (!target.StartsWith(dest + Path.DirectorySeparatorChar))
        continue;  // skip entries that escape the folder
    if (entry.EntryType == TarEntryType.Directory)
        Directory.CreateDirectory(target);
    else if (entry.EntryType is TarEntryType.RegularFile or TarEntryType.V7RegularFile)
    {
        Directory.CreateDirectory(Path.GetDirectoryName(target)!);
        entry.ExtractToFile(target, overwrite: true);
    }
}

Tips#