// NOTE this returns the offset of the first byte after the signature. long LocateBlockWithSignature(int signature, long endLocation, int minimumBlockSize, int maximumVariableData) { using ( ZipHelperStream les = new ZipHelperStream(baseStream_) ) { return les.LocateBlockWithSignature(signature, endLocation, minimumBlockSize, maximumVariableData); } }
void RunUpdates() { long sizeEntries = 0; long endOfStream = 0; bool allOk = true; bool directUpdate = false; long destinationPosition = 0; // NOT SFX friendly ZipFile workFile; if ( IsNewArchive ) { workFile = this; workFile.baseStream_.Position = 0; directUpdate = true; } else if ( archiveStorage_.UpdateMode == FileUpdateMode.Direct ) { workFile = this; workFile.baseStream_.Position = 0; directUpdate = true; // Sort the updates with order offset within copies/modifies, then adds. // This ensures that copies will not overwrite any required data. updates_.Sort(new UpdateComparer()); } else { workFile = ZipFile.Create(archiveStorage_.GetTemporaryOutput()); } try { foreach ( ZipUpdate update in updates_ ) { switch ( update.Command ) { case UpdateCommand.Copy: if ( directUpdate ) { CopyEntryDirect(workFile, update, ref destinationPosition); } else { CopyEntry(workFile, update); } break; case UpdateCommand.Modify: // TODO: Direct modifying of an entry. ModifyEntry(workFile, update); break; case UpdateCommand.Add: if ( !IsNewArchive && directUpdate ) { workFile.baseStream_.Position = destinationPosition; } AddEntry(workFile, update); if ( directUpdate ) { destinationPosition = workFile.baseStream_.Position; } break; } } if ( !IsNewArchive && directUpdate ) { workFile.baseStream_.Position = destinationPosition; } long centralDirOffset = workFile.baseStream_.Position; foreach ( ZipUpdate update in updates_ ) { sizeEntries += workFile.WriteCentralDirectoryHeader(update.OutEntry); } byte[] theComment = (newComment_ != null) ? newComment_.RawComment : ZipConstants.ConvertToArray(comment_); using ( ZipHelperStream zhs = new ZipHelperStream(workFile.baseStream_) ) { zhs.WriteEndOfCentralDirectory(updates_.Count, sizeEntries, centralDirOffset, theComment); } endOfStream = workFile.baseStream_.Position; // And now patch entries... foreach ( ZipUpdate update in updates_ ) { // If the size of the entry is zero leave the crc as 0 as well. // The calculated crc will be all bits on... if ( (update.CrcPatchOffset > 0) && (update.OutEntry.CompressedSize > 0) ) { workFile.baseStream_.Position = update.CrcPatchOffset; workFile.WriteLEInt(( int )update.OutEntry.Crc); } if ( update.SizePatchOffset > 0 ) { workFile.baseStream_.Position = update.SizePatchOffset; if ( update.Entry.LocalHeaderRequiresZip64 ) { workFile.WriteLeLong(update.OutEntry.Size); workFile.WriteLeLong(update.OutEntry.CompressedSize); } else { workFile.WriteLEInt(( int )update.OutEntry.CompressedSize); workFile.WriteLEInt(( int )update.OutEntry.Size); } } } } catch(Exception) { allOk = false; } finally { if ( directUpdate ) { if ( allOk ) { workFile.baseStream_.Flush(); workFile.baseStream_.SetLength(endOfStream); } } else { workFile.Close(); } } if ( allOk ) { if ( directUpdate ) { isNewArchive_ = false; workFile.baseStream_.Flush(); ReadEntries(); } else { baseStream_.Close(); Reopen(archiveStorage_.ConvertTemporaryToFinal()); } } else { workFile.Close(); if ( !directUpdate && (workFile.Name != null) ) { File.Delete(workFile.Name); } } }
void UpdateCommentOnly() { long baseLength = baseStream_.Length; ZipHelperStream updateFile = null; if ( archiveStorage_.UpdateMode == FileUpdateMode.Safe ) { Stream copyStream = archiveStorage_.MakeTemporaryCopy(baseStream_); updateFile = new ZipHelperStream(copyStream); updateFile.IsStreamOwner = true; baseStream_.Close(); baseStream_ = null; } else { baseStream_.Close(); baseStream_ = null; updateFile = new ZipHelperStream(Name); } using ( updateFile ) { long locatedCentralDirOffset = updateFile.LocateBlockWithSignature(ZipConstants.EndOfCentralDirectorySignature, baseLength, ZipConstants.EndOfCentralRecordBaseSize, 0xffff); if ( locatedCentralDirOffset < 0 ) { throw new ZipException("Cannot find central directory"); } const int CentralHeaderCommentSizeOffset = 16; updateFile.Position += CentralHeaderCommentSizeOffset; byte[] rawComment = newComment_.RawComment; updateFile.WriteLEShort(rawComment.Length); updateFile.Write(rawComment, 0, rawComment.Length); updateFile.SetLength(updateFile.Position); } if ( archiveStorage_.UpdateMode == FileUpdateMode.Safe ) { Reopen(archiveStorage_.ConvertTemporaryToFinal()); } else { Reopen(); } }
/// <summary> /// Commit current updates, updating this archive. /// </summary> /// <seealso cref="BeginUpdate"></seealso> /// <seealso cref="AbortUpdate"></seealso> public void CommitUpdate() { CheckUpdating(); if ( contentsEdited_ ) { RunUpdates(); } else if ( commentEdited_ ) { UpdateCommentOnly(); } else { // Create an empty archive if none existed originally. if ( (entries_ != null) && (entries_.Length == 0) ) { byte[] theComment = (newComment_ != null) ? newComment_.RawComment : ZipConstants.ConvertToArray(comment_); using ( ZipHelperStream zhs = new ZipHelperStream(baseStream_) ) { zhs.WriteEndOfCentralDirectory(0, 0, 0, theComment); } } } PostUpdateCleanup(); }
/// <summary> /// Finishes the stream. This will write the central directory at the /// end of the zip file and flush the stream. /// </summary> /// <remarks> /// This is automatically called when the stream is closed. /// </remarks> /// <exception cref="System.IO.IOException"> /// An I/O error occurs. /// </exception> /// <exception cref="ZipException"> /// Comment exceeds the maximum length<br/> /// Entry name exceeds the maximum length /// </exception> public override void Finish() { if (entries == null) { return; } if (curEntry != null) { CloseEntry(); } long numEntries = entries.Count; long sizeEntries = 0; foreach (ZipEntry entry in entries) { WriteLeInt(ZipConstants.CentralHeaderSignature); WriteLeShort(ZipConstants.VersionMadeBy); WriteLeShort(entry.Version); WriteLeShort(entry.Flags); WriteLeShort((short)entry.CompressionMethod); WriteLeInt((int)entry.DosTime); WriteLeInt((int)entry.Crc); #if !FORCE_ZIP64 if (entry.IsZip64Forced() || (entry.CompressedSize >= uint.MaxValue)) #endif { WriteLeInt(-1); } else { WriteLeInt((int)entry.CompressedSize); } #if !FORCE_ZIP64 if (entry.IsZip64Forced() || (entry.Size >= uint.MaxValue)) #endif { WriteLeInt(-1); } else { WriteLeInt((int)entry.Size); } byte[] name = ZipConstants.ConvertToArray(entry.Name); if (name.Length > 0xffff) { throw new ZipException("Name too long."); } ZipExtraData ed = new ZipExtraData(entry.ExtraData); if (entry.CentralHeaderRequiresZip64) { ed.StartNewEntry(); #if !FORCE_ZIP64 if (entry.IsZip64Forced() || (entry.Size >= 0xffffffff)) #endif { ed.AddLeLong(entry.Size); } #if !FORCE_ZIP64 if (entry.IsZip64Forced() || (entry.CompressedSize >= 0xffffffff)) #endif { ed.AddLeLong(entry.CompressedSize); } if (entry.Offset >= 0xffffffff) { ed.AddLeLong(entry.Offset); } ed.AddNewEntry(1); } else { ed.Delete(1); } byte[] extra = ed.GetEntryData(); byte[] entryComment = (entry.Comment != null) ? ZipConstants.ConvertToArray(entry.Comment) : new byte[0]; if (entryComment.Length > 0xffff) { throw new ZipException("Comment too long."); } WriteLeShort(name.Length); WriteLeShort(extra.Length); WriteLeShort(entryComment.Length); WriteLeShort(0); // disk number WriteLeShort(0); // internal file attributes // external file attributes if (entry.ExternalFileAttributes != -1) { WriteLeInt(entry.ExternalFileAttributes); } else { if (entry.IsDirectory) // mark entry as directory (from nikolam.AT.perfectinfo.com) { WriteLeInt(16); } else { WriteLeInt(0); } } if (entry.Offset >= uint.MaxValue) { WriteLeInt(-1); } else { WriteLeInt((int)entry.Offset); } if (name.Length > 0) { baseOutputStream.Write(name, 0, name.Length); } if (extra.Length > 0) { baseOutputStream.Write(extra, 0, extra.Length); } if (entryComment.Length > 0) { baseOutputStream.Write(entryComment, 0, entryComment.Length); } sizeEntries += ZipConstants.CentralHeaderBaseSize + name.Length + extra.Length + entryComment.Length; } using (ZipHelperStream zhs = new ZipHelperStream(baseOutputStream)) { zhs.WriteEndOfCentralDirectory(numEntries, sizeEntries, offset, zipComment); } entries = null; }
/// <summary> /// Finishes the stream. This will write the central directory at the /// end of the zip file and flush the stream. /// </summary> /// <remarks> /// This is automatically called when the stream is closed. /// </remarks> /// <exception cref="System.IO.IOException"> /// An I/O error occurs. /// </exception> /// <exception cref="ZipException"> /// Comment exceeds the maximum length<br/> /// Entry name exceeds the maximum length /// </exception> public override void Finish() { if (entries == null) { return; } if (curEntry != null) { CloseEntry(); } long numEntries = entries.Count; long sizeEntries = 0; foreach (ZipEntry entry in entries) { WriteLeInt(ZipConstants.CentralHeaderSignature); WriteLeShort(ZipConstants.VersionMadeBy); WriteLeShort(entry.Version); WriteLeShort(entry.Flags); WriteLeShort((short)entry.CompressionMethod); WriteLeInt((int)entry.DosTime); WriteLeInt((int)entry.Crc); #if !FORCE_ZIP64 if ( entry.IsZip64Forced() || (entry.CompressedSize >= uint.MaxValue) ) #endif { WriteLeInt(-1); } else { WriteLeInt((int)entry.CompressedSize); } #if !FORCE_ZIP64 if ( entry.IsZip64Forced() || (entry.Size >= uint.MaxValue) ) #endif { WriteLeInt(-1); } else { WriteLeInt((int)entry.Size); } byte[] name = ZipConstants.ConvertToArray(entry.Name); if (name.Length > 0xffff) { throw new ZipException("Name too long."); } ZipExtraData ed = new ZipExtraData(entry.ExtraData); if ( entry.CentralHeaderRequiresZip64 ) { ed.StartNewEntry(); #if !FORCE_ZIP64 if ( entry.IsZip64Forced() || (entry.Size >= 0xffffffff) ) #endif { ed.AddLeLong(entry.Size); } #if !FORCE_ZIP64 if ( entry.IsZip64Forced() || (entry.CompressedSize >= 0xffffffff) ) #endif { ed.AddLeLong(entry.CompressedSize); } if ( entry.Offset >= 0xffffffff ) { ed.AddLeLong(entry.Offset); } ed.AddNewEntry(1); } else { ed.Delete(1); } byte[] extra = ed.GetEntryData(); byte[] entryComment = (entry.Comment != null) ? ZipConstants.ConvertToArray(entry.Comment) : new byte[0]; if (entryComment.Length > 0xffff) { throw new ZipException("Comment too long."); } WriteLeShort(name.Length); WriteLeShort(extra.Length); WriteLeShort(entryComment.Length); WriteLeShort(0); // disk number WriteLeShort(0); // internal file attributes // external file attributes if (entry.ExternalFileAttributes != -1) { WriteLeInt(entry.ExternalFileAttributes); } else { if (entry.IsDirectory) { // mark entry as directory (from nikolam.AT.perfectinfo.com) WriteLeInt(16); } else { WriteLeInt(0); } } if ( entry.Offset >= uint.MaxValue ) { WriteLeInt(-1); } else { WriteLeInt((int)entry.Offset); } if ( name.Length > 0 ) { baseOutputStream.Write(name, 0, name.Length); } if ( extra.Length > 0 ) { baseOutputStream.Write(extra, 0, extra.Length); } if ( entryComment.Length > 0 ) { baseOutputStream.Write(entryComment, 0, entryComment.Length); } sizeEntries += ZipConstants.CentralHeaderBaseSize + name.Length + extra.Length + entryComment.Length; } using ( ZipHelperStream zhs = new ZipHelperStream(baseOutputStream) ) { zhs.WriteEndOfCentralDirectory(numEntries, sizeEntries, offset, zipComment); } entries = null; }