Gzip.cpp 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. /*
  2. * Copyright (c) 2020, the SerenityOS developers.
  3. * Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #include <LibCompress/Gzip.h>
  8. #include <AK/MemoryStream.h>
  9. #include <AK/String.h>
  10. #include <LibCore/DateTime.h>
  11. namespace Compress {
  12. bool GzipDecompressor::is_likely_compressed(ReadonlyBytes bytes)
  13. {
  14. return bytes.size() >= 2 && bytes[0] == gzip_magic_1 && bytes[1] == gzip_magic_2;
  15. }
  16. bool BlockHeader::valid_magic_number() const
  17. {
  18. return identification_1 == gzip_magic_1 && identification_2 == gzip_magic_2;
  19. }
  20. bool BlockHeader::supported_by_implementation() const
  21. {
  22. if (compression_method != 0x08) {
  23. // RFC 1952 does not define any compression methods other than deflate.
  24. return false;
  25. }
  26. if (flags > Flags::MAX) {
  27. // RFC 1952 does not define any more flags.
  28. return false;
  29. }
  30. return true;
  31. }
  32. GzipDecompressor::GzipDecompressor(InputStream& stream)
  33. : m_input_stream(stream)
  34. {
  35. }
  36. GzipDecompressor::~GzipDecompressor()
  37. {
  38. m_current_member.clear();
  39. }
  40. // FIXME: Again, there are surely a ton of bugs because the code doesn't check for read errors.
  41. size_t GzipDecompressor::read(Bytes bytes)
  42. {
  43. size_t total_read = 0;
  44. while (total_read < bytes.size()) {
  45. if (has_any_error() || m_eof)
  46. break;
  47. auto slice = bytes.slice(total_read);
  48. if (m_current_member.has_value()) {
  49. size_t nread = current_member().m_stream.read(slice);
  50. current_member().m_checksum.update(slice.trim(nread));
  51. current_member().m_nread += nread;
  52. if (current_member().m_stream.handle_any_error()) {
  53. set_fatal_error();
  54. break;
  55. }
  56. if (nread < slice.size()) {
  57. LittleEndian<u32> crc32, input_size;
  58. m_input_stream >> crc32 >> input_size;
  59. if (crc32 != current_member().m_checksum.digest()) {
  60. // FIXME: Somehow the checksum is incorrect?
  61. set_fatal_error();
  62. break;
  63. }
  64. if (input_size != current_member().m_nread) {
  65. set_fatal_error();
  66. break;
  67. }
  68. m_current_member.clear();
  69. total_read += nread;
  70. continue;
  71. }
  72. total_read += nread;
  73. continue;
  74. } else {
  75. m_partial_header_offset += m_input_stream.read(Bytes { m_partial_header, sizeof(BlockHeader) }.slice(m_partial_header_offset));
  76. if (m_input_stream.handle_any_error() || m_input_stream.unreliable_eof()) {
  77. m_eof = true;
  78. break;
  79. }
  80. if (m_partial_header_offset < sizeof(BlockHeader)) {
  81. break; // partial header read
  82. }
  83. m_partial_header_offset = 0;
  84. BlockHeader header = *(reinterpret_cast<BlockHeader*>(m_partial_header));
  85. if (!header.valid_magic_number() || !header.supported_by_implementation()) {
  86. set_fatal_error();
  87. break;
  88. }
  89. if (header.flags & Flags::FEXTRA) {
  90. LittleEndian<u16> subfield_id, length;
  91. m_input_stream >> subfield_id >> length;
  92. m_input_stream.discard_or_error(length);
  93. }
  94. auto discard_string = [&]() {
  95. char next_char;
  96. do {
  97. m_input_stream >> next_char;
  98. if (m_input_stream.has_any_error()) {
  99. set_fatal_error();
  100. break;
  101. }
  102. } while (next_char);
  103. };
  104. if (header.flags & Flags::FNAME) {
  105. discard_string();
  106. if (has_any_error())
  107. break;
  108. }
  109. if (header.flags & Flags::FCOMMENT) {
  110. discard_string();
  111. if (has_any_error())
  112. break;
  113. }
  114. if (header.flags & Flags::FHCRC) {
  115. LittleEndian<u16> crc16;
  116. m_input_stream >> crc16;
  117. // FIXME: we should probably verify this instead of just assuming it matches
  118. }
  119. m_current_member.emplace(header, m_input_stream);
  120. continue;
  121. }
  122. }
  123. return total_read;
  124. }
  125. Optional<String> GzipDecompressor::describe_header(ReadonlyBytes bytes)
  126. {
  127. if (bytes.size() < sizeof(BlockHeader))
  128. return {};
  129. auto& header = *(reinterpret_cast<const BlockHeader*>(bytes.data()));
  130. if (!header.valid_magic_number() || !header.supported_by_implementation())
  131. return {};
  132. LittleEndian<u32> original_size = *reinterpret_cast<const u32*>(bytes.offset(bytes.size() - sizeof(u32)));
  133. return String::formatted("last modified: {}, original size {}", Core::DateTime::from_timestamp(header.modification_time).to_string(), (u32)original_size);
  134. }
  135. bool GzipDecompressor::read_or_error(Bytes bytes)
  136. {
  137. if (read(bytes) < bytes.size()) {
  138. set_fatal_error();
  139. return false;
  140. }
  141. return true;
  142. }
  143. bool GzipDecompressor::discard_or_error(size_t count)
  144. {
  145. u8 buffer[4096];
  146. size_t ndiscarded = 0;
  147. while (ndiscarded < count) {
  148. if (unreliable_eof()) {
  149. set_fatal_error();
  150. return false;
  151. }
  152. ndiscarded += read({ buffer, min<size_t>(count - ndiscarded, sizeof(buffer)) });
  153. }
  154. return true;
  155. }
  156. Optional<ByteBuffer> GzipDecompressor::decompress_all(ReadonlyBytes bytes)
  157. {
  158. InputMemoryStream memory_stream { bytes };
  159. GzipDecompressor gzip_stream { memory_stream };
  160. DuplexMemoryStream output_stream;
  161. u8 buffer[4096];
  162. while (!gzip_stream.has_any_error() && !gzip_stream.unreliable_eof()) {
  163. const auto nread = gzip_stream.read({ buffer, sizeof(buffer) });
  164. output_stream.write_or_error({ buffer, nread });
  165. }
  166. if (gzip_stream.handle_any_error())
  167. return {};
  168. return output_stream.copy_into_contiguous_buffer();
  169. }
  170. bool GzipDecompressor::unreliable_eof() const { return m_eof; }
  171. bool GzipDecompressor::handle_any_error()
  172. {
  173. bool handled_errors = m_input_stream.handle_any_error();
  174. return Stream::handle_any_error() || handled_errors;
  175. }
  176. GzipCompressor::GzipCompressor(OutputStream& stream)
  177. : m_output_stream(stream)
  178. {
  179. }
  180. GzipCompressor::~GzipCompressor()
  181. {
  182. }
  183. size_t GzipCompressor::write(ReadonlyBytes bytes)
  184. {
  185. BlockHeader header;
  186. header.identification_1 = 0x1f;
  187. header.identification_2 = 0x8b;
  188. header.compression_method = 0x08;
  189. header.flags = 0;
  190. header.modification_time = 0;
  191. header.extra_flags = 3; // DEFLATE sets 2 for maximum compression and 4 for minimum compression
  192. header.operating_system = 3; // unix
  193. m_output_stream << Bytes { &header, sizeof(header) };
  194. DeflateCompressor compressed_stream { m_output_stream };
  195. VERIFY(compressed_stream.write_or_error(bytes));
  196. compressed_stream.final_flush();
  197. Crypto::Checksum::CRC32 crc32;
  198. crc32.update(bytes);
  199. LittleEndian<u32> digest = crc32.digest();
  200. LittleEndian<u32> size = bytes.size();
  201. m_output_stream << digest << size;
  202. return bytes.size();
  203. }
  204. bool GzipCompressor::write_or_error(ReadonlyBytes bytes)
  205. {
  206. if (write(bytes) < bytes.size()) {
  207. set_fatal_error();
  208. return false;
  209. }
  210. return true;
  211. }
  212. Optional<ByteBuffer> GzipCompressor::compress_all(const ReadonlyBytes& bytes)
  213. {
  214. DuplexMemoryStream output_stream;
  215. GzipCompressor gzip_stream { output_stream };
  216. gzip_stream.write_or_error(bytes);
  217. if (gzip_stream.handle_any_error())
  218. return {};
  219. return output_stream.copy_into_contiguous_buffer();
  220. }
  221. }