| File: Signing\Archive\EndOfCentralDirectoryRecord.cs | Web Access |
| Project: src\nuget-client\src\NuGet.Core\NuGet.Packaging\NuGet.Packaging.csproj (NuGet.Packaging) |
// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; // ZIP specification: http://www.pkware.com/documents/casestudies/APPNOTE.TXT namespace NuGet.Packaging.Signing { internal sealed class EndOfCentralDirectoryRecord { private static readonly IReadOnlyList<byte> Signature = new byte[4] { 0x50, 0x4b, 0x05, 0x06 }; internal ushort NumberOfThisDisk { get; private set; } internal ushort NumberOfTheDiskWithTheStartOfTheCentralDirectory { get; private set; } internal ushort CountOfEntriesInCentralDirectoryOnThisDisk { get; private set; } internal ushort CountOfEntriesInCentralDirectory { get; private set; } internal uint SizeOfCentralDirectory { get; private set; } internal uint OffsetOfStartOfCentralDirectory { get; private set; } internal ushort FileCommentLength { get; private set; } internal byte[] FileComment { get; private set; } = null!; // Set to non-null in the static Read method. // This property is not part of the ZIP specification. internal long OffsetFromStart { get; private set; } internal static EndOfCentralDirectoryRecord Read(BinaryReader reader) { SeekToEndOfCentralDirectoryRecord(reader); var header = new EndOfCentralDirectoryRecord(); header.OffsetFromStart = reader.BaseStream.Position; reader.ReadUInt32(); // Read and discard the byte signature. header.NumberOfThisDisk = reader.ReadUInt16(); header.NumberOfTheDiskWithTheStartOfTheCentralDirectory = reader.ReadUInt16(); header.CountOfEntriesInCentralDirectoryOnThisDisk = reader.ReadUInt16(); header.CountOfEntriesInCentralDirectory = reader.ReadUInt16(); header.SizeOfCentralDirectory = reader.ReadUInt32(); header.OffsetOfStartOfCentralDirectory = reader.ReadUInt32(); header.FileCommentLength = reader.ReadUInt16(); header.FileComment = reader.ReadBytes(header.FileCommentLength); return header; } private static void SeekToEndOfCentralDirectoryRecord(BinaryReader reader) { var length = reader.BaseStream.Length; if (length < Signature.Count) { ThrowByteSignatureNotFoundException(Signature); } const int DefaultBufferSize = 4096; var bufferSize = (int)Math.Min(length, DefaultBufferSize); var matchingByteCount = 0; // Read backwards from the end of stream in adjacent, non-overlapping chunks of size "bufferSize", // and search backwards for the byte signature. // Note: position is 0-based, while bufferSize is 1-based. for (var position = length - bufferSize; position >= 0; position -= bufferSize) { reader.BaseStream.Position = position; var buffer = reader.ReadBytes(bufferSize); for (var i = buffer.Length - 1; i >= 0; --i) { if (buffer[i] == Signature[Signature.Count - matchingByteCount - 1]) { ++matchingByteCount; if (matchingByteCount == Signature.Count) { reader.BaseStream.Position = position + i; return; } } else { matchingByteCount = 0; } } bufferSize = (int)Math.Min(position, bufferSize); if (bufferSize == 0) { break; } } ThrowByteSignatureNotFoundException(Signature); } private static void ThrowByteSignatureNotFoundException(IReadOnlyList<byte> signature) { throw new InvalidDataException( string.Format(CultureInfo.CurrentCulture, Strings.ErrorByteSignatureNotFound, #if NETCOREAPP BitConverter.ToString(signature.ToArray()).Replace("-", "", StringComparison.Ordinal))); #else BitConverter.ToString(signature.ToArray()).Replace("-", ""))); #endif } } }