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