FileChecker.cs 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. namespace SevenZip
  2. {
  3. using System;
  4. using System.IO;
  5. #if UNMANAGED
  6. /// <summary>
  7. /// The signature checker class. Original code by Siddharth Uppal, adapted by Markhor.
  8. /// </summary>
  9. /// <remarks>Based on the code at http://blog.somecreativity.com/2008/04/08/how-to-check-if-a-file-is-compressed-in-c/#</remarks>
  10. internal static class FileChecker
  11. {
  12. private const int SIGNATURE_SIZE = 21;
  13. private const int SFX_SCAN_LENGTH = 256 * 1024;
  14. private static bool SpecialDetect(Stream stream, int offset, InArchiveFormat expectedFormat)
  15. {
  16. if (stream.Length > offset + SIGNATURE_SIZE)
  17. {
  18. var signature = new byte[SIGNATURE_SIZE];
  19. var bytesRequired = SIGNATURE_SIZE;
  20. var index = 0;
  21. stream.Seek(offset, SeekOrigin.Begin);
  22. while (bytesRequired > 0)
  23. {
  24. var bytesRead = stream.Read(signature, index, bytesRequired);
  25. bytesRequired -= bytesRead;
  26. index += bytesRead;
  27. }
  28. var actualSignature = BitConverter.ToString(signature);
  29. foreach (var expectedSignature in Formats.InSignatureFormats.Keys)
  30. {
  31. if (Formats.InSignatureFormats[expectedSignature] != expectedFormat)
  32. {
  33. continue;
  34. }
  35. if (actualSignature.StartsWith(expectedSignature, StringComparison.OrdinalIgnoreCase))
  36. {
  37. return true;
  38. }
  39. }
  40. }
  41. return false;
  42. }
  43. /// <summary>
  44. /// Gets the InArchiveFormat for a specific extension.
  45. /// </summary>
  46. /// <param name="stream">The stream to identify.</param>
  47. /// <param name="offset">The archive beginning offset.</param>
  48. /// <param name="isExecutable">True if the original format of the stream is PE; otherwise, false.</param>
  49. /// <returns>Corresponding InArchiveFormat.</returns>
  50. public static InArchiveFormat CheckSignature(Stream stream, out int offset, out bool isExecutable)
  51. {
  52. offset = 0;
  53. if (!stream.CanRead)
  54. {
  55. throw new ArgumentException("The stream must be readable.");
  56. }
  57. if (stream.Length < SIGNATURE_SIZE)
  58. {
  59. throw new ArgumentException("The stream is invalid.");
  60. }
  61. #region Get file signature
  62. var signature = new byte[SIGNATURE_SIZE];
  63. var bytesRequired = SIGNATURE_SIZE;
  64. var index = 0;
  65. stream.Seek(0, SeekOrigin.Begin);
  66. while (bytesRequired > 0)
  67. {
  68. var bytesRead = stream.Read(signature, index, bytesRequired);
  69. bytesRequired -= bytesRead;
  70. index += bytesRead;
  71. }
  72. var actualSignature = BitConverter.ToString(signature);
  73. #endregion
  74. var suspectedFormat = InArchiveFormat.XZ; // any except PE and Cab
  75. isExecutable = false;
  76. foreach (var expectedSignature in Formats.InSignatureFormats.Keys)
  77. {
  78. if (actualSignature.StartsWith(expectedSignature, StringComparison.OrdinalIgnoreCase) ||
  79. actualSignature.Substring(6).StartsWith(expectedSignature, StringComparison.OrdinalIgnoreCase) &&
  80. Formats.InSignatureFormats[expectedSignature] == InArchiveFormat.Lzh)
  81. {
  82. if (Formats.InSignatureFormats[expectedSignature] == InArchiveFormat.PE)
  83. {
  84. suspectedFormat = InArchiveFormat.PE;
  85. isExecutable = true;
  86. }
  87. else
  88. {
  89. return Formats.InSignatureFormats[expectedSignature];
  90. }
  91. }
  92. }
  93. // Many Microsoft formats
  94. if (actualSignature.StartsWith("D0-CF-11-E0-A1-B1-1A-E1", StringComparison.OrdinalIgnoreCase))
  95. {
  96. suspectedFormat = InArchiveFormat.Cab; // != InArchiveFormat.XZ
  97. }
  98. #region SpecialDetect
  99. try
  100. {
  101. SpecialDetect(stream, 257, InArchiveFormat.Tar);
  102. }
  103. catch (ArgumentException) {}
  104. if (SpecialDetect(stream, 0x8001, InArchiveFormat.Iso))
  105. {
  106. return InArchiveFormat.Iso;
  107. }
  108. if (SpecialDetect(stream, 0x8801, InArchiveFormat.Iso))
  109. {
  110. return InArchiveFormat.Iso;
  111. }
  112. if (SpecialDetect(stream, 0x9001, InArchiveFormat.Iso))
  113. {
  114. return InArchiveFormat.Iso;
  115. }
  116. if (SpecialDetect(stream, 0x200, InArchiveFormat.Gpt))
  117. {
  118. return InArchiveFormat.Gpt;
  119. }
  120. if (SpecialDetect(stream, 0x400, InArchiveFormat.Hfs))
  121. {
  122. return InArchiveFormat.Hfs;
  123. }
  124. #region Last resort for tar - can mistake
  125. if (stream.Length >= 1024)
  126. {
  127. stream.Seek(-1024, SeekOrigin.End);
  128. var buf = new byte[1024];
  129. stream.Read(buf, 0, 1024);
  130. var isTar = true;
  131. for (var i = 0; i < 1024; i++)
  132. {
  133. isTar = isTar && buf[i] == 0;
  134. }
  135. if (isTar)
  136. {
  137. return InArchiveFormat.Tar;
  138. }
  139. }
  140. #endregion
  141. #endregion
  142. #region Check if it is an SFX archive or a file with an embedded archive.
  143. if (suspectedFormat != InArchiveFormat.XZ)
  144. {
  145. #region Get first Min(stream.Length, SFX_SCAN_LENGTH) bytes
  146. var scanLength = Math.Min(stream.Length, SFX_SCAN_LENGTH);
  147. signature = new byte[scanLength];
  148. bytesRequired = (int)scanLength;
  149. index = 0;
  150. stream.Seek(0, SeekOrigin.Begin);
  151. while (bytesRequired > 0)
  152. {
  153. var bytesRead = stream.Read(signature, index, bytesRequired);
  154. bytesRequired -= bytesRead;
  155. index += bytesRead;
  156. }
  157. actualSignature = BitConverter.ToString(signature);
  158. #endregion
  159. foreach (var format in new[]
  160. {
  161. InArchiveFormat.Zip,
  162. InArchiveFormat.SevenZip,
  163. InArchiveFormat.Rar4,
  164. InArchiveFormat.Rar,
  165. InArchiveFormat.Cab,
  166. InArchiveFormat.Arj
  167. })
  168. {
  169. var pos = actualSignature.IndexOf(Formats.InSignatureFormatsReversed[format], StringComparison.InvariantCulture);
  170. if (pos > -1)
  171. {
  172. offset = pos / 3;
  173. return format;
  174. }
  175. }
  176. // Nothing
  177. if (suspectedFormat == InArchiveFormat.PE)
  178. {
  179. return InArchiveFormat.PE;
  180. }
  181. }
  182. #endregion
  183. throw new ArgumentException("The stream is invalid or no corresponding signature was found.");
  184. }
  185. /// <summary>
  186. /// Gets the InArchiveFormat for a specific file name.
  187. /// </summary>
  188. /// <param name="fileName">The archive file name.</param>
  189. /// <param name="offset">The archive beginning offset.</param>
  190. /// <param name="isExecutable">True if the original format of the file is PE; otherwise, false.</param>
  191. /// <returns>Corresponding InArchiveFormat.</returns>
  192. /// <exception cref="System.ArgumentException"/>
  193. public static InArchiveFormat CheckSignature(string fileName, out int offset, out bool isExecutable)
  194. {
  195. using (var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
  196. {
  197. try
  198. {
  199. return CheckSignature(fs, out offset, out isExecutable);
  200. }
  201. catch (ArgumentException)
  202. {
  203. offset = 0;
  204. isExecutable = false;
  205. return Formats.FormatByFileName(fileName, true);
  206. }
  207. }
  208. }
  209. }
  210. #endif
  211. }