Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
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/SharpCompress/Archives/SevenZip/SevenZipArchive.cs
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,12 @@ protected override EntryStream GetEntryStream()

var folder = entry.FilePart.Folder;

// If folder is null (empty stream entry), return empty stream
if (folder is null)
{
return CreateEntryStream(Stream.Null);
}

// Check if we're starting a new folder - dispose old folder stream if needed
if (folder != _currentFolder)
{
Expand Down
12 changes: 6 additions & 6 deletions src/SharpCompress/packages.lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -216,9 +216,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",
Expand Down Expand Up @@ -264,9 +264,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",
Expand Down
50 changes: 50 additions & 0 deletions tests/SharpCompress.Test/SevenZip/SevenZipArchiveTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using SharpCompress.Archives;
using SharpCompress.Archives.SevenZip;
using SharpCompress.Common;
using SharpCompress.Common.SevenZip;
using SharpCompress.Factories;
using SharpCompress.Readers;
using Xunit;
Expand Down Expand Up @@ -344,4 +345,53 @@ public void SevenZipArchive_Solid_VerifyStreamReuse()
// The critical check: within a single folder, the stream should NEVER be recreated
Assert.Equal(0, streamRecreationsWithinFolder); // Folder stream should remain the same for all entries in the same folder
}

[Fact]
public void SevenZipArchive_EmptyStream_WriteToDirectory()
{
// This test specifically verifies that archives with empty-stream entries
// (files with size 0 and no compressed data) can be extracted without throwing
// NullReferenceException. This was previously failing because the folder was null
// for empty-stream entries.
var testArchive = Path.Combine(TEST_ARCHIVES_PATH, "7Zip.EmptyStream.7z");
using var archive = SevenZipArchive.OpenArchive(testArchive);

var emptyStreamFileCount = 0;
foreach (var entry in archive.Entries)
{
if (!entry.IsDirectory)
{
// Verify this is actually an empty-stream entry (HasStream == false)
var sevenZipEntry = entry as SevenZipEntry;
if (sevenZipEntry?.FilePart.Header.HasStream == false)
{
emptyStreamFileCount++;
}

// This should not throw NullReferenceException
entry.WriteToDirectory(SCRATCH_FILES_PATH);
}
}

// Ensure we actually tested empty-stream entries
Assert.True(
emptyStreamFileCount > 0,
"Test archive should contain at least one empty-stream entry"
);

// Verify that empty files were created
var extractedFiles = Directory.GetFiles(
SCRATCH_FILES_PATH,
"*",
SearchOption.AllDirectories
);
Assert.NotEmpty(extractedFiles);

// All extracted files should be empty (0 bytes)
foreach (var file in extractedFiles)
{
var fileInfo = new FileInfo(file);
Assert.Equal(0, fileInfo.Length);
}
}
}
Binary file added tests/TestArchives/Archives/7Zip.EmptyStream.7z
Binary file not shown.
Loading