Fix DataErrorException when extracting 0-size LZMA ZIP entries

Co-authored-by: adamhathcock <527620+adamhathcock@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-02-27 22:06:18 +00:00
parent 55c2e75b1d
commit 75d2b70f20
6 changed files with 72 additions and 6 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

@@ -268,9 +268,9 @@
"net10.0": {
"Microsoft.NET.ILLink.Tasks": {
"type": "Direct",
"requested": "[10.0.0, )",
"resolved": "10.0.0",
"contentHash": "kICGrGYEzCNI3wPzfEXcwNHgTvlvVn9yJDhSdRK+oZQy4jvYH529u7O0xf5ocQKzOMjfS07+3z9PKRIjrFMJDA=="
"requested": "[10.0.2, )",
"resolved": "10.0.2",
"contentHash": "sXdDtMf2qcnbygw9OdE535c2lxSxrZP8gO4UhDJ0xiJbl1wIqXS1OTcTDFTIJPOFd6Mhcm8gPEthqWGUxBsTqw=="
},
"Microsoft.NETFramework.ReferenceAssemblies": {
"type": "Direct",
@@ -442,9 +442,9 @@
"net8.0": {
"Microsoft.NET.ILLink.Tasks": {
"type": "Direct",
"requested": "[8.0.22, )",
"resolved": "8.0.22",
"contentHash": "MhcMithKEiyyNkD2ZfbDZPmcOdi0GheGfg8saEIIEfD/fol3iHmcV8TsZkD4ZYz5gdUuoX4YtlVySUU7Sxl9SQ=="
"requested": "[8.0.23, )",
"resolved": "8.0.23",
"contentHash": "GqHiB1HbbODWPbY/lc5xLQH8siEEhNA0ptpJCC6X6adtAYNEzu5ZlqV3YHA3Gh7fuEwgA8XqVwMtH2KNtuQM1Q=="
},
"Microsoft.NETFramework.ReferenceAssemblies": {
"type": "Direct",

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.