private void _SetWriteStream(uint increment) { if (_innerStream != null) { #if NETCF _innerStream.Close(); #else _innerStream.Dispose(); #endif if (File.Exists(CurrentName)) { File.Delete(CurrentName); } File.Move(_currentTempName, CurrentName); // Console.WriteLine("ZSS: SWS close ({0})", // Path.GetFileName(CurrentName)); } if (increment > 0) { CurrentSegment += increment; } SharedUtilities.CreateAndOpenUniqueTempFile(_baseDir, out _innerStream, out _currentTempName); // Console.WriteLine("ZSS: SWS open ({0})", // Path.GetFileName(_currentTempName)); if (CurrentSegment == 0) { _innerStream.Write(BitConverter.GetBytes(ZipConstants.SplitArchiveSignature), 0, 4); } }
public override long Seek(long offset, System.IO.SeekOrigin origin) { var x = _innerStream.Seek(offset, origin); // workitem 10178 SharedUtilities.Workaround_Ladybug318918(_innerStream); return(x); }
/// <summary> /// This initializes the cipher with the given password. /// See AppNote.txt for details. /// </summary> /// /// <param name="passphrase"> /// The passphrase for encrypting or decrypting with this cipher. /// </param> /// /// <remarks> /// <code> /// Step 1 - Initializing the encryption keys /// ----------------------------------------- /// Start with these keys: /// Key(0) := 305419896 (0x12345678) /// Key(1) := 591751049 (0x23456789) /// Key(2) := 878082192 (0x34567890) /// /// Then, initialize the keys with a password: /// /// loop for i from 0 to length(password)-1 /// update_keys(password(i)) /// end loop /// /// Where update_keys() is defined as: /// /// update_keys(char): /// Key(0) := crc32(key(0),char) /// Key(1) := Key(1) + (Key(0) bitwiseAND 000000ffH) /// Key(1) := Key(1) * 134775813 + 1 /// Key(2) := crc32(key(2),key(1) rightshift 24) /// end update_keys /// /// Where crc32(old_crc,char) is a routine that given a CRC value and a /// character, returns an updated CRC value after applying the CRC-32 /// algorithm described elsewhere in this document. /// /// </code> /// /// <para> /// After the keys are initialized, then you can use the cipher to /// encrypt the plaintext. /// </para> /// /// <para> /// Essentially we encrypt the password with the keys, then discard the /// ciphertext for the password. This initializes the keys for later use. /// </para> /// /// </remarks> public void InitCipher(string passphrase) { byte[] p = SharedUtilities.StringToByteArray(passphrase); for (int i = 0; i < passphrase.Length; i++) { UpdateKeys(p[i]); } }
/// <summary> /// This method seeks in the underlying stream. /// </summary> /// /// <remarks> /// <para> /// Call this method if you want to seek around within the zip file for random access. /// </para> /// /// <para> /// Applications can intermix calls to <c>Seek()</c> with calls to <see /// cref="GetNextEntry()"/>. After a call to <c>Seek()</c>, /// <c>GetNextEntry()</c> will get the next <c>ZipEntry</c> that falls after /// the current position in the input stream. You're on your own for finding /// out just where to seek in the stream, to get to the various entries. /// </para> /// /// </remarks> /// /// <param name="offset">the offset point to seek to</param> /// <param name="origin">the reference point from which to seek</param> /// <returns>The new position</returns> public override long Seek(long offset, SeekOrigin origin) { _findRequired = true; var x = _inputStream.Seek(offset, origin); // workitem 10178 SharedUtilities.Workaround_Ladybug318918(_inputStream); return(x); }
/// <summary> /// Reads one <c>ZipEntry</c> from the given stream. The content for /// the entry does not get decompressed or decrypted. This method /// basically reads metadata, and seeks. /// </summary> /// <param name="zc">the ZipContainer this entry belongs to.</param> /// <param name="first"> /// true of this is the first entry being read from the stream. /// </param> /// <returns>the <c>ZipEntry</c> read from the stream.</returns> internal static ZipEntry ReadEntry(ZipContainer zc, bool first) { ZipFile zf = zc.ZipFile; Stream s = zc.ReadStream; System.Text.Encoding defaultEncoding = zc.AlternateEncoding; ZipEntry entry = new ZipEntry(); entry._Source = ZipEntrySource.ZipFile; entry._container = zc; entry._archiveStream = s; if (zf != null) { zf.OnReadEntry(true, null); } if (first) { HandlePK00Prefix(s); } // Read entry header, including any encryption header if (!ReadHeader(entry, defaultEncoding)) { return(null); } // Store the position in the stream for this entry // change for workitem 8098 entry.__FileDataPosition = entry.ArchiveStream.Position; // seek past the data without reading it. We will read on Extract() s.Seek(entry._CompressedFileDataSize + entry._LengthOfTrailer, SeekOrigin.Current); // workitem 10178 SharedUtilities.Workaround_Ladybug318918(s); // ReadHeader moves the file pointer to the end of the entry header, // as well as any encryption header. // CompressedFileDataSize includes: // the maybe compressed, maybe encrypted file data // the encryption trailer, if any // the bit 3 descriptor, if any // workitem 5306 // http://www.codeplex.com/DotNetZip/WorkItem/View.aspx?WorkItemId=5306 HandleUnexpectedDataDescriptor(entry); if (zf != null) { zf.OnReadBytes(entry); zf.OnReadEntry(false, entry); } return(entry); }
internal static void HandlePK00Prefix(Stream s) { // in some cases, the zip file begins with "PK00". This is a throwback and is rare, // but we handle it anyway. We do not change behavior based on it. uint datum = (uint)SharedUtilities.ReadInt(s); if (datum != ZipConstants.PackedToRemovableMedia) { s.Seek(-4, SeekOrigin.Current); // unread the block // workitem 10178 SharedUtilities.Workaround_Ladybug318918(s); } }
private static void HandleUnexpectedDataDescriptor(ZipEntry entry) { Stream s = entry.ArchiveStream; // In some cases, the "data descriptor" is present, without a signature, even when // bit 3 of the BitField is NOT SET. This is the CRC, followed // by the compressed length and the uncompressed length (4 bytes for each // of those three elements). Need to check that here. // uint datum = (uint)SharedUtilities.ReadInt(s); if (datum == entry._Crc32) { int sz = SharedUtilities.ReadInt(s); if (sz == entry._CompressedSize) { sz = SharedUtilities.ReadInt(s); if (sz == entry._UncompressedSize) { // ignore everything and discard it. } else { s.Seek(-12, SeekOrigin.Current); // unread the three blocks // workitem 10178 SharedUtilities.Workaround_Ladybug318918(s); } } else { s.Seek(-8, SeekOrigin.Current); // unread the two blocks // workitem 10178 SharedUtilities.Workaround_Ladybug318918(s); } } else { s.Seek(-4, SeekOrigin.Current); // unread the block // workitem 10178 SharedUtilities.Workaround_Ladybug318918(s); } }
private static void Zip64SeekToCentralDirectory(ZipFile zf) { Stream s = zf.ReadStream; byte[] block = new byte[16]; // seek back to find the ZIP64 EoCD. // I think this might not work for .NET CF ? s.Seek(-40, SeekOrigin.Current); s.Read(block, 0, 16); Int64 offset64 = BitConverter.ToInt64(block, 8); zf._OffsetOfCentralDirectory = 0xFFFFFFFF; zf._OffsetOfCentralDirectory64 = offset64; // change for workitem 8098 s.Seek(offset64, SeekOrigin.Begin); //zf.SeekFromOrigin(Offset64); uint datum = (uint)SharedUtilities.ReadInt(s); if (datum != ZipConstants.Zip64EndOfCentralDirectoryRecordSignature) { throw new BadReadException(String.Format(" Bad signature (0x{0:X8}) looking for ZIP64 EoCD Record at position 0x{1:X8}", datum, s.Position)); } s.Read(block, 0, 8); Int64 Size = BitConverter.ToInt64(block, 0); block = new byte[Size]; s.Read(block, 0, block.Length); offset64 = BitConverter.ToInt64(block, 36); // change for workitem 8098 s.Seek(offset64, SeekOrigin.Begin); //zf.SeekFromOrigin(Offset64); }
/// <summary> /// Reads one entry from the zip directory structure in the zip file. /// </summary> /// /// <param name="zf"> /// The zipfile for which a directory entry will be read. From this param, the /// method gets the ReadStream and the expected text encoding /// (ProvisionalAlternateEncoding) which is used if the entry is not marked /// UTF-8. /// </param> /// /// <param name="previouslySeen"> /// a list of previously seen entry names; used to prevent duplicates. /// </param> /// /// <returns>the entry read from the archive.</returns> internal static ZipEntry ReadDirEntry(ZipFile zf, Dictionary <String, Object> previouslySeen) { System.IO.Stream s = zf.ReadStream; System.Text.Encoding expectedEncoding = (zf.AlternateEncodingUsage == ZipOption.Always) ? zf.AlternateEncoding : ZipFile.DefaultEncoding; int signature = SharedUtilities.ReadSignature(s); // return null if this is not a local file header signature if (IsNotValidZipDirEntrySig(signature)) { s.Seek(-4, System.IO.SeekOrigin.Current); // workitem 10178 SharedUtilities.Workaround_Ladybug318918(s); // Getting "not a ZipDirEntry signature" here is not always wrong or an // error. This can happen when walking through a zipfile. After the // last ZipDirEntry, we expect to read an // EndOfCentralDirectorySignature. When we get this is how we know // we've reached the end of the central directory. if (signature != ZipConstants.EndOfCentralDirectorySignature && signature != ZipConstants.Zip64EndOfCentralDirectoryRecordSignature && signature != ZipConstants.ZipEntrySignature // workitem 8299 ) { throw new BadReadException(String.Format(" Bad signature (0x{0:X8}) at position 0x{1:X8}", signature, s.Position)); } return(null); } int bytesRead = 42 + 4; byte[] block = new byte[42]; int n = s.Read(block, 0, block.Length); if (n != block.Length) { return(null); } int i = 0; ZipEntry zde = new ZipEntry(); zde.AlternateEncoding = expectedEncoding; zde._Source = ZipEntrySource.ZipFile; zde._container = new ZipContainer(zf); unchecked { zde._VersionMadeBy = (short)(block[i++] + block[i++] * 256); zde._VersionNeeded = (short)(block[i++] + block[i++] * 256); zde._BitField = (short)(block[i++] + block[i++] * 256); zde._CompressionMethod = (Int16)(block[i++] + block[i++] * 256); zde._TimeBlob = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256; zde._LastModified = SharedUtilities.PackedToDateTime(zde._TimeBlob); zde._timestamp |= ZipEntryTimestamp.DOS; zde._Crc32 = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256; zde._CompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); zde._UncompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); } // preserve zde._CompressionMethod_FromZipFile = zde._CompressionMethod; zde._filenameLength = (short)(block[i++] + block[i++] * 256); zde._extraFieldLength = (short)(block[i++] + block[i++] * 256); zde._commentLength = (short)(block[i++] + block[i++] * 256); zde._diskNumber = (UInt32)(block[i++] + block[i++] * 256); zde._InternalFileAttrs = (short)(block[i++] + block[i++] * 256); zde._ExternalFileAttrs = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256; zde._RelativeOffsetOfLocalHeader = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); // workitem 7801 zde.IsText = ((zde._InternalFileAttrs & 0x01) == 0x01); block = new byte[zde._filenameLength]; n = s.Read(block, 0, block.Length); bytesRead += n; if ((zde._BitField & 0x0800) == 0x0800) { // UTF-8 is in use zde._FileNameInArchive = SharedUtilities.Utf8StringFromBuffer(block); } else { zde._FileNameInArchive = SharedUtilities.StringFromBuffer(block, expectedEncoding); } // workitem 10330 // insure unique entry names while (previouslySeen.ContainsKey(zde._FileNameInArchive)) { zde._FileNameInArchive = CopyHelper.AppendCopyToFileName(zde._FileNameInArchive); zde._metadataChanged = true; } if (zde.AttributesIndicateDirectory) { zde.MarkAsDirectory(); // may append a slash to filename if nec. } // workitem 6898 else if (zde._FileNameInArchive.EndsWith("/")) { zde.MarkAsDirectory(); } zde._CompressedFileDataSize = zde._CompressedSize; if ((zde._BitField & 0x01) == 0x01) { // this may change after processing the Extra field zde._Encryption_FromZipFile = zde._Encryption = EncryptionAlgorithm.PkzipWeak; zde._sourceIsEncrypted = true; } if (zde._extraFieldLength > 0) { zde._InputUsesZip64 = (zde._CompressedSize == 0xFFFFFFFF || zde._UncompressedSize == 0xFFFFFFFF || zde._RelativeOffsetOfLocalHeader == 0xFFFFFFFF); // Console.WriteLine(" Input uses Z64?: {0}", zde._InputUsesZip64); bytesRead += zde.ProcessExtraField(s, zde._extraFieldLength); zde._CompressedFileDataSize = zde._CompressedSize; } // we've processed the extra field, so we know the encryption method is set now. if (zde._Encryption == EncryptionAlgorithm.PkzipWeak) { // the "encryption header" of 12 bytes precedes the file data zde._CompressedFileDataSize -= 12; } #if AESCRYPTO else if (zde.Encryption == EncryptionAlgorithm.WinZipAes128 || zde.Encryption == EncryptionAlgorithm.WinZipAes256) { zde._CompressedFileDataSize = zde.CompressedSize - (ZipEntry.GetLengthOfCryptoHeaderBytes(zde.Encryption) + 10); zde._LengthOfTrailer = 10; } #endif // tally the trailing descriptor if ((zde._BitField & 0x0008) == 0x0008) { // sig, CRC, Comp and Uncomp sizes if (zde._InputUsesZip64) { zde._LengthOfTrailer += 24; } else { zde._LengthOfTrailer += 16; } } // workitem 12744 zde.AlternateEncoding = ((zde._BitField & 0x0800) == 0x0800) ? System.Text.Encoding.UTF8 :expectedEncoding; zde.AlternateEncodingUsage = ZipOption.Always; if (zde._commentLength > 0) { block = new byte[zde._commentLength]; n = s.Read(block, 0, block.Length); bytesRead += n; if ((zde._BitField & 0x0800) == 0x0800) { // UTF-8 is in use zde._Comment = SharedUtilities.Utf8StringFromBuffer(block); } else { zde._Comment = SharedUtilities.StringFromBuffer(block, expectedEncoding); } } //zde._LengthOfDirEntry = bytesRead; return(zde); }
private static bool ReadHeader(ZipEntry ze, System.Text.Encoding defaultEncoding) { int bytesRead = 0; // change for workitem 8098 ze._RelativeOffsetOfLocalHeader = ze.ArchiveStream.Position; int signature = SharedUtilities.ReadEntrySignature(ze.ArchiveStream); bytesRead += 4; // Return false if this is not a local file header signature. if (ZipEntry.IsNotValidSig(signature)) { // Getting "not a ZipEntry signature" is not always wrong or an error. // This will happen after the last entry in a zipfile. In that case, we // expect to read : // a ZipDirEntry signature (if a non-empty zip file) or // a ZipConstants.EndOfCentralDirectorySignature. // // Anything else is a surprise. ze.ArchiveStream.Seek(-4, SeekOrigin.Current); // unread the signature // workitem 10178 SharedUtilities.Workaround_Ladybug318918(ze.ArchiveStream); if (ZipEntry.IsNotValidZipDirEntrySig(signature) && (signature != ZipConstants.EndOfCentralDirectorySignature)) { throw new BadReadException(String.Format(" Bad signature (0x{0:X8}) at position 0x{1:X8}", signature, ze.ArchiveStream.Position)); } return(false); } byte[] block = new byte[26]; int n = ze.ArchiveStream.Read(block, 0, block.Length); if (n != block.Length) { return(false); } bytesRead += n; int i = 0; ze._VersionNeeded = (Int16)(block[i++] + block[i++] * 256); ze._BitField = (Int16)(block[i++] + block[i++] * 256); ze._CompressionMethod_FromZipFile = ze._CompressionMethod = (Int16)(block[i++] + block[i++] * 256); ze._TimeBlob = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256; // transform the time data into something usable (a DateTime) ze._LastModified = SharedUtilities.PackedToDateTime(ze._TimeBlob); ze._timestamp |= ZipEntryTimestamp.DOS; if ((ze._BitField & 0x01) == 0x01) { ze._Encryption_FromZipFile = ze._Encryption = EncryptionAlgorithm.PkzipWeak; // this *may* change after processing the Extra field ze._sourceIsEncrypted = true; } // NB: if ((ze._BitField & 0x0008) != 0x0008), then the Compressed, uncompressed and // CRC values are not true values; the true values will follow the entry data. // But, regardless of the status of bit 3 in the bitfield, the slots for // the three amigos may contain marker values for ZIP64. So we must read them. { ze._Crc32 = (Int32)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); ze._CompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); ze._UncompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); if ((uint)ze._CompressedSize == 0xFFFFFFFF || (uint)ze._UncompressedSize == 0xFFFFFFFF) { ze._InputUsesZip64 = true; } } Int16 filenameLength = (short)(block[i++] + block[i++] * 256); Int16 extraFieldLength = (short)(block[i++] + block[i++] * 256); block = new byte[filenameLength]; n = ze.ArchiveStream.Read(block, 0, block.Length); bytesRead += n; // if the UTF8 bit is set for this entry, override the // encoding the application requested. if ((ze._BitField & 0x0800) == 0x0800) { // workitem 12744 ze.AlternateEncoding = System.Text.Encoding.UTF8; ze.AlternateEncodingUsage = ZipOption.Always; } // need to use this form of GetString() for .NET CF ze._FileNameInArchive = ze.AlternateEncoding.GetString(block, 0, block.Length); // workitem 6898 if (ze._FileNameInArchive.EndsWith("/")) { ze.MarkAsDirectory(); } bytesRead += ze.ProcessExtraField(ze.ArchiveStream, extraFieldLength); ze._LengthOfTrailer = 0; // workitem 6607 - don't read for directories // actually get the compressed size and CRC if necessary if (!ze._FileNameInArchive.EndsWith("/") && (ze._BitField & 0x0008) == 0x0008) { // This descriptor exists only if bit 3 of the general // purpose bit flag is set (see below). It is byte aligned // and immediately follows the last byte of compressed data, // as well as any encryption trailer, as with AES. // This descriptor is used only when it was not possible to // seek in the output .ZIP file, e.g., when the output .ZIP file // was standard output or a non-seekable device. For ZIP64(tm) format // archives, the compressed and uncompressed sizes are 8 bytes each. // workitem 8098: ok (restore) long posn = ze.ArchiveStream.Position; // Here, we're going to loop until we find a ZipEntryDataDescriptorSignature and // a consistent data record after that. To be consistent, the data record must // indicate the length of the entry data. bool wantMore = true; long SizeOfDataRead = 0; int tries = 0; while (wantMore) { tries++; // We call the FindSignature shared routine to find the specified signature // in the already-opened zip archive, starting from the current cursor // position in that filestream. If we cannot find the signature, then the // routine returns -1, and the ReadHeader() method returns false, // indicating we cannot read a legal entry header. If we have found it, // then the FindSignature() method returns the number of bytes in the // stream we had to seek forward, to find the sig. We need this to // determine if the zip entry is valid, later. if (ze._container.ZipFile != null) { ze._container.ZipFile.OnReadBytes(ze); } long d = SharedUtilities.FindSignature(ze.ArchiveStream, ZipConstants.ZipEntryDataDescriptorSignature); if (d == -1) { return(false); } // total size of data read (through all loops of this). SizeOfDataRead += d; if (ze._InputUsesZip64) { // read 1x 4-byte (CRC) and 2x 8-bytes (Compressed Size, Uncompressed Size) block = new byte[20]; n = ze.ArchiveStream.Read(block, 0, block.Length); if (n != 20) { return(false); } // do not increment bytesRead - it is for entry header only. // the data we have just read is a footer (falls after the file data) //bytesRead += n; i = 0; ze._Crc32 = (Int32)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); ze._CompressedSize = BitConverter.ToInt64(block, i); i += 8; ze._UncompressedSize = BitConverter.ToInt64(block, i); i += 8; ze._LengthOfTrailer += 24; // bytes including sig, CRC, Comp and Uncomp sizes } else { // read 3x 4-byte fields (CRC, Compressed Size, Uncompressed Size) block = new byte[12]; n = ze.ArchiveStream.Read(block, 0, block.Length); if (n != 12) { return(false); } // do not increment bytesRead - it is for entry header only. // the data we have just read is a footer (falls after the file data) //bytesRead += n; i = 0; ze._Crc32 = (Int32)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); ze._CompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); ze._UncompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); ze._LengthOfTrailer += 16; // bytes including sig, CRC, Comp and Uncomp sizes } wantMore = (SizeOfDataRead != ze._CompressedSize); if (wantMore) { // Seek back to un-read the last 12 bytes - maybe THEY contain // the ZipEntryDataDescriptorSignature. // (12 bytes for the CRC, Comp and Uncomp size.) ze.ArchiveStream.Seek(-12, SeekOrigin.Current); // workitem 10178 SharedUtilities.Workaround_Ladybug318918(ze.ArchiveStream); // Adjust the size to account for the false signature read in // FindSignature(). SizeOfDataRead += 4; } } // seek back to previous position, to prepare to read file data // workitem 8098: ok (restore) ze.ArchiveStream.Seek(posn, SeekOrigin.Begin); // workitem 10178 SharedUtilities.Workaround_Ladybug318918(ze.ArchiveStream); } ze._CompressedFileDataSize = ze._CompressedSize; // bit 0 set indicates that some kind of encryption is in use if ((ze._BitField & 0x01) == 0x01) { #if AESCRYPTO if (ze.Encryption == EncryptionAlgorithm.WinZipAes128 || ze.Encryption == EncryptionAlgorithm.WinZipAes256) { int bits = ZipEntry.GetKeyStrengthInBits(ze._Encryption_FromZipFile); // read in the WinZip AES metadata: salt + PV. 18 bytes for AES256. 10 bytes for AES128. ze._aesCrypto_forExtract = WinZipAesCrypto.ReadFromStream(null, bits, ze.ArchiveStream); bytesRead += ze._aesCrypto_forExtract.SizeOfEncryptionMetadata - 10; // MAC (follows crypto bytes) // according to WinZip, the CompressedSize includes the AES Crypto framing data. ze._CompressedFileDataSize -= ze._aesCrypto_forExtract.SizeOfEncryptionMetadata; ze._LengthOfTrailer += 10; // MAC } else #endif { // read in the header data for "weak" encryption ze._WeakEncryptionHeader = new byte[12]; bytesRead += ZipEntry.ReadWeakEncryptionHeader(ze._archiveStream, ze._WeakEncryptionHeader); // decrease the filedata size by 12 bytes ze._CompressedFileDataSize -= 12; } } // Remember the size of the blob for this entry. // We also have the starting position in the stream for this entry. ze._LengthOfHeader = bytesRead; ze._TotalEntrySize = ze._LengthOfHeader + ze._CompressedFileDataSize + ze._LengthOfTrailer; // We've read in the regular entry header, the extra field, and any // encryption header. The pointer in the file is now at the start of the // filedata, which is potentially compressed and encrypted. Just ahead in // the file, there are _CompressedFileDataSize bytes of data, followed by // potentially a non-zero length trailer, consisting of optionally, some // encryption stuff (10 byte MAC for AES), and the bit-3 trailer (16 or 24 // bytes). return(true); }
public long TruncateBackward(uint diskNumber, long offset) { // Console.WriteLine("***ZSS.Trunc to disk {0}", diskNumber); // Console.WriteLine("***ZSS.Trunc: current disk {0}", CurrentSegment); if (diskNumber >= 99) { throw new ArgumentOutOfRangeException("diskNumber"); } if (rwMode != RwMode.Write) { _exceptionPending = true; throw new ZipException("bad state."); } // Seek back in the segmented stream to a (maybe) prior segment. // Check if it is the same segment. If it is, very simple. if (diskNumber == CurrentSegment) { var x = _innerStream.Seek(offset, SeekOrigin.Begin); // workitem 10178 SharedUtilities.Workaround_Ladybug318918(_innerStream); return(x); } // Seeking back to a prior segment. // The current segment and any intervening segments must be removed. // First, close the current segment, and then remove it. if (_innerStream != null) { #if NETCF _innerStream.Close(); #else _innerStream.Dispose(); #endif if (File.Exists(_currentTempName)) { File.Delete(_currentTempName); } } // Now, remove intervening segments. for (uint j = CurrentSegment - 1; j > diskNumber; j--) { string s = _NameForSegment(j); // Console.WriteLine("***ZSS.Trunc: removing file {0}", s); if (File.Exists(s)) { File.Delete(s); } } // now, open the desired segment. It must exist. CurrentSegment = diskNumber; // get a new temp file, try 3 times: for (int i = 0; i < 3; i++) { try { _currentTempName = SharedUtilities.InternalGetTempFileName(); // move the .z0x file back to a temp name File.Move(CurrentName, _currentTempName); break; // workitem 12403 } catch (IOException) { if (i == 2) { throw; } } } // open it _innerStream = new FileStream(_currentTempName, FileMode.Open); var r = _innerStream.Seek(offset, SeekOrigin.Begin); // workitem 10178 SharedUtilities.Workaround_Ladybug318918(_innerStream); return(r); }
/// <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; }
private static void ReadCentralDirectoryFooter(ZipFile zf) { Stream s = zf.ReadStream; int signature = SharedUtilities.ReadSignature(s); byte[] block = null; int j = 0; if (signature == ZipConstants.Zip64EndOfCentralDirectoryRecordSignature) { // We have a ZIP64 EOCD // This data block is 4 bytes sig, 8 bytes size, 44 bytes fixed data, // followed by a variable-sized extension block. We have read the sig already. // 8 - datasize (64 bits) // 2 - version made by // 2 - version needed to extract // 4 - number of this disk // 4 - number of the disk with the start of the CD // 8 - total number of entries in the CD on this disk // 8 - total number of entries in the CD // 8 - size of the CD // 8 - offset of the CD // ----------------------- // 52 bytes block = new byte[8 + 44]; s.Read(block, 0, block.Length); Int64 DataSize = BitConverter.ToInt64(block, 0); // == 44 + the variable length if (DataSize < 44) { throw new ZipException("Bad size in the ZIP64 Central Directory."); } zf._versionMadeBy = BitConverter.ToUInt16(block, j); j += 2; zf._versionNeededToExtract = BitConverter.ToUInt16(block, j); j += 2; zf._diskNumberWithCd = BitConverter.ToUInt32(block, j); j += 2; //zf._diskNumberWithCd++; // hack!! // read the extended block block = new byte[DataSize - 44]; s.Read(block, 0, block.Length); // discard the result signature = SharedUtilities.ReadSignature(s); if (signature != ZipConstants.Zip64EndOfCentralDirectoryLocatorSignature) { throw new ZipException("Inconsistent metadata in the ZIP64 Central Directory."); } block = new byte[16]; s.Read(block, 0, block.Length); // discard the result signature = SharedUtilities.ReadSignature(s); } // Throw if this is not a signature for "end of central directory record" // This is a sanity check. if (signature != ZipConstants.EndOfCentralDirectorySignature) { s.Seek(-4, SeekOrigin.Current); throw new BadReadException(String.Format("Bad signature ({0:X8}) at position 0x{1:X8}", signature, s.Position)); } // read the End-of-Central-Directory-Record block = new byte[16]; zf.ReadStream.Read(block, 0, block.Length); // off sz data // ------------------------------------------------------- // 0 4 end of central dir signature (0x06054b50) // 4 2 number of this disk // 6 2 number of the disk with start of the central directory // 8 2 total number of entries in the central directory on this disk // 10 2 total number of entries in the central directory // 12 4 size of the central directory // 16 4 offset of start of central directory with respect to the starting disk number // 20 2 ZIP file comment length // 22 ?? ZIP file comment if (zf._diskNumberWithCd == 0) { zf._diskNumberWithCd = BitConverter.ToUInt16(block, 2); //zf._diskNumberWithCd++; // hack!! } // read the comment here ReadZipFileComment(zf); }
private static uint ReadFirstFourBytes(Stream s) { uint datum = (uint)SharedUtilities.ReadInt(s); return(datum); }
private static void ReadIntoInstance(ZipFile zf) { Stream s = zf.ReadStream; try { zf._readName = zf._name; // workitem 13915 if (!s.CanSeek) { ReadIntoInstance_Orig(zf); return; } zf.OnReadStarted(); // change for workitem 8098 //zf._originPosition = s.Position; // Try reading the central directory, rather than scanning the file. uint datum = ReadFirstFourBytes(s); if (datum == ZipConstants.EndOfCentralDirectorySignature) { return; } // start at the end of the file... // seek backwards a bit, then look for the EoCD signature. int nTries = 0; bool success = false; // The size of the end-of-central-directory-footer plus 2 bytes is 18. // This implies an archive comment length of 0. We'll add a margin of // safety and start "in front" of that, when looking for the // EndOfCentralDirectorySignature long posn = s.Length - 64; long maxSeekback = Math.Max(s.Length - 0x4000, 10); do { if (posn < 0) { posn = 0; // BOF } s.Seek(posn, SeekOrigin.Begin); long bytesRead = SharedUtilities.FindSignature(s, (int)ZipConstants.EndOfCentralDirectorySignature); if (bytesRead != -1) { success = true; } else { if (posn == 0) { break; // started at the BOF and found nothing } nTries++; // Weird: with NETCF, negative offsets from SeekOrigin.End DO // NOT WORK. So rather than seek a negative offset, we seek // from SeekOrigin.Begin using a smaller number. posn -= (32 * (nTries + 1) * nTries); } }while (!success && posn > maxSeekback); if (success) { // workitem 8299 zf._locEndOfCDS = s.Position - 4; byte[] block = new byte[16]; s.Read(block, 0, block.Length); zf._diskNumberWithCd = BitConverter.ToUInt16(block, 2); if (zf._diskNumberWithCd == 0xFFFF) { throw new ZipException("Spanned archives with more than 65534 segments are not supported at this time."); } zf._diskNumberWithCd++; // I think the number in the file differs from reality by 1 int i = 12; uint offset32 = (uint)BitConverter.ToUInt32(block, i); if (offset32 == 0xFFFFFFFF) { Zip64SeekToCentralDirectory(zf); } else { zf._OffsetOfCentralDirectory = offset32; // change for workitem 8098 s.Seek(offset32, SeekOrigin.Begin); } ReadCentralDirectory(zf); } else { // Could not find the central directory. // Fallback to the old method. // workitem 8098: ok //s.Seek(zf._originPosition, SeekOrigin.Begin); s.Seek(0L, SeekOrigin.Begin); ReadIntoInstance_Orig(zf); } } catch (Exception ex1) { if (zf._ReadStreamIsOurs && zf._readstream != null) { try { #if NETCF zf._readstream.Close(); #else zf._readstream.Dispose(); #endif zf._readstream = null; } finally { } } throw new ZipException("Cannot read that as a ZipFile", ex1); } // the instance has been read in zf._contentsChanged = false; }