private List <Zip64LocalFileDataField> ReadZip64DataFields(LocalFileHeader entry) { // The Zip64 data field in the local file header is a subset of the information in the central directory // header, so use the central directory flow and map the input/output. var centralDirectoryHeader = new CentralDirectoryHeader { DataFields = entry.DataFields, UncompressedSize = entry.UncompressedSize, CompressedSize = entry.CompressedSize, }; var fields = ReadZip64DataFields(centralDirectoryHeader, ZipConstants.MaximumZip64LocalFileDataFieldSize); return(fields .Select(x => new Zip64LocalFileDataField { CompressedSize = x.CompressedSize, UncompressedSize = x.UncompressedSize, }) .ToList()); }
private async Task <DataDescriptor> ReadDataDescriptor( ZipDirectory directory, List <CentralDirectoryHeader> entries, CentralDirectoryHeader entry, Zip64DataField zip64, LocalFileHeader localEntry) { var compressedSize = zip64?.CompressedSize ?? entry.CompressedSize; var uncompressedSize = zip64?.UncompressedSize ?? entry.UncompressedSize; var dataDescriptorPosition = entry.LocalHeaderOffset + ZipConstants.LocalFileHeaderSize + localEntry.NameSize + localEntry.ExtraFieldSize + compressedSize; _stream.Position = (long)dataDescriptorPosition; var dataDescriptor = new DataDescriptor(); using (var buffer = await LoadBinaryReaderAsync(_buffer, ZipConstants.DataDescriptorSize)) { var fieldA = buffer.ReadUInt32(); var fieldB = buffer.ReadUInt32(); var fieldC = buffer.ReadUInt32(); var fieldD = buffer.ReadUInt32(); // Check the first field to see if is the signature. This is the most reliable check but can yield // false negatives. This is because it's possible for the CRC-32 to be the same as the optional // data descriptor signature. There is no possibility for false positive. That is, if the first // byte does not match signature, then the first byte is definitely the CRC-32. var firstFieldImpliesNoSignature = fieldA != ZipConstants.DataDescriptorSignature; // 1. Use the known field values from the central directory, given the first field matches the signature. var valuesImplySignature = !firstFieldImpliesNoSignature && fieldB == entry.Crc32 && fieldC == compressedSize && fieldD == uncompressedSize; if (valuesImplySignature) { return(CreateDataDescriptor(fieldB, fieldC, fieldD, hasSignature: true)); } // 2. Use the known field values from the central directory. var valuesImplyNoSignature = fieldA == entry.Crc32 && fieldB == compressedSize && fieldC == uncompressedSize; if (valuesImplyNoSignature) { return(CreateDataDescriptor(fieldA, fieldB, fieldC, hasSignature: false)); } // 3. Use just the signature. if (!firstFieldImpliesNoSignature) { return(CreateDataDescriptor(fieldB, fieldC, fieldD, hasSignature: true)); } return(CreateDataDescriptor(fieldA, fieldB, fieldC, hasSignature: false)); } }
/// <summary> /// Reads a specific local file header given a central directory header for a file. /// </summary> /// <param name="directory">The ZIP directory instance containing the provided <paramref name="entry"/>.</param> /// <param name="entry">The central directory header to read the local file header for.</param> /// <param name="readDataDescriptor">Whether or not to read the data descriptor, if present.</param> /// <returns>The local file header.</returns> public async Task <LocalFileHeader> ReadLocalFileHeaderAsync(ZipDirectory directory, CentralDirectoryHeader entry, bool readDataDescriptor) { if (directory == null) { throw new ArgumentNullException(nameof(directory)); } if (entry == null) { throw new ArgumentNullException(nameof(entry)); } var entries = directory.Entries ?? new List <CentralDirectoryHeader>(); if (!entries.Contains(entry)) { throw new ArgumentException(Strings.HeaderDoesNotMatchDirectory, nameof(entry)); } if ((entry.Flags & (ushort)ZipEntryFlags.Encrypted) != 0) { throw new MiniZipException(Strings.EncryptedFilesNotSupported); } var zip64 = entry.Zip64DataFields.FirstOrDefault(); var localHeaderOffset = zip64?.LocalHeaderOffset ?? entry.LocalHeaderOffset; _stream.Position = (long)localHeaderOffset; if (await ReadUInt32Async() != ZipConstants.LocalFileHeaderSignature) { throw new MiniZipException(Strings.InvalidLocalFileHeaderSignature); } var localEntry = new LocalFileHeader(); using (var buffer = await LoadBinaryReaderAsync(_buffer, ZipConstants.LocalFileHeaderSizeWithoutSignature)) { localEntry.VersionToExtract = buffer.ReadUInt16(); localEntry.Flags = buffer.ReadUInt16(); localEntry.CompressionMethod = buffer.ReadUInt16(); localEntry.LastModifiedTime = buffer.ReadUInt16(); localEntry.LastModifiedDate = buffer.ReadUInt16(); localEntry.Crc32 = buffer.ReadUInt32(); localEntry.CompressedSize = buffer.ReadUInt32(); localEntry.UncompressedSize = buffer.ReadUInt32(); localEntry.NameSize = buffer.ReadUInt16(); localEntry.ExtraFieldSize = buffer.ReadUInt16(); } localEntry.Name = new byte[localEntry.NameSize]; await ReadFullyAsync(localEntry.Name); localEntry.ExtraField = new byte[localEntry.ExtraFieldSize]; await ReadFullyAsync(localEntry.ExtraField); localEntry.DataFields = ReadDataFields(localEntry.ExtraField); localEntry.Zip64DataFields = ReadZip64DataFields(localEntry); // Try to read the data descriptor. if (readDataDescriptor && (localEntry.Flags & (ushort)ZipEntryFlags.DataDescriptor) != 0) { localEntry.DataDescriptor = await ReadDataDescriptor(directory, entries, entry, zip64, localEntry); } return(localEntry); }