PageRenderTime 51ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/mordor/zip.cpp

http://github.com/mozy/mordor
C++ | 819 lines | 771 code | 35 blank | 13 comment | 185 complexity | 42d354a397b42449d2e0e3bed40948e6 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. // Copyright (c) 2010 - Mozy, Inc.
  2. #include "zip.h"
  3. #include "mordor/assert.h"
  4. #include "mordor/log.h"
  5. #include "mordor/streams/buffered.h"
  6. #include "mordor/streams/deflate.h"
  7. #include "mordor/streams/hash.h"
  8. #include "mordor/streams/limited.h"
  9. #include "mordor/streams/notify.h"
  10. #include "mordor/streams/null.h"
  11. #include "mordor/streams/singleplex.h"
  12. #include "mordor/streams/transfer.h"
  13. namespace {
  14. #pragma pack(push)
  15. #pragma pack(1)
  16. struct LocalFileHeader
  17. {
  18. unsigned int signature; // 0x04034b50
  19. unsigned short extractVersion;
  20. unsigned short generalPurposeFlags;
  21. unsigned short compressionMethod;
  22. unsigned short modifiedTime;
  23. unsigned short modifiedDate;
  24. unsigned int crc32;
  25. unsigned int compressedSize;
  26. unsigned int decompressedSize;
  27. unsigned short fileNameLength;
  28. unsigned short extraFieldLength;
  29. //char FileName[FileNameLength];
  30. // ExtraField[ExtraFieldLength]; // Array of ExtraField structures; length is in bytes
  31. };
  32. struct DataDescriptor
  33. {
  34. unsigned int signature;
  35. unsigned int crc32;
  36. unsigned int compressedSize;
  37. unsigned int uncompressedSize;
  38. };
  39. struct DataDescriptor64
  40. {
  41. unsigned int signature;
  42. unsigned int crc32;
  43. long long compressedSize;
  44. long long uncompressedSize;
  45. };
  46. struct FileHeader
  47. {
  48. unsigned int signature;
  49. unsigned short versionMadeBy;
  50. unsigned short extractVersion;
  51. unsigned short generalPurposeFlags;
  52. unsigned short compressionMethod;
  53. unsigned short modifiedTime;
  54. unsigned short modifiedDate;
  55. unsigned int crc32;
  56. unsigned int compressedSize;
  57. unsigned int uncompressedSize;
  58. unsigned short fileNameLength;
  59. unsigned short extraFieldLength;
  60. unsigned short fileCommentLength;
  61. unsigned short diskNumberStart;
  62. unsigned short internalFileAttributes;
  63. unsigned int externalFileAttributes;
  64. unsigned int localHeaderOffset;
  65. // char fileName[fileNameLength];
  66. // char extraField[extraFieldLength];
  67. // char fileComment[fileCommentLength];
  68. };
  69. struct Zip64EndOfCentralDirectory
  70. {
  71. unsigned int signature;
  72. unsigned long long sizeOfEndOfCentralDirectory;
  73. unsigned short versionMadeBy;
  74. unsigned short extractVersion;
  75. unsigned int numberOfThisDisk;
  76. unsigned int startOfCentralDirectoryDisk;
  77. unsigned long long centralDirectoryEntriesThisDisk;
  78. unsigned long long centralDirectoryEntries;
  79. unsigned long long sizeOfCentralDirectory;
  80. long long startOfCentralDirectoryOffset;
  81. };
  82. struct Zip64EndOfCentralDirectoryLocator
  83. {
  84. unsigned int signature;
  85. unsigned int endOfCentralDirectoryDisk;
  86. long long endOfCentralDirectoryOffset;
  87. unsigned int totalDisks;
  88. };
  89. struct EndOfCentralDirectory
  90. {
  91. unsigned int signature;
  92. unsigned short numberOfThisDisk;
  93. unsigned short startOfCentralDirectoryDisk;
  94. unsigned short centralDirectoryEntriesThisDisk;
  95. unsigned short centralDirectoryEntries;
  96. unsigned int sizeOfCentralDirectory;
  97. unsigned int startOfCentralDirectoryOffset;
  98. unsigned short commentLength;
  99. };
  100. #pragma pack(pop)
  101. }
  102. namespace Mordor {
  103. Zip::Zip(Stream::ptr stream, OpenMode mode)
  104. : m_stream(stream),
  105. m_currentFile(NULL),
  106. #ifdef MSVC
  107. #pragma warning(push)
  108. #pragma warning(disable: 4355)
  109. #endif
  110. m_scratchFile(*this)
  111. #ifdef MSVC
  112. #pragma warning(pop)
  113. #endif
  114. {
  115. if (!m_stream->supportsTell())
  116. m_stream.reset(new LimitedStream(m_stream,
  117. 0x7fffffffffffffffll));
  118. m_stream.reset(new BufferedStream(m_stream));
  119. if (mode == INFER) {
  120. if (m_stream->supportsWrite())
  121. mode = WRITE;
  122. else
  123. mode = READ;
  124. }
  125. switch (mode) {
  126. case READ:
  127. MORDOR_ASSERT(m_stream->supportsRead());
  128. if (m_stream->supportsSeek() && m_stream->supportsSize()) {
  129. unsigned char buffer[65536];
  130. long long fileSize = m_stream->size();
  131. if (fileSize < (long long)sizeof(EndOfCentralDirectory))
  132. MORDOR_THROW_EXCEPTION(CorruptZipException());
  133. size_t toRead = (size_t)(std::min)(65536ll, fileSize);
  134. m_stream->seek(-(long long)toRead, Stream::END);
  135. m_stream->read(buffer, toRead);
  136. unsigned char *search = buffer + toRead -
  137. sizeof(EndOfCentralDirectory);
  138. while (search >= buffer) {
  139. if (*(unsigned int *)search == 0x06054b50)
  140. break;
  141. --search;
  142. }
  143. if (search < buffer)
  144. MORDOR_THROW_EXCEPTION(CorruptZipException());
  145. EndOfCentralDirectory &eocd = *(EndOfCentralDirectory *)search;
  146. if (eocd.numberOfThisDisk != eocd.startOfCentralDirectoryDisk)
  147. MORDOR_THROW_EXCEPTION(SpannedZipNotSupportedException());
  148. // Look for zip64 extensions
  149. search -= sizeof(Zip64EndOfCentralDirectoryLocator);
  150. long long startOfCentralDirectory;
  151. unsigned long long entries = 0;
  152. if (search >= buffer && *(unsigned int*)search == 0x07064b50) {
  153. Zip64EndOfCentralDirectoryLocator &z64eocdl =
  154. *(Zip64EndOfCentralDirectoryLocator *)search;
  155. if (z64eocdl.totalDisks > 1)
  156. MORDOR_THROW_EXCEPTION(SpannedZipNotSupportedException());
  157. m_stream->seek(z64eocdl.endOfCentralDirectoryOffset);
  158. Zip64EndOfCentralDirectory z64eocd;
  159. if (m_stream->read(&z64eocd,
  160. sizeof(Zip64EndOfCentralDirectory)) !=
  161. sizeof(Zip64EndOfCentralDirectory))
  162. MORDOR_THROW_EXCEPTION(UnexpectedEofException());
  163. if (z64eocd.startOfCentralDirectoryDisk !=
  164. z64eocd.numberOfThisDisk)
  165. MORDOR_THROW_EXCEPTION(SpannedZipNotSupportedException());
  166. startOfCentralDirectory =
  167. z64eocd.startOfCentralDirectoryOffset;
  168. entries = z64eocd.centralDirectoryEntries;
  169. } else {
  170. startOfCentralDirectory =
  171. eocd.startOfCentralDirectoryOffset;
  172. entries = eocd.centralDirectoryEntries;
  173. }
  174. m_stream->seek(startOfCentralDirectory);
  175. for (unsigned long long i = 0; i < entries; ++i) {
  176. FileHeader header;
  177. if (m_stream->read(&header, sizeof(FileHeader))
  178. != sizeof(FileHeader))
  179. MORDOR_THROW_EXCEPTION(UnexpectedEofException());
  180. if (header.signature != 0x02014b50)
  181. MORDOR_THROW_EXCEPTION(CorruptZipException());
  182. ZipEntry file(*this);
  183. if (header.fileNameLength) {
  184. file.m_filename.resize(header.fileNameLength);
  185. if (m_stream->read(&file.m_filename[0],
  186. header.fileNameLength) != header.fileNameLength)
  187. MORDOR_THROW_EXCEPTION(UnexpectedEofException());
  188. }
  189. file.m_crc = header.crc32;
  190. file.m_compressedSize = header.compressedSize;
  191. file.m_compressionMethod = header.compressionMethod;
  192. file.m_size = header.uncompressedSize;
  193. file.m_startOffset = header.localHeaderOffset;
  194. file.m_flags = header.generalPurposeFlags;
  195. if (header.extraFieldLength) {
  196. std::string extraFields;
  197. extraFields.resize(header.extraFieldLength);
  198. size_t read = m_stream->read(&extraFields[0],
  199. header.extraFieldLength);
  200. if (read != header.extraFieldLength)
  201. MORDOR_THROW_EXCEPTION(UnexpectedEofException());
  202. const unsigned char *extra =
  203. (const unsigned char *)extraFields.c_str();
  204. const unsigned char *end = extra +
  205. header.extraFieldLength;
  206. while (true) {
  207. if (extra + 2 >= end)
  208. break;
  209. unsigned short type =
  210. *(const unsigned short *)extra;
  211. extra += 2;
  212. if (extra + 2 >= end)
  213. break;
  214. unsigned short length =
  215. *(const unsigned short *)extra;
  216. if (extra + length >= end)
  217. break;
  218. if (type == 0x0001u) {
  219. unsigned short expectedLength = 0;
  220. if (header.uncompressedSize == 0xffffffffu)
  221. expectedLength += 8;
  222. if (header.compressedSize == 0xffffffffu)
  223. expectedLength += 8;
  224. if (header.localHeaderOffset == 0xffffffffu)
  225. expectedLength += 8;
  226. if (header.diskNumberStart == 0xffffu)
  227. expectedLength += 4;
  228. if (length != expectedLength) {
  229. extra += length;
  230. continue;
  231. }
  232. if (header.uncompressedSize == 0xffffffffu) {
  233. file.m_size = *(const long long *)extra;
  234. extra += 8;
  235. }
  236. if (header.compressedSize == 0xffffffffu) {
  237. file.m_compressedSize =
  238. *(const long long *)extra;
  239. extra += 8;
  240. }
  241. if (header.localHeaderOffset == 0xffffffffu)
  242. file.m_startOffset =
  243. *(const long long *)extra;
  244. } else {
  245. extra += length;
  246. }
  247. }
  248. }
  249. m_centralDirectory.insert(std::make_pair(file.m_filename,
  250. file));
  251. }
  252. // Return stream pointer to 0 for getNextFile to work properly
  253. m_stream->seek(0);
  254. }
  255. break;
  256. case WRITE:
  257. MORDOR_ASSERT(m_stream->supportsWrite());
  258. break;
  259. default:
  260. MORDOR_NOTREACHED();
  261. }
  262. m_mode = mode;
  263. }
  264. ZipEntry &
  265. Zip::addFile()
  266. {
  267. if (m_currentFile) {
  268. MORDOR_ASSERT(m_currentFile == &m_scratchFile);
  269. m_scratchFile.close();
  270. m_centralDirectory.insert(std::make_pair(m_scratchFile.filename(),
  271. m_scratchFile));
  272. m_scratchFile.clear();
  273. } else {
  274. m_currentFile = &m_scratchFile;
  275. }
  276. return m_scratchFile;
  277. }
  278. void
  279. Zip::close()
  280. {
  281. MORDOR_ASSERT(m_mode == WRITE);
  282. if (m_currentFile) {
  283. MORDOR_ASSERT(m_currentFile == &m_scratchFile);
  284. m_scratchFile.close();
  285. m_centralDirectory.insert(std::make_pair(m_scratchFile.filename(),
  286. m_scratchFile));
  287. m_scratchFile.clear();
  288. }
  289. unsigned long long centralDirectorySize = 0;
  290. unsigned long long centralDirectoryRecords = 0;
  291. long long startOfCentralDirectory = m_stream->tell();
  292. for (ZipEntries::const_iterator it = m_centralDirectory.begin();
  293. it != m_centralDirectory.end();
  294. ++it) {
  295. const ZipEntry &file = it->second;
  296. FileHeader header;
  297. header.signature = 0x02014b50;
  298. header.versionMadeBy = 10;
  299. header.extractVersion = 10;
  300. header.generalPurposeFlags = file.m_flags;
  301. header.compressionMethod = file.m_compressionMethod;
  302. header.modifiedTime = 0;
  303. header.modifiedDate = 0;
  304. header.crc32 = file.m_crc;
  305. header.compressedSize = (unsigned int)std::min<long long>(0xffffffff,
  306. file.m_compressedSize);
  307. header.uncompressedSize = (unsigned int)std::min<long long>(0xffffffff,
  308. file.m_size);
  309. header.fileNameLength = (unsigned short)file.m_filename.size();
  310. header.extraFieldLength = 0;
  311. header.fileCommentLength = (unsigned short)file.m_comment.size();
  312. header.diskNumberStart = 0;
  313. header.internalFileAttributes = 0;
  314. header.externalFileAttributes = 0;
  315. header.localHeaderOffset = (unsigned int)std::min<long long>(0xffffffff,
  316. file.m_startOffset);
  317. std::string extraFields;
  318. if (header.compressedSize == 0xffffffffu ||
  319. header.uncompressedSize == 0xffffffffu ||
  320. header.localHeaderOffset == 0xffffffffu) {
  321. header.extraFieldLength += 4;
  322. if (header.uncompressedSize == 0xffffffffu)
  323. header.extraFieldLength += 8;
  324. if (header.compressedSize == 0xffffffffu)
  325. header.extraFieldLength += 8;
  326. if (header.localHeaderOffset == 0xffffffffu)
  327. header.extraFieldLength += 8;
  328. extraFields.resize(header.extraFieldLength);
  329. unsigned char *extra = (unsigned char *)&extraFields[0];
  330. *(unsigned short *)extra = 0x0001;
  331. extra += 2;
  332. *(unsigned short *)extra = header.extraFieldLength - 4u;
  333. extra += 2;
  334. if (header.uncompressedSize == 0xffffffffu) {
  335. *(long long *)extra = file.m_size;
  336. extra += 8;
  337. }
  338. if (header.compressedSize == 0xffffffffu) {
  339. *(long long *)extra = file.m_compressedSize;
  340. extra += 8;
  341. }
  342. if (header.localHeaderOffset == 0xffffffffu) {
  343. *(long long *)extra = file.m_startOffset;
  344. extra += 8;
  345. }
  346. }
  347. m_stream->write(&header, sizeof(FileHeader));
  348. if (header.fileNameLength)
  349. m_stream->write(file.m_filename.c_str(), file.m_filename.size());
  350. if (header.extraFieldLength)
  351. m_stream->write(extraFields.c_str(), extraFields.size());
  352. if (header.fileCommentLength)
  353. m_stream->write(file.m_comment.c_str(), file.m_comment.size());
  354. ++centralDirectoryRecords;
  355. centralDirectorySize += sizeof(FileHeader) + header.fileNameLength +
  356. header.extraFieldLength + header.fileCommentLength;
  357. }
  358. if (centralDirectoryRecords >= 0xffffu ||
  359. centralDirectorySize >= 0xffffffffu ||
  360. startOfCentralDirectory >= 0xffffffffu) {
  361. Zip64EndOfCentralDirectory eocd;
  362. eocd.signature = 0x06064b50;
  363. eocd.sizeOfEndOfCentralDirectory =
  364. sizeof(Zip64EndOfCentralDirectory) - 12;
  365. eocd.versionMadeBy = 10;
  366. eocd.extractVersion = 10;
  367. eocd.numberOfThisDisk = 0;
  368. eocd.startOfCentralDirectoryDisk = 0;
  369. eocd.centralDirectoryEntriesThisDisk = centralDirectoryRecords;
  370. eocd.centralDirectoryEntries = centralDirectoryRecords;
  371. eocd.sizeOfCentralDirectory = centralDirectorySize;
  372. eocd.startOfCentralDirectoryOffset = startOfCentralDirectory;
  373. Zip64EndOfCentralDirectoryLocator locator;
  374. locator.signature = 0x07064b50;
  375. locator.endOfCentralDirectoryDisk = 0;
  376. locator.endOfCentralDirectoryOffset = m_stream->tell();
  377. locator.totalDisks = 1;
  378. m_stream->write(&eocd, sizeof(Zip64EndOfCentralDirectory));
  379. m_stream->write(&locator, sizeof(Zip64EndOfCentralDirectoryLocator));
  380. }
  381. EndOfCentralDirectory eocd;
  382. eocd.signature = 0x06054b50;
  383. eocd.numberOfThisDisk = 0;
  384. eocd.startOfCentralDirectoryDisk = 0;
  385. eocd.centralDirectoryEntriesThisDisk = (unsigned short)
  386. std::min<unsigned long long>(0xffffu, centralDirectoryRecords);
  387. eocd.centralDirectoryEntries = eocd.centralDirectoryEntriesThisDisk;
  388. eocd.sizeOfCentralDirectory = (unsigned int)
  389. std::min<unsigned long long>(0xffffffffu, centralDirectorySize);
  390. eocd.startOfCentralDirectoryOffset = (unsigned int)
  391. std::min<unsigned long long>(0xffffffffu, startOfCentralDirectory);
  392. eocd.commentLength = 0;
  393. m_stream->write(&eocd, sizeof(EndOfCentralDirectory));
  394. m_stream->close();
  395. }
  396. ZipEntry const *
  397. Zip::getNextEntry()
  398. {
  399. if (m_currentFile) {
  400. if (!m_stream->supportsSeek()) {
  401. transferStream(m_currentFile->stream(), NullStream::get());
  402. } else {
  403. m_stream->seek(m_currentFile->m_startOffset +
  404. sizeof(LocalFileHeader) +
  405. m_currentFile->m_filename.size() +
  406. m_currentFile->m_extraFieldsLength +
  407. m_currentFile->m_compressedSize);
  408. }
  409. }
  410. LocalFileHeader header;
  411. m_scratchFile.clear();
  412. m_scratchFile.m_startOffset = m_stream->tell();
  413. m_currentFile = NULL;
  414. if (m_stream->read(&header, sizeof(LocalFileHeader)) !=
  415. sizeof(LocalFileHeader))
  416. return NULL;
  417. if (header.signature != 0x04034b50)
  418. return NULL;
  419. if (header.fileNameLength) {
  420. m_scratchFile.m_filename.resize(header.fileNameLength);
  421. if (m_stream->read(&m_scratchFile.m_filename[0],
  422. header.fileNameLength) != header.fileNameLength)
  423. MORDOR_THROW_EXCEPTION(UnexpectedEofException());
  424. }
  425. m_scratchFile.m_crc = header.crc32;
  426. m_scratchFile.m_compressedSize = header.compressedSize;
  427. m_scratchFile.m_size = header.decompressedSize;
  428. m_scratchFile.m_compressionMethod = header.compressionMethod;
  429. m_currentFile = &m_scratchFile;
  430. std::pair<ZipEntries::iterator, ZipEntries::iterator> range =
  431. m_centralDirectory.equal_range(m_scratchFile.m_filename);
  432. while (range.first != range.second) {
  433. if (range.first->second.m_startOffset == m_scratchFile.m_startOffset) {
  434. m_currentFile = &range.first->second;
  435. break;
  436. }
  437. ++range.first;
  438. }
  439. if (header.extraFieldLength) {
  440. if (m_currentFile != &m_scratchFile) {
  441. // We got an entry from the central directory; no need to bother
  442. // reading the extra fields from the local header
  443. MORDOR_ASSERT(m_stream->supportsSeek());
  444. m_stream->seek(header.extraFieldLength, Stream::CURRENT);
  445. } else {
  446. std::string extraFields;
  447. extraFields.resize(header.extraFieldLength);
  448. size_t read = m_stream->read(&extraFields[0],
  449. header.extraFieldLength);
  450. if (read != header.extraFieldLength)
  451. MORDOR_THROW_EXCEPTION(UnexpectedEofException());
  452. const unsigned char *extra =
  453. (const unsigned char *)extraFields.c_str();
  454. const unsigned char *end = extra + header.extraFieldLength;
  455. while (true) {
  456. if (extra + 2 >= end)
  457. break;
  458. unsigned short type = *(const unsigned short *)extra;
  459. extra += 2;
  460. if (extra + 2 >= end)
  461. break;
  462. unsigned short length = *(const unsigned short *)extra;
  463. if (extra + length >= end)
  464. break;
  465. if (type == 0x0001u) {
  466. unsigned short expectedLength = 0;
  467. if (header.decompressedSize == 0xffffffffu)
  468. expectedLength += 8;
  469. if (header.compressedSize == 0xffffffffu)
  470. expectedLength += 8;
  471. if (length != expectedLength) {
  472. extra += length;
  473. continue;
  474. }
  475. if (header.decompressedSize == 0xffffffffu) {
  476. m_scratchFile.m_size = *(const long long *)extra;
  477. extra += 8;
  478. }
  479. if (header.compressedSize == 0xffffffffu)
  480. m_scratchFile.m_compressedSize =
  481. *(const long long *)extra;
  482. } else {
  483. extra += length;
  484. }
  485. }
  486. }
  487. }
  488. if (!m_deflateStream) {
  489. MORDOR_ASSERT(!m_compressedStream);
  490. MORDOR_ASSERT(!m_deflateStream);
  491. m_compressedStream.reset(
  492. new LimitedStream(m_stream, m_currentFile->m_compressedSize,
  493. false));
  494. m_deflateStream.reset(new DeflateStream(
  495. m_compressedStream));
  496. } else {
  497. MORDOR_ASSERT(m_compressedStream);
  498. MORDOR_ASSERT(m_deflateStream);
  499. m_compressedStream->reset(m_currentFile->m_compressedSize);
  500. m_deflateStream->reset();
  501. }
  502. switch (header.compressionMethod) {
  503. case 0:
  504. m_fileStream = m_compressedStream;
  505. break;
  506. case 8:
  507. m_fileStream = m_deflateStream;
  508. break;
  509. default:
  510. MORDOR_THROW_EXCEPTION(
  511. UnsupportedCompressionMethodException());
  512. }
  513. if (header.generalPurposeFlags & 0x0008) {
  514. if (!m_notifyStream)
  515. m_notifyStream.reset(new NotifyStream(
  516. m_fileStream));
  517. m_notifyStream->parent(m_fileStream);
  518. m_notifyStream->notifyOnEof = boost::bind(&Zip::onFileEOF, this);
  519. m_fileStream = m_notifyStream;
  520. }
  521. return m_currentFile;
  522. }
  523. const ZipEntries &
  524. Zip::getAllEntries()
  525. {
  526. MORDOR_ASSERT(m_stream->supportsSeek() && m_stream->supportsSize());
  527. return m_centralDirectory;
  528. }
  529. void
  530. Zip::onFileEOF()
  531. {
  532. MORDOR_ASSERT(m_currentFile);
  533. MORDOR_ASSERT(m_notifyStream);
  534. m_notifyStream->notifyOnEof = NULL;
  535. unsigned char buffer[24];
  536. size_t expectedSize = 12;
  537. // TODO: if you read the same file more than once, this will break
  538. if (m_currentFile->m_compressedSize == 0xffffffffll)
  539. expectedSize += 4;
  540. if (m_currentFile->m_size == 0xffffffffll)
  541. expectedSize += 4;
  542. if (m_stream->read(buffer, expectedSize) != expectedSize)
  543. MORDOR_THROW_EXCEPTION(UnexpectedEofException());
  544. unsigned char *start = buffer;
  545. if (*(unsigned int *)buffer == 0x08074b50) {
  546. // Had a signature, read an additional four bytes
  547. start += 4u;
  548. if (m_stream->read(buffer + expectedSize, 4u) != 4u)
  549. MORDOR_THROW_EXCEPTION(UnexpectedEofException());
  550. }
  551. m_currentFile->m_crc = *(unsigned int *)start;
  552. start += 4;
  553. if (m_currentFile->m_compressedSize == 0xffffffffll) {
  554. m_currentFile->m_compressedSize = *(long long *)start;
  555. start += 8;
  556. } else {
  557. m_currentFile->m_compressedSize = *(unsigned int *)start;
  558. start += 4;
  559. }
  560. if (m_currentFile->m_size == 0xffffffffll)
  561. m_currentFile->m_size = *(long long *)start;
  562. else
  563. m_currentFile->m_size = *(unsigned int *)start;
  564. }
  565. void
  566. ZipEntry::filename(const std::string &filename)
  567. {
  568. m_filename = filename;
  569. }
  570. void
  571. ZipEntry::comment(const std::string &comment)
  572. {
  573. m_comment = comment;
  574. }
  575. void
  576. ZipEntry::size(long long size)
  577. {
  578. m_size = size;
  579. }
  580. void
  581. ZipEntry::compressionMethod(unsigned short method)
  582. {
  583. m_compressionMethod = method;
  584. }
  585. Stream::ptr
  586. ZipEntry::stream()
  587. {
  588. m_startOffset = m_outer->m_stream->tell();
  589. commit();
  590. if (m_outer->m_compressedStream) {
  591. MORDOR_ASSERT(m_outer->m_crcStream);
  592. MORDOR_ASSERT(m_outer->m_uncompressedStream);
  593. m_outer->m_compressedStream->reset();
  594. if (m_outer->m_deflateStream) {
  595. m_outer->m_deflateStream->reset();
  596. }
  597. m_outer->m_crcStream->reset();
  598. m_outer->m_uncompressedStream->reset(
  599. m_size == -1ll ? 0x7fffffffffffffffll : m_size);
  600. } else {
  601. MORDOR_ASSERT(!m_outer->m_crcStream);
  602. MORDOR_ASSERT(!m_outer->m_uncompressedStream);
  603. m_outer->m_compressedStream.reset(
  604. new LimitedStream(m_outer->m_stream, 0x7fffffffffffffffll));
  605. if (m_compressionMethod == 0) { // no compression
  606. m_outer->m_crcStream.reset(
  607. new CRC32Stream(m_outer->m_compressedStream, CRC32Stream::IEEE, false));
  608. } else if (m_compressionMethod == 8) {
  609. MORDOR_ASSERT(!m_outer->m_deflateStream);
  610. m_outer->m_deflateStream.reset(
  611. new DeflateStream(m_outer->m_compressedStream, false));
  612. m_outer->m_crcStream.reset(
  613. new CRC32Stream(m_outer->m_deflateStream));
  614. } else {
  615. MORDOR_THROW_EXCEPTION(UnsupportedCompressionMethodException());
  616. }
  617. m_outer->m_uncompressedStream.reset(
  618. new LimitedStream(m_outer->m_crcStream,
  619. m_size == -1ll ? 0x7fffffffffffffffll : m_size));
  620. }
  621. return m_outer->m_uncompressedStream;
  622. }
  623. Stream::ptr
  624. ZipEntry::stream() const
  625. {
  626. if (m_outer->m_currentFile != this) {
  627. MORDOR_ASSERT(m_outer->m_stream->supportsSeek());
  628. m_outer->m_stream->seek(m_startOffset);
  629. LocalFileHeader header;
  630. size_t read = m_outer->m_stream->read(&header, sizeof(LocalFileHeader));
  631. if (read < sizeof(LocalFileHeader))
  632. MORDOR_THROW_EXCEPTION(UnexpectedEofException());
  633. if (header.signature != 0x04034b50)
  634. MORDOR_THROW_EXCEPTION(CorruptZipException());
  635. if (header.fileNameLength + header.extraFieldLength)
  636. m_outer->m_stream->seek(header.fileNameLength +
  637. header.extraFieldLength, Stream::CURRENT);
  638. if (!m_outer->m_deflateStream) {
  639. MORDOR_ASSERT(!m_outer->m_compressedStream);
  640. MORDOR_ASSERT(!m_outer->m_deflateStream);
  641. m_outer->m_compressedStream.reset(
  642. new LimitedStream(m_outer->m_stream, m_compressedSize, false));
  643. m_outer->m_deflateStream.reset(new DeflateStream(
  644. m_outer->m_compressedStream));
  645. } else {
  646. MORDOR_ASSERT(m_outer->m_compressedStream);
  647. MORDOR_ASSERT(m_outer->m_deflateStream);
  648. m_outer->m_compressedStream->reset(m_compressedSize);
  649. m_outer->m_deflateStream->reset();
  650. }
  651. switch (header.compressionMethod) {
  652. case 0:
  653. m_outer->m_fileStream = m_outer->m_compressedStream;
  654. break;
  655. case 8:
  656. m_outer->m_fileStream = m_outer->m_deflateStream;
  657. break;
  658. default:
  659. MORDOR_THROW_EXCEPTION(
  660. UnsupportedCompressionMethodException());
  661. }
  662. if (header.generalPurposeFlags & 0x0008) {
  663. if (!m_outer->m_notifyStream)
  664. m_outer->m_notifyStream.reset(new NotifyStream(
  665. m_outer->m_fileStream));
  666. m_outer->m_notifyStream->parent(m_outer->m_fileStream);
  667. m_outer->m_notifyStream->notifyOnEof = boost::bind(&Zip::onFileEOF,
  668. m_outer);
  669. m_outer->m_fileStream = m_outer->m_notifyStream;
  670. }
  671. } else {
  672. MORDOR_ASSERT(m_outer->m_fileStream);
  673. }
  674. return m_outer->m_fileStream;
  675. }
  676. void
  677. ZipEntry::commit()
  678. {
  679. if (!m_outer->m_stream->supportsSeek())
  680. m_flags = 0x0808;
  681. LocalFileHeader header;
  682. header.signature = 0x04034b50;
  683. header.extractVersion = 10;
  684. header.generalPurposeFlags = m_flags;
  685. header.compressionMethod = m_compressionMethod;
  686. header.modifiedTime = 0;
  687. header.modifiedDate = 0;
  688. header.crc32 = 0;
  689. // Enabling Zip64
  690. if (m_size == -1ll || m_size >= 0xffffffffll) {
  691. header.compressedSize = 0xffffffffu;
  692. header.decompressedSize = 0xffffffffu;
  693. } else {
  694. header.compressedSize = 0;
  695. header.decompressedSize = 0;
  696. }
  697. header.fileNameLength = (unsigned int)m_filename.size();
  698. std::string extraFields;
  699. if ((m_size == -1ll || m_size >= 0xffffffffll) && !(m_flags & 0x0008)) {
  700. extraFields.resize(20);
  701. unsigned char *extra = (unsigned char *)&extraFields[0];
  702. *(unsigned short *)extra = 0x0001u;
  703. extra += 2;
  704. *(unsigned short *)extra = 16u;
  705. extra += 2;
  706. }
  707. header.extraFieldLength = m_extraFieldsLength = (unsigned short)extraFields.size();
  708. m_outer->m_stream->write(&header, sizeof(LocalFileHeader));
  709. m_outer->m_stream->write(m_filename.c_str(), m_filename.size());
  710. if (header.extraFieldLength)
  711. m_outer->m_stream->write(extraFields.c_str(), extraFields.size());
  712. }
  713. void
  714. ZipEntry::close()
  715. {
  716. MORDOR_ASSERT(m_size == -1ll ||
  717. m_outer->m_uncompressedStream->tell() == m_size);
  718. m_outer->m_uncompressedStream->close();
  719. bool zip64 = (m_size == -1ll || m_size >= 0xffffffffll);
  720. m_size = m_outer->m_uncompressedStream->tell();
  721. if (m_outer->m_stream->supportsSeek()) {
  722. m_compressedSize = m_outer->m_compressedStream->tell() - (
  723. m_startOffset + sizeof(LocalFileHeader) + m_filename.size() + m_extraFieldsLength);
  724. } else {
  725. m_compressedSize = m_outer->m_compressedStream->tell();
  726. }
  727. m_outer->m_crcStream->hash(&m_crc, sizeof(unsigned int));
  728. m_crc = byteswapOnLittleEndian(m_crc);
  729. if (m_flags & 0x0008) {
  730. if (zip64) {
  731. DataDescriptor64 dd;
  732. dd.signature = 0x08074b50;
  733. dd.crc32 = m_crc;
  734. dd.compressedSize = m_compressedSize;
  735. dd.uncompressedSize = m_size;
  736. m_outer->m_stream->write(&dd, sizeof(DataDescriptor64));
  737. } else {
  738. DataDescriptor dd;
  739. dd.signature = 0x08074b50;
  740. dd.crc32 = m_crc;
  741. dd.compressedSize = (unsigned int)m_compressedSize;
  742. dd.uncompressedSize = (unsigned int)m_size;
  743. m_outer->m_stream->write(&dd, sizeof(DataDescriptor));
  744. }
  745. }
  746. if (m_outer->m_stream->supportsSeek()) {
  747. long long current = m_outer->m_stream->tell();
  748. m_outer->m_stream->seek(m_startOffset + 14);
  749. m_outer->m_stream->write(&m_crc, 4);
  750. unsigned int size = (unsigned int)std::min<long long>(
  751. 0xffffffffu, m_compressedSize);
  752. m_outer->m_stream->write(&size, 4);
  753. size = (unsigned int)std::min<long long>(0xffffffffu, m_size);
  754. m_outer->m_stream->write(&size, 4);
  755. if (zip64 && !(m_flags & 0x0008)) {
  756. std::string extraFields;
  757. extraFields.resize(20);
  758. unsigned char *extra = (unsigned char *)&extraFields[0];
  759. *(unsigned short *)extra = 0x0001u;
  760. extra += 2;
  761. *(unsigned short *)extra = 16u;
  762. extra += 2;
  763. *(long long *)extra = m_size;
  764. extra += 8;
  765. *(long long *)extra = m_compressedSize;
  766. extra += 8;
  767. m_outer->m_stream->seek(4 + m_filename.size(), Stream::CURRENT);
  768. m_outer->m_stream->write(extraFields.c_str(), extraFields.size());
  769. }
  770. m_outer->m_stream->seek(current);
  771. }
  772. }
  773. void
  774. ZipEntry::clear()
  775. {
  776. m_filename.clear();
  777. m_comment.clear();
  778. m_size = m_compressedSize = -1ll;
  779. m_startOffset = 0;
  780. m_crc = 0;
  781. m_flags = 0x0800;
  782. m_extraFieldsLength = 0;
  783. }
  784. }