gzip.cc 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. // Copyright (C) 2011 Michael McMaster <michael@codesrc.com>
  2. //
  3. // This file is part of libzipper.
  4. //
  5. // libzipper is free software: you can redistribute it and/or modify
  6. // it under the terms of the GNU General Public License as published by
  7. // the Free Software Foundation, either version 3 of the License, or
  8. // (at your option) any later version.
  9. //
  10. // libzipper is distributed in the hope that it will be useful,
  11. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. // GNU General Public License for more details.
  14. //
  15. // You should have received a copy of the GNU General Public License
  16. // along with libzipper. If not, see <http://www.gnu.org/licenses/>.
  17. #include "zipper.hh"
  18. #include "gzip.hh"
  19. #include "util.hh"
  20. #include "deflate.hh"
  21. #include <algorithm>
  22. #include <cassert>
  23. #include <iostream>
  24. #include <vector>
  25. #include <string.h>
  26. using namespace zipper;
  27. namespace
  28. {
  29. size_t
  30. findNull(const std::vector<uint8_t>& zipData, size_t start)
  31. {
  32. if (start >= zipData.size())
  33. {
  34. throw FormatException("Unexpected end-of-file");
  35. }
  36. while (zipData[start] != 0)
  37. {
  38. ++start;
  39. if (start >= zipData.size())
  40. {
  41. throw FormatException("Unexpected end-of-file");
  42. }
  43. }
  44. return start;
  45. }
  46. class FileEntry : public CompressedFile
  47. {
  48. public:
  49. FileEntry(
  50. const ReaderPtr& reader,
  51. zsize_t dataOffset,
  52. const std::string& filename,
  53. time_t modTime
  54. ) :
  55. m_reader(reader),
  56. m_dataOffset(dataOffset),
  57. m_fileName(filename)
  58. {
  59. m_modTime.tv_sec = modTime;
  60. m_modTime.tv_usec = 0;
  61. }
  62. virtual bool isDecompressSupported() const
  63. {
  64. return true;
  65. }
  66. virtual const std::string& getPath() const
  67. {
  68. return m_fileName;
  69. }
  70. virtual zsize_t getCompressedSize() const { return -1; }
  71. virtual zsize_t getUncompressedSize() const { return -1; }
  72. virtual const timeval& getModificationTime() const { return m_modTime; }
  73. virtual void decompress(Writer& writer)
  74. {
  75. zsize_t endCompressedBytes = m_reader->getSize() - 8; // CRC+ISIZE
  76. zsize_t inPos(m_dataOffset);
  77. zsize_t outPos(0);
  78. uint32_t crc(0);
  79. inflate(
  80. m_reader,
  81. writer,
  82. inPos,
  83. endCompressedBytes,
  84. outPos,
  85. crc);
  86. uint8_t crcBuffer[4];
  87. m_reader->readData(inPos, sizeof(crcBuffer), &crcBuffer[0]);
  88. uint32_t savedCRC = read32_le(&crcBuffer[0]);
  89. if (savedCRC != crc)
  90. {
  91. throw FormatException("Corrupt Data (CRC Failure)");
  92. }
  93. }
  94. private:
  95. ReaderPtr m_reader;
  96. zsize_t m_dataOffset;
  97. std::string m_fileName;
  98. timeval m_modTime;
  99. };
  100. }
  101. std::vector<zipper::CompressedFilePtr>
  102. zipper::ungzip(const ReaderPtr& reader)
  103. {
  104. enum
  105. {
  106. MaxHeader = 64*1024 // Artifical limit to simplify code
  107. };
  108. if (!isGzip(reader))
  109. {
  110. throw FormatException("Invalid gzip file");
  111. }
  112. std::vector<uint8_t> header(
  113. std::min(reader->getSize(), zsize_t(MaxHeader)));
  114. reader->readData(0, header.size(), &header[0]);
  115. if (header[2] != 8) // "deflate" method
  116. {
  117. throw UnsupportedException("Unknown gzip compression method");
  118. }
  119. bool fextra = (header[3] & 4) != 0;
  120. bool fname = (header[3] & 8) != 0;
  121. bool fcomment = (header[3] & 0x10) != 0;
  122. bool fhcrc = (header[3] & 2) != 0;
  123. time_t modTime = read32_le(&header[4]);
  124. size_t offset(10);
  125. if (fextra)
  126. {
  127. if (offset + 2 > header.size())
  128. {
  129. throw FormatException("Unexpected end-of-file");
  130. }
  131. uint16_t fextraBytes(read16_le(header, offset));
  132. offset += 2;
  133. offset += fextraBytes;
  134. }
  135. std::string embeddedName(reader->getSourceName());
  136. if (fname)
  137. {
  138. size_t nullOffset(findNull(header, offset));
  139. embeddedName =
  140. std::string(
  141. reinterpret_cast<char*>(&header[offset]), nullOffset - offset);
  142. offset = nullOffset + 1;
  143. }
  144. if (fcomment)
  145. {
  146. size_t nullOffset(findNull(header, offset));
  147. offset = nullOffset + 1;
  148. }
  149. if (fhcrc)
  150. {
  151. offset += 2;
  152. }
  153. if (offset >= header.size())
  154. {
  155. throw FormatException("Unexpected end-of-file");
  156. }
  157. std::vector<CompressedFilePtr> result;
  158. result.push_back(
  159. CompressedFilePtr(
  160. new FileEntry(reader, offset, embeddedName, modTime)));
  161. return result;
  162. }
  163. bool
  164. zipper::isGzip(const ReaderPtr& reader)
  165. {
  166. enum Constants
  167. {
  168. MinFileBytes = 18, // Header + CRC + size
  169. ID1 = 0x1f,
  170. ID2 = 0x8b
  171. };
  172. bool isGzip(false);
  173. if (reader->getSize() >= MinFileBytes)
  174. {
  175. uint8_t magic[2];
  176. reader->readData(0, sizeof(magic), &magic[0]);
  177. isGzip = (magic[0] == ID1) && (magic[1] == ID2);
  178. }
  179. return isGzip;
  180. }
  181. void
  182. zipper::gzip(
  183. const std::string& filename,
  184. const Reader& reader,
  185. const WriterPtr& writer)
  186. {
  187. enum Constants
  188. {
  189. ChunkSize = 64*1024,
  190. WindowBits = 15
  191. };
  192. static uint8_t Header[] =
  193. {
  194. 0x1f, 0x8b, // ID
  195. 0x08, // deflate
  196. 0x8, // Flags (filename set)
  197. 0x0, 0x0, 0x0, 0x0, // mtime
  198. 0x0, // Extra flags
  199. 0xff // OS
  200. };
  201. zsize_t outPos(writer->getSize());
  202. // Write header
  203. {
  204. uint8_t buffer[ChunkSize];
  205. memcpy(buffer, Header, sizeof(Header));
  206. write32_le(reader.getModTime().tv_sec, &buffer[4]); // modtime
  207. zsize_t pos(sizeof(Header));
  208. zsize_t filenameSize(filename.size());
  209. if (filenameSize > (ChunkSize - pos - 1))
  210. {
  211. filenameSize = ChunkSize - pos - 1;
  212. }
  213. std::copy(&filename[0], &filename[filenameSize], &buffer[pos]);
  214. pos += filenameSize;
  215. buffer[pos++] = '\0';
  216. writer->writeData(outPos, pos, &buffer[0]);
  217. outPos += pos;
  218. }
  219. // Compress data
  220. zsize_t uncompressedSize(0);
  221. zsize_t compressedSize(0);
  222. uint32_t crc(0);
  223. deflate(reader, writer, outPos, uncompressedSize, compressedSize, crc);
  224. // Write trailer.
  225. uint8_t trailer[8];
  226. write32_le(crc, &trailer[0]);
  227. write32_le(reader.getSize(), &trailer[4]);
  228. writer->writeData(outPos, sizeof(trailer), &trailer[0]);
  229. }