FIX for creating corrupt zip files after using ReadCentralDir()

May 24, 2012 at 7:50 AM

Hi There,

 Calling ReadCentralDir(),  which calls GetFileOffset(uint headerOffset), messes with the ZipFileStream position, which leads to corrupt .zips being written out. Failing unit test and fix are below:

Many Thanks for the cool library!

 
     // Calculate the file offset by reading the corresponding local header
        private uint GetFileOffset(uint _headerOffset)
        {
            byte[] buffer = new byte[2];

   // added for fix
            long savedPosition = ZipFileStream.Position;

            this.ZipFileStream.Seek(_headerOffset + 26, SeekOrigin.Begin);
            this.ZipFileStream.Read(buffer, 0, 2);
            ushort filenameSize = BitConverter.ToUInt16(buffer, 0);
            this.ZipFileStream.Read(buffer, 0, 2);
            ushort extraSize = BitConverter.ToUInt16(buffer, 0);

   // added for fix
            ZipFileStream.Position = savedPosition;

            return (uint)(30 + filenameSize + extraSize + _headerOffset);
        }

 

  /// <summary>
        ///Trying to track down an issue where bad .zip file made?
        ///</summary>
        [TestMethod()]
        [DeploymentItem("Archiver.exe")]
        [DeploymentItem("TestData\\Reports\\ZipTest1.txt", "TestData\\Reports")]
        [DeploymentItem("TestData\\Reports\\ZipTest2.txt", "TestData\\Reports")]
        [DeploymentItem("TestData\\Reports\\ZipTest3.txt", "TestData\\Reports")]
        public void AddThreeFilesInrementallyAndReadCentralDirToZipTest()
        {
            ZipStorer.Compression _method1 = ZipStorer.Compression.Deflate;
            string _pathname1 = "TestData\\Reports\\ZipTest1.txt";
            string _filenameInZip1 = "Reports\\ZipTest1.txt";
            string _comment1 = string.Empty;
            ZipStorer.Compression _method2 = ZipStorer.Compression.Deflate;
            string _pathname2 = "TestData\\Reports\\ZipTest2.txt";
            string _filenameInZip2 = "Reports\\ZipTest2.txt";
            string _comment2 = string.Empty;
            ZipStorer.Compression _method3 = ZipStorer.Compression.Deflate;
            string _pathname3 = "TestData\\Reports\\ZipTest3.txt";
            string _filenameInZip3 = "Reports\\ZipTest3.txt";
            string _comment3 = string.Empty;

            using (ZipStorer target = ZipStorer.Create("testZipHas3Files.zip", String.Empty))
            {
                target.AddFile(_method1, _pathname1, _filenameInZip1, _comment1);
            }
            using (ZipStorer target = ZipStorer.Open("testZipHas3Files.zip", FileAccess.Write))
            {
                // This is the method call that kills the .zip being written out.
               
                List<ZipStorer.ZipFileEntry> zipOriginalContents = target.ReadCentralDir();
                target.AddFile(_method2, _pathname2, _filenameInZip2, _comment2);
            }
            using (ZipStorer target = ZipStorer.Open("testZipHas3Files.zip", FileAccess.Write))
            {
                target.AddFile(_method3, _pathname3, _filenameInZip3, _comment3);
            }

            using (ZipStorer target = ZipStorer.Open("testZipHas3Files.zip", FileAccess.Read))
            {
                List<ZipStorer.ZipFileEntry> files = target.ReadCentralDir();
                Assert.IsTrue(files.Count == 3, String.Format("files in zip = {0}, expected 3", files.Count));

                // The crc32 values came from using windows explorer and looking in zip file.

                Assert.AreEqual(9305U, files[0].FileSize);
                Assert.AreEqual(3782U, files[1].FileSize);
                Assert.AreEqual(21391U, files[2].FileSize);
            }

        }