예제 #1
0
        internal static Zip64ExtraField GetJustZip64Block(Stream extraFieldStream,
                                                          bool readUncompressedSize, bool readCompressedSize,
                                                          bool readLocalHeaderOffset, bool readStartDiskNumber)
        {
            Zip64ExtraField zip64Field;

            using (var reader = new BinaryReader(extraFieldStream, _utf8EncodingNoBOM))
            {
                while (ZipGenericExtraField.TryReadBlock(reader, extraFieldStream.Length, out var currentExtraField))
                {
                    if (TryGetZip64BlockFromGenericExtraField(currentExtraField, readUncompressedSize,
                                                              readCompressedSize, readLocalHeaderOffset, readStartDiskNumber, out zip64Field))
                    {
                        return(zip64Field);
                    }
                }
            }

            zip64Field = new Zip64ExtraField
            {
                CompressedSize    = null,
                UncompressedSize  = null,
                LocalHeaderOffset = null,
                StartDiskNumber   = null
            };

            return(zip64Field);
        }
예제 #2
0
        // if saveExtraFieldsAndComments is false, FileComment and ExtraFields will be null
        // in either case, the zip64 extra field info will be incorporated into other fields
        internal static bool TryReadBlock(BinaryReader reader, out ZipCentralDirectoryFileHeader header)
        {
            header = new ZipCentralDirectoryFileHeader();

            if (reader.ReadUInt32() != SignatureConstant)
            {
                return(false);
            }

            reader.ReadByte();   // VersionMadeBySpecification
            header.VersionMadeByCompatibility = reader.ReadByte();
            reader.ReadUInt16(); // VersionNeededToExtract
            header.GeneralPurposeBitFlag = reader.ReadUInt16();
            header.CompressionMethod     = reader.ReadUInt16();
            header.LastModified          = reader.ReadUInt32();
            reader.ReadUInt32(); // Crc32
            uint compressedSizeSmall   = reader.ReadUInt32();
            uint uncompressedSizeSmall = reader.ReadUInt32();

            header.FilenameLength    = reader.ReadUInt16();
            header.ExtraFieldLength  = reader.ReadUInt16();
            header.FileCommentLength = reader.ReadUInt16();
            ushort diskNumberStartSmall = reader.ReadUInt16();

            reader.ReadUInt16(); // InternalFileAttributes
            reader.ReadUInt32(); // ExternalFileAttributes
            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 SubReadStream(reader.BaseStream, reader.BaseStream.Position, header.ExtraFieldLength))
            {
                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.

            // Fen's note: Original did a seek here, which for some reason is like 300x slower than a read, and
            // also inexplicably causes ReadUInt32() to be 4x as slow and/or occur 4x as often(?!)
            // Buffer alignments...? I dunno. Anyway. Speed.
            // Also maybe not a good idea to use something that's faster when I don't know why it's faster.
            // But my results are the same as the old method, so herpaderp.
            reader.BaseStream.AdvanceToPosition(endExtraFields + header.FileCommentLength);

            header.UncompressedSize            = zip64.UncompressedSize ?? uncompressedSizeSmall;
            header.CompressedSize              = zip64.CompressedSize ?? compressedSizeSmall;
            header.RelativeOffsetOfLocalHeader = zip64.LocalHeaderOffset ?? relativeOffsetOfLocalHeaderSmall;
            header.DiskNumberStart             = zip64.StartDiskNumber ?? diskNumberStartSmall;

            return(true);
        }
예제 #3
0
        private static bool TryGetZip64BlockFromGenericExtraField(ZipGenericExtraField extraField,
                                                                  bool readUncompressedSize, bool readCompressedSize,
                                                                  bool readLocalHeaderOffset, bool readStartDiskNumber,
                                                                  out Zip64ExtraField zip64Block)
        {
            zip64Block = new Zip64ExtraField
            {
                CompressedSize    = null,
                UncompressedSize  = null,
                LocalHeaderOffset = null,
                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 var reader = new BinaryReader(ms);

                // Why did they do this and how does it still work?!
                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 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
            {
                ms?.Dispose();
            }
        }