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/. */
7 * This module implements a simple archive extractor for the PKZIP format.
10 #define READTYPE int32_t
12 #include "nsISupportsUtils.h"
13 #include "mozilla/MmapFaultHandler.h"
15 #include "mozilla/Attributes.h"
16 #include "mozilla/Logging.h"
17 #include "mozilla/MemUtils.h"
18 #include "mozilla/UniquePtrExtensions.h"
19 #include "mozilla/StaticMutex.h"
20 #include "mozilla/StaticPrefs_network.h"
22 #include "nsDirectoryService.h"
23 #include "nsWildCard.h"
24 #include "nsXULAppAPI.h"
25 #include "nsZipArchive.h"
32 // For placement new used for arena allocations of zip file list
34 #define ZIP_ARENABLOCKSIZE (1 * 1024)
37 # include <sys/mman.h>
38 # include <sys/types.h>
39 # include <sys/stat.h>
47 # include <sys/syslimits.h>
48 #endif /*__SYMBIAN32__*/
50 #ifndef XP_UNIX /* we need some constants defined in limits.h and unistd.h */
52 # define S_IFMT 0170000
55 # define S_IFLNK 0120000
58 # define PATH_MAX 1024
63 # include "private/pprio.h" // To get PR_ImportFile
66 using namespace mozilla
;
68 static LazyLogModule
gZipLog("nsZipArchive");
77 #define LOG(args) MOZ_LOG(gZipLog, mozilla::LogLevel::Debug, args)
78 #define LOG_ENABLED() MOZ_LOG_TEST(gZipLog, mozilla::LogLevel::Debug)
80 static const uint32_t kMaxNameLength
= PATH_MAX
; /* Maximum name length */
81 // For synthetic zip entries. Date/time corresponds to 1980-01-01 00:00.
82 static const uint16_t kSyntheticTime
= 0;
83 static const uint16_t kSyntheticDate
= (1 + (1 << 5) + (0 << 9));
85 static uint16_t xtoint(const uint8_t* ii
);
86 static uint32_t xtolong(const uint8_t* ll
);
87 static uint32_t HashName(const char* aName
, uint16_t nameLen
);
89 class ZipArchiveLogger
{
91 void Init(const char* env
) {
92 StaticMutexAutoLock
lock(sLock
);
95 MOZ_ASSERT(mRefCnt
>= 0);
99 nsCOMPtr
<nsIFile
> logFile
;
100 nsresult rv
= NS_NewLocalFile(NS_ConvertUTF8toUTF16(env
), false,
101 getter_AddRefs(logFile
));
102 if (NS_FAILED(rv
)) return;
104 // Create the log file and its parent directory (in case it doesn't exist)
105 rv
= logFile
->Create(nsIFile::NORMAL_FILE_TYPE
, 0644);
106 if (NS_FAILED(rv
)) return;
110 // PR_APPEND is racy on Windows, so open a handle ourselves with flags
111 // that will work, and use PR_ImportFile to make it a PRFileDesc. This can
112 // go away when bug 840435 is fixed.
114 logFile
->GetPath(path
);
115 if (path
.IsEmpty()) return;
117 CreateFileW(path
.get(), FILE_APPEND_DATA
, FILE_SHARE_WRITE
, nullptr,
118 OPEN_ALWAYS
, 0, nullptr);
119 if (handle
== INVALID_HANDLE_VALUE
) return;
120 file
= PR_ImportFile((PROsfd
)handle
);
123 rv
= logFile
->OpenNSPRFileDesc(
124 PR_WRONLY
| PR_CREATE_FILE
| PR_APPEND
| PR_SYNC
, 0644, &file
);
125 if (NS_FAILED(rv
)) return;
131 void Write(const nsACString
& zip
, const char* entry
) {
132 StaticMutexAutoLock
lock(sLock
);
139 PR_Write(mFd
, buf
.get(), buf
.Length());
144 StaticMutexAutoLock
lock(sLock
);
146 MOZ_ASSERT(mRefCnt
> 0);
147 if ((0 == --mRefCnt
) && mFd
) {
154 static StaticMutex sLock
;
155 int mRefCnt
MOZ_GUARDED_BY(sLock
);
156 PRFileDesc
* mFd
MOZ_GUARDED_BY(sLock
);
159 StaticMutex
ZipArchiveLogger::sLock
;
160 static ZipArchiveLogger zipLog
;
162 //***********************************************************
163 // For every inflation the following allocations are done:
166 //***********************************************************
168 nsresult
gZlibInit(z_stream
* zs
) {
169 memset(zs
, 0, sizeof(z_stream
));
170 int zerr
= inflateInit2(zs
, -MAX_WBITS
);
171 if (zerr
!= Z_OK
) return NS_ERROR_OUT_OF_MEMORY
;
176 nsZipHandle::nsZipHandle()
177 : mFileData(nullptr),
184 NS_IMPL_ADDREF(nsZipHandle
)
185 NS_IMPL_RELEASE(nsZipHandle
)
187 nsresult
nsZipHandle::Init(nsIFile
* file
, nsZipHandle
** ret
, PRFileDesc
** aFd
) {
188 mozilla::AutoFDClose fd
;
189 int32_t flags
= PR_RDONLY
;
191 flags
|= nsIFile::OS_READAHEAD
;
193 LOG(("ZipHandle::Init %s", file
->HumanReadablePath().get()));
194 nsresult rv
= file
->OpenNSPRFileDesc(flags
, 0000, getter_Transfers(fd
));
195 if (NS_FAILED(rv
)) return rv
;
197 int64_t size
= PR_Available64(fd
.get());
198 if (size
>= INT32_MAX
) return NS_ERROR_FILE_TOO_BIG
;
200 PRFileMap
* map
= PR_CreateFileMap(fd
.get(), size
, PR_PROT_READONLY
);
201 if (!map
) return NS_ERROR_FAILURE
;
203 uint8_t* buf
= (uint8_t*)PR_MemMap(map
, 0, (uint32_t)size
);
204 // Bug 525755: PR_MemMap fails when fd points at something other than a normal
207 PR_CloseFileMap(map
);
208 return NS_ERROR_FAILURE
;
211 RefPtr
<nsZipHandle
> handle
= new nsZipHandle();
213 PR_MemUnmap(buf
, (uint32_t)size
);
214 PR_CloseFileMap(map
);
215 return NS_ERROR_OUT_OF_MEMORY
;
223 handle
->mNSPRFileDesc
= std::move(fd
);
225 handle
->mFile
.Init(file
);
226 handle
->mTotalLen
= (uint32_t)size
;
227 handle
->mFileStart
= buf
;
228 rv
= handle
->findDataStart();
230 PR_MemUnmap(buf
, (uint32_t)size
);
231 handle
->mFileStart
= nullptr;
232 PR_CloseFileMap(map
);
240 nsresult
nsZipHandle::Init(nsZipArchive
* zip
, const char* entry
,
242 RefPtr
<nsZipHandle
> handle
= new nsZipHandle();
243 if (!handle
) return NS_ERROR_OUT_OF_MEMORY
;
245 LOG(("ZipHandle::Init entry %s", entry
));
247 nsZipItem
* item
= zip
->GetItem(entry
);
248 if (item
&& item
->Compression() == DEFLATED
&&
249 StaticPrefs::network_jar_max_entry_size()) {
250 if (item
->RealSize() > StaticPrefs::network_jar_max_entry_size()) {
251 return NS_ERROR_OUT_OF_MEMORY
;
255 handle
->mBuf
= MakeUnique
<nsZipItemPtr
<uint8_t>>(zip
, entry
);
256 if (!handle
->mBuf
) return NS_ERROR_OUT_OF_MEMORY
;
258 if (!handle
->mBuf
->Buffer()) return NS_ERROR_UNEXPECTED
;
260 handle
->mMap
= nullptr;
261 handle
->mFile
.Init(zip
, entry
);
262 handle
->mTotalLen
= handle
->mBuf
->Length();
263 handle
->mFileStart
= handle
->mBuf
->Buffer();
264 nsresult rv
= handle
->findDataStart();
272 nsresult
nsZipHandle::Init(const uint8_t* aData
, uint32_t aLen
,
273 nsZipHandle
** aRet
) {
274 RefPtr
<nsZipHandle
> handle
= new nsZipHandle();
276 handle
->mFileStart
= aData
;
277 handle
->mTotalLen
= aLen
;
278 nsresult rv
= handle
->findDataStart();
286 // This function finds the start of the ZIP data. If the file is a regular ZIP,
287 // this is just the start of the file. If the file is a CRX file, the start of
288 // the data is after the CRX header.
290 // CRX header reference, version 2:
291 // Header requires little-endian byte ordering with 4-byte alignment.
292 // 32 bits : magicNumber - Defined as a |char m[] = "Cr24"|.
293 // Equivilant to |uint32_t m = 0x34327243|.
294 // 32 bits : version - Unsigned integer representing the CRX file
295 // format version. Currently equal to 2.
296 // 32 bits : pubKeyLength - Unsigned integer representing the length
297 // of the public key in bytes.
298 // 32 bits : sigLength - Unsigned integer representing the length
299 // of the signature in bytes.
300 // pubKeyLength : publicKey - Contents of the author's public key.
301 // sigLength : signature - Signature of the ZIP content.
302 // Signature is created using the RSA
303 // algorithm with the SHA-1 hash function.
305 // CRX header reference, version 3:
306 // Header requires little-endian byte ordering with 4-byte alignment.
307 // 32 bits : magicNumber - Defined as a |char m[] = "Cr24"|.
308 // Equivilant to |uint32_t m = 0x34327243|.
309 // 32 bits : version - Unsigned integer representing the CRX file
310 // format version. Currently equal to 3.
311 // 32 bits : headerLength - Unsigned integer representing the length
312 // of the CRX header in bytes.
313 // headerLength : header - CRXv3 header.
314 nsresult
nsZipHandle::findDataStart() {
315 // In the CRX header, integers are 32 bits. Our pointer to the file is of
316 // type |uint8_t|, which is guaranteed to be 8 bits.
317 const uint32_t CRXIntSize
= 4;
319 MMAP_FAULT_HANDLER_BEGIN_HANDLE(this)
320 if (mTotalLen
> CRXIntSize
* 4 && xtolong(mFileStart
) == kCRXMagic
) {
321 const uint8_t* headerData
= mFileStart
;
322 headerData
+= CRXIntSize
; // Skip magic number
323 uint32_t version
= xtolong(headerData
);
324 headerData
+= CRXIntSize
; // Skip version
325 uint32_t headerSize
= CRXIntSize
* 2;
327 uint32_t subHeaderSize
= xtolong(headerData
);
328 headerSize
+= CRXIntSize
+ subHeaderSize
;
329 } else if (version
< 3) {
330 uint32_t pubKeyLength
= xtolong(headerData
);
331 headerData
+= CRXIntSize
;
332 uint32_t sigLength
= xtolong(headerData
);
333 headerSize
+= CRXIntSize
* 2 + pubKeyLength
+ sigLength
;
335 return NS_ERROR_FILE_CORRUPTED
;
337 if (mTotalLen
> headerSize
) {
338 mLen
= mTotalLen
- headerSize
;
339 mFileData
= mFileStart
+ headerSize
;
342 return NS_ERROR_FILE_CORRUPTED
;
345 mFileData
= mFileStart
;
346 MMAP_FAULT_HANDLER_CATCH(NS_ERROR_FAILURE
)
350 int64_t nsZipHandle::SizeOfMapping() { return mTotalLen
; }
352 nsresult
nsZipHandle::GetNSPRFileDesc(PRFileDesc
** aNSPRFileDesc
) {
353 if (!aNSPRFileDesc
) {
354 return NS_ERROR_ILLEGAL_VALUE
;
357 *aNSPRFileDesc
= mNSPRFileDesc
.get();
358 if (!mNSPRFileDesc
) {
359 return NS_ERROR_NOT_AVAILABLE
;
365 nsZipHandle::~nsZipHandle() {
367 PR_MemUnmap((void*)mFileStart
, mTotalLen
);
368 PR_CloseFileMap(mMap
);
370 mFileStart
= nullptr;
376 //***********************************************************
377 // nsZipArchive -- public methods
378 //***********************************************************
380 //---------------------------------------------
381 // nsZipArchive::OpenArchive
382 //---------------------------------------------
384 already_AddRefed
<nsZipArchive
> nsZipArchive::OpenArchive(
385 nsZipHandle
* aZipHandle
, PRFileDesc
* aFd
) {
387 RefPtr
<nsZipArchive
> self(new nsZipArchive(aZipHandle
, aFd
, rv
));
388 LOG(("ZipHandle::OpenArchive[%p]", self
.get()));
392 return self
.forget();
396 already_AddRefed
<nsZipArchive
> nsZipArchive::OpenArchive(nsIFile
* aFile
) {
397 RefPtr
<nsZipHandle
> handle
;
399 mozilla::AutoFDClose fd
;
401 nsZipHandle::Init(aFile
, getter_AddRefs(handle
), getter_Transfers(fd
));
403 nsresult rv
= nsZipHandle::Init(aFile
, getter_AddRefs(handle
));
405 if (NS_FAILED(rv
)) return nullptr;
408 return OpenArchive(handle
, fd
.get());
410 return OpenArchive(handle
);
414 //---------------------------------------------
415 // nsZipArchive::Test
416 //---------------------------------------------
417 nsresult
nsZipArchive::Test(const char* aEntryName
) {
420 if (aEntryName
) // only test specified item
422 currItem
= GetItem(aEntryName
);
423 if (!currItem
) return NS_ERROR_FILE_NOT_FOUND
;
424 //-- don't test (synthetic) directory items
425 if (currItem
->IsDirectory()) return NS_OK
;
426 return ExtractFile(currItem
, 0, 0);
429 // test all items in archive
430 for (auto* item
: mFiles
) {
431 for (currItem
= item
; currItem
; currItem
= currItem
->next
) {
432 //-- don't test (synthetic) directory items
433 if (currItem
->IsDirectory()) continue;
434 nsresult rv
= ExtractFile(currItem
, 0, 0);
435 if (rv
!= NS_OK
) return rv
;
442 //---------------------------------------------
443 // nsZipArchive::GetItem
444 //---------------------------------------------
445 nsZipItem
* nsZipArchive::GetItem(const char* aEntryName
) {
446 MutexAutoLock
lock(mLock
);
448 LOG(("ZipHandle::GetItem[%p] %s", this, aEntryName
));
450 uint32_t len
= strlen(aEntryName
);
451 //-- If the request is for a directory, make sure that synthetic entries
452 //-- are created for the directories without their own entry.
453 if (!mBuiltSynthetics
) {
454 if ((len
> 0) && (aEntryName
[len
- 1] == '/')) {
455 if (BuildSynthetics() != NS_OK
) return 0;
458 MMAP_FAULT_HANDLER_BEGIN_HANDLE(mFd
)
459 nsZipItem
* item
= mFiles
[HashName(aEntryName
, len
)];
461 if ((len
== item
->nameLength
) &&
462 (!memcmp(aEntryName
, item
->Name(), len
))) {
463 // Successful GetItem() is a good indicator that the file is about to be
465 if (mUseZipLog
&& mURI
.Length()) {
466 zipLog
.Write(mURI
, aEntryName
);
468 return item
; //-- found it
472 MMAP_FAULT_HANDLER_CATCH(nullptr)
477 //---------------------------------------------
478 // nsZipArchive::ExtractFile
479 // This extracts the item to the filehandle provided.
480 // If 'aFd' is null, it only tests the extraction.
481 // On extraction error(s) it removes the file.
482 //---------------------------------------------
483 nsresult
nsZipArchive::ExtractFile(nsZipItem
* item
, nsIFile
* outFile
,
485 MutexAutoLock
lock(mLock
);
486 LOG(("ZipHandle::ExtractFile[%p]", this));
487 if (!item
) return NS_ERROR_ILLEGAL_VALUE
;
488 if (!mFd
) return NS_ERROR_FAILURE
;
490 // Directory extraction is handled in nsJAR::Extract,
491 // so the item to be extracted should never be a directory
492 MOZ_ASSERT(!item
->IsDirectory());
494 Bytef outbuf
[ZIP_BUFLEN
];
496 nsZipCursor
cursor(item
, this, outbuf
, ZIP_BUFLEN
, true);
502 uint8_t* buf
= cursor
.Read(&count
);
504 rv
= NS_ERROR_FILE_CORRUPTED
;
511 if (aFd
&& PR_Write(aFd
, buf
, count
) < (READTYPE
)count
) {
512 rv
= NS_ERROR_FILE_NO_DEVICE_SPACE
;
517 //-- delete the file on errors
520 if (NS_FAILED(rv
) && outFile
) {
521 outFile
->Remove(false);
528 //---------------------------------------------
529 // nsZipArchive::FindInit
530 //---------------------------------------------
531 nsresult
nsZipArchive::FindInit(const char* aPattern
, nsZipFind
** aFind
) {
532 if (!aFind
) return NS_ERROR_ILLEGAL_VALUE
;
534 MutexAutoLock
lock(mLock
);
536 LOG(("ZipHandle::FindInit[%p]", this));
537 // null out param in case an error happens
543 // Create synthetic directory entries on demand
544 nsresult rv
= BuildSynthetics();
545 if (rv
!= NS_OK
) return rv
;
547 // validate the pattern
549 switch (NS_WildCardValid((char*)aPattern
)) {
551 return NS_ERROR_ILLEGAL_VALUE
;
562 // undocumented return value from RegExpValid!
564 return NS_ERROR_ILLEGAL_VALUE
;
567 pattern
= strdup(aPattern
);
568 if (!pattern
) return NS_ERROR_OUT_OF_MEMORY
;
571 *aFind
= new nsZipFind(this, pattern
, regExp
);
574 return NS_ERROR_OUT_OF_MEMORY
;
580 //---------------------------------------------
581 // nsZipFind::FindNext
582 //---------------------------------------------
583 nsresult
nsZipFind::FindNext(const char** aResult
, uint16_t* aNameLen
) {
584 if (!mArchive
|| !aResult
|| !aNameLen
) return NS_ERROR_ILLEGAL_VALUE
;
586 MutexAutoLock
lock(mArchive
->mLock
);
589 MMAP_FAULT_HANDLER_BEGIN_HANDLE(mArchive
->GetFD())
590 // we start from last match, look for next
591 while (mSlot
< ZIP_TABSIZE
) {
592 // move to next in current chain, or move to new slot
593 mItem
= mItem
? mItem
->next
: mArchive
->mFiles
[mSlot
];
597 ++mSlot
; // no more in this chain, move to next slot
599 found
= true; // always match
601 char buf
[kMaxNameLength
+ 1];
602 memcpy(buf
, mItem
->Name(), mItem
->nameLength
);
603 buf
[mItem
->nameLength
] = '\0';
604 found
= (NS_WildCardMatch(buf
, mPattern
, false) == MATCH
);
606 found
= ((mItem
->nameLength
== strlen(mPattern
)) &&
607 (memcmp(mItem
->Name(), mPattern
, mItem
->nameLength
) == 0));
609 // Need also to return the name length, as it is NOT zero-terminatdd...
610 *aResult
= mItem
->Name();
611 *aNameLen
= mItem
->nameLength
;
612 LOG(("ZipHandle::FindNext[%p] %s", this, *aResult
));
616 MMAP_FAULT_HANDLER_CATCH(NS_ERROR_FAILURE
)
617 LOG(("ZipHandle::FindNext[%p] not found %s", this, mPattern
));
618 return NS_ERROR_FILE_NOT_FOUND
;
621 //***********************************************************
622 // nsZipArchive -- private implementation
623 //***********************************************************
625 //---------------------------------------------
626 // nsZipArchive::CreateZipItem
627 //---------------------------------------------
628 nsZipItem
* nsZipArchive::CreateZipItem() {
629 // Arena allocate the nsZipItem
630 return (nsZipItem
*)mArena
.Allocate(sizeof(nsZipItem
), mozilla::fallible
);
633 //---------------------------------------------
634 // nsZipArchive::BuildFileList
635 //---------------------------------------------
636 nsresult
nsZipArchive::BuildFileList(PRFileDesc
* aFd
)
637 MOZ_NO_THREAD_SAFETY_ANALYSIS
{
638 // We're only called from the constructor, but need to call
639 // CreateZipItem(), which touches locked data, and modify mFiles. Turn
640 // off thread-safety, which normally doesn't apply for constructors
643 // Get archive size using end pos
645 const uint8_t* startp
= mFd
->mFileData
;
646 const uint8_t* endp
= startp
+ mFd
->mLen
;
647 MMAP_FAULT_HANDLER_BEGIN_HANDLE(mFd
)
648 uint32_t centralOffset
= 4;
649 LOG(("ZipHandle::BuildFileList[%p]", this));
650 // Only perform readahead in the parent process. Children processes
651 // don't need readahead when the file has already been readahead by
652 // the parent process, and readahead only really happens for omni.ja,
653 // which is used in the parent process.
654 if (XRE_IsParentProcess() && mFd
->mLen
> ZIPCENTRAL_SIZE
&&
655 xtolong(startp
+ centralOffset
) == CENTRALSIG
) {
656 // Success means optimized jar layout from bug 559961 is in effect
657 uint32_t readaheadLength
= xtolong(startp
);
658 mozilla::PrefetchMemory(const_cast<uint8_t*>(startp
), readaheadLength
);
660 for (buf
= endp
- ZIPEND_SIZE
; buf
> startp
; buf
--) {
661 if (xtolong(buf
) == ENDSIG
) {
662 centralOffset
= xtolong(((ZipEnd
*)buf
)->offset_central_dir
);
668 if (!centralOffset
) {
669 return NS_ERROR_FILE_CORRUPTED
;
672 buf
= startp
+ centralOffset
;
674 // avoid overflow of startp + centralOffset.
676 return NS_ERROR_FILE_CORRUPTED
;
679 //-- Read the central directory headers
681 while ((buf
+ int32_t(sizeof(uint32_t)) > buf
) &&
682 (buf
+ int32_t(sizeof(uint32_t)) <= endp
) &&
683 ((sig
= xtolong(buf
)) == CENTRALSIG
)) {
684 // Make sure there is enough data available.
685 if ((buf
> endp
) || (endp
- buf
< ZIPCENTRAL_SIZE
)) {
686 return NS_ERROR_FILE_CORRUPTED
;
689 // Read the fixed-size data.
690 ZipCentral
* central
= (ZipCentral
*)buf
;
692 uint16_t namelen
= xtoint(central
->filename_len
);
693 uint16_t extralen
= xtoint(central
->extrafield_len
);
694 uint16_t commentlen
= xtoint(central
->commentfield_len
);
695 uint32_t diff
= ZIPCENTRAL_SIZE
+ namelen
+ extralen
+ commentlen
;
697 // Sanity check variable sizes and refuse to deal with
698 // anything too big: it's likely a corrupt archive.
699 if (namelen
< 1 || namelen
> kMaxNameLength
) {
700 return NS_ERROR_FILE_CORRUPTED
;
702 if (buf
>= buf
+ diff
|| // No overflow
703 buf
>= endp
- diff
) {
704 return NS_ERROR_FILE_CORRUPTED
;
707 // Point to the next item at the top of loop
710 nsZipItem
* item
= CreateZipItem();
711 if (!item
) return NS_ERROR_OUT_OF_MEMORY
;
713 item
->central
= central
;
714 item
->nameLength
= namelen
;
715 item
->isSynthetic
= false;
717 // Add item to file table
719 nsDependentCSubstring
name(item
->Name(), namelen
);
720 LOG((" %s", PromiseFlatCString(name
).get()));
722 uint32_t hash
= HashName(item
->Name(), namelen
);
723 item
->next
= mFiles
[hash
];
727 } /* while reading central directory records */
729 if (sig
!= ENDSIG
&& sig
!= ENDSIG64
) {
730 return NS_ERROR_FILE_CORRUPTED
;
733 MMAP_FAULT_HANDLER_CATCH(NS_ERROR_FAILURE
)
737 //---------------------------------------------
738 // nsZipArchive::BuildSynthetics
739 //---------------------------------------------
740 nsresult
nsZipArchive::BuildSynthetics() {
741 mLock
.AssertCurrentThreadOwns();
743 if (mBuiltSynthetics
) return NS_OK
;
744 mBuiltSynthetics
= true;
746 MMAP_FAULT_HANDLER_BEGIN_HANDLE(mFd
)
747 // Create synthetic entries for any missing directories.
748 // Do this when all ziptable has scanned to prevent double entries.
749 for (auto* item
: mFiles
) {
750 for (; item
!= nullptr; item
= item
->next
) {
751 if (item
->isSynthetic
) continue;
753 //-- add entries for directories in the current item's path
754 //-- go from end to beginning, because then we can stop trying
755 //-- to create diritems if we find that the diritem we want to
756 //-- create already exists
757 //-- start just before the last char so as to not add the item
758 //-- twice if it's a directory
759 uint16_t namelen
= item
->nameLength
;
760 MOZ_ASSERT(namelen
> 0,
761 "Attempt to build synthetic for zero-length entry name!");
762 const char* name
= item
->Name();
763 for (uint16_t dirlen
= namelen
- 1; dirlen
> 0; dirlen
--) {
764 if (name
[dirlen
- 1] != '/') continue;
766 // The character before this is '/', so if this is also '/' then we
767 // have an empty path component. Skip it.
768 if (name
[dirlen
] == '/') continue;
770 // Is the directory already in the file table?
771 uint32_t hash
= HashName(item
->Name(), dirlen
);
773 for (nsZipItem
* zi
= mFiles
[hash
]; zi
!= nullptr; zi
= zi
->next
) {
774 if ((dirlen
== zi
->nameLength
) &&
775 (0 == memcmp(item
->Name(), zi
->Name(), dirlen
))) {
776 // we've already added this dir and all its parents
781 // if the directory was found, break out of the directory
782 // creation loop now that we know all implicit directories
783 // are there -- otherwise, start creating the zip item
786 nsZipItem
* diritem
= CreateZipItem();
787 if (!diritem
) return NS_ERROR_OUT_OF_MEMORY
;
789 // Point to the central record of the original item for the name part.
790 diritem
->central
= item
->central
;
791 diritem
->nameLength
= dirlen
;
792 diritem
->isSynthetic
= true;
794 // add diritem to the file table
795 diritem
->next
= mFiles
[hash
];
796 mFiles
[hash
] = diritem
;
797 } /* end processing of dirs in item's name */
800 MMAP_FAULT_HANDLER_CATCH(NS_ERROR_FAILURE
)
804 //---------------------------------------------
805 // nsZipArchive::GetFD
806 //---------------------------------------------
807 nsZipHandle
* nsZipArchive::GetFD() const { return mFd
.get(); }
809 //---------------------------------------------
810 // nsZipArchive::GetDataOffset
811 // Returns 0 on an error; 0 is not a valid result for any success case
812 //---------------------------------------------
813 uint32_t nsZipArchive::GetDataOffset(nsZipItem
* aItem
) {
815 MOZ_DIAGNOSTIC_ASSERT(mFd
);
818 MMAP_FAULT_HANDLER_BEGIN_HANDLE(mFd
)
819 //-- read local header to get variable length values and calculate
820 //-- the real data offset
821 uint32_t len
= mFd
->mLen
;
822 MOZ_DIAGNOSTIC_ASSERT(len
<= UINT32_MAX
, "mLen > 2GB");
823 const uint8_t* data
= mFd
->mFileData
;
824 offset
= aItem
->LocalOffset();
825 if (len
< ZIPLOCAL_SIZE
|| offset
> len
- ZIPLOCAL_SIZE
) {
828 // Check there's enough space for the signature
829 if (offset
> mFd
->mLen
) {
830 NS_WARNING("Corrupt local offset in JAR file");
834 // -- check signature before using the structure, in case the zip file is
836 ZipLocal
* Local
= (ZipLocal
*)(data
+ offset
);
837 if ((xtolong(Local
->signature
) != LOCALSIG
)) return 0;
839 //-- NOTE: extralen is different in central header and local header
840 //-- for archives created using the Unix "zip" utility. To set
841 //-- the offset accurately we need the _local_ extralen.
842 offset
+= ZIPLOCAL_SIZE
+ xtoint(Local
->filename_len
) +
843 xtoint(Local
->extrafield_len
);
844 // Check data points inside the file.
845 if (offset
> mFd
->mLen
) {
846 NS_WARNING("Corrupt data offset in JAR file");
850 MMAP_FAULT_HANDLER_CATCH(0)
855 //---------------------------------------------
856 // nsZipArchive::GetData
857 //---------------------------------------------
858 const uint8_t* nsZipArchive::GetData(nsZipItem
* aItem
) {
859 MOZ_DIAGNOSTIC_ASSERT(aItem
);
863 uint32_t offset
= GetDataOffset(aItem
);
865 MMAP_FAULT_HANDLER_BEGIN_HANDLE(mFd
)
866 // -- check if there is enough source data in the file
867 if (!offset
|| mFd
->mLen
< aItem
->Size() ||
868 offset
> mFd
->mLen
- aItem
->Size() ||
869 (aItem
->Compression() == STORED
&& aItem
->Size() != aItem
->RealSize())) {
872 MMAP_FAULT_HANDLER_CATCH(nullptr)
874 return mFd
->mFileData
+ offset
;
877 //---------------------------------------------
878 // nsZipArchive::SizeOfMapping
879 //---------------------------------------------
880 int64_t nsZipArchive::SizeOfMapping() { return mFd
? mFd
->SizeOfMapping() : 0; }
882 //------------------------------------------
883 // nsZipArchive constructor and destructor
884 //------------------------------------------
886 nsZipArchive::nsZipArchive(nsZipHandle
* aZipHandle
, PRFileDesc
* aFd
,
888 : mRefCnt(0), mFd(aZipHandle
), mUseZipLog(false), mBuiltSynthetics(false) {
889 // initialize the table to nullptr
890 memset(mFiles
, 0, sizeof(mFiles
));
891 MOZ_DIAGNOSTIC_ASSERT(aZipHandle
);
893 //-- get table of contents for archive
894 aRv
= BuildFileList(aFd
);
895 if (NS_FAILED(aRv
)) {
896 return; // whomever created us must destroy us in this case
898 if (aZipHandle
->mFile
&& XRE_IsParentProcess()) {
899 static char* env
= PR_GetEnv("MOZ_JAR_LOG_FILE");
904 // We only log accesses in jar/zip archives within the NS_GRE_DIR
905 // and/or the APK on Android. For the former, we log the archive path
906 // relative to NS_GRE_DIR, and for the latter, the nested-archive
907 // path within the APK. This makes the path match the path of the
908 // archives relative to the packaged dist/$APP_NAME directory in a
910 if (aZipHandle
->mFile
.IsZip()) {
911 // Nested archive, likely omni.ja in APK.
912 aZipHandle
->mFile
.GetPath(mURI
);
913 } else if (nsDirectoryService::gService
) {
914 // We can reach here through the initialization of Omnijar from
915 // XRE_InitCommandLine, which happens before the directory service
916 // is initialized. When that happens, it means the opened archive is
917 // the APK, and we don't care to log that one, so we just skip
918 // when the directory service is not initialized.
919 nsCOMPtr
<nsIFile
> dir
= aZipHandle
->mFile
.GetBaseFile();
920 nsCOMPtr
<nsIFile
> gre_dir
;
922 if (NS_SUCCEEDED(nsDirectoryService::gService
->Get(
923 NS_GRE_DIR
, NS_GET_IID(nsIFile
), getter_AddRefs(gre_dir
)))) {
925 nsCOMPtr
<nsIFile
> parent
;
926 while (NS_SUCCEEDED(dir
->GetNativeLeafName(leaf
)) &&
927 NS_SUCCEEDED(dir
->GetParent(getter_AddRefs(parent
)))) {
935 path
.Insert(leaf
, 0);
937 if (NS_SUCCEEDED(dir
->Equals(gre_dir
, &equals
)) && equals
) {
948 NS_IMPL_ADDREF(nsZipArchive
)
949 NS_IMPL_RELEASE(nsZipArchive
)
951 nsZipArchive::~nsZipArchive() {
952 LOG(("Closing nsZipArchive[%p]", this));
958 //------------------------------------------
959 // nsZipFind constructor and destructor
960 //------------------------------------------
962 nsZipFind::nsZipFind(nsZipArchive
* aZip
, char* aPattern
, bool aRegExp
)
968 MOZ_COUNT_CTOR(nsZipFind
);
971 nsZipFind::~nsZipFind() {
974 MOZ_COUNT_DTOR(nsZipFind
);
977 //------------------------------------------
979 //------------------------------------------
984 * returns a hash key for the entry name
986 MOZ_NO_SANITIZE_UNSIGNED_OVERFLOW
987 static uint32_t HashName(const char* aName
, uint16_t len
) {
988 MOZ_ASSERT(aName
!= 0);
990 const uint8_t* p
= (const uint8_t*)aName
;
991 const uint8_t* endp
= p
+ len
;
994 val
= val
* 37 + *p
++;
997 return (val
% ZIP_TABSIZE
);
1003 * Converts a two byte ugly endianed integer
1004 * to our platform's integer.
1006 static uint16_t xtoint(const uint8_t* ii
) {
1007 return (uint16_t)((ii
[0]) | (ii
[1] << 8));
1013 * Converts a four byte ugly endianed integer
1014 * to our platform's integer.
1016 static uint32_t xtolong(const uint8_t* ll
) {
1017 return (uint32_t)((ll
[0] << 0) | (ll
[1] << 8) | (ll
[2] << 16) |
1024 * returns last modification time in microseconds
1026 static PRTime
GetModTime(uint16_t aDate
, uint16_t aTime
) {
1027 // Note that on DST shift we can't handle correctly the hour that is valid
1028 // in both DST zones
1029 PRExplodedTime time
;
1033 time
.tm_hour
= (aTime
>> 11) & 0x1F;
1034 time
.tm_min
= (aTime
>> 5) & 0x3F;
1035 time
.tm_sec
= (aTime
& 0x1F) * 2;
1037 time
.tm_year
= (aDate
>> 9) + 1980;
1038 time
.tm_month
= ((aDate
>> 5) & 0x0F) - 1;
1039 time
.tm_mday
= aDate
& 0x1F;
1041 time
.tm_params
.tp_gmt_offset
= 0;
1042 time
.tm_params
.tp_dst_offset
= 0;
1044 PR_NormalizeTime(&time
, PR_GMTParameters
);
1045 time
.tm_params
.tp_gmt_offset
= PR_LocalTimeParameters(&time
).tp_gmt_offset
;
1046 PR_NormalizeTime(&time
, PR_GMTParameters
);
1047 time
.tm_params
.tp_dst_offset
= PR_LocalTimeParameters(&time
).tp_dst_offset
;
1049 return PR_ImplodeTime(&time
);
1052 nsZipItem::nsZipItem()
1053 : next(nullptr), central(nullptr), nameLength(0), isSynthetic(false) {}
1055 uint32_t nsZipItem::LocalOffset() { return xtolong(central
->localhdr_offset
); }
1057 uint32_t nsZipItem::Size() { return isSynthetic
? 0 : xtolong(central
->size
); }
1059 uint32_t nsZipItem::RealSize() {
1060 return isSynthetic
? 0 : xtolong(central
->orglen
);
1063 uint32_t nsZipItem::CRC32() {
1064 return isSynthetic
? 0 : xtolong(central
->crc32
);
1067 uint16_t nsZipItem::Date() {
1068 return isSynthetic
? kSyntheticDate
: xtoint(central
->date
);
1071 uint16_t nsZipItem::Time() {
1072 return isSynthetic
? kSyntheticTime
: xtoint(central
->time
);
1075 uint16_t nsZipItem::Compression() {
1076 return isSynthetic
? STORED
: xtoint(central
->method
);
1079 bool nsZipItem::IsDirectory() {
1080 return isSynthetic
|| ((nameLength
> 0) && ('/' == Name()[nameLength
- 1]));
1083 uint16_t nsZipItem::Mode() {
1084 if (isSynthetic
) return 0755;
1085 return ((uint16_t)(central
->external_attributes
[2]) | 0x100);
1088 const uint8_t* nsZipItem::GetExtraField(uint16_t aTag
, uint16_t* aBlockSize
) {
1089 if (isSynthetic
) return nullptr;
1091 const unsigned char* buf
=
1092 ((const unsigned char*)central
) + ZIPCENTRAL_SIZE
+ nameLength
;
1095 MMAP_FAULT_HANDLER_BEGIN_BUFFER(central
, ZIPCENTRAL_SIZE
+ nameLength
)
1096 buflen
= (uint32_t)xtoint(central
->extrafield_len
);
1097 MMAP_FAULT_HANDLER_CATCH(nullptr)
1100 uint16_t tag
, blocksize
;
1102 MMAP_FAULT_HANDLER_BEGIN_BUFFER(buf
, buflen
)
1103 while (buf
&& (pos
+ 4) <= buflen
) {
1104 tag
= xtoint(buf
+ pos
);
1105 blocksize
= xtoint(buf
+ pos
+ 2);
1107 if (aTag
== tag
&& (pos
+ 4 + blocksize
) <= buflen
) {
1108 *aBlockSize
= blocksize
;
1112 pos
+= blocksize
+ 4;
1114 MMAP_FAULT_HANDLER_CATCH(nullptr)
1119 PRTime
nsZipItem::LastModTime() {
1120 if (isSynthetic
) return GetModTime(kSyntheticDate
, kSyntheticTime
);
1122 // Try to read timestamp from extra field
1124 const uint8_t* tsField
= GetExtraField(EXTENDED_TIMESTAMP_FIELD
, &blocksize
);
1125 if (tsField
&& blocksize
>= 5 && tsField
[4] & EXTENDED_TIMESTAMP_MODTIME
) {
1126 return (PRTime
)(xtolong(tsField
+ 5)) * PR_USEC_PER_SEC
;
1129 return GetModTime(Date(), Time());
1132 nsZipCursor::nsZipCursor(nsZipItem
* item
, nsZipArchive
* aZip
, uint8_t* aBuf
,
1133 uint32_t aBufSize
, bool doCRC
)
1140 if (mItem
->Compression() == DEFLATED
) {
1145 NS_ASSERTION(status
== NS_OK
, "Zlib failed to initialize");
1146 NS_ASSERTION(aBuf
, "Must pass in a buffer for DEFLATED nsZipItem");
1149 mZs
.avail_in
= item
->Size();
1150 mZs
.next_in
= (Bytef
*)aZip
->GetData(item
);
1152 if (doCRC
) mCRC
= crc32(0L, Z_NULL
, 0);
1155 nsZipCursor::~nsZipCursor() {
1156 if (mItem
->Compression() == DEFLATED
) {
1161 uint8_t* nsZipCursor::ReadOrCopy(uint32_t* aBytesRead
, bool aCopy
) {
1163 uint8_t* buf
= nullptr;
1164 bool verifyCRC
= true;
1166 if (!mZs
.next_in
) return nullptr;
1167 MMAP_FAULT_HANDLER_BEGIN_BUFFER(mZs
.next_in
, mZs
.avail_in
)
1168 switch (mItem
->Compression()) {
1171 *aBytesRead
= mZs
.avail_in
;
1173 mZs
.next_in
+= mZs
.avail_in
;
1176 *aBytesRead
= mZs
.avail_in
> mBufSize
? mBufSize
: mZs
.avail_in
;
1177 memcpy(mBuf
, mZs
.next_in
, *aBytesRead
);
1178 mZs
.avail_in
-= *aBytesRead
;
1179 mZs
.next_in
+= *aBytesRead
;
1185 mZs
.avail_out
= mBufSize
;
1187 zerr
= inflate(&mZs
, Z_PARTIAL_FLUSH
);
1188 if (zerr
!= Z_OK
&& zerr
!= Z_STREAM_END
) return nullptr;
1190 *aBytesRead
= mZs
.next_out
- buf
;
1191 verifyCRC
= (zerr
== Z_STREAM_END
);
1198 mCRC
= crc32(mCRC
, (const unsigned char*)buf
, *aBytesRead
);
1199 if (verifyCRC
&& mCRC
!= mItem
->CRC32()) return nullptr;
1201 MMAP_FAULT_HANDLER_CATCH(nullptr)
1205 nsZipItemPtr_base::nsZipItemPtr_base(nsZipArchive
* aZip
, const char* aEntryName
,
1207 : mReturnBuf(nullptr), mReadlen(0) {
1208 // make sure the ziparchive hangs around
1209 mZipHandle
= aZip
->GetFD();
1211 nsZipItem
* item
= aZip
->GetItem(aEntryName
);
1215 bool compressed
= (item
->Compression() == DEFLATED
);
1217 size
= item
->RealSize();
1218 mAutoBuf
= MakeUniqueFallible
<uint8_t[]>(size
);
1224 nsZipCursor
cursor(item
, aZip
, mAutoBuf
.get(), size
, doCRC
);
1225 mReturnBuf
= cursor
.Read(&mReadlen
);
1230 if (mReadlen
!= item
->RealSize()) {
1231 NS_ASSERTION(mReadlen
== item
->RealSize(), "nsZipCursor underflow");
1232 mReturnBuf
= nullptr;