Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
71 changes: 51 additions & 20 deletions src/ICSharpCode.SharpZipLib/Zip/ZipInputStream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using ICSharpCode.SharpZipLib.Zip.Compression;
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
using System;
using System.Diagnostics;
using System.IO;

namespace ICSharpCode.SharpZipLib.Zip
Expand Down Expand Up @@ -180,31 +181,12 @@ public ZipEntry GetNextEntry()
CloseEntry();
}

int header = inputBuffer.ReadLeInt();

if (header == ZipConstants.CentralHeaderSignature ||
header == ZipConstants.EndOfCentralDirectorySignature ||
header == ZipConstants.CentralHeaderDigitalSignature ||
header == ZipConstants.ArchiveExtraDataSignature ||
header == ZipConstants.Zip64CentralFileHeaderSignature)
if (!SkipUntilNextEntry())
{
// No more individual entries exist
Dispose();
return null;
}

// -jr- 07-Dec-2003 Ignore spanning temporary signatures if found
// Spanning signature is same as descriptor signature and is untested as yet.
if ((header == ZipConstants.SpanningTempSignature) || (header == ZipConstants.SpanningSignature))
{
header = inputBuffer.ReadLeInt();
}

if (header != ZipConstants.LocalHeaderSignature)
{
throw new ZipException("Wrong Local header signature: 0x" + String.Format("{0:X}", header));
}

var versionRequiredToExtract = (short)inputBuffer.ReadLeShort();

flags = inputBuffer.ReadLeShort();
Expand Down Expand Up @@ -300,6 +282,54 @@ public ZipEntry GetNextEntry()
return entry;
}

/// <summary>
/// Reads bytes from the input stream until either a local file header signature, or another signature
/// indicating that no more entries should be present, is found.
/// </summary>
/// <exception cref="ZipException">Thrown if the end of the input stream is reached without any signatures found</exception>
/// <returns>Returns whether the found signature is for a local entry header</returns>
private bool SkipUntilNextEntry()
{
// First let's skip all null bytes since it's the sane padding to add when updating an entry with smaller size
var paddingSkipped = 0;
while(inputBuffer.ReadLeByte() == 0) {
paddingSkipped++;
}

// Last byte read was not actually consumed, restore the offset
inputBuffer.Available += 1;
if(paddingSkipped > 0) {
Debug.WriteLine("Skipped {0} null byte(s) before reading signature", paddingSkipped);
}

var offset = 0;
// Read initial header quad directly after the last entry
var header = (uint)inputBuffer.ReadLeInt();
do
{
switch (header)
{
case ZipConstants.CentralHeaderSignature:
case ZipConstants.EndOfCentralDirectorySignature:
case ZipConstants.CentralHeaderDigitalSignature:
case ZipConstants.ArchiveExtraDataSignature:
case ZipConstants.Zip64CentralFileHeaderSignature:
Debug.WriteLine("Non-entry signature found at offset {0,2}: 0x{1:x8}", offset, header);
// No more individual entries exist
return false;

case ZipConstants.LocalHeaderSignature:
Debug.WriteLine("Entry local header signature found at offset {0,2}: 0x{1:x8}", offset, header);
return true;
default:
// Current header quad did not match any signature, shift in another byte
header = (uint) (inputBuffer.ReadLeByte() << 24) | (header >> 8);
offset++;
break;
}
} while (true); // Loop until we either get an EOF exception or we find the next signature
}

/// <summary>
/// Read data descriptor at the end of compressed data.
/// </summary>
Expand Down Expand Up @@ -397,6 +427,7 @@ public void CloseEntry()

if ((inputBuffer.Available > csize) && (csize >= 0))
{
// Buffer can contain entire entry data. Internally offsetting position inside buffer
inputBuffer.Available = (int)((long)inputBuffer.Available - csize);
}
else
Expand Down
28 changes: 27 additions & 1 deletion test/ICSharpCode.SharpZipLib.Tests/TestSupport/Utils.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using NUnit.Framework;
using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using ICSharpCode.SharpZipLib.Tests.Zip;

namespace ICSharpCode.SharpZipLib.Tests.TestSupport
{
Expand All @@ -12,7 +14,7 @@ public static class Utils
{
public static int DummyContentLength = 16;

private static Random random = new Random();
private static Random random = new Random(5);

/// <summary>
/// Returns the system root for the current platform (usually c:\ for windows and / for others)
Expand Down Expand Up @@ -160,5 +162,29 @@ internal string CreateDummyFile(string name, int size = -1)

#endregion IDisposable Support
}

public static void PatchFirstEntrySize(Stream stream, int newSize)
{
using(stream)
{
var sizeBytes = BitConverter.GetBytes(newSize);

stream.Seek(18, SeekOrigin.Begin);
stream.Write(sizeBytes, 0, 4);
stream.Write(sizeBytes, 0, 4);
}
}
}

public class TestTraceListener : TraceListener
{
private readonly TextWriter _writer;
public TestTraceListener(TextWriter writer)
{
_writer = writer;
}

public override void WriteLine(string message) => _writer.WriteLine(message);
public override void Write(string message) => _writer.Write(message);
}
}
46 changes: 46 additions & 0 deletions test/ICSharpCode.SharpZipLib.Tests/Zip/StreamHandling.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
using ICSharpCode.SharpZipLib.Zip;
using NUnit.Framework;
using System;
using System.Diagnostics;
using System.IO;
using System.Text;

namespace ICSharpCode.SharpZipLib.Tests.Zip
{
Expand All @@ -13,6 +15,12 @@ namespace ICSharpCode.SharpZipLib.Tests.Zip
[TestFixture]
public class StreamHandling : ZipBase
{
private TestTraceListener Listener;
[SetUp]
public void Init() => Trace.Listeners.Add(Listener = new TestTraceListener(TestContext.Out));
[TearDown]
public void Deinit() => Trace.Listeners.Remove(Listener);

private void MustFailRead(Stream s, byte[] buffer, int offset, int count)
{
bool exception = false;
Expand Down Expand Up @@ -556,5 +564,43 @@ public void ShouldThrowDescriptiveExceptionOnUncompressedDescriptorEntry()
}
}
}

[Test]
[Category("Zip")]
public void IteratingOverEntriesInDirectUpdatedArchive()
{
using (var tempFile = new Utils.TempFile())
{
var fi = new FileInfo(tempFile.Filename);

using (var zf = ZipFile.Create(tempFile.Filename))
{
zf.BeginUpdate();
// Add a "large" file
zf.Add(new MemoryDataSource(new byte[1024]), "FirstFile", CompressionMethod.Stored);
// Add a second file after the first one
zf.Add(new StringMemoryDataSource("fileContents"), "SecondFile", CompressionMethod.Stored);
zf.CommitUpdate();
}

// Since ZipFile doesn't support UpdateCommand.Modify yet we'll have to simulate it by patching the header
Utils.PatchFirstEntrySize(fi.Open(FileMode.Open), 1);

// Iterate updated entries
using (var fs = File.OpenRead(tempFile.Filename))
using (var zis = new ZipInputStream(fs))
{
var firstEntry = zis.GetNextEntry();
Assert.NotNull(firstEntry);
Assert.AreEqual(1, firstEntry.CompressedSize);
Assert.AreEqual(1, firstEntry.Size);

var secondEntry = zis.GetNextEntry();
Assert.NotNull(secondEntry, "Zip entry following padding not found");
var contents = new StreamReader(zis, Encoding.UTF8, false, 128, true).ReadToEnd();
Assert.AreEqual("fileContents", contents);
}
}
}
}
}