Coverage Report

Created: 2018-09-25 14:53

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