/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; |