1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is Mozilla Communicator client code, released
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998
21 * the Initial Developer. All Rights Reserved.
24 * Daniel Veditz <dveditz@netscape.com>
25 * Samir Gehani <sgehani@netscape.com>
26 * Mitch Stoltz <mstoltz@netscape.com>
27 * Jeroen Dobbelaere <jeroen.dobbelaere@acunia.com>
28 * Jeff Walden <jwalden+code@mit.edu>
30 * Alternatively, the contents of this file may be used under the terms of
31 * either the GNU General Public License Version 2 or later (the "GPL"), or
32 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
33 * in which case the provisions of the GPL or the LGPL are applicable instead
34 * of those above. If you wish to allow use of your version of this file only
35 * under the terms of either the GPL or the LGPL, and not to allow others to
36 * use your version of this file under the terms of the MPL, indicate your
37 * decision by deleting the provisions above and replace them with the notice
38 * and other provisions required by the GPL or the LGPL. If you do not delete
39 * the provisions above, a recipient may use your version of this file under
40 * the terms of any one of the MPL, the GPL or the LGPL.
42 * ***** END LICENSE BLOCK ***** */
45 * This module implements a simple archive extractor for the PKZIP format.
47 * The underlying nsZipArchive is NOT thread-safe. Do not pass references
48 * or pointers to it across thread boundaries.
54 #include "nsWildCard.h"
60 #define ZFILE_CREATE PR_WRONLY | PR_CREATE_FILE
61 #define READTYPE PRInt32
63 #include "nsISupportsUtils.h"
64 #include "nsRecyclingAllocator.h"
69 * Global allocator used with zlib. Destroyed in module shutdown.
72 #define BY4ALLOC_ITEMS 320
73 nsRecyclingAllocator
*gZlibAllocator
= NULL
;
75 // For placement new used for arena allocations of zip file list
77 #define ZIP_ARENABLOCKSIZE (1*1024)
79 #else /* STANDALONE */
85 #undef MOZILLA_CLIENT // undoes prtypes damage in zlib.h
86 #define ZFILE_CREATE "wb"
87 #define READTYPE PRUint32
96 char * strdup(const char *src
);
97 char * strdup(const char *src
)
99 long len
= strlen(src
);
100 char *dup
= (char *)malloc(len
+1 * sizeof(char));
101 memcpy(dup
, src
, len
+1);
106 #endif /* STANDALONE */
109 #include <sys/types.h>
110 #include <sys/stat.h>
113 #elif defined(XP_WIN) || defined(XP_OS2)
115 #elif defined(XP_BEOS)
119 #ifndef XP_UNIX /* we need some constants defined in limits.h and unistd.h */
121 # define S_IFMT 0170000
124 # define S_IFLNK 0120000
127 # define PATH_MAX 1024
132 #include "zipstruct.h"
133 #include "nsZipArchive.h"
135 static PRUint16
xtoint(unsigned char *ii
);
136 static PRUint32
xtolong(unsigned char *ll
);
137 static PRUint16
ExtractMode(unsigned char *ll
);
138 static PRUint32
HashName(const char* aName
);
139 #if defined(XP_UNIX) || defined(XP_BEOS)
140 static PRBool
IsSymlink(unsigned char *ll
);
141 static nsresult
ResolveSymlink(const char *path
);
144 /*---------------------------------------------
145 * C API wrapper for nsZipArchive
146 *--------------------------------------------*/
153 * opens the named zip/jar archive and returns a handle that
154 * represents the archive in other ZIP_ calls.
156 * @param zipname archive filename
157 * @param hZip receives handle if archive opened OK
158 * @return status code
160 PR_PUBLIC_API(PRInt32
) ZIP_OpenArchive(const char * zipname
, void** hZip
)
164 /*--- error check args ---*/
166 return ZIP_ERR_PARAM
;
168 /*--- NULL output to prevent use by bozos who don't check errors ---*/
171 /*--- create and open the archive ---*/
172 nsZipArchive
* zip
= new nsZipArchive();
174 return ZIP_ERR_MEMORY
;
176 PRFileDesc
* fd
= PR_Open(zipname
, PR_RDONLY
, 0400);
182 status
= zip
->OpenArchive(fd
);
183 if (status
== ZIP_OK
)
184 *hZip
= static_cast<void*>(zip
);
198 * Tests the integrity of this open zip archive by extracting each
199 * item to memory and performing a CRC check.
201 * @param hZip handle obtained from ZIP_OpenArchive
202 * @return status code (success indicated by ZIP_OK)
204 PR_PUBLIC_API(PRInt32
) ZIP_TestArchive(void *hZip
)
206 /*--- error check args ---*/
208 return ZIP_ERR_PARAM
;
210 nsZipArchive
* zip
= static_cast<nsZipArchive
*>(hZip
);
211 if (zip
->kMagic
!= ZIP_MAGIC
)
212 return ZIP_ERR_PARAM
; /* whatever it is isn't one of ours! */
214 /*--- test the archive ---*/
215 return zip
->Test(NULL
);
222 * closes zip archive and frees memory
223 * @param hZip handle obtained from ZIP_OpenArchive
224 * @return status code
226 PR_PUBLIC_API(PRInt32
) ZIP_CloseArchive(void** hZip
)
228 /*--- error check args ---*/
229 if (hZip
== 0 || *hZip
== 0)
230 return ZIP_ERR_PARAM
;
232 nsZipArchive
* zip
= static_cast<nsZipArchive
*>(*hZip
);
233 if (zip
->kMagic
!= ZIP_MAGIC
)
234 return ZIP_ERR_PARAM
; /* whatever it is isn't one of ours! */
236 /*--- close the archive ---*/
248 * extracts named file from an opened archive
250 * @param hZip handle obtained from ZIP_OpenArchive
251 * @param filename name of file in archive
252 * @param outname filename to extract to
254 PR_PUBLIC_API(PRInt32
) ZIP_ExtractFile(void* hZip
, const char * filename
, const char * outname
)
256 /*--- error check args ---*/
258 return ZIP_ERR_PARAM
;
260 nsZipArchive
* zip
= static_cast<nsZipArchive
*>(hZip
);
261 if (zip
->kMagic
!= ZIP_MAGIC
)
262 return ZIP_ERR_PARAM
; /* whatever it is isn't one of ours! */
264 //-- Find item in archive
265 nsZipItem
* item
= zip
->GetItem(filename
);
269 // Can't extract a directory
270 if (item
->isDirectory
)
271 return ZIP_ERR_PARAM
;
273 // delete any existing file so that we overwrite the file permissions
276 PRFileDesc
* fOut
= PR_Open(outname
, ZFILE_CREATE
, item
->mode
);
280 #if defined(XP_UNIX) && defined(STANDALONE)
281 // When STANDALONE is defined, PR_Open ignores its 3d argument.
282 mode_t msk
= umask(0);
284 chmod(outname
, (item
->mode
| S_IRUSR
) & ~msk
);
287 // ExtractFile also closes the fOut handle and resolves the symlink if needed
288 return zip
->ExtractFile(item
, outname
, fOut
);
296 * Initializes an enumeration of files in the archive
298 * @param hZip handle obtained from ZIP_OpenArchive
299 * @param pattern regexp to match files in archive, the usual shell expressions.
300 * NULL pattern also matches all files, faster than "*"
302 PR_PUBLIC_API(void*) ZIP_FindInit(void* hZip
, const char * pattern
)
304 /*--- error check args ---*/
308 nsZipArchive
* zip
= static_cast<nsZipArchive
*>(hZip
);
309 if (zip
->kMagic
!= ZIP_MAGIC
)
310 return 0; /* whatever it is isn't one of ours! */
312 /*--- initialize the pattern search ---*/
314 PRInt32 rv
= zip
->FindInit(pattern
, &find
);
326 * Puts the next name in the passed buffer. Returns ZIP_ERR_SMALLBUF when
327 * the name is too large for the buffer, and ZIP_ERR_FNF when there are no
328 * more files that match the pattern
330 * @param hFind handle obtained from ZIP_FindInit
331 * @param outbuf buffer to receive next filename
332 * @param bufsize size of allocated buffer
334 PR_PUBLIC_API(PRInt32
) ZIP_FindNext(void* hFind
, char * outbuf
, PRUint16 bufsize
)
338 /*--- error check args ---*/
340 return ZIP_ERR_PARAM
;
342 nsZipFind
* find
= static_cast<nsZipFind
*>(hFind
);
343 if (find
->kMagic
!= ZIPFIND_MAGIC
)
344 return ZIP_ERR_PARAM
; /* whatever it is isn't one of ours! */
346 /*--- return next filename file ---*/
347 const char* itemName
;
348 status
= find
->FindNext(&itemName
);
349 if (status
== ZIP_OK
)
351 PRUint16 namelen
= (PRUint16
)PL_strlen(itemName
);
353 if (bufsize
> namelen
)
355 PL_strcpy(outbuf
, itemName
);
358 status
= ZIP_ERR_SMALLBUF
;
369 * Releases allocated memory associated with the find token
371 * @param hFind handle obtained from ZIP_FindInit
373 PR_PUBLIC_API(PRInt32
) ZIP_FindFree(void* hFind
)
375 /*--- error check args ---*/
377 return ZIP_ERR_PARAM
;
379 nsZipFind
* find
= static_cast<nsZipFind
*>(hFind
);
380 if (find
->kMagic
!= ZIPFIND_MAGIC
)
381 return ZIP_ERR_PARAM
; /* whatever it is isn't one of ours! */
383 /* free the find structure */
389 void ProcessWindowsMessages()
393 while(PeekMessage(&msg
, 0, 0, 0, PM_REMOVE
))
395 TranslateMessage(&msg
);
396 DispatchMessage(&msg
);
401 #else /* STANDALONE */
403 //***********************************************************
404 // Allocators for use with zlib
406 // These are allocators that are performance tuned for
407 // use with zlib. Our use of zlib for every file we read from
408 // the jar file when running navigator, we do these allocation.
413 // alloc 1216 [304x4] max
416 // alloc 1152 [288x4]
426 // The pool will allocate these as:
430 // 1,280 [320x4] - shared by first x4 alloc, 28
431 // 1,280 [320x4] - shared by second and third x4 alloc
437 // And almost all of the file reads happen serially. Hence this
438 // allocator tries to keep one set of memory needed for one file around
439 // and reused the same blocks for other file reads.
441 // The interesting question is when should be free this ?
442 // - memory pressure should be one.
443 // - after startup of navigator
444 // - after startup of mail
445 // In general, this allocator should be enabled before
446 // we startup and disabled after we startup if memory is a concern.
447 //***********************************************************
449 PR_STATIC_CALLBACK(void *)
450 zlibAlloc(void *opaque
, uInt items
, uInt size
)
452 nsRecyclingAllocator
*zallocator
= (nsRecyclingAllocator
*)opaque
;
454 // Bump up x4 allocations
455 PRUint32 realitems
= items
;
456 if (size
== 4 && items
< BY4ALLOC_ITEMS
)
457 realitems
= BY4ALLOC_ITEMS
;
458 return zallocator
->Calloc(realitems
, size
);
461 return calloc(items
, size
);
464 PR_STATIC_CALLBACK(void)
465 zlibFree(void *opaque
, void *ptr
)
467 nsRecyclingAllocator
*zallocator
= (nsRecyclingAllocator
*)opaque
;
469 zallocator
->Free(ptr
);
474 #endif /* STANDALONE */
476 nsresult
gZlibInit(z_stream
*zs
)
478 memset(zs
, 0, sizeof(z_stream
));
480 //-- ensure we have our zlib allocator for better performance
481 if (!gZlibAllocator
) {
482 gZlibAllocator
= new nsRecyclingAllocator(NBUCKETS
, NS_DEFAULT_RECYCLE_TIMEOUT
, "libjar");
484 if (gZlibAllocator
) {
485 zs
->zalloc
= zlibAlloc
;
486 zs
->zfree
= zlibFree
;
487 zs
->opaque
= gZlibAllocator
;
489 #endif /* STANDALONE */
490 int zerr
= inflateInit2(zs
, -MAX_WBITS
);
491 if (zerr
!= Z_OK
) return ZIP_ERR_MEMORY
;
496 //***********************************************************
497 // nsZipArchive -- public methods
498 //***********************************************************
501 //---------------------------------------------
502 // nsZipArchive::OpenArchive
503 //---------------------------------------------
504 nsresult
nsZipArchive::OpenArchive(PRFileDesc
* fd
)
507 return ZIP_ERR_PARAM
;
510 // Initialize our arena
511 PL_INIT_ARENA_POOL(&mArena
, "ZipArena", ZIP_ARENABLOCKSIZE
);
514 //-- Keep the filedescriptor for further reading...
517 //-- get table of contents for archive
518 return BuildFileList();
521 //---------------------------------------------
522 // nsZipArchive::Test
523 //---------------------------------------------
524 nsresult
nsZipArchive::Test(const char *aEntryName
)
528 if (aEntryName
) // only test specified item
530 currItem
= GetItem(aEntryName
);
533 //-- don't test (synthetic) directory items
534 if (currItem
->isDirectory
)
536 return ExtractFile(currItem
, 0, 0);
539 // test all items in archive
540 for (int i
= 0; i
< ZIP_TABSIZE
; i
++) {
541 for (currItem
= mFiles
[i
]; currItem
; currItem
= currItem
->next
) {
542 //-- don't test (synthetic) directory items
543 if (currItem
->isDirectory
)
545 nsresult rv
= ExtractFile(currItem
, 0, 0);
548 #if defined STANDALONE && defined XP_WIN
549 ProcessWindowsMessages();
557 //---------------------------------------------
558 // nsZipArchive::CloseArchive
559 //---------------------------------------------
560 nsresult
nsZipArchive::CloseArchive()
564 PL_FinishArenaPool(&mArena
);
568 // We don't need to delete each of the nsZipItem as the memory for
569 // the zip item and the filename it holds are both allocated from the Arena.
570 // Hence, destroying the Arena is like destroying all the memory
571 // for all the nsZipItem in one shot. But if the ~nsZipItem is doing
572 // anything more than cleaning up memory, we should start calling it.
573 // Let us also cleanup the mFiles table for re-use on the next 'open' call
574 for (int i
= 0; i
< ZIP_TABSIZE
; i
++) {
578 // delete nsZipItems in table
580 for (int i
= 0; i
< ZIP_TABSIZE
; ++i
)
585 mFiles
[i
] = pItem
->next
;
589 mFiles
[i
] = 0; // make sure we don't double-delete
597 mBuiltSynthetics
= PR_FALSE
;
601 //---------------------------------------------
602 // nsZipArchive::GetItem
603 //---------------------------------------------
604 nsZipItem
* nsZipArchive::GetItem(const char * aEntryName
)
607 //-- If the request is for a directory, make sure that synthetic entries
608 //-- are created for the directories without their own entry.
609 if (!mBuiltSynthetics
) {
610 PRUint32 len
= strlen(aEntryName
);
611 if ((len
> 0) && (aEntryName
[len
-1] == '/')) {
612 if (BuildSynthetics() != ZIP_OK
)
617 nsZipItem
* item
= mFiles
[ HashName(aEntryName
) ];
619 if (!strcmp(aEntryName
, item
->name
))
620 return item
; //-- found it
627 //---------------------------------------------
628 // nsZipArchive::ExtractFile
629 // This extracts the item to the filehandle provided.
630 // If 'aFd' is null, it only tests the extraction.
631 // On extraction error(s) it removes the file.
632 // When needed, it also resolves the symlink.
633 //---------------------------------------------
634 nsresult
nsZipArchive::ExtractFile(nsZipItem
*item
, const char *outname
,
638 return ZIP_ERR_PARAM
;
640 return ZIP_ERR_GENERAL
;
642 // Directory extraction is handled in nsJAR::Extract,
643 // so the item to be extracted should never be a directory
644 PR_ASSERT(!item
->isDirectory
);
646 //-- move to the start of file's data
647 if (SeekToItem(item
, mFd
) != ZIP_OK
)
648 return ZIP_ERR_CORRUPT
;
652 //-- extract the file using the appropriate method
653 switch(item
->compression
)
656 rv
= CopyItemToDisk(item
->size
, item
->crc32
, aFd
);
660 rv
= InflateItem(item
, aFd
);
664 //-- unsupported compression type
665 rv
= ZIP_ERR_UNSUPPORTED
;
668 //-- delete the file on errors, or resolve symlink if needed
673 #if defined(XP_UNIX) || defined(XP_BEOS)
674 else if (item
->isSymlink
)
675 rv
= ResolveSymlink(outname
);
682 //---------------------------------------------
683 // nsZipArchive::FindInit
684 //---------------------------------------------
686 nsZipArchive::FindInit(const char * aPattern
, nsZipFind
**aFind
)
689 return ZIP_ERR_PARAM
;
691 // null out param in case an error happens
694 PRBool regExp
= PR_FALSE
;
697 // Create synthetic directory entries on demand
698 nsresult rv
= BuildSynthetics();
702 // validate the pattern
705 switch (NS_WildCardValid((char*)aPattern
))
708 return ZIP_ERR_PARAM
;
719 // undocumented return value from RegExpValid!
721 return ZIP_ERR_PARAM
;
724 pattern
= PL_strdup(aPattern
);
726 return ZIP_ERR_MEMORY
;
729 *aFind
= new nsZipFind(this, pattern
, regExp
);
732 return ZIP_ERR_MEMORY
;
740 //---------------------------------------------
741 // nsZipFind::FindNext
742 //---------------------------------------------
743 nsresult
nsZipFind::FindNext(const char ** aResult
)
745 if (!mArchive
|| !aResult
)
746 return ZIP_ERR_PARAM
;
750 // we start from last match, look for next
751 while (mSlot
< ZIP_TABSIZE
)
753 // move to next in current chain, or move to new slot
754 mItem
= mItem
? mItem
->next
: mArchive
->mFiles
[mSlot
];
756 PRBool found
= PR_FALSE
;
758 ++mSlot
; // no more in this chain, move to next slot
760 found
= PR_TRUE
; // always match
762 found
= (NS_WildCardMatch(mItem
->name
, mPattern
, PR_FALSE
) == MATCH
);
764 #if defined(STANDALONE) && defined(XP_MAC)
765 // simulate <regexp>* matches
766 found
= (strncmp(mItem
->name
, mPattern
, strlen(mPattern
)) == 0);
768 found
= (PL_strcmp(mItem
->name
, mPattern
) == 0);
772 *aResult
= mItem
->name
;
780 #if defined(XP_UNIX) || defined(XP_BEOS)
781 //---------------------------------------------
783 //---------------------------------------------
784 static nsresult
ResolveSymlink(const char *path
)
786 PRFileDesc
* fIn
= PR_Open(path
, PR_RDONLY
, 0000);
790 char buf
[PATH_MAX
+1];
791 PRInt32 length
= PR_Read(fIn
, (void*)buf
, PATH_MAX
);
795 || ((buf
[length
] = 0, PR_Delete(path
)) != 0)
796 || (symlink(buf
, path
) != 0))
804 //***********************************************************
805 // nsZipArchive -- private implementation
806 //***********************************************************
808 #define BR_BUF_SIZE 1024 /* backward read buffer size */
810 //---------------------------------------------
811 // nsZipArchive::CreateZipItem
812 //---------------------------------------------
813 nsZipItem
* nsZipArchive::CreateZipItem(PRUint16 namelen
)
815 // sizeof(nsZipItem) includes space for name's null byte
817 // Arena allocate the nsZipItem
819 PL_ARENA_ALLOCATE(mem
, &mArena
, sizeof(nsZipItem
)+namelen
);
820 return (nsZipItem
*)mem
;
822 return (nsZipItem
*)malloc(sizeof(nsZipItem
)+namelen
);
826 //---------------------------------------------
827 // nsZipArchive::BuildFileList
828 //---------------------------------------------
829 nsresult
nsZipArchive::BuildFileList()
831 PRUint8 buf
[4*BR_BUF_SIZE
];
833 //-----------------------------------------------------------------------
834 // locate the central directory via the End record
835 //-----------------------------------------------------------------------
837 //-- get archive size using end pos
838 PRInt32 pos
= PR_Seek(mFd
, 0, PR_SEEK_END
);
842 if (pos
|| ((pos
= ftell(mFd
)) <= 0))
844 return ZIP_ERR_CORRUPT
;
846 PRBool bEndsigFound
= PR_FALSE
;
847 while (!bEndsigFound
)
849 //-- read backwards in 1K-sized chunks (unless file is less than 1K)
850 PRInt32 bufsize
= pos
> BR_BUF_SIZE
? BR_BUF_SIZE
: pos
;
853 if (!ZIP_Seek(mFd
, pos
, PR_SEEK_SET
))
854 return ZIP_ERR_CORRUPT
;
856 if (PR_Read(mFd
, buf
, bufsize
) != (READTYPE
)bufsize
)
857 return ZIP_ERR_CORRUPT
;
860 PRUint8
*endp
= buf
+ bufsize
;
861 for (endp
-= ZIPEND_SIZE
; endp
>= buf
; endp
--)
863 if (xtolong(endp
) == ENDSIG
)
865 //-- Seek to start of central directory
866 PRInt32 central
= xtolong(((ZipEnd
*) endp
)->offset_central_dir
);
867 if (!ZIP_Seek(mFd
, central
, PR_SEEK_SET
))
868 return ZIP_ERR_CORRUPT
;
870 bEndsigFound
= PR_TRUE
;
879 //-- We're at the beginning of the file, and still no sign
880 //-- of the end signature. File must be corrupted!
881 return ZIP_ERR_CORRUPT
;
883 //-- backward read must overlap ZipEnd length
886 } /* while looking for end signature */
889 //-------------------------------------------------------
890 // read the central directory headers
891 //-------------------------------------------------------
892 PRInt32 byteCount
= PR_Read(mFd
, &buf
, sizeof(buf
));
894 PRUint32 sig
= xtolong(buf
);
895 while (sig
== CENTRALSIG
) {
896 //-- make sure we've read enough
897 if (byteCount
- pos
< ZIPCENTRAL_SIZE
)
898 return ZIP_ERR_CORRUPT
;
900 //-------------------------------------------------------
901 // read the fixed-size data
902 //-------------------------------------------------------
903 ZipCentral
* central
= (ZipCentral
*)(buf
+pos
);
905 PRUint16 namelen
= xtoint(central
->filename_len
);
906 PRUint16 extralen
= xtoint(central
->extrafield_len
);
907 PRUint16 commentlen
= xtoint(central
->commentfield_len
);
909 //-- sanity check variable sizes and refuse to deal with
910 //-- anything too big: it's likely a corrupt archive
911 if (namelen
> BR_BUF_SIZE
|| extralen
> BR_BUF_SIZE
|| commentlen
> 2*BR_BUF_SIZE
)
912 return ZIP_ERR_CORRUPT
;
914 nsZipItem
* item
= CreateZipItem(namelen
);
916 return ZIP_ERR_MEMORY
;
918 item
->headerOffset
= xtolong(central
->localhdr_offset
);
919 item
->dataOffset
= 0;
920 item
->size
= xtolong(central
->size
);
921 item
->realsize
= xtolong(central
->orglen
);
922 item
->crc32
= xtolong(central
->crc32
);
923 item
->time
= xtoint(central
->time
);
924 item
->date
= xtoint(central
->date
);
925 item
->isSynthetic
= PR_FALSE
;
926 item
->hasDataOffset
= PR_FALSE
;
928 PRUint16 compression
= xtoint(central
->method
);
929 item
->compression
= (compression
< UNSUPPORTED
) ? (PRUint8
)compression
932 item
->mode
= ExtractMode(central
->external_attributes
);
933 #if defined(XP_UNIX) || defined(XP_BEOS)
934 // Check if item is a symlink
935 item
->isSymlink
= IsSymlink(central
->external_attributes
);
938 pos
+= ZIPCENTRAL_SIZE
;
940 //-------------------------------------------------------
941 // Make sure that remainder of this record (name, comments, extra)
942 // and the next ZipCentral is all in the buffer
943 //-------------------------------------------------------
944 PRInt32 leftover
= byteCount
- pos
;
945 if (leftover
< (namelen
+ extralen
+ commentlen
+ ZIPCENTRAL_SIZE
)) {
946 //-- not enough data left to process at top of loop.
947 //-- move leftover and read more
948 memcpy(buf
, buf
+pos
, leftover
);
949 byteCount
= leftover
+ PR_Read(mFd
, buf
+leftover
, sizeof(buf
)-leftover
);
952 if (byteCount
< (namelen
+ extralen
+ commentlen
+ sizeof(sig
))) {
954 return ZIP_ERR_CORRUPT
;
958 //-------------------------------------------------------
960 //-------------------------------------------------------
961 memcpy(item
->name
, buf
+pos
, namelen
);
962 item
->name
[namelen
] = 0;
964 //-- an item whose name ends with '/' is a directory
965 item
->isDirectory
= ('/' == item
->name
[namelen
- 1]);
967 //-- add item to file table
968 //-- note that an explicit entry for a directory will override
969 //-- a fake entry created for that directory (as in the case
970 //-- of processing foo/bar.txt and then foo/) -- this will
971 //-- preserve an explicit directory's metadata at the cost of
972 //-- an extra nsZipItem (and that only happens if we process a
973 //-- file inside that directory before processing the directory
975 PRUint32 hash
= HashName(item
->name
);
976 item
->next
= mFiles
[hash
];
979 //-------------------------------------------------------
980 // set up to process the next item at the top of loop
981 //-------------------------------------------------------
982 pos
+= namelen
+ extralen
+ commentlen
;
983 sig
= xtolong(buf
+pos
);
984 } /* while reading central directory records */
987 return ZIP_ERR_CORRUPT
;
992 //---------------------------------------------
993 // nsZipArchive::BuildSynthetics
994 //---------------------------------------------
995 nsresult
nsZipArchive::BuildSynthetics()
997 if (mBuiltSynthetics
)
999 mBuiltSynthetics
= PR_TRUE
;
1001 // Create synthetic entries for any missing directories.
1002 // Do this when all ziptable has scanned to prevent double entries.
1003 for (int i
= 0; i
< ZIP_TABSIZE
; ++i
)
1005 for (nsZipItem
* item
= mFiles
[i
]; item
!= 0; item
= item
->next
)
1007 if (item
->isSynthetic
)
1010 //-- add entries for directories in the current item's path
1011 //-- go from end to beginning, because then we can stop trying
1012 //-- to create diritems if we find that the diritem we want to
1013 //-- create already exists
1014 //-- start just before the last char so as to not add the item
1015 //-- twice if it's a directory
1016 PRUint16 namelen
= strlen(item
->name
);
1017 for (char* p
= item
->name
+ namelen
- 2; p
>= item
->name
; p
--)
1022 // See whether we need to create any more implicit directories,
1023 // because if we don't we can avoid a lot of work.
1024 // We can even avoid (de)allocating space for a bogus dirname with
1025 // a little trickery -- save the char at item->name[dirnamelen],
1026 // set it to 0, compare the strings, and restore the saved
1028 const PRUint32 dirnamelen
= p
+ 1 - item
->name
;
1029 const char savedChar
= item
->name
[dirnamelen
];
1030 item
->name
[dirnamelen
] = 0;
1032 // Is the directory in the file table?
1033 PRUint32 hash
= HashName(item
->name
);
1034 PRBool found
= PR_FALSE
;
1035 for (nsZipItem
* zi
= mFiles
[hash
]; zi
!= NULL
; zi
= zi
->next
)
1037 if (0 == strcmp(item
->name
, zi
->name
))
1039 // we've already added this dir and all its parents
1045 // restore the char immediately
1046 item
->name
[dirnamelen
] = savedChar
;
1048 // if the directory was found, break out of the directory
1049 // creation loop now that we know all implicit directories
1050 // are there -- otherwise, start creating the zip item
1054 nsZipItem
* diritem
= CreateZipItem(dirnamelen
);
1056 return ZIP_ERR_MEMORY
;
1058 memcpy(diritem
->name
, item
->name
, dirnamelen
);
1059 diritem
->name
[dirnamelen
] = 0;
1061 diritem
->isDirectory
= PR_TRUE
;
1062 diritem
->isSynthetic
= PR_TRUE
;
1063 diritem
->compression
= STORED
;
1064 diritem
->size
= diritem
->realsize
= 0;
1066 diritem
->mode
= 0755;
1068 // Set an obviously wrong last-modified date/time, because
1069 // finding something more accurate like the most recent
1070 // last-modified date/time of the dir's contents is a lot
1071 // of effort. The date/time corresponds to 1980-01-01 00:00.
1073 diritem
->date
= 1 + (1 << 5) + (0 << 9);
1075 // add diritem to the file table
1076 diritem
->next
= mFiles
[hash
];
1077 mFiles
[hash
] = diritem
;
1078 } /* end processing of dirs in item's name */
1085 //---------------------------------------------
1086 // nsZipArchive::SeekToItem
1087 //---------------------------------------------
1088 nsresult
nsZipArchive::SeekToItem(nsZipItem
* aItem
, PRFileDesc
* aFd
)
1092 //-- the first time an item is used we need to calculate its offset
1093 if (!aItem
->hasDataOffset
)
1095 //-- read local header to get variable length values and calculate
1096 //-- the real data offset
1098 //-- NOTE: extralen is different in central header and local header
1099 //-- for archives created using the Unix "zip" utility. To set
1100 //-- the offset accurately we need the _local_ extralen.
1101 if (!ZIP_Seek(aFd
, aItem
->headerOffset
, PR_SEEK_SET
))
1102 return ZIP_ERR_CORRUPT
;
1105 if ((PR_Read(aFd
, (char*)&Local
, ZIPLOCAL_SIZE
) != (READTYPE
) ZIPLOCAL_SIZE
) ||
1106 (xtolong(Local
.signature
) != LOCALSIG
))
1108 //-- read error or local header not found
1109 return ZIP_ERR_CORRUPT
;
1112 aItem
->dataOffset
= aItem
->headerOffset
+
1114 xtoint(Local
.filename_len
) +
1115 xtoint(Local
.extrafield_len
);
1116 aItem
->hasDataOffset
= PR_TRUE
;
1119 //-- move to start of file in archive
1120 if (!ZIP_Seek(aFd
, aItem
->dataOffset
, PR_SEEK_SET
))
1121 return ZIP_ERR_CORRUPT
;
1126 //---------------------------------------------
1127 // nsZipArchive::CopyItemToDisk
1128 //---------------------------------------------
1130 nsZipArchive::CopyItemToDisk(PRUint32 itemSize
, PRUint32 itemCrc
, PRFileDesc
* outFD
)
1132 * This function copies an archive item to disk, to the
1133 * file specified by outFD. If outFD is zero, the extracted data is
1134 * not written, only checked for CRC, so this is in effect same as 'Test'.
1137 PRUint32 chunk
, pos
, crc
;
1138 char buf
[ZIP_BUFLEN
];
1141 crc
= crc32(0L, Z_NULL
, 0);
1143 //-- copy chunks until file is done
1144 for (pos
= 0; pos
< itemSize
; pos
+= chunk
)
1146 chunk
= (itemSize
- pos
< ZIP_BUFLEN
) ? (itemSize
- pos
) : ZIP_BUFLEN
;
1148 if (PR_Read(mFd
, buf
, chunk
) != (READTYPE
)chunk
)
1150 //-- unexpected end of data in archive
1151 return ZIP_ERR_CORRUPT
;
1154 //-- incrementally update crc32
1155 crc
= crc32(crc
, (const unsigned char*)buf
, chunk
);
1157 if (outFD
&& PR_Write(outFD
, buf
, chunk
) < (READTYPE
)chunk
)
1159 //-- Couldn't write all the data (disk full?)
1160 return ZIP_ERR_DISK
;
1166 return ZIP_ERR_CORRUPT
;
1172 //---------------------------------------------
1173 // nsZipArchive::InflateItem
1174 //---------------------------------------------
1175 nsresult
nsZipArchive::InflateItem(const nsZipItem
* aItem
, PRFileDesc
* outFD
)
1177 * This function inflates an archive item to disk, to the
1178 * file specified by outFD. If outFD is zero, the extracted data is
1179 * not written, only checked for CRC, so this is in effect same as 'Test'.
1184 //-- allocate deflation buffers
1185 Bytef inbuf
[ZIP_BUFLEN
];
1186 Bytef outbuf
[ZIP_BUFLEN
];
1188 //-- set up the inflate
1190 nsresult status
= gZlibInit(&zs
);
1191 if (status
!= ZIP_OK
)
1192 return ZIP_ERR_GENERAL
;
1195 zs
.next_out
= outbuf
;
1196 zs
.avail_out
= ZIP_BUFLEN
;
1198 PRUint32 size
= aItem
->size
;
1199 PRUint32 outpos
= 0;
1200 PRUint32 crc
= crc32(0L, Z_NULL
, 0);
1202 while (zerr
== Z_OK
)
1204 PRBool bRead
= PR_FALSE
;
1205 PRBool bWrote
= PR_FALSE
;
1207 if (zs
.avail_in
== 0 && zs
.total_in
< size
)
1209 //-- no data to inflate yet still more in file:
1210 //-- read another chunk of compressed data
1211 PRUint32 chunk
= (size
-zs
.total_in
< ZIP_BUFLEN
) ? size
-zs
.total_in
: ZIP_BUFLEN
;
1213 if (PR_Read(mFd
, inbuf
, chunk
) != (READTYPE
)chunk
)
1215 //-- unexpected end of data
1216 status
= ZIP_ERR_CORRUPT
;
1221 zs
.avail_in
= chunk
;
1225 if (zs
.avail_out
== 0)
1227 //-- write inflated buffer to disk and make space
1228 if (outFD
&& PR_Write(outFD
, outbuf
, ZIP_BUFLEN
) < ZIP_BUFLEN
)
1230 //-- Couldn't write all the data (disk full?)
1231 status
= ZIP_ERR_DISK
;
1235 outpos
= zs
.total_out
;
1236 zs
.next_out
= outbuf
;
1237 zs
.avail_out
= ZIP_BUFLEN
;
1243 Bytef
* old_next_out
= zs
.next_out
;
1245 zerr
= inflate(&zs
, Z_PARTIAL_FLUSH
);
1247 //-- incrementally update crc32
1248 crc
= crc32(crc
, (const unsigned char*)old_next_out
, zs
.next_out
- old_next_out
);
1251 zerr
= Z_STREAM_END
;
1253 #if defined STANDALONE && defined XP_WIN
1254 ProcessWindowsMessages();
1259 if ((status
== ZIP_OK
) && (crc
!= aItem
->crc32
))
1261 status
= ZIP_ERR_CORRUPT
;
1265 //-- write last inflated bit to disk
1266 if (zerr
== Z_STREAM_END
&& outpos
< zs
.total_out
)
1268 PRUint32 chunk
= zs
.total_out
- outpos
;
1269 if (outFD
&& PR_Write(outFD
, outbuf
, chunk
) < (READTYPE
)chunk
)
1270 status
= ZIP_ERR_DISK
;
1273 //-- convert zlib error to return value
1274 if (status
== ZIP_OK
&& zerr
!= Z_OK
&& zerr
!= Z_STREAM_END
)
1276 status
= (zerr
== Z_MEM_ERROR
) ? ZIP_ERR_MEMORY
: ZIP_ERR_CORRUPT
;
1279 //-- if found no errors make sure we've converted the whole thing
1280 PR_ASSERT(status
!= ZIP_OK
|| zs
.total_in
== aItem
->size
);
1281 PR_ASSERT(status
!= ZIP_OK
|| zs
.total_out
== aItem
->realsize
);
1284 //-- free zlib internal state
1290 //------------------------------------------
1291 // nsZipArchive constructor and destructor
1292 //------------------------------------------
1294 nsZipArchive::nsZipArchive() :
1299 mBuiltSynthetics(PR_FALSE
)
1301 MOZ_COUNT_CTOR(nsZipArchive
);
1303 // initialize the table to NULL
1304 memset(mFiles
, 0, sizeof mFiles
);
1307 nsZipArchive::~nsZipArchive()
1311 MOZ_COUNT_DTOR(nsZipArchive
);
1315 //------------------------------------------
1316 // nsZipFind constructor and destructor
1317 //------------------------------------------
1319 nsZipFind::nsZipFind(nsZipArchive
* aZip
, char* aPattern
, PRBool aRegExp
) :
1321 kMagic(ZIPFIND_MAGIC
),
1329 MOZ_COUNT_CTOR(nsZipFind
);
1332 nsZipFind::~nsZipFind()
1334 PR_FREEIF(mPattern
);
1336 MOZ_COUNT_DTOR(nsZipFind
);
1339 //------------------------------------------
1341 //------------------------------------------
1346 * returns a hash key for the entry name
1348 static PRUint32
HashName(const char* aName
)
1350 PR_ASSERT(aName
!= 0);
1353 for (PRUint8
* c
= (PRUint8
*)aName
; *c
!= 0; c
++) {
1357 return (val
% ZIP_TABSIZE
);
1363 * Converts a two byte ugly endianed integer
1364 * to our platform's integer.
1366 static PRUint16
xtoint (unsigned char *ii
)
1368 return (PRUint16
) ((ii
[0]) | (ii
[1] << 8));
1374 * Converts a four byte ugly endianed integer
1375 * to our platform's integer.
1377 static PRUint32
xtolong (unsigned char *ll
)
1379 return (PRUint32
)( (ll
[0] << 0) |
1388 * Extracts bits 17-24 from a 32-bit unsigned long
1389 * representation of the external attributes field.
1390 * Subsequently it tacks on the implicit user-read
1393 static PRUint16
ExtractMode(unsigned char *ll
)
1395 return ((PRUint16
)(ll
[2])) | 0x0100;
1398 #if defined(XP_UNIX) || defined(XP_BEOS)
1401 * Return true if the attributes are for a symbolic link
1405 static PRBool
IsSymlink(unsigned char *ll
)
1407 return ((xtoint(ll
+2) & S_IFMT
) == S_IFLNK
);