2

Closed

Saving to a Stream using Encryption corrupts the output file

description

It seems that DotNetZip creates corrupt ZIP files, when using encryption (any algorithm) and saving to a stream. I was able to create a small test case and could reproduce the problem with the 1.9.1.5 and 1.9.5.2 versions. Apparently, the problem occurs (only?) when trying to add 0-byte-files to the ZIP file. When using the filename-variant of the Save method the resulting file seems to be okay.
 
-------------------------------- Test case --------------------------------
using (ZipFile zipFile = new ZipFile())
{
zipFile.Encryption = EncryptionAlgorithm.WinZipAes256;
zipFile.Password = "test";
 
zipFile.AddEntry("dummy", new byte[0]);
 
using (Stream stream = File.Create("test.zip"))
{
    zipFile.Save(stream);
}
 
// This seems to work.
//zipFile.Save("test.zip");
}
 
try
{
using (ZipFile zipFile = new ZipFile("test.zip")) { }
}
catch (ZipException ex)
{
Console.WriteLine(ex.ToString());
Console.ReadKey();
}
-------------------------------- Test case --------------------------------
 
-------------------------------- Output --------------------------------
Ionic.Zip.ZipException: test.zip is not a valid zip file ---> Ionic.Zip.BadReadException: ZipEntry::ReadDirEntry(): Bad signature (0x00000000) at position 0x00000064
at Ionic.Zip.ZipEntry.ReadDirEntry(ZipFile zf)
at Ionic.Zip.ZipFile.ReadCentralDirectory(ZipFile zf)
at Ionic.Zip.ZipFile.ReadIntoInstance(ZipFile zf)
at Ionic.Zip.ZipFile._InitInstance(String zipFileName, TextWriter statusMessageWriter)
at Ionic.Zip.ZipFile..ctor(String fileName)
--- End of inner exception stack trace ---
at Ionic.Zip.ZipFile..ctor(String fileName)
at ZipTest.Program.Main(String[] args) in C:\Daten\Privat\Projekte\Neu\ZipTest\ZipTest\Program.cs:line 31
-------------------------------- Output --------------------------------
Closed Jun 21, 2011 at 8:21 PM by Cheeso
fixed in changeset 79399. the first binary version that will have this fix is v1.9.1.6

comments

Gnafoo wrote Jun 22, 2010 at 10:17 PM

I think, that the problem is caused by truncating the CountingStream, that wraps around the provided stream. PostProcessOutput(Stream) in ZipEntry.Write.cs will call SetLength when using encryption. WriteCentralDirectoryStructure(...) in ZipFile.Save.cs will then access the ComputedPosition property which will point beyond the actual stream length. The above test case will work, when modifying the CountingStream, to update the count on SetLength:

public override void SetLength(long value)
{
_initialOffset = Math.Min(value, _initialOffset);
_bytesWritten = value - _initialOffset;

_s.SetLength(value);
}

I'm not sure though, if this breaks other code, since _bytesWritten will not longer reflect the total number of bytes written, after truncation occurs.

Cheeso wrote Jun 18, 2011 at 2:55 PM

Thanks for the bug report. I've reproduced this problem here. Looking into a fix now.

Cheeso wrote Jun 18, 2011 at 4:20 PM

CountingStream is sort of a "leaky abstraction", because of the need for Seek and SetLength and so on. It exposes a Adjust() method that is designed for this - the code in PostProcessOutput() should have called it right near calling s.SetLength().