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