modules/libjar/nsZipArchive.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 /*
     7  * This module implements a simple archive extractor for the PKZIP format.
     8  *
     9  * The underlying nsZipArchive is NOT thread-safe. Do not pass references
    10  * or pointers to it across thread boundaries.
    11  */
    13 // This must be the first include in the file in order for the
    14 // PL_ARENA_CONST_ALIGN_MASK macro to be effective.
    15 #define PL_ARENA_CONST_ALIGN_MASK  (sizeof(void*)-1)
    16 #include "plarena.h"
    18 #define READTYPE  int32_t
    19 #include "zlib.h"
    20 #include "nsISupportsUtils.h"
    21 #include "prio.h"
    22 #include "plstr.h"
    23 #include "prlog.h"
    24 #include "stdlib.h"
    25 #include "nsWildCard.h"
    26 #include "nsZipArchive.h"
    27 #include "nsString.h"
    28 #include "prenv.h"
    29 #if defined(XP_WIN)
    30 #include <windows.h>
    31 #endif
    33 // For placement new used for arena allocations of zip file list
    34 #include <new>
    35 #define ZIP_ARENABLOCKSIZE (1*1024)
    37 #ifdef XP_UNIX
    38     #include <sys/mman.h>
    39     #include <sys/types.h>
    40     #include <sys/stat.h>
    41     #include <limits.h>
    42     #include <unistd.h>
    43 #elif defined(XP_WIN)
    44     #include <io.h>
    45 #endif
    47 #ifdef __SYMBIAN32__
    48     #include <sys/syslimits.h>
    49 #endif /*__SYMBIAN32__*/
    52 #ifndef XP_UNIX /* we need some constants defined in limits.h and unistd.h */
    53 #  ifndef S_IFMT
    54 #    define S_IFMT 0170000
    55 #  endif
    56 #  ifndef S_IFLNK
    57 #    define S_IFLNK  0120000
    58 #  endif
    59 #  ifndef PATH_MAX
    60 #    define PATH_MAX 1024
    61 #  endif
    62 #endif  /* XP_UNIX */
    64 #ifdef XP_WIN
    65 #include "private/pprio.h"  // To get PR_ImportFile
    66 #endif
    68 using namespace mozilla;
    70 static const uint32_t kMaxNameLength = PATH_MAX; /* Maximum name length */
    71 // For synthetic zip entries. Date/time corresponds to 1980-01-01 00:00.
    72 static const uint16_t kSyntheticTime = 0;
    73 static const uint16_t kSyntheticDate = (1 + (1 << 5) + (0 << 9));
    75 static uint16_t xtoint(const uint8_t *ii);
    76 static uint32_t xtolong(const uint8_t *ll);
    77 static uint32_t HashName(const char* aName, uint16_t nameLen);
    78 #ifdef XP_UNIX
    79 static nsresult ResolveSymlink(const char *path);
    80 #endif
    82 class ZipArchiveLogger {
    83 public:
    84   void Write(const nsACString &zip, const char *entry) const {
    85     if (!fd) {
    86       char *env = PR_GetEnv("MOZ_JAR_LOG_FILE");
    87       if (!env)
    88         return;
    90       nsCOMPtr<nsIFile> logFile;
    91       nsresult rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(env), false, getter_AddRefs(logFile));
    92       if (NS_FAILED(rv))
    93         return;
    95       // Create the log file and its parent directory (in case it doesn't exist)
    96       logFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
    98       PRFileDesc* file;
    99 #ifdef XP_WIN
   100       // PR_APPEND is racy on Windows, so open a handle ourselves with flags that
   101       // will work, and use PR_ImportFile to make it a PRFileDesc.
   102       // This can go away when bug 840435 is fixed.
   103       nsAutoString path;
   104       logFile->GetPath(path);
   105       if (path.IsEmpty())
   106         return;
   107       HANDLE handle = CreateFileW(path.get(), FILE_APPEND_DATA, FILE_SHARE_WRITE,
   108                                   nullptr, OPEN_ALWAYS, 0, nullptr);
   109       if (handle == INVALID_HANDLE_VALUE)
   110         return;
   111       file = PR_ImportFile((PROsfd)handle);
   112       if (!file)
   113         return;
   114 #else
   115       rv = logFile->OpenNSPRFileDesc(PR_WRONLY|PR_CREATE_FILE|PR_APPEND, 0644, &file);
   116       if (NS_FAILED(rv))
   117         return;
   118 #endif
   119       fd = file;
   120     }
   121     nsCString buf(zip);
   122     buf.Append(" ");
   123     buf.Append(entry);
   124     buf.Append('\n');
   125     PR_Write(fd, buf.get(), buf.Length());
   126   }
   128   void AddRef() {
   129     MOZ_ASSERT(refCnt >= 0);
   130     ++refCnt;
   131   }
   133   void Release() {
   134     MOZ_ASSERT(refCnt > 0);
   135     if ((0 == --refCnt) && fd) {
   136       PR_Close(fd);
   137       fd = nullptr;
   138     }
   139   }
   140 private:
   141   int refCnt;
   142   mutable PRFileDesc *fd;
   143 };
   145 static ZipArchiveLogger zipLog;
   147 //***********************************************************
   148 // For every inflation the following allocations are done:
   149 // malloc(1 * 9520)
   150 // malloc(32768 * 1)
   151 //***********************************************************
   153 nsresult gZlibInit(z_stream *zs)
   154 {
   155   memset(zs, 0, sizeof(z_stream));
   156   int zerr = inflateInit2(zs, -MAX_WBITS);
   157   if (zerr != Z_OK) return NS_ERROR_OUT_OF_MEMORY;
   159   return NS_OK;
   160 }
   162 nsZipHandle::nsZipHandle()
   163   : mFileData(nullptr)
   164   , mLen(0)
   165   , mMap(nullptr)
   166   , mRefCnt(0)
   167 {
   168   MOZ_COUNT_CTOR(nsZipHandle);
   169 }
   171 NS_IMPL_ADDREF(nsZipHandle)
   172 NS_IMPL_RELEASE(nsZipHandle)
   174 nsresult nsZipHandle::Init(nsIFile *file, nsZipHandle **ret, PRFileDesc **aFd)
   175 {
   176   mozilla::AutoFDClose fd;
   177   int32_t flags = PR_RDONLY;
   178 #if defined(XP_WIN)
   179   flags |= nsIFile::OS_READAHEAD;
   180 #endif
   181   nsresult rv = file->OpenNSPRFileDesc(flags, 0000, &fd.rwget());
   182   if (NS_FAILED(rv))
   183     return rv;
   185   int64_t size = PR_Available64(fd);
   186   if (size >= INT32_MAX)
   187     return NS_ERROR_FILE_TOO_BIG;
   189   PRFileMap *map = PR_CreateFileMap(fd, size, PR_PROT_READONLY);
   190   if (!map)
   191     return NS_ERROR_FAILURE;
   193   uint8_t *buf = (uint8_t*) PR_MemMap(map, 0, (uint32_t) size);
   194   // Bug 525755: PR_MemMap fails when fd points at something other than a normal file.
   195   if (!buf) {
   196     PR_CloseFileMap(map);
   197     return NS_ERROR_FAILURE;
   198   }
   200   nsRefPtr<nsZipHandle> handle = new nsZipHandle();
   201   if (!handle) {
   202     PR_MemUnmap(buf, (uint32_t) size);
   203     PR_CloseFileMap(map);
   204     return NS_ERROR_OUT_OF_MEMORY;
   205   }
   207 #if defined(XP_WIN)
   208   if (aFd) {
   209     *aFd = fd.forget();
   210   }
   211 #endif
   212   handle->mMap = map;
   213   handle->mFile.Init(file);
   214   handle->mLen = (uint32_t) size;
   215   handle->mFileData = buf;
   216   handle.forget(ret);
   217   return NS_OK;
   218 }
   220 nsresult nsZipHandle::Init(nsZipArchive *zip, const char *entry,
   221                            nsZipHandle **ret)
   222 {
   223   nsRefPtr<nsZipHandle> handle = new nsZipHandle();
   224   if (!handle)
   225     return NS_ERROR_OUT_OF_MEMORY;
   227   handle->mBuf = new nsZipItemPtr<uint8_t>(zip, entry);
   228   if (!handle->mBuf)
   229     return NS_ERROR_OUT_OF_MEMORY;
   231   if (!handle->mBuf->Buffer())
   232     return NS_ERROR_UNEXPECTED;
   234   handle->mMap = nullptr;
   235   handle->mFile.Init(zip, entry);
   236   handle->mLen = handle->mBuf->Length();
   237   handle->mFileData = handle->mBuf->Buffer();
   238   handle.forget(ret);
   239   return NS_OK;
   240 }
   242 int64_t nsZipHandle::SizeOfMapping()
   243 {
   244     return mLen;
   245 }
   247 nsZipHandle::~nsZipHandle()
   248 {
   249   if (mMap) {
   250     PR_MemUnmap((void *)mFileData, mLen);
   251     PR_CloseFileMap(mMap);
   252   }
   253   mFileData = nullptr;
   254   mMap = nullptr;
   255   mBuf = nullptr;
   256   MOZ_COUNT_DTOR(nsZipHandle);
   257 }
   259 //***********************************************************
   260 //      nsZipArchive  --  public methods
   261 //***********************************************************
   263 //---------------------------------------------
   264 //  nsZipArchive::OpenArchive
   265 //---------------------------------------------
   266 nsresult nsZipArchive::OpenArchive(nsZipHandle *aZipHandle, PRFileDesc *aFd)
   267 {
   268   mFd = aZipHandle;
   270   // Initialize our arena
   271   PL_INIT_ARENA_POOL(&mArena, "ZipArena", ZIP_ARENABLOCKSIZE);
   273   //-- get table of contents for archive
   274   nsresult rv = BuildFileList(aFd);
   275   if (NS_SUCCEEDED(rv)) {
   276     if (aZipHandle->mFile)
   277       aZipHandle->mFile.GetURIString(mURI);
   278   }
   279   return rv;
   280 }
   282 nsresult nsZipArchive::OpenArchive(nsIFile *aFile)
   283 {
   284   nsRefPtr<nsZipHandle> handle;
   285 #if defined(XP_WIN)
   286   mozilla::AutoFDClose fd;
   287   nsresult rv = nsZipHandle::Init(aFile, getter_AddRefs(handle), &fd.rwget());
   288 #else
   289   nsresult rv = nsZipHandle::Init(aFile, getter_AddRefs(handle));
   290 #endif
   291   if (NS_FAILED(rv))
   292     return rv;
   294 #if defined(XP_WIN)
   295   return OpenArchive(handle, fd.get());
   296 #else
   297   return OpenArchive(handle);
   298 #endif
   299 }
   301 //---------------------------------------------
   302 //  nsZipArchive::Test
   303 //---------------------------------------------
   304 nsresult nsZipArchive::Test(const char *aEntryName)
   305 {
   306   nsZipItem* currItem;
   308   if (aEntryName) // only test specified item
   309   {
   310     currItem = GetItem(aEntryName);
   311     if (!currItem)
   312       return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST;
   313     //-- don't test (synthetic) directory items
   314     if (currItem->IsDirectory())
   315       return NS_OK;
   316     return ExtractFile(currItem, 0, 0);
   317   }
   319   // test all items in archive
   320   for (int i = 0; i < ZIP_TABSIZE; i++) {
   321     for (currItem = mFiles[i]; currItem; currItem = currItem->next) {
   322       //-- don't test (synthetic) directory items
   323       if (currItem->IsDirectory())
   324         continue;
   325       nsresult rv = ExtractFile(currItem, 0, 0);
   326       if (rv != NS_OK)
   327         return rv;
   328     }
   329   }
   331   return NS_OK;
   332 }
   334 //---------------------------------------------
   335 //  nsZipArchive::CloseArchive
   336 //---------------------------------------------
   337 nsresult nsZipArchive::CloseArchive()
   338 {
   339   if (mFd) {
   340     PL_FinishArenaPool(&mArena);
   341     mFd = nullptr;
   342   }
   344   // CAUTION:
   345   // We don't need to delete each of the nsZipItem as the memory for
   346   // the zip item and the filename it holds are both allocated from the Arena.
   347   // Hence, destroying the Arena is like destroying all the memory
   348   // for all the nsZipItem in one shot. But if the ~nsZipItem is doing
   349   // anything more than cleaning up memory, we should start calling it.
   350   // Let us also cleanup the mFiles table for re-use on the next 'open' call
   351   memset(mFiles, 0, sizeof(mFiles));
   352   mBuiltSynthetics = false;
   353   return NS_OK;
   354 }
   356 //---------------------------------------------
   357 // nsZipArchive::GetItem
   358 //---------------------------------------------
   359 nsZipItem*  nsZipArchive::GetItem(const char * aEntryName)
   360 {
   361   if (aEntryName) {
   362     uint32_t len = strlen(aEntryName);
   363     //-- If the request is for a directory, make sure that synthetic entries 
   364     //-- are created for the directories without their own entry.
   365     if (!mBuiltSynthetics) {
   366         if ((len > 0) && (aEntryName[len-1] == '/')) {
   367             if (BuildSynthetics() != NS_OK)
   368                 return 0;
   369         }
   370     }
   371 MOZ_WIN_MEM_TRY_BEGIN
   372     nsZipItem* item = mFiles[ HashName(aEntryName, len) ];
   373     while (item) {
   374       if ((len == item->nameLength) && 
   375           (!memcmp(aEntryName, item->Name(), len))) {
   377         // Successful GetItem() is a good indicator that the file is about to be read
   378         zipLog.Write(mURI, aEntryName);
   379         return item; //-- found it
   380       }
   381       item = item->next;
   382     }
   383 MOZ_WIN_MEM_TRY_CATCH(return nullptr)
   384   }
   385   return nullptr;
   386 }
   388 //---------------------------------------------
   389 // nsZipArchive::ExtractFile
   390 // This extracts the item to the filehandle provided.
   391 // If 'aFd' is null, it only tests the extraction.
   392 // On extraction error(s) it removes the file.
   393 // When needed, it also resolves the symlink.
   394 //---------------------------------------------
   395 nsresult nsZipArchive::ExtractFile(nsZipItem *item, const char *outname,
   396                                    PRFileDesc* aFd)
   397 {
   398   if (!item)
   399     return NS_ERROR_ILLEGAL_VALUE;
   400   if (!mFd)
   401     return NS_ERROR_FAILURE;
   403   // Directory extraction is handled in nsJAR::Extract,
   404   // so the item to be extracted should never be a directory
   405   PR_ASSERT(!item->IsDirectory());
   407   Bytef outbuf[ZIP_BUFLEN];
   409   nsZipCursor cursor(item, this, outbuf, ZIP_BUFLEN, true);
   411   nsresult rv = NS_OK;
   413   while (true) {
   414     uint32_t count = 0;
   415     uint8_t* buf = cursor.Read(&count);
   416     if (!buf) {
   417       rv = NS_ERROR_FILE_CORRUPTED;
   418       break;
   419     } else if (count == 0) {
   420       break;
   421     }
   423     if (aFd && PR_Write(aFd, buf, count) < (READTYPE)count) {
   424       rv = NS_ERROR_FILE_DISK_FULL;
   425       break;
   426     }
   427   }
   429   //-- delete the file on errors, or resolve symlink if needed
   430   if (aFd) {
   431     PR_Close(aFd);
   432     if (rv != NS_OK)
   433       PR_Delete(outname);
   434 #ifdef XP_UNIX
   435     else if (item->IsSymlink())
   436       rv = ResolveSymlink(outname);
   437 #endif
   438   }
   440   return rv;
   441 }
   443 //---------------------------------------------
   444 // nsZipArchive::FindInit
   445 //---------------------------------------------
   446 nsresult
   447 nsZipArchive::FindInit(const char * aPattern, nsZipFind **aFind)
   448 {
   449   if (!aFind)
   450     return NS_ERROR_ILLEGAL_VALUE;
   452   // null out param in case an error happens
   453   *aFind = nullptr;
   455   bool    regExp = false;
   456   char*   pattern = 0;
   458   // Create synthetic directory entries on demand
   459   nsresult rv = BuildSynthetics();
   460   if (rv != NS_OK)
   461     return rv;
   463   // validate the pattern
   464   if (aPattern)
   465   {
   466     switch (NS_WildCardValid((char*)aPattern))
   467     {
   468       case INVALID_SXP:
   469         return NS_ERROR_ILLEGAL_VALUE;
   471       case NON_SXP:
   472         regExp = false;
   473         break;
   475       case VALID_SXP:
   476         regExp = true;
   477         break;
   479       default:
   480         // undocumented return value from RegExpValid!
   481         PR_ASSERT(false);
   482         return NS_ERROR_ILLEGAL_VALUE;
   483     }
   485     pattern = PL_strdup(aPattern);
   486     if (!pattern)
   487       return NS_ERROR_OUT_OF_MEMORY;
   488   }
   490   *aFind = new nsZipFind(this, pattern, regExp);
   491   if (!*aFind) {
   492     PL_strfree(pattern);
   493     return NS_ERROR_OUT_OF_MEMORY;
   494   }
   496   return NS_OK;
   497 }
   501 //---------------------------------------------
   502 // nsZipFind::FindNext
   503 //---------------------------------------------
   504 nsresult nsZipFind::FindNext(const char ** aResult, uint16_t *aNameLen)
   505 {
   506   if (!mArchive || !aResult || !aNameLen)
   507     return NS_ERROR_ILLEGAL_VALUE;
   509   *aResult = 0;
   510   *aNameLen = 0;
   511 MOZ_WIN_MEM_TRY_BEGIN
   512   // we start from last match, look for next
   513   while (mSlot < ZIP_TABSIZE)
   514   {
   515     // move to next in current chain, or move to new slot
   516     mItem = mItem ? mItem->next : mArchive->mFiles[mSlot];
   518     bool found = false;
   519     if (!mItem)
   520       ++mSlot;                          // no more in this chain, move to next slot
   521     else if (!mPattern)
   522       found = true;            // always match
   523     else if (mRegExp)
   524     {
   525       char buf[kMaxNameLength+1];
   526       memcpy(buf, mItem->Name(), mItem->nameLength);
   527       buf[mItem->nameLength]='\0';
   528       found = (NS_WildCardMatch(buf, mPattern, false) == MATCH);
   529     }
   530     else
   531       found = ((mItem->nameLength == strlen(mPattern)) &&
   532                (memcmp(mItem->Name(), mPattern, mItem->nameLength) == 0));
   533     if (found) {
   534       // Need also to return the name length, as it is NOT zero-terminatdd...
   535       *aResult = mItem->Name();
   536       *aNameLen = mItem->nameLength;
   537       return NS_OK;
   538     }
   539   }
   540 MOZ_WIN_MEM_TRY_CATCH(return NS_ERROR_FAILURE)
   541   return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST;
   542 }
   544 #ifdef XP_UNIX
   545 //---------------------------------------------
   546 // ResolveSymlink
   547 //---------------------------------------------
   548 static nsresult ResolveSymlink(const char *path)
   549 {
   550   PRFileDesc * fIn = PR_Open(path, PR_RDONLY, 0000);
   551   if (!fIn)
   552     return NS_ERROR_FILE_DISK_FULL;
   554   char buf[PATH_MAX+1];
   555   int32_t length = PR_Read(fIn, (void*)buf, PATH_MAX);
   556   PR_Close(fIn);
   558   if ( (length <= 0)
   559     || ((buf[length] = 0, PR_Delete(path)) != 0)
   560     || (symlink(buf, path) != 0))
   561   {
   562      return NS_ERROR_FILE_DISK_FULL;
   563   }
   564   return NS_OK;
   565 }
   566 #endif
   568 //***********************************************************
   569 //      nsZipArchive  --  private implementation
   570 //***********************************************************
   572 //---------------------------------------------
   573 //  nsZipArchive::CreateZipItem
   574 //---------------------------------------------
   575 nsZipItem* nsZipArchive::CreateZipItem()
   576 {
   577   // Arena allocate the nsZipItem
   578   void *mem;
   579   PL_ARENA_ALLOCATE(mem, &mArena, sizeof(nsZipItem));
   580   return (nsZipItem*)mem;
   581 }
   583 //---------------------------------------------
   584 //  nsZipArchive::BuildFileList
   585 //---------------------------------------------
   586 nsresult nsZipArchive::BuildFileList(PRFileDesc *aFd)
   587 {
   588   // Get archive size using end pos
   589   const uint8_t* buf;
   590   const uint8_t* startp = mFd->mFileData;
   591   const uint8_t* endp = startp + mFd->mLen;
   592 MOZ_WIN_MEM_TRY_BEGIN
   593   uint32_t centralOffset = 4;
   594   if (mFd->mLen > ZIPCENTRAL_SIZE && xtolong(startp + centralOffset) == CENTRALSIG) {
   595     // Success means optimized jar layout from bug 559961 is in effect
   596     uint32_t readaheadLength = xtolong(startp);
   597     if (readaheadLength) {
   598 #if defined(XP_UNIX)
   599       madvise(const_cast<uint8_t*>(startp), readaheadLength, MADV_WILLNEED);
   600 #elif defined(XP_WIN)
   601       if (aFd) {
   602         HANDLE hFile = (HANDLE) PR_FileDesc2NativeHandle(aFd);
   603         mozilla::ReadAhead(hFile, 0, readaheadLength);
   604       }
   605 #endif
   606     }
   607   } else {
   608     for (buf = endp - ZIPEND_SIZE; buf > startp; buf--)
   609       {
   610         if (xtolong(buf) == ENDSIG) {
   611           centralOffset = xtolong(((ZipEnd *)buf)->offset_central_dir);
   612           break;
   613         }
   614       }
   615   }
   617   if (!centralOffset)
   618     return NS_ERROR_FILE_CORRUPTED;
   620   //-- Read the central directory headers
   621   buf = startp + centralOffset;
   622   uint32_t sig = 0;
   623   while (buf + int32_t(sizeof(uint32_t)) <= endp &&
   624          (sig = xtolong(buf)) == CENTRALSIG) {
   625     // Make sure there is enough data available.
   626     if (endp - buf < ZIPCENTRAL_SIZE)
   627       return NS_ERROR_FILE_CORRUPTED;
   629     // Read the fixed-size data.
   630     ZipCentral* central = (ZipCentral*)buf;
   632     uint16_t namelen = xtoint(central->filename_len);
   633     uint16_t extralen = xtoint(central->extrafield_len);
   634     uint16_t commentlen = xtoint(central->commentfield_len);
   636     // Point to the next item at the top of loop
   637     buf += ZIPCENTRAL_SIZE + namelen + extralen + commentlen;
   639     // Sanity check variable sizes and refuse to deal with
   640     // anything too big: it's likely a corrupt archive.
   641     if (namelen < 1 ||
   642         namelen > kMaxNameLength ||
   643         buf >= endp) {
   644       return NS_ERROR_FILE_CORRUPTED;
   645     }
   647     nsZipItem* item = CreateZipItem();
   648     if (!item)
   649       return NS_ERROR_OUT_OF_MEMORY;
   651     item->central = central;
   652     item->nameLength = namelen;
   653     item->isSynthetic = false;
   655     // Add item to file table
   656     uint32_t hash = HashName(item->Name(), namelen);
   657     item->next = mFiles[hash];
   658     mFiles[hash] = item;
   660     sig = 0;
   661   } /* while reading central directory records */
   663   if (sig != ENDSIG)
   664     return NS_ERROR_FILE_CORRUPTED;
   666   // Make the comment available for consumers.
   667   if (endp - buf >= ZIPEND_SIZE) {
   668     ZipEnd *zipend = (ZipEnd *)buf;
   670     buf += ZIPEND_SIZE;
   671     uint16_t commentlen = xtoint(zipend->commentfield_len);
   672     if (endp - buf >= commentlen) {
   673       mCommentPtr = (const char *)buf;
   674       mCommentLen = commentlen;
   675     }
   676   }
   678 MOZ_WIN_MEM_TRY_CATCH(return NS_ERROR_FAILURE)
   679   return NS_OK;
   680 }
   682 //---------------------------------------------
   683 //  nsZipArchive::BuildSynthetics
   684 //---------------------------------------------
   685 nsresult nsZipArchive::BuildSynthetics()
   686 {
   687   if (mBuiltSynthetics)
   688     return NS_OK;
   689   mBuiltSynthetics = true;
   691 MOZ_WIN_MEM_TRY_BEGIN
   692   // Create synthetic entries for any missing directories.
   693   // Do this when all ziptable has scanned to prevent double entries.
   694   for (int i = 0; i < ZIP_TABSIZE; ++i)
   695   {
   696     for (nsZipItem* item = mFiles[i]; item != nullptr; item = item->next)
   697     {
   698       if (item->isSynthetic)
   699         continue;
   701       //-- add entries for directories in the current item's path
   702       //-- go from end to beginning, because then we can stop trying
   703       //-- to create diritems if we find that the diritem we want to
   704       //-- create already exists
   705       //-- start just before the last char so as to not add the item
   706       //-- twice if it's a directory
   707       uint16_t namelen = item->nameLength;
   708       MOZ_ASSERT(namelen > 0, "Attempt to build synthetic for zero-length entry name!");
   709       const char *name = item->Name();
   710       for (uint16_t dirlen = namelen - 1; dirlen > 0; dirlen--)
   711       {
   712         if (name[dirlen-1] != '/')
   713           continue;
   715         // The character before this is '/', so if this is also '/' then we
   716         // have an empty path component. Skip it.
   717         if (name[dirlen] == '/')
   718           continue;
   720         // Is the directory already in the file table?
   721         uint32_t hash = HashName(item->Name(), dirlen);
   722         bool found = false;
   723         for (nsZipItem* zi = mFiles[hash]; zi != nullptr; zi = zi->next)
   724         {
   725           if ((dirlen == zi->nameLength) &&
   726               (0 == memcmp(item->Name(), zi->Name(), dirlen)))
   727           {
   728             // we've already added this dir and all its parents
   729             found = true;
   730             break;
   731           }
   732         }
   733         // if the directory was found, break out of the directory
   734         // creation loop now that we know all implicit directories
   735         // are there -- otherwise, start creating the zip item
   736         if (found)
   737           break;
   739         nsZipItem* diritem = CreateZipItem();
   740         if (!diritem)
   741           return NS_ERROR_OUT_OF_MEMORY;
   743         // Point to the central record of the original item for the name part.
   744         diritem->central =  item->central;
   745         diritem->nameLength = dirlen;
   746         diritem->isSynthetic = true;
   748         // add diritem to the file table
   749         diritem->next = mFiles[hash];
   750         mFiles[hash] = diritem;
   751       } /* end processing of dirs in item's name */
   752     }
   753   }
   754 MOZ_WIN_MEM_TRY_CATCH(return NS_ERROR_FAILURE)
   755   return NS_OK;
   756 }
   758 nsZipHandle* nsZipArchive::GetFD()
   759 {
   760   if (!mFd)
   761     return nullptr;
   762   return mFd.get();
   763 }
   765 //---------------------------------------------
   766 // nsZipArchive::GetData
   767 //---------------------------------------------
   768 const uint8_t* nsZipArchive::GetData(nsZipItem* aItem)
   769 {
   770   PR_ASSERT (aItem);
   771 MOZ_WIN_MEM_TRY_BEGIN
   772   //-- read local header to get variable length values and calculate
   773   //-- the real data offset
   774   uint32_t len = mFd->mLen;
   775   const uint8_t* data = mFd->mFileData;
   776   uint32_t offset = aItem->LocalOffset();
   777   if (offset + ZIPLOCAL_SIZE > len)
   778     return nullptr;
   780   // -- check signature before using the structure, in case the zip file is corrupt
   781   ZipLocal* Local = (ZipLocal*)(data + offset);
   782   if ((xtolong(Local->signature) != LOCALSIG))
   783     return nullptr;
   785   //-- NOTE: extralen is different in central header and local header
   786   //--       for archives created using the Unix "zip" utility. To set
   787   //--       the offset accurately we need the _local_ extralen.
   788   offset += ZIPLOCAL_SIZE +
   789             xtoint(Local->filename_len) +
   790             xtoint(Local->extrafield_len);
   792   // -- check if there is enough source data in the file
   793   if (offset + aItem->Size() > len)
   794     return nullptr;
   796   return data + offset;
   797 MOZ_WIN_MEM_TRY_CATCH(return nullptr)
   798 }
   800 // nsZipArchive::GetComment
   801 bool nsZipArchive::GetComment(nsACString &aComment)
   802 {
   803 MOZ_WIN_MEM_TRY_BEGIN
   804   aComment.Assign(mCommentPtr, mCommentLen);
   805 MOZ_WIN_MEM_TRY_CATCH(return false)
   806   return true;
   807 }
   809 //---------------------------------------------
   810 // nsZipArchive::SizeOfMapping
   811 //---------------------------------------------
   812 int64_t nsZipArchive::SizeOfMapping()
   813 {
   814     return mFd ? mFd->SizeOfMapping() : 0;
   815 }
   817 //------------------------------------------
   818 // nsZipArchive constructor and destructor
   819 //------------------------------------------
   821 nsZipArchive::nsZipArchive()
   822   : mRefCnt(0)
   823   , mBuiltSynthetics(false)
   824 {
   825   zipLog.AddRef();
   827   MOZ_COUNT_CTOR(nsZipArchive);
   829   // initialize the table to nullptr
   830   memset(mFiles, 0, sizeof(mFiles));
   831 }
   833 NS_IMPL_ADDREF(nsZipArchive)
   834 NS_IMPL_RELEASE(nsZipArchive)
   836 nsZipArchive::~nsZipArchive()
   837 {
   838   CloseArchive();
   840   MOZ_COUNT_DTOR(nsZipArchive);
   842   zipLog.Release();
   843 }
   846 //------------------------------------------
   847 // nsZipFind constructor and destructor
   848 //------------------------------------------
   850 nsZipFind::nsZipFind(nsZipArchive* aZip, char* aPattern, bool aRegExp) : 
   851   mArchive(aZip),
   852   mPattern(aPattern),
   853   mItem(0),
   854   mSlot(0),
   855   mRegExp(aRegExp)
   856 {
   857   MOZ_COUNT_CTOR(nsZipFind);
   858 }
   860 nsZipFind::~nsZipFind()
   861 {
   862   PL_strfree(mPattern);
   864   MOZ_COUNT_DTOR(nsZipFind);
   865 }
   867 //------------------------------------------
   868 // helper functions
   869 //------------------------------------------
   871 /* 
   872  * HashName 
   873  *
   874  * returns a hash key for the entry name 
   875  */
   876 static uint32_t HashName(const char* aName, uint16_t len)
   877 {
   878   PR_ASSERT(aName != 0);
   880   const uint8_t* p = (const uint8_t*)aName;
   881   const uint8_t* endp = p + len;
   882   uint32_t val = 0;
   883   while (p != endp) {
   884     val = val*37 + *p++;
   885   }
   887   return (val % ZIP_TABSIZE);
   888 }
   890 /*
   891  *  x t o i n t
   892  *
   893  *  Converts a two byte ugly endianed integer
   894  *  to our platform's integer.
   895  */
   896 static uint16_t xtoint (const uint8_t *ii)
   897 {
   898   return (uint16_t) ((ii [0]) | (ii [1] << 8));
   899 }
   901 /*
   902  *  x t o l o n g
   903  *
   904  *  Converts a four byte ugly endianed integer
   905  *  to our platform's integer.
   906  */
   907 static uint32_t xtolong (const uint8_t *ll)
   908 {
   909   return (uint32_t)( (ll [0] <<  0) |
   910                      (ll [1] <<  8) |
   911                      (ll [2] << 16) |
   912                      (ll [3] << 24) );
   913 }
   915 /*
   916  * GetModTime
   917  *
   918  * returns last modification time in microseconds
   919  */
   920 static PRTime GetModTime(uint16_t aDate, uint16_t aTime)
   921 {
   922   // Note that on DST shift we can't handle correctly the hour that is valid
   923   // in both DST zones
   924   PRExplodedTime time;
   926   time.tm_usec = 0;
   928   time.tm_hour = (aTime >> 11) & 0x1F;
   929   time.tm_min = (aTime >> 5) & 0x3F;
   930   time.tm_sec = (aTime & 0x1F) * 2;
   932   time.tm_year = (aDate >> 9) + 1980;
   933   time.tm_month = ((aDate >> 5) & 0x0F) - 1;
   934   time.tm_mday = aDate & 0x1F;
   936   time.tm_params.tp_gmt_offset = 0;
   937   time.tm_params.tp_dst_offset = 0;
   939   PR_NormalizeTime(&time, PR_GMTParameters);
   940   time.tm_params.tp_gmt_offset = PR_LocalTimeParameters(&time).tp_gmt_offset;
   941   PR_NormalizeTime(&time, PR_GMTParameters);
   942   time.tm_params.tp_dst_offset = PR_LocalTimeParameters(&time).tp_dst_offset;
   944   return PR_ImplodeTime(&time);
   945 }
   947 uint32_t nsZipItem::LocalOffset()
   948 {
   949   return xtolong(central->localhdr_offset);
   950 }
   952 uint32_t nsZipItem::Size()
   953 {
   954   return isSynthetic ? 0 : xtolong(central->size);
   955 }
   957 uint32_t nsZipItem::RealSize()
   958 {
   959   return isSynthetic ? 0 : xtolong(central->orglen);
   960 }
   962 uint32_t nsZipItem::CRC32()
   963 {
   964   return isSynthetic ? 0 : xtolong(central->crc32);
   965 }
   967 uint16_t nsZipItem::Date()
   968 {
   969   return isSynthetic ? kSyntheticDate : xtoint(central->date);
   970 }
   972 uint16_t nsZipItem::Time()
   973 {
   974   return isSynthetic ? kSyntheticTime : xtoint(central->time);
   975 }
   977 uint16_t nsZipItem::Compression()
   978 {
   979   return isSynthetic ? STORED : xtoint(central->method);
   980 }
   982 bool nsZipItem::IsDirectory()
   983 {
   984   return isSynthetic || ((nameLength > 0) && ('/' == Name()[nameLength - 1]));
   985 }
   987 uint16_t nsZipItem::Mode()
   988 {
   989   if (isSynthetic) return 0755;
   990   return ((uint16_t)(central->external_attributes[2]) | 0x100);
   991 }
   993 const uint8_t * nsZipItem::GetExtraField(uint16_t aTag, uint16_t *aBlockSize)
   994 {
   995   if (isSynthetic) return nullptr;
   996 MOZ_WIN_MEM_TRY_BEGIN
   997   const unsigned char *buf = ((const unsigned char*)central) + ZIPCENTRAL_SIZE +
   998                              nameLength;
   999   uint32_t buflen = (uint32_t)xtoint(central->extrafield_len);
  1000   uint32_t pos = 0;
  1001   uint16_t tag, blocksize;
  1003   while (buf && (pos + 4) <= buflen) {
  1004     tag = xtoint(buf + pos);
  1005     blocksize = xtoint(buf + pos + 2);
  1007     if (aTag == tag && (pos + 4 + blocksize) <= buflen) {
  1008       *aBlockSize = blocksize;
  1009       return buf + pos;
  1012     pos += blocksize + 4;
  1015 MOZ_WIN_MEM_TRY_CATCH(return nullptr)
  1016   return nullptr;
  1020 PRTime nsZipItem::LastModTime()
  1022   if (isSynthetic) return GetModTime(kSyntheticDate, kSyntheticTime);
  1024   // Try to read timestamp from extra field
  1025   uint16_t blocksize;
  1026   const uint8_t *tsField = GetExtraField(EXTENDED_TIMESTAMP_FIELD, &blocksize);
  1027   if (tsField && blocksize >= 5 && tsField[4] & EXTENDED_TIMESTAMP_MODTIME) {
  1028     return (PRTime)(xtolong(tsField + 5)) * PR_USEC_PER_SEC;
  1031   return GetModTime(Date(), Time());
  1034 #ifdef XP_UNIX
  1035 bool nsZipItem::IsSymlink()
  1037   if (isSynthetic) return false;
  1038   return (xtoint(central->external_attributes+2) & S_IFMT) == S_IFLNK;
  1040 #endif
  1042 nsZipCursor::nsZipCursor(nsZipItem *item, nsZipArchive *aZip, uint8_t* aBuf, uint32_t aBufSize, bool doCRC) :
  1043   mItem(item),
  1044   mBuf(aBuf),
  1045   mBufSize(aBufSize),
  1046   mDoCRC(doCRC)
  1048   if (mItem->Compression() == DEFLATED) {
  1049 #ifdef DEBUG
  1050     nsresult status =
  1051 #endif
  1052       gZlibInit(&mZs);
  1053     NS_ASSERTION(status == NS_OK, "Zlib failed to initialize");
  1054     NS_ASSERTION(aBuf, "Must pass in a buffer for DEFLATED nsZipItem");
  1057   mZs.avail_in = item->Size();
  1058   mZs.next_in = (Bytef*)aZip->GetData(item);
  1060   if (doCRC)
  1061     mCRC = crc32(0L, Z_NULL, 0);
  1064 nsZipCursor::~nsZipCursor()
  1066   if (mItem->Compression() == DEFLATED) {
  1067     inflateEnd(&mZs);
  1071 uint8_t* nsZipCursor::ReadOrCopy(uint32_t *aBytesRead, bool aCopy) {
  1072   int zerr;
  1073   uint8_t *buf = nullptr;
  1074   bool verifyCRC = true;
  1076   if (!mZs.next_in)
  1077     return nullptr;
  1078 MOZ_WIN_MEM_TRY_BEGIN
  1079   switch (mItem->Compression()) {
  1080   case STORED:
  1081     if (!aCopy) {
  1082       *aBytesRead = mZs.avail_in;
  1083       buf = mZs.next_in;
  1084       mZs.next_in += mZs.avail_in;
  1085       mZs.avail_in = 0;
  1086     } else {
  1087       *aBytesRead = mZs.avail_in > mBufSize ? mBufSize : mZs.avail_in;
  1088       memcpy(mBuf, mZs.next_in, *aBytesRead);
  1089       mZs.avail_in -= *aBytesRead;
  1090       mZs.next_in += *aBytesRead;
  1092     break;
  1093   case DEFLATED:
  1094     buf = mBuf;
  1095     mZs.next_out = buf;
  1096     mZs.avail_out = mBufSize;
  1098     zerr = inflate(&mZs, Z_PARTIAL_FLUSH);
  1099     if (zerr != Z_OK && zerr != Z_STREAM_END)
  1100       return nullptr;
  1102     *aBytesRead = mZs.next_out - buf;
  1103     verifyCRC = (zerr == Z_STREAM_END);
  1104     break;
  1105   default:
  1106     return nullptr;
  1109   if (mDoCRC) {
  1110     mCRC = crc32(mCRC, (const unsigned char*)buf, *aBytesRead);
  1111     if (verifyCRC && mCRC != mItem->CRC32())
  1112       return nullptr;
  1114 MOZ_WIN_MEM_TRY_CATCH(return nullptr)
  1115   return buf;
  1118 nsZipItemPtr_base::nsZipItemPtr_base(nsZipArchive *aZip, const char * aEntryName, bool doCRC) :
  1119   mReturnBuf(nullptr)
  1121   // make sure the ziparchive hangs around
  1122   mZipHandle = aZip->GetFD();
  1124   nsZipItem* item = aZip->GetItem(aEntryName);
  1125   if (!item)
  1126     return;
  1128   uint32_t size = 0;
  1129   if (item->Compression() == DEFLATED) {
  1130     size = item->RealSize();
  1131     mAutoBuf = new ((fallible_t())) uint8_t[size];
  1132     if (!mAutoBuf) {
  1133       return;
  1137   nsZipCursor cursor(item, aZip, mAutoBuf, size, doCRC);
  1138   mReturnBuf = cursor.Read(&mReadlen);
  1139   if (!mReturnBuf) {
  1140     return;
  1143   if (mReadlen != item->RealSize()) {
  1144     NS_ASSERTION(mReadlen == item->RealSize(), "nsZipCursor underflow");
  1145     mReturnBuf = nullptr;
  1146     return;

mercurial