protected override void Dispose(bool disposing) { if (this._disposed) { return; } if (disposing && !this._exceptionPending) { this._FinishCurrentEntry(); this._directoryNeededZip64 = ZipOutput.WriteCentralDirectoryStructure(this._outputStream, this._entriesWritten.Values, 1u, this._zip64, this.Comment, new ZipContainer(this)); CountingStream countingStream = this._outputStream as CountingStream; Stream stream; if (countingStream != null) { stream = countingStream.WrappedStream; countingStream.Dispose(); } else { stream = this._outputStream; } if (!this._leaveUnderlyingStreamOpen) { stream.Dispose(); } this._outputStream = null; } this._disposed = true; }
public static bool WriteCentralDirectoryStructure(Stream s, ICollection <ZipEntry> entries, uint numSegments, Zip64Option zip64, string comment, ZipContainer container) { ZipSegmentedStream zipSegmentedStream = s as ZipSegmentedStream; if (zipSegmentedStream != null) { zipSegmentedStream.ContiguousWrite = true; } long num = 0L; using (MemoryStream memoryStream = new MemoryStream()) { foreach (ZipEntry current in entries) { if (current.IncludedInMostRecentSave) { current.WriteCentralDirectoryEntry(memoryStream); } } byte[] array = memoryStream.ToArray(); s.Write(array, 0, array.Length); num = (long)array.Length; } CountingStream countingStream = s as CountingStream; long num2 = (countingStream == null) ? s.Position : countingStream.ComputedPosition; long num3 = num2 - num; uint num4 = (zipSegmentedStream == null) ? 0u : zipSegmentedStream.CurrentSegment; long num5 = num2 - num3; int num6 = ZipOutput.CountEntries(entries); bool flag = zip64 == Zip64Option.Always || num6 >= 65535 || num5 > (long)((ulong)-1) || num3 > (long)((ulong)-1); byte[] array3; if (flag) { if (zip64 == Zip64Option.Default) { StackFrame stackFrame = new StackFrame(1); if (stackFrame.GetMethod().DeclaringType == typeof(ZipFile)) { throw new ZipException("The archive requires a ZIP64 Central Directory. Consider setting the ZipFile.UseZip64WhenSaving property."); } throw new ZipException("The archive requires a ZIP64 Central Directory. Consider setting the ZipOutputStream.EnableZip64 property."); } else { byte[] array2 = ZipOutput.GenZip64EndOfCentralDirectory(num3, num2, num6, numSegments); array3 = ZipOutput.GenCentralDirectoryFooter(num3, num2, zip64, num6, comment, container); if (num4 != 0u) { uint value = zipSegmentedStream.ComputeSegment(array2.Length + array3.Length); int num7 = 16; Array.Copy(BitConverter.GetBytes(value), 0, array2, num7, 4); num7 += 4; Array.Copy(BitConverter.GetBytes(value), 0, array2, num7, 4); num7 = 60; Array.Copy(BitConverter.GetBytes(value), 0, array2, num7, 4); num7 += 4; num7 += 8; Array.Copy(BitConverter.GetBytes(value), 0, array2, num7, 4); } s.Write(array2, 0, array2.Length); } } else { array3 = ZipOutput.GenCentralDirectoryFooter(num3, num2, zip64, num6, comment, container); } if (num4 != 0u) { ushort value2 = (ushort)zipSegmentedStream.ComputeSegment(array3.Length); int num8 = 4; Array.Copy(BitConverter.GetBytes(value2), 0, array3, num8, 2); num8 += 2; Array.Copy(BitConverter.GetBytes(value2), 0, array3, num8, 2); num8 += 2; } s.Write(array3, 0, array3.Length); if (zipSegmentedStream != null) { zipSegmentedStream.ContiguousWrite = false; } return(flag); }
private static byte[] GenCentralDirectoryFooter(long StartOfCentralDirectory, long EndOfCentralDirectory, Zip64Option zip64, int entryCount, string comment, ZipContainer container) { Encoding encoding = ZipOutput.GetEncoding(container, comment); int num = 22; byte[] array = null; short num2 = 0; if (comment != null && comment.Length != 0) { array = encoding.GetBytes(comment); num2 = (short)array.Length; } num += (int)num2; byte[] array2 = new byte[num]; int num3 = 0; byte[] bytes = BitConverter.GetBytes(101010256u); Array.Copy(bytes, 0, array2, num3, 4); num3 += 4; array2[num3++] = 0; array2[num3++] = 0; array2[num3++] = 0; array2[num3++] = 0; if (entryCount >= 65535 || zip64 == Zip64Option.Always) { for (int i = 0; i < 4; i++) { array2[num3++] = 255; } } else { array2[num3++] = (byte)(entryCount & 255); array2[num3++] = (byte)((entryCount & 65280) >> 8); array2[num3++] = (byte)(entryCount & 255); array2[num3++] = (byte)((entryCount & 65280) >> 8); } long num4 = EndOfCentralDirectory - StartOfCentralDirectory; if (num4 >= (long)((ulong)-1) || StartOfCentralDirectory >= (long)((ulong)-1)) { for (int i = 0; i < 8; i++) { array2[num3++] = 255; } } else { array2[num3++] = (byte)(num4 & 255L); array2[num3++] = (byte)((num4 & 65280L) >> 8); array2[num3++] = (byte)((num4 & 16711680L) >> 16); array2[num3++] = (byte)((num4 & (long)((ulong)-16777216)) >> 24); array2[num3++] = (byte)(StartOfCentralDirectory & 255L); array2[num3++] = (byte)((StartOfCentralDirectory & 65280L) >> 8); array2[num3++] = (byte)((StartOfCentralDirectory & 16711680L) >> 16); array2[num3++] = (byte)((StartOfCentralDirectory & (long)((ulong)-16777216)) >> 24); } if (comment == null || comment.Length == 0) { array2[num3++] = 0; array2[num3++] = 0; } else { if ((int)num2 + num3 + 2 > array2.Length) { num2 = (short)(array2.Length - num3 - 2); } array2[num3++] = (byte)(num2 & 255); array2[num3++] = (byte)(((int)num2 & 65280) >> 8); if (num2 != 0) { int i = 0; while (i < (int)num2 && num3 + i < array2.Length) { array2[num3 + i] = array[i]; i++; } num3 += i; } } return(array2); }
/// <summary> /// Saves the Zip archive to a file, specified by the Name property of the /// <c>ZipFile</c>. /// </summary> /// /// <remarks> /// <para> /// The <c>ZipFile</c> instance is written to storage, typically a zip file /// in a filesystem, only when the caller calls <c>Save</c>. In the typical /// case, the Save operation writes the zip content to a temporary file, and /// then renames the temporary file to the desired name. If necessary, this /// method will delete a pre-existing file before the rename. /// </para> /// /// <para> /// The <see cref="ZipFile.Name"/> property is specified either explicitly, /// or implicitly using one of the parameterized ZipFile constructors. For /// COM Automation clients, the <c>Name</c> property must be set explicitly, /// because COM Automation clients cannot call parameterized constructors. /// </para> /// /// <para> /// When using a filesystem file for the Zip output, it is possible to call /// <c>Save</c> multiple times on the <c>ZipFile</c> instance. With each /// call the zip content is re-written to the same output file. /// </para> /// /// <para> /// Data for entries that have been added to the <c>ZipFile</c> instance is /// written to the output when the <c>Save</c> method is called. This means /// that the input streams for those entries must be available at the time /// the application calls <c>Save</c>. If, for example, the application /// adds entries with <c>AddEntry</c> using a dynamically-allocated /// <c>MemoryStream</c>, the memory stream must not have been disposed /// before the call to <c>Save</c>. See the <see /// cref="ZipEntry.InputStream"/> property for more discussion of the /// availability requirements of the input stream for an entry, and an /// approach for providing just-in-time stream lifecycle management. /// </para> /// /// </remarks> /// /// <seealso cref="Ionic.Zip.ZipFile.AddEntry(String, System.IO.Stream)"/> /// /// <exception cref="Ionic.Zip.BadStateException"> /// Thrown if you haven't specified a location or stream for saving the zip, /// either in the constructor or by setting the Name property, or if you try /// to save a regular zip archive to a filename with a .exe extension. /// </exception> /// /// <exception cref="System.OverflowException"> /// Thrown if <see cref="MaxOutputSegmentSize"/> is non-zero, and the number /// of segments that would be generated for the spanned zip file during the /// save operation exceeds 99. If this happens, you need to increase the /// segment size. /// </exception> /// public void Save() { try { bool thisSaveUsedZip64 = false; _saveOperationCanceled = false; _numberOfSegmentsForMostRecentSave = 0; OnSaveStarted(); if (WriteStream == null) { throw new BadStateException("You haven't specified where to save the zip."); } if (_name != null && _name.EndsWith(".exe") && !_SavingSfx) { throw new BadStateException("You specified an EXE for a plain zip file."); } // check if modified, before saving. if (!_contentsChanged) { OnSaveCompleted(); if (Verbose) { StatusMessageTextWriter.WriteLine("No save is necessary...."); } return; } Reset(true); if (Verbose) { StatusMessageTextWriter.WriteLine("saving...."); } // validate the number of entries if (_entries.Count >= 0xFFFF && _zip64 == Zip64Option.Never) { throw new ZipException("The number of entries is 65535 or greater. Consider setting the UseZip64WhenSaving property on the ZipFile instance."); } // write an entry in the zip for each file int n = 0; // workitem 9831 ICollection <ZipEntry> c = (SortEntriesBeforeSaving) ? EntriesSorted : Entries; foreach (ZipEntry e in c) // _entries.Values { OnSaveEntry(n, e, true); e.Write(WriteStream); if (_saveOperationCanceled) { break; } n++; OnSaveEntry(n, e, false); if (_saveOperationCanceled) { break; } // Some entries can be skipped during the save. if (e.IncludedInMostRecentSave) { thisSaveUsedZip64 |= e.OutputUsedZip64.Value; } } if (_saveOperationCanceled) { return; } var zss = WriteStream as ZipSegmentedStream; _numberOfSegmentsForMostRecentSave = (zss != null) ? zss.CurrentSegment : 1; bool directoryNeededZip64 = ZipOutput.WriteCentralDirectoryStructure (WriteStream, c, _numberOfSegmentsForMostRecentSave, _zip64, Comment, new ZipContainer(this)); OnSaveEvent(ZipProgressEventType.Saving_AfterSaveTempArchive); _hasBeenSaved = true; _contentsChanged = false; thisSaveUsedZip64 |= directoryNeededZip64; _OutputUsesZip64 = new Nullable <bool>(thisSaveUsedZip64); // do the rename as necessary if (_name != null && (_temporaryFileName != null || zss != null)) { // _temporaryFileName may remain null if we are writing to a stream. // only close the stream if there is a file behind it. #if NETCF WriteStream.Close(); #else WriteStream.Dispose(); #endif if (_saveOperationCanceled) { return; } if (_fileAlreadyExists && this._readstream != null) { // This means we opened and read a zip file. // If we are now saving to the same file, we need to close the // orig file, first. this._readstream.Close(); this._readstream = null; // the archiveStream for each entry needs to be null foreach (var e in c) { var zss1 = e._archiveStream as ZipSegmentedStream; if (zss1 != null) #if NETCF { zss1.Close(); } #else { zss1.Dispose(); } #endif e._archiveStream = null; } } string tmpName = null; if (File.Exists(_name)) { // the steps: // // 1. Delete tmpName // 2. move existing zip to tmpName // 3. rename (File.Move) working file to name of existing zip // 4. delete tmpName // // This series of steps avoids the exception, // System.IO.IOException: // "Cannot create a file when that file already exists." // // Cannot just call File.Replace() here because // there is a possibility that the TEMP volume is different // that the volume for the final file (c:\ vs d:\). // So we need to do a Delete+Move pair. // // But, when doing the delete, Windows allows a process to // delete the file, even though it is held open by, say, a // virus scanner. It gets internally marked as "delete // pending". The file does not actually get removed from the // file system, it is still there after the File.Delete // call. // // Therefore, we need to move the existing zip, which may be // held open, to some other name. Then rename our working // file to the desired name, then delete (possibly delete // pending) the "other name". // // Ideally this would be transactional. It's possible that the // delete succeeds and the move fails. Lacking transactions, if // this kind of failure happens, we're hosed, and this logic will // throw on the next File.Move(). // //File.Delete(_name); // workitem 10447 #if NETCF || SILVERLIGHT tmpName = _name + "." + SharedUtilities.GenerateRandomStringImpl(8, 0) + ".tmp"; #else tmpName = _name + "." + Path.GetRandomFileName(); #endif if (File.Exists(tmpName)) { DeleteFileWithRetry(tmpName); } File.Move(_name, tmpName); } OnSaveEvent(ZipProgressEventType.Saving_BeforeRenameTempArchive); File.Move((zss != null) ? zss.CurrentTempName : _temporaryFileName, _name); OnSaveEvent(ZipProgressEventType.Saving_AfterRenameTempArchive); if (tmpName != null) { try { // not critical if (File.Exists(tmpName)) { File.Delete(tmpName); } } catch { // don't care about exceptions here. } } _fileAlreadyExists = true; } NotifyEntriesSaveComplete(c); OnSaveCompleted(); _JustSaved = true; } // workitem 5043 finally { CleanupAfterSaveOperation(); } return; }