Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 /*
7 * This module implements a simple archive extractor for the PKZIP format.
8 *
9 * The underlying nsZipArchive is NOT thread-safe. Do not pass references
10 * or pointers to it across thread boundaries.
11 */
13 // This must be the first include in the file in order for the
14 // PL_ARENA_CONST_ALIGN_MASK macro to be effective.
15 #define PL_ARENA_CONST_ALIGN_MASK (sizeof(void*)-1)
16 #include "plarena.h"
18 #define READTYPE int32_t
19 #include "zlib.h"
20 #include "nsISupportsUtils.h"
21 #include "prio.h"
22 #include "plstr.h"
23 #include "prlog.h"
24 #include "stdlib.h"
25 #include "nsWildCard.h"
26 #include "nsZipArchive.h"
27 #include "nsString.h"
28 #include "prenv.h"
29 #if defined(XP_WIN)
30 #include <windows.h>
31 #endif
33 // For placement new used for arena allocations of zip file list
34 #include <new>
35 #define ZIP_ARENABLOCKSIZE (1*1024)
37 #ifdef XP_UNIX
38 #include <sys/mman.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <limits.h>
42 #include <unistd.h>
43 #elif defined(XP_WIN)
44 #include <io.h>
45 #endif
47 #ifdef __SYMBIAN32__
48 #include <sys/syslimits.h>
49 #endif /*__SYMBIAN32__*/
52 #ifndef XP_UNIX /* we need some constants defined in limits.h and unistd.h */
53 # ifndef S_IFMT
54 # define S_IFMT 0170000
55 # endif
56 # ifndef S_IFLNK
57 # define S_IFLNK 0120000
58 # endif
59 # ifndef PATH_MAX
60 # define PATH_MAX 1024
61 # endif
62 #endif /* XP_UNIX */
64 #ifdef XP_WIN
65 #include "private/pprio.h" // To get PR_ImportFile
66 #endif
68 using namespace mozilla;
70 static const uint32_t kMaxNameLength = PATH_MAX; /* Maximum name length */
71 // For synthetic zip entries. Date/time corresponds to 1980-01-01 00:00.
72 static const uint16_t kSyntheticTime = 0;
73 static const uint16_t kSyntheticDate = (1 + (1 << 5) + (0 << 9));
75 static uint16_t xtoint(const uint8_t *ii);
76 static uint32_t xtolong(const uint8_t *ll);
77 static uint32_t HashName(const char* aName, uint16_t nameLen);
78 #ifdef XP_UNIX
79 static nsresult ResolveSymlink(const char *path);
80 #endif
82 class ZipArchiveLogger {
83 public:
84 void Write(const nsACString &zip, const char *entry) const {
85 if (!fd) {
86 char *env = PR_GetEnv("MOZ_JAR_LOG_FILE");
87 if (!env)
88 return;
90 nsCOMPtr<nsIFile> logFile;
91 nsresult rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(env), false, getter_AddRefs(logFile));
92 if (NS_FAILED(rv))
93 return;
95 // Create the log file and its parent directory (in case it doesn't exist)
96 logFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
98 PRFileDesc* file;
99 #ifdef XP_WIN
100 // PR_APPEND is racy on Windows, so open a handle ourselves with flags that
101 // will work, and use PR_ImportFile to make it a PRFileDesc.
102 // This can go away when bug 840435 is fixed.
103 nsAutoString path;
104 logFile->GetPath(path);
105 if (path.IsEmpty())
106 return;
107 HANDLE handle = CreateFileW(path.get(), FILE_APPEND_DATA, FILE_SHARE_WRITE,
108 nullptr, OPEN_ALWAYS, 0, nullptr);
109 if (handle == INVALID_HANDLE_VALUE)
110 return;
111 file = PR_ImportFile((PROsfd)handle);
112 if (!file)
113 return;
114 #else
115 rv = logFile->OpenNSPRFileDesc(PR_WRONLY|PR_CREATE_FILE|PR_APPEND, 0644, &file);
116 if (NS_FAILED(rv))
117 return;
118 #endif
119 fd = file;
120 }
121 nsCString buf(zip);
122 buf.Append(" ");
123 buf.Append(entry);
124 buf.Append('\n');
125 PR_Write(fd, buf.get(), buf.Length());
126 }
128 void AddRef() {
129 MOZ_ASSERT(refCnt >= 0);
130 ++refCnt;
131 }
133 void Release() {
134 MOZ_ASSERT(refCnt > 0);
135 if ((0 == --refCnt) && fd) {
136 PR_Close(fd);
137 fd = nullptr;
138 }
139 }
140 private:
141 int refCnt;
142 mutable PRFileDesc *fd;
143 };
145 static ZipArchiveLogger zipLog;
147 //***********************************************************
148 // For every inflation the following allocations are done:
149 // malloc(1 * 9520)
150 // malloc(32768 * 1)
151 //***********************************************************
153 nsresult gZlibInit(z_stream *zs)
154 {
155 memset(zs, 0, sizeof(z_stream));
156 int zerr = inflateInit2(zs, -MAX_WBITS);
157 if (zerr != Z_OK) return NS_ERROR_OUT_OF_MEMORY;
159 return NS_OK;
160 }
162 nsZipHandle::nsZipHandle()
163 : mFileData(nullptr)
164 , mLen(0)
165 , mMap(nullptr)
166 , mRefCnt(0)
167 {
168 MOZ_COUNT_CTOR(nsZipHandle);
169 }
171 NS_IMPL_ADDREF(nsZipHandle)
172 NS_IMPL_RELEASE(nsZipHandle)
174 nsresult nsZipHandle::Init(nsIFile *file, nsZipHandle **ret, PRFileDesc **aFd)
175 {
176 mozilla::AutoFDClose fd;
177 int32_t flags = PR_RDONLY;
178 #if defined(XP_WIN)
179 flags |= nsIFile::OS_READAHEAD;
180 #endif
181 nsresult rv = file->OpenNSPRFileDesc(flags, 0000, &fd.rwget());
182 if (NS_FAILED(rv))
183 return rv;
185 int64_t size = PR_Available64(fd);
186 if (size >= INT32_MAX)
187 return NS_ERROR_FILE_TOO_BIG;
189 PRFileMap *map = PR_CreateFileMap(fd, size, PR_PROT_READONLY);
190 if (!map)
191 return NS_ERROR_FAILURE;
193 uint8_t *buf = (uint8_t*) PR_MemMap(map, 0, (uint32_t) size);
194 // Bug 525755: PR_MemMap fails when fd points at something other than a normal file.
195 if (!buf) {
196 PR_CloseFileMap(map);
197 return NS_ERROR_FAILURE;
198 }
200 nsRefPtr<nsZipHandle> handle = new nsZipHandle();
201 if (!handle) {
202 PR_MemUnmap(buf, (uint32_t) size);
203 PR_CloseFileMap(map);
204 return NS_ERROR_OUT_OF_MEMORY;
205 }
207 #if defined(XP_WIN)
208 if (aFd) {
209 *aFd = fd.forget();
210 }
211 #endif
212 handle->mMap = map;
213 handle->mFile.Init(file);
214 handle->mLen = (uint32_t) size;
215 handle->mFileData = buf;
216 handle.forget(ret);
217 return NS_OK;
218 }
220 nsresult nsZipHandle::Init(nsZipArchive *zip, const char *entry,
221 nsZipHandle **ret)
222 {
223 nsRefPtr<nsZipHandle> handle = new nsZipHandle();
224 if (!handle)
225 return NS_ERROR_OUT_OF_MEMORY;
227 handle->mBuf = new nsZipItemPtr<uint8_t>(zip, entry);
228 if (!handle->mBuf)
229 return NS_ERROR_OUT_OF_MEMORY;
231 if (!handle->mBuf->Buffer())
232 return NS_ERROR_UNEXPECTED;
234 handle->mMap = nullptr;
235 handle->mFile.Init(zip, entry);
236 handle->mLen = handle->mBuf->Length();
237 handle->mFileData = handle->mBuf->Buffer();
238 handle.forget(ret);
239 return NS_OK;
240 }
242 int64_t nsZipHandle::SizeOfMapping()
243 {
244 return mLen;
245 }
247 nsZipHandle::~nsZipHandle()
248 {
249 if (mMap) {
250 PR_MemUnmap((void *)mFileData, mLen);
251 PR_CloseFileMap(mMap);
252 }
253 mFileData = nullptr;
254 mMap = nullptr;
255 mBuf = nullptr;
256 MOZ_COUNT_DTOR(nsZipHandle);
257 }
259 //***********************************************************
260 // nsZipArchive -- public methods
261 //***********************************************************
263 //---------------------------------------------
264 // nsZipArchive::OpenArchive
265 //---------------------------------------------
266 nsresult nsZipArchive::OpenArchive(nsZipHandle *aZipHandle, PRFileDesc *aFd)
267 {
268 mFd = aZipHandle;
270 // Initialize our arena
271 PL_INIT_ARENA_POOL(&mArena, "ZipArena", ZIP_ARENABLOCKSIZE);
273 //-- get table of contents for archive
274 nsresult rv = BuildFileList(aFd);
275 if (NS_SUCCEEDED(rv)) {
276 if (aZipHandle->mFile)
277 aZipHandle->mFile.GetURIString(mURI);
278 }
279 return rv;
280 }
282 nsresult nsZipArchive::OpenArchive(nsIFile *aFile)
283 {
284 nsRefPtr<nsZipHandle> handle;
285 #if defined(XP_WIN)
286 mozilla::AutoFDClose fd;
287 nsresult rv = nsZipHandle::Init(aFile, getter_AddRefs(handle), &fd.rwget());
288 #else
289 nsresult rv = nsZipHandle::Init(aFile, getter_AddRefs(handle));
290 #endif
291 if (NS_FAILED(rv))
292 return rv;
294 #if defined(XP_WIN)
295 return OpenArchive(handle, fd.get());
296 #else
297 return OpenArchive(handle);
298 #endif
299 }
301 //---------------------------------------------
302 // nsZipArchive::Test
303 //---------------------------------------------
304 nsresult nsZipArchive::Test(const char *aEntryName)
305 {
306 nsZipItem* currItem;
308 if (aEntryName) // only test specified item
309 {
310 currItem = GetItem(aEntryName);
311 if (!currItem)
312 return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST;
313 //-- don't test (synthetic) directory items
314 if (currItem->IsDirectory())
315 return NS_OK;
316 return ExtractFile(currItem, 0, 0);
317 }
319 // test all items in archive
320 for (int i = 0; i < ZIP_TABSIZE; i++) {
321 for (currItem = mFiles[i]; currItem; currItem = currItem->next) {
322 //-- don't test (synthetic) directory items
323 if (currItem->IsDirectory())
324 continue;
325 nsresult rv = ExtractFile(currItem, 0, 0);
326 if (rv != NS_OK)
327 return rv;
328 }
329 }
331 return NS_OK;
332 }
334 //---------------------------------------------
335 // nsZipArchive::CloseArchive
336 //---------------------------------------------
337 nsresult nsZipArchive::CloseArchive()
338 {
339 if (mFd) {
340 PL_FinishArenaPool(&mArena);
341 mFd = nullptr;
342 }
344 // CAUTION:
345 // We don't need to delete each of the nsZipItem as the memory for
346 // the zip item and the filename it holds are both allocated from the Arena.
347 // Hence, destroying the Arena is like destroying all the memory
348 // for all the nsZipItem in one shot. But if the ~nsZipItem is doing
349 // anything more than cleaning up memory, we should start calling it.
350 // Let us also cleanup the mFiles table for re-use on the next 'open' call
351 memset(mFiles, 0, sizeof(mFiles));
352 mBuiltSynthetics = false;
353 return NS_OK;
354 }
356 //---------------------------------------------
357 // nsZipArchive::GetItem
358 //---------------------------------------------
359 nsZipItem* nsZipArchive::GetItem(const char * aEntryName)
360 {
361 if (aEntryName) {
362 uint32_t len = strlen(aEntryName);
363 //-- If the request is for a directory, make sure that synthetic entries
364 //-- are created for the directories without their own entry.
365 if (!mBuiltSynthetics) {
366 if ((len > 0) && (aEntryName[len-1] == '/')) {
367 if (BuildSynthetics() != NS_OK)
368 return 0;
369 }
370 }
371 MOZ_WIN_MEM_TRY_BEGIN
372 nsZipItem* item = mFiles[ HashName(aEntryName, len) ];
373 while (item) {
374 if ((len == item->nameLength) &&
375 (!memcmp(aEntryName, item->Name(), len))) {
377 // Successful GetItem() is a good indicator that the file is about to be read
378 zipLog.Write(mURI, aEntryName);
379 return item; //-- found it
380 }
381 item = item->next;
382 }
383 MOZ_WIN_MEM_TRY_CATCH(return nullptr)
384 }
385 return nullptr;
386 }
388 //---------------------------------------------
389 // nsZipArchive::ExtractFile
390 // This extracts the item to the filehandle provided.
391 // If 'aFd' is null, it only tests the extraction.
392 // On extraction error(s) it removes the file.
393 // When needed, it also resolves the symlink.
394 //---------------------------------------------
395 nsresult nsZipArchive::ExtractFile(nsZipItem *item, const char *outname,
396 PRFileDesc* aFd)
397 {
398 if (!item)
399 return NS_ERROR_ILLEGAL_VALUE;
400 if (!mFd)
401 return NS_ERROR_FAILURE;
403 // Directory extraction is handled in nsJAR::Extract,
404 // so the item to be extracted should never be a directory
405 PR_ASSERT(!item->IsDirectory());
407 Bytef outbuf[ZIP_BUFLEN];
409 nsZipCursor cursor(item, this, outbuf, ZIP_BUFLEN, true);
411 nsresult rv = NS_OK;
413 while (true) {
414 uint32_t count = 0;
415 uint8_t* buf = cursor.Read(&count);
416 if (!buf) {
417 rv = NS_ERROR_FILE_CORRUPTED;
418 break;
419 } else if (count == 0) {
420 break;
421 }
423 if (aFd && PR_Write(aFd, buf, count) < (READTYPE)count) {
424 rv = NS_ERROR_FILE_DISK_FULL;
425 break;
426 }
427 }
429 //-- delete the file on errors, or resolve symlink if needed
430 if (aFd) {
431 PR_Close(aFd);
432 if (rv != NS_OK)
433 PR_Delete(outname);
434 #ifdef XP_UNIX
435 else if (item->IsSymlink())
436 rv = ResolveSymlink(outname);
437 #endif
438 }
440 return rv;
441 }
443 //---------------------------------------------
444 // nsZipArchive::FindInit
445 //---------------------------------------------
446 nsresult
447 nsZipArchive::FindInit(const char * aPattern, nsZipFind **aFind)
448 {
449 if (!aFind)
450 return NS_ERROR_ILLEGAL_VALUE;
452 // null out param in case an error happens
453 *aFind = nullptr;
455 bool regExp = false;
456 char* pattern = 0;
458 // Create synthetic directory entries on demand
459 nsresult rv = BuildSynthetics();
460 if (rv != NS_OK)
461 return rv;
463 // validate the pattern
464 if (aPattern)
465 {
466 switch (NS_WildCardValid((char*)aPattern))
467 {
468 case INVALID_SXP:
469 return NS_ERROR_ILLEGAL_VALUE;
471 case NON_SXP:
472 regExp = false;
473 break;
475 case VALID_SXP:
476 regExp = true;
477 break;
479 default:
480 // undocumented return value from RegExpValid!
481 PR_ASSERT(false);
482 return NS_ERROR_ILLEGAL_VALUE;
483 }
485 pattern = PL_strdup(aPattern);
486 if (!pattern)
487 return NS_ERROR_OUT_OF_MEMORY;
488 }
490 *aFind = new nsZipFind(this, pattern, regExp);
491 if (!*aFind) {
492 PL_strfree(pattern);
493 return NS_ERROR_OUT_OF_MEMORY;
494 }
496 return NS_OK;
497 }
501 //---------------------------------------------
502 // nsZipFind::FindNext
503 //---------------------------------------------
504 nsresult nsZipFind::FindNext(const char ** aResult, uint16_t *aNameLen)
505 {
506 if (!mArchive || !aResult || !aNameLen)
507 return NS_ERROR_ILLEGAL_VALUE;
509 *aResult = 0;
510 *aNameLen = 0;
511 MOZ_WIN_MEM_TRY_BEGIN
512 // we start from last match, look for next
513 while (mSlot < ZIP_TABSIZE)
514 {
515 // move to next in current chain, or move to new slot
516 mItem = mItem ? mItem->next : mArchive->mFiles[mSlot];
518 bool found = false;
519 if (!mItem)
520 ++mSlot; // no more in this chain, move to next slot
521 else if (!mPattern)
522 found = true; // always match
523 else if (mRegExp)
524 {
525 char buf[kMaxNameLength+1];
526 memcpy(buf, mItem->Name(), mItem->nameLength);
527 buf[mItem->nameLength]='\0';
528 found = (NS_WildCardMatch(buf, mPattern, false) == MATCH);
529 }
530 else
531 found = ((mItem->nameLength == strlen(mPattern)) &&
532 (memcmp(mItem->Name(), mPattern, mItem->nameLength) == 0));
533 if (found) {
534 // Need also to return the name length, as it is NOT zero-terminatdd...
535 *aResult = mItem->Name();
536 *aNameLen = mItem->nameLength;
537 return NS_OK;
538 }
539 }
540 MOZ_WIN_MEM_TRY_CATCH(return NS_ERROR_FAILURE)
541 return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST;
542 }
544 #ifdef XP_UNIX
545 //---------------------------------------------
546 // ResolveSymlink
547 //---------------------------------------------
548 static nsresult ResolveSymlink(const char *path)
549 {
550 PRFileDesc * fIn = PR_Open(path, PR_RDONLY, 0000);
551 if (!fIn)
552 return NS_ERROR_FILE_DISK_FULL;
554 char buf[PATH_MAX+1];
555 int32_t length = PR_Read(fIn, (void*)buf, PATH_MAX);
556 PR_Close(fIn);
558 if ( (length <= 0)
559 || ((buf[length] = 0, PR_Delete(path)) != 0)
560 || (symlink(buf, path) != 0))
561 {
562 return NS_ERROR_FILE_DISK_FULL;
563 }
564 return NS_OK;
565 }
566 #endif
568 //***********************************************************
569 // nsZipArchive -- private implementation
570 //***********************************************************
572 //---------------------------------------------
573 // nsZipArchive::CreateZipItem
574 //---------------------------------------------
575 nsZipItem* nsZipArchive::CreateZipItem()
576 {
577 // Arena allocate the nsZipItem
578 void *mem;
579 PL_ARENA_ALLOCATE(mem, &mArena, sizeof(nsZipItem));
580 return (nsZipItem*)mem;
581 }
583 //---------------------------------------------
584 // nsZipArchive::BuildFileList
585 //---------------------------------------------
586 nsresult nsZipArchive::BuildFileList(PRFileDesc *aFd)
587 {
588 // Get archive size using end pos
589 const uint8_t* buf;
590 const uint8_t* startp = mFd->mFileData;
591 const uint8_t* endp = startp + mFd->mLen;
592 MOZ_WIN_MEM_TRY_BEGIN
593 uint32_t centralOffset = 4;
594 if (mFd->mLen > ZIPCENTRAL_SIZE && xtolong(startp + centralOffset) == CENTRALSIG) {
595 // Success means optimized jar layout from bug 559961 is in effect
596 uint32_t readaheadLength = xtolong(startp);
597 if (readaheadLength) {
598 #if defined(XP_UNIX)
599 madvise(const_cast<uint8_t*>(startp), readaheadLength, MADV_WILLNEED);
600 #elif defined(XP_WIN)
601 if (aFd) {
602 HANDLE hFile = (HANDLE) PR_FileDesc2NativeHandle(aFd);
603 mozilla::ReadAhead(hFile, 0, readaheadLength);
604 }
605 #endif
606 }
607 } else {
608 for (buf = endp - ZIPEND_SIZE; buf > startp; buf--)
609 {
610 if (xtolong(buf) == ENDSIG) {
611 centralOffset = xtolong(((ZipEnd *)buf)->offset_central_dir);
612 break;
613 }
614 }
615 }
617 if (!centralOffset)
618 return NS_ERROR_FILE_CORRUPTED;
620 //-- Read the central directory headers
621 buf = startp + centralOffset;
622 uint32_t sig = 0;
623 while (buf + int32_t(sizeof(uint32_t)) <= endp &&
624 (sig = xtolong(buf)) == CENTRALSIG) {
625 // Make sure there is enough data available.
626 if (endp - buf < ZIPCENTRAL_SIZE)
627 return NS_ERROR_FILE_CORRUPTED;
629 // Read the fixed-size data.
630 ZipCentral* central = (ZipCentral*)buf;
632 uint16_t namelen = xtoint(central->filename_len);
633 uint16_t extralen = xtoint(central->extrafield_len);
634 uint16_t commentlen = xtoint(central->commentfield_len);
636 // Point to the next item at the top of loop
637 buf += ZIPCENTRAL_SIZE + namelen + extralen + commentlen;
639 // Sanity check variable sizes and refuse to deal with
640 // anything too big: it's likely a corrupt archive.
641 if (namelen < 1 ||
642 namelen > kMaxNameLength ||
643 buf >= endp) {
644 return NS_ERROR_FILE_CORRUPTED;
645 }
647 nsZipItem* item = CreateZipItem();
648 if (!item)
649 return NS_ERROR_OUT_OF_MEMORY;
651 item->central = central;
652 item->nameLength = namelen;
653 item->isSynthetic = false;
655 // Add item to file table
656 uint32_t hash = HashName(item->Name(), namelen);
657 item->next = mFiles[hash];
658 mFiles[hash] = item;
660 sig = 0;
661 } /* while reading central directory records */
663 if (sig != ENDSIG)
664 return NS_ERROR_FILE_CORRUPTED;
666 // Make the comment available for consumers.
667 if (endp - buf >= ZIPEND_SIZE) {
668 ZipEnd *zipend = (ZipEnd *)buf;
670 buf += ZIPEND_SIZE;
671 uint16_t commentlen = xtoint(zipend->commentfield_len);
672 if (endp - buf >= commentlen) {
673 mCommentPtr = (const char *)buf;
674 mCommentLen = commentlen;
675 }
676 }
678 MOZ_WIN_MEM_TRY_CATCH(return NS_ERROR_FAILURE)
679 return NS_OK;
680 }
682 //---------------------------------------------
683 // nsZipArchive::BuildSynthetics
684 //---------------------------------------------
685 nsresult nsZipArchive::BuildSynthetics()
686 {
687 if (mBuiltSynthetics)
688 return NS_OK;
689 mBuiltSynthetics = true;
691 MOZ_WIN_MEM_TRY_BEGIN
692 // Create synthetic entries for any missing directories.
693 // Do this when all ziptable has scanned to prevent double entries.
694 for (int i = 0; i < ZIP_TABSIZE; ++i)
695 {
696 for (nsZipItem* item = mFiles[i]; item != nullptr; item = item->next)
697 {
698 if (item->isSynthetic)
699 continue;
701 //-- add entries for directories in the current item's path
702 //-- go from end to beginning, because then we can stop trying
703 //-- to create diritems if we find that the diritem we want to
704 //-- create already exists
705 //-- start just before the last char so as to not add the item
706 //-- twice if it's a directory
707 uint16_t namelen = item->nameLength;
708 MOZ_ASSERT(namelen > 0, "Attempt to build synthetic for zero-length entry name!");
709 const char *name = item->Name();
710 for (uint16_t dirlen = namelen - 1; dirlen > 0; dirlen--)
711 {
712 if (name[dirlen-1] != '/')
713 continue;
715 // The character before this is '/', so if this is also '/' then we
716 // have an empty path component. Skip it.
717 if (name[dirlen] == '/')
718 continue;
720 // Is the directory already in the file table?
721 uint32_t hash = HashName(item->Name(), dirlen);
722 bool found = false;
723 for (nsZipItem* zi = mFiles[hash]; zi != nullptr; zi = zi->next)
724 {
725 if ((dirlen == zi->nameLength) &&
726 (0 == memcmp(item->Name(), zi->Name(), dirlen)))
727 {
728 // we've already added this dir and all its parents
729 found = true;
730 break;
731 }
732 }
733 // if the directory was found, break out of the directory
734 // creation loop now that we know all implicit directories
735 // are there -- otherwise, start creating the zip item
736 if (found)
737 break;
739 nsZipItem* diritem = CreateZipItem();
740 if (!diritem)
741 return NS_ERROR_OUT_OF_MEMORY;
743 // Point to the central record of the original item for the name part.
744 diritem->central = item->central;
745 diritem->nameLength = dirlen;
746 diritem->isSynthetic = true;
748 // add diritem to the file table
749 diritem->next = mFiles[hash];
750 mFiles[hash] = diritem;
751 } /* end processing of dirs in item's name */
752 }
753 }
754 MOZ_WIN_MEM_TRY_CATCH(return NS_ERROR_FAILURE)
755 return NS_OK;
756 }
758 nsZipHandle* nsZipArchive::GetFD()
759 {
760 if (!mFd)
761 return nullptr;
762 return mFd.get();
763 }
765 //---------------------------------------------
766 // nsZipArchive::GetData
767 //---------------------------------------------
768 const uint8_t* nsZipArchive::GetData(nsZipItem* aItem)
769 {
770 PR_ASSERT (aItem);
771 MOZ_WIN_MEM_TRY_BEGIN
772 //-- read local header to get variable length values and calculate
773 //-- the real data offset
774 uint32_t len = mFd->mLen;
775 const uint8_t* data = mFd->mFileData;
776 uint32_t offset = aItem->LocalOffset();
777 if (offset + ZIPLOCAL_SIZE > len)
778 return nullptr;
780 // -- check signature before using the structure, in case the zip file is corrupt
781 ZipLocal* Local = (ZipLocal*)(data + offset);
782 if ((xtolong(Local->signature) != LOCALSIG))
783 return nullptr;
785 //-- NOTE: extralen is different in central header and local header
786 //-- for archives created using the Unix "zip" utility. To set
787 //-- the offset accurately we need the _local_ extralen.
788 offset += ZIPLOCAL_SIZE +
789 xtoint(Local->filename_len) +
790 xtoint(Local->extrafield_len);
792 // -- check if there is enough source data in the file
793 if (offset + aItem->Size() > len)
794 return nullptr;
796 return data + offset;
797 MOZ_WIN_MEM_TRY_CATCH(return nullptr)
798 }
800 // nsZipArchive::GetComment
801 bool nsZipArchive::GetComment(nsACString &aComment)
802 {
803 MOZ_WIN_MEM_TRY_BEGIN
804 aComment.Assign(mCommentPtr, mCommentLen);
805 MOZ_WIN_MEM_TRY_CATCH(return false)
806 return true;
807 }
809 //---------------------------------------------
810 // nsZipArchive::SizeOfMapping
811 //---------------------------------------------
812 int64_t nsZipArchive::SizeOfMapping()
813 {
814 return mFd ? mFd->SizeOfMapping() : 0;
815 }
817 //------------------------------------------
818 // nsZipArchive constructor and destructor
819 //------------------------------------------
821 nsZipArchive::nsZipArchive()
822 : mRefCnt(0)
823 , mBuiltSynthetics(false)
824 {
825 zipLog.AddRef();
827 MOZ_COUNT_CTOR(nsZipArchive);
829 // initialize the table to nullptr
830 memset(mFiles, 0, sizeof(mFiles));
831 }
833 NS_IMPL_ADDREF(nsZipArchive)
834 NS_IMPL_RELEASE(nsZipArchive)
836 nsZipArchive::~nsZipArchive()
837 {
838 CloseArchive();
840 MOZ_COUNT_DTOR(nsZipArchive);
842 zipLog.Release();
843 }
846 //------------------------------------------
847 // nsZipFind constructor and destructor
848 //------------------------------------------
850 nsZipFind::nsZipFind(nsZipArchive* aZip, char* aPattern, bool aRegExp) :
851 mArchive(aZip),
852 mPattern(aPattern),
853 mItem(0),
854 mSlot(0),
855 mRegExp(aRegExp)
856 {
857 MOZ_COUNT_CTOR(nsZipFind);
858 }
860 nsZipFind::~nsZipFind()
861 {
862 PL_strfree(mPattern);
864 MOZ_COUNT_DTOR(nsZipFind);
865 }
867 //------------------------------------------
868 // helper functions
869 //------------------------------------------
871 /*
872 * HashName
873 *
874 * returns a hash key for the entry name
875 */
876 static uint32_t HashName(const char* aName, uint16_t len)
877 {
878 PR_ASSERT(aName != 0);
880 const uint8_t* p = (const uint8_t*)aName;
881 const uint8_t* endp = p + len;
882 uint32_t val = 0;
883 while (p != endp) {
884 val = val*37 + *p++;
885 }
887 return (val % ZIP_TABSIZE);
888 }
890 /*
891 * x t o i n t
892 *
893 * Converts a two byte ugly endianed integer
894 * to our platform's integer.
895 */
896 static uint16_t xtoint (const uint8_t *ii)
897 {
898 return (uint16_t) ((ii [0]) | (ii [1] << 8));
899 }
901 /*
902 * x t o l o n g
903 *
904 * Converts a four byte ugly endianed integer
905 * to our platform's integer.
906 */
907 static uint32_t xtolong (const uint8_t *ll)
908 {
909 return (uint32_t)( (ll [0] << 0) |
910 (ll [1] << 8) |
911 (ll [2] << 16) |
912 (ll [3] << 24) );
913 }
915 /*
916 * GetModTime
917 *
918 * returns last modification time in microseconds
919 */
920 static PRTime GetModTime(uint16_t aDate, uint16_t aTime)
921 {
922 // Note that on DST shift we can't handle correctly the hour that is valid
923 // in both DST zones
924 PRExplodedTime time;
926 time.tm_usec = 0;
928 time.tm_hour = (aTime >> 11) & 0x1F;
929 time.tm_min = (aTime >> 5) & 0x3F;
930 time.tm_sec = (aTime & 0x1F) * 2;
932 time.tm_year = (aDate >> 9) + 1980;
933 time.tm_month = ((aDate >> 5) & 0x0F) - 1;
934 time.tm_mday = aDate & 0x1F;
936 time.tm_params.tp_gmt_offset = 0;
937 time.tm_params.tp_dst_offset = 0;
939 PR_NormalizeTime(&time, PR_GMTParameters);
940 time.tm_params.tp_gmt_offset = PR_LocalTimeParameters(&time).tp_gmt_offset;
941 PR_NormalizeTime(&time, PR_GMTParameters);
942 time.tm_params.tp_dst_offset = PR_LocalTimeParameters(&time).tp_dst_offset;
944 return PR_ImplodeTime(&time);
945 }
947 uint32_t nsZipItem::LocalOffset()
948 {
949 return xtolong(central->localhdr_offset);
950 }
952 uint32_t nsZipItem::Size()
953 {
954 return isSynthetic ? 0 : xtolong(central->size);
955 }
957 uint32_t nsZipItem::RealSize()
958 {
959 return isSynthetic ? 0 : xtolong(central->orglen);
960 }
962 uint32_t nsZipItem::CRC32()
963 {
964 return isSynthetic ? 0 : xtolong(central->crc32);
965 }
967 uint16_t nsZipItem::Date()
968 {
969 return isSynthetic ? kSyntheticDate : xtoint(central->date);
970 }
972 uint16_t nsZipItem::Time()
973 {
974 return isSynthetic ? kSyntheticTime : xtoint(central->time);
975 }
977 uint16_t nsZipItem::Compression()
978 {
979 return isSynthetic ? STORED : xtoint(central->method);
980 }
982 bool nsZipItem::IsDirectory()
983 {
984 return isSynthetic || ((nameLength > 0) && ('/' == Name()[nameLength - 1]));
985 }
987 uint16_t nsZipItem::Mode()
988 {
989 if (isSynthetic) return 0755;
990 return ((uint16_t)(central->external_attributes[2]) | 0x100);
991 }
993 const uint8_t * nsZipItem::GetExtraField(uint16_t aTag, uint16_t *aBlockSize)
994 {
995 if (isSynthetic) return nullptr;
996 MOZ_WIN_MEM_TRY_BEGIN
997 const unsigned char *buf = ((const unsigned char*)central) + ZIPCENTRAL_SIZE +
998 nameLength;
999 uint32_t buflen = (uint32_t)xtoint(central->extrafield_len);
1000 uint32_t pos = 0;
1001 uint16_t tag, blocksize;
1003 while (buf && (pos + 4) <= buflen) {
1004 tag = xtoint(buf + pos);
1005 blocksize = xtoint(buf + pos + 2);
1007 if (aTag == tag && (pos + 4 + blocksize) <= buflen) {
1008 *aBlockSize = blocksize;
1009 return buf + pos;
1010 }
1012 pos += blocksize + 4;
1013 }
1015 MOZ_WIN_MEM_TRY_CATCH(return nullptr)
1016 return nullptr;
1017 }
1020 PRTime nsZipItem::LastModTime()
1021 {
1022 if (isSynthetic) return GetModTime(kSyntheticDate, kSyntheticTime);
1024 // Try to read timestamp from extra field
1025 uint16_t blocksize;
1026 const uint8_t *tsField = GetExtraField(EXTENDED_TIMESTAMP_FIELD, &blocksize);
1027 if (tsField && blocksize >= 5 && tsField[4] & EXTENDED_TIMESTAMP_MODTIME) {
1028 return (PRTime)(xtolong(tsField + 5)) * PR_USEC_PER_SEC;
1029 }
1031 return GetModTime(Date(), Time());
1032 }
1034 #ifdef XP_UNIX
1035 bool nsZipItem::IsSymlink()
1036 {
1037 if (isSynthetic) return false;
1038 return (xtoint(central->external_attributes+2) & S_IFMT) == S_IFLNK;
1039 }
1040 #endif
1042 nsZipCursor::nsZipCursor(nsZipItem *item, nsZipArchive *aZip, uint8_t* aBuf, uint32_t aBufSize, bool doCRC) :
1043 mItem(item),
1044 mBuf(aBuf),
1045 mBufSize(aBufSize),
1046 mDoCRC(doCRC)
1047 {
1048 if (mItem->Compression() == DEFLATED) {
1049 #ifdef DEBUG
1050 nsresult status =
1051 #endif
1052 gZlibInit(&mZs);
1053 NS_ASSERTION(status == NS_OK, "Zlib failed to initialize");
1054 NS_ASSERTION(aBuf, "Must pass in a buffer for DEFLATED nsZipItem");
1055 }
1057 mZs.avail_in = item->Size();
1058 mZs.next_in = (Bytef*)aZip->GetData(item);
1060 if (doCRC)
1061 mCRC = crc32(0L, Z_NULL, 0);
1062 }
1064 nsZipCursor::~nsZipCursor()
1065 {
1066 if (mItem->Compression() == DEFLATED) {
1067 inflateEnd(&mZs);
1068 }
1069 }
1071 uint8_t* nsZipCursor::ReadOrCopy(uint32_t *aBytesRead, bool aCopy) {
1072 int zerr;
1073 uint8_t *buf = nullptr;
1074 bool verifyCRC = true;
1076 if (!mZs.next_in)
1077 return nullptr;
1078 MOZ_WIN_MEM_TRY_BEGIN
1079 switch (mItem->Compression()) {
1080 case STORED:
1081 if (!aCopy) {
1082 *aBytesRead = mZs.avail_in;
1083 buf = mZs.next_in;
1084 mZs.next_in += mZs.avail_in;
1085 mZs.avail_in = 0;
1086 } else {
1087 *aBytesRead = mZs.avail_in > mBufSize ? mBufSize : mZs.avail_in;
1088 memcpy(mBuf, mZs.next_in, *aBytesRead);
1089 mZs.avail_in -= *aBytesRead;
1090 mZs.next_in += *aBytesRead;
1091 }
1092 break;
1093 case DEFLATED:
1094 buf = mBuf;
1095 mZs.next_out = buf;
1096 mZs.avail_out = mBufSize;
1098 zerr = inflate(&mZs, Z_PARTIAL_FLUSH);
1099 if (zerr != Z_OK && zerr != Z_STREAM_END)
1100 return nullptr;
1102 *aBytesRead = mZs.next_out - buf;
1103 verifyCRC = (zerr == Z_STREAM_END);
1104 break;
1105 default:
1106 return nullptr;
1107 }
1109 if (mDoCRC) {
1110 mCRC = crc32(mCRC, (const unsigned char*)buf, *aBytesRead);
1111 if (verifyCRC && mCRC != mItem->CRC32())
1112 return nullptr;
1113 }
1114 MOZ_WIN_MEM_TRY_CATCH(return nullptr)
1115 return buf;
1116 }
1118 nsZipItemPtr_base::nsZipItemPtr_base(nsZipArchive *aZip, const char * aEntryName, bool doCRC) :
1119 mReturnBuf(nullptr)
1120 {
1121 // make sure the ziparchive hangs around
1122 mZipHandle = aZip->GetFD();
1124 nsZipItem* item = aZip->GetItem(aEntryName);
1125 if (!item)
1126 return;
1128 uint32_t size = 0;
1129 if (item->Compression() == DEFLATED) {
1130 size = item->RealSize();
1131 mAutoBuf = new ((fallible_t())) uint8_t[size];
1132 if (!mAutoBuf) {
1133 return;
1134 }
1135 }
1137 nsZipCursor cursor(item, aZip, mAutoBuf, size, doCRC);
1138 mReturnBuf = cursor.Read(&mReadlen);
1139 if (!mReturnBuf) {
1140 return;
1141 }
1143 if (mReadlen != item->RealSize()) {
1144 NS_ASSERTION(mReadlen == item->RealSize(), "nsZipCursor underflow");
1145 mReturnBuf = nullptr;
1146 return;
1147 }
1148 }