SeekableZipHeaderFactory.cs 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using SharpCompress.Common.Zip.Headers;
  5. using SharpCompress.IO;
  6. using System.Text;
  7. namespace SharpCompress.Common.Zip
  8. {
  9. internal class SeekableZipHeaderFactory : ZipHeaderFactory
  10. {
  11. private const int MAX_ITERATIONS_FOR_DIRECTORY_HEADER = 4096;
  12. private bool _zip64;
  13. internal SeekableZipHeaderFactory(string password, ArchiveEncoding archiveEncoding)
  14. : base(StreamingMode.Seekable, password, archiveEncoding)
  15. {
  16. }
  17. internal IEnumerable<DirectoryEntryHeader> ReadSeekableHeader(Stream stream)
  18. {
  19. var reader = new BinaryReader(stream);
  20. SeekBackToHeader(stream, reader, DIRECTORY_END_HEADER_BYTES);
  21. var entry = new DirectoryEndHeader();
  22. entry.Read(reader);
  23. if (entry.IsZip64)
  24. {
  25. _zip64 = true;
  26. SeekBackToHeader(stream, reader, ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR);
  27. var zip64Locator = new Zip64DirectoryEndLocatorHeader();
  28. zip64Locator.Read(reader);
  29. stream.Seek(zip64Locator.RelativeOffsetOfTheEndOfDirectoryRecord, SeekOrigin.Begin);
  30. uint zip64Signature = reader.ReadUInt32();
  31. if (zip64Signature != ZIP64_END_OF_CENTRAL_DIRECTORY)
  32. throw new ArchiveException("Failed to locate the Zip64 Header");
  33. var zip64Entry = new Zip64DirectoryEndHeader();
  34. zip64Entry.Read(reader);
  35. stream.Seek(zip64Entry.DirectoryStartOffsetRelativeToDisk, SeekOrigin.Begin);
  36. }
  37. else
  38. {
  39. stream.Seek(entry.DirectoryStartOffsetRelativeToDisk, SeekOrigin.Begin);
  40. }
  41. long position = stream.Position;
  42. while (true)
  43. {
  44. stream.Position = position;
  45. uint signature = reader.ReadUInt32();
  46. var directoryEntryHeader = ReadHeader(signature, reader, _zip64) as DirectoryEntryHeader;
  47. position = stream.Position;
  48. if (directoryEntryHeader == null)
  49. {
  50. yield break;
  51. }
  52. //entry could be zero bytes so we need to know that.
  53. directoryEntryHeader.HasData = directoryEntryHeader.CompressedSize != 0;
  54. yield return directoryEntryHeader;
  55. }
  56. }
  57. private static void SeekBackToHeader(Stream stream, BinaryReader reader, uint headerSignature)
  58. {
  59. long offset = 0;
  60. uint signature;
  61. int iterationCount = 0;
  62. do
  63. {
  64. if ((stream.Length + offset) - 4 < 0)
  65. {
  66. throw new ArchiveException("Failed to locate the Zip Header");
  67. }
  68. stream.Seek(offset - 4, SeekOrigin.End);
  69. signature = reader.ReadUInt32();
  70. offset--;
  71. iterationCount++;
  72. if (iterationCount > MAX_ITERATIONS_FOR_DIRECTORY_HEADER)
  73. {
  74. throw new ArchiveException("Could not find Zip file Directory at the end of the file. File may be corrupted.");
  75. }
  76. }
  77. while (signature != headerSignature);
  78. }
  79. internal LocalEntryHeader GetLocalHeader(Stream stream, DirectoryEntryHeader directoryEntryHeader)
  80. {
  81. stream.Seek(directoryEntryHeader.RelativeOffsetOfEntryHeader, SeekOrigin.Begin);
  82. BinaryReader reader = new BinaryReader(stream);
  83. uint signature = reader.ReadUInt32();
  84. var localEntryHeader = ReadHeader(signature, reader, _zip64) as LocalEntryHeader;
  85. if (localEntryHeader == null)
  86. {
  87. throw new InvalidOperationException();
  88. }
  89. return localEntryHeader;
  90. }
  91. }
  92. }