mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-07-08 18:16:30 +00:00
Fix compressed Tar files not listing all entries in archive API
Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com> Agent-Logs-Url: https://github.com/adamhathcock/sharpcompress/sessions/c7d10bd8-f355-4ff4-bd72-d7a9e4e02e92
This commit is contained in:
@@ -119,6 +119,26 @@ public partial class TarArchive
|
||||
{
|
||||
if (header != null)
|
||||
{
|
||||
// For compressed (streaming) archives, buffer each entry's packed stream into
|
||||
// memory immediately. This ensures the underlying compressed stream advances
|
||||
// past each entry's data, and allows entry streams to be opened after the
|
||||
// full entry list has been enumerated.
|
||||
if (_compressionType != CompressionType.None && header.PackedStream != null)
|
||||
{
|
||||
var buffered = new MemoryStream();
|
||||
#if NET8_0_OR_GREATER
|
||||
await using (header.PackedStream)
|
||||
#else
|
||||
using (header.PackedStream)
|
||||
#endif
|
||||
{
|
||||
await header.PackedStream.CopyToAsync(buffered).ConfigureAwait(false);
|
||||
// Disposing TarReadOnlySubStream skips any remaining bytes and padding
|
||||
}
|
||||
buffered.Position = 0;
|
||||
header.PackedStream = buffered;
|
||||
}
|
||||
|
||||
if (header.EntryType == EntryType.LongName)
|
||||
{
|
||||
previousHeader = header;
|
||||
@@ -137,22 +157,37 @@ public partial class TarArchive
|
||||
ReaderOptions
|
||||
);
|
||||
|
||||
var oldStreamPos = stream.Position;
|
||||
|
||||
using (var entryStream = entry.OpenEntryStream())
|
||||
if (stream.CanSeek)
|
||||
{
|
||||
var oldStreamPos = stream.Position;
|
||||
|
||||
using (var entryStream = entry.OpenEntryStream())
|
||||
{
|
||||
using var memoryStream = new MemoryStream();
|
||||
await entryStream
|
||||
.CopyToAsync(memoryStream)
|
||||
.ConfigureAwait(false);
|
||||
var bytes = memoryStream.ToArray();
|
||||
|
||||
header.Name = ReaderOptions
|
||||
.ArchiveEncoding.Decode(bytes)
|
||||
.TrimNulls();
|
||||
}
|
||||
|
||||
stream.Position = oldStreamPos;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Streaming mode: PackedStream is already a buffered MemoryStream
|
||||
using var entryStream = entry.OpenEntryStream();
|
||||
using var memoryStream = new MemoryStream();
|
||||
await entryStream.CopyToAsync(memoryStream).ConfigureAwait(false);
|
||||
memoryStream.Position = 0;
|
||||
var bytes = memoryStream.ToArray();
|
||||
|
||||
header.Name = ReaderOptions
|
||||
.ArchiveEncoding.Decode(bytes)
|
||||
.TrimNulls();
|
||||
}
|
||||
|
||||
stream.Position = oldStreamPos;
|
||||
|
||||
previousHeader = null;
|
||||
}
|
||||
yield return new TarArchiveEntry(
|
||||
|
||||
@@ -129,6 +129,22 @@ public partial class TarArchive
|
||||
{
|
||||
if (header != null)
|
||||
{
|
||||
// For compressed (streaming) archives, buffer each entry's packed stream into
|
||||
// memory immediately. This ensures the underlying compressed stream advances
|
||||
// past each entry's data, and allows entry streams to be opened after the
|
||||
// full entry list has been enumerated.
|
||||
if (_compressionType != CompressionType.None && header.PackedStream != null)
|
||||
{
|
||||
var buffered = new MemoryStream();
|
||||
using (header.PackedStream)
|
||||
{
|
||||
header.PackedStream.CopyTo(buffered, Constants.BufferSize);
|
||||
// Disposing TarReadOnlySubStream skips any remaining bytes and padding
|
||||
}
|
||||
buffered.Position = 0;
|
||||
header.PackedStream = buffered;
|
||||
}
|
||||
|
||||
if (header.EntryType == EntryType.LongName)
|
||||
{
|
||||
previousHeader = header;
|
||||
@@ -147,20 +163,33 @@ public partial class TarArchive
|
||||
ReaderOptions
|
||||
);
|
||||
|
||||
var oldStreamPos = stream.Position;
|
||||
|
||||
using (var entryStream = entry.OpenEntryStream())
|
||||
if (stream.CanSeek)
|
||||
{
|
||||
var oldStreamPos = stream.Position;
|
||||
|
||||
using (var entryStream = entry.OpenEntryStream())
|
||||
{
|
||||
using var memoryStream = new MemoryStream();
|
||||
entryStream.CopyTo(memoryStream, Constants.BufferSize);
|
||||
var bytes = memoryStream.ToArray();
|
||||
|
||||
header.Name = ReaderOptions
|
||||
.ArchiveEncoding.Decode(bytes)
|
||||
.TrimNulls();
|
||||
}
|
||||
|
||||
stream.Position = oldStreamPos;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Streaming mode: PackedStream is already a buffered MemoryStream
|
||||
using var entryStream = entry.OpenEntryStream();
|
||||
using var memoryStream = new MemoryStream();
|
||||
entryStream.CopyTo(memoryStream, Constants.BufferSize);
|
||||
memoryStream.Position = 0;
|
||||
var bytes = memoryStream.ToArray();
|
||||
|
||||
header.Name = ReaderOptions.ArchiveEncoding.Decode(bytes).TrimNulls();
|
||||
}
|
||||
|
||||
stream.Position = oldStreamPos;
|
||||
|
||||
previousHeader = null;
|
||||
}
|
||||
yield return new TarArchiveEntry(
|
||||
|
||||
@@ -268,9 +268,9 @@
|
||||
"net10.0": {
|
||||
"Microsoft.NET.ILLink.Tasks": {
|
||||
"type": "Direct",
|
||||
"requested": "[10.0.2, )",
|
||||
"resolved": "10.0.2",
|
||||
"contentHash": "sXdDtMf2qcnbygw9OdE535c2lxSxrZP8gO4UhDJ0xiJbl1wIqXS1OTcTDFTIJPOFd6Mhcm8gPEthqWGUxBsTqw=="
|
||||
"requested": "[10.0.0, )",
|
||||
"resolved": "10.0.0",
|
||||
"contentHash": "kICGrGYEzCNI3wPzfEXcwNHgTvlvVn9yJDhSdRK+oZQy4jvYH529u7O0xf5ocQKzOMjfS07+3z9PKRIjrFMJDA=="
|
||||
},
|
||||
"Microsoft.NETFramework.ReferenceAssemblies": {
|
||||
"type": "Direct",
|
||||
@@ -442,9 +442,9 @@
|
||||
"net8.0": {
|
||||
"Microsoft.NET.ILLink.Tasks": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.0.23, )",
|
||||
"resolved": "8.0.23",
|
||||
"contentHash": "GqHiB1HbbODWPbY/lc5xLQH8siEEhNA0ptpJCC6X6adtAYNEzu5ZlqV3YHA3Gh7fuEwgA8XqVwMtH2KNtuQM1Q=="
|
||||
"requested": "[8.0.22, )",
|
||||
"resolved": "8.0.22",
|
||||
"contentHash": "MhcMithKEiyyNkD2ZfbDZPmcOdi0GheGfg8saEIIEfD/fol3iHmcV8TsZkD4ZYz5gdUuoX4YtlVySUU7Sxl9SQ=="
|
||||
},
|
||||
"Microsoft.NETFramework.ReferenceAssemblies": {
|
||||
"type": "Direct",
|
||||
|
||||
@@ -275,4 +275,50 @@ public class TarArchiveAsyncTests : ArchiveTests
|
||||
|
||||
Assert.Equal(2, numberOfEntries);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("Tar.tar.gz")]
|
||||
[InlineData("Tar.tar.bz2")]
|
||||
[InlineData("Tar.tar.xz")]
|
||||
[InlineData("Tar.tar.zst")]
|
||||
public async ValueTask CompressedTar_AsyncArchive_Lists_All_Entries_Async(
|
||||
string archiveFileName
|
||||
)
|
||||
{
|
||||
await using var archive = await TarArchive.OpenAsyncArchive(
|
||||
new AsyncOnlyStream(File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, archiveFileName))),
|
||||
new ReaderOptions { LeaveStreamOpen = false }
|
||||
);
|
||||
|
||||
Assert.Equal(6, await archive.EntriesAsync.CountAsync());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("Tar.tar.gz")]
|
||||
[InlineData("Tar.tar.bz2")]
|
||||
[InlineData("Tar.tar.xz")]
|
||||
[InlineData("Tar.tar.zst")]
|
||||
public async ValueTask CompressedTar_AsyncArchive_Entry_Streams_Are_Readable_Async(
|
||||
string archiveFileName
|
||||
)
|
||||
{
|
||||
await using var archive = await TarArchive.OpenAsyncArchive(
|
||||
new AsyncOnlyStream(File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, archiveFileName))),
|
||||
new ReaderOptions { LeaveStreamOpen = false }
|
||||
);
|
||||
|
||||
await foreach (
|
||||
var entry in archive.EntriesAsync.Where(e => !e.IsDirectory).ConfigureAwait(false)
|
||||
)
|
||||
{
|
||||
#if LEGACY_DOTNET
|
||||
using var entryStream = await entry.OpenEntryStreamAsync();
|
||||
#else
|
||||
await using var entryStream = await entry.OpenEntryStreamAsync();
|
||||
#endif
|
||||
using var ms = new MemoryStream();
|
||||
await entryStream.CopyToAsync(ms);
|
||||
Assert.Equal(entry.Size, ms.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -339,4 +339,37 @@ public class TarArchiveTests : ArchiveTests
|
||||
Assert.Equal(ArchiveType.Tar, reader.Type);
|
||||
Assert.True(reader.MoveToNextEntry());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("Tar.tar.gz")]
|
||||
[InlineData("Tar.tar.bz2")]
|
||||
[InlineData("Tar.tar.xz")]
|
||||
[InlineData("Tar.tar.zst")]
|
||||
public void CompressedTar_Archive_Lists_All_Entries(string archiveFileName)
|
||||
{
|
||||
using Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, archiveFileName));
|
||||
using var archive = ArchiveFactory.OpenArchive(stream);
|
||||
|
||||
Assert.Equal(ArchiveType.Tar, archive.Type);
|
||||
Assert.Equal(6, archive.Entries.Count());
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("Tar.tar.gz")]
|
||||
[InlineData("Tar.tar.bz2")]
|
||||
[InlineData("Tar.tar.xz")]
|
||||
[InlineData("Tar.tar.zst")]
|
||||
public void CompressedTar_Archive_Entry_Streams_Are_Readable(string archiveFileName)
|
||||
{
|
||||
using Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, archiveFileName));
|
||||
using var archive = ArchiveFactory.OpenArchive(stream);
|
||||
|
||||
foreach (var entry in archive.Entries.Where(e => !e.IsDirectory))
|
||||
{
|
||||
using var entryStream = entry.OpenEntryStream();
|
||||
using var ms = new MemoryStream();
|
||||
entryStream.CopyTo(ms);
|
||||
Assert.Equal(entry.Size, ms.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user