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.
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
16 #include "brotli/decode.h" // brotli
18 #include "nsISupportsUtils.h"
21 #include "mozilla/Attributes.h"
22 #include "mozilla/Logging.h"
23 #include "mozilla/UniquePtrExtensions.h"
25 #include "nsWildCard.h"
26 #include "nsZipArchive.h"
33 // For placement new used for arena allocations of zip file list
35 #define ZIP_ARENABLOCKSIZE (1*1024)
39 #include <sys/types.h>
48 #include <sys/syslimits.h>
49 #endif /*__SYMBIAN32__*/
52 #ifndef XP_UNIX /* we need some constants defined in limits.h and unistd.h */
54 # define S_IFMT 0170000
57 # define S_IFLNK 0120000
60 # define PATH_MAX 1024
65 #include "private/pprio.h" // To get PR_ImportFile
68 using namespace mozilla
;
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));
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
);
79 class ZipArchiveLogger
{
81 void Write(const nsACString
&zip
, const char *entry
) const {
83 char *env
= PR_GetEnv("MOZ_JAR_LOG_FILE");
87 nsCOMPtr
<nsIFile
> logFile
;
88 nsresult rv
= NS_NewLocalFile(NS_ConvertUTF8toUTF16(env
), false, getter_AddRefs(logFile
));
92 // Create the log file and its parent directory (in case it doesn't exist)
93 rv
= logFile
->Create(nsIFile::NORMAL_FILE_TYPE
, 0644);
99 // PR_APPEND is racy on Windows, so open a handle ourselves with flags that
100 // will work, and use PR_ImportFile to make it a PRFileDesc.
101 // This can go away when bug 840435 is fixed.
103 logFile
->GetPath(path
);
106 HANDLE handle
= CreateFileW(path
.get(), FILE_APPEND_DATA
, FILE_SHARE_WRITE
,
107 nullptr, OPEN_ALWAYS
, 0, nullptr);
108 if (handle
== INVALID_HANDLE_VALUE
)
110 file
= PR_ImportFile((PROsfd
)handle
);
114 rv
= logFile
->OpenNSPRFileDesc(PR_WRONLY
|PR_CREATE_FILE
|PR_APPEND
, 0644, &file
);
124 PR_Write(fd
, buf
.get(), buf
.Length());
128 MOZ_ASSERT(refCnt
>= 0);
133 MOZ_ASSERT(refCnt
> 0);
134 if ((0 == --refCnt
) && fd
) {
141 mutable PRFileDesc
*fd
;
144 static ZipArchiveLogger zipLog
;
146 //***********************************************************
147 // For every inflation the following allocations are done:
150 //***********************************************************
152 nsresult
gZlibInit(z_stream
*zs
)
154 memset(zs
, 0, sizeof(z_stream
));
155 int zerr
= inflateInit2(zs
, -MAX_WBITS
);
156 if (zerr
!= Z_OK
) return NS_ERROR_OUT_OF_MEMORY
;
161 nsZipHandle::nsZipHandle()
166 , mFileStart(nullptr)
171 NS_IMPL_ADDREF(nsZipHandle
)
172 NS_IMPL_RELEASE(nsZipHandle
)
174 nsresult
nsZipHandle::Init(nsIFile
*file
, nsZipHandle
**ret
,
177 mozilla::AutoFDClose fd
;
178 int32_t flags
= PR_RDONLY
;
180 flags
|= nsIFile::OS_READAHEAD
;
182 nsresult rv
= file
->OpenNSPRFileDesc(flags
, 0000, &fd
.rwget());
186 int64_t size
= PR_Available64(fd
);
187 if (size
>= INT32_MAX
)
188 return NS_ERROR_FILE_TOO_BIG
;
190 PRFileMap
*map
= PR_CreateFileMap(fd
, size
, PR_PROT_READONLY
);
192 return NS_ERROR_FAILURE
;
194 uint8_t *buf
= (uint8_t*) PR_MemMap(map
, 0, (uint32_t) size
);
195 // Bug 525755: PR_MemMap fails when fd points at something other than a normal file.
197 PR_CloseFileMap(map
);
198 return NS_ERROR_FAILURE
;
201 RefPtr
<nsZipHandle
> handle
= new nsZipHandle();
203 PR_MemUnmap(buf
, (uint32_t) size
);
204 PR_CloseFileMap(map
);
205 return NS_ERROR_OUT_OF_MEMORY
;
213 handle
->mNSPRFileDesc
= fd
.forget();
216 handle
->mFile
.Init(file
);
217 handle
->mTotalLen
= (uint32_t) size
;
218 handle
->mFileStart
= buf
;
219 rv
= handle
->findDataStart();
221 PR_MemUnmap(buf
, (uint32_t) size
);
222 PR_CloseFileMap(map
);
229 nsresult
nsZipHandle::Init(nsZipArchive
*zip
, const char *entry
,
232 RefPtr
<nsZipHandle
> handle
= new nsZipHandle();
234 return NS_ERROR_OUT_OF_MEMORY
;
236 handle
->mBuf
= new nsZipItemPtr
<uint8_t>(zip
, entry
);
238 return NS_ERROR_OUT_OF_MEMORY
;
240 if (!handle
->mBuf
->Buffer())
241 return NS_ERROR_UNEXPECTED
;
243 handle
->mMap
= nullptr;
244 handle
->mFile
.Init(zip
, entry
);
245 handle
->mTotalLen
= handle
->mBuf
->Length();
246 handle
->mFileStart
= handle
->mBuf
->Buffer();
247 nsresult rv
= handle
->findDataStart();
255 nsresult
nsZipHandle::Init(const uint8_t* aData
, uint32_t aLen
,
258 RefPtr
<nsZipHandle
> handle
= new nsZipHandle();
260 handle
->mFileStart
= aData
;
261 handle
->mTotalLen
= aLen
;
262 nsresult rv
= handle
->findDataStart();
270 // This function finds the start of the ZIP data. If the file is a regular ZIP,
271 // this is just the start of the file. If the file is a CRX file, the start of
272 // the data is after the CRX header.
273 // CRX header reference: (CRX version 2)
274 // Header requires little-endian byte ordering with 4-byte alignment.
275 // 32 bits : magicNumber - Defined as a |char m[] = "Cr24"|.
276 // Equivilant to |uint32_t m = 0x34327243|.
277 // 32 bits : version - Unsigned integer representing the CRX file
278 // format version. Currently equal to 2.
279 // 32 bits : pubKeyLength - Unsigned integer representing the length
280 // of the public key in bytes.
281 // 32 bits : sigLength - Unsigned integer representing the length
282 // of the signature in bytes.
283 // pubKeyLength : publicKey - Contents of the author's public key.
284 // sigLength : signature - Signature of the ZIP content.
285 // Signature is created using the RSA
286 // algorithm with the SHA-1 hash function.
287 nsresult
nsZipHandle::findDataStart()
289 // In the CRX header, integers are 32 bits. Our pointer to the file is of
290 // type |uint8_t|, which is guaranteed to be 8 bits.
291 const uint32_t CRXIntSize
= 4;
293 MOZ_WIN_MEM_TRY_BEGIN
294 if (mTotalLen
> CRXIntSize
* 4 && xtolong(mFileStart
) == kCRXMagic
) {
295 const uint8_t* headerData
= mFileStart
;
296 headerData
+= CRXIntSize
* 2; // Skip magic number and version number
297 uint32_t pubKeyLength
= xtolong(headerData
);
298 headerData
+= CRXIntSize
;
299 uint32_t sigLength
= xtolong(headerData
);
300 uint32_t headerSize
= CRXIntSize
* 4 + pubKeyLength
+ sigLength
;
301 if (mTotalLen
> headerSize
) {
302 mLen
= mTotalLen
- headerSize
;
303 mFileData
= mFileStart
+ headerSize
;
308 mFileData
= mFileStart
;
309 MOZ_WIN_MEM_TRY_CATCH(return NS_ERROR_FAILURE
)
313 int64_t nsZipHandle::SizeOfMapping()
318 nsresult
nsZipHandle::GetNSPRFileDesc(PRFileDesc
** aNSPRFileDesc
)
320 if (!aNSPRFileDesc
) {
321 return NS_ERROR_ILLEGAL_VALUE
;
324 *aNSPRFileDesc
= mNSPRFileDesc
;
325 if (!mNSPRFileDesc
) {
326 return NS_ERROR_NOT_AVAILABLE
;
332 nsZipHandle::~nsZipHandle()
335 PR_MemUnmap((void *)mFileStart
, mTotalLen
);
336 PR_CloseFileMap(mMap
);
338 mFileStart
= nullptr;
344 //***********************************************************
345 // nsZipArchive -- public methods
346 //***********************************************************
348 //---------------------------------------------
349 // nsZipArchive::OpenArchive
350 //---------------------------------------------
351 nsresult
nsZipArchive::OpenArchive(nsZipHandle
*aZipHandle
, PRFileDesc
*aFd
)
355 //-- get table of contents for archive
356 nsresult rv
= BuildFileList(aFd
);
357 if (NS_SUCCEEDED(rv
)) {
358 if (aZipHandle
->mFile
)
359 aZipHandle
->mFile
.GetURIString(mURI
);
364 nsresult
nsZipArchive::OpenArchive(nsIFile
*aFile
)
366 RefPtr
<nsZipHandle
> handle
;
368 mozilla::AutoFDClose fd
;
369 nsresult rv
= nsZipHandle::Init(aFile
, getter_AddRefs(handle
),
372 nsresult rv
= nsZipHandle::Init(aFile
, getter_AddRefs(handle
));
378 return OpenArchive(handle
, fd
.get());
380 return OpenArchive(handle
);
384 //---------------------------------------------
385 // nsZipArchive::Test
386 //---------------------------------------------
387 nsresult
nsZipArchive::Test(const char *aEntryName
)
391 if (aEntryName
) // only test specified item
393 currItem
= GetItem(aEntryName
);
395 return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST
;
396 //-- don't test (synthetic) directory items
397 if (currItem
->IsDirectory())
399 return ExtractFile(currItem
, 0, 0);
402 // test all items in archive
403 for (auto* item
: mFiles
) {
404 for (currItem
= item
; currItem
; currItem
= currItem
->next
) {
405 //-- don't test (synthetic) directory items
406 if (currItem
->IsDirectory())
408 nsresult rv
= ExtractFile(currItem
, 0, 0);
417 //---------------------------------------------
418 // nsZipArchive::CloseArchive
419 //---------------------------------------------
420 nsresult
nsZipArchive::CloseArchive()
428 // We don't need to delete each of the nsZipItem as the memory for
429 // the zip item and the filename it holds are both allocated from the Arena.
430 // Hence, destroying the Arena is like destroying all the memory
431 // for all the nsZipItem in one shot. But if the ~nsZipItem is doing
432 // anything more than cleaning up memory, we should start calling it.
433 // Let us also cleanup the mFiles table for re-use on the next 'open' call
434 memset(mFiles
, 0, sizeof(mFiles
));
435 mBuiltSynthetics
= false;
439 //---------------------------------------------
440 // nsZipArchive::GetItem
441 //---------------------------------------------
442 nsZipItem
* nsZipArchive::GetItem(const char * aEntryName
)
445 uint32_t len
= strlen(aEntryName
);
446 //-- If the request is for a directory, make sure that synthetic entries
447 //-- are created for the directories without their own entry.
448 if (!mBuiltSynthetics
) {
449 if ((len
> 0) && (aEntryName
[len
-1] == '/')) {
450 if (BuildSynthetics() != NS_OK
)
454 MOZ_WIN_MEM_TRY_BEGIN
455 nsZipItem
* item
= mFiles
[ HashName(aEntryName
, len
) ];
457 if ((len
== item
->nameLength
) &&
458 (!memcmp(aEntryName
, item
->Name(), len
))) {
460 // Successful GetItem() is a good indicator that the file is about to be read
461 zipLog
.Write(mURI
, aEntryName
);
462 return item
; //-- found it
466 MOZ_WIN_MEM_TRY_CATCH(return nullptr)
471 //---------------------------------------------
472 // nsZipArchive::ExtractFile
473 // This extracts the item to the filehandle provided.
474 // If 'aFd' is null, it only tests the extraction.
475 // On extraction error(s) it removes the file.
476 //---------------------------------------------
477 nsresult
nsZipArchive::ExtractFile(nsZipItem
*item
, nsIFile
* outFile
,
481 return NS_ERROR_ILLEGAL_VALUE
;
483 return NS_ERROR_FAILURE
;
485 // Directory extraction is handled in nsJAR::Extract,
486 // so the item to be extracted should never be a directory
487 MOZ_ASSERT(!item
->IsDirectory());
489 Bytef outbuf
[ZIP_BUFLEN
];
491 nsZipCursor
cursor(item
, this, outbuf
, ZIP_BUFLEN
, true);
497 uint8_t* buf
= cursor
.Read(&count
);
499 nsZipArchive::sFileCorruptedReason
= "nsZipArchive: Read() failed to return a buffer";
500 rv
= NS_ERROR_FILE_CORRUPTED
;
507 if (aFd
&& PR_Write(aFd
, buf
, count
) < (READTYPE
)count
) {
508 rv
= NS_ERROR_FILE_DISK_FULL
;
513 //-- delete the file on errors
516 if (NS_FAILED(rv
) && outFile
) {
517 outFile
->Remove(false);
524 //---------------------------------------------
525 // nsZipArchive::FindInit
526 //---------------------------------------------
528 nsZipArchive::FindInit(const char * aPattern
, nsZipFind
**aFind
)
531 return NS_ERROR_ILLEGAL_VALUE
;
533 // null out param in case an error happens
539 // Create synthetic directory entries on demand
540 nsresult rv
= BuildSynthetics();
544 // validate the pattern
547 switch (NS_WildCardValid((char*)aPattern
))
550 return NS_ERROR_ILLEGAL_VALUE
;
561 // undocumented return value from RegExpValid!
563 return NS_ERROR_ILLEGAL_VALUE
;
566 pattern
= PL_strdup(aPattern
);
568 return NS_ERROR_OUT_OF_MEMORY
;
571 *aFind
= new nsZipFind(this, pattern
, regExp
);
574 return NS_ERROR_OUT_OF_MEMORY
;
582 //---------------------------------------------
583 // nsZipFind::FindNext
584 //---------------------------------------------
585 nsresult
nsZipFind::FindNext(const char ** aResult
, uint16_t *aNameLen
)
587 if (!mArchive
|| !aResult
|| !aNameLen
)
588 return NS_ERROR_ILLEGAL_VALUE
;
592 MOZ_WIN_MEM_TRY_BEGIN
593 // we start from last match, look for next
594 while (mSlot
< ZIP_TABSIZE
)
596 // move to next in current chain, or move to new slot
597 mItem
= mItem
? mItem
->next
: mArchive
->mFiles
[mSlot
];
601 ++mSlot
; // no more in this chain, move to next slot
603 found
= true; // always match
606 char buf
[kMaxNameLength
+1];
607 memcpy(buf
, mItem
->Name(), mItem
->nameLength
);
608 buf
[mItem
->nameLength
]='\0';
609 found
= (NS_WildCardMatch(buf
, mPattern
, false) == MATCH
);
612 found
= ((mItem
->nameLength
== strlen(mPattern
)) &&
613 (memcmp(mItem
->Name(), mPattern
, mItem
->nameLength
) == 0));
615 // Need also to return the name length, as it is NOT zero-terminatdd...
616 *aResult
= mItem
->Name();
617 *aNameLen
= mItem
->nameLength
;
621 MOZ_WIN_MEM_TRY_CATCH(return NS_ERROR_FAILURE
)
622 return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST
;
625 //***********************************************************
626 // nsZipArchive -- private implementation
627 //***********************************************************
629 //---------------------------------------------
630 // nsZipArchive::CreateZipItem
631 //---------------------------------------------
632 nsZipItem
* nsZipArchive::CreateZipItem()
634 // Arena allocate the nsZipItem
635 return (nsZipItem
*)mArena
.Allocate(sizeof(nsZipItem
));
638 //---------------------------------------------
639 // nsZipArchive::BuildFileList
640 //---------------------------------------------
641 nsresult
nsZipArchive::BuildFileList(PRFileDesc
*aFd
)
643 // Get archive size using end pos
645 const uint8_t* startp
= mFd
->mFileData
;
646 const uint8_t* endp
= startp
+ mFd
->mLen
;
647 MOZ_WIN_MEM_TRY_BEGIN
648 uint32_t centralOffset
= 4;
649 if (mFd
->mLen
> ZIPCENTRAL_SIZE
&& xtolong(startp
+ centralOffset
) == CENTRALSIG
) {
650 // Success means optimized jar layout from bug 559961 is in effect
651 uint32_t readaheadLength
= xtolong(startp
);
652 if (readaheadLength
) {
653 #if defined(XP_SOLARIS)
654 posix_madvise(const_cast<uint8_t*>(startp
), readaheadLength
, POSIX_MADV_WILLNEED
);
655 #elif defined(XP_UNIX)
656 madvise(const_cast<uint8_t*>(startp
), readaheadLength
, MADV_WILLNEED
);
657 #elif defined(XP_WIN)
659 HANDLE hFile
= (HANDLE
) PR_FileDesc2NativeHandle(aFd
);
660 mozilla::ReadAhead(hFile
, 0, readaheadLength
);
665 for (buf
= endp
- ZIPEND_SIZE
; buf
> startp
; buf
--)
667 if (xtolong(buf
) == ENDSIG
) {
668 centralOffset
= xtolong(((ZipEnd
*)buf
)->offset_central_dir
);
674 if (!centralOffset
) {
675 nsZipArchive::sFileCorruptedReason
= "nsZipArchive: no central offset";
676 return NS_ERROR_FILE_CORRUPTED
;
679 buf
= startp
+ centralOffset
;
681 // avoid overflow of startp + centralOffset.
683 nsZipArchive::sFileCorruptedReason
= "nsZipArchive: overflow looking for central directory";
684 return NS_ERROR_FILE_CORRUPTED
;
687 //-- Read the central directory headers
689 while ((buf
+ int32_t(sizeof(uint32_t)) > buf
) &&
690 (buf
+ int32_t(sizeof(uint32_t)) <= endp
) &&
691 ((sig
= xtolong(buf
)) == CENTRALSIG
)) {
692 // Make sure there is enough data available.
693 if ((buf
> endp
) || (endp
- buf
< ZIPCENTRAL_SIZE
)) {
694 nsZipArchive::sFileCorruptedReason
= "nsZipArchive: central directory too small";
695 return NS_ERROR_FILE_CORRUPTED
;
698 // Read the fixed-size data.
699 ZipCentral
* central
= (ZipCentral
*)buf
;
701 uint16_t namelen
= xtoint(central
->filename_len
);
702 uint16_t extralen
= xtoint(central
->extrafield_len
);
703 uint16_t commentlen
= xtoint(central
->commentfield_len
);
704 uint32_t diff
= ZIPCENTRAL_SIZE
+ namelen
+ extralen
+ commentlen
;
706 // Sanity check variable sizes and refuse to deal with
707 // anything too big: it's likely a corrupt archive.
709 namelen
> kMaxNameLength
) {
710 nsZipArchive::sFileCorruptedReason
= "nsZipArchive: namelen out of range";
711 return NS_ERROR_FILE_CORRUPTED
;
713 if (buf
>= buf
+ diff
|| // No overflow
714 buf
>= endp
- diff
) {
715 nsZipArchive::sFileCorruptedReason
= "nsZipArchive: overflow looking for next item";
716 return NS_ERROR_FILE_CORRUPTED
;
719 // Point to the next item at the top of loop
722 nsZipItem
* item
= CreateZipItem();
724 return NS_ERROR_OUT_OF_MEMORY
;
726 item
->central
= central
;
727 item
->nameLength
= namelen
;
728 item
->isSynthetic
= false;
730 // Add item to file table
731 uint32_t hash
= HashName(item
->Name(), namelen
);
732 item
->next
= mFiles
[hash
];
736 } /* while reading central directory records */
739 nsZipArchive::sFileCorruptedReason
= "nsZipArchive: unexpected sig";
740 return NS_ERROR_FILE_CORRUPTED
;
743 // Make the comment available for consumers.
744 if ((endp
>= buf
) && (endp
- buf
>= ZIPEND_SIZE
)) {
745 ZipEnd
*zipend
= (ZipEnd
*)buf
;
748 uint16_t commentlen
= xtoint(zipend
->commentfield_len
);
749 if (endp
- buf
>= commentlen
) {
750 mCommentPtr
= (const char *)buf
;
751 mCommentLen
= commentlen
;
755 MOZ_WIN_MEM_TRY_CATCH(return NS_ERROR_FAILURE
)
759 //---------------------------------------------
760 // nsZipArchive::BuildSynthetics
761 //---------------------------------------------
762 nsresult
nsZipArchive::BuildSynthetics()
764 if (mBuiltSynthetics
)
766 mBuiltSynthetics
= true;
768 MOZ_WIN_MEM_TRY_BEGIN
769 // Create synthetic entries for any missing directories.
770 // Do this when all ziptable has scanned to prevent double entries.
771 for (auto* item
: mFiles
)
773 for (; item
!= nullptr; item
= item
->next
)
775 if (item
->isSynthetic
)
778 //-- add entries for directories in the current item's path
779 //-- go from end to beginning, because then we can stop trying
780 //-- to create diritems if we find that the diritem we want to
781 //-- create already exists
782 //-- start just before the last char so as to not add the item
783 //-- twice if it's a directory
784 uint16_t namelen
= item
->nameLength
;
785 MOZ_ASSERT(namelen
> 0, "Attempt to build synthetic for zero-length entry name!");
786 const char *name
= item
->Name();
787 for (uint16_t dirlen
= namelen
- 1; dirlen
> 0; dirlen
--)
789 if (name
[dirlen
-1] != '/')
792 // The character before this is '/', so if this is also '/' then we
793 // have an empty path component. Skip it.
794 if (name
[dirlen
] == '/')
797 // Is the directory already in the file table?
798 uint32_t hash
= HashName(item
->Name(), dirlen
);
800 for (nsZipItem
* zi
= mFiles
[hash
]; zi
!= nullptr; zi
= zi
->next
)
802 if ((dirlen
== zi
->nameLength
) &&
803 (0 == memcmp(item
->Name(), zi
->Name(), dirlen
)))
805 // we've already added this dir and all its parents
810 // if the directory was found, break out of the directory
811 // creation loop now that we know all implicit directories
812 // are there -- otherwise, start creating the zip item
816 nsZipItem
* diritem
= CreateZipItem();
818 return NS_ERROR_OUT_OF_MEMORY
;
820 // Point to the central record of the original item for the name part.
821 diritem
->central
= item
->central
;
822 diritem
->nameLength
= dirlen
;
823 diritem
->isSynthetic
= true;
825 // add diritem to the file table
826 diritem
->next
= mFiles
[hash
];
827 mFiles
[hash
] = diritem
;
828 } /* end processing of dirs in item's name */
831 MOZ_WIN_MEM_TRY_CATCH(return NS_ERROR_FAILURE
)
835 nsZipHandle
* nsZipArchive::GetFD()
842 //---------------------------------------------
843 // nsZipArchive::GetDataOffset
844 //---------------------------------------------
845 uint32_t nsZipArchive::GetDataOffset(nsZipItem
* aItem
)
848 MOZ_WIN_MEM_TRY_BEGIN
849 //-- read local header to get variable length values and calculate
850 //-- the real data offset
851 uint32_t len
= mFd
->mLen
;
852 const uint8_t* data
= mFd
->mFileData
;
853 uint32_t offset
= aItem
->LocalOffset();
854 if (len
< ZIPLOCAL_SIZE
|| offset
> len
- ZIPLOCAL_SIZE
)
857 // -- check signature before using the structure, in case the zip file is corrupt
858 ZipLocal
* Local
= (ZipLocal
*)(data
+ offset
);
859 if ((xtolong(Local
->signature
) != LOCALSIG
))
862 //-- NOTE: extralen is different in central header and local header
863 //-- for archives created using the Unix "zip" utility. To set
864 //-- the offset accurately we need the _local_ extralen.
865 offset
+= ZIPLOCAL_SIZE
+
866 xtoint(Local
->filename_len
) +
867 xtoint(Local
->extrafield_len
);
870 MOZ_WIN_MEM_TRY_CATCH(return 0)
873 //---------------------------------------------
874 // nsZipArchive::GetData
875 //---------------------------------------------
876 const uint8_t* nsZipArchive::GetData(nsZipItem
* aItem
)
879 MOZ_WIN_MEM_TRY_BEGIN
880 uint32_t offset
= GetDataOffset(aItem
);
882 // -- check if there is enough source data in the file
884 mFd
->mLen
< aItem
->Size() ||
885 offset
> mFd
->mLen
- aItem
->Size() ||
886 (aItem
->Compression() == STORED
&& aItem
->Size() != aItem
->RealSize())) {
890 return mFd
->mFileData
+ offset
;
891 MOZ_WIN_MEM_TRY_CATCH(return nullptr)
894 // nsZipArchive::GetComment
895 bool nsZipArchive::GetComment(nsACString
&aComment
)
897 MOZ_WIN_MEM_TRY_BEGIN
898 aComment
.Assign(mCommentPtr
, mCommentLen
);
899 MOZ_WIN_MEM_TRY_CATCH(return false)
903 //---------------------------------------------
904 // nsZipArchive::SizeOfMapping
905 //---------------------------------------------
906 int64_t nsZipArchive::SizeOfMapping()
908 return mFd
? mFd
->SizeOfMapping() : 0;
911 //------------------------------------------
912 // nsZipArchive constructor and destructor
913 //------------------------------------------
915 nsZipArchive::nsZipArchive()
917 , mCommentPtr(nullptr)
919 , mBuiltSynthetics(false)
923 // initialize the table to nullptr
924 memset(mFiles
, 0, sizeof(mFiles
));
927 NS_IMPL_ADDREF(nsZipArchive
)
928 NS_IMPL_RELEASE(nsZipArchive
)
930 nsZipArchive::~nsZipArchive()
938 //------------------------------------------
939 // nsZipFind constructor and destructor
940 //------------------------------------------
942 nsZipFind::nsZipFind(nsZipArchive
* aZip
, char* aPattern
, bool aRegExp
)
949 MOZ_COUNT_CTOR(nsZipFind
);
952 nsZipFind::~nsZipFind()
954 PL_strfree(mPattern
);
956 MOZ_COUNT_DTOR(nsZipFind
);
959 //------------------------------------------
961 //------------------------------------------
966 * returns a hash key for the entry name
968 MOZ_NO_SANITIZE_UNSIGNED_OVERFLOW
969 static uint32_t HashName(const char* aName
, uint16_t len
)
971 MOZ_ASSERT(aName
!= 0);
973 const uint8_t* p
= (const uint8_t*)aName
;
974 const uint8_t* endp
= p
+ len
;
980 return (val
% ZIP_TABSIZE
);
986 * Converts a two byte ugly endianed integer
987 * to our platform's integer.
989 static uint16_t xtoint (const uint8_t *ii
)
991 return (uint16_t) ((ii
[0]) | (ii
[1] << 8));
997 * Converts a four byte ugly endianed integer
998 * to our platform's integer.
1000 static uint32_t xtolong (const uint8_t *ll
)
1002 return (uint32_t)( (ll
[0] << 0) |
1011 * returns last modification time in microseconds
1013 static PRTime
GetModTime(uint16_t aDate
, uint16_t aTime
)
1015 // Note that on DST shift we can't handle correctly the hour that is valid
1016 // in both DST zones
1017 PRExplodedTime time
;
1021 time
.tm_hour
= (aTime
>> 11) & 0x1F;
1022 time
.tm_min
= (aTime
>> 5) & 0x3F;
1023 time
.tm_sec
= (aTime
& 0x1F) * 2;
1025 time
.tm_year
= (aDate
>> 9) + 1980;
1026 time
.tm_month
= ((aDate
>> 5) & 0x0F) - 1;
1027 time
.tm_mday
= aDate
& 0x1F;
1029 time
.tm_params
.tp_gmt_offset
= 0;
1030 time
.tm_params
.tp_dst_offset
= 0;
1032 PR_NormalizeTime(&time
, PR_GMTParameters
);
1033 time
.tm_params
.tp_gmt_offset
= PR_LocalTimeParameters(&time
).tp_gmt_offset
;
1034 PR_NormalizeTime(&time
, PR_GMTParameters
);
1035 time
.tm_params
.tp_dst_offset
= PR_LocalTimeParameters(&time
).tp_dst_offset
;
1037 return PR_ImplodeTime(&time
);
1040 nsZipItem::nsZipItem()
1044 , isSynthetic(false)
1047 uint32_t nsZipItem::LocalOffset()
1049 return xtolong(central
->localhdr_offset
);
1052 uint32_t nsZipItem::Size()
1054 return isSynthetic
? 0 : xtolong(central
->size
);
1057 uint32_t nsZipItem::RealSize()
1059 return isSynthetic
? 0 : xtolong(central
->orglen
);
1062 uint32_t nsZipItem::CRC32()
1064 return isSynthetic
? 0 : xtolong(central
->crc32
);
1067 uint16_t nsZipItem::Date()
1069 return isSynthetic
? kSyntheticDate
: xtoint(central
->date
);
1072 uint16_t nsZipItem::Time()
1074 return isSynthetic
? kSyntheticTime
: xtoint(central
->time
);
1077 uint16_t nsZipItem::Compression()
1079 return isSynthetic
? STORED
: xtoint(central
->method
);
1082 bool nsZipItem::IsDirectory()
1084 return isSynthetic
|| ((nameLength
> 0) && ('/' == Name()[nameLength
- 1]));
1087 uint16_t nsZipItem::Mode()
1089 if (isSynthetic
) return 0755;
1090 return ((uint16_t)(central
->external_attributes
[2]) | 0x100);
1093 const uint8_t * nsZipItem::GetExtraField(uint16_t aTag
, uint16_t *aBlockSize
)
1095 if (isSynthetic
) return nullptr;
1096 MOZ_WIN_MEM_TRY_BEGIN
1097 const unsigned char *buf
= ((const unsigned char*)central
) + ZIPCENTRAL_SIZE
+
1099 uint32_t buflen
= (uint32_t)xtoint(central
->extrafield_len
);
1101 uint16_t tag
, blocksize
;
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;
1115 MOZ_WIN_MEM_TRY_CATCH(return nullptr)
1120 PRTime
nsZipItem::LastModTime()
1122 if (isSynthetic
) return GetModTime(kSyntheticDate
, kSyntheticTime
);
1124 // Try to read timestamp from extra field
1126 const uint8_t *tsField
= GetExtraField(EXTENDED_TIMESTAMP_FIELD
, &blocksize
);
1127 if (tsField
&& blocksize
>= 5 && tsField
[4] & EXTENDED_TIMESTAMP_MODTIME
) {
1128 return (PRTime
)(xtolong(tsField
+ 5)) * PR_USEC_PER_SEC
;
1131 return GetModTime(Date(), Time());
1134 nsZipCursor::nsZipCursor(nsZipItem
*item
, nsZipArchive
*aZip
, uint8_t* aBuf
,
1135 uint32_t aBufSize
, bool doCRC
)
1138 , mBufSize(aBufSize
)
1140 #ifdef MOZ_JAR_BROTLI
1141 , mBrotliState(nullptr)
1146 if (mItem
->Compression() == DEFLATED
) {
1151 NS_ASSERTION(status
== NS_OK
, "Zlib failed to initialize");
1152 NS_ASSERTION(aBuf
, "Must pass in a buffer for DEFLATED nsZipItem");
1155 mZs
.avail_in
= item
->Size();
1156 mZs
.next_in
= (Bytef
*)aZip
->GetData(item
);
1158 #ifdef MOZ_JAR_BROTLI
1159 if (mItem
->Compression() == MOZ_JAR_BROTLI
) {
1160 mBrotliState
= BrotliDecoderCreateInstance(nullptr, nullptr, nullptr);
1165 mCRC
= crc32(0L, Z_NULL
, 0);
1168 nsZipCursor::~nsZipCursor()
1170 if (mItem
->Compression() == DEFLATED
) {
1173 #ifdef MOZ_JAR_BROTLI
1174 if (mItem
->Compression() == MOZ_JAR_BROTLI
) {
1175 BrotliDecoderDestroyInstance(mBrotliState
);
1180 uint8_t* nsZipCursor::ReadOrCopy(uint32_t *aBytesRead
, bool aCopy
) {
1182 uint8_t *buf
= nullptr;
1183 bool verifyCRC
= true;
1187 MOZ_WIN_MEM_TRY_BEGIN
1188 switch (mItem
->Compression()) {
1191 *aBytesRead
= mZs
.avail_in
;
1193 mZs
.next_in
+= mZs
.avail_in
;
1196 *aBytesRead
= mZs
.avail_in
> mBufSize
? mBufSize
: mZs
.avail_in
;
1197 memcpy(mBuf
, mZs
.next_in
, *aBytesRead
);
1198 mZs
.avail_in
-= *aBytesRead
;
1199 mZs
.next_in
+= *aBytesRead
;
1205 mZs
.avail_out
= mBufSize
;
1207 zerr
= inflate(&mZs
, Z_PARTIAL_FLUSH
);
1208 if (zerr
!= Z_OK
&& zerr
!= Z_STREAM_END
)
1211 *aBytesRead
= mZs
.next_out
- buf
;
1212 verifyCRC
= (zerr
== Z_STREAM_END
);
1214 #ifdef MOZ_JAR_BROTLI
1215 case MOZ_JAR_BROTLI
: {
1218 /* The brotli library wants size_t, but z_stream only contains
1219 * unsigned int for avail_*. So use temporary stack values. */
1220 size_t avail_out
= mBufSize
;
1221 size_t avail_in
= mZs
.avail_in
;
1222 BrotliDecoderResult result
= BrotliDecoderDecompressStream(
1224 &avail_in
, const_cast<const unsigned char**>(&mZs
.next_in
),
1225 &avail_out
, &mZs
.next_out
, nullptr);
1226 /* We don't need to update avail_out, it's not used outside this
1228 mZs
.avail_in
= avail_in
;
1230 if (result
== BROTLI_DECODER_RESULT_ERROR
) {
1234 *aBytesRead
= mZs
.next_out
- buf
;
1235 verifyCRC
= (result
== BROTLI_DECODER_RESULT_SUCCESS
);
1244 mCRC
= crc32(mCRC
, (const unsigned char*)buf
, *aBytesRead
);
1245 if (verifyCRC
&& mCRC
!= mItem
->CRC32())
1248 MOZ_WIN_MEM_TRY_CATCH(return nullptr)
1252 nsZipItemPtr_base::nsZipItemPtr_base(nsZipArchive
*aZip
,
1253 const char * aEntryName
, bool doCRC
)
1254 : mReturnBuf(nullptr)
1257 // make sure the ziparchive hangs around
1258 mZipHandle
= aZip
->GetFD();
1260 nsZipItem
* item
= aZip
->GetItem(aEntryName
);
1265 bool compressed
= (item
->Compression() == DEFLATED
);
1266 #ifdef MOZ_JAR_BROTLI
1267 compressed
|= (item
->Compression() == MOZ_JAR_BROTLI
);
1270 size
= item
->RealSize();
1271 mAutoBuf
= MakeUniqueFallible
<uint8_t[]>(size
);
1277 nsZipCursor
cursor(item
, aZip
, mAutoBuf
.get(), size
, doCRC
);
1278 mReturnBuf
= cursor
.Read(&mReadlen
);
1283 if (mReadlen
!= item
->RealSize()) {
1284 NS_ASSERTION(mReadlen
== item
->RealSize(), "nsZipCursor underflow");
1285 mReturnBuf
= nullptr;
1290 /* static */ const char*
1291 nsZipArchive::sFileCorruptedReason
= nullptr;