Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/libraries/System.Formats.Tar/src/Resources/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -208,4 +208,10 @@
<data name="ExtHeaderInvalidRecords" xml:space="preserve">
<value>The extended header contains invalid records.</value>
</data>
<data name="TarSupportedCompressionDetected" xml:space="preserve">
<value>The archive appears to be a {0} compressed file. Wrap the stream with {1} before reading it as a tar archive.</value>
</data>
<data name="TarUnsupportedCompressionDetected" xml:space="preserve">
<value>The archive appears to be a {0} compressed file. Decompress the file before reading it as a tar archive. Note: {0} decompression is not built into .NET.</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ internal sealed partial class TarHeader

archiveStream.ReadExactly(buffer);

// Check for compression magic numbers to provide better error messages if the file is a compressed tar archive (tar.gz, tar.bz2, etc.)
CheckForCompressionMagicNumbers(buffer);

TarHeader? header = TryReadAttributes(initialFormat, buffer, archiveStream);
if (header != null && processDataBlock)
{
Expand All @@ -48,6 +51,9 @@ internal sealed partial class TarHeader

await archiveStream.ReadExactlyAsync(buffer, cancellationToken).ConfigureAwait(false);

// Check for compression magic numbers to provide better error messages if the file is a compressed tar archive (tar.gz, tar.bz2, etc.)
CheckForCompressionMagicNumbers(buffer.Span);

TarHeader? header = TryReadAttributes(initialFormat, buffer.Span, archiveStream);
if (header != null && processDataBlock)
{
Expand Down Expand Up @@ -764,5 +770,47 @@ private static bool TryGetNextExtendedAttribute(
buffer = buffer.Slice(newlinePos + 1);
return true;
}

/// <summary>
/// Checks if the buffer starts with common compression magic numbers and throws appropriate exceptions.
/// This provides better error messages when users try to read compressed tar files without decompressing them first.
/// </summary>
private static void CheckForCompressionMagicNumbers(ReadOnlySpan<byte> buffer)
{
if (buffer.Length < 2)
{
return;
}

static void ThrowIfSupportedCompression(ReadOnlySpan<byte> buffer, ReadOnlySpan<byte> magic, string compressionType, string streamType)
{
if (buffer.StartsWith(magic))
{
throw new InvalidDataException(SR.Format(SR.TarSupportedCompressionDetected, compressionType, streamType));
}
}

static void ThrowIfUnsupportedCompression(ReadOnlySpan<byte> buffer, ReadOnlySpan<byte> magic, string compressionType)
{
if (buffer.StartsWith(magic))
{
throw new InvalidDataException(SR.Format(SR.TarUnsupportedCompressionDetected, compressionType));
}
}

// Check for supported compression algorithms (built into .NET)
ThrowIfSupportedCompression(buffer, [0x1F, 0x8B], "GZIP", "GZipStream");
ThrowIfSupportedCompression(buffer, [0x78, 0x01], "ZLIB", "ZLibStream");
ThrowIfSupportedCompression(buffer, [0x78, 0x5E], "ZLIB", "ZLibStream");
ThrowIfSupportedCompression(buffer, [0x78, 0x9C], "ZLIB", "ZLibStream");
ThrowIfSupportedCompression(buffer, [0x78, 0xDA], "ZLIB", "ZLibStream");

// Check for unsupported compression algorithms (not built into .NET)
ThrowIfUnsupportedCompression(buffer, [0x42, 0x5A], "BZIP2");
ThrowIfUnsupportedCompression(buffer, [0x04, 0x22, 0x4D, 0x18], "LZ4");
ThrowIfUnsupportedCompression(buffer, [0x5D, 0x00, 0x00], "LZMA");
ThrowIfUnsupportedCompression(buffer, [0x89, 0x4C, 0x5A, 0x4F], "LZO");
ThrowIfUnsupportedCompression(buffer, [0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00], "XZ");
}
}
}
Loading