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);
107 int remove(const char* inPath
)
109 unsigned short wPath
[MAX_PATH
];
110 MultiByteToWideChar(CP_ACP
,
117 if(FALSE
!= DeleteFileW(wPath
))
123 #endif /* STANDALONE */
126 #include <sys/types.h>
127 #include <sys/stat.h>
130 #elif defined(XP_WIN) || defined(XP_OS2)
132 #elif defined(XP_BEOS)
136 #ifndef XP_UNIX /* we need some constants defined in limits.h and unistd.h */
138 # define S_IFMT 0170000
141 # define S_IFLNK 0120000
144 # define PATH_MAX 1024
149 #include "zipstruct.h"
150 #include "nsZipArchive.h"
152 static PRUint16
xtoint(unsigned char *ii
);
153 static PRUint32
xtolong(unsigned char *ll
);
154 static PRUint16
ExtractMode(unsigned char *ll
);
155 static PRUint32
HashName(const char* aName
);
156 #if defined(XP_UNIX) || defined(XP_BEOS)
157 static PRBool
IsSymlink(unsigned char *ll
);
158 static nsresult
ResolveSymlink(const char *path
);
161 /*---------------------------------------------
162 * C API wrapper for nsZipArchive
163 *--------------------------------------------*/
170 * opens the named zip/jar archive and returns a handle that
171 * represents the archive in other ZIP_ calls.
173 * @param zipname archive filename
174 * @param hZip receives handle if archive opened OK
175 * @return status code
177 PR_PUBLIC_API(PRInt32
) ZIP_OpenArchive(const char * zipname
, void** hZip
)
181 /*--- error check args ---*/
183 return ZIP_ERR_PARAM
;
185 /*--- NULL output to prevent use by bozos who don't check errors ---*/
188 /*--- create and open the archive ---*/
189 nsZipArchive
* zip
= new nsZipArchive();
191 return ZIP_ERR_MEMORY
;
193 PRFileDesc
* fd
= PR_Open(zipname
, PR_RDONLY
, 0400);
199 status
= zip
->OpenArchive(fd
);
200 if (status
== ZIP_OK
)
201 *hZip
= static_cast<void*>(zip
);
215 * Tests the integrity of this open zip archive by extracting each
216 * item to memory and performing a CRC check.
218 * @param hZip handle obtained from ZIP_OpenArchive
219 * @return status code (success indicated by ZIP_OK)
221 PR_PUBLIC_API(PRInt32
) ZIP_TestArchive(void *hZip
)
223 /*--- error check args ---*/
225 return ZIP_ERR_PARAM
;
227 nsZipArchive
* zip
= static_cast<nsZipArchive
*>(hZip
);
228 if (zip
->kMagic
!= ZIP_MAGIC
)
229 return ZIP_ERR_PARAM
; /* whatever it is isn't one of ours! */
231 /*--- test the archive ---*/
232 return zip
->Test(NULL
);
239 * closes zip archive and frees memory
240 * @param hZip handle obtained from ZIP_OpenArchive
241 * @return status code
243 PR_PUBLIC_API(PRInt32
) ZIP_CloseArchive(void** hZip
)
245 /*--- error check args ---*/
246 if (hZip
== 0 || *hZip
== 0)
247 return ZIP_ERR_PARAM
;
249 nsZipArchive
* zip
= static_cast<nsZipArchive
*>(*hZip
);
250 if (zip
->kMagic
!= ZIP_MAGIC
)
251 return ZIP_ERR_PARAM
; /* whatever it is isn't one of ours! */
253 /*--- close the archive ---*/
265 * extracts named file from an opened archive
267 * @param hZip handle obtained from ZIP_OpenArchive
268 * @param filename name of file in archive
269 * @param outname filename to extract to
271 PR_PUBLIC_API(PRInt32
) ZIP_ExtractFile(void* hZip
, const char * filename
, const char * outname
)
273 /*--- error check args ---*/
275 return ZIP_ERR_PARAM
;
277 nsZipArchive
* zip
= static_cast<nsZipArchive
*>(hZip
);
278 if (zip
->kMagic
!= ZIP_MAGIC
)
279 return ZIP_ERR_PARAM
; /* whatever it is isn't one of ours! */
281 //-- Find item in archive
282 nsZipItem
* item
= zip
->GetItem(filename
);
286 // Can't extract a directory
287 if (item
->isDirectory
)
288 return ZIP_ERR_PARAM
;
290 // delete any existing file so that we overwrite the file permissions
293 PRFileDesc
* fOut
= PR_Open(outname
, ZFILE_CREATE
, item
->mode
);
297 #if defined(XP_UNIX) && defined(STANDALONE)
298 // When STANDALONE is defined, PR_Open ignores its 3d argument.
299 mode_t msk
= umask(0);
301 chmod(outname
, (item
->mode
| S_IRUSR
) & ~msk
);
304 // ExtractFile also closes the fOut handle and resolves the symlink if needed
305 return zip
->ExtractFile(item
, outname
, fOut
);
313 * Initializes an enumeration of files in the archive
315 * @param hZip handle obtained from ZIP_OpenArchive
316 * @param pattern regexp to match files in archive, the usual shell expressions.
317 * NULL pattern also matches all files, faster than "*"
319 PR_PUBLIC_API(void*) ZIP_FindInit(void* hZip
, const char * pattern
)
321 /*--- error check args ---*/
325 nsZipArchive
* zip
= static_cast<nsZipArchive
*>(hZip
);
326 if (zip
->kMagic
!= ZIP_MAGIC
)
327 return 0; /* whatever it is isn't one of ours! */
329 /*--- initialize the pattern search ---*/
331 PRInt32 rv
= zip
->FindInit(pattern
, &find
);
343 * Puts the next name in the passed buffer. Returns ZIP_ERR_SMALLBUF when
344 * the name is too large for the buffer, and ZIP_ERR_FNF when there are no
345 * more files that match the pattern
347 * @param hFind handle obtained from ZIP_FindInit
348 * @param outbuf buffer to receive next filename
349 * @param bufsize size of allocated buffer
351 PR_PUBLIC_API(PRInt32
) ZIP_FindNext(void* hFind
, char * outbuf
, PRUint16 bufsize
)
355 /*--- error check args ---*/
357 return ZIP_ERR_PARAM
;
359 nsZipFind
* find
= static_cast<nsZipFind
*>(hFind
);
360 if (find
->kMagic
!= ZIPFIND_MAGIC
)
361 return ZIP_ERR_PARAM
; /* whatever it is isn't one of ours! */
363 /*--- return next filename file ---*/
364 const char* itemName
;
365 status
= find
->FindNext(&itemName
);
366 if (status
== ZIP_OK
)
368 PRUint16 namelen
= (PRUint16
)PL_strlen(itemName
);
370 if (bufsize
> namelen
)
372 PL_strcpy(outbuf
, itemName
);
375 status
= ZIP_ERR_SMALLBUF
;
386 * Releases allocated memory associated with the find token
388 * @param hFind handle obtained from ZIP_FindInit
390 PR_PUBLIC_API(PRInt32
) ZIP_FindFree(void* hFind
)
392 /*--- error check args ---*/
394 return ZIP_ERR_PARAM
;
396 nsZipFind
* find
= static_cast<nsZipFind
*>(hFind
);
397 if (find
->kMagic
!= ZIPFIND_MAGIC
)
398 return ZIP_ERR_PARAM
; /* whatever it is isn't one of ours! */
400 /* free the find structure */
406 void ProcessWindowsMessages()
410 while(PeekMessage(&msg
, 0, 0, 0, PM_REMOVE
))
412 TranslateMessage(&msg
);
413 DispatchMessage(&msg
);
418 #else /* STANDALONE */
420 //***********************************************************
421 // Allocators for use with zlib
423 // These are allocators that are performance tuned for
424 // use with zlib. Our use of zlib for every file we read from
425 // the jar file when running navigator, we do these allocation.
430 // alloc 1216 [304x4] max
433 // alloc 1152 [288x4]
443 // The pool will allocate these as:
447 // 1,280 [320x4] - shared by first x4 alloc, 28
448 // 1,280 [320x4] - shared by second and third x4 alloc
454 // And almost all of the file reads happen serially. Hence this
455 // allocator tries to keep one set of memory needed for one file around
456 // and reused the same blocks for other file reads.
458 // The interesting question is when should be free this ?
459 // - memory pressure should be one.
460 // - after startup of navigator
461 // - after startup of mail
462 // In general, this allocator should be enabled before
463 // we startup and disabled after we startup if memory is a concern.
464 //***********************************************************
467 zlibAlloc(void *opaque
, uInt items
, uInt size
)
469 nsRecyclingAllocator
*zallocator
= (nsRecyclingAllocator
*)opaque
;
471 // Bump up x4 allocations
472 PRUint32 realitems
= items
;
473 if (size
== 4 && items
< BY4ALLOC_ITEMS
)
474 realitems
= BY4ALLOC_ITEMS
;
475 return zallocator
->Calloc(realitems
, size
);
478 return calloc(items
, size
);
482 zlibFree(void *opaque
, void *ptr
)
484 nsRecyclingAllocator
*zallocator
= (nsRecyclingAllocator
*)opaque
;
486 zallocator
->Free(ptr
);
491 #endif /* STANDALONE */
493 nsresult
gZlibInit(z_stream
*zs
)
495 memset(zs
, 0, sizeof(z_stream
));
497 //-- ensure we have our zlib allocator for better performance
498 if (!gZlibAllocator
) {
499 gZlibAllocator
= new nsRecyclingAllocator(NBUCKETS
, NS_DEFAULT_RECYCLE_TIMEOUT
, "libjar");
501 if (gZlibAllocator
) {
502 zs
->zalloc
= zlibAlloc
;
503 zs
->zfree
= zlibFree
;
504 zs
->opaque
= gZlibAllocator
;
506 #endif /* STANDALONE */
507 int zerr
= inflateInit2(zs
, -MAX_WBITS
);
508 if (zerr
!= Z_OK
) return ZIP_ERR_MEMORY
;
513 //***********************************************************
514 // nsZipArchive -- public methods
515 //***********************************************************
518 //---------------------------------------------
519 // nsZipArchive::OpenArchive
520 //---------------------------------------------
521 nsresult
nsZipArchive::OpenArchive(PRFileDesc
* fd
)
524 return ZIP_ERR_PARAM
;
527 // Initialize our arena
528 PL_INIT_ARENA_POOL(&mArena
, "ZipArena", ZIP_ARENABLOCKSIZE
);
531 //-- Keep the filedescriptor for further reading...
534 //-- get table of contents for archive
535 return BuildFileList();
538 //---------------------------------------------
539 // nsZipArchive::Test
540 //---------------------------------------------
541 nsresult
nsZipArchive::Test(const char *aEntryName
)
545 if (aEntryName
) // only test specified item
547 currItem
= GetItem(aEntryName
);
550 //-- don't test (synthetic) directory items
551 if (currItem
->isDirectory
)
553 return ExtractFile(currItem
, 0, 0);
556 // test all items in archive
557 for (int i
= 0; i
< ZIP_TABSIZE
; i
++) {
558 for (currItem
= mFiles
[i
]; currItem
; currItem
= currItem
->next
) {
559 //-- don't test (synthetic) directory items
560 if (currItem
->isDirectory
)
562 nsresult rv
= ExtractFile(currItem
, 0, 0);
565 #if defined STANDALONE && defined XP_WIN
566 ProcessWindowsMessages();
574 //---------------------------------------------
575 // nsZipArchive::CloseArchive
576 //---------------------------------------------
577 nsresult
nsZipArchive::CloseArchive()
581 PL_FinishArenaPool(&mArena
);
585 // We don't need to delete each of the nsZipItem as the memory for
586 // the zip item and the filename it holds are both allocated from the Arena.
587 // Hence, destroying the Arena is like destroying all the memory
588 // for all the nsZipItem in one shot. But if the ~nsZipItem is doing
589 // anything more than cleaning up memory, we should start calling it.
590 // Let us also cleanup the mFiles table for re-use on the next 'open' call
591 for (int i
= 0; i
< ZIP_TABSIZE
; i
++) {
595 // delete nsZipItems in table
597 for (int i
= 0; i
< ZIP_TABSIZE
; ++i
)
602 mFiles
[i
] = pItem
->next
;
606 mFiles
[i
] = 0; // make sure we don't double-delete
614 mBuiltSynthetics
= PR_FALSE
;
618 //---------------------------------------------
619 // nsZipArchive::GetItem
620 //---------------------------------------------
621 nsZipItem
* nsZipArchive::GetItem(const char * aEntryName
)
624 //-- If the request is for a directory, make sure that synthetic entries
625 //-- are created for the directories without their own entry.
626 if (!mBuiltSynthetics
) {
627 PRUint32 len
= strlen(aEntryName
);
628 if ((len
> 0) && (aEntryName
[len
-1] == '/')) {
629 if (BuildSynthetics() != ZIP_OK
)
634 nsZipItem
* item
= mFiles
[ HashName(aEntryName
) ];
636 if (!strcmp(aEntryName
, item
->name
))
637 return item
; //-- found it
644 //---------------------------------------------
645 // nsZipArchive::ExtractFile
646 // This extracts the item to the filehandle provided.
647 // If 'aFd' is null, it only tests the extraction.
648 // On extraction error(s) it removes the file.
649 // When needed, it also resolves the symlink.
650 //---------------------------------------------
651 nsresult
nsZipArchive::ExtractFile(nsZipItem
*item
, const char *outname
,
655 return ZIP_ERR_PARAM
;
657 return ZIP_ERR_GENERAL
;
659 // Directory extraction is handled in nsJAR::Extract,
660 // so the item to be extracted should never be a directory
661 PR_ASSERT(!item
->isDirectory
);
663 //-- move to the start of file's data
664 if (SeekToItem(item
, mFd
) != ZIP_OK
)
665 return ZIP_ERR_CORRUPT
;
669 //-- extract the file using the appropriate method
670 switch(item
->compression
)
673 rv
= CopyItemToDisk(item
->size
, item
->crc32
, aFd
);
677 rv
= InflateItem(item
, aFd
);
681 //-- unsupported compression type
682 rv
= ZIP_ERR_UNSUPPORTED
;
685 //-- delete the file on errors, or resolve symlink if needed
690 #if defined(XP_UNIX) || defined(XP_BEOS)
691 else if (item
->isSymlink
)
692 rv
= ResolveSymlink(outname
);
699 //---------------------------------------------
700 // nsZipArchive::FindInit
701 //---------------------------------------------
703 nsZipArchive::FindInit(const char * aPattern
, nsZipFind
**aFind
)
706 return ZIP_ERR_PARAM
;
708 // null out param in case an error happens
711 PRBool regExp
= PR_FALSE
;
714 // Create synthetic directory entries on demand
715 nsresult rv
= BuildSynthetics();
719 // validate the pattern
722 switch (NS_WildCardValid((char*)aPattern
))
725 return ZIP_ERR_PARAM
;
736 // undocumented return value from RegExpValid!
738 return ZIP_ERR_PARAM
;
741 pattern
= PL_strdup(aPattern
);
743 return ZIP_ERR_MEMORY
;
746 *aFind
= new nsZipFind(this, pattern
, regExp
);
749 return ZIP_ERR_MEMORY
;
757 //---------------------------------------------
758 // nsZipFind::FindNext
759 //---------------------------------------------
760 nsresult
nsZipFind::FindNext(const char ** aResult
)
762 if (!mArchive
|| !aResult
)
763 return ZIP_ERR_PARAM
;
767 // we start from last match, look for next
768 while (mSlot
< ZIP_TABSIZE
)
770 // move to next in current chain, or move to new slot
771 mItem
= mItem
? mItem
->next
: mArchive
->mFiles
[mSlot
];
773 PRBool found
= PR_FALSE
;
775 ++mSlot
; // no more in this chain, move to next slot
777 found
= PR_TRUE
; // always match
779 found
= (NS_WildCardMatch(mItem
->name
, mPattern
, PR_FALSE
) == MATCH
);
781 #if defined(STANDALONE) && defined(XP_MAC)
782 // simulate <regexp>* matches
783 found
= (strncmp(mItem
->name
, mPattern
, strlen(mPattern
)) == 0);
785 found
= (PL_strcmp(mItem
->name
, mPattern
) == 0);
789 *aResult
= mItem
->name
;
797 #if defined(XP_UNIX) || defined(XP_BEOS)
798 //---------------------------------------------
800 //---------------------------------------------
801 static nsresult
ResolveSymlink(const char *path
)
803 PRFileDesc
* fIn
= PR_Open(path
, PR_RDONLY
, 0000);
807 char buf
[PATH_MAX
+1];
808 PRInt32 length
= PR_Read(fIn
, (void*)buf
, PATH_MAX
);
812 || ((buf
[length
] = 0, PR_Delete(path
)) != 0)
813 || (symlink(buf
, path
) != 0))
821 //***********************************************************
822 // nsZipArchive -- private implementation
823 //***********************************************************
825 #define BR_BUF_SIZE 1024 /* backward read buffer size */
827 //---------------------------------------------
828 // nsZipArchive::CreateZipItem
829 //---------------------------------------------
830 nsZipItem
* nsZipArchive::CreateZipItem(PRUint16 namelen
)
832 // sizeof(nsZipItem) includes space for name's null byte
834 // Arena allocate the nsZipItem
836 PL_ARENA_ALLOCATE(mem
, &mArena
, sizeof(nsZipItem
)+namelen
);
837 return (nsZipItem
*)mem
;
839 return (nsZipItem
*)malloc(sizeof(nsZipItem
)+namelen
);
843 //---------------------------------------------
844 // nsZipArchive::BuildFileList
845 //---------------------------------------------
846 nsresult
nsZipArchive::BuildFileList()
848 PRUint8 buf
[4*BR_BUF_SIZE
];
850 //-----------------------------------------------------------------------
851 // locate the central directory via the End record
852 //-----------------------------------------------------------------------
854 //-- get archive size using end pos
855 PRInt32 pos
= PR_Seek(mFd
, 0, PR_SEEK_END
);
859 if (pos
|| ((pos
= ftell(mFd
)) <= 0))
861 return ZIP_ERR_CORRUPT
;
863 PRBool bEndsigFound
= PR_FALSE
;
864 while (!bEndsigFound
)
866 //-- read backwards in 1K-sized chunks (unless file is less than 1K)
867 PRInt32 bufsize
= pos
> BR_BUF_SIZE
? BR_BUF_SIZE
: pos
;
870 if (!ZIP_Seek(mFd
, pos
, PR_SEEK_SET
))
871 return ZIP_ERR_CORRUPT
;
873 if (PR_Read(mFd
, buf
, bufsize
) != (READTYPE
)bufsize
)
874 return ZIP_ERR_CORRUPT
;
877 PRUint8
*endp
= buf
+ bufsize
;
878 for (endp
-= ZIPEND_SIZE
; endp
>= buf
; endp
--)
880 if (xtolong(endp
) == ENDSIG
)
882 //-- Seek to start of central directory
883 PRInt32 central
= xtolong(((ZipEnd
*) endp
)->offset_central_dir
);
884 if (!ZIP_Seek(mFd
, central
, PR_SEEK_SET
))
885 return ZIP_ERR_CORRUPT
;
887 bEndsigFound
= PR_TRUE
;
896 //-- We're at the beginning of the file, and still no sign
897 //-- of the end signature. File must be corrupted!
898 return ZIP_ERR_CORRUPT
;
900 //-- backward read must overlap ZipEnd length
903 } /* while looking for end signature */
906 //-------------------------------------------------------
907 // read the central directory headers
908 //-------------------------------------------------------
909 PRInt32 byteCount
= PR_Read(mFd
, &buf
, sizeof(buf
));
911 PRUint32 sig
= xtolong(buf
);
912 while (sig
== CENTRALSIG
) {
913 //-- make sure we've read enough
914 if (byteCount
- pos
< ZIPCENTRAL_SIZE
)
915 return ZIP_ERR_CORRUPT
;
917 //-------------------------------------------------------
918 // read the fixed-size data
919 //-------------------------------------------------------
920 ZipCentral
* central
= (ZipCentral
*)(buf
+pos
);
922 PRUint16 namelen
= xtoint(central
->filename_len
);
923 PRUint16 extralen
= xtoint(central
->extrafield_len
);
924 PRUint16 commentlen
= xtoint(central
->commentfield_len
);
926 //-- sanity check variable sizes and refuse to deal with
927 //-- anything too big: it's likely a corrupt archive
928 if (namelen
> BR_BUF_SIZE
|| extralen
> BR_BUF_SIZE
|| commentlen
> 2*BR_BUF_SIZE
)
929 return ZIP_ERR_CORRUPT
;
931 nsZipItem
* item
= CreateZipItem(namelen
);
933 return ZIP_ERR_MEMORY
;
935 item
->headerOffset
= xtolong(central
->localhdr_offset
);
936 item
->dataOffset
= 0;
937 item
->size
= xtolong(central
->size
);
938 item
->realsize
= xtolong(central
->orglen
);
939 item
->crc32
= xtolong(central
->crc32
);
940 item
->time
= xtoint(central
->time
);
941 item
->date
= xtoint(central
->date
);
942 item
->isSynthetic
= PR_FALSE
;
943 item
->hasDataOffset
= PR_FALSE
;
945 PRUint16 compression
= xtoint(central
->method
);
946 item
->compression
= (compression
< UNSUPPORTED
) ? (PRUint8
)compression
949 item
->mode
= ExtractMode(central
->external_attributes
);
950 #if defined(XP_UNIX) || defined(XP_BEOS)
951 // Check if item is a symlink
952 item
->isSymlink
= IsSymlink(central
->external_attributes
);
955 pos
+= ZIPCENTRAL_SIZE
;
957 //-------------------------------------------------------
958 // Make sure that remainder of this record (name, comments, extra)
959 // and the next ZipCentral is all in the buffer
960 //-------------------------------------------------------
961 PRInt32 leftover
= byteCount
- pos
;
962 if (leftover
< (namelen
+ extralen
+ commentlen
+ ZIPCENTRAL_SIZE
)) {
963 //-- not enough data left to process at top of loop.
964 //-- move leftover and read more
965 memcpy(buf
, buf
+pos
, leftover
);
966 byteCount
= leftover
+ PR_Read(mFd
, buf
+leftover
, sizeof(buf
)-leftover
);
969 if (byteCount
< (namelen
+ extralen
+ commentlen
+ sizeof(sig
))) {
971 return ZIP_ERR_CORRUPT
;
975 //-------------------------------------------------------
977 //-------------------------------------------------------
978 memcpy(item
->name
, buf
+pos
, namelen
);
979 item
->name
[namelen
] = 0;
981 //-- an item whose name ends with '/' is a directory
982 item
->isDirectory
= ('/' == item
->name
[namelen
- 1]);
984 //-- add item to file table
985 //-- note that an explicit entry for a directory will override
986 //-- a fake entry created for that directory (as in the case
987 //-- of processing foo/bar.txt and then foo/) -- this will
988 //-- preserve an explicit directory's metadata at the cost of
989 //-- an extra nsZipItem (and that only happens if we process a
990 //-- file inside that directory before processing the directory
992 PRUint32 hash
= HashName(item
->name
);
993 item
->next
= mFiles
[hash
];
996 //-------------------------------------------------------
997 // set up to process the next item at the top of loop
998 //-------------------------------------------------------
999 pos
+= namelen
+ extralen
+ commentlen
;
1000 sig
= xtolong(buf
+pos
);
1001 } /* while reading central directory records */
1004 return ZIP_ERR_CORRUPT
;
1009 //---------------------------------------------
1010 // nsZipArchive::BuildSynthetics
1011 //---------------------------------------------
1012 nsresult
nsZipArchive::BuildSynthetics()
1014 if (mBuiltSynthetics
)
1016 mBuiltSynthetics
= PR_TRUE
;
1018 // Create synthetic entries for any missing directories.
1019 // Do this when all ziptable has scanned to prevent double entries.
1020 for (int i
= 0; i
< ZIP_TABSIZE
; ++i
)
1022 for (nsZipItem
* item
= mFiles
[i
]; item
!= 0; item
= item
->next
)
1024 if (item
->isSynthetic
)
1027 //-- add entries for directories in the current item's path
1028 //-- go from end to beginning, because then we can stop trying
1029 //-- to create diritems if we find that the diritem we want to
1030 //-- create already exists
1031 //-- start just before the last char so as to not add the item
1032 //-- twice if it's a directory
1033 PRUint16 namelen
= strlen(item
->name
);
1034 for (char* p
= item
->name
+ namelen
- 2; p
>= item
->name
; p
--)
1039 // See whether we need to create any more implicit directories,
1040 // because if we don't we can avoid a lot of work.
1041 // We can even avoid (de)allocating space for a bogus dirname with
1042 // a little trickery -- save the char at item->name[dirnamelen],
1043 // set it to 0, compare the strings, and restore the saved
1045 const PRUint32 dirnamelen
= p
+ 1 - item
->name
;
1046 const char savedChar
= item
->name
[dirnamelen
];
1047 item
->name
[dirnamelen
] = 0;
1049 // Is the directory in the file table?
1050 PRUint32 hash
= HashName(item
->name
);
1051 PRBool found
= PR_FALSE
;
1052 for (nsZipItem
* zi
= mFiles
[hash
]; zi
!= NULL
; zi
= zi
->next
)
1054 if (0 == strcmp(item
->name
, zi
->name
))
1056 // we've already added this dir and all its parents
1062 // restore the char immediately
1063 item
->name
[dirnamelen
] = savedChar
;
1065 // if the directory was found, break out of the directory
1066 // creation loop now that we know all implicit directories
1067 // are there -- otherwise, start creating the zip item
1071 nsZipItem
* diritem
= CreateZipItem(dirnamelen
);
1073 return ZIP_ERR_MEMORY
;
1075 memcpy(diritem
->name
, item
->name
, dirnamelen
);
1076 diritem
->name
[dirnamelen
] = 0;
1078 diritem
->isDirectory
= PR_TRUE
;
1079 diritem
->isSynthetic
= PR_TRUE
;
1080 diritem
->compression
= STORED
;
1081 diritem
->size
= diritem
->realsize
= 0;
1083 diritem
->mode
= 0755;
1085 // Set an obviously wrong last-modified date/time, because
1086 // finding something more accurate like the most recent
1087 // last-modified date/time of the dir's contents is a lot
1088 // of effort. The date/time corresponds to 1980-01-01 00:00.
1090 diritem
->date
= 1 + (1 << 5) + (0 << 9);
1092 // add diritem to the file table
1093 diritem
->next
= mFiles
[hash
];
1094 mFiles
[hash
] = diritem
;
1095 } /* end processing of dirs in item's name */
1102 //---------------------------------------------
1103 // nsZipArchive::SeekToItem
1104 //---------------------------------------------
1105 nsresult
nsZipArchive::SeekToItem(nsZipItem
* aItem
, PRFileDesc
* aFd
)
1109 //-- the first time an item is used we need to calculate its offset
1110 if (!aItem
->hasDataOffset
)
1112 //-- read local header to get variable length values and calculate
1113 //-- the real data offset
1115 //-- NOTE: extralen is different in central header and local header
1116 //-- for archives created using the Unix "zip" utility. To set
1117 //-- the offset accurately we need the _local_ extralen.
1118 if (!ZIP_Seek(aFd
, aItem
->headerOffset
, PR_SEEK_SET
))
1119 return ZIP_ERR_CORRUPT
;
1122 if ((PR_Read(aFd
, (char*)&Local
, ZIPLOCAL_SIZE
) != (READTYPE
) ZIPLOCAL_SIZE
) ||
1123 (xtolong(Local
.signature
) != LOCALSIG
))
1125 //-- read error or local header not found
1126 return ZIP_ERR_CORRUPT
;
1129 aItem
->dataOffset
= aItem
->headerOffset
+
1131 xtoint(Local
.filename_len
) +
1132 xtoint(Local
.extrafield_len
);
1133 aItem
->hasDataOffset
= PR_TRUE
;
1136 //-- move to start of file in archive
1137 if (!ZIP_Seek(aFd
, aItem
->dataOffset
, PR_SEEK_SET
))
1138 return ZIP_ERR_CORRUPT
;
1143 //---------------------------------------------
1144 // nsZipArchive::CopyItemToDisk
1145 //---------------------------------------------
1147 nsZipArchive::CopyItemToDisk(PRUint32 itemSize
, PRUint32 itemCrc
, PRFileDesc
* outFD
)
1149 * This function copies an archive item to disk, to the
1150 * file specified by outFD. If outFD is zero, the extracted data is
1151 * not written, only checked for CRC, so this is in effect same as 'Test'.
1154 PRUint32 chunk
, pos
, crc
;
1155 char buf
[ZIP_BUFLEN
];
1158 crc
= crc32(0L, Z_NULL
, 0);
1160 //-- copy chunks until file is done
1161 for (pos
= 0; pos
< itemSize
; pos
+= chunk
)
1163 chunk
= (itemSize
- pos
< ZIP_BUFLEN
) ? (itemSize
- pos
) : ZIP_BUFLEN
;
1165 if (PR_Read(mFd
, buf
, chunk
) != (READTYPE
)chunk
)
1167 //-- unexpected end of data in archive
1168 return ZIP_ERR_CORRUPT
;
1171 //-- incrementally update crc32
1172 crc
= crc32(crc
, (const unsigned char*)buf
, chunk
);
1174 if (outFD
&& PR_Write(outFD
, buf
, chunk
) < (READTYPE
)chunk
)
1176 //-- Couldn't write all the data (disk full?)
1177 return ZIP_ERR_DISK
;
1183 return ZIP_ERR_CORRUPT
;
1189 //---------------------------------------------
1190 // nsZipArchive::InflateItem
1191 //---------------------------------------------
1192 nsresult
nsZipArchive::InflateItem(const nsZipItem
* aItem
, PRFileDesc
* outFD
)
1194 * This function inflates an archive item to disk, to the
1195 * file specified by outFD. If outFD is zero, the extracted data is
1196 * not written, only checked for CRC, so this is in effect same as 'Test'.
1201 //-- allocate deflation buffers
1202 Bytef inbuf
[ZIP_BUFLEN
];
1203 Bytef outbuf
[ZIP_BUFLEN
];
1205 //-- set up the inflate
1207 nsresult status
= gZlibInit(&zs
);
1208 if (status
!= ZIP_OK
)
1209 return ZIP_ERR_GENERAL
;
1212 zs
.next_out
= outbuf
;
1213 zs
.avail_out
= ZIP_BUFLEN
;
1215 PRUint32 size
= aItem
->size
;
1216 PRUint32 outpos
= 0;
1217 PRUint32 crc
= crc32(0L, Z_NULL
, 0);
1219 while (zerr
== Z_OK
)
1221 PRBool bRead
= PR_FALSE
;
1222 PRBool bWrote
= PR_FALSE
;
1224 if (zs
.avail_in
== 0 && zs
.total_in
< size
)
1226 //-- no data to inflate yet still more in file:
1227 //-- read another chunk of compressed data
1228 PRUint32 chunk
= (size
-zs
.total_in
< ZIP_BUFLEN
) ? size
-zs
.total_in
: ZIP_BUFLEN
;
1230 if (PR_Read(mFd
, inbuf
, chunk
) != (READTYPE
)chunk
)
1232 //-- unexpected end of data
1233 status
= ZIP_ERR_CORRUPT
;
1238 zs
.avail_in
= chunk
;
1242 if (zs
.avail_out
== 0)
1244 //-- write inflated buffer to disk and make space
1245 if (outFD
&& PR_Write(outFD
, outbuf
, ZIP_BUFLEN
) < ZIP_BUFLEN
)
1247 //-- Couldn't write all the data (disk full?)
1248 status
= ZIP_ERR_DISK
;
1252 outpos
= zs
.total_out
;
1253 zs
.next_out
= outbuf
;
1254 zs
.avail_out
= ZIP_BUFLEN
;
1260 Bytef
* old_next_out
= zs
.next_out
;
1262 zerr
= inflate(&zs
, Z_PARTIAL_FLUSH
);
1264 //-- incrementally update crc32
1265 crc
= crc32(crc
, (const unsigned char*)old_next_out
, zs
.next_out
- old_next_out
);
1268 zerr
= Z_STREAM_END
;
1270 #if defined STANDALONE && defined XP_WIN
1271 ProcessWindowsMessages();
1276 if ((status
== ZIP_OK
) && (crc
!= aItem
->crc32
))
1278 status
= ZIP_ERR_CORRUPT
;
1282 //-- write last inflated bit to disk
1283 if (zerr
== Z_STREAM_END
&& outpos
< zs
.total_out
)
1285 PRUint32 chunk
= zs
.total_out
- outpos
;
1286 if (outFD
&& PR_Write(outFD
, outbuf
, chunk
) < (READTYPE
)chunk
)
1287 status
= ZIP_ERR_DISK
;
1290 //-- convert zlib error to return value
1291 if (status
== ZIP_OK
&& zerr
!= Z_OK
&& zerr
!= Z_STREAM_END
)
1293 status
= (zerr
== Z_MEM_ERROR
) ? ZIP_ERR_MEMORY
: ZIP_ERR_CORRUPT
;
1296 //-- if found no errors make sure we've converted the whole thing
1297 PR_ASSERT(status
!= ZIP_OK
|| zs
.total_in
== aItem
->size
);
1298 PR_ASSERT(status
!= ZIP_OK
|| zs
.total_out
== aItem
->realsize
);
1301 //-- free zlib internal state
1307 //------------------------------------------
1308 // nsZipArchive constructor and destructor
1309 //------------------------------------------
1311 nsZipArchive::nsZipArchive() :
1316 mBuiltSynthetics(PR_FALSE
)
1318 MOZ_COUNT_CTOR(nsZipArchive
);
1320 // initialize the table to NULL
1321 memset(mFiles
, 0, sizeof mFiles
);
1324 nsZipArchive::~nsZipArchive()
1328 MOZ_COUNT_DTOR(nsZipArchive
);
1332 //------------------------------------------
1333 // nsZipFind constructor and destructor
1334 //------------------------------------------
1336 nsZipFind::nsZipFind(nsZipArchive
* aZip
, char* aPattern
, PRBool aRegExp
) :
1338 kMagic(ZIPFIND_MAGIC
),
1346 MOZ_COUNT_CTOR(nsZipFind
);
1349 nsZipFind::~nsZipFind()
1351 PR_FREEIF(mPattern
);
1353 MOZ_COUNT_DTOR(nsZipFind
);
1356 //------------------------------------------
1358 //------------------------------------------
1363 * returns a hash key for the entry name
1365 static PRUint32
HashName(const char* aName
)
1367 PR_ASSERT(aName
!= 0);
1370 for (PRUint8
* c
= (PRUint8
*)aName
; *c
!= 0; c
++) {
1374 return (val
% ZIP_TABSIZE
);
1380 * Converts a two byte ugly endianed integer
1381 * to our platform's integer.
1383 static PRUint16
xtoint (unsigned char *ii
)
1385 return (PRUint16
) ((ii
[0]) | (ii
[1] << 8));
1391 * Converts a four byte ugly endianed integer
1392 * to our platform's integer.
1394 static PRUint32
xtolong (unsigned char *ll
)
1396 return (PRUint32
)( (ll
[0] << 0) |
1405 * Extracts bits 17-24 from a 32-bit unsigned long
1406 * representation of the external attributes field.
1407 * Subsequently it tacks on the implicit user-read
1410 static PRUint16
ExtractMode(unsigned char *ll
)
1412 return ((PRUint16
)(ll
[2])) | 0x0100;
1415 #if defined(XP_UNIX) || defined(XP_BEOS)
1418 * Return true if the attributes are for a symbolic link
1422 static PRBool
IsSymlink(unsigned char *ll
)
1424 return ((xtoint(ll
+2) & S_IFMT
) == S_IFLNK
);