// There is a small chance that something very weird could happen here. The code calling into this function // will ask for a value from the extra field if the field was masked with FF's. It's theoretically possible // that a field was FF's legitimately, and the writer didn't decide to write the corresponding extra field. // Also, at the same time, other fields were masked with FF's to indicate looking in the zip64 record. // Then, the search for the zip64 record will fail because the expected size is wrong, // and a nulled out Zip64ExtraField will be returned. Thus, even though there was Zip64 data, // it will not be used. It is questionable whether this situation is possible to detect // unlike the other functions that have try-pattern semantics, these functions always return a // Zip64ExtraField. If a Zip64 extra field actually doesn't exist, all of the fields in the // returned struct will be null // // If there are more than one Zip64 extra fields, we take the first one that has the expected size // public static Zip64ExtraField GetJustZip64Block(Stream extraFieldStream, bool readUncompressedSize, bool readCompressedSize, bool readLocalHeaderOffset, bool readStartDiskNumber) { Zip64ExtraField zip64Field; using (BinaryReader reader = new BinaryReader(extraFieldStream)) { ZipGenericExtraField currentExtraField; while (ZipGenericExtraField.TryReadBlock(reader, extraFieldStream.Length, out currentExtraField)) { if (TryGetZip64BlockFromGenericExtraField(currentExtraField, readUncompressedSize, readCompressedSize, readLocalHeaderOffset, readStartDiskNumber, out zip64Field)) { return(zip64Field); } } } zip64Field = new Zip64ExtraField(); zip64Field._compressedSize = null; zip64Field._uncompressedSize = null; zip64Field._localHeaderOffset = null; zip64Field._startDiskNumber = null; return(zip64Field); }
// if saveExtraFieldsAndComments is false, FileComment and ExtraFields will be null // in either case, the zip64 extra field info will be incorporated into other fields public static bool TryReadBlock(BinaryReader reader, out ZipCentralDirectoryFileHeader header) { header = new ZipCentralDirectoryFileHeader(); if (reader.ReadUInt32() != SignatureConstant) { return(false); } header.VersionMadeBySpecification = reader.ReadByte(); header.VersionMadeByCompatibility = reader.ReadByte(); header.VersionNeededToExtract = reader.ReadUInt16(); header.GeneralPurposeBitFlag = reader.ReadUInt16(); header.CompressionMethod = reader.ReadUInt16(); header.LastModified = reader.ReadUInt32(); header.Crc32 = reader.ReadUInt32(); uint compressedSizeSmall = reader.ReadUInt32(); uint uncompressedSizeSmall = reader.ReadUInt32(); header.FilenameLength = reader.ReadUInt16(); header.ExtraFieldLength = reader.ReadUInt16(); header.FileCommentLength = reader.ReadUInt16(); ushort diskNumberStartSmall = reader.ReadUInt16(); header.InternalFileAttributes = reader.ReadUInt16(); header.ExternalFileAttributes = reader.ReadUInt32(); uint relativeOffsetOfLocalHeaderSmall = reader.ReadUInt32(); header.Filename = reader.ReadBytes(header.FilenameLength); bool uncompressedSizeInZip64 = uncompressedSizeSmall == ZipHelper.Mask32Bit; bool compressedSizeInZip64 = compressedSizeSmall == ZipHelper.Mask32Bit; bool relativeOffsetInZip64 = relativeOffsetOfLocalHeaderSmall == ZipHelper.Mask32Bit; bool diskNumberStartInZip64 = diskNumberStartSmall == ZipHelper.Mask16Bit; Zip64ExtraField zip64; long endExtraFields = reader.BaseStream.Position + header.ExtraFieldLength; using (Stream str = new SubReadOnlyStream(reader.BaseStream, reader.BaseStream.Position, header.ExtraFieldLength, leaveOpen: true)) { header.ExtraFields = null; zip64 = Zip64ExtraField.GetJustZip64Block(str, uncompressedSizeInZip64, compressedSizeInZip64, relativeOffsetInZip64, diskNumberStartInZip64); } // There are zip files that have malformed ExtraField blocks in which GetJustZip64Block() silently bails out without reading all the way to the end // of the ExtraField block. Thus we must force the stream's position to the proper place. reader.BaseStream.AdvanceToPosition(endExtraFields); reader.BaseStream.Position += header.FileCommentLength; header.FileComment = null; header.UncompressedSize = zip64.UncompressedSize == null ? uncompressedSizeSmall : zip64.UncompressedSize.Value; header.CompressedSize = zip64.CompressedSize == null ? compressedSizeSmall : zip64.CompressedSize.Value; header.RelativeOffsetOfLocalHeader = zip64.LocalHeaderOffset == null ? relativeOffsetOfLocalHeaderSmall : zip64.LocalHeaderOffset.Value; header.DiskNumberStart = zip64.StartDiskNumber == null ? diskNumberStartSmall : zip64.StartDiskNumber.Value; return(true); }
private static bool TryGetZip64BlockFromGenericExtraField(ZipGenericExtraField extraField, bool readUncompressedSize, bool readCompressedSize, bool readLocalHeaderOffset, bool readStartDiskNumber, out Zip64ExtraField zip64Block) { zip64Block = new Zip64ExtraField(); zip64Block._compressedSize = null; zip64Block._uncompressedSize = null; zip64Block._localHeaderOffset = null; zip64Block._startDiskNumber = null; if (extraField.Tag != TagConstant) { return(false); } // this pattern needed because nested using blocks trigger CA2202 MemoryStream ms = null; try { ms = new MemoryStream(extraField.Data); using (BinaryReader reader = new BinaryReader(ms)) { ms = null; zip64Block._size = extraField.Size; ushort expectedSize = 0; if (readUncompressedSize) { expectedSize += 8; } if (readCompressedSize) { expectedSize += 8; } if (readLocalHeaderOffset) { expectedSize += 8; } if (readStartDiskNumber) { expectedSize += 4; } // if it is not the expected size, perhaps there is another extra field that matches if (expectedSize != zip64Block._size) { return(false); } if (readUncompressedSize) { zip64Block._uncompressedSize = reader.ReadInt64(); } if (readCompressedSize) { zip64Block._compressedSize = reader.ReadInt64(); } if (readLocalHeaderOffset) { zip64Block._localHeaderOffset = reader.ReadInt64(); } if (readStartDiskNumber) { zip64Block._startDiskNumber = reader.ReadInt32(); } // original values are unsigned, so implies value is too big to fit in signed integer if (zip64Block._uncompressedSize < 0) { throw new ZipArchiveException("FieldTooBigUncompressedSize"); } if (zip64Block._compressedSize < 0) { throw new ZipArchiveException("FieldTooBigCompressedSize"); } if (zip64Block._localHeaderOffset < 0) { throw new ZipArchiveException("FieldTooBigLocalHeaderOffset"); } if (zip64Block._startDiskNumber < 0) { throw new ZipArchiveException("FieldTooBigStartDiskNumber"); } return(true); } } finally { if (ms != null) { ms.Dispose(); } } }
private static bool TryGetZip64BlockFromGenericExtraField(ZipGenericExtraField extraField, bool readUncompressedSize, bool readCompressedSize, bool readLocalHeaderOffset, bool readStartDiskNumber, out Zip64ExtraField zip64Block) { zip64Block = new Zip64ExtraField(); zip64Block._compressedSize = null; zip64Block._uncompressedSize = null; zip64Block._localHeaderOffset = null; zip64Block._startDiskNumber = null; if (extraField.Tag != TagConstant) { return(false); } MemoryStream ms = null; try { ms = new MemoryStream(extraField.Data); using (BinaryReader reader = new BinaryReader(ms)) { ms = null; zip64Block._size = extraField.Size; ushort expectedSize = 0; if (readUncompressedSize) { expectedSize += 8; } if (readCompressedSize) { expectedSize += 8; } if (readLocalHeaderOffset) { expectedSize += 8; } if (readStartDiskNumber) { expectedSize += 4; } if (expectedSize != zip64Block._size) { return(false); } if (readUncompressedSize) { zip64Block._uncompressedSize = reader.ReadInt64(); } if (readCompressedSize) { zip64Block._compressedSize = reader.ReadInt64(); } if (readLocalHeaderOffset) { zip64Block._localHeaderOffset = reader.ReadInt64(); } if (readStartDiskNumber) { zip64Block._startDiskNumber = reader.ReadInt32(); } if (zip64Block._uncompressedSize < 0) { throw new ZipArchiveException("FieldTooBigUncompressedSize"); } if (zip64Block._compressedSize < 0) { throw new ZipArchiveException("FieldTooBigCompressedSize"); } if (zip64Block._localHeaderOffset < 0) { throw new ZipArchiveException("FieldTooBigLocalHeaderOffset"); } if (zip64Block._startDiskNumber < 0) { throw new ZipArchiveException("FieldTooBigStartDiskNumber"); } return(true); } } finally { if (ms != null) { ms.Dispose(); } } }