private static bool ReadHeader(ZipEntry ze, System.Text.Encoding defaultEncoding) { int bytesRead = 0; ze._RelativeOffsetOfHeader = (int)ze.ArchiveStream.Position; int signature = Zip.SharedUtilities.ReadSignature(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, System.IO.SeekOrigin.Current); // unread the signature if (ZipDirEntry.IsNotValidSig(signature) && (signature != ZipConstants.EndOfCentralDirectorySignature)) { throw new BadReadException(String.Format(" ZipEntry::ReadHeader(): 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 = (short)(block[i++] + block[i++] * 256); ze._BitField = (short)(block[i++] + block[i++] * 256); ze._CompressionMethod = (short)(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 = Zip.SharedUtilities.PackedToDateTime(ze._TimeBlob); // The PKZIP spec says that if bit 3 is set (0x0008) in the General Purpose BitField, then the CRC, // Compressed size, and uncompressed size come directly after the file data. The only way to find // it is to scan the zip archive for the signature of the Data Descriptor, and presume that that // signature does not appear in the (compressed) data of the compressed file. if ((ze._BitField & 0x0008) != 0x0008) { ze._Crc32 = (Int32)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); ze._CompressedSize = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256; ze._UncompressedSize = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256; } else { // The CRC, compressed size, and uncompressed size are stored later in the stream. // Here, we advance the pointer. i += 12; } 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 ((ze._BitField & 0x0800) == 0x0800) { ze._FileNameInArchive = Zip.SharedUtilities.StringFromBuffer(block, block.Length, System.Text.Encoding.UTF8); ze.UseUtf8Encoding = true; } else { ze._FileNameInArchive = Zip.SharedUtilities.StringFromBuffer(block, block.Length, defaultEncoding); ze._encoding = defaultEncoding; } // when creating an entry by reading, the LocalFileName is the same as the FileNameInArchivre ze._LocalFileName = ze._FileNameInArchive; if (extraFieldLength > 0) { ze._Extra = new byte[extraFieldLength]; n = ze.ArchiveStream.Read(ze._Extra, 0, ze._Extra.Length); bytesRead += n; } // workitem 6607: don't seek if it is a directory. if (!ze.FileName.EndsWith("/")) // actually get the compressed size and CRC if necessary if ((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. // 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. 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. There are two possibilities: either we find the // signature or we don't. If we cannot find it, 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. long d = Zip.SharedUtilities.FindSignature(ze.ArchiveStream, ZipConstants.ZipEntryDataDescriptorSignature); if (d == -1) return false; // total size of data read (through all loops of this). SizeOfDataRead += d; // 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; bytesRead += n; i = 0; ze._Crc32 = (Int32)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); ze._CompressedSize = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256; ze._UncompressedSize = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256; 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, System.IO.SeekOrigin.Current); // Adjust the size to account for the false signature read in // FindSignature(). SizeOfDataRead += 4; } } //if (SizeOfDataRead != ze._CompressedSize) // throw new BadReadException("Data format error (bit 3 is set)"); // seek back to previous position, to prepare to read file data ze.ArchiveStream.Seek(posn, System.IO.SeekOrigin.Begin); } ze._CompressedFileDataSize = ze._CompressedSize; if ((ze._BitField & 0x01) == 0x01) { // PKZIP encrypts the compressed data stream. Encrypted files must // be decrypted before they can be extracted. // Each encrypted file has an extra 12 bytes stored at the start of // the data area defining the encryption header for that file. The // encryption header is originally set to random values, and then // itself encrypted, using three, 32-bit keys. The key values are // initialized using the supplied encryption password. After each byte // is encrypted, the keys are then updated using pseudo-random number // generation techniques in combination with the same CRC-32 algorithm // used in PKZIP and described elsewhere in this document. ze._Encryption = EncryptionAlgorithm.PkzipWeak; // read the 12-byte encryption header ze._WeakEncryptionHeader = new byte[12]; n = ze.ArchiveStream.Read(ze._WeakEncryptionHeader, 0, 12); if (n != 12) return false; bytesRead += n; // 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._TotalEntrySize = bytesRead + ze._CompressedFileDataSize; ze._LengthOfHeader = bytesRead; // The pointer in the file is now at the start of the filedata, // which is potentially compressed and encrypted. return true; }
private static void HandleUnexpectedDataDescriptor(ZipEntry entry) { System.IO.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)Zip.SharedUtilities.ReadInt(s); if (datum == entry._Crc32) { int sz = Zip.SharedUtilities.ReadInt(s); if (sz == entry._CompressedSize) { sz = Zip.SharedUtilities.ReadInt(s); if (sz == entry._UncompressedSize) { // ignore everything and discard it. } else s.Seek(-12, System.IO.SeekOrigin.Current); // unread the three blocks } else s.Seek(-8, System.IO.SeekOrigin.Current); // unread the two blocks } else s.Seek(-4, System.IO.SeekOrigin.Current); // unread the block }
/// <summary> /// Reads one ZipEntry from the given stream. If the entry is encrypted, we don't /// actuall decrypt at this point. /// </summary> /// <param name="s">the stream to read from.</param> /// <param name="defaultEncoding"> /// The text encoding to use when reading the ZipEntry, if it is not marked as UTF-8. /// </param> /// <returns>the ZipEntry read from the stream.</returns> internal static ZipEntry Read(System.IO.Stream s, System.Text.Encoding defaultEncoding) { ZipEntry entry = new ZipEntry(); entry._Source = EntrySource.Zipfile; entry._archiveStream = s; if (!ReadHeader(entry, defaultEncoding)) return null; // store the position in the stream for this entry entry.__FileDataPosition = entry.ArchiveStream.Position; // seek past the data without reading it. We will read on Extract() s.Seek(entry._CompressedFileDataSize, System.IO.SeekOrigin.Current); // workitem 6607: don't seek if it is a directory. if (!entry.FileName.EndsWith("/")) // finally, seek past the (already read) Data descriptor if necessary if ((entry._BitField & 0x0008) == 0x0008) { s.Seek(16, System.IO.SeekOrigin.Current); } // workitem 5306 // http://www.codeplex.com/DotNetZip/WorkItem/View.aspx?WorkItemId=5306 HandleUnexpectedDataDescriptor(entry); return entry; }
internal void CopyMetaData(ZipEntry source) { this.__FileDataPosition = source.__FileDataPosition; this.CompressionMethod = source.CompressionMethod; this._CompressedFileDataSize = source._CompressedFileDataSize; this._UncompressedSize = source._UncompressedSize; this._BitField = source._BitField; this._LastModified = source._LastModified; }
internal static ZipEntry Create(String filename, string nameInArchive, System.IO.Stream stream) { if (String.IsNullOrEmpty(filename)) throw new Zip.ZipException("The entry name must be non-null and non-empty."); ZipEntry entry = new ZipEntry(); if (stream != null) { entry._sourceStream = stream; entry._LastModified = DateTime.Now; } else { entry._LastModified = (System.IO.File.Exists(filename) || System.IO.Directory.Exists(filename)) ? SharedUtilities.RoundToEvenSecond(System.IO.File.GetLastWriteTime(filename)) : DateTime.Now; if (!entry._LastModified.IsDaylightSavingTime() && DateTime.Now.IsDaylightSavingTime()) { entry._LastModified = entry._LastModified + new System.TimeSpan(1, 0, 0); } if (entry._LastModified.IsDaylightSavingTime() && !DateTime.Now.IsDaylightSavingTime()) { entry._LastModified = entry._LastModified - new System.TimeSpan(1, 0, 0); } } entry._LocalFileName = filename; // may include a path entry._FileNameInArchive = nameInArchive.Replace('\\', '/'); // we don't actually slurp in the file until the caller invokes Write on this entry. return entry; }