[
  {
    "Id": "512340",
    "ThreadId": "232268",
    "Html": "\r\n<p>I am using DotNetZip to create backups of directories and their contents with VB .Net.&nbsp; For security reasons, I chose to use a zip file obscured inside a second zip file, so that contents aren't as visible, with the encapsulating zip file being uncompressed\r\n &amp; AES-256 encrypted.&nbsp; It contains both the lightly-compressed &quot;Inner&quot; zip file of the backup itself and a small text &quot;label&quot; file with version info and a few other bits of information.</p>\r\n<p>Occasionally, the resulting zip gets corrupted during the zip operation when it is being encapsulated in the Outer Zip file; I know it's here because I have the program check the file contents list after each zip operation to ensure that there isn't a failure\r\n to generate a proper zip file, and it doesn't throw any errors in between the two operations, run sequentially.&nbsp; It's a bit of a rough way to check but it has worked until now. I haven't been able to replicate the error in testing, and this has only happened\r\n a few times out of a few hundred runs, but is troubling due to my need for consistent backups.&nbsp;</p>\r\n<p>I have to create the Inner zip file as a second file rather than an output stream because of memory limitations on the systems that we are running it on (a few of our different production computers running XP / Vista / 7, so far no commonality).&nbsp; I\r\n am adding a Threading.Thread.Sleep(2000) to be 100% sure the transition from &quot;DotNetZip-XXXXX.tmp&quot; to resulting &quot;Inner&quot; zip file isn't being caught in mid-stream and that all disk access has completed before the encapsulation begins.</p>\r\n<p>I was hoping someone could help here:</p>\r\n<p>Is there a way I can get the contents of the Inner Zip file after the encapsulation, while it is inside the Outer? Is there a way to do this without extracting the files, even temporarily?&nbsp; If so, I could check the contents list to verify it's not corrupt.&nbsp;\r\n Is there a way I can check the CRC values of the inner files a different way? Am I not seeing something else?</p>\r\n<p>If anyone could provide any other insights, I'd really appreciate it.&nbsp; Thanks!</p>\r\n<p>Here is the core of the compressing sub, with a few bits chopped out above and below... not very pretty, but it usually works ;) .&nbsp; It handles the &quot;Outer&quot; zip / &quot;Inner&quot; zip switch with &quot;blnCompressing&quot; - if true, create an &quot;Inner&quot; zip / if false, create\r\n an &quot;Outer&quot;:</p>\r\n<p>&nbsp;</p>\r\n<div style=\"color:black; background-color:white\">\r\n<pre><span style=\"color:blue\">            Using</span> zip1 <span style=\"color:blue\">As</span> ZipFile = <span style=\"color:blue\">New</span> ZipFile\r\n                zip1.UseZip64WhenSaving = Zip64Option.AsNecessary\r\n                zip1.Name = strZipToCreatePub\r\n                    <span style=\"color:blue\">If</span> blnCompressingZip <span style=\"color:blue\">Then</span>\r\n                        zip1.CompressionLevel = Ionic.Zlib.CompressionLevel.BestSpeed\r\n                        zip1.AddDirectory(strToBeZippedPub, <span style=\"color:#a31515\">&quot;&quot;</span>)\r\n                        zip1.Comment = strComment &amp; <span style=\"color:#a31515\">&quot;, &quot;</span> &amp; Now.ToString(<span style=\"color:#a31515\">&quot;hh:mm tt&quot;</span>) &amp; <span style=\"color:#a31515\">&quot;, &quot;</span> &amp; Now.ToString(<span style=\"color:#a31515\">&quot;MM-dd-yy&quot;</span>)\r\n                    <span style=\"color:blue\">Else</span>\r\n                        zip1.Encryption = EncryptionAlgorithm.WinZipAes256\r\n                        zip1.Password = strPasswordPub\r\n                        zip1.CompressionLevel = Ionic.Zlib.CompressionLevel.None\r\n                        zip1.AddFile(strLabelFilePub, <span style=\"color:#a31515\">&quot;&quot;</span>)\r\n                        zip1.AddFile(strToBeZippedPub, <span style=\"color:#a31515\">&quot;&quot;</span>)\r\n                    <span style=\"color:blue\">End</span> <span style=\"color:blue\">If</span>\r\n                <span style=\"color:blue\">Me</span>._entriesToZip = intAllFilesAndDirs       <span style=\"color:green\">'this is the total # of files that are being zipped to display in the progress bar</span>\r\n                <span style=\"color:blue\">Me</span>.SetProgressBars()\r\n                <span style=\"color:blue\">AddHandler</span> zip1.SaveProgress, <span style=\"color:blue\">New</span> EventHandler(Of SaveProgressEventArgs)(<span style=\"color:blue\">AddressOf</span> <span style=\"color:blue\">Me</span>.zip1_SaveProgress)\r\n                zip1.Save(zip1.Name)\r\n            <span style=\"color:blue\">End</span> <span style=\"color:blue\">Using</span>\r\n</pre>\r\n</div>\r\n<p>&nbsp;</p>\r\n<p>&nbsp;</p>\r\n",
    "PostedDate": "2010-10-25T12:29:44.607-07:00",
    "UserRole": null,
    "MarkedAsAnswerDate": null
  },
  {
    "Id": "513073",
    "ThreadId": "232268",
    "Html": "\r\n<p>I understand what you're doing, and it makes sense to me.</p>\r\n<p>What you want to do, I think, is validate the outer zip after it's been written.&nbsp; I think you said you want to validate the contents of the inner zip, but without extracting those files to the filesystem.</p>\r\n<p>To do this, open the outer zip for reading with ZipFile.Read().&nbsp; There's just one ZipEntry in it, if I understand the situation correctly.&nbsp; Call\r\n<a href=\"http://cheeso.members.winisp.net/DotNetZipHelp/html/4ef6405c-33ff-a8aa-1731-def3ec5cac24.htm\">\r\nZipEntry.OpenReader()</a> on that entry, which gives you a readable stream.&nbsp; You can then\r\n<a href=\"http://cheeso.members.winisp.net/DotNetZipHelp/html/479339f4-2d5f-40bd-7199-1d894468dc7e.htm\">\r\ncreate a ZipInputStream around that read-only stream</a>.&nbsp;</p>\r\n<p style=\"padding-left:30px\">You also <em>may </em>be able to wrap the result of ZipEntry.OpenReader() in a BufferedStream, and call ZipFile.Read() on THAT.</p>\r\n<p style=\"padding-left:30px\">You cannot succeed by calling&nbsp;ZipFile.Read() using the result of ZipEntry.OpenReader(), in order to read and validate the inner zip.&nbsp; This is because when the ZipFile class is used to read a zip file, it seeks around in\r\n the file.&nbsp; It seeks to the end, reads some things, then seeks backwards, and so on.&nbsp; The result of ZipEntry.OpenReader(), though, is a read-forward-only stream.&nbsp; It doesn't support seeks.&nbsp; Using a BufferedStream basically just caches all\r\n the inner zip data, and so once again Seek() will work. But be careful, this only works if the buffer size is as large as the entire inner zip file.&nbsp; Maybe impractical.</p>\r\n<p style=\"padding-left:30px\">On the other hand, the ZipInputStream does support reading from a read-forward-only stream, so it can be used to validate the result of ZipEntry.OpenReader().&nbsp;&nbsp;</p>\r\n<p>The interface you want to use on ZipInputStream is <a href=\"http://cheeso.members.winisp.net/DotNetZipHelp/html/bbb33899-b86a-9691-e5c3-fb65ecc47ce5.htm\">\r\nGetNextEntry()</a>;&nbsp; call that and then call ZipInputStream.Read() to read the de-compressed content of the entry in the inner zip.&nbsp; Do GetNextEntry &#43; Read it in a loop to read all entries.&nbsp;</p>\r\n<p>If you call ZipInputStream.Read() repeatedly until it returns 0 (implying no more data is available for the ZipEntry), the Read()&nbsp;method will automatically and transparently validate the CRC for the entry, and throw an exception if the CRC does not\r\n match the expected value.&nbsp; If you do not call Read() until it returns 0, then the ZipInputStream.Read()&nbsp;method will not validate the CRC of the entry.</p>\r\n<p>Therefore to validate the CRC of the entry, just call Read() into a small buffer (say 2k), repeatedly, until it returns 0.&nbsp;</p>\r\n<p>Does this answer the question?</p>\r\n<p>ps: I am interested in tracking down any problem with corruption, but to do it, I guess you realize I need a reproducible test case.</p>\r\n<p>last thing: if you want to add an entry to a zip, and the entry is a small string, you can call AddEntry() and pass a System.String.&nbsp; For your &quot;label&quot; entry, you may find that option interesting.</p>\r\n<p>&nbsp;</p>\r\n",
    "PostedDate": "2010-10-26T17:45:43.817-07:00",
    "UserRole": null,
    "MarkedAsAnswerDate": null
  },
  {
    "Id": "525903",
    "ThreadId": "232268",
    "Html": "Well, I got better info from my end users and caught a corruption in the act. A backup utility that was set to backup everything put into the destination zip directory would sometimes step in and try to backup parts of the zip file as it was copying in.\r\n Without the password , it could backup the zip header and file list of the zip, but nothing else... presto! Corrupted backup, good source, good destination.<br>\r\n<br>\r\nI ended up putting in a diff check from the source to both the destination and backup files and haven't seen it happen again.<br>\r\n<br>\r\nThanks for all the help; I will certainly look into some of the other capabilities!<br>\r\n<br>\r\n<div>On Tue, Oct 26, 2010 at 8:45 PM, Cheeso <span dir=\"ltr\">&lt;<a href=\"mailto:notifications@codeplex.com\">notifications@codeplex.com</a>&gt;</span> wrote:<br>\r\n<blockquote style=\"margin:0pt 0pt 0pt 0.8ex; border-left:1px solid rgb(204,204,204); padding-left:1ex\">\r\n<div>\r\n<p>From: Cheeso</p>\r\n<div>\r\n<p>I understand what you're doing, and it makes sense to me.</p>\r\n<p>What you want to do, I think, is validate the outer zip after it's been written. I think you said you want to validate the contents of the inner zip, but without extracting those files to the filesystem.</p>\r\n<p>To do this, open the outer zip for reading with ZipFile.Read(). There's just one ZipEntry in it, if I understand the situation correctly. Call\r\n<a href=\"http://cheeso.members.winisp.net/DotNetZipHelp/html/4ef6405c-33ff-a8aa-1731-def3ec5cac24.htm\" target=\"_blank\">\r\nZipEntry.OpenReader()</a> on that entry, which gives you a readable stream. You can then\r\n<a href=\"http://cheeso.members.winisp.net/DotNetZipHelp/html/479339f4-2d5f-40bd-7199-1d894468dc7e.htm\" target=\"_blank\">\r\ncreate a ZipInputStream around that read-only stream</a>. </p>\r\n<p style=\"padding-left:30px\">You also <i>may </i>be able to wrap the result of ZipEntry.OpenReader() in a BufferedStream, and call ZipFile.Read() on THAT.</p>\r\n<p style=\"padding-left:30px\">You cannot succeed by calling ZipFile.Read() using the result of ZipEntry.OpenReader(), in order to read and validate the inner zip. This is because when the ZipFile class is used to read a zip file, it seeks around in the file.\r\n It seeks to the end, reads some things, then seeks backwards, and so on. The result of ZipEntry.OpenReader(), though, is a read-forward-only stream. It doesn't support seeks. Using a BufferedStream basically just caches all the inner zip data, and so once\r\n again Seek() will work. But be careful, this only works if the buffer size is as large as the entire inner zip file. Maybe impractical.</p>\r\n<p style=\"padding-left:30px\">On the other hand, the ZipInputStream does support reading from a read-forward-only stream, so it can be used to validate the result of ZipEntry.OpenReader().\r\n</p>\r\n<p>The interface you want to use on ZipInputStream is <a href=\"http://cheeso.members.winisp.net/DotNetZipHelp/html/bbb33899-b86a-9691-e5c3-fb65ecc47ce5.htm\" target=\"_blank\">\r\nGetNextEntry()</a>; call that and then call ZipInputStream.Read() to read the de-compressed content of the entry in the inner zip. Do GetNextEntry &#43; Read it in a loop to read all entries.\r\n</p>\r\n<p>If you call ZipInputStream.Read() repeatedly until it returns 0 (implying no more data is available for the ZipEntry), the Read() method will automatically and transparently validate the CRC for the entry, and throw an exception if the CRC does not match\r\n the expected value. If you do not call Read() until it returns 0, then the ZipInputStream.Read() method will not validate the CRC of the entry.</p>\r\n<p>Therefore to validate the CRC of the entry, just call Read() into a small buffer (say 2k), repeatedly, until it returns 0.\r\n</p>\r\n<p>Does this answer the question?</p>\r\n<p>ps: I am interested in tracking down any problem with corruption, but to do it, I guess you realize I need a reproducible test case.</p>\r\n<p>last thing: if you want to add an entry to a zip, and the entry is a small string, you can call AddEntry() and pass a System.String. For your &quot;label&quot; entry, you may find that option interesting.</p>\r\n<p></p>\r\n</div>\r\n<div>\r\n<p>Read the <a href=\"http://dotnetzip.codeplex.com/Thread/View.aspx?ThreadId=232268&ANCHOR#Post513073\" target=\"_blank\">\r\nfull discussion online</a>.</p>\r\n<p>To add a post to this discussion, reply to this email (<a href=\"mailto:DotNetZip@discussions.codeplex.com?subject=[DotNetZip:232268]\" target=\"_blank\">DotNetZip@discussions.codeplex.com</a>)</p>\r\n<p>To start a new discussion for this project, email <a href=\"mailto:DotNetZip@discussions.codeplex.com\" target=\"_blank\">\r\nDotNetZip@discussions.codeplex.com</a></p>\r\n<p>You are receiving this email because you subscribed to this discussion on CodePlex. You can\r\n<a href=\"http://www.codeplex.com/site/discussions/thread/unsubscribe/232268\" target=\"_blank\">\r\nunsubscribe</a> on CodePlex.com.</p>\r\n<p>Please note: Images and attachments will be removed from emails. Any posts to this discussion will also be available online at CodePlex.com</p>\r\n</div>\r\n</div>\r\n</blockquote>\r\n</div>\r\n<br>\r\n",
    "PostedDate": "2010-11-23T08:22:20.683-08:00",
    "UserRole": null,
    "MarkedAsAnswerDate": null
  },
  {
    "Id": "525923",
    "ThreadId": "232268",
    "Html": "\r\n<p>Wow, I'm glad you figured this out.&nbsp; I'll think about the possibility of how the library could be modified, to avoid this pitfall.&nbsp;</p>\r\n<p>&nbsp;</p>\r\n",
    "PostedDate": "2010-11-23T08:40:55.44-08:00",
    "UserRole": null,
    "MarkedAsAnswerDate": null
  },
  {
    "Id": "526084",
    "ThreadId": "232268",
    "Html": "Oh, sorry. It wasn't a problem with DotNetZip after all! I'd only written to see if I could detect the bad CRC's somehow on my end... which you explained very well, and thanks again for that.<br>\r\n<br>\r\nThe issue stemmed from the third party backup (I can PM you the name). I'd have my backups create the zip file in a local backup directory (which always worked correctly), then copy the zip into a &quot;Backup Transfer&quot; folder, that the backup utility was watching.\r\n It would see the zip file hit the &quot;Backup Transfer&quot; folder and would (rarely) start the transfer with just the header info and the contained files. It seemed to treat the zip as a directory; since that is all that the internal Windows &quot;Compressed Folders&quot;\r\n could see without password support. I'd end up with a jumbled mess about 1% of the time, and have to put the local backup back into the &quot;Backup Transfer&quot; folder.<br>\r\n<br>\r\nSo, this actually wasn't a problem with DotNetZip at all... and you answered the questions I did have, which I appreciate. The only issue I have ever had with DotNetZip is that I am still puzzling over how to correctly restore the Date Modified stamps (LastWriteTime),\r\n when I create a backup that crosses a time zone. When I unzip it with DotNetZip, the time is off by the difference between the two time zones... but not when I use WinRAR. Should I make a new thread for questions about this for your tracking purposes? I haven't\r\n seen this specific thing being asked.<br>\r\n",
    "PostedDate": "2010-11-23T13:22:00.877-08:00",
    "UserRole": null,
    "MarkedAsAnswerDate": null
  },
  {
    "Id": "526535",
    "ThreadId": "232268",
    "Html": "\r\n<p>ah, ok, I understand.&nbsp;</p>\r\n<p>I think DotNetZip could suffer a similar problem if you zip into a directory that you are currently zipping.&nbsp; It would be nice to be able to avoid that pitfall but it may be that this is not important enough to enough people, for me to spend time on\r\n solving it.</p>\r\n<p>Regarding the timestamp thing - yes, that is better covered in a new thread if you please. &nbsp;It's not really for tracking - it's for search.&nbsp; I want people to be able to search on relevant topics, and keeping threads narrowly constrained helps out\r\n there.</p>\r\n<p>thanks for posting the resolution to your problem!</p>\r\n<p>&nbsp;</p>\r\n",
    "PostedDate": "2010-11-24T09:34:43.783-08:00",
    "UserRole": null,
    "MarkedAsAnswerDate": null
  }
]