Merge pull request #1237 from adamhathcock/copilot/fix-zip-extraction-error

Fix DataErrorException when extracting LZMA-compressed zero-byte ZIP entries
This commit is contained in:
Adam Hathcock
2026-03-03 16:21:11 +00:00
committed by GitHub
5 changed files with 66 additions and 0 deletions

View File

@@ -158,6 +158,20 @@ internal abstract partial class ZipFilePart
.ReadFullyAsync(props, 0, propsSize, cancellationToken)
.ConfigureAwait(false);
// When the uncompressed size is known to be zero, skip remaining compressed
// bytes (required for streaming reads) and return an empty stream.
// Bit1 (EOS marker flag) means the output size is not stored in the header
// (the LZMA stream itself contains an end-of-stream marker instead), so we
// only short-circuit when the size is explicitly known to be zero.
if (
!FlagUtility.HasFlag(Header.Flags, HeaderFlags.Bit1)
&& Header.UncompressedSize == 0
)
{
await stream.SkipAsync(cancellationToken).ConfigureAwait(false);
return Stream.Null;
}
context = context with
{
Properties = props,

View File

@@ -149,6 +149,21 @@ internal abstract partial class ZipFilePart : FilePart
reader.ReadUInt16(); // LZMA version
var propsLength = reader.ReadUInt16();
var props = reader.ReadBytes(propsLength);
// When the uncompressed size is known to be zero, skip remaining compressed
// bytes (required for streaming reads) and return an empty stream.
// Bit1 (EOS marker flag) means the output size is not stored in the header
// (the LZMA stream itself contains an end-of-stream marker instead), so we
// only short-circuit when the size is explicitly known to be zero.
if (
!FlagUtility.HasFlag(Header.Flags, HeaderFlags.Bit1)
&& Header.UncompressedSize == 0
)
{
stream.Skip();
return Stream.Null;
}
context = context with
{
Properties = props,

View File

@@ -896,4 +896,18 @@ public class ZipArchiveTests : ArchiveTests
const int expected = (S_IFREG | 0b110_100_100) << 16; // 0644 mode regular file
Assert.Equal(expected, firstEntry.Attrib);
}
[Fact]
public void Zip_LZMA_ZeroSizeEntry_CanExtract()
{
using var archive = ArchiveFactory.OpenArchive(
Path.Combine(TEST_ARCHIVES_PATH, "Zip.lzma.empty.zip")
);
var entries = archive.Entries.Where(x => !x.IsDirectory).ToList();
Assert.Single(entries);
Assert.Equal(0, entries[0].Size);
var outStream = new MemoryStream();
entries[0].WriteTo(outStream);
Assert.Equal(0, outStream.Length);
}
}

View File

@@ -543,4 +543,27 @@ public class ZipReaderTests : ReaderTests
// Should iterate through all entries, not just the first one
Assert.True(count > 1, $"Expected more than 1 entry, but got {count}");
}
[Fact]
public void Zip_LZMA_ZeroSizeEntry_CanExtract_Streaming()
{
var path = Path.Combine(TEST_ARCHIVES_PATH, "Zip.lzma.empty.zip");
using var fileStream = File.OpenRead(path);
using Stream stream = new ForwardOnlyStream(fileStream);
using var reader = ReaderFactory.OpenReader(stream);
var count = 0;
while (reader.MoveToNextEntry())
{
if (!reader.Entry.IsDirectory)
{
count++;
Assert.Equal(0, reader.Entry.Size);
var outStream = new MemoryStream();
reader.WriteEntryTo(outStream);
Assert.Equal(0, outStream.Length);
}
}
Assert.Equal(1, count);
}
}

Binary file not shown.