This document describes the Tar implementation that exists in SharpCompress today.
It is intentionally SharpCompress-specific. It documents actual behavior in the current codebase, including partial support and limitations. It is not a general tar format reference.
Write support is implemented in `src/SharpCompress/Writers/Tar/TarWriter.cs` and currently accepts only `CompressionType.None`, `CompressionType.GZip`, `CompressionType.BZip2`, and `CompressionType.LZip`.
## Detection Behavior
Tar detection is implemented in `TarFactory.IsArchive`, `TarFactory.IsArchiveAsync`, `TarFactory.GetCompressionType`, and `TarFactory.GetCompressionTypeAsync`.
Detection behavior is:
1. Wrap the incoming stream in `SharpCompressStream`.
2. Start recording with a rewind buffer sized from `TarWrapper.MaximumRewindBufferSize`.
3. Probe each registered wrapper in order.
4. If a wrapper matches, create a decompression stream for that wrapper.
5. Call `TarArchive.IsTarFile` or `TarArchive.IsTarFileAsync` on the decompressed stream.
6. If the tar probe succeeds, treat the stream as tar with that wrapper compression.
Implications:
- Tar detection is content-based, not extension-based.
- Wrapper detection is not sufficient by itself. The decompressed payload must also parse as tar.
- Non-seekable detection is supported through the recording and rewind mechanism.
- The largest rewind requirement currently comes from BZip2, which declares a larger minimum probe buffer in `TarWrapper`.
`TarArchive.IsTarFile` and `TarArchive.IsTarFileAsync` attempt to read a single tar header and return `false` on any exception. They also treat an all-zero empty archive block as a valid empty tar archive when the entry type is defined.
- The reader always enumerates entries in streaming mode.
- It works with non-seekable input streams.
- It applies decompression based on the detected wrapper compression type before parsing tar headers.
- Entry streams are backed by `TarReadOnlySubStream`.
`TarReadOnlySubStream` has an important behavior: disposing an entry stream consumes any unread entry bytes and any required 512-byte padding so that the next header can be read correctly. This is what makes skipping entries work in streaming mode.
### Reader Compression Mapping
`TarReader.RequestInitialStream` and `RequestInitialStreamAsync` map the detected wrapper to the corresponding decompression stream:
-`None`
-`BZip2`
-`GZip`
-`ZStandard`
-`LZip`
-`Xz`
-`Lzw`
### Reader Entry Semantics
For each entry, SharpCompress exposes:
-`Key` from the parsed tar name
-`LinkTarget` for symbolic and hard links
-`Size`
-`CompressedSize`
-`LastModifiedTime`
-`IsDirectory`
-`Mode`
-`UserID`
-`GroupId`
Tar entries are always reported as unencrypted and CRC is always `0`.
`TarArchive.OpenArchive(Stream)` and `TarArchive.OpenAsyncArchive(Stream)` require a seekable stream and throw `ArgumentException` when `CanSeek` is `false`.
`TarArchive.OpenArchive(FileInfo)` and the list-based overloads use `SourceStream` and determine wrapper compression by calling `TarFactory.GetCompressionType`.
The writer supports these output compression types:
-`CompressionType.None`
-`CompressionType.GZip`
-`CompressionType.BZip2`
-`CompressionType.LZip`
Any other compression type causes `InvalidFormatException`.
### Stream Ownership
If `LeaveStreamOpen` is `true`, `TarWriter` wraps the destination in a non-disposing stream.
### File Writing
`TarWriter.Write` and `WriteAsync` write a tar header followed by file contents, then pad the payload to the next 512-byte boundary.
If the source stream is non-seekable and the caller does not supply `size`, the writer throws `ArgumentException` because tar requires the file size in the header.
### Directory Writing
`WriteDirectory` and `WriteDirectoryAsync` normalize the directory name to use forward slashes and ensure the key ends with `/`.
Empty or root-equivalent directory names are skipped.
### Archive Finalization
If `FinalizeArchiveOnClose` is `true`, disposing the writer writes two 512-byte zero blocks to terminate the archive.
If the output stream implements `IFinishable`, dispose also calls `Finish()`.
## Header Write Formats
`TarHeaderWriteFormat` is defined in `src/SharpCompress/Common/Tar/Headers/TarHeaderWriteFormat.cs`.
Supported write formats:
-`GNU_TAR_LONG_LINK`
-`USTAR`
`TarWriterOptions.HeaderFormat` defaults to `GNU_TAR_LONG_LINK`.
Current implementation behavior is narrower than the option surface suggests:
- sync file writes use the configured `HeaderFormat`
- sync directory writes currently construct the default tar header format
- async file writes currently construct the default tar header format
- async directory writes currently construct the default tar header format
In practice, this means the configured `HeaderFormat` is currently honored only by the synchronous file write path.
### GNU Long Name Write Behavior
In GNU mode, when a file name exceeds the 100-byte field, `TarHeader.WriteGnuTarLongLink` writes a synthetic long-name header using `././@LongLink` and `EntryType.LongName`, then writes the long name payload, and finally writes the actual file entry.
GNU mode also writes large file sizes using binary size encoding when the size does not fit the standard octal field.
### USTAR Write Behavior
When the synchronous file write path is configured for `USTAR`, `TarHeader.WriteUstar` attempts to split a long path into:
- the main `name` field
- the `prefix` field
If the name cannot be represented in USTAR field limits, the writer throws `InvalidFormatException` and instructs the caller to use GNU Tar format instead.
## Header Read Behavior
Tar header parsing is implemented in `TarHeader.Read` and `TarHeader.ReadAsync`.
For USTAR headers, if the magic field is `ustar` and the prefix field is populated, SharpCompress reconstructs the entry name as `prefix + "/" + name`.
## Name and Metadata Handling
### Path Normalization
Writer path normalization is implemented in `TarWriter.NormalizeFilename` and `NormalizeDirectoryName`.
Behavior:
- backslashes are converted to `/`
- drive prefixes before `:` are removed
- leading and trailing `/` are trimmed for file entries
- directory entries are normalized to end with `/`
### Encoding
Tar name encoding and decoding is controlled by `IArchiveEncoding`.
- reader APIs decode names with `ReaderOptions.ArchiveEncoding`
- writer APIs encode names with `TarWriterOptions.ArchiveEncoding`
The tests include UTF-8 and code page coverage for tar name handling.
### Metadata Surface
Tar metadata currently surfaced through `TarEntry` includes:
- name
- link target
- mode
- uid
- gid
- size
- last modified time
Writer metadata is narrower. The writer sets:
-`LastModifiedTime`
-`Name`
-`Size`
- entry type for file or directory
The current writer writes fixed mode, owner id, and group id defaults rather than round-tripping full metadata.