/modules/libjar/nsZipArchive.cpp

http://github.com/zpao/v8monkey · C++ · 1078 lines · 717 code · 161 blank · 200 comment · 160 complexity · e807c4ba40141536e1994c62339fb7f4 MD5 · raw file

  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* ***** BEGIN LICENSE BLOCK *****
  3. * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  4. *
  5. * The contents of this file are subject to the Mozilla Public License Version
  6. * 1.1 (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. * http://www.mozilla.org/MPL/
  9. *
  10. * Software distributed under the License is distributed on an "AS IS" basis,
  11. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12. * for the specific language governing rights and limitations under the
  13. * License.
  14. *
  15. * The Original Code is Mozilla Communicator client code, released
  16. * March 31, 1998.
  17. *
  18. * The Initial Developer of the Original Code is
  19. * Netscape Communications Corporation.
  20. * Portions created by the Initial Developer are Copyright (C) 1998
  21. * the Initial Developer. All Rights Reserved.
  22. *
  23. * Contributor(s):
  24. * Daniel Veditz <dveditz@netscape.com>
  25. * Samir Gehani <sgehani@netscape.com>
  26. * Mitch Stoltz <mstoltz@netscape.com>
  27. * Jeroen Dobbelaere <jeroen.dobbelaere@acunia.com>
  28. * Jeff Walden <jwalden+code@mit.edu>
  29. * Taras Glek <tglek@mozilla.com>
  30. *
  31. * Alternatively, the contents of this file may be used under the terms of
  32. * either the GNU General Public License Version 2 or later (the "GPL"), or
  33. * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  34. * in which case the provisions of the GPL or the LGPL are applicable instead
  35. * of those above. If you wish to allow use of your version of this file only
  36. * under the terms of either the GPL or the LGPL, and not to allow others to
  37. * use your version of this file under the terms of the MPL, indicate your
  38. * decision by deleting the provisions above and replace them with the notice
  39. * and other provisions required by the GPL or the LGPL. If you do not delete
  40. * the provisions above, a recipient may use your version of this file under
  41. * the terms of any one of the MPL, the GPL or the LGPL.
  42. *
  43. * ***** END LICENSE BLOCK ***** */
  44. /*
  45. * This module implements a simple archive extractor for the PKZIP format.
  46. *
  47. * The underlying nsZipArchive is NOT thread-safe. Do not pass references
  48. * or pointers to it across thread boundaries.
  49. */
  50. #define READTYPE PRInt32
  51. #include "zlib.h"
  52. #include "nsISupportsUtils.h"
  53. #include "prio.h"
  54. #include "plstr.h"
  55. #include "prlog.h"
  56. #include "stdlib.h"
  57. #include "nsWildCard.h"
  58. #include "nsZipArchive.h"
  59. #include "nsString.h"
  60. #include "mozilla/FunctionTimer.h"
  61. #include "prenv.h"
  62. #if defined(XP_WIN)
  63. #include <windows.h>
  64. #endif
  65. // For placement new used for arena allocations of zip file list
  66. #include NEW_H
  67. #define ZIP_ARENABLOCKSIZE (1*1024)
  68. #ifdef XP_UNIX
  69. #include <sys/types.h>
  70. #include <sys/stat.h>
  71. #include <limits.h>
  72. #include <unistd.h>
  73. #elif defined(XP_WIN) || defined(XP_OS2)
  74. #include <io.h>
  75. #endif
  76. #ifdef __SYMBIAN32__
  77. #include <sys/syslimits.h>
  78. #endif /*__SYMBIAN32__*/
  79. #ifndef XP_UNIX /* we need some constants defined in limits.h and unistd.h */
  80. # ifndef S_IFMT
  81. # define S_IFMT 0170000
  82. # endif
  83. # ifndef S_IFLNK
  84. # define S_IFLNK 0120000
  85. # endif
  86. # ifndef PATH_MAX
  87. # define PATH_MAX 1024
  88. # endif
  89. #endif /* XP_UNIX */
  90. using namespace mozilla;
  91. static const PRUint32 kMaxNameLength = PATH_MAX; /* Maximum name length */
  92. // For synthetic zip entries. Date/time corresponds to 1980-01-01 00:00.
  93. static const PRUint16 kSyntheticTime = 0;
  94. static const PRUint16 kSyntheticDate = (1 + (1 << 5) + (0 << 9));
  95. static PRUint16 xtoint(const PRUint8 *ii);
  96. static PRUint32 xtolong(const PRUint8 *ll);
  97. static PRUint32 HashName(const char* aName, PRUint16 nameLen);
  98. #ifdef XP_UNIX
  99. static nsresult ResolveSymlink(const char *path);
  100. #endif
  101. //***********************************************************
  102. // For every inflation the following allocations are done:
  103. // malloc(1 * 9520)
  104. // malloc(32768 * 1)
  105. //***********************************************************
  106. nsresult gZlibInit(z_stream *zs)
  107. {
  108. memset(zs, 0, sizeof(z_stream));
  109. int zerr = inflateInit2(zs, -MAX_WBITS);
  110. if (zerr != Z_OK) return NS_ERROR_OUT_OF_MEMORY;
  111. return NS_OK;
  112. }
  113. nsZipHandle::nsZipHandle()
  114. : mFileData(nsnull)
  115. , mLen(0)
  116. , mMap(nsnull)
  117. , mRefCnt(0)
  118. {
  119. MOZ_COUNT_CTOR(nsZipHandle);
  120. }
  121. NS_IMPL_THREADSAFE_ADDREF(nsZipHandle)
  122. NS_IMPL_THREADSAFE_RELEASE(nsZipHandle)
  123. nsresult nsZipHandle::Init(nsILocalFile *file, nsZipHandle **ret)
  124. {
  125. mozilla::AutoFDClose fd;
  126. nsresult rv = file->OpenNSPRFileDesc(PR_RDONLY, 0000, &fd);
  127. if (NS_FAILED(rv))
  128. return rv;
  129. PRInt64 size = PR_Available64(fd);
  130. if (size >= PR_INT32_MAX)
  131. return NS_ERROR_FILE_TOO_BIG;
  132. PRFileMap *map = PR_CreateFileMap(fd, size, PR_PROT_READONLY);
  133. if (!map)
  134. return NS_ERROR_FAILURE;
  135. PRUint8 *buf = (PRUint8*) PR_MemMap(map, 0, (PRUint32) size);
  136. // Bug 525755: PR_MemMap fails when fd points at something other than a normal file.
  137. if (!buf) {
  138. PR_CloseFileMap(map);
  139. return NS_ERROR_FAILURE;
  140. }
  141. nsRefPtr<nsZipHandle> handle = new nsZipHandle();
  142. if (!handle) {
  143. PR_MemUnmap(buf, (PRUint32) size);
  144. PR_CloseFileMap(map);
  145. return NS_ERROR_OUT_OF_MEMORY;
  146. }
  147. handle->mMap = map;
  148. handle->mFile.Init(file);
  149. handle->mLen = (PRUint32) size;
  150. handle->mFileData = buf;
  151. *ret = handle.forget().get();
  152. return NS_OK;
  153. }
  154. nsresult nsZipHandle::Init(nsZipArchive *zip, const char *entry,
  155. nsZipHandle **ret)
  156. {
  157. nsRefPtr<nsZipHandle> handle = new nsZipHandle();
  158. if (!handle)
  159. return NS_ERROR_OUT_OF_MEMORY;
  160. handle->mBuf = new nsZipItemPtr<PRUint8>(zip, entry);
  161. if (!handle->mBuf)
  162. return NS_ERROR_OUT_OF_MEMORY;
  163. if (!handle->mBuf->Buffer())
  164. return NS_ERROR_UNEXPECTED;
  165. handle->mMap = nsnull;
  166. handle->mFile.Init(zip, entry);
  167. handle->mLen = handle->mBuf->Length();
  168. handle->mFileData = handle->mBuf->Buffer();
  169. *ret = handle.forget().get();
  170. return NS_OK;
  171. }
  172. PRInt64 nsZipHandle::SizeOfMapping()
  173. {
  174. return mLen;
  175. }
  176. nsZipHandle::~nsZipHandle()
  177. {
  178. if (mMap) {
  179. PR_MemUnmap((void *)mFileData, mLen);
  180. PR_CloseFileMap(mMap);
  181. }
  182. mFileData = nsnull;
  183. mMap = nsnull;
  184. mBuf = nsnull;
  185. MOZ_COUNT_DTOR(nsZipHandle);
  186. }
  187. //***********************************************************
  188. // nsZipArchive -- public methods
  189. //***********************************************************
  190. //---------------------------------------------
  191. // nsZipArchive::OpenArchive
  192. //---------------------------------------------
  193. nsresult nsZipArchive::OpenArchive(nsZipHandle *aZipHandle)
  194. {
  195. mFd = aZipHandle;
  196. // Initialize our arena
  197. PL_INIT_ARENA_POOL(&mArena, "ZipArena", ZIP_ARENABLOCKSIZE);
  198. //-- get table of contents for archive
  199. nsresult rv = BuildFileList();
  200. char *env = PR_GetEnv("MOZ_JAR_LOG_DIR");
  201. if (env && NS_SUCCEEDED(rv) && aZipHandle->mFile) {
  202. nsCOMPtr<nsILocalFile> logFile;
  203. nsresult rv2 = NS_NewLocalFile(NS_ConvertUTF8toUTF16(env), false, getter_AddRefs(logFile));
  204. if (!NS_SUCCEEDED(rv2))
  205. return rv;
  206. // Create a directory for the log (in case it doesn't exist)
  207. logFile->Create(nsIFile::DIRECTORY_TYPE, 0700);
  208. nsAutoString name;
  209. nsCOMPtr<nsILocalFile> file = aZipHandle->mFile.GetBaseFile();
  210. file->GetLeafName(name);
  211. name.Append(NS_LITERAL_STRING(".log"));
  212. logFile->Append(name);
  213. PRFileDesc* fd;
  214. rv2 = logFile->OpenNSPRFileDesc(PR_WRONLY|PR_CREATE_FILE|PR_APPEND, 0644, &fd);
  215. if (NS_SUCCEEDED(rv2))
  216. mLog = fd;
  217. }
  218. return rv;
  219. }
  220. nsresult nsZipArchive::OpenArchive(nsIFile *aFile)
  221. {
  222. nsresult rv;
  223. nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(aFile, &rv);
  224. NS_ENSURE_SUCCESS(rv, rv);
  225. nsRefPtr<nsZipHandle> handle;
  226. rv = nsZipHandle::Init(localFile, getter_AddRefs(handle));
  227. if (NS_FAILED(rv))
  228. return rv;
  229. return OpenArchive(handle);
  230. }
  231. //---------------------------------------------
  232. // nsZipArchive::Test
  233. //---------------------------------------------
  234. nsresult nsZipArchive::Test(const char *aEntryName)
  235. {
  236. nsZipItem* currItem;
  237. if (aEntryName) // only test specified item
  238. {
  239. currItem = GetItem(aEntryName);
  240. if (!currItem)
  241. return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST;
  242. //-- don't test (synthetic) directory items
  243. if (currItem->IsDirectory())
  244. return NS_OK;
  245. return ExtractFile(currItem, 0, 0);
  246. }
  247. // test all items in archive
  248. for (int i = 0; i < ZIP_TABSIZE; i++) {
  249. for (currItem = mFiles[i]; currItem; currItem = currItem->next) {
  250. //-- don't test (synthetic) directory items
  251. if (currItem->IsDirectory())
  252. continue;
  253. nsresult rv = ExtractFile(currItem, 0, 0);
  254. if (rv != NS_OK)
  255. return rv;
  256. }
  257. }
  258. return NS_OK;
  259. }
  260. //---------------------------------------------
  261. // nsZipArchive::CloseArchive
  262. //---------------------------------------------
  263. nsresult nsZipArchive::CloseArchive()
  264. {
  265. if (mFd) {
  266. PL_FinishArenaPool(&mArena);
  267. mFd = NULL;
  268. }
  269. // CAUTION:
  270. // We don't need to delete each of the nsZipItem as the memory for
  271. // the zip item and the filename it holds are both allocated from the Arena.
  272. // Hence, destroying the Arena is like destroying all the memory
  273. // for all the nsZipItem in one shot. But if the ~nsZipItem is doing
  274. // anything more than cleaning up memory, we should start calling it.
  275. // Let us also cleanup the mFiles table for re-use on the next 'open' call
  276. memset(mFiles, 0, sizeof(mFiles));
  277. mBuiltSynthetics = false;
  278. return NS_OK;
  279. }
  280. //---------------------------------------------
  281. // nsZipArchive::GetItem
  282. //---------------------------------------------
  283. nsZipItem* nsZipArchive::GetItem(const char * aEntryName)
  284. {
  285. if (aEntryName) {
  286. PRUint32 len = strlen(aEntryName);
  287. //-- If the request is for a directory, make sure that synthetic entries
  288. //-- are created for the directories without their own entry.
  289. if (!mBuiltSynthetics) {
  290. if ((len > 0) && (aEntryName[len-1] == '/')) {
  291. if (BuildSynthetics() != NS_OK)
  292. return 0;
  293. }
  294. }
  295. MOZ_WIN_MEM_TRY_BEGIN
  296. nsZipItem* item = mFiles[ HashName(aEntryName, len) ];
  297. while (item) {
  298. if ((len == item->nameLength) &&
  299. (!memcmp(aEntryName, item->Name(), len))) {
  300. if (mLog) {
  301. // Successful GetItem() is a good indicator that the file is about to be read
  302. char *tmp = PL_strdup(aEntryName);
  303. tmp[len]='\n';
  304. PR_Write(mLog, tmp, len+1);
  305. PL_strfree(tmp);
  306. }
  307. return item; //-- found it
  308. }
  309. item = item->next;
  310. }
  311. MOZ_WIN_MEM_TRY_CATCH(return nsnull)
  312. }
  313. return nsnull;
  314. }
  315. //---------------------------------------------
  316. // nsZipArchive::ExtractFile
  317. // This extracts the item to the filehandle provided.
  318. // If 'aFd' is null, it only tests the extraction.
  319. // On extraction error(s) it removes the file.
  320. // When needed, it also resolves the symlink.
  321. //---------------------------------------------
  322. nsresult nsZipArchive::ExtractFile(nsZipItem *item, const char *outname,
  323. PRFileDesc* aFd)
  324. {
  325. if (!item)
  326. return NS_ERROR_ILLEGAL_VALUE;
  327. if (!mFd)
  328. return NS_ERROR_FAILURE;
  329. // Directory extraction is handled in nsJAR::Extract,
  330. // so the item to be extracted should never be a directory
  331. PR_ASSERT(!item->IsDirectory());
  332. Bytef outbuf[ZIP_BUFLEN];
  333. nsZipCursor cursor(item, this, outbuf, ZIP_BUFLEN, true);
  334. nsresult rv = NS_OK;
  335. while (true) {
  336. PRUint32 count = 0;
  337. PRUint8* buf = cursor.Read(&count);
  338. if (!buf) {
  339. rv = NS_ERROR_FILE_CORRUPTED;
  340. break;
  341. } else if (count == 0) {
  342. break;
  343. }
  344. if (aFd && PR_Write(aFd, buf, count) < (READTYPE)count) {
  345. rv = NS_ERROR_FILE_DISK_FULL;
  346. break;
  347. }
  348. }
  349. //-- delete the file on errors, or resolve symlink if needed
  350. if (aFd) {
  351. PR_Close(aFd);
  352. if (rv != NS_OK)
  353. PR_Delete(outname);
  354. #ifdef XP_UNIX
  355. else if (item->IsSymlink())
  356. rv = ResolveSymlink(outname);
  357. #endif
  358. }
  359. return rv;
  360. }
  361. //---------------------------------------------
  362. // nsZipArchive::FindInit
  363. //---------------------------------------------
  364. PRInt32
  365. nsZipArchive::FindInit(const char * aPattern, nsZipFind **aFind)
  366. {
  367. if (!aFind)
  368. return NS_ERROR_ILLEGAL_VALUE;
  369. // null out param in case an error happens
  370. *aFind = NULL;
  371. bool regExp = false;
  372. char* pattern = 0;
  373. // Create synthetic directory entries on demand
  374. nsresult rv = BuildSynthetics();
  375. if (rv != NS_OK)
  376. return rv;
  377. // validate the pattern
  378. if (aPattern)
  379. {
  380. switch (NS_WildCardValid((char*)aPattern))
  381. {
  382. case INVALID_SXP:
  383. return NS_ERROR_ILLEGAL_VALUE;
  384. case NON_SXP:
  385. regExp = false;
  386. break;
  387. case VALID_SXP:
  388. regExp = true;
  389. break;
  390. default:
  391. // undocumented return value from RegExpValid!
  392. PR_ASSERT(false);
  393. return NS_ERROR_ILLEGAL_VALUE;
  394. }
  395. pattern = PL_strdup(aPattern);
  396. if (!pattern)
  397. return NS_ERROR_OUT_OF_MEMORY;
  398. }
  399. *aFind = new nsZipFind(this, pattern, regExp);
  400. if (!*aFind) {
  401. PL_strfree(pattern);
  402. return NS_ERROR_OUT_OF_MEMORY;
  403. }
  404. return NS_OK;
  405. }
  406. //---------------------------------------------
  407. // nsZipFind::FindNext
  408. //---------------------------------------------
  409. nsresult nsZipFind::FindNext(const char ** aResult, PRUint16 *aNameLen)
  410. {
  411. if (!mArchive || !aResult || !aNameLen)
  412. return NS_ERROR_ILLEGAL_VALUE;
  413. *aResult = 0;
  414. *aNameLen = 0;
  415. MOZ_WIN_MEM_TRY_BEGIN
  416. // we start from last match, look for next
  417. while (mSlot < ZIP_TABSIZE)
  418. {
  419. // move to next in current chain, or move to new slot
  420. mItem = mItem ? mItem->next : mArchive->mFiles[mSlot];
  421. bool found = false;
  422. if (!mItem)
  423. ++mSlot; // no more in this chain, move to next slot
  424. else if (!mPattern)
  425. found = true; // always match
  426. else if (mRegExp)
  427. {
  428. char buf[kMaxNameLength+1];
  429. memcpy(buf, mItem->Name(), mItem->nameLength);
  430. buf[mItem->nameLength]='\0';
  431. found = (NS_WildCardMatch(buf, mPattern, false) == MATCH);
  432. }
  433. else
  434. found = ((mItem->nameLength == strlen(mPattern)) &&
  435. (memcmp(mItem->Name(), mPattern, mItem->nameLength) == 0));
  436. if (found) {
  437. // Need also to return the name length, as it is NOT zero-terminatdd...
  438. *aResult = mItem->Name();
  439. *aNameLen = mItem->nameLength;
  440. return NS_OK;
  441. }
  442. }
  443. MOZ_WIN_MEM_TRY_CATCH(return NS_ERROR_FAILURE)
  444. return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST;
  445. }
  446. #ifdef XP_UNIX
  447. //---------------------------------------------
  448. // ResolveSymlink
  449. //---------------------------------------------
  450. static nsresult ResolveSymlink(const char *path)
  451. {
  452. PRFileDesc * fIn = PR_Open(path, PR_RDONLY, 0000);
  453. if (!fIn)
  454. return NS_ERROR_FILE_DISK_FULL;
  455. char buf[PATH_MAX+1];
  456. PRInt32 length = PR_Read(fIn, (void*)buf, PATH_MAX);
  457. PR_Close(fIn);
  458. if ( (length <= 0)
  459. || ((buf[length] = 0, PR_Delete(path)) != 0)
  460. || (symlink(buf, path) != 0))
  461. {
  462. return NS_ERROR_FILE_DISK_FULL;
  463. }
  464. return NS_OK;
  465. }
  466. #endif
  467. //***********************************************************
  468. // nsZipArchive -- private implementation
  469. //***********************************************************
  470. //---------------------------------------------
  471. // nsZipArchive::CreateZipItem
  472. //---------------------------------------------
  473. nsZipItem* nsZipArchive::CreateZipItem()
  474. {
  475. // Arena allocate the nsZipItem
  476. void *mem;
  477. PL_ARENA_ALLOCATE(mem, &mArena, sizeof(nsZipItem));
  478. return (nsZipItem*)mem;
  479. }
  480. //---------------------------------------------
  481. // nsZipArchive::BuildFileList
  482. //---------------------------------------------
  483. nsresult nsZipArchive::BuildFileList()
  484. {
  485. #ifndef XP_WIN
  486. NS_TIME_FUNCTION;
  487. #endif
  488. // Get archive size using end pos
  489. const PRUint8* buf;
  490. const PRUint8* startp = mFd->mFileData;
  491. const PRUint8* endp = startp + mFd->mLen;
  492. MOZ_WIN_MEM_TRY_BEGIN
  493. PRUint32 centralOffset = 4;
  494. if (mFd->mLen > ZIPCENTRAL_SIZE && xtolong(startp + centralOffset) == CENTRALSIG) {
  495. // Success means optimized jar layout from bug 559961 is in effect
  496. } else {
  497. for (buf = endp - ZIPEND_SIZE; buf > startp; buf--)
  498. {
  499. if (xtolong(buf) == ENDSIG) {
  500. centralOffset = xtolong(((ZipEnd *)buf)->offset_central_dir);
  501. break;
  502. }
  503. }
  504. }
  505. if (!centralOffset)
  506. return NS_ERROR_FILE_CORRUPTED;
  507. //-- Read the central directory headers
  508. buf = startp + centralOffset;
  509. PRUint32 sig = 0;
  510. while (buf + PRInt32(sizeof(PRUint32)) <= endp &&
  511. (sig = xtolong(buf)) == CENTRALSIG) {
  512. // Make sure there is enough data available.
  513. if (endp - buf < ZIPCENTRAL_SIZE)
  514. return NS_ERROR_FILE_CORRUPTED;
  515. // Read the fixed-size data.
  516. ZipCentral* central = (ZipCentral*)buf;
  517. PRUint16 namelen = xtoint(central->filename_len);
  518. PRUint16 extralen = xtoint(central->extrafield_len);
  519. PRUint16 commentlen = xtoint(central->commentfield_len);
  520. // Point to the next item at the top of loop
  521. buf += ZIPCENTRAL_SIZE + namelen + extralen + commentlen;
  522. // Sanity check variable sizes and refuse to deal with
  523. // anything too big: it's likely a corrupt archive.
  524. if (namelen > kMaxNameLength || buf >= endp)
  525. return NS_ERROR_FILE_CORRUPTED;
  526. nsZipItem* item = CreateZipItem();
  527. if (!item)
  528. return NS_ERROR_OUT_OF_MEMORY;
  529. item->central = central;
  530. item->nameLength = namelen;
  531. item->isSynthetic = false;
  532. // Add item to file table
  533. PRUint32 hash = HashName(item->Name(), namelen);
  534. item->next = mFiles[hash];
  535. mFiles[hash] = item;
  536. sig = 0;
  537. } /* while reading central directory records */
  538. if (sig != ENDSIG)
  539. return NS_ERROR_FILE_CORRUPTED;
  540. MOZ_WIN_MEM_TRY_CATCH(return NS_ERROR_FAILURE)
  541. return NS_OK;
  542. }
  543. //---------------------------------------------
  544. // nsZipArchive::BuildSynthetics
  545. //---------------------------------------------
  546. nsresult nsZipArchive::BuildSynthetics()
  547. {
  548. if (mBuiltSynthetics)
  549. return NS_OK;
  550. mBuiltSynthetics = true;
  551. MOZ_WIN_MEM_TRY_BEGIN
  552. // Create synthetic entries for any missing directories.
  553. // Do this when all ziptable has scanned to prevent double entries.
  554. for (int i = 0; i < ZIP_TABSIZE; ++i)
  555. {
  556. for (nsZipItem* item = mFiles[i]; item != 0; item = item->next)
  557. {
  558. if (item->isSynthetic)
  559. continue;
  560. //-- add entries for directories in the current item's path
  561. //-- go from end to beginning, because then we can stop trying
  562. //-- to create diritems if we find that the diritem we want to
  563. //-- create already exists
  564. //-- start just before the last char so as to not add the item
  565. //-- twice if it's a directory
  566. PRUint16 namelen = item->nameLength;
  567. const char *name = item->Name();
  568. for (PRUint16 dirlen = namelen - 1; dirlen > 0; dirlen--)
  569. {
  570. if (name[dirlen-1] != '/')
  571. continue;
  572. // Is the directory already in the file table?
  573. PRUint32 hash = HashName(item->Name(), dirlen);
  574. bool found = false;
  575. for (nsZipItem* zi = mFiles[hash]; zi != NULL; zi = zi->next)
  576. {
  577. if ((dirlen == zi->nameLength) &&
  578. (0 == memcmp(item->Name(), zi->Name(), dirlen)))
  579. {
  580. // we've already added this dir and all its parents
  581. found = true;
  582. break;
  583. }
  584. }
  585. // if the directory was found, break out of the directory
  586. // creation loop now that we know all implicit directories
  587. // are there -- otherwise, start creating the zip item
  588. if (found)
  589. break;
  590. nsZipItem* diritem = CreateZipItem();
  591. if (!diritem)
  592. return NS_ERROR_OUT_OF_MEMORY;
  593. // Point to the central record of the original item for the name part.
  594. diritem->central = item->central;
  595. diritem->nameLength = dirlen;
  596. diritem->isSynthetic = true;
  597. // add diritem to the file table
  598. diritem->next = mFiles[hash];
  599. mFiles[hash] = diritem;
  600. } /* end processing of dirs in item's name */
  601. }
  602. }
  603. MOZ_WIN_MEM_TRY_CATCH(return NS_ERROR_FAILURE)
  604. return NS_OK;
  605. }
  606. nsZipHandle* nsZipArchive::GetFD()
  607. {
  608. if (!mFd)
  609. return NULL;
  610. return mFd.get();
  611. }
  612. //---------------------------------------------
  613. // nsZipArchive::GetData
  614. //---------------------------------------------
  615. const PRUint8* nsZipArchive::GetData(nsZipItem* aItem)
  616. {
  617. PR_ASSERT (aItem);
  618. MOZ_WIN_MEM_TRY_BEGIN
  619. //-- read local header to get variable length values and calculate
  620. //-- the real data offset
  621. PRUint32 len = mFd->mLen;
  622. const PRUint8* data = mFd->mFileData;
  623. PRUint32 offset = aItem->LocalOffset();
  624. if (offset + ZIPLOCAL_SIZE > len)
  625. return nsnull;
  626. // -- check signature before using the structure, in case the zip file is corrupt
  627. ZipLocal* Local = (ZipLocal*)(data + offset);
  628. if ((xtolong(Local->signature) != LOCALSIG))
  629. return nsnull;
  630. //-- NOTE: extralen is different in central header and local header
  631. //-- for archives created using the Unix "zip" utility. To set
  632. //-- the offset accurately we need the _local_ extralen.
  633. offset += ZIPLOCAL_SIZE +
  634. xtoint(Local->filename_len) +
  635. xtoint(Local->extrafield_len);
  636. // -- check if there is enough source data in the file
  637. if (offset + aItem->Size() > len)
  638. return nsnull;
  639. return data + offset;
  640. MOZ_WIN_MEM_TRY_CATCH(return nsnull)
  641. }
  642. //---------------------------------------------
  643. // nsZipArchive::SizeOfMapping
  644. //---------------------------------------------
  645. PRInt64 nsZipArchive::SizeOfMapping()
  646. {
  647. return mFd ? mFd->SizeOfMapping() : 0;
  648. }
  649. //------------------------------------------
  650. // nsZipArchive constructor and destructor
  651. //------------------------------------------
  652. nsZipArchive::nsZipArchive()
  653. : mRefCnt(0)
  654. , mBuiltSynthetics(false)
  655. {
  656. MOZ_COUNT_CTOR(nsZipArchive);
  657. // initialize the table to NULL
  658. memset(mFiles, 0, sizeof(mFiles));
  659. }
  660. NS_IMPL_THREADSAFE_ADDREF(nsZipArchive)
  661. NS_IMPL_THREADSAFE_RELEASE(nsZipArchive)
  662. nsZipArchive::~nsZipArchive()
  663. {
  664. CloseArchive();
  665. MOZ_COUNT_DTOR(nsZipArchive);
  666. }
  667. //------------------------------------------
  668. // nsZipFind constructor and destructor
  669. //------------------------------------------
  670. nsZipFind::nsZipFind(nsZipArchive* aZip, char* aPattern, bool aRegExp) :
  671. mArchive(aZip),
  672. mPattern(aPattern),
  673. mItem(0),
  674. mSlot(0),
  675. mRegExp(aRegExp)
  676. {
  677. MOZ_COUNT_CTOR(nsZipFind);
  678. }
  679. nsZipFind::~nsZipFind()
  680. {
  681. PL_strfree(mPattern);
  682. MOZ_COUNT_DTOR(nsZipFind);
  683. }
  684. //------------------------------------------
  685. // helper functions
  686. //------------------------------------------
  687. /*
  688. * HashName
  689. *
  690. * returns a hash key for the entry name
  691. */
  692. static PRUint32 HashName(const char* aName, PRUint16 len)
  693. {
  694. PR_ASSERT(aName != 0);
  695. const PRUint8* p = (const PRUint8*)aName;
  696. const PRUint8* endp = p + len;
  697. PRUint32 val = 0;
  698. while (p != endp) {
  699. val = val*37 + *p++;
  700. }
  701. return (val % ZIP_TABSIZE);
  702. }
  703. /*
  704. * x t o i n t
  705. *
  706. * Converts a two byte ugly endianed integer
  707. * to our platform's integer.
  708. */
  709. static PRUint16 xtoint (const PRUint8 *ii)
  710. {
  711. return (PRUint16) ((ii [0]) | (ii [1] << 8));
  712. }
  713. /*
  714. * x t o l o n g
  715. *
  716. * Converts a four byte ugly endianed integer
  717. * to our platform's integer.
  718. */
  719. static PRUint32 xtolong (const PRUint8 *ll)
  720. {
  721. return (PRUint32)( (ll [0] << 0) |
  722. (ll [1] << 8) |
  723. (ll [2] << 16) |
  724. (ll [3] << 24) );
  725. }
  726. /*
  727. * GetModTime
  728. *
  729. * returns last modification time in microseconds
  730. */
  731. static PRTime GetModTime(PRUint16 aDate, PRUint16 aTime)
  732. {
  733. // Note that on DST shift we can't handle correctly the hour that is valid
  734. // in both DST zones
  735. PRExplodedTime time;
  736. time.tm_usec = 0;
  737. time.tm_hour = (aTime >> 11) & 0x1F;
  738. time.tm_min = (aTime >> 5) & 0x3F;
  739. time.tm_sec = (aTime & 0x1F) * 2;
  740. time.tm_year = (aDate >> 9) + 1980;
  741. time.tm_month = ((aDate >> 5) & 0x0F) - 1;
  742. time.tm_mday = aDate & 0x1F;
  743. time.tm_params.tp_gmt_offset = 0;
  744. time.tm_params.tp_dst_offset = 0;
  745. PR_NormalizeTime(&time, PR_GMTParameters);
  746. time.tm_params.tp_gmt_offset = PR_LocalTimeParameters(&time).tp_gmt_offset;
  747. PR_NormalizeTime(&time, PR_GMTParameters);
  748. time.tm_params.tp_dst_offset = PR_LocalTimeParameters(&time).tp_dst_offset;
  749. return PR_ImplodeTime(&time);
  750. }
  751. PRUint32 nsZipItem::LocalOffset()
  752. {
  753. return xtolong(central->localhdr_offset);
  754. }
  755. PRUint32 nsZipItem::Size()
  756. {
  757. return isSynthetic ? 0 : xtolong(central->size);
  758. }
  759. PRUint32 nsZipItem::RealSize()
  760. {
  761. return isSynthetic ? 0 : xtolong(central->orglen);
  762. }
  763. PRUint32 nsZipItem::CRC32()
  764. {
  765. return isSynthetic ? 0 : xtolong(central->crc32);
  766. }
  767. PRUint16 nsZipItem::Date()
  768. {
  769. return isSynthetic ? kSyntheticDate : xtoint(central->date);
  770. }
  771. PRUint16 nsZipItem::Time()
  772. {
  773. return isSynthetic ? kSyntheticTime : xtoint(central->time);
  774. }
  775. PRUint16 nsZipItem::Compression()
  776. {
  777. return isSynthetic ? STORED : xtoint(central->method);
  778. }
  779. bool nsZipItem::IsDirectory()
  780. {
  781. return isSynthetic || ((nameLength > 0) && ('/' == Name()[nameLength - 1]));
  782. }
  783. PRUint16 nsZipItem::Mode()
  784. {
  785. if (isSynthetic) return 0755;
  786. return ((PRUint16)(central->external_attributes[2]) | 0x100);
  787. }
  788. const PRUint8 * nsZipItem::GetExtraField(PRUint16 aTag, PRUint16 *aBlockSize)
  789. {
  790. if (isSynthetic) return nsnull;
  791. MOZ_WIN_MEM_TRY_BEGIN
  792. const unsigned char *buf = ((const unsigned char*)central) + ZIPCENTRAL_SIZE +
  793. nameLength;
  794. PRUint32 buflen = (PRUint32)xtoint(central->extrafield_len);
  795. PRUint32 pos = 0;
  796. PRUint16 tag, blocksize;
  797. while (buf && (pos + 4) <= buflen) {
  798. tag = xtoint(buf + pos);
  799. blocksize = xtoint(buf + pos + 2);
  800. if (aTag == tag && (pos + 4 + blocksize) <= buflen) {
  801. *aBlockSize = blocksize;
  802. return buf + pos;
  803. }
  804. pos += blocksize + 4;
  805. }
  806. MOZ_WIN_MEM_TRY_CATCH(return nsnull)
  807. return nsnull;
  808. }
  809. PRTime nsZipItem::LastModTime()
  810. {
  811. if (isSynthetic) return GetModTime(kSyntheticDate, kSyntheticTime);
  812. // Try to read timestamp from extra field
  813. PRUint16 blocksize;
  814. const PRUint8 *tsField = GetExtraField(EXTENDED_TIMESTAMP_FIELD, &blocksize);
  815. if (tsField && blocksize >= 5 && tsField[4] & EXTENDED_TIMESTAMP_MODTIME) {
  816. return (PRTime)(xtolong(tsField + 5)) * PR_USEC_PER_SEC;
  817. }
  818. return GetModTime(Date(), Time());
  819. }
  820. #ifdef XP_UNIX
  821. bool nsZipItem::IsSymlink()
  822. {
  823. if (isSynthetic) return false;
  824. return (xtoint(central->external_attributes+2) & S_IFMT) == S_IFLNK;
  825. }
  826. #endif
  827. nsZipCursor::nsZipCursor(nsZipItem *item, nsZipArchive *aZip, PRUint8* aBuf, PRUint32 aBufSize, bool doCRC) :
  828. mItem(item),
  829. mBuf(aBuf),
  830. mBufSize(aBufSize),
  831. mDoCRC(doCRC)
  832. {
  833. if (mItem->Compression() == DEFLATED) {
  834. #ifdef DEBUG
  835. nsresult status =
  836. #endif
  837. gZlibInit(&mZs);
  838. NS_ASSERTION(status == NS_OK, "Zlib failed to initialize");
  839. NS_ASSERTION(aBuf, "Must pass in a buffer for DEFLATED nsZipItem");
  840. }
  841. mZs.avail_in = item->Size();
  842. mZs.next_in = (Bytef*)aZip->GetData(item);
  843. if (doCRC)
  844. mCRC = crc32(0L, Z_NULL, 0);
  845. }
  846. nsZipCursor::~nsZipCursor()
  847. {
  848. if (mItem->Compression() == DEFLATED) {
  849. inflateEnd(&mZs);
  850. }
  851. }
  852. PRUint8* nsZipCursor::ReadOrCopy(PRUint32 *aBytesRead, bool aCopy) {
  853. int zerr;
  854. PRUint8 *buf = nsnull;
  855. bool verifyCRC = true;
  856. if (!mZs.next_in)
  857. return nsnull;
  858. MOZ_WIN_MEM_TRY_BEGIN
  859. switch (mItem->Compression()) {
  860. case STORED:
  861. if (!aCopy) {
  862. *aBytesRead = mZs.avail_in;
  863. buf = mZs.next_in;
  864. mZs.next_in += mZs.avail_in;
  865. mZs.avail_in = 0;
  866. } else {
  867. *aBytesRead = mZs.avail_in > mBufSize ? mBufSize : mZs.avail_in;
  868. memcpy(mBuf, mZs.next_in, *aBytesRead);
  869. mZs.avail_in -= *aBytesRead;
  870. mZs.next_in += *aBytesRead;
  871. }
  872. break;
  873. case DEFLATED:
  874. buf = mBuf;
  875. mZs.next_out = buf;
  876. mZs.avail_out = mBufSize;
  877. zerr = inflate(&mZs, Z_PARTIAL_FLUSH);
  878. if (zerr != Z_OK && zerr != Z_STREAM_END)
  879. return nsnull;
  880. *aBytesRead = mZs.next_out - buf;
  881. verifyCRC = (zerr == Z_STREAM_END);
  882. break;
  883. default:
  884. return nsnull;
  885. }
  886. if (mDoCRC) {
  887. mCRC = crc32(mCRC, (const unsigned char*)buf, *aBytesRead);
  888. if (verifyCRC && mCRC != mItem->CRC32())
  889. return nsnull;
  890. }
  891. MOZ_WIN_MEM_TRY_CATCH(return nsnull)
  892. return buf;
  893. }
  894. nsZipItemPtr_base::nsZipItemPtr_base(nsZipArchive *aZip, const char * aEntryName, bool doCRC) :
  895. mReturnBuf(nsnull)
  896. {
  897. // make sure the ziparchive hangs around
  898. mZipHandle = aZip->GetFD();
  899. nsZipItem* item = aZip->GetItem(aEntryName);
  900. if (!item)
  901. return;
  902. PRUint32 size = 0;
  903. if (item->Compression() == DEFLATED) {
  904. size = item->RealSize();
  905. mAutoBuf = new PRUint8[size];
  906. }
  907. nsZipCursor cursor(item, aZip, mAutoBuf, size, doCRC);
  908. mReturnBuf = cursor.Read(&mReadlen);
  909. if (!mReturnBuf) {
  910. return;
  911. }
  912. if (mReadlen != item->RealSize()) {
  913. NS_ASSERTION(mReadlen == item->RealSize(), "nsZipCursor underflow");
  914. mReturnBuf = nsnull;
  915. return;
  916. }
  917. }