Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/modules/libjar/nsZipArchive.h
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 2; 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
#ifndef nsZipArchive_h_
7
#define nsZipArchive_h_
8
9
#include "mozilla/Attributes.h"
10
11
18.0k
#define ZIP_TABSIZE   256
12
0
#define ZIP_BUFLEN    (4*1024)      /* Used as output buffer when deflating items to a file */
13
14
#include "zlib.h"
15
#include "zipstruct.h"
16
#include "nsAutoPtr.h"
17
#include "nsIFile.h"
18
#include "nsISupportsImpl.h" // For mozilla::ThreadSafeAutoRefCnt
19
#include "mozilla/ArenaAllocator.h"
20
#include "mozilla/FileUtils.h"
21
#include "mozilla/FileLocation.h"
22
#include "mozilla/UniquePtr.h"
23
24
#ifdef HAVE_SEH_EXCEPTIONS
25
#define MOZ_WIN_MEM_TRY_BEGIN __try {
26
#define MOZ_WIN_MEM_TRY_CATCH(cmd) }                                \
27
  __except(GetExceptionCode()==EXCEPTION_IN_PAGE_ERROR ?            \
28
           EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)   \
29
  {                                                                 \
30
    NS_WARNING("unexpected EXCEPTION_IN_PAGE_ERROR");               \
31
    cmd;                                                            \
32
  }
33
#else
34
145
#define MOZ_WIN_MEM_TRY_BEGIN {
35
145
#define MOZ_WIN_MEM_TRY_CATCH(cmd) }
36
#endif
37
38
class nsZipFind;
39
struct PRFileDesc;
40
#ifdef MOZ_JAR_BROTLI
41
struct BrotliDecoderStateStruct;
42
#endif
43
44
/**
45
 * This file defines some of the basic structures used by libjar to
46
 * read Zip files. It makes use of zlib in order to do the decompression.
47
 *
48
 * A few notes on the classes/structs:
49
 * nsZipArchive   represents a single Zip file, and maintains an index
50
 *                of all the items in the file.
51
 * nsZipItem      represents a single item (file) in the Zip archive.
52
 * nsZipFind      represents the metadata involved in doing a search,
53
 *                and current state of the iteration of found objects.
54
 * 'MT''safe' reading from the zipfile is performed through JARInputStream,
55
 * which maintains its own file descriptor, allowing for multiple reads
56
 * concurrently from the same zip file.
57
 */
58
59
/**
60
 * nsZipItem -- a helper struct for nsZipArchive
61
 *
62
 * each nsZipItem represents one file in the archive and all the
63
 * information needed to manipulate it.
64
 */
65
class nsZipItem final
66
{
67
public:
68
  nsZipItem();
69
70
28.0k
  const char* Name() { return ((const char*)central) + ZIPCENTRAL_SIZE; }
71
72
  uint32_t LocalOffset();
73
  uint32_t Size();
74
  uint32_t RealSize();
75
  uint32_t CRC32();
76
  uint16_t Date();
77
  uint16_t Time();
78
  uint16_t Compression();
79
  bool     IsDirectory();
80
  uint16_t Mode();
81
  const uint8_t* GetExtraField(uint16_t aTag, uint16_t *aBlockSize);
82
  PRTime   LastModTime();
83
84
  nsZipItem*         next;
85
  const ZipCentral*  central;
86
  uint16_t           nameLength;
87
  bool               isSynthetic;
88
};
89
90
class nsZipHandle;
91
92
/**
93
 * nsZipArchive -- a class for reading the PKZIP file format.
94
 *
95
 */
96
class nsZipArchive final
97
{
98
  friend class nsZipFind;
99
100
  /** destructing the object closes the archive */
101
  ~nsZipArchive();
102
103
public:
104
  static const char* sFileCorruptedReason;
105
106
  /** constructing does not open the archive. See OpenArchive() */
107
  nsZipArchive();
108
109
  /**
110
   * OpenArchive
111
   *
112
   * It's an error to call this more than once on the same nsZipArchive
113
   * object. If we were allowed to use exceptions this would have been
114
   * part of the constructor
115
   *
116
   * @param   aZipHandle  The nsZipHandle used to access the zip
117
   * @param   aFd         Optional PRFileDesc for Windows readahead optimization
118
   * @return  status code
119
   */
120
  nsresult OpenArchive(nsZipHandle *aZipHandle, PRFileDesc *aFd = nullptr);
121
122
  /**
123
   * OpenArchive
124
   *
125
   * Convenience function that generates nsZipHandle
126
   *
127
   * @param   aFile         The file used to access the zip
128
   * @return  status code
129
   */
130
  nsresult OpenArchive(nsIFile *aFile);
131
132
  /**
133
   * Test the integrity of items in this archive by running
134
   * a CRC check after extracting each item into a memory
135
   * buffer.  If an entry name is supplied only the
136
   * specified item is tested.  Else, if null is supplied
137
   * then all the items in the archive are tested.
138
   *
139
   * @return  status code
140
   */
141
  nsresult Test(const char *aEntryName);
142
143
  /**
144
   * Closes an open archive.
145
   */
146
  nsresult CloseArchive();
147
148
  /**
149
   * GetItem
150
   * @param   aEntryName Name of file in the archive
151
   * @return  pointer to nsZipItem
152
   */
153
  nsZipItem* GetItem(const char * aEntryName);
154
155
  /**
156
   * ExtractFile
157
   *
158
   * @param   zipEntry   Name of file in archive to extract
159
   * @param   outFD      Filedescriptor to write contents to
160
   * @param   outname    Name of file to write to
161
   * @return  status code
162
   */
163
  nsresult ExtractFile(nsZipItem * zipEntry, nsIFile* outFile, PRFileDesc * outFD);
164
165
  /**
166
   * FindInit
167
   *
168
   * Initializes a search for files in the archive. FindNext() returns
169
   * the actual matches. The nsZipFind must be deleted when you're done
170
   *
171
   * @param   aPattern    a string or RegExp pattern to search for
172
   *                      (may be nullptr to find all files in archive)
173
   * @param   aFind       a pointer to a pointer to a structure used
174
   *                      in FindNext.  In the case of an error this
175
   *                      will be set to nullptr.
176
   * @return  status code
177
   */
178
  nsresult FindInit(const char * aPattern, nsZipFind** aFind);
179
180
  /*
181
   * Gets an undependent handle to the mapped file.
182
   */
183
  nsZipHandle* GetFD();
184
185
  /**
186
   * Gets the data offset.
187
   * @param   aItem       Pointer to nsZipItem
188
   * returns 0 on failure.
189
   */
190
  uint32_t GetDataOffset(nsZipItem* aItem);
191
192
  /**
193
   * Get pointer to the data of the item.
194
   * @param   aItem       Pointer to nsZipItem
195
   * reutrns null when zip file is corrupt.
196
   */
197
  const uint8_t* GetData(nsZipItem* aItem);
198
199
  bool GetComment(nsACString &aComment);
200
201
  /**
202
   * Gets the amount of memory taken up by the archive's mapping.
203
   * @return the size
204
   */
205
  int64_t SizeOfMapping();
206
207
  /*
208
   * Refcounting
209
   */
210
  NS_METHOD_(MozExternalRefCountType) AddRef(void);
211
  NS_METHOD_(MozExternalRefCountType) Release(void);
212
213
private:
214
  //--- private members ---
215
  mozilla::ThreadSafeAutoRefCnt mRefCnt; /* ref count */
216
  NS_DECL_OWNINGTHREAD
217
218
  nsZipItem*    mFiles[ZIP_TABSIZE];
219
  mozilla::ArenaAllocator<1024, sizeof(void*)> mArena;
220
221
  const char*   mCommentPtr;
222
  uint16_t      mCommentLen;
223
224
  // Whether we synthesized the directory entries
225
  bool          mBuiltSynthetics;
226
227
  // file handle
228
  RefPtr<nsZipHandle> mFd;
229
230
  // file URI, for logging
231
  nsCString mURI;
232
233
private:
234
  //--- private methods ---
235
  nsZipItem*        CreateZipItem();
236
  nsresult          BuildFileList(PRFileDesc *aFd = nullptr);
237
  nsresult          BuildSynthetics();
238
239
  nsZipArchive& operator=(const nsZipArchive& rhs) = delete;
240
  nsZipArchive(const nsZipArchive& rhs) = delete;
241
};
242
243
/**
244
 * nsZipFind
245
 *
246
 * a helper class for nsZipArchive, representing a search
247
 */
248
class nsZipFind final
249
{
250
public:
251
  nsZipFind(nsZipArchive* aZip, char* aPattern, bool regExp);
252
  ~nsZipFind();
253
254
  nsresult      FindNext(const char** aResult, uint16_t* aNameLen);
255
256
private:
257
  RefPtr<nsZipArchive> mArchive;
258
  char*         mPattern;
259
  nsZipItem*    mItem;
260
  uint16_t      mSlot;
261
  bool          mRegExp;
262
263
  nsZipFind& operator=(const nsZipFind& rhs) = delete;
264
  nsZipFind(const nsZipFind& rhs) = delete;
265
};
266
267
/**
268
 * nsZipCursor -- a low-level class for reading the individual items in a zip.
269
 */
270
class nsZipCursor final
271
{
272
public:
273
  /**
274
   * Initializes the cursor
275
   *
276
   * @param   aItem       Item of interest
277
   * @param   aZip        Archive
278
   * @param   aBuf        Buffer used for decompression.
279
   *                      This determines the maximum Read() size in the compressed case.
280
   * @param   aBufSize    Buffer size
281
   * @param   doCRC       When set to true Read() will check crc
282
   */
283
  nsZipCursor(nsZipItem *aItem, nsZipArchive *aZip, uint8_t* aBuf = nullptr, uint32_t aBufSize = 0, bool doCRC = false);
284
285
  ~nsZipCursor();
286
287
  /**
288
   * Performs reads. In the compressed case it uses aBuf(passed in constructor), for stored files
289
   * it returns a zero-copy buffer.
290
   *
291
   * @param   aBytesRead  Outparam for number of bytes read.
292
   * @return  data read or nullptr if item is corrupted.
293
   */
294
6
  uint8_t* Read(uint32_t *aBytesRead) {
295
6
    return ReadOrCopy(aBytesRead, false);
296
6
  }
297
298
  /**
299
   * Performs a copy. It always uses aBuf(passed in constructor).
300
   *
301
   * @param   aBytesRead  Outparam for number of bytes read.
302
   * @return  data read or nullptr if item is corrupted.
303
   */
304
  uint8_t* Copy(uint32_t *aBytesRead) {
305
    return ReadOrCopy(aBytesRead, true);
306
  }
307
308
private:
309
  /* Actual implementation for both Read and Copy above */
310
  uint8_t* ReadOrCopy(uint32_t *aBytesRead, bool aCopy);
311
312
  nsZipItem *mItem;
313
  uint8_t  *mBuf;
314
  uint32_t  mBufSize;
315
  z_stream  mZs;
316
#ifdef MOZ_JAR_BROTLI
317
  BrotliDecoderStateStruct* mBrotliState;
318
#endif
319
  uint32_t mCRC;
320
  bool mDoCRC;
321
};
322
323
/**
324
 * nsZipItemPtr - a RAII convenience class for reading the individual items in a zip.
325
 * It reads whole files and does zero-copy IO for stored files. A buffer is allocated
326
 * for decompression.
327
 * Do not use when the file may be very large.
328
 */
329
class nsZipItemPtr_base
330
{
331
public:
332
  /**
333
   * Initializes the reader
334
   *
335
   * @param   aZip        Archive
336
   * @param   aEntryName  Archive membername
337
   * @param   doCRC       When set to true Read() will check crc
338
   */
339
  nsZipItemPtr_base(nsZipArchive *aZip, const char *aEntryName, bool doCRC);
340
341
  uint32_t Length() const {
342
    return mReadlen;
343
  }
344
345
protected:
346
  RefPtr<nsZipHandle> mZipHandle;
347
  mozilla::UniquePtr<uint8_t[]> mAutoBuf;
348
  uint8_t *mReturnBuf;
349
  uint32_t mReadlen;
350
};
351
352
template <class T>
353
class nsZipItemPtr final : public nsZipItemPtr_base
354
{
355
  static_assert(sizeof(T) == sizeof(char),
356
                "This class cannot be used with larger T without re-examining"
357
                " a number of assumptions.");
358
359
public:
360
3
  nsZipItemPtr(nsZipArchive *aZip, const char *aEntryName, bool doCRC = false) : nsZipItemPtr_base(aZip, aEntryName, doCRC) { }
361
  /**
362
   * @return buffer containing the whole zip member or nullptr on error.
363
   * The returned buffer is owned by nsZipItemReader.
364
   */
365
3
  const T* Buffer() const {
366
3
    return (const T*)mReturnBuf;
367
3
  }
368
369
  operator const T*() const {
370
    return Buffer();
371
  }
372
373
  /**
374
   * Relinquish ownership of zip member if compressed.
375
   * Copy member into a new buffer if uncompressed.
376
   * @return a buffer with whole zip member. It is caller's responsibility to free() it.
377
   */
378
  mozilla::UniquePtr<T[]> Forget() {
379
    if (!mReturnBuf)
380
      return nullptr;
381
    // In uncompressed mmap case, give up buffer
382
    if (mAutoBuf.get() == mReturnBuf) {
383
      mReturnBuf = nullptr;
384
      return mozilla::UniquePtr<T[]>(reinterpret_cast<T*>(mAutoBuf.release()));
385
    }
386
    auto ret = mozilla::MakeUnique<T[]>(Length());
387
    memcpy(ret.get(), mReturnBuf, Length());
388
    mReturnBuf = nullptr;
389
    return ret;
390
  }
391
};
392
393
class nsZipHandle final
394
{
395
friend class nsZipArchive;
396
friend class mozilla::FileLocation;
397
public:
398
  static nsresult Init(nsIFile *file, nsZipHandle **ret,
399
                       PRFileDesc **aFd = nullptr);
400
  static nsresult Init(nsZipArchive *zip, const char *entry,
401
                       nsZipHandle **ret);
402
  static nsresult Init(const uint8_t* aData, uint32_t aLen,
403
                       nsZipHandle **aRet);
404
405
  NS_METHOD_(MozExternalRefCountType) AddRef(void);
406
  NS_METHOD_(MozExternalRefCountType) Release(void);
407
408
  int64_t SizeOfMapping();
409
410
  nsresult GetNSPRFileDesc(PRFileDesc** aNSPRFileDesc);
411
412
protected:
413
  const uint8_t * mFileData; /* pointer to zip data */
414
  uint32_t        mLen;      /* length of zip data */
415
  mozilla::FileLocation mFile; /* source file if any, for logging */
416
417
private:
418
  nsZipHandle();
419
  ~nsZipHandle();
420
421
  nsresult findDataStart();
422
423
  PRFileMap *                       mMap;    /* nspr datastructure for mmap */
424
  mozilla::AutoFDClose              mNSPRFileDesc;
425
  nsAutoPtr<nsZipItemPtr<uint8_t> > mBuf;
426
  mozilla::ThreadSafeAutoRefCnt     mRefCnt; /* ref count */
427
  NS_DECL_OWNINGTHREAD
428
429
  const uint8_t * mFileStart; /* pointer to mmaped file */
430
  uint32_t        mTotalLen;  /* total length of the mmaped file */
431
432
  /* Magic number for CRX type expressed in Big Endian since it is a literal */
433
  static const uint32_t kCRXMagic = 0x34327243;
434
};
435
436
nsresult gZlibInit(z_stream *zs);
437
438
#endif /* nsZipArchive_h_ */