/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_ */ |