b=450088 backing out (new reftest failed)
[wine-gecko.git] / modules / libjar / nsZipArchive.cpp
blobe022e36e96164f18ffac0a7a6a01875ea7da8395
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
13 * License.
15 * The Original Code is Mozilla Communicator client code, released
16 * March 31, 1998.
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.
23 * Contributor(s):
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.
52 #ifndef STANDALONE
54 #include "nsWildCard.h"
55 #include "nscore.h"
56 #include "prmem.h"
57 #include "prio.h"
58 #include "plstr.h"
59 #include "prlog.h"
60 #define ZFILE_CREATE PR_WRONLY | PR_CREATE_FILE
61 #define READTYPE PRInt32
62 #include "zlib.h"
63 #include "nsISupportsUtils.h"
64 #include "nsRecyclingAllocator.h"
66 /**
67 * Globals
69 * Global allocator used with zlib. Destroyed in module shutdown.
71 #define NBUCKETS 6
72 #define BY4ALLOC_ITEMS 320
73 nsRecyclingAllocator *gZlibAllocator = NULL;
75 // For placement new used for arena allocations of zip file list
76 #include NEW_H
77 #define ZIP_ARENABLOCKSIZE (1*1024)
79 #else /* STANDALONE */
81 #ifdef XP_WIN
82 #include "windows.h"
83 #endif
85 #undef MOZILLA_CLIENT // undoes prtypes damage in zlib.h
86 #define ZFILE_CREATE "wb"
87 #define READTYPE PRUint32
88 #include "zlib.h"
89 #undef PR_PUBLIC_API
90 #include "zipstub.h"
92 #ifdef XP_MAC
93 #include <string.h>
94 #include <stdlib.h>
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);
102 return dup;
104 #endif
106 #ifdef WINCE
107 int remove(const char* inPath)
109 unsigned short wPath[MAX_PATH];
110 MultiByteToWideChar(CP_ACP,
112 inPath,
114 wPath,
115 MAX_PATH);
117 if(FALSE != DeleteFileW(wPath))
118 return 0;
119 return -1;
121 #endif
123 #endif /* STANDALONE */
125 #ifdef XP_UNIX
126 #include <sys/types.h>
127 #include <sys/stat.h>
128 #include <limits.h>
129 #include <unistd.h>
130 #elif defined(XP_WIN) || defined(XP_OS2)
131 #include <io.h>
132 #elif defined(XP_BEOS)
133 #include <unistd.h>
134 #endif
136 #ifndef XP_UNIX /* we need some constants defined in limits.h and unistd.h */
137 # ifndef S_IFMT
138 # define S_IFMT 0170000
139 # endif
140 # ifndef S_IFLNK
141 # define S_IFLNK 0120000
142 # endif
143 # ifndef PATH_MAX
144 # define PATH_MAX 1024
145 # endif
146 #endif /* XP_UNIX */
148 #include "zipfile.h"
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);
159 #endif
161 /*---------------------------------------------
162 * C API wrapper for nsZipArchive
163 *--------------------------------------------*/
165 #ifdef STANDALONE
168 * ZIP_OpenArchive
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)
179 PRInt32 status;
181 /*--- error check args ---*/
182 if (hZip == 0)
183 return ZIP_ERR_PARAM;
185 /*--- NULL output to prevent use by bozos who don't check errors ---*/
186 *hZip = 0;
188 /*--- create and open the archive ---*/
189 nsZipArchive* zip = new nsZipArchive();
190 if (zip == 0)
191 return ZIP_ERR_MEMORY;
193 PRFileDesc * fd = PR_Open(zipname, PR_RDONLY, 0400);
194 if (!fd) {
195 delete zip;
196 return ZIP_ERR_DISK;
199 status = zip->OpenArchive(fd);
200 if (status == ZIP_OK)
201 *hZip = static_cast<void*>(zip);
202 else {
203 delete zip;
204 PR_Close(fd);
207 return status;
213 * ZIP_TestArchive
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 ---*/
224 if (hZip == 0)
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);
237 * ZIP_CloseArchive
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 ---*/
254 *hZip = 0;
255 delete zip;
257 return ZIP_OK;
263 * ZIP_ExtractFile
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 ---*/
274 if (hZip == 0)
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);
283 if (!item)
284 return ZIP_ERR_FNF;
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
291 PR_Delete(outname);
293 PRFileDesc* fOut = PR_Open(outname, ZFILE_CREATE, item->mode);
294 if (!fOut)
295 return ZIP_ERR_DISK;
297 #if defined(XP_UNIX) && defined(STANDALONE)
298 // When STANDALONE is defined, PR_Open ignores its 3d argument.
299 mode_t msk = umask(0);
300 umask(msk);
301 chmod(outname, (item->mode | S_IRUSR) & ~msk);
302 #endif
304 // ExtractFile also closes the fOut handle and resolves the symlink if needed
305 return zip->ExtractFile(item, outname, fOut);
311 * ZIP_FindInit
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 ---*/
322 if (hZip == 0)
323 return 0;
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 ---*/
330 nsZipFind* find;
331 PRInt32 rv = zip->FindInit(pattern, &find);
332 if (rv != ZIP_OK)
333 find = NULL;
335 return find;
341 * ZIP_FindNext
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)
353 PRInt32 status;
355 /*--- error check args ---*/
356 if (hFind == 0)
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);
374 else
375 status = ZIP_ERR_SMALLBUF;
378 return status;
384 * ZIP_FindFree
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 ---*/
393 if (hFind == 0)
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 */
401 delete find;
402 return ZIP_OK;
405 #if defined XP_WIN
406 void ProcessWindowsMessages()
408 MSG msg;
410 while(PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
412 TranslateMessage(&msg);
413 DispatchMessage(&msg);
416 #endif /* XP_WIN */
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.
426 // alloc 24
427 // alloc 64
428 // alloc 11520
429 // alloc 32768
430 // alloc 1216 [304x4] max
431 // alloc 76 [19x4]
432 // free 76 [19x4]
433 // alloc 1152 [288x4]
434 // free 1152 [288x4]
435 // free 1216 [304x4]
436 // alloc 28
437 // free 28
438 // free 32768
439 // free 11520
440 // free 64
441 // free 24
443 // The pool will allocate these as:
445 // 32,768
446 // 11,520
447 // 1,280 [320x4] - shared by first x4 alloc, 28
448 // 1,280 [320x4] - shared by second and third x4 alloc
449 // 64
450 // 24
451 // ------
452 // 46,936
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 //***********************************************************
466 static void *
467 zlibAlloc(void *opaque, uInt items, uInt size)
469 nsRecyclingAllocator *zallocator = (nsRecyclingAllocator *)opaque;
470 if (zallocator) {
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);
477 else
478 return calloc(items, size);
481 static void
482 zlibFree(void *opaque, void *ptr)
484 nsRecyclingAllocator *zallocator = (nsRecyclingAllocator *)opaque;
485 if (zallocator)
486 zallocator->Free(ptr);
487 else
488 free(ptr);
489 return;
491 #endif /* STANDALONE */
493 nsresult gZlibInit(z_stream *zs)
495 memset(zs, 0, sizeof(z_stream));
496 #ifndef STANDALONE
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;
510 return ZIP_OK;
513 //***********************************************************
514 // nsZipArchive -- public methods
515 //***********************************************************
518 //---------------------------------------------
519 // nsZipArchive::OpenArchive
520 //---------------------------------------------
521 nsresult nsZipArchive::OpenArchive(PRFileDesc * fd)
523 if (!fd)
524 return ZIP_ERR_PARAM;
526 #ifndef STANDALONE
527 // Initialize our arena
528 PL_INIT_ARENA_POOL(&mArena, "ZipArena", ZIP_ARENABLOCKSIZE);
529 #endif
531 //-- Keep the filedescriptor for further reading...
532 mFd = fd;
534 //-- get table of contents for archive
535 return BuildFileList();
538 //---------------------------------------------
539 // nsZipArchive::Test
540 //---------------------------------------------
541 nsresult nsZipArchive::Test(const char *aEntryName)
543 nsZipItem* currItem;
545 if (aEntryName) // only test specified item
547 currItem = GetItem(aEntryName);
548 if (!currItem)
549 return ZIP_ERR_FNF;
550 //-- don't test (synthetic) directory items
551 if (currItem->isDirectory)
552 return ZIP_OK;
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)
561 continue;
562 nsresult rv = ExtractFile(currItem, 0, 0);
563 if (rv != ZIP_OK)
564 return rv;
565 #if defined STANDALONE && defined XP_WIN
566 ProcessWindowsMessages();
567 #endif
571 return ZIP_OK;
574 //---------------------------------------------
575 // nsZipArchive::CloseArchive
576 //---------------------------------------------
577 nsresult nsZipArchive::CloseArchive()
579 #ifndef STANDALONE
580 if (mFd) {
581 PL_FinishArenaPool(&mArena);
584 // CAUTION:
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++) {
592 mFiles[i] = 0;
594 #else
595 // delete nsZipItems in table
596 nsZipItem* pItem;
597 for (int i = 0; i < ZIP_TABSIZE; ++i)
599 pItem = mFiles[i];
600 while (pItem != 0)
602 mFiles[i] = pItem->next;
603 free(pItem);
604 pItem = mFiles[i];
606 mFiles[i] = 0; // make sure we don't double-delete
608 #endif
610 if (mFd) {
611 PR_Close(mFd);
612 mFd = 0;
614 mBuiltSynthetics = PR_FALSE;
615 return ZIP_OK;
618 //---------------------------------------------
619 // nsZipArchive::GetItem
620 //---------------------------------------------
621 nsZipItem* nsZipArchive::GetItem(const char * aEntryName)
623 if (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)
630 return 0;
634 nsZipItem* item = mFiles[ HashName(aEntryName) ];
635 while (item) {
636 if (!strcmp(aEntryName, item->name))
637 return item; //-- found it
638 item = item->next;
641 return 0;
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,
652 PRFileDesc* aFd)
654 if (!item)
655 return ZIP_ERR_PARAM;
656 if (!mFd)
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;
667 nsresult rv;
669 //-- extract the file using the appropriate method
670 switch(item->compression)
672 case STORED:
673 rv = CopyItemToDisk(item->size, item->crc32, aFd);
674 break;
676 case DEFLATED:
677 rv = InflateItem(item, aFd);
678 break;
680 default:
681 //-- unsupported compression type
682 rv = ZIP_ERR_UNSUPPORTED;
685 //-- delete the file on errors, or resolve symlink if needed
686 if (aFd) {
687 PR_Close(aFd);
688 if (rv != ZIP_OK)
689 PR_Delete(outname);
690 #if defined(XP_UNIX) || defined(XP_BEOS)
691 else if (item->isSymlink)
692 rv = ResolveSymlink(outname);
693 #endif
696 return rv;
699 //---------------------------------------------
700 // nsZipArchive::FindInit
701 //---------------------------------------------
702 PRInt32
703 nsZipArchive::FindInit(const char * aPattern, nsZipFind **aFind)
705 if (!aFind)
706 return ZIP_ERR_PARAM;
708 // null out param in case an error happens
709 *aFind = NULL;
711 PRBool regExp = PR_FALSE;
712 char* pattern = 0;
714 // Create synthetic directory entries on demand
715 nsresult rv = BuildSynthetics();
716 if (rv != ZIP_OK)
717 return rv;
719 // validate the pattern
720 if (aPattern)
722 switch (NS_WildCardValid((char*)aPattern))
724 case INVALID_SXP:
725 return ZIP_ERR_PARAM;
727 case NON_SXP:
728 regExp = PR_FALSE;
729 break;
731 case VALID_SXP:
732 regExp = PR_TRUE;
733 break;
735 default:
736 // undocumented return value from RegExpValid!
737 PR_ASSERT(PR_FALSE);
738 return ZIP_ERR_PARAM;
741 pattern = PL_strdup(aPattern);
742 if (!pattern)
743 return ZIP_ERR_MEMORY;
746 *aFind = new nsZipFind(this, pattern, regExp);
747 if (!*aFind) {
748 PR_FREEIF(pattern);
749 return ZIP_ERR_MEMORY;
752 return ZIP_OK;
757 //---------------------------------------------
758 // nsZipFind::FindNext
759 //---------------------------------------------
760 nsresult nsZipFind::FindNext(const char ** aResult)
762 if (!mArchive || !aResult)
763 return ZIP_ERR_PARAM;
765 *aResult = 0;
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;
774 if (!mItem)
775 ++mSlot; // no more in this chain, move to next slot
776 else if (!mPattern)
777 found = PR_TRUE; // always match
778 else if (mRegExp)
779 found = (NS_WildCardMatch(mItem->name, mPattern, PR_FALSE) == MATCH);
780 else
781 #if defined(STANDALONE) && defined(XP_MAC)
782 // simulate <regexp>* matches
783 found = (strncmp(mItem->name, mPattern, strlen(mPattern)) == 0);
784 #else
785 found = (PL_strcmp(mItem->name, mPattern) == 0);
786 #endif
788 if (found) {
789 *aResult = mItem->name;
790 return ZIP_OK;
794 return ZIP_ERR_FNF;
797 #if defined(XP_UNIX) || defined(XP_BEOS)
798 //---------------------------------------------
799 // ResolveSymlink
800 //---------------------------------------------
801 static nsresult ResolveSymlink(const char *path)
803 PRFileDesc * fIn = PR_Open(path, PR_RDONLY, 0000);
804 if (!fIn)
805 return ZIP_ERR_DISK;
807 char buf[PATH_MAX+1];
808 PRInt32 length = PR_Read(fIn, (void*)buf, PATH_MAX);
809 PR_Close(fIn);
811 if ( (length <= 0)
812 || ((buf[length] = 0, PR_Delete(path)) != 0)
813 || (symlink(buf, path) != 0))
815 return ZIP_ERR_DISK;
817 return ZIP_OK;
819 #endif
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
833 #ifndef STANDALONE
834 // Arena allocate the nsZipItem
835 void *mem;
836 PL_ARENA_ALLOCATE(mem, &mArena, sizeof(nsZipItem)+namelen);
837 return (nsZipItem*)mem;
838 #else
839 return (nsZipItem*)malloc(sizeof(nsZipItem)+namelen);
840 #endif
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);
856 #ifndef STANDALONE
857 if (pos <= 0)
858 #else
859 if (pos || ((pos = ftell(mFd)) <= 0))
860 #endif
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;
868 pos -= bufsize;
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;
876 //-- scan for ENDSIG
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;
888 break;
892 if (bEndsigFound)
893 break;
895 if (pos <= 0)
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
901 pos += ZIPEND_SIZE;
903 } /* while looking for end signature */
906 //-------------------------------------------------------
907 // read the central directory headers
908 //-------------------------------------------------------
909 PRInt32 byteCount = PR_Read(mFd, &buf, sizeof(buf));
910 pos = 0;
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);
932 if (!item)
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
947 : UNSUPPORTED;
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);
953 #endif
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);
967 pos = 0;
969 if (byteCount < (namelen + extralen + commentlen + sizeof(sig))) {
970 // truncated file
971 return ZIP_ERR_CORRUPT;
975 //-------------------------------------------------------
976 // get the item name
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
991 //-- entry itself)
992 PRUint32 hash = HashName(item->name);
993 item->next = mFiles[hash];
994 mFiles[hash] = item;
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 */
1003 if (sig != ENDSIG)
1004 return ZIP_ERR_CORRUPT;
1006 return ZIP_OK;
1009 //---------------------------------------------
1010 // nsZipArchive::BuildSynthetics
1011 //---------------------------------------------
1012 nsresult nsZipArchive::BuildSynthetics()
1014 if (mBuiltSynthetics)
1015 return ZIP_OK;
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)
1025 continue;
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--)
1036 if ('/' != *p)
1037 continue;
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
1044 // char when done
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
1057 found = PR_TRUE;
1058 break;
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
1068 if (found)
1069 break;
1071 nsZipItem* diritem = CreateZipItem(dirnamelen);
1072 if (!diritem)
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;
1082 diritem->crc32 = 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.
1089 diritem->time = 0;
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 */
1098 return ZIP_OK;
1102 //---------------------------------------------
1103 // nsZipArchive::SeekToItem
1104 //---------------------------------------------
1105 nsresult nsZipArchive::SeekToItem(nsZipItem* aItem, PRFileDesc* aFd)
1107 PR_ASSERT (aItem);
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
1114 //--
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;
1121 ZipLocal Local;
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 +
1130 ZIPLOCAL_SIZE +
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;
1140 return ZIP_OK;
1143 //---------------------------------------------
1144 // nsZipArchive::CopyItemToDisk
1145 //---------------------------------------------
1146 nsresult
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];
1157 //-- initialize crc
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;
1181 //-- verify crc32
1182 if (crc != itemCrc)
1183 return ZIP_ERR_CORRUPT;
1185 return ZIP_OK;
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'.
1199 PR_ASSERT(aItem);
1201 //-- allocate deflation buffers
1202 Bytef inbuf[ZIP_BUFLEN];
1203 Bytef outbuf[ZIP_BUFLEN];
1205 //-- set up the inflate
1206 z_stream zs;
1207 nsresult status = gZlibInit(&zs);
1208 if (status != ZIP_OK)
1209 return ZIP_ERR_GENERAL;
1211 //-- inflate loop
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);
1218 int zerr = Z_OK;
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;
1234 break;
1237 zs.next_in = inbuf;
1238 zs.avail_in = chunk;
1239 bRead = PR_TRUE;
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;
1249 break;
1252 outpos = zs.total_out;
1253 zs.next_out = outbuf;
1254 zs.avail_out = ZIP_BUFLEN;
1255 bWrote = PR_TRUE;
1258 if(bRead || bWrote)
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);
1267 else
1268 zerr = Z_STREAM_END;
1270 #if defined STANDALONE && defined XP_WIN
1271 ProcessWindowsMessages();
1272 #endif
1273 } // while
1275 //-- verify crc32
1276 if ((status == ZIP_OK) && (crc != aItem->crc32))
1278 status = ZIP_ERR_CORRUPT;
1279 goto cleanup;
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);
1300 cleanup:
1301 //-- free zlib internal state
1302 inflateEnd(&zs);
1304 return status;
1307 //------------------------------------------
1308 // nsZipArchive constructor and destructor
1309 //------------------------------------------
1311 nsZipArchive::nsZipArchive() :
1312 #ifdef STANDALONE
1313 kMagic(ZIP_MAGIC),
1314 #endif
1315 mFd(0),
1316 mBuiltSynthetics(PR_FALSE)
1318 MOZ_COUNT_CTOR(nsZipArchive);
1320 // initialize the table to NULL
1321 memset(mFiles, 0, sizeof mFiles);
1324 nsZipArchive::~nsZipArchive()
1326 CloseArchive();
1328 MOZ_COUNT_DTOR(nsZipArchive);
1332 //------------------------------------------
1333 // nsZipFind constructor and destructor
1334 //------------------------------------------
1336 nsZipFind::nsZipFind(nsZipArchive* aZip, char* aPattern, PRBool aRegExp) :
1337 #ifdef STANDALONE
1338 kMagic(ZIPFIND_MAGIC),
1339 #endif
1340 mArchive(aZip),
1341 mPattern(aPattern),
1342 mItem(0),
1343 mSlot(0),
1344 mRegExp(aRegExp)
1346 MOZ_COUNT_CTOR(nsZipFind);
1349 nsZipFind::~nsZipFind()
1351 PR_FREEIF(mPattern);
1353 MOZ_COUNT_DTOR(nsZipFind);
1356 //------------------------------------------
1357 // helper functions
1358 //------------------------------------------
1361 * HashName
1363 * returns a hash key for the entry name
1365 static PRUint32 HashName(const char* aName)
1367 PR_ASSERT(aName != 0);
1369 PRUint32 val = 0;
1370 for (PRUint8* c = (PRUint8*)aName; *c != 0; c++) {
1371 val = val*37 + *c;
1374 return (val % ZIP_TABSIZE);
1378 * x t o i n t
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));
1389 * x t o l o n g
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) |
1397 (ll [1] << 8) |
1398 (ll [2] << 16) |
1399 (ll [3] << 24) );
1403 * ExtractMode
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
1408 * bit.
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);
1426 #endif