/* 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, Boolean readUncompressedSize, Boolean readCompressedSize, Boolean readLocalHeaderOffset, Boolean 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); }
//shouldn't ever read the byte at position endExtraField //assumes we are positioned at the beginning of an extra field subfield public static Boolean TryReadBlock(BinaryReader reader, Int64 endExtraField, out ZipGenericExtraField field) { field = new ZipGenericExtraField(); //not enough bytes to read tag + size if (endExtraField - reader.BaseStream.Position < 4) { return(false); } field._tag = reader.ReadUInt16(); field._size = reader.ReadUInt16(); //not enough bytes to read the data if (endExtraField - reader.BaseStream.Position < field._size) { return(false); } field._data = reader.ReadBytes(field._size); return(true); }
static public List <ZipGenericExtraField> GetExtraFields(BinaryReader reader) { //assumes that TrySkipBlock has already been called, so we don't have to validate twice List <ZipGenericExtraField> result; const Int32 OffsetToFilenameLength = 26; //from the point before the signature reader.BaseStream.Seek(OffsetToFilenameLength, SeekOrigin.Current); UInt16 filenameLength = reader.ReadUInt16(); UInt16 extraFieldLength = reader.ReadUInt16(); reader.BaseStream.Seek(filenameLength, SeekOrigin.Current); using (Stream str = new SubReadStream(reader.BaseStream, reader.BaseStream.Position, extraFieldLength)) { result = ZipGenericExtraField.ParseExtraField(str); } Zip64ExtraField.RemoveZip64Blocks(result); return(result); }
//if saveExtraFieldsAndComments is false, FileComment and ExtraFields will be null //in either case, the zip64 extra field info will be incorporated into other fields static public Boolean TryReadBlock(BinaryReader reader, Boolean saveExtraFieldsAndComments, out ZipCentralDirectoryFileHeader header) { header = new ZipCentralDirectoryFileHeader(); if (reader.ReadUInt32() != SignatureConstant) { return(false); } header.VersionMadeBy = reader.ReadUInt16(); header.VersionNeededToExtract = reader.ReadUInt16(); header.GeneralPurposeBitFlag = reader.ReadUInt16(); header.CompressionMethod = reader.ReadUInt16(); header.LastModified = reader.ReadUInt32(); header.Crc32 = reader.ReadUInt32(); UInt32 compressedSizeSmall = reader.ReadUInt32(); UInt32 uncompressedSizeSmall = reader.ReadUInt32(); header.FilenameLength = reader.ReadUInt16(); header.ExtraFieldLength = reader.ReadUInt16(); header.FileCommentLength = reader.ReadUInt16(); UInt16 diskNumberStartSmall = reader.ReadUInt16(); header.InternalFileAttributes = reader.ReadUInt16(); header.ExternalFileAttributes = reader.ReadUInt32(); UInt32 relativeOffsetOfLocalHeaderSmall = reader.ReadUInt32(); header.Filename = reader.ReadBytes(header.FilenameLength); Boolean uncompressedSizeInZip64 = uncompressedSizeSmall == ZipHelper.Mask32Bit; Boolean compressedSizeInZip64 = compressedSizeSmall == ZipHelper.Mask32Bit; Boolean relativeOffsetInZip64 = relativeOffsetOfLocalHeaderSmall == ZipHelper.Mask32Bit; Boolean diskNumberStartInZip64 = diskNumberStartSmall == ZipHelper.Mask16Bit; Zip64ExtraField zip64; long endExtraFields = reader.BaseStream.Position + header.ExtraFieldLength; using (Stream str = new SubReadStream(reader.BaseStream, reader.BaseStream.Position, header.ExtraFieldLength)) { if (saveExtraFieldsAndComments) { header.ExtraFields = ZipGenericExtraField.ParseExtraField(str); zip64 = Zip64ExtraField.GetAndRemoveZip64Block(header.ExtraFields, uncompressedSizeInZip64, compressedSizeInZip64, relativeOffsetInZip64, diskNumberStartInZip64); } else { 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); if (saveExtraFieldsAndComments) { header.FileComment = reader.ReadBytes(header.FileCommentLength); } else { 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 Boolean TryGetZip64BlockFromGenericExtraField(ZipGenericExtraField extraField, Boolean readUncompressedSize, Boolean readCompressedSize, Boolean readLocalHeaderOffset, Boolean 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; UInt16 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 InvalidDataException(SR.FieldTooBigUncompressedSize); } if (zip64Block._compressedSize < 0) { throw new InvalidDataException(SR.FieldTooBigCompressedSize); } if (zip64Block._localHeaderOffset < 0) { throw new InvalidDataException(SR.FieldTooBigLocalHeaderOffset); } if (zip64Block._startDiskNumber < 0) { throw new InvalidDataException(SR.FieldTooBigStartDiskNumber); } return(true); } } finally { if (ms != null) { ms.Dispose(); } } }