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/. */
5
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 */
12
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"
17
18 #define READTYPE int32_t
19 #include "zlib.h"
20 #include "nsISupportsUtils.h"
21 #include "prio.h"
22 #include "plstr.h"
23 #include "mozilla/Logging.h"
24 #include "mozilla/UniquePtrExtensions.h"
25 #include "stdlib.h"
26 #include "nsWildCard.h"
27 #include "nsZipArchive.h"
28 #include "nsString.h"
29 #include "prenv.h"
30 #if defined(XP_WIN)
31 #include <windows.h>
32 #endif
33
34 // For placement new used for arena allocations of zip file list
35 #include <new>
36 #define ZIP_ARENABLOCKSIZE (1*1024)
37
38 #ifdef XP_UNIX
39 #include <sys/mman.h>
40 #include <sys/types.h>
41 #include <sys/stat.h>
42 #include <limits.h>
43 #include <unistd.h>
44 #elif defined(XP_WIN)
45 #include <io.h>
46 #endif
47
48 #ifdef __SYMBIAN32__
49 #include <sys/syslimits.h>
50 #endif /*__SYMBIAN32__*/
51
52
53 #ifndef XP_UNIX /* we need some constants defined in limits.h and unistd.h */
54 # ifndef S_IFMT
55 # define S_IFMT 0170000
56 # endif
57 # ifndef S_IFLNK
58 # define S_IFLNK 0120000
59 # endif
60 # ifndef PATH_MAX
61 # define PATH_MAX 1024
62 # endif
63 #endif /* XP_UNIX */
64
65 #ifdef XP_WIN
66 #include "private/pprio.h" // To get PR_ImportFile
67 #endif
68
69 using namespace mozilla;
70
71 static const uint32_t kMaxNameLength = PATH_MAX; /* Maximum name length */
72 // For synthetic zip entries. Date/time corresponds to 1980-01-01 00:00.
73 static const uint16_t kSyntheticTime = 0;
74 static const uint16_t kSyntheticDate = (1 + (1 << 5) + (0 << 9));
75
76 static uint16_t xtoint(const uint8_t *ii);
77 static uint32_t xtolong(const uint8_t *ll);
78 static uint32_t HashName(const char* aName, uint16_t nameLen);
79 #ifdef XP_UNIX
80 static nsresult ResolveSymlink(const char *path);
81 #endif
82
83 class ZipArchiveLogger {
84 public:
Write(const nsACString & zip,const char * entry) const85 void Write(const nsACString &zip, const char *entry) const {
86 if (!fd) {
87 char *env = PR_GetEnv("MOZ_JAR_LOG_FILE");
88 if (!env)
89 return;
90
91 nsCOMPtr<nsIFile> logFile;
92 nsresult rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(env), false, getter_AddRefs(logFile));
93 if (NS_FAILED(rv))
94 return;
95
96 // Create the log file and its parent directory (in case it doesn't exist)
97 rv = logFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
98 if (NS_FAILED(rv))
99 return;
100
101 PRFileDesc* file;
102 #ifdef XP_WIN
103 // PR_APPEND is racy on Windows, so open a handle ourselves with flags that
104 // will work, and use PR_ImportFile to make it a PRFileDesc.
105 // This can go away when bug 840435 is fixed.
106 nsAutoString path;
107 logFile->GetPath(path);
108 if (path.IsEmpty())
109 return;
110 HANDLE handle = CreateFileW(path.get(), FILE_APPEND_DATA, FILE_SHARE_WRITE,
111 nullptr, OPEN_ALWAYS, 0, nullptr);
112 if (handle == INVALID_HANDLE_VALUE)
113 return;
114 file = PR_ImportFile((PROsfd)handle);
115 if (!file)
116 return;
117 #else
118 rv = logFile->OpenNSPRFileDesc(PR_WRONLY|PR_CREATE_FILE|PR_APPEND, 0644, &file);
119 if (NS_FAILED(rv))
120 return;
121 #endif
122 fd = file;
123 }
124 nsCString buf(zip);
125 buf.Append(' ');
126 buf.Append(entry);
127 buf.Append('\n');
128 PR_Write(fd, buf.get(), buf.Length());
129 }
130
AddRef()131 void AddRef() {
132 MOZ_ASSERT(refCnt >= 0);
133 ++refCnt;
134 }
135
Release()136 void Release() {
137 MOZ_ASSERT(refCnt > 0);
138 if ((0 == --refCnt) && fd) {
139 PR_Close(fd);
140 fd = nullptr;
141 }
142 }
143 private:
144 int refCnt;
145 mutable PRFileDesc *fd;
146 };
147
148 static ZipArchiveLogger zipLog;
149
150 //***********************************************************
151 // For every inflation the following allocations are done:
152 // malloc(1 * 9520)
153 // malloc(32768 * 1)
154 //***********************************************************
155
gZlibInit(z_stream * zs)156 nsresult gZlibInit(z_stream *zs)
157 {
158 memset(zs, 0, sizeof(z_stream));
159 int zerr = inflateInit2(zs, -MAX_WBITS);
160 if (zerr != Z_OK) return NS_ERROR_OUT_OF_MEMORY;
161
162 return NS_OK;
163 }
164
nsZipHandle()165 nsZipHandle::nsZipHandle()
166 : mFileData(nullptr)
167 , mLen(0)
168 , mMap(nullptr)
169 , mRefCnt(0)
170 , mFileStart(nullptr)
171 , mTotalLen(0)
172 {
173 MOZ_COUNT_CTOR(nsZipHandle);
174 }
175
176 NS_IMPL_ADDREF(nsZipHandle)
NS_IMPL_RELEASE(nsZipHandle)177 NS_IMPL_RELEASE(nsZipHandle)
178
179 nsresult nsZipHandle::Init(nsIFile *file, nsZipHandle **ret,
180 PRFileDesc **aFd)
181 {
182 mozilla::AutoFDClose fd;
183 int32_t flags = PR_RDONLY;
184 #if defined(XP_WIN)
185 flags |= nsIFile::OS_READAHEAD;
186 #endif
187 nsresult rv = file->OpenNSPRFileDesc(flags, 0000, &fd.rwget());
188 if (NS_FAILED(rv))
189 return rv;
190
191 int64_t size = PR_Available64(fd);
192 if (size >= INT32_MAX)
193 return NS_ERROR_FILE_TOO_BIG;
194
195 PRFileMap *map = PR_CreateFileMap(fd, size, PR_PROT_READONLY);
196 if (!map)
197 return NS_ERROR_FAILURE;
198
199 uint8_t *buf = (uint8_t*) PR_MemMap(map, 0, (uint32_t) size);
200 // Bug 525755: PR_MemMap fails when fd points at something other than a normal file.
201 if (!buf) {
202 PR_CloseFileMap(map);
203 return NS_ERROR_FAILURE;
204 }
205
206 RefPtr<nsZipHandle> handle = new nsZipHandle();
207 if (!handle) {
208 PR_MemUnmap(buf, (uint32_t) size);
209 PR_CloseFileMap(map);
210 return NS_ERROR_OUT_OF_MEMORY;
211 }
212
213 #if defined(XP_WIN)
214 if (aFd) {
215 *aFd = fd.forget();
216 }
217 #else
218 handle->mNSPRFileDesc = fd.forget();
219 #endif
220 handle->mMap = map;
221 handle->mFile.Init(file);
222 handle->mTotalLen = (uint32_t) size;
223 handle->mFileStart = buf;
224 rv = handle->findDataStart();
225 if (NS_FAILED(rv)) {
226 PR_MemUnmap(buf, (uint32_t) size);
227 PR_CloseFileMap(map);
228 return rv;
229 }
230 handle.forget(ret);
231 return NS_OK;
232 }
233
Init(nsZipArchive * zip,const char * entry,nsZipHandle ** ret)234 nsresult nsZipHandle::Init(nsZipArchive *zip, const char *entry,
235 nsZipHandle **ret)
236 {
237 RefPtr<nsZipHandle> handle = new nsZipHandle();
238 if (!handle)
239 return NS_ERROR_OUT_OF_MEMORY;
240
241 handle->mBuf = new nsZipItemPtr<uint8_t>(zip, entry);
242 if (!handle->mBuf)
243 return NS_ERROR_OUT_OF_MEMORY;
244
245 if (!handle->mBuf->Buffer())
246 return NS_ERROR_UNEXPECTED;
247
248 handle->mMap = nullptr;
249 handle->mFile.Init(zip, entry);
250 handle->mTotalLen = handle->mBuf->Length();
251 handle->mFileStart = handle->mBuf->Buffer();
252 nsresult rv = handle->findDataStart();
253 if (NS_FAILED(rv)) {
254 return rv;
255 }
256 handle.forget(ret);
257 return NS_OK;
258 }
259
Init(const uint8_t * aData,uint32_t aLen,nsZipHandle ** aRet)260 nsresult nsZipHandle::Init(const uint8_t* aData, uint32_t aLen,
261 nsZipHandle **aRet)
262 {
263 RefPtr<nsZipHandle> handle = new nsZipHandle();
264
265 handle->mFileStart = aData;
266 handle->mTotalLen = aLen;
267 nsresult rv = handle->findDataStart();
268 if (NS_FAILED(rv)) {
269 return rv;
270 }
271 handle.forget(aRet);
272 return NS_OK;
273 }
274
275 // This function finds the start of the ZIP data. If the file is a regular ZIP,
276 // this is just the start of the file. If the file is a CRX file, the start of
277 // the data is after the CRX header.
278 // CRX header reference: (CRX version 2)
279 // Header requires little-endian byte ordering with 4-byte alignment.
280 // 32 bits : magicNumber - Defined as a |char m[] = "Cr24"|.
281 // Equivilant to |uint32_t m = 0x34327243|.
282 // 32 bits : version - Unsigned integer representing the CRX file
283 // format version. Currently equal to 2.
284 // 32 bits : pubKeyLength - Unsigned integer representing the length
285 // of the public key in bytes.
286 // 32 bits : sigLength - Unsigned integer representing the length
287 // of the signature in bytes.
288 // pubKeyLength : publicKey - Contents of the author's public key.
289 // sigLength : signature - Signature of the ZIP content.
290 // Signature is created using the RSA
291 // algorighm with the SHA-1 hash function.
findDataStart()292 nsresult nsZipHandle::findDataStart()
293 {
294 // In the CRX header, integers are 32 bits. Our pointer to the file is of
295 // type |uint8_t|, which is guaranteed to be 8 bits.
296 const uint32_t CRXIntSize = 4;
297
298 MOZ_WIN_MEM_TRY_BEGIN
299 if (mTotalLen > CRXIntSize * 4 && xtolong(mFileStart) == kCRXMagic) {
300 const uint8_t* headerData = mFileStart;
301 headerData += CRXIntSize * 2; // Skip magic number and version number
302 uint32_t pubKeyLength = xtolong(headerData);
303 headerData += CRXIntSize;
304 uint32_t sigLength = xtolong(headerData);
305 uint32_t headerSize = CRXIntSize * 4 + pubKeyLength + sigLength;
306 if (mTotalLen > headerSize) {
307 mLen = mTotalLen - headerSize;
308 mFileData = mFileStart + headerSize;
309 return NS_OK;
310 }
311 }
312 mLen = mTotalLen;
313 mFileData = mFileStart;
314 MOZ_WIN_MEM_TRY_CATCH(return NS_ERROR_FAILURE)
315 return NS_OK;
316 }
317
SizeOfMapping()318 int64_t nsZipHandle::SizeOfMapping()
319 {
320 return mTotalLen;
321 }
322
GetNSPRFileDesc(PRFileDesc ** aNSPRFileDesc)323 nsresult nsZipHandle::GetNSPRFileDesc(PRFileDesc** aNSPRFileDesc)
324 {
325 if (!aNSPRFileDesc) {
326 return NS_ERROR_ILLEGAL_VALUE;
327 }
328
329 *aNSPRFileDesc = mNSPRFileDesc;
330 if (!mNSPRFileDesc) {
331 return NS_ERROR_NOT_AVAILABLE;
332 }
333
334 return NS_OK;
335 }
336
~nsZipHandle()337 nsZipHandle::~nsZipHandle()
338 {
339 if (mMap) {
340 PR_MemUnmap((void *)mFileStart, mTotalLen);
341 PR_CloseFileMap(mMap);
342 }
343 mFileStart = nullptr;
344 mFileData = nullptr;
345 mMap = nullptr;
346 mBuf = nullptr;
347 MOZ_COUNT_DTOR(nsZipHandle);
348 }
349
350 //***********************************************************
351 // nsZipArchive -- public methods
352 //***********************************************************
353
354 //---------------------------------------------
355 // nsZipArchive::OpenArchive
356 //---------------------------------------------
OpenArchive(nsZipHandle * aZipHandle,PRFileDesc * aFd)357 nsresult nsZipArchive::OpenArchive(nsZipHandle *aZipHandle, PRFileDesc *aFd)
358 {
359 mFd = aZipHandle;
360
361 // Initialize our arena
362 PL_INIT_ARENA_POOL(&mArena, "ZipArena", ZIP_ARENABLOCKSIZE);
363
364 //-- get table of contents for archive
365 nsresult rv = BuildFileList(aFd);
366 if (NS_SUCCEEDED(rv)) {
367 if (aZipHandle->mFile)
368 aZipHandle->mFile.GetURIString(mURI);
369 }
370 return rv;
371 }
372
OpenArchive(nsIFile * aFile)373 nsresult nsZipArchive::OpenArchive(nsIFile *aFile)
374 {
375 RefPtr<nsZipHandle> handle;
376 #if defined(XP_WIN)
377 mozilla::AutoFDClose fd;
378 nsresult rv = nsZipHandle::Init(aFile, getter_AddRefs(handle),
379 &fd.rwget());
380 #else
381 nsresult rv = nsZipHandle::Init(aFile, getter_AddRefs(handle));
382 #endif
383 if (NS_FAILED(rv))
384 return rv;
385
386 #if defined(XP_WIN)
387 return OpenArchive(handle, fd.get());
388 #else
389 return OpenArchive(handle);
390 #endif
391 }
392
393 //---------------------------------------------
394 // nsZipArchive::Test
395 //---------------------------------------------
Test(const char * aEntryName)396 nsresult nsZipArchive::Test(const char *aEntryName)
397 {
398 nsZipItem* currItem;
399
400 if (aEntryName) // only test specified item
401 {
402 currItem = GetItem(aEntryName);
403 if (!currItem)
404 return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST;
405 //-- don't test (synthetic) directory items
406 if (currItem->IsDirectory())
407 return NS_OK;
408 return ExtractFile(currItem, 0, 0);
409 }
410
411 // test all items in archive
412 for (int i = 0; i < ZIP_TABSIZE; i++) {
413 for (currItem = mFiles[i]; currItem; currItem = currItem->next) {
414 //-- don't test (synthetic) directory items
415 if (currItem->IsDirectory())
416 continue;
417 nsresult rv = ExtractFile(currItem, 0, 0);
418 if (rv != NS_OK)
419 return rv;
420 }
421 }
422
423 return NS_OK;
424 }
425
426 //---------------------------------------------
427 // nsZipArchive::CloseArchive
428 //---------------------------------------------
CloseArchive()429 nsresult nsZipArchive::CloseArchive()
430 {
431 if (mFd) {
432 PL_FinishArenaPool(&mArena);
433 mFd = nullptr;
434 }
435
436 // CAUTION:
437 // We don't need to delete each of the nsZipItem as the memory for
438 // the zip item and the filename it holds are both allocated from the Arena.
439 // Hence, destroying the Arena is like destroying all the memory
440 // for all the nsZipItem in one shot. But if the ~nsZipItem is doing
441 // anything more than cleaning up memory, we should start calling it.
442 // Let us also cleanup the mFiles table for re-use on the next 'open' call
443 memset(mFiles, 0, sizeof(mFiles));
444 mBuiltSynthetics = false;
445 return NS_OK;
446 }
447
448 //---------------------------------------------
449 // nsZipArchive::GetItem
450 //---------------------------------------------
GetItem(const char * aEntryName)451 nsZipItem* nsZipArchive::GetItem(const char * aEntryName)
452 {
453 if (aEntryName) {
454 uint32_t len = strlen(aEntryName);
455 //-- If the request is for a directory, make sure that synthetic entries
456 //-- are created for the directories without their own entry.
457 if (!mBuiltSynthetics) {
458 if ((len > 0) && (aEntryName[len-1] == '/')) {
459 if (BuildSynthetics() != NS_OK)
460 return 0;
461 }
462 }
463 MOZ_WIN_MEM_TRY_BEGIN
464 nsZipItem* item = mFiles[ HashName(aEntryName, len) ];
465 while (item) {
466 if ((len == item->nameLength) &&
467 (!memcmp(aEntryName, item->Name(), len))) {
468
469 // Successful GetItem() is a good indicator that the file is about to be read
470 zipLog.Write(mURI, aEntryName);
471 return item; //-- found it
472 }
473 item = item->next;
474 }
475 MOZ_WIN_MEM_TRY_CATCH(return nullptr)
476 }
477 return nullptr;
478 }
479
480 //---------------------------------------------
481 // nsZipArchive::ExtractFile
482 // This extracts the item to the filehandle provided.
483 // If 'aFd' is null, it only tests the extraction.
484 // On extraction error(s) it removes the file.
485 // When needed, it also resolves the symlink.
486 //---------------------------------------------
ExtractFile(nsZipItem * item,const char * outname,PRFileDesc * aFd)487 nsresult nsZipArchive::ExtractFile(nsZipItem *item, const char *outname,
488 PRFileDesc* aFd)
489 {
490 if (!item)
491 return NS_ERROR_ILLEGAL_VALUE;
492 if (!mFd)
493 return NS_ERROR_FAILURE;
494
495 // Directory extraction is handled in nsJAR::Extract,
496 // so the item to be extracted should never be a directory
497 MOZ_ASSERT(!item->IsDirectory());
498
499 Bytef outbuf[ZIP_BUFLEN];
500
501 nsZipCursor cursor(item, this, outbuf, ZIP_BUFLEN, true);
502
503 nsresult rv = NS_OK;
504
505 while (true) {
506 uint32_t count = 0;
507 uint8_t* buf = cursor.Read(&count);
508 if (!buf) {
509 nsZipArchive::sFileCorruptedReason = "nsZipArchive: Read() failed to return a buffer";
510 rv = NS_ERROR_FILE_CORRUPTED;
511 break;
512 } else if (count == 0) {
513 break;
514 }
515
516 if (aFd && PR_Write(aFd, buf, count) < (READTYPE)count) {
517 rv = NS_ERROR_FILE_DISK_FULL;
518 break;
519 }
520 }
521
522 //-- delete the file on errors, or resolve symlink if needed
523 if (aFd) {
524 PR_Close(aFd);
525 if (rv != NS_OK)
526 PR_Delete(outname);
527 #ifdef XP_UNIX
528 else if (item->IsSymlink())
529 rv = ResolveSymlink(outname);
530 #endif
531 }
532
533 return rv;
534 }
535
536 //---------------------------------------------
537 // nsZipArchive::FindInit
538 //---------------------------------------------
539 nsresult
FindInit(const char * aPattern,nsZipFind ** aFind)540 nsZipArchive::FindInit(const char * aPattern, nsZipFind **aFind)
541 {
542 if (!aFind)
543 return NS_ERROR_ILLEGAL_VALUE;
544
545 // null out param in case an error happens
546 *aFind = nullptr;
547
548 bool regExp = false;
549 char* pattern = 0;
550
551 // Create synthetic directory entries on demand
552 nsresult rv = BuildSynthetics();
553 if (rv != NS_OK)
554 return rv;
555
556 // validate the pattern
557 if (aPattern)
558 {
559 switch (NS_WildCardValid((char*)aPattern))
560 {
561 case INVALID_SXP:
562 return NS_ERROR_ILLEGAL_VALUE;
563
564 case NON_SXP:
565 regExp = false;
566 break;
567
568 case VALID_SXP:
569 regExp = true;
570 break;
571
572 default:
573 // undocumented return value from RegExpValid!
574 PR_ASSERT(false);
575 return NS_ERROR_ILLEGAL_VALUE;
576 }
577
578 pattern = PL_strdup(aPattern);
579 if (!pattern)
580 return NS_ERROR_OUT_OF_MEMORY;
581 }
582
583 *aFind = new nsZipFind(this, pattern, regExp);
584 if (!*aFind) {
585 PL_strfree(pattern);
586 return NS_ERROR_OUT_OF_MEMORY;
587 }
588
589 return NS_OK;
590 }
591
592
593
594 //---------------------------------------------
595 // nsZipFind::FindNext
596 //---------------------------------------------
FindNext(const char ** aResult,uint16_t * aNameLen)597 nsresult nsZipFind::FindNext(const char ** aResult, uint16_t *aNameLen)
598 {
599 if (!mArchive || !aResult || !aNameLen)
600 return NS_ERROR_ILLEGAL_VALUE;
601
602 *aResult = 0;
603 *aNameLen = 0;
604 MOZ_WIN_MEM_TRY_BEGIN
605 // we start from last match, look for next
606 while (mSlot < ZIP_TABSIZE)
607 {
608 // move to next in current chain, or move to new slot
609 mItem = mItem ? mItem->next : mArchive->mFiles[mSlot];
610
611 bool found = false;
612 if (!mItem)
613 ++mSlot; // no more in this chain, move to next slot
614 else if (!mPattern)
615 found = true; // always match
616 else if (mRegExp)
617 {
618 char buf[kMaxNameLength+1];
619 memcpy(buf, mItem->Name(), mItem->nameLength);
620 buf[mItem->nameLength]='\0';
621 found = (NS_WildCardMatch(buf, mPattern, false) == MATCH);
622 }
623 else
624 found = ((mItem->nameLength == strlen(mPattern)) &&
625 (memcmp(mItem->Name(), mPattern, mItem->nameLength) == 0));
626 if (found) {
627 // Need also to return the name length, as it is NOT zero-terminatdd...
628 *aResult = mItem->Name();
629 *aNameLen = mItem->nameLength;
630 return NS_OK;
631 }
632 }
633 MOZ_WIN_MEM_TRY_CATCH(return NS_ERROR_FAILURE)
634 return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST;
635 }
636
637 #ifdef XP_UNIX
638 //---------------------------------------------
639 // ResolveSymlink
640 //---------------------------------------------
ResolveSymlink(const char * path)641 static nsresult ResolveSymlink(const char *path)
642 {
643 PRFileDesc * fIn = PR_Open(path, PR_RDONLY, 0000);
644 if (!fIn)
645 return NS_ERROR_FILE_DISK_FULL;
646
647 char buf[PATH_MAX+1];
648 int32_t length = PR_Read(fIn, (void*)buf, PATH_MAX);
649 PR_Close(fIn);
650
651 if ( (length <= 0)
652 || ((buf[length] = 0, PR_Delete(path)) != 0)
653 || (symlink(buf, path) != 0))
654 {
655 return NS_ERROR_FILE_DISK_FULL;
656 }
657 return NS_OK;
658 }
659 #endif
660
661 //***********************************************************
662 // nsZipArchive -- private implementation
663 //***********************************************************
664
665 //---------------------------------------------
666 // nsZipArchive::CreateZipItem
667 //---------------------------------------------
CreateZipItem()668 nsZipItem* nsZipArchive::CreateZipItem()
669 {
670 // Arena allocate the nsZipItem
671 void *mem;
672 PL_ARENA_ALLOCATE(mem, &mArena, sizeof(nsZipItem));
673 return (nsZipItem*)mem;
674 }
675
676 //---------------------------------------------
677 // nsZipArchive::BuildFileList
678 //---------------------------------------------
BuildFileList(PRFileDesc * aFd)679 nsresult nsZipArchive::BuildFileList(PRFileDesc *aFd)
680 {
681 // Get archive size using end pos
682 const uint8_t* buf;
683 const uint8_t* startp = mFd->mFileData;
684 const uint8_t* endp = startp + mFd->mLen;
685 MOZ_WIN_MEM_TRY_BEGIN
686 uint32_t centralOffset = 4;
687 if (mFd->mLen > ZIPCENTRAL_SIZE && xtolong(startp + centralOffset) == CENTRALSIG) {
688 // Success means optimized jar layout from bug 559961 is in effect
689 uint32_t readaheadLength = xtolong(startp);
690 if (readaheadLength) {
691 #if defined(XP_UNIX)
692 madvise(const_cast<uint8_t*>(startp), readaheadLength, MADV_WILLNEED);
693 #elif defined(XP_WIN)
694 if (aFd) {
695 HANDLE hFile = (HANDLE) PR_FileDesc2NativeHandle(aFd);
696 mozilla::ReadAhead(hFile, 0, readaheadLength);
697 }
698 #endif
699 }
700 } else {
701 for (buf = endp - ZIPEND_SIZE; buf > startp; buf--)
702 {
703 if (xtolong(buf) == ENDSIG) {
704 centralOffset = xtolong(((ZipEnd *)buf)->offset_central_dir);
705 break;
706 }
707 }
708 }
709
710 if (!centralOffset) {
711 nsZipArchive::sFileCorruptedReason = "nsZipArchive: no central offset";
712 return NS_ERROR_FILE_CORRUPTED;
713 }
714
715 buf = startp + centralOffset;
716
717 // avoid overflow of startp + centralOffset.
718 if (buf < startp) {
719 nsZipArchive::sFileCorruptedReason = "nsZipArchive: overflow looking for central directory";
720 return NS_ERROR_FILE_CORRUPTED;
721 }
722
723 //-- Read the central directory headers
724 uint32_t sig = 0;
725 while ((buf + int32_t(sizeof(uint32_t)) > buf) &&
726 (buf + int32_t(sizeof(uint32_t)) <= endp) &&
727 ((sig = xtolong(buf)) == CENTRALSIG)) {
728 // Make sure there is enough data available.
729 if ((buf > endp) || (endp - buf < ZIPCENTRAL_SIZE)) {
730 nsZipArchive::sFileCorruptedReason = "nsZipArchive: central directory too small";
731 return NS_ERROR_FILE_CORRUPTED;
732 }
733
734 // Read the fixed-size data.
735 ZipCentral* central = (ZipCentral*)buf;
736
737 uint16_t namelen = xtoint(central->filename_len);
738 uint16_t extralen = xtoint(central->extrafield_len);
739 uint16_t commentlen = xtoint(central->commentfield_len);
740 uint32_t diff = ZIPCENTRAL_SIZE + namelen + extralen + commentlen;
741
742 // Sanity check variable sizes and refuse to deal with
743 // anything too big: it's likely a corrupt archive.
744 if (namelen < 1 ||
745 namelen > kMaxNameLength) {
746 nsZipArchive::sFileCorruptedReason = "nsZipArchive: namelen out of range";
747 return NS_ERROR_FILE_CORRUPTED;
748 }
749 if (buf >= buf + diff || // No overflow
750 buf >= endp - diff) {
751 nsZipArchive::sFileCorruptedReason = "nsZipArchive: overflow looking for next item";
752 return NS_ERROR_FILE_CORRUPTED;
753 }
754
755 // Point to the next item at the top of loop
756 buf += diff;
757
758 nsZipItem* item = CreateZipItem();
759 if (!item)
760 return NS_ERROR_OUT_OF_MEMORY;
761
762 item->central = central;
763 item->nameLength = namelen;
764 item->isSynthetic = false;
765
766 // Add item to file table
767 uint32_t hash = HashName(item->Name(), namelen);
768 item->next = mFiles[hash];
769 mFiles[hash] = item;
770
771 sig = 0;
772 } /* while reading central directory records */
773
774 if (sig != ENDSIG) {
775 nsZipArchive::sFileCorruptedReason = "nsZipArchive: unexpected sig";
776 return NS_ERROR_FILE_CORRUPTED;
777 }
778
779 // Make the comment available for consumers.
780 if ((endp >= buf) && (endp - buf >= ZIPEND_SIZE)) {
781 ZipEnd *zipend = (ZipEnd *)buf;
782
783 buf += ZIPEND_SIZE;
784 uint16_t commentlen = xtoint(zipend->commentfield_len);
785 if (endp - buf >= commentlen) {
786 mCommentPtr = (const char *)buf;
787 mCommentLen = commentlen;
788 }
789 }
790
791 MOZ_WIN_MEM_TRY_CATCH(return NS_ERROR_FAILURE)
792 return NS_OK;
793 }
794
795 //---------------------------------------------
796 // nsZipArchive::BuildSynthetics
797 //---------------------------------------------
BuildSynthetics()798 nsresult nsZipArchive::BuildSynthetics()
799 {
800 if (mBuiltSynthetics)
801 return NS_OK;
802 mBuiltSynthetics = true;
803
804 MOZ_WIN_MEM_TRY_BEGIN
805 // Create synthetic entries for any missing directories.
806 // Do this when all ziptable has scanned to prevent double entries.
807 for (int i = 0; i < ZIP_TABSIZE; ++i)
808 {
809 for (nsZipItem* item = mFiles[i]; item != nullptr; item = item->next)
810 {
811 if (item->isSynthetic)
812 continue;
813
814 //-- add entries for directories in the current item's path
815 //-- go from end to beginning, because then we can stop trying
816 //-- to create diritems if we find that the diritem we want to
817 //-- create already exists
818 //-- start just before the last char so as to not add the item
819 //-- twice if it's a directory
820 uint16_t namelen = item->nameLength;
821 MOZ_ASSERT(namelen > 0, "Attempt to build synthetic for zero-length entry name!");
822 const char *name = item->Name();
823 for (uint16_t dirlen = namelen - 1; dirlen > 0; dirlen--)
824 {
825 if (name[dirlen-1] != '/')
826 continue;
827
828 // The character before this is '/', so if this is also '/' then we
829 // have an empty path component. Skip it.
830 if (name[dirlen] == '/')
831 continue;
832
833 // Is the directory already in the file table?
834 uint32_t hash = HashName(item->Name(), dirlen);
835 bool found = false;
836 for (nsZipItem* zi = mFiles[hash]; zi != nullptr; zi = zi->next)
837 {
838 if ((dirlen == zi->nameLength) &&
839 (0 == memcmp(item->Name(), zi->Name(), dirlen)))
840 {
841 // we've already added this dir and all its parents
842 found = true;
843 break;
844 }
845 }
846 // if the directory was found, break out of the directory
847 // creation loop now that we know all implicit directories
848 // are there -- otherwise, start creating the zip item
849 if (found)
850 break;
851
852 nsZipItem* diritem = CreateZipItem();
853 if (!diritem)
854 return NS_ERROR_OUT_OF_MEMORY;
855
856 // Point to the central record of the original item for the name part.
857 diritem->central = item->central;
858 diritem->nameLength = dirlen;
859 diritem->isSynthetic = true;
860
861 // add diritem to the file table
862 diritem->next = mFiles[hash];
863 mFiles[hash] = diritem;
864 } /* end processing of dirs in item's name */
865 }
866 }
867 MOZ_WIN_MEM_TRY_CATCH(return NS_ERROR_FAILURE)
868 return NS_OK;
869 }
870
GetFD()871 nsZipHandle* nsZipArchive::GetFD()
872 {
873 if (!mFd)
874 return nullptr;
875 return mFd.get();
876 }
877
878 //---------------------------------------------
879 // nsZipArchive::GetDataOffset
880 //---------------------------------------------
GetDataOffset(nsZipItem * aItem)881 uint32_t nsZipArchive::GetDataOffset(nsZipItem* aItem)
882 {
883 MOZ_ASSERT(aItem);
884 MOZ_WIN_MEM_TRY_BEGIN
885 //-- read local header to get variable length values and calculate
886 //-- the real data offset
887 uint32_t len = mFd->mLen;
888 const uint8_t* data = mFd->mFileData;
889 uint32_t offset = aItem->LocalOffset();
890 if (len < ZIPLOCAL_SIZE || offset > len - ZIPLOCAL_SIZE)
891 return 0;
892
893 // -- check signature before using the structure, in case the zip file is corrupt
894 ZipLocal* Local = (ZipLocal*)(data + offset);
895 if ((xtolong(Local->signature) != LOCALSIG))
896 return 0;
897
898 //-- NOTE: extralen is different in central header and local header
899 //-- for archives created using the Unix "zip" utility. To set
900 //-- the offset accurately we need the _local_ extralen.
901 offset += ZIPLOCAL_SIZE +
902 xtoint(Local->filename_len) +
903 xtoint(Local->extrafield_len);
904
905 return offset;
906 MOZ_WIN_MEM_TRY_CATCH(return 0)
907 }
908
909 //---------------------------------------------
910 // nsZipArchive::GetData
911 //---------------------------------------------
GetData(nsZipItem * aItem)912 const uint8_t* nsZipArchive::GetData(nsZipItem* aItem)
913 {
914 MOZ_ASSERT(aItem);
915 MOZ_WIN_MEM_TRY_BEGIN
916 uint32_t offset = GetDataOffset(aItem);
917
918 // -- check if there is enough source data in the file
919 if (!offset ||
920 mFd->mLen < aItem->Size() ||
921 offset > mFd->mLen - aItem->Size() ||
922 (aItem->Compression() == STORED && aItem->Size() != aItem->RealSize())) {
923 return nullptr;
924 }
925
926 return mFd->mFileData + offset;
927 MOZ_WIN_MEM_TRY_CATCH(return nullptr)
928 }
929
930 // nsZipArchive::GetComment
GetComment(nsACString & aComment)931 bool nsZipArchive::GetComment(nsACString &aComment)
932 {
933 MOZ_WIN_MEM_TRY_BEGIN
934 aComment.Assign(mCommentPtr, mCommentLen);
935 MOZ_WIN_MEM_TRY_CATCH(return false)
936 return true;
937 }
938
939 //---------------------------------------------
940 // nsZipArchive::SizeOfMapping
941 //---------------------------------------------
SizeOfMapping()942 int64_t nsZipArchive::SizeOfMapping()
943 {
944 return mFd ? mFd->SizeOfMapping() : 0;
945 }
946
947 //------------------------------------------
948 // nsZipArchive constructor and destructor
949 //------------------------------------------
950
nsZipArchive()951 nsZipArchive::nsZipArchive()
952 : mRefCnt(0)
953 , mCommentPtr(nullptr)
954 , mCommentLen(0)
955 , mBuiltSynthetics(false)
956 {
957 zipLog.AddRef();
958
959 MOZ_COUNT_CTOR(nsZipArchive);
960
961 // initialize the table to nullptr
962 memset(mFiles, 0, sizeof(mFiles));
963 }
964
965 NS_IMPL_ADDREF(nsZipArchive)
NS_IMPL_RELEASE(nsZipArchive)966 NS_IMPL_RELEASE(nsZipArchive)
967
968 nsZipArchive::~nsZipArchive()
969 {
970 CloseArchive();
971
972 MOZ_COUNT_DTOR(nsZipArchive);
973
974 zipLog.Release();
975 }
976
977
978 //------------------------------------------
979 // nsZipFind constructor and destructor
980 //------------------------------------------
981
nsZipFind(nsZipArchive * aZip,char * aPattern,bool aRegExp)982 nsZipFind::nsZipFind(nsZipArchive* aZip, char* aPattern, bool aRegExp)
983 : mArchive(aZip)
984 , mPattern(aPattern)
985 , mItem(nullptr)
986 , mSlot(0)
987 , mRegExp(aRegExp)
988 {
989 MOZ_COUNT_CTOR(nsZipFind);
990 }
991
~nsZipFind()992 nsZipFind::~nsZipFind()
993 {
994 PL_strfree(mPattern);
995
996 MOZ_COUNT_DTOR(nsZipFind);
997 }
998
999 //------------------------------------------
1000 // helper functions
1001 //------------------------------------------
1002
1003 /*
1004 * HashName
1005 *
1006 * returns a hash key for the entry name
1007 */
HashName(const char * aName,uint16_t len)1008 static uint32_t HashName(const char* aName, uint16_t len)
1009 {
1010 MOZ_ASSERT(aName != 0);
1011
1012 const uint8_t* p = (const uint8_t*)aName;
1013 const uint8_t* endp = p + len;
1014 uint32_t val = 0;
1015 while (p != endp) {
1016 val = val*37 + *p++;
1017 }
1018
1019 return (val % ZIP_TABSIZE);
1020 }
1021
1022 /*
1023 * x t o i n t
1024 *
1025 * Converts a two byte ugly endianed integer
1026 * to our platform's integer.
1027 */
xtoint(const uint8_t * ii)1028 static uint16_t xtoint (const uint8_t *ii)
1029 {
1030 return (uint16_t) ((ii [0]) | (ii [1] << 8));
1031 }
1032
1033 /*
1034 * x t o l o n g
1035 *
1036 * Converts a four byte ugly endianed integer
1037 * to our platform's integer.
1038 */
xtolong(const uint8_t * ll)1039 static uint32_t xtolong (const uint8_t *ll)
1040 {
1041 return (uint32_t)( (ll [0] << 0) |
1042 (ll [1] << 8) |
1043 (ll [2] << 16) |
1044 (ll [3] << 24) );
1045 }
1046
1047 /*
1048 * GetModTime
1049 *
1050 * returns last modification time in microseconds
1051 */
GetModTime(uint16_t aDate,uint16_t aTime)1052 static PRTime GetModTime(uint16_t aDate, uint16_t aTime)
1053 {
1054 // Note that on DST shift we can't handle correctly the hour that is valid
1055 // in both DST zones
1056 PRExplodedTime time;
1057
1058 time.tm_usec = 0;
1059
1060 time.tm_hour = (aTime >> 11) & 0x1F;
1061 time.tm_min = (aTime >> 5) & 0x3F;
1062 time.tm_sec = (aTime & 0x1F) * 2;
1063
1064 time.tm_year = (aDate >> 9) + 1980;
1065 time.tm_month = ((aDate >> 5) & 0x0F) - 1;
1066 time.tm_mday = aDate & 0x1F;
1067
1068 time.tm_params.tp_gmt_offset = 0;
1069 time.tm_params.tp_dst_offset = 0;
1070
1071 PR_NormalizeTime(&time, PR_GMTParameters);
1072 time.tm_params.tp_gmt_offset = PR_LocalTimeParameters(&time).tp_gmt_offset;
1073 PR_NormalizeTime(&time, PR_GMTParameters);
1074 time.tm_params.tp_dst_offset = PR_LocalTimeParameters(&time).tp_dst_offset;
1075
1076 return PR_ImplodeTime(&time);
1077 }
1078
nsZipItem()1079 nsZipItem::nsZipItem()
1080 : next(nullptr)
1081 , central(nullptr)
1082 , nameLength(0)
1083 , isSynthetic(false)
1084 {}
1085
LocalOffset()1086 uint32_t nsZipItem::LocalOffset()
1087 {
1088 return xtolong(central->localhdr_offset);
1089 }
1090
Size()1091 uint32_t nsZipItem::Size()
1092 {
1093 return isSynthetic ? 0 : xtolong(central->size);
1094 }
1095
RealSize()1096 uint32_t nsZipItem::RealSize()
1097 {
1098 return isSynthetic ? 0 : xtolong(central->orglen);
1099 }
1100
CRC32()1101 uint32_t nsZipItem::CRC32()
1102 {
1103 return isSynthetic ? 0 : xtolong(central->crc32);
1104 }
1105
Date()1106 uint16_t nsZipItem::Date()
1107 {
1108 return isSynthetic ? kSyntheticDate : xtoint(central->date);
1109 }
1110
Time()1111 uint16_t nsZipItem::Time()
1112 {
1113 return isSynthetic ? kSyntheticTime : xtoint(central->time);
1114 }
1115
Compression()1116 uint16_t nsZipItem::Compression()
1117 {
1118 return isSynthetic ? STORED : xtoint(central->method);
1119 }
1120
IsDirectory()1121 bool nsZipItem::IsDirectory()
1122 {
1123 return isSynthetic || ((nameLength > 0) && ('/' == Name()[nameLength - 1]));
1124 }
1125
Mode()1126 uint16_t nsZipItem::Mode()
1127 {
1128 if (isSynthetic) return 0755;
1129 return ((uint16_t)(central->external_attributes[2]) | 0x100);
1130 }
1131
GetExtraField(uint16_t aTag,uint16_t * aBlockSize)1132 const uint8_t * nsZipItem::GetExtraField(uint16_t aTag, uint16_t *aBlockSize)
1133 {
1134 if (isSynthetic) return nullptr;
1135 MOZ_WIN_MEM_TRY_BEGIN
1136 const unsigned char *buf = ((const unsigned char*)central) + ZIPCENTRAL_SIZE +
1137 nameLength;
1138 uint32_t buflen = (uint32_t)xtoint(central->extrafield_len);
1139 uint32_t pos = 0;
1140 uint16_t tag, blocksize;
1141
1142 while (buf && (pos + 4) <= buflen) {
1143 tag = xtoint(buf + pos);
1144 blocksize = xtoint(buf + pos + 2);
1145
1146 if (aTag == tag && (pos + 4 + blocksize) <= buflen) {
1147 *aBlockSize = blocksize;
1148 return buf + pos;
1149 }
1150
1151 pos += blocksize + 4;
1152 }
1153
1154 MOZ_WIN_MEM_TRY_CATCH(return nullptr)
1155 return nullptr;
1156 }
1157
1158
LastModTime()1159 PRTime nsZipItem::LastModTime()
1160 {
1161 if (isSynthetic) return GetModTime(kSyntheticDate, kSyntheticTime);
1162
1163 // Try to read timestamp from extra field
1164 uint16_t blocksize;
1165 const uint8_t *tsField = GetExtraField(EXTENDED_TIMESTAMP_FIELD, &blocksize);
1166 if (tsField && blocksize >= 5 && tsField[4] & EXTENDED_TIMESTAMP_MODTIME) {
1167 return (PRTime)(xtolong(tsField + 5)) * PR_USEC_PER_SEC;
1168 }
1169
1170 return GetModTime(Date(), Time());
1171 }
1172
1173 #ifdef XP_UNIX
IsSymlink()1174 bool nsZipItem::IsSymlink()
1175 {
1176 if (isSynthetic) return false;
1177 return (xtoint(central->external_attributes+2) & S_IFMT) == S_IFLNK;
1178 }
1179 #endif
1180
nsZipCursor(nsZipItem * item,nsZipArchive * aZip,uint8_t * aBuf,uint32_t aBufSize,bool doCRC)1181 nsZipCursor::nsZipCursor(nsZipItem *item, nsZipArchive *aZip, uint8_t* aBuf,
1182 uint32_t aBufSize, bool doCRC)
1183 : mItem(item)
1184 , mBuf(aBuf)
1185 , mBufSize(aBufSize)
1186 , mCRC(0)
1187 , mDoCRC(doCRC)
1188 {
1189 if (mItem->Compression() == DEFLATED) {
1190 #ifdef DEBUG
1191 nsresult status =
1192 #endif
1193 gZlibInit(&mZs);
1194 NS_ASSERTION(status == NS_OK, "Zlib failed to initialize");
1195 NS_ASSERTION(aBuf, "Must pass in a buffer for DEFLATED nsZipItem");
1196 }
1197
1198 mZs.avail_in = item->Size();
1199 mZs.next_in = (Bytef*)aZip->GetData(item);
1200
1201 if (doCRC)
1202 mCRC = crc32(0L, Z_NULL, 0);
1203 }
1204
~nsZipCursor()1205 nsZipCursor::~nsZipCursor()
1206 {
1207 if (mItem->Compression() == DEFLATED) {
1208 inflateEnd(&mZs);
1209 }
1210 }
1211
ReadOrCopy(uint32_t * aBytesRead,bool aCopy)1212 uint8_t* nsZipCursor::ReadOrCopy(uint32_t *aBytesRead, bool aCopy) {
1213 int zerr;
1214 uint8_t *buf = nullptr;
1215 bool verifyCRC = true;
1216
1217 if (!mZs.next_in)
1218 return nullptr;
1219 MOZ_WIN_MEM_TRY_BEGIN
1220 switch (mItem->Compression()) {
1221 case STORED:
1222 if (!aCopy) {
1223 *aBytesRead = mZs.avail_in;
1224 buf = mZs.next_in;
1225 mZs.next_in += mZs.avail_in;
1226 mZs.avail_in = 0;
1227 } else {
1228 *aBytesRead = mZs.avail_in > mBufSize ? mBufSize : mZs.avail_in;
1229 memcpy(mBuf, mZs.next_in, *aBytesRead);
1230 mZs.avail_in -= *aBytesRead;
1231 mZs.next_in += *aBytesRead;
1232 }
1233 break;
1234 case DEFLATED:
1235 buf = mBuf;
1236 mZs.next_out = buf;
1237 mZs.avail_out = mBufSize;
1238
1239 zerr = inflate(&mZs, Z_PARTIAL_FLUSH);
1240 if (zerr != Z_OK && zerr != Z_STREAM_END)
1241 return nullptr;
1242
1243 *aBytesRead = mZs.next_out - buf;
1244 verifyCRC = (zerr == Z_STREAM_END);
1245 break;
1246 default:
1247 return nullptr;
1248 }
1249
1250 if (mDoCRC) {
1251 mCRC = crc32(mCRC, (const unsigned char*)buf, *aBytesRead);
1252 if (verifyCRC && mCRC != mItem->CRC32())
1253 return nullptr;
1254 }
1255 MOZ_WIN_MEM_TRY_CATCH(return nullptr)
1256 return buf;
1257 }
1258
nsZipItemPtr_base(nsZipArchive * aZip,const char * aEntryName,bool doCRC)1259 nsZipItemPtr_base::nsZipItemPtr_base(nsZipArchive *aZip,
1260 const char * aEntryName, bool doCRC)
1261 : mReturnBuf(nullptr)
1262 , mReadlen(0)
1263 {
1264 // make sure the ziparchive hangs around
1265 mZipHandle = aZip->GetFD();
1266
1267 nsZipItem* item = aZip->GetItem(aEntryName);
1268 if (!item)
1269 return;
1270
1271 uint32_t size = 0;
1272 if (item->Compression() == DEFLATED) {
1273 size = item->RealSize();
1274 mAutoBuf = MakeUniqueFallible<uint8_t[]>(size);
1275 if (!mAutoBuf) {
1276 return;
1277 }
1278 }
1279
1280 nsZipCursor cursor(item, aZip, mAutoBuf.get(), size, doCRC);
1281 mReturnBuf = cursor.Read(&mReadlen);
1282 if (!mReturnBuf) {
1283 return;
1284 }
1285
1286 if (mReadlen != item->RealSize()) {
1287 NS_ASSERTION(mReadlen == item->RealSize(), "nsZipCursor underflow");
1288 mReturnBuf = nullptr;
1289 return;
1290 }
1291 }
1292
1293 /* static */ const char*
1294 nsZipArchive::sFileCorruptedReason = nullptr;
1295