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
15 #include "nsISupportsUtils.h"
20 #include "nsWildCard.h"
21 #include "nsZipArchive.h"
28 // For placement new used for arena allocations of zip file list
30 #define ZIP_ARENABLOCKSIZE (1*1024)
34 #include <sys/types.h>
38 #elif defined(XP_WIN) || defined(XP_OS2)
43 #include <sys/syslimits.h>
44 #endif /*__SYMBIAN32__*/
47 #ifndef XP_UNIX /* we need some constants defined in limits.h and unistd.h */
49 # define S_IFMT 0170000
52 # define S_IFLNK 0120000
55 # define PATH_MAX 1024
60 #include "private/pprio.h" // To get PR_ImportFile
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
);
74 static nsresult
ResolveSymlink(const char *path
);
77 class ZipArchiveLogger
{
79 void Write(const nsACString
&zip
, const char *entry
) const {
81 char *env
= PR_GetEnv("MOZ_JAR_LOG_FILE");
85 nsCOMPtr
<nsIFile
> logFile
;
86 nsresult rv
= NS_NewLocalFile(NS_ConvertUTF8toUTF16(env
), false, getter_AddRefs(logFile
));
90 // Create the log file and its parent directory (in case it doesn't exist)
91 logFile
->Create(nsIFile::NORMAL_FILE_TYPE
, 0644);
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.
99 logFile
->GetPath(path
);
102 HANDLE handle
= CreateFileW(path
.get(), FILE_APPEND_DATA
, FILE_SHARE_WRITE
,
103 NULL
, OPEN_ALWAYS
, 0, NULL
);
104 if (handle
== INVALID_HANDLE_VALUE
)
106 file
= PR_ImportFile((PROsfd
)handle
);
110 rv
= logFile
->OpenNSPRFileDesc(PR_WRONLY
|PR_CREATE_FILE
|PR_APPEND
, 0644, &file
);
120 PR_Write(fd
, buf
.get(), buf
.Length());
124 MOZ_ASSERT(refCnt
>= 0);
129 MOZ_ASSERT(refCnt
> 0);
130 if ((0 == --refCnt
) && fd
) {
137 mutable PRFileDesc
*fd
;
140 static ZipArchiveLogger zipLog
;
142 //***********************************************************
143 // For every inflation the following allocations are done:
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
;
157 nsZipHandle::nsZipHandle()
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
;
174 flags
|= nsIFile::OS_READAHEAD
;
176 nsresult rv
= file
->OpenNSPRFileDesc(flags
, 0000, &fd
.rwget());
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
);
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.
191 PR_CloseFileMap(map
);
192 return NS_ERROR_FAILURE
;
195 nsRefPtr
<nsZipHandle
> handle
= new nsZipHandle();
197 PR_MemUnmap(buf
, (uint32_t) size
);
198 PR_CloseFileMap(map
);
199 return NS_ERROR_OUT_OF_MEMORY
;
208 handle
->mFile
.Init(file
);
209 handle
->mLen
= (uint32_t) size
;
210 handle
->mFileData
= buf
;
211 *ret
= handle
.forget().get();
215 nsresult
nsZipHandle::Init(nsZipArchive
*zip
, const char *entry
,
218 nsRefPtr
<nsZipHandle
> handle
= new nsZipHandle();
220 return NS_ERROR_OUT_OF_MEMORY
;
222 handle
->mBuf
= new nsZipItemPtr
<uint8_t>(zip
, entry
);
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();
237 int64_t nsZipHandle::SizeOfMapping()
242 nsZipHandle::~nsZipHandle()
245 PR_MemUnmap((void *)mFileData
, mLen
);
246 PR_CloseFileMap(mMap
);
251 MOZ_COUNT_DTOR(nsZipHandle
);
254 //***********************************************************
255 // nsZipArchive -- public methods
256 //***********************************************************
258 //---------------------------------------------
259 // nsZipArchive::OpenArchive
260 //---------------------------------------------
261 nsresult
nsZipArchive::OpenArchive(nsZipHandle
*aZipHandle
, PRFileDesc
*aFd
)
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
);
277 nsresult
nsZipArchive::OpenArchive(nsIFile
*aFile
)
279 nsRefPtr
<nsZipHandle
> handle
;
281 mozilla::AutoFDClose fd
;
282 nsresult rv
= nsZipHandle::Init(aFile
, getter_AddRefs(handle
), &fd
.rwget());
284 nsresult rv
= nsZipHandle::Init(aFile
, getter_AddRefs(handle
));
290 return OpenArchive(handle
, fd
.get());
292 return OpenArchive(handle
);
296 //---------------------------------------------
297 // nsZipArchive::Test
298 //---------------------------------------------
299 nsresult
nsZipArchive::Test(const char *aEntryName
)
303 if (aEntryName
) // only test specified item
305 currItem
= GetItem(aEntryName
);
307 return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST
;
308 //-- don't test (synthetic) directory items
309 if (currItem
->IsDirectory())
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())
320 nsresult rv
= ExtractFile(currItem
, 0, 0);
329 //---------------------------------------------
330 // nsZipArchive::CloseArchive
331 //---------------------------------------------
332 nsresult
nsZipArchive::CloseArchive()
335 PL_FinishArenaPool(&mArena
);
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;
351 //---------------------------------------------
352 // nsZipArchive::GetItem
353 //---------------------------------------------
354 nsZipItem
* nsZipArchive::GetItem(const char * 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
)
366 MOZ_WIN_MEM_TRY_BEGIN
367 nsZipItem
* item
= mFiles
[ HashName(aEntryName
, len
) ];
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
378 MOZ_WIN_MEM_TRY_CATCH(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
,
394 return NS_ERROR_ILLEGAL_VALUE
;
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);
410 uint8_t* buf
= cursor
.Read(&count
);
412 rv
= NS_ERROR_FILE_CORRUPTED
;
414 } else if (count
== 0) {
418 if (aFd
&& PR_Write(aFd
, buf
, count
) < (READTYPE
)count
) {
419 rv
= NS_ERROR_FILE_DISK_FULL
;
424 //-- delete the file on errors, or resolve symlink if needed
430 else if (item
->IsSymlink())
431 rv
= ResolveSymlink(outname
);
438 //---------------------------------------------
439 // nsZipArchive::FindInit
440 //---------------------------------------------
442 nsZipArchive::FindInit(const char * aPattern
, nsZipFind
**aFind
)
445 return NS_ERROR_ILLEGAL_VALUE
;
447 // null out param in case an error happens
453 // Create synthetic directory entries on demand
454 nsresult rv
= BuildSynthetics();
458 // validate the pattern
461 switch (NS_WildCardValid((char*)aPattern
))
464 return NS_ERROR_ILLEGAL_VALUE
;
475 // undocumented return value from RegExpValid!
477 return NS_ERROR_ILLEGAL_VALUE
;
480 pattern
= PL_strdup(aPattern
);
482 return NS_ERROR_OUT_OF_MEMORY
;
485 *aFind
= new nsZipFind(this, pattern
, regExp
);
488 return NS_ERROR_OUT_OF_MEMORY
;
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
;
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
];
515 ++mSlot
; // no more in this chain, move to next slot
517 found
= true; // always match
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
);
526 found
= ((mItem
->nameLength
== strlen(mPattern
)) &&
527 (memcmp(mItem
->Name(), mPattern
, mItem
->nameLength
) == 0));
529 // Need also to return the name length, as it is NOT zero-terminatdd...
530 *aResult
= mItem
->Name();
531 *aNameLen
= mItem
->nameLength
;
535 MOZ_WIN_MEM_TRY_CATCH(return NS_ERROR_FAILURE
)
536 return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST
;
540 //---------------------------------------------
542 //---------------------------------------------
543 static nsresult
ResolveSymlink(const char *path
)
545 PRFileDesc
* fIn
= PR_Open(path
, PR_RDONLY
, 0000);
547 return NS_ERROR_FILE_DISK_FULL
;
549 char buf
[PATH_MAX
+1];
550 int32_t length
= PR_Read(fIn
, (void*)buf
, PATH_MAX
);
554 || ((buf
[length
] = 0, PR_Delete(path
)) != 0)
555 || (symlink(buf
, path
) != 0))
557 return NS_ERROR_FILE_DISK_FULL
;
563 //***********************************************************
564 // nsZipArchive -- private implementation
565 //***********************************************************
567 //---------------------------------------------
568 // nsZipArchive::CreateZipItem
569 //---------------------------------------------
570 nsZipItem
* nsZipArchive::CreateZipItem()
572 // Arena allocate the nsZipItem
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
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
) {
594 madvise(const_cast<uint8_t*>(startp
), readaheadLength
, MADV_WILLNEED
);
595 #elif defined(XP_WIN)
597 HANDLE hFile
= (HANDLE
) PR_FileDesc2NativeHandle(aFd
);
598 mozilla::ReadAhead(hFile
, 0, readaheadLength
);
603 for (buf
= endp
- ZIPEND_SIZE
; buf
> startp
; buf
--)
605 if (xtolong(buf
) == ENDSIG
) {
606 centralOffset
= xtolong(((ZipEnd
*)buf
)->offset_central_dir
);
613 return NS_ERROR_FILE_CORRUPTED
;
615 //-- Read the central directory headers
616 buf
= startp
+ centralOffset
;
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.
637 namelen
> kMaxNameLength
||
639 return NS_ERROR_FILE_CORRUPTED
;
642 nsZipItem
* item
= CreateZipItem();
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
];
656 } /* while reading central directory records */
659 return NS_ERROR_FILE_CORRUPTED
;
661 // Make the comment available for consumers.
662 if (endp
- buf
>= ZIPEND_SIZE
) {
663 ZipEnd
*zipend
= (ZipEnd
*)buf
;
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
)
677 //---------------------------------------------
678 // nsZipArchive::BuildSynthetics
679 //---------------------------------------------
680 nsresult
nsZipArchive::BuildSynthetics()
682 if (mBuiltSynthetics
)
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
)
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] != '/')
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
] == '/')
715 // Is the directory already in the file table?
716 uint32_t hash
= HashName(item
->Name(), dirlen
);
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
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
734 nsZipItem
* diritem
= CreateZipItem();
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
)
753 nsZipHandle
* nsZipArchive::GetFD()
760 //---------------------------------------------
761 // nsZipArchive::GetData
762 //---------------------------------------------
763 const uint8_t* nsZipArchive::GetData(nsZipItem
* 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
)
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
))
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
)
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)
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()
818 , mBuiltSynthetics(false)
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()
835 MOZ_COUNT_DTOR(nsZipArchive
);
841 //------------------------------------------
842 // nsZipFind constructor and destructor
843 //------------------------------------------
845 nsZipFind::nsZipFind(nsZipArchive
* aZip
, char* aPattern
, bool aRegExp
) :
852 MOZ_COUNT_CTOR(nsZipFind
);
855 nsZipFind::~nsZipFind()
857 PL_strfree(mPattern
);
859 MOZ_COUNT_DTOR(nsZipFind
);
862 //------------------------------------------
864 //------------------------------------------
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
;
882 return (val
% ZIP_TABSIZE
);
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));
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) |
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
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
+
994 uint32_t buflen
= (uint32_t)xtoint(central
->extrafield_len
);
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
;
1007 pos
+= blocksize
+ 4;
1010 MOZ_WIN_MEM_TRY_CATCH(return nullptr)
1015 PRTime
nsZipItem::LastModTime()
1017 if (isSynthetic
) return GetModTime(kSyntheticDate
, kSyntheticTime
);
1019 // Try to read timestamp from extra field
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());
1030 bool nsZipItem::IsSymlink()
1032 if (isSynthetic
) return false;
1033 return (xtoint(central
->external_attributes
+2) & S_IFMT
) == S_IFLNK
;
1037 nsZipCursor::nsZipCursor(nsZipItem
*item
, nsZipArchive
*aZip
, uint8_t* aBuf
, uint32_t aBufSize
, bool doCRC
) :
1043 if (mItem
->Compression() == DEFLATED
) {
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
);
1056 mCRC
= crc32(0L, Z_NULL
, 0);
1059 nsZipCursor::~nsZipCursor()
1061 if (mItem
->Compression() == DEFLATED
) {
1066 uint8_t* nsZipCursor::ReadOrCopy(uint32_t *aBytesRead
, bool aCopy
) {
1068 uint8_t *buf
= nullptr;
1069 bool verifyCRC
= true;
1073 MOZ_WIN_MEM_TRY_BEGIN
1074 switch (mItem
->Compression()) {
1077 *aBytesRead
= mZs
.avail_in
;
1079 mZs
.next_in
+= mZs
.avail_in
;
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
;
1091 mZs
.avail_out
= mBufSize
;
1093 zerr
= inflate(&mZs
, Z_PARTIAL_FLUSH
);
1094 if (zerr
!= Z_OK
&& zerr
!= Z_STREAM_END
)
1097 *aBytesRead
= mZs
.next_out
- buf
;
1098 verifyCRC
= (zerr
== Z_STREAM_END
);
1105 mCRC
= crc32(mCRC
, (const unsigned char*)buf
, *aBytesRead
);
1106 if (verifyCRC
&& mCRC
!= mItem
->CRC32())
1109 MOZ_WIN_MEM_TRY_CATCH(return nullptr)
1113 nsZipItemPtr_base::nsZipItemPtr_base(nsZipArchive
*aZip
, const char * aEntryName
, bool doCRC
) :
1116 // make sure the ziparchive hangs around
1117 mZipHandle
= aZip
->GetFD();
1119 nsZipItem
* item
= aZip
->GetItem(aEntryName
);
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
);
1135 if (mReadlen
!= item
->RealSize()) {
1136 NS_ASSERTION(mReadlen
== item
->RealSize(), "nsZipCursor underflow");
1137 mReturnBuf
= nullptr;