Bug 899135 - Drop buffers in ScriptProcessorNode instead of buffering them when the...
[gecko.git] / modules / libjar / nsZipArchive.cpp
blob2c1920176bd7d788fcf03e749da26e91bc9a1d29
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/. */
6 /*
7 * This module implements a simple archive extractor for the PKZIP format.
9 * The underlying nsZipArchive is NOT thread-safe. Do not pass references
10 * or pointers to it across thread boundaries.
13 #define READTYPE int32_t
14 #include "zlib.h"
15 #include "nsISupportsUtils.h"
16 #include "prio.h"
17 #include "plstr.h"
18 #include "prlog.h"
19 #include "stdlib.h"
20 #include "nsWildCard.h"
21 #include "nsZipArchive.h"
22 #include "nsString.h"
23 #include "prenv.h"
24 #if defined(XP_WIN)
25 #include <windows.h>
26 #endif
28 // For placement new used for arena allocations of zip file list
29 #include <new>
30 #define ZIP_ARENABLOCKSIZE (1*1024)
32 #ifdef XP_UNIX
33 #include <sys/mman.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <limits.h>
37 #include <unistd.h>
38 #elif defined(XP_WIN) || defined(XP_OS2)
39 #include <io.h>
40 #endif
42 #ifdef __SYMBIAN32__
43 #include <sys/syslimits.h>
44 #endif /*__SYMBIAN32__*/
47 #ifndef XP_UNIX /* we need some constants defined in limits.h and unistd.h */
48 # ifndef S_IFMT
49 # define S_IFMT 0170000
50 # endif
51 # ifndef S_IFLNK
52 # define S_IFLNK 0120000
53 # endif
54 # ifndef PATH_MAX
55 # define PATH_MAX 1024
56 # endif
57 #endif /* XP_UNIX */
59 #ifdef XP_WIN
60 #include "private/pprio.h" // To get PR_ImportFile
61 #endif
63 using namespace mozilla;
65 static const uint32_t kMaxNameLength = PATH_MAX; /* Maximum name length */
66 // For synthetic zip entries. Date/time corresponds to 1980-01-01 00:00.
67 static const uint16_t kSyntheticTime = 0;
68 static const uint16_t kSyntheticDate = (1 + (1 << 5) + (0 << 9));
70 static uint16_t xtoint(const uint8_t *ii);
71 static uint32_t xtolong(const uint8_t *ll);
72 static uint32_t HashName(const char* aName, uint16_t nameLen);
73 #ifdef XP_UNIX
74 static nsresult ResolveSymlink(const char *path);
75 #endif
77 class ZipArchiveLogger {
78 public:
79 void Write(const nsACString &zip, const char *entry) const {
80 if (!fd) {
81 char *env = PR_GetEnv("MOZ_JAR_LOG_FILE");
82 if (!env)
83 return;
85 nsCOMPtr<nsIFile> logFile;
86 nsresult rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(env), false, getter_AddRefs(logFile));
87 if (NS_FAILED(rv))
88 return;
90 // Create the log file and its parent directory (in case it doesn't exist)
91 logFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
93 PRFileDesc* file;
94 #ifdef XP_WIN
95 // PR_APPEND is racy on Windows, so open a handle ourselves with flags that
96 // will work, and use PR_ImportFile to make it a PRFileDesc.
97 // This can go away when bug 840435 is fixed.
98 nsAutoString path;
99 logFile->GetPath(path);
100 if (path.IsEmpty())
101 return;
102 HANDLE handle = CreateFileW(path.get(), FILE_APPEND_DATA, FILE_SHARE_WRITE,
103 NULL, OPEN_ALWAYS, 0, NULL);
104 if (handle == INVALID_HANDLE_VALUE)
105 return;
106 file = PR_ImportFile((PROsfd)handle);
107 if (!file)
108 return;
109 #else
110 rv = logFile->OpenNSPRFileDesc(PR_WRONLY|PR_CREATE_FILE|PR_APPEND, 0644, &file);
111 if (NS_FAILED(rv))
112 return;
113 #endif
114 fd = file;
116 nsCString buf(zip);
117 buf.Append(" ");
118 buf.Append(entry);
119 buf.Append('\n');
120 PR_Write(fd, buf.get(), buf.Length());
123 void AddRef() {
124 MOZ_ASSERT(refCnt >= 0);
125 ++refCnt;
128 void Release() {
129 MOZ_ASSERT(refCnt > 0);
130 if ((0 == --refCnt) && fd) {
131 PR_Close(fd);
132 fd = NULL;
135 private:
136 int refCnt;
137 mutable PRFileDesc *fd;
140 static ZipArchiveLogger zipLog;
142 //***********************************************************
143 // For every inflation the following allocations are done:
144 // malloc(1 * 9520)
145 // malloc(32768 * 1)
146 //***********************************************************
148 nsresult gZlibInit(z_stream *zs)
150 memset(zs, 0, sizeof(z_stream));
151 int zerr = inflateInit2(zs, -MAX_WBITS);
152 if (zerr != Z_OK) return NS_ERROR_OUT_OF_MEMORY;
154 return NS_OK;
157 nsZipHandle::nsZipHandle()
158 : mFileData(nullptr)
159 , mLen(0)
160 , mMap(nullptr)
161 , mRefCnt(0)
163 MOZ_COUNT_CTOR(nsZipHandle);
166 NS_IMPL_ADDREF(nsZipHandle)
167 NS_IMPL_RELEASE(nsZipHandle)
169 nsresult nsZipHandle::Init(nsIFile *file, nsZipHandle **ret, PRFileDesc **aFd)
171 mozilla::AutoFDClose fd;
172 int32_t flags = PR_RDONLY;
173 #if defined(XP_WIN)
174 flags |= nsIFile::OS_READAHEAD;
175 #endif
176 nsresult rv = file->OpenNSPRFileDesc(flags, 0000, &fd.rwget());
177 if (NS_FAILED(rv))
178 return rv;
180 int64_t size = PR_Available64(fd);
181 if (size >= INT32_MAX)
182 return NS_ERROR_FILE_TOO_BIG;
184 PRFileMap *map = PR_CreateFileMap(fd, size, PR_PROT_READONLY);
185 if (!map)
186 return NS_ERROR_FAILURE;
188 uint8_t *buf = (uint8_t*) PR_MemMap(map, 0, (uint32_t) size);
189 // Bug 525755: PR_MemMap fails when fd points at something other than a normal file.
190 if (!buf) {
191 PR_CloseFileMap(map);
192 return NS_ERROR_FAILURE;
195 nsRefPtr<nsZipHandle> handle = new nsZipHandle();
196 if (!handle) {
197 PR_MemUnmap(buf, (uint32_t) size);
198 PR_CloseFileMap(map);
199 return NS_ERROR_OUT_OF_MEMORY;
202 #if defined(XP_WIN)
203 if (aFd) {
204 *aFd = fd.forget();
206 #endif
207 handle->mMap = map;
208 handle->mFile.Init(file);
209 handle->mLen = (uint32_t) size;
210 handle->mFileData = buf;
211 *ret = handle.forget().get();
212 return NS_OK;
215 nsresult nsZipHandle::Init(nsZipArchive *zip, const char *entry,
216 nsZipHandle **ret)
218 nsRefPtr<nsZipHandle> handle = new nsZipHandle();
219 if (!handle)
220 return NS_ERROR_OUT_OF_MEMORY;
222 handle->mBuf = new nsZipItemPtr<uint8_t>(zip, entry);
223 if (!handle->mBuf)
224 return NS_ERROR_OUT_OF_MEMORY;
226 if (!handle->mBuf->Buffer())
227 return NS_ERROR_UNEXPECTED;
229 handle->mMap = nullptr;
230 handle->mFile.Init(zip, entry);
231 handle->mLen = handle->mBuf->Length();
232 handle->mFileData = handle->mBuf->Buffer();
233 *ret = handle.forget().get();
234 return NS_OK;
237 int64_t nsZipHandle::SizeOfMapping()
239 return mLen;
242 nsZipHandle::~nsZipHandle()
244 if (mMap) {
245 PR_MemUnmap((void *)mFileData, mLen);
246 PR_CloseFileMap(mMap);
248 mFileData = nullptr;
249 mMap = nullptr;
250 mBuf = nullptr;
251 MOZ_COUNT_DTOR(nsZipHandle);
254 //***********************************************************
255 // nsZipArchive -- public methods
256 //***********************************************************
258 //---------------------------------------------
259 // nsZipArchive::OpenArchive
260 //---------------------------------------------
261 nsresult nsZipArchive::OpenArchive(nsZipHandle *aZipHandle, PRFileDesc *aFd)
263 mFd = aZipHandle;
265 // Initialize our arena
266 PL_INIT_ARENA_POOL(&mArena, "ZipArena", ZIP_ARENABLOCKSIZE);
268 //-- get table of contents for archive
269 nsresult rv = BuildFileList(aFd);
270 if (NS_SUCCEEDED(rv)) {
271 if (aZipHandle->mFile)
272 aZipHandle->mFile.GetURIString(mURI);
274 return rv;
277 nsresult nsZipArchive::OpenArchive(nsIFile *aFile)
279 nsRefPtr<nsZipHandle> handle;
280 #if defined(XP_WIN)
281 mozilla::AutoFDClose fd;
282 nsresult rv = nsZipHandle::Init(aFile, getter_AddRefs(handle), &fd.rwget());
283 #else
284 nsresult rv = nsZipHandle::Init(aFile, getter_AddRefs(handle));
285 #endif
286 if (NS_FAILED(rv))
287 return rv;
289 #if defined(XP_WIN)
290 return OpenArchive(handle, fd.get());
291 #else
292 return OpenArchive(handle);
293 #endif
296 //---------------------------------------------
297 // nsZipArchive::Test
298 //---------------------------------------------
299 nsresult nsZipArchive::Test(const char *aEntryName)
301 nsZipItem* currItem;
303 if (aEntryName) // only test specified item
305 currItem = GetItem(aEntryName);
306 if (!currItem)
307 return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST;
308 //-- don't test (synthetic) directory items
309 if (currItem->IsDirectory())
310 return NS_OK;
311 return ExtractFile(currItem, 0, 0);
314 // test all items in archive
315 for (int i = 0; i < ZIP_TABSIZE; i++) {
316 for (currItem = mFiles[i]; currItem; currItem = currItem->next) {
317 //-- don't test (synthetic) directory items
318 if (currItem->IsDirectory())
319 continue;
320 nsresult rv = ExtractFile(currItem, 0, 0);
321 if (rv != NS_OK)
322 return rv;
326 return NS_OK;
329 //---------------------------------------------
330 // nsZipArchive::CloseArchive
331 //---------------------------------------------
332 nsresult nsZipArchive::CloseArchive()
334 if (mFd) {
335 PL_FinishArenaPool(&mArena);
336 mFd = NULL;
339 // CAUTION:
340 // We don't need to delete each of the nsZipItem as the memory for
341 // the zip item and the filename it holds are both allocated from the Arena.
342 // Hence, destroying the Arena is like destroying all the memory
343 // for all the nsZipItem in one shot. But if the ~nsZipItem is doing
344 // anything more than cleaning up memory, we should start calling it.
345 // Let us also cleanup the mFiles table for re-use on the next 'open' call
346 memset(mFiles, 0, sizeof(mFiles));
347 mBuiltSynthetics = false;
348 return NS_OK;
351 //---------------------------------------------
352 // nsZipArchive::GetItem
353 //---------------------------------------------
354 nsZipItem* nsZipArchive::GetItem(const char * aEntryName)
356 if (aEntryName) {
357 uint32_t len = strlen(aEntryName);
358 //-- If the request is for a directory, make sure that synthetic entries
359 //-- are created for the directories without their own entry.
360 if (!mBuiltSynthetics) {
361 if ((len > 0) && (aEntryName[len-1] == '/')) {
362 if (BuildSynthetics() != NS_OK)
363 return 0;
366 MOZ_WIN_MEM_TRY_BEGIN
367 nsZipItem* item = mFiles[ HashName(aEntryName, len) ];
368 while (item) {
369 if ((len == item->nameLength) &&
370 (!memcmp(aEntryName, item->Name(), len))) {
372 // Successful GetItem() is a good indicator that the file is about to be read
373 zipLog.Write(mURI, aEntryName);
374 return item; //-- found it
376 item = item->next;
378 MOZ_WIN_MEM_TRY_CATCH(return nullptr)
380 return nullptr;
383 //---------------------------------------------
384 // nsZipArchive::ExtractFile
385 // This extracts the item to the filehandle provided.
386 // If 'aFd' is null, it only tests the extraction.
387 // On extraction error(s) it removes the file.
388 // When needed, it also resolves the symlink.
389 //---------------------------------------------
390 nsresult nsZipArchive::ExtractFile(nsZipItem *item, const char *outname,
391 PRFileDesc* aFd)
393 if (!item)
394 return NS_ERROR_ILLEGAL_VALUE;
395 if (!mFd)
396 return NS_ERROR_FAILURE;
398 // Directory extraction is handled in nsJAR::Extract,
399 // so the item to be extracted should never be a directory
400 PR_ASSERT(!item->IsDirectory());
402 Bytef outbuf[ZIP_BUFLEN];
404 nsZipCursor cursor(item, this, outbuf, ZIP_BUFLEN, true);
406 nsresult rv = NS_OK;
408 while (true) {
409 uint32_t count = 0;
410 uint8_t* buf = cursor.Read(&count);
411 if (!buf) {
412 rv = NS_ERROR_FILE_CORRUPTED;
413 break;
414 } else if (count == 0) {
415 break;
418 if (aFd && PR_Write(aFd, buf, count) < (READTYPE)count) {
419 rv = NS_ERROR_FILE_DISK_FULL;
420 break;
424 //-- delete the file on errors, or resolve symlink if needed
425 if (aFd) {
426 PR_Close(aFd);
427 if (rv != NS_OK)
428 PR_Delete(outname);
429 #ifdef XP_UNIX
430 else if (item->IsSymlink())
431 rv = ResolveSymlink(outname);
432 #endif
435 return rv;
438 //---------------------------------------------
439 // nsZipArchive::FindInit
440 //---------------------------------------------
441 nsresult
442 nsZipArchive::FindInit(const char * aPattern, nsZipFind **aFind)
444 if (!aFind)
445 return NS_ERROR_ILLEGAL_VALUE;
447 // null out param in case an error happens
448 *aFind = NULL;
450 bool regExp = false;
451 char* pattern = 0;
453 // Create synthetic directory entries on demand
454 nsresult rv = BuildSynthetics();
455 if (rv != NS_OK)
456 return rv;
458 // validate the pattern
459 if (aPattern)
461 switch (NS_WildCardValid((char*)aPattern))
463 case INVALID_SXP:
464 return NS_ERROR_ILLEGAL_VALUE;
466 case NON_SXP:
467 regExp = false;
468 break;
470 case VALID_SXP:
471 regExp = true;
472 break;
474 default:
475 // undocumented return value from RegExpValid!
476 PR_ASSERT(false);
477 return NS_ERROR_ILLEGAL_VALUE;
480 pattern = PL_strdup(aPattern);
481 if (!pattern)
482 return NS_ERROR_OUT_OF_MEMORY;
485 *aFind = new nsZipFind(this, pattern, regExp);
486 if (!*aFind) {
487 PL_strfree(pattern);
488 return NS_ERROR_OUT_OF_MEMORY;
491 return NS_OK;
496 //---------------------------------------------
497 // nsZipFind::FindNext
498 //---------------------------------------------
499 nsresult nsZipFind::FindNext(const char ** aResult, uint16_t *aNameLen)
501 if (!mArchive || !aResult || !aNameLen)
502 return NS_ERROR_ILLEGAL_VALUE;
504 *aResult = 0;
505 *aNameLen = 0;
506 MOZ_WIN_MEM_TRY_BEGIN
507 // we start from last match, look for next
508 while (mSlot < ZIP_TABSIZE)
510 // move to next in current chain, or move to new slot
511 mItem = mItem ? mItem->next : mArchive->mFiles[mSlot];
513 bool found = false;
514 if (!mItem)
515 ++mSlot; // no more in this chain, move to next slot
516 else if (!mPattern)
517 found = true; // always match
518 else if (mRegExp)
520 char buf[kMaxNameLength+1];
521 memcpy(buf, mItem->Name(), mItem->nameLength);
522 buf[mItem->nameLength]='\0';
523 found = (NS_WildCardMatch(buf, mPattern, false) == MATCH);
525 else
526 found = ((mItem->nameLength == strlen(mPattern)) &&
527 (memcmp(mItem->Name(), mPattern, mItem->nameLength) == 0));
528 if (found) {
529 // Need also to return the name length, as it is NOT zero-terminatdd...
530 *aResult = mItem->Name();
531 *aNameLen = mItem->nameLength;
532 return NS_OK;
535 MOZ_WIN_MEM_TRY_CATCH(return NS_ERROR_FAILURE)
536 return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST;
539 #ifdef XP_UNIX
540 //---------------------------------------------
541 // ResolveSymlink
542 //---------------------------------------------
543 static nsresult ResolveSymlink(const char *path)
545 PRFileDesc * fIn = PR_Open(path, PR_RDONLY, 0000);
546 if (!fIn)
547 return NS_ERROR_FILE_DISK_FULL;
549 char buf[PATH_MAX+1];
550 int32_t length = PR_Read(fIn, (void*)buf, PATH_MAX);
551 PR_Close(fIn);
553 if ( (length <= 0)
554 || ((buf[length] = 0, PR_Delete(path)) != 0)
555 || (symlink(buf, path) != 0))
557 return NS_ERROR_FILE_DISK_FULL;
559 return NS_OK;
561 #endif
563 //***********************************************************
564 // nsZipArchive -- private implementation
565 //***********************************************************
567 //---------------------------------------------
568 // nsZipArchive::CreateZipItem
569 //---------------------------------------------
570 nsZipItem* nsZipArchive::CreateZipItem()
572 // Arena allocate the nsZipItem
573 void *mem;
574 PL_ARENA_ALLOCATE(mem, &mArena, sizeof(nsZipItem));
575 return (nsZipItem*)mem;
578 //---------------------------------------------
579 // nsZipArchive::BuildFileList
580 //---------------------------------------------
581 nsresult nsZipArchive::BuildFileList(PRFileDesc *aFd)
583 // Get archive size using end pos
584 const uint8_t* buf;
585 const uint8_t* startp = mFd->mFileData;
586 const uint8_t* endp = startp + mFd->mLen;
587 MOZ_WIN_MEM_TRY_BEGIN
588 uint32_t centralOffset = 4;
589 if (mFd->mLen > ZIPCENTRAL_SIZE && xtolong(startp + centralOffset) == CENTRALSIG) {
590 // Success means optimized jar layout from bug 559961 is in effect
591 uint32_t readaheadLength = xtolong(startp);
592 if (readaheadLength) {
593 #if defined(XP_UNIX)
594 madvise(const_cast<uint8_t*>(startp), readaheadLength, MADV_WILLNEED);
595 #elif defined(XP_WIN)
596 if (aFd) {
597 HANDLE hFile = (HANDLE) PR_FileDesc2NativeHandle(aFd);
598 mozilla::ReadAhead(hFile, 0, readaheadLength);
600 #endif
602 } else {
603 for (buf = endp - ZIPEND_SIZE; buf > startp; buf--)
605 if (xtolong(buf) == ENDSIG) {
606 centralOffset = xtolong(((ZipEnd *)buf)->offset_central_dir);
607 break;
612 if (!centralOffset)
613 return NS_ERROR_FILE_CORRUPTED;
615 //-- Read the central directory headers
616 buf = startp + centralOffset;
617 uint32_t sig = 0;
618 while (buf + int32_t(sizeof(uint32_t)) <= endp &&
619 (sig = xtolong(buf)) == CENTRALSIG) {
620 // Make sure there is enough data available.
621 if (endp - buf < ZIPCENTRAL_SIZE)
622 return NS_ERROR_FILE_CORRUPTED;
624 // Read the fixed-size data.
625 ZipCentral* central = (ZipCentral*)buf;
627 uint16_t namelen = xtoint(central->filename_len);
628 uint16_t extralen = xtoint(central->extrafield_len);
629 uint16_t commentlen = xtoint(central->commentfield_len);
631 // Point to the next item at the top of loop
632 buf += ZIPCENTRAL_SIZE + namelen + extralen + commentlen;
634 // Sanity check variable sizes and refuse to deal with
635 // anything too big: it's likely a corrupt archive.
636 if (namelen < 1 ||
637 namelen > kMaxNameLength ||
638 buf >= endp) {
639 return NS_ERROR_FILE_CORRUPTED;
642 nsZipItem* item = CreateZipItem();
643 if (!item)
644 return NS_ERROR_OUT_OF_MEMORY;
646 item->central = central;
647 item->nameLength = namelen;
648 item->isSynthetic = false;
650 // Add item to file table
651 uint32_t hash = HashName(item->Name(), namelen);
652 item->next = mFiles[hash];
653 mFiles[hash] = item;
655 sig = 0;
656 } /* while reading central directory records */
658 if (sig != ENDSIG)
659 return NS_ERROR_FILE_CORRUPTED;
661 // Make the comment available for consumers.
662 if (endp - buf >= ZIPEND_SIZE) {
663 ZipEnd *zipend = (ZipEnd *)buf;
665 buf += ZIPEND_SIZE;
666 uint16_t commentlen = xtoint(zipend->commentfield_len);
667 if (endp - buf >= commentlen) {
668 mCommentPtr = (const char *)buf;
669 mCommentLen = commentlen;
673 MOZ_WIN_MEM_TRY_CATCH(return NS_ERROR_FAILURE)
674 return NS_OK;
677 //---------------------------------------------
678 // nsZipArchive::BuildSynthetics
679 //---------------------------------------------
680 nsresult nsZipArchive::BuildSynthetics()
682 if (mBuiltSynthetics)
683 return NS_OK;
684 mBuiltSynthetics = true;
686 MOZ_WIN_MEM_TRY_BEGIN
687 // Create synthetic entries for any missing directories.
688 // Do this when all ziptable has scanned to prevent double entries.
689 for (int i = 0; i < ZIP_TABSIZE; ++i)
691 for (nsZipItem* item = mFiles[i]; item != nullptr; item = item->next)
693 if (item->isSynthetic)
694 continue;
696 //-- add entries for directories in the current item's path
697 //-- go from end to beginning, because then we can stop trying
698 //-- to create diritems if we find that the diritem we want to
699 //-- create already exists
700 //-- start just before the last char so as to not add the item
701 //-- twice if it's a directory
702 uint16_t namelen = item->nameLength;
703 MOZ_ASSERT(namelen > 0, "Attempt to build synthetic for zero-length entry name!");
704 const char *name = item->Name();
705 for (uint16_t dirlen = namelen - 1; dirlen > 0; dirlen--)
707 if (name[dirlen-1] != '/')
708 continue;
710 // The character before this is '/', so if this is also '/' then we
711 // have an empty path component. Skip it.
712 if (name[dirlen] == '/')
713 continue;
715 // Is the directory already in the file table?
716 uint32_t hash = HashName(item->Name(), dirlen);
717 bool found = false;
718 for (nsZipItem* zi = mFiles[hash]; zi != NULL; zi = zi->next)
720 if ((dirlen == zi->nameLength) &&
721 (0 == memcmp(item->Name(), zi->Name(), dirlen)))
723 // we've already added this dir and all its parents
724 found = true;
725 break;
728 // if the directory was found, break out of the directory
729 // creation loop now that we know all implicit directories
730 // are there -- otherwise, start creating the zip item
731 if (found)
732 break;
734 nsZipItem* diritem = CreateZipItem();
735 if (!diritem)
736 return NS_ERROR_OUT_OF_MEMORY;
738 // Point to the central record of the original item for the name part.
739 diritem->central = item->central;
740 diritem->nameLength = dirlen;
741 diritem->isSynthetic = true;
743 // add diritem to the file table
744 diritem->next = mFiles[hash];
745 mFiles[hash] = diritem;
746 } /* end processing of dirs in item's name */
749 MOZ_WIN_MEM_TRY_CATCH(return NS_ERROR_FAILURE)
750 return NS_OK;
753 nsZipHandle* nsZipArchive::GetFD()
755 if (!mFd)
756 return NULL;
757 return mFd.get();
760 //---------------------------------------------
761 // nsZipArchive::GetData
762 //---------------------------------------------
763 const uint8_t* nsZipArchive::GetData(nsZipItem* aItem)
765 PR_ASSERT (aItem);
766 MOZ_WIN_MEM_TRY_BEGIN
767 //-- read local header to get variable length values and calculate
768 //-- the real data offset
769 uint32_t len = mFd->mLen;
770 const uint8_t* data = mFd->mFileData;
771 uint32_t offset = aItem->LocalOffset();
772 if (offset + ZIPLOCAL_SIZE > len)
773 return nullptr;
775 // -- check signature before using the structure, in case the zip file is corrupt
776 ZipLocal* Local = (ZipLocal*)(data + offset);
777 if ((xtolong(Local->signature) != LOCALSIG))
778 return nullptr;
780 //-- NOTE: extralen is different in central header and local header
781 //-- for archives created using the Unix "zip" utility. To set
782 //-- the offset accurately we need the _local_ extralen.
783 offset += ZIPLOCAL_SIZE +
784 xtoint(Local->filename_len) +
785 xtoint(Local->extrafield_len);
787 // -- check if there is enough source data in the file
788 if (offset + aItem->Size() > len)
789 return nullptr;
791 return data + offset;
792 MOZ_WIN_MEM_TRY_CATCH(return nullptr)
795 // nsZipArchive::GetComment
796 bool nsZipArchive::GetComment(nsACString &aComment)
798 MOZ_WIN_MEM_TRY_BEGIN
799 aComment.Assign(mCommentPtr, mCommentLen);
800 MOZ_WIN_MEM_TRY_CATCH(return false)
801 return true;
804 //---------------------------------------------
805 // nsZipArchive::SizeOfMapping
806 //---------------------------------------------
807 int64_t nsZipArchive::SizeOfMapping()
809 return mFd ? mFd->SizeOfMapping() : 0;
812 //------------------------------------------
813 // nsZipArchive constructor and destructor
814 //------------------------------------------
816 nsZipArchive::nsZipArchive()
817 : mRefCnt(0)
818 , mBuiltSynthetics(false)
820 zipLog.AddRef();
822 MOZ_COUNT_CTOR(nsZipArchive);
824 // initialize the table to NULL
825 memset(mFiles, 0, sizeof(mFiles));
828 NS_IMPL_ADDREF(nsZipArchive)
829 NS_IMPL_RELEASE(nsZipArchive)
831 nsZipArchive::~nsZipArchive()
833 CloseArchive();
835 MOZ_COUNT_DTOR(nsZipArchive);
837 zipLog.Release();
841 //------------------------------------------
842 // nsZipFind constructor and destructor
843 //------------------------------------------
845 nsZipFind::nsZipFind(nsZipArchive* aZip, char* aPattern, bool aRegExp) :
846 mArchive(aZip),
847 mPattern(aPattern),
848 mItem(0),
849 mSlot(0),
850 mRegExp(aRegExp)
852 MOZ_COUNT_CTOR(nsZipFind);
855 nsZipFind::~nsZipFind()
857 PL_strfree(mPattern);
859 MOZ_COUNT_DTOR(nsZipFind);
862 //------------------------------------------
863 // helper functions
864 //------------------------------------------
867 * HashName
869 * returns a hash key for the entry name
871 static uint32_t HashName(const char* aName, uint16_t len)
873 PR_ASSERT(aName != 0);
875 const uint8_t* p = (const uint8_t*)aName;
876 const uint8_t* endp = p + len;
877 uint32_t val = 0;
878 while (p != endp) {
879 val = val*37 + *p++;
882 return (val % ZIP_TABSIZE);
886 * x t o i n t
888 * Converts a two byte ugly endianed integer
889 * to our platform's integer.
891 static uint16_t xtoint (const uint8_t *ii)
893 return (uint16_t) ((ii [0]) | (ii [1] << 8));
897 * x t o l o n g
899 * Converts a four byte ugly endianed integer
900 * to our platform's integer.
902 static uint32_t xtolong (const uint8_t *ll)
904 return (uint32_t)( (ll [0] << 0) |
905 (ll [1] << 8) |
906 (ll [2] << 16) |
907 (ll [3] << 24) );
911 * GetModTime
913 * returns last modification time in microseconds
915 static PRTime GetModTime(uint16_t aDate, uint16_t aTime)
917 // Note that on DST shift we can't handle correctly the hour that is valid
918 // in both DST zones
919 PRExplodedTime time;
921 time.tm_usec = 0;
923 time.tm_hour = (aTime >> 11) & 0x1F;
924 time.tm_min = (aTime >> 5) & 0x3F;
925 time.tm_sec = (aTime & 0x1F) * 2;
927 time.tm_year = (aDate >> 9) + 1980;
928 time.tm_month = ((aDate >> 5) & 0x0F) - 1;
929 time.tm_mday = aDate & 0x1F;
931 time.tm_params.tp_gmt_offset = 0;
932 time.tm_params.tp_dst_offset = 0;
934 PR_NormalizeTime(&time, PR_GMTParameters);
935 time.tm_params.tp_gmt_offset = PR_LocalTimeParameters(&time).tp_gmt_offset;
936 PR_NormalizeTime(&time, PR_GMTParameters);
937 time.tm_params.tp_dst_offset = PR_LocalTimeParameters(&time).tp_dst_offset;
939 return PR_ImplodeTime(&time);
942 uint32_t nsZipItem::LocalOffset()
944 return xtolong(central->localhdr_offset);
947 uint32_t nsZipItem::Size()
949 return isSynthetic ? 0 : xtolong(central->size);
952 uint32_t nsZipItem::RealSize()
954 return isSynthetic ? 0 : xtolong(central->orglen);
957 uint32_t nsZipItem::CRC32()
959 return isSynthetic ? 0 : xtolong(central->crc32);
962 uint16_t nsZipItem::Date()
964 return isSynthetic ? kSyntheticDate : xtoint(central->date);
967 uint16_t nsZipItem::Time()
969 return isSynthetic ? kSyntheticTime : xtoint(central->time);
972 uint16_t nsZipItem::Compression()
974 return isSynthetic ? STORED : xtoint(central->method);
977 bool nsZipItem::IsDirectory()
979 return isSynthetic || ((nameLength > 0) && ('/' == Name()[nameLength - 1]));
982 uint16_t nsZipItem::Mode()
984 if (isSynthetic) return 0755;
985 return ((uint16_t)(central->external_attributes[2]) | 0x100);
988 const uint8_t * nsZipItem::GetExtraField(uint16_t aTag, uint16_t *aBlockSize)
990 if (isSynthetic) return nullptr;
991 MOZ_WIN_MEM_TRY_BEGIN
992 const unsigned char *buf = ((const unsigned char*)central) + ZIPCENTRAL_SIZE +
993 nameLength;
994 uint32_t buflen = (uint32_t)xtoint(central->extrafield_len);
995 uint32_t pos = 0;
996 uint16_t tag, blocksize;
998 while (buf && (pos + 4) <= buflen) {
999 tag = xtoint(buf + pos);
1000 blocksize = xtoint(buf + pos + 2);
1002 if (aTag == tag && (pos + 4 + blocksize) <= buflen) {
1003 *aBlockSize = blocksize;
1004 return buf + pos;
1007 pos += blocksize + 4;
1010 MOZ_WIN_MEM_TRY_CATCH(return nullptr)
1011 return nullptr;
1015 PRTime nsZipItem::LastModTime()
1017 if (isSynthetic) return GetModTime(kSyntheticDate, kSyntheticTime);
1019 // Try to read timestamp from extra field
1020 uint16_t blocksize;
1021 const uint8_t *tsField = GetExtraField(EXTENDED_TIMESTAMP_FIELD, &blocksize);
1022 if (tsField && blocksize >= 5 && tsField[4] & EXTENDED_TIMESTAMP_MODTIME) {
1023 return (PRTime)(xtolong(tsField + 5)) * PR_USEC_PER_SEC;
1026 return GetModTime(Date(), Time());
1029 #ifdef XP_UNIX
1030 bool nsZipItem::IsSymlink()
1032 if (isSynthetic) return false;
1033 return (xtoint(central->external_attributes+2) & S_IFMT) == S_IFLNK;
1035 #endif
1037 nsZipCursor::nsZipCursor(nsZipItem *item, nsZipArchive *aZip, uint8_t* aBuf, uint32_t aBufSize, bool doCRC) :
1038 mItem(item),
1039 mBuf(aBuf),
1040 mBufSize(aBufSize),
1041 mDoCRC(doCRC)
1043 if (mItem->Compression() == DEFLATED) {
1044 #ifdef DEBUG
1045 nsresult status =
1046 #endif
1047 gZlibInit(&mZs);
1048 NS_ASSERTION(status == NS_OK, "Zlib failed to initialize");
1049 NS_ASSERTION(aBuf, "Must pass in a buffer for DEFLATED nsZipItem");
1052 mZs.avail_in = item->Size();
1053 mZs.next_in = (Bytef*)aZip->GetData(item);
1055 if (doCRC)
1056 mCRC = crc32(0L, Z_NULL, 0);
1059 nsZipCursor::~nsZipCursor()
1061 if (mItem->Compression() == DEFLATED) {
1062 inflateEnd(&mZs);
1066 uint8_t* nsZipCursor::ReadOrCopy(uint32_t *aBytesRead, bool aCopy) {
1067 int zerr;
1068 uint8_t *buf = nullptr;
1069 bool verifyCRC = true;
1071 if (!mZs.next_in)
1072 return nullptr;
1073 MOZ_WIN_MEM_TRY_BEGIN
1074 switch (mItem->Compression()) {
1075 case STORED:
1076 if (!aCopy) {
1077 *aBytesRead = mZs.avail_in;
1078 buf = mZs.next_in;
1079 mZs.next_in += mZs.avail_in;
1080 mZs.avail_in = 0;
1081 } else {
1082 *aBytesRead = mZs.avail_in > mBufSize ? mBufSize : mZs.avail_in;
1083 memcpy(mBuf, mZs.next_in, *aBytesRead);
1084 mZs.avail_in -= *aBytesRead;
1085 mZs.next_in += *aBytesRead;
1087 break;
1088 case DEFLATED:
1089 buf = mBuf;
1090 mZs.next_out = buf;
1091 mZs.avail_out = mBufSize;
1093 zerr = inflate(&mZs, Z_PARTIAL_FLUSH);
1094 if (zerr != Z_OK && zerr != Z_STREAM_END)
1095 return nullptr;
1097 *aBytesRead = mZs.next_out - buf;
1098 verifyCRC = (zerr == Z_STREAM_END);
1099 break;
1100 default:
1101 return nullptr;
1104 if (mDoCRC) {
1105 mCRC = crc32(mCRC, (const unsigned char*)buf, *aBytesRead);
1106 if (verifyCRC && mCRC != mItem->CRC32())
1107 return nullptr;
1109 MOZ_WIN_MEM_TRY_CATCH(return nullptr)
1110 return buf;
1113 nsZipItemPtr_base::nsZipItemPtr_base(nsZipArchive *aZip, const char * aEntryName, bool doCRC) :
1114 mReturnBuf(nullptr)
1116 // make sure the ziparchive hangs around
1117 mZipHandle = aZip->GetFD();
1119 nsZipItem* item = aZip->GetItem(aEntryName);
1120 if (!item)
1121 return;
1123 uint32_t size = 0;
1124 if (item->Compression() == DEFLATED) {
1125 size = item->RealSize();
1126 mAutoBuf = new uint8_t[size];
1129 nsZipCursor cursor(item, aZip, mAutoBuf, size, doCRC);
1130 mReturnBuf = cursor.Read(&mReadlen);
1131 if (!mReturnBuf) {
1132 return;
1135 if (mReadlen != item->RealSize()) {
1136 NS_ASSERTION(mReadlen == item->RealSize(), "nsZipCursor underflow");
1137 mReturnBuf = nullptr;
1138 return;