forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			1193 lines
		
	
	
	
		
			35 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1193 lines
		
	
	
	
		
			35 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| /*
 | |
|  * This module implements a simple archive extractor for the PKZIP format.
 | |
|  *
 | |
|  * The underlying nsZipArchive is NOT thread-safe. Do not pass references
 | |
|  * or pointers to it across thread boundaries.
 | |
|  */
 | |
| 
 | |
| #define READTYPE int32_t
 | |
| #include "zlib.h"
 | |
| #include "nsISupportsUtils.h"
 | |
| #include "mozilla/MmapFaultHandler.h"
 | |
| #include "prio.h"
 | |
| #include "mozilla/Attributes.h"
 | |
| #include "mozilla/Logging.h"
 | |
| #include "mozilla/MemUtils.h"
 | |
| #include "mozilla/UniquePtrExtensions.h"
 | |
| #include "mozilla/StaticMutex.h"
 | |
| #include "stdlib.h"
 | |
| #include "nsDirectoryService.h"
 | |
| #include "nsWildCard.h"
 | |
| #include "nsXULAppAPI.h"
 | |
| #include "nsZipArchive.h"
 | |
| #include "nsString.h"
 | |
| #include "prenv.h"
 | |
| #if defined(XP_WIN)
 | |
| #  include <windows.h>
 | |
| #endif
 | |
| 
 | |
| // For placement new used for arena allocations of zip file list
 | |
| #include <new>
 | |
| #define ZIP_ARENABLOCKSIZE (1 * 1024)
 | |
| 
 | |
| #ifdef XP_UNIX
 | |
| #  include <sys/mman.h>
 | |
| #  include <sys/types.h>
 | |
| #  include <sys/stat.h>
 | |
| #  include <limits.h>
 | |
| #  include <unistd.h>
 | |
| #elif defined(XP_WIN)
 | |
| #  include <io.h>
 | |
| #endif
 | |
| 
 | |
| #ifdef __SYMBIAN32__
 | |
| #  include <sys/syslimits.h>
 | |
| #endif /*__SYMBIAN32__*/
 | |
| 
 | |
| #ifndef XP_UNIX /* we need some constants defined in limits.h and unistd.h */
 | |
| #  ifndef S_IFMT
 | |
| #    define S_IFMT 0170000
 | |
| #  endif
 | |
| #  ifndef S_IFLNK
 | |
| #    define S_IFLNK 0120000
 | |
| #  endif
 | |
| #  ifndef PATH_MAX
 | |
| #    define PATH_MAX 1024
 | |
| #  endif
 | |
| #endif /* XP_UNIX */
 | |
| 
 | |
| #ifdef XP_WIN
 | |
| #  include "private/pprio.h"  // To get PR_ImportFile
 | |
| #endif
 | |
| 
 | |
| using namespace mozilla;
 | |
| 
 | |
| static const uint32_t kMaxNameLength = PATH_MAX; /* Maximum name length */
 | |
| // For synthetic zip entries. Date/time corresponds to 1980-01-01 00:00.
 | |
| static const uint16_t kSyntheticTime = 0;
 | |
| static const uint16_t kSyntheticDate = (1 + (1 << 5) + (0 << 9));
 | |
| 
 | |
| static uint16_t xtoint(const uint8_t* ii);
 | |
| static uint32_t xtolong(const uint8_t* ll);
 | |
| static uint32_t HashName(const char* aName, uint16_t nameLen);
 | |
| 
 | |
| class ZipArchiveLogger {
 | |
|  public:
 | |
|   void Init(const char* env) {
 | |
|     StaticMutexAutoLock lock(sLock);
 | |
| 
 | |
|     // AddRef
 | |
|     MOZ_ASSERT(mRefCnt >= 0);
 | |
|     ++mRefCnt;
 | |
| 
 | |
|     if (!mFd) {
 | |
|       nsCOMPtr<nsIFile> logFile;
 | |
|       nsresult rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(env), false,
 | |
|                                     getter_AddRefs(logFile));
 | |
|       if (NS_FAILED(rv)) return;
 | |
| 
 | |
|       // Create the log file and its parent directory (in case it doesn't exist)
 | |
|       rv = logFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
 | |
|       if (NS_FAILED(rv)) return;
 | |
| 
 | |
|       PRFileDesc* file;
 | |
| #ifdef XP_WIN
 | |
|       // PR_APPEND is racy on Windows, so open a handle ourselves with flags
 | |
|       // that will work, and use PR_ImportFile to make it a PRFileDesc. This can
 | |
|       // go away when bug 840435 is fixed.
 | |
|       nsAutoString path;
 | |
|       logFile->GetPath(path);
 | |
|       if (path.IsEmpty()) return;
 | |
|       HANDLE handle =
 | |
|           CreateFileW(path.get(), FILE_APPEND_DATA, FILE_SHARE_WRITE, nullptr,
 | |
|                       OPEN_ALWAYS, 0, nullptr);
 | |
|       if (handle == INVALID_HANDLE_VALUE) return;
 | |
|       file = PR_ImportFile((PROsfd)handle);
 | |
|       if (!file) return;
 | |
| #else
 | |
|       rv = logFile->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE | PR_APPEND,
 | |
|                                      0644, &file);
 | |
|       if (NS_FAILED(rv)) return;
 | |
| #endif
 | |
|       mFd = file;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   void Write(const nsACString& zip, const char* entry) {
 | |
|     StaticMutexAutoLock lock(sLock);
 | |
| 
 | |
|     if (mFd) {
 | |
|       nsCString buf(zip);
 | |
|       buf.Append(' ');
 | |
|       buf.Append(entry);
 | |
|       buf.Append('\n');
 | |
|       PR_Write(mFd, buf.get(), buf.Length());
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   void Release() {
 | |
|     StaticMutexAutoLock lock(sLock);
 | |
| 
 | |
|     MOZ_ASSERT(mRefCnt > 0);
 | |
|     if ((0 == --mRefCnt) && mFd) {
 | |
|       PR_Close(mFd);
 | |
|       mFd = nullptr;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   static StaticMutex sLock;
 | |
|   int mRefCnt;
 | |
|   PRFileDesc* mFd;
 | |
| };
 | |
| 
 | |
| StaticMutex ZipArchiveLogger::sLock;
 | |
| static ZipArchiveLogger zipLog;
 | |
| 
 | |
| //***********************************************************
 | |
| // For every inflation the following allocations are done:
 | |
| // malloc(1 * 9520)
 | |
| // malloc(32768 * 1)
 | |
| //***********************************************************
 | |
| 
 | |
| nsresult gZlibInit(z_stream* zs) {
 | |
|   memset(zs, 0, sizeof(z_stream));
 | |
|   int zerr = inflateInit2(zs, -MAX_WBITS);
 | |
|   if (zerr != Z_OK) return NS_ERROR_OUT_OF_MEMORY;
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsZipHandle::nsZipHandle()
 | |
|     : mFileData(nullptr),
 | |
|       mLen(0),
 | |
|       mMap(nullptr),
 | |
|       mRefCnt(0),
 | |
|       mFileStart(nullptr),
 | |
|       mTotalLen(0) {}
 | |
| 
 | |
| NS_IMPL_ADDREF(nsZipHandle)
 | |
| NS_IMPL_RELEASE(nsZipHandle)
 | |
| 
 | |
| nsresult nsZipHandle::Init(nsIFile* file, nsZipHandle** ret, PRFileDesc** aFd) {
 | |
|   mozilla::AutoFDClose fd;
 | |
|   int32_t flags = PR_RDONLY;
 | |
| #if defined(XP_WIN)
 | |
|   flags |= nsIFile::OS_READAHEAD;
 | |
| #endif
 | |
|   nsresult rv = file->OpenNSPRFileDesc(flags, 0000, &fd.rwget());
 | |
|   if (NS_FAILED(rv)) return rv;
 | |
| 
 | |
|   int64_t size = PR_Available64(fd);
 | |
|   if (size >= INT32_MAX) return NS_ERROR_FILE_TOO_BIG;
 | |
| 
 | |
|   PRFileMap* map = PR_CreateFileMap(fd, size, PR_PROT_READONLY);
 | |
|   if (!map) return NS_ERROR_FAILURE;
 | |
| 
 | |
|   uint8_t* buf = (uint8_t*)PR_MemMap(map, 0, (uint32_t)size);
 | |
|   // Bug 525755: PR_MemMap fails when fd points at something other than a normal
 | |
|   // file.
 | |
|   if (!buf) {
 | |
|     PR_CloseFileMap(map);
 | |
|     return NS_ERROR_FAILURE;
 | |
|   }
 | |
| 
 | |
|   RefPtr<nsZipHandle> handle = new nsZipHandle();
 | |
|   if (!handle) {
 | |
|     PR_MemUnmap(buf, (uint32_t)size);
 | |
|     PR_CloseFileMap(map);
 | |
|     return NS_ERROR_OUT_OF_MEMORY;
 | |
|   }
 | |
| 
 | |
| #if defined(XP_WIN)
 | |
|   if (aFd) {
 | |
|     *aFd = fd.forget();
 | |
|   }
 | |
| #else
 | |
|   handle->mNSPRFileDesc = fd.forget();
 | |
| #endif
 | |
|   handle->mFile.Init(file);
 | |
|   handle->mTotalLen = (uint32_t)size;
 | |
|   handle->mFileStart = buf;
 | |
|   rv = handle->findDataStart();
 | |
|   if (NS_FAILED(rv)) {
 | |
|     PR_MemUnmap(buf, (uint32_t)size);
 | |
|     handle->mFileStart = nullptr;
 | |
|     PR_CloseFileMap(map);
 | |
|     return rv;
 | |
|   }
 | |
|   handle->mMap = map;
 | |
|   handle.forget(ret);
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult nsZipHandle::Init(nsZipArchive* zip, const char* entry,
 | |
|                            nsZipHandle** ret) {
 | |
|   RefPtr<nsZipHandle> handle = new nsZipHandle();
 | |
|   if (!handle) return NS_ERROR_OUT_OF_MEMORY;
 | |
| 
 | |
|   handle->mBuf = MakeUnique<nsZipItemPtr<uint8_t>>(zip, entry);
 | |
|   if (!handle->mBuf) return NS_ERROR_OUT_OF_MEMORY;
 | |
| 
 | |
|   if (!handle->mBuf->Buffer()) return NS_ERROR_UNEXPECTED;
 | |
| 
 | |
|   handle->mMap = nullptr;
 | |
|   handle->mFile.Init(zip, entry);
 | |
|   handle->mTotalLen = handle->mBuf->Length();
 | |
|   handle->mFileStart = handle->mBuf->Buffer();
 | |
|   nsresult rv = handle->findDataStart();
 | |
|   if (NS_FAILED(rv)) {
 | |
|     return rv;
 | |
|   }
 | |
|   handle.forget(ret);
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult nsZipHandle::Init(const uint8_t* aData, uint32_t aLen,
 | |
|                            nsZipHandle** aRet) {
 | |
|   RefPtr<nsZipHandle> handle = new nsZipHandle();
 | |
| 
 | |
|   handle->mFileStart = aData;
 | |
|   handle->mTotalLen = aLen;
 | |
|   nsresult rv = handle->findDataStart();
 | |
|   if (NS_FAILED(rv)) {
 | |
|     return rv;
 | |
|   }
 | |
|   handle.forget(aRet);
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| // This function finds the start of the ZIP data. If the file is a regular ZIP,
 | |
| // this is just the start of the file. If the file is a CRX file, the start of
 | |
| // the data is after the CRX header.
 | |
| // CRX header reference: (CRX version 2)
 | |
| //    Header requires little-endian byte ordering with 4-byte alignment.
 | |
| //    32 bits       : magicNumber   - Defined as a |char m[] = "Cr24"|.
 | |
| //                                    Equivilant to |uint32_t m = 0x34327243|.
 | |
| //    32 bits       : version       - Unsigned integer representing the CRX file
 | |
| //                                    format version. Currently equal to 2.
 | |
| //    32 bits       : pubKeyLength  - Unsigned integer representing the length
 | |
| //                                    of the public key in bytes.
 | |
| //    32 bits       : sigLength     - Unsigned integer representing the length
 | |
| //                                    of the signature in bytes.
 | |
| //    pubKeyLength  : publicKey     - Contents of the author's public key.
 | |
| //    sigLength     : signature     - Signature of the ZIP content.
 | |
| //                                    Signature is created using the RSA
 | |
| //                                    algorithm with the SHA-1 hash function.
 | |
| nsresult nsZipHandle::findDataStart() {
 | |
|   // In the CRX header, integers are 32 bits. Our pointer to the file is of
 | |
|   // type |uint8_t|, which is guaranteed to be 8 bits.
 | |
|   const uint32_t CRXIntSize = 4;
 | |
| 
 | |
|   MMAP_FAULT_HANDLER_BEGIN_HANDLE(this)
 | |
|   if (mTotalLen > CRXIntSize * 4 && xtolong(mFileStart) == kCRXMagic) {
 | |
|     const uint8_t* headerData = mFileStart;
 | |
|     headerData += CRXIntSize * 2;  // Skip magic number and version number
 | |
|     uint32_t pubKeyLength = xtolong(headerData);
 | |
|     headerData += CRXIntSize;
 | |
|     uint32_t sigLength = xtolong(headerData);
 | |
|     uint32_t headerSize = CRXIntSize * 4 + pubKeyLength + sigLength;
 | |
|     if (mTotalLen > headerSize) {
 | |
|       mLen = mTotalLen - headerSize;
 | |
|       mFileData = mFileStart + headerSize;
 | |
|       return NS_OK;
 | |
|     }
 | |
|   }
 | |
|   mLen = mTotalLen;
 | |
|   mFileData = mFileStart;
 | |
|   MMAP_FAULT_HANDLER_CATCH(NS_ERROR_FAILURE)
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| int64_t nsZipHandle::SizeOfMapping() { return mTotalLen; }
 | |
| 
 | |
| nsresult nsZipHandle::GetNSPRFileDesc(PRFileDesc** aNSPRFileDesc) {
 | |
|   if (!aNSPRFileDesc) {
 | |
|     return NS_ERROR_ILLEGAL_VALUE;
 | |
|   }
 | |
| 
 | |
|   *aNSPRFileDesc = mNSPRFileDesc;
 | |
|   if (!mNSPRFileDesc) {
 | |
|     return NS_ERROR_NOT_AVAILABLE;
 | |
|   }
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsZipHandle::~nsZipHandle() {
 | |
|   if (mMap) {
 | |
|     PR_MemUnmap((void*)mFileStart, mTotalLen);
 | |
|     PR_CloseFileMap(mMap);
 | |
|   }
 | |
|   mFileStart = nullptr;
 | |
|   mFileData = nullptr;
 | |
|   mMap = nullptr;
 | |
|   mBuf = nullptr;
 | |
| }
 | |
| 
 | |
| //***********************************************************
 | |
| //      nsZipArchive  --  public methods
 | |
| //***********************************************************
 | |
| 
 | |
| //---------------------------------------------
 | |
| //  nsZipArchive::OpenArchive
 | |
| //---------------------------------------------
 | |
| nsresult nsZipArchive::OpenArchive(nsZipHandle* aZipHandle, PRFileDesc* aFd) {
 | |
|   mFd = aZipHandle;
 | |
| 
 | |
|   //-- get table of contents for archive
 | |
|   nsresult rv = BuildFileList(aFd);
 | |
|   if (NS_SUCCEEDED(rv)) {
 | |
|     if (aZipHandle->mFile && XRE_IsParentProcess()) {
 | |
|       static char* env = PR_GetEnv("MOZ_JAR_LOG_FILE");
 | |
|       if (env) {
 | |
|         mUseZipLog = true;
 | |
| 
 | |
|         zipLog.Init(env);
 | |
|         // We only log accesses in jar/zip archives within the NS_GRE_DIR
 | |
|         // and/or the APK on Android. For the former, we log the archive path
 | |
|         // relative to NS_GRE_DIR, and for the latter, the nested-archive
 | |
|         // path within the APK. This makes the path match the path of the
 | |
|         // archives relative to the packaged dist/$APP_NAME directory in a
 | |
|         // build.
 | |
|         if (aZipHandle->mFile.IsZip()) {
 | |
|           // Nested archive, likely omni.ja in APK.
 | |
|           aZipHandle->mFile.GetPath(mURI);
 | |
|         } else if (nsDirectoryService::gService) {
 | |
|           // We can reach here through the initialization of Omnijar from
 | |
|           // XRE_InitCommandLine, which happens before the directory service
 | |
|           // is initialized. When that happens, it means the opened archive is
 | |
|           // the APK, and we don't care to log that one, so we just skip
 | |
|           // when the directory service is not initialized.
 | |
|           nsCOMPtr<nsIFile> dir = aZipHandle->mFile.GetBaseFile();
 | |
|           nsCOMPtr<nsIFile> gre_dir;
 | |
|           nsAutoCString path;
 | |
|           if (NS_SUCCEEDED(nsDirectoryService::gService->Get(
 | |
|                   NS_GRE_DIR, NS_GET_IID(nsIFile), getter_AddRefs(gre_dir)))) {
 | |
|             nsAutoCString leaf;
 | |
|             nsCOMPtr<nsIFile> parent;
 | |
|             while (NS_SUCCEEDED(dir->GetNativeLeafName(leaf)) &&
 | |
|                    NS_SUCCEEDED(dir->GetParent(getter_AddRefs(parent)))) {
 | |
|               if (!parent) {
 | |
|                 break;
 | |
|               }
 | |
|               dir = parent;
 | |
|               if (path.Length()) {
 | |
|                 path.Insert('/', 0);
 | |
|               }
 | |
|               path.Insert(leaf, 0);
 | |
|               bool equals;
 | |
|               if (NS_SUCCEEDED(dir->Equals(gre_dir, &equals)) && equals) {
 | |
|                 mURI.Assign(path);
 | |
|                 break;
 | |
|               }
 | |
|             }
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   return rv;
 | |
| }
 | |
| 
 | |
| nsresult nsZipArchive::OpenArchive(nsIFile* aFile) {
 | |
|   RefPtr<nsZipHandle> handle;
 | |
| #if defined(XP_WIN)
 | |
|   mozilla::AutoFDClose fd;
 | |
|   nsresult rv = nsZipHandle::Init(aFile, getter_AddRefs(handle), &fd.rwget());
 | |
| #else
 | |
|   nsresult rv = nsZipHandle::Init(aFile, getter_AddRefs(handle));
 | |
| #endif
 | |
|   if (NS_FAILED(rv)) return rv;
 | |
| 
 | |
| #if defined(XP_WIN)
 | |
|   return OpenArchive(handle, fd.get());
 | |
| #else
 | |
|   return OpenArchive(handle);
 | |
| #endif
 | |
| }
 | |
| 
 | |
| //---------------------------------------------
 | |
| //  nsZipArchive::Test
 | |
| //---------------------------------------------
 | |
| nsresult nsZipArchive::Test(const char* aEntryName) {
 | |
|   nsZipItem* currItem;
 | |
| 
 | |
|   if (aEntryName)  // only test specified item
 | |
|   {
 | |
|     currItem = GetItem(aEntryName);
 | |
|     if (!currItem) return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST;
 | |
|     //-- don't test (synthetic) directory items
 | |
|     if (currItem->IsDirectory()) return NS_OK;
 | |
|     return ExtractFile(currItem, 0, 0);
 | |
|   }
 | |
| 
 | |
|   // test all items in archive
 | |
|   for (auto* item : mFiles) {
 | |
|     for (currItem = item; currItem; currItem = currItem->next) {
 | |
|       //-- don't test (synthetic) directory items
 | |
|       if (currItem->IsDirectory()) continue;
 | |
|       nsresult rv = ExtractFile(currItem, 0, 0);
 | |
|       if (rv != NS_OK) return rv;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| //---------------------------------------------
 | |
| //  nsZipArchive::CloseArchive
 | |
| //---------------------------------------------
 | |
| nsresult nsZipArchive::CloseArchive() {
 | |
|   MutexAutoLock lock(mLock);
 | |
|   if (mFd) {
 | |
|     mArena.Clear();
 | |
|     mFd = nullptr;
 | |
|   }
 | |
| 
 | |
|   // CAUTION:
 | |
|   // We don't need to delete each of the nsZipItem as the memory for
 | |
|   // the zip item and the filename it holds are both allocated from the Arena.
 | |
|   // Hence, destroying the Arena is like destroying all the memory
 | |
|   // for all the nsZipItem in one shot. But if the ~nsZipItem is doing
 | |
|   // anything more than cleaning up memory, we should start calling it.
 | |
|   // Let us also cleanup the mFiles table for re-use on the next 'open' call
 | |
|   memset(mFiles, 0, sizeof(mFiles));
 | |
|   mBuiltSynthetics = false;
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| //---------------------------------------------
 | |
| // nsZipArchive::GetItem
 | |
| //---------------------------------------------
 | |
| nsZipItem* nsZipArchive::GetItem(const char* aEntryName) {
 | |
|   MutexAutoLock lock(mLock);
 | |
| 
 | |
|   if (aEntryName) {
 | |
|     uint32_t len = strlen(aEntryName);
 | |
|     //-- If the request is for a directory, make sure that synthetic entries
 | |
|     //-- are created for the directories without their own entry.
 | |
|     if (!mBuiltSynthetics) {
 | |
|       if ((len > 0) && (aEntryName[len - 1] == '/')) {
 | |
|         if (BuildSynthetics() != NS_OK) return 0;
 | |
|       }
 | |
|     }
 | |
|     MMAP_FAULT_HANDLER_BEGIN_HANDLE(mFd)
 | |
|     nsZipItem* item = mFiles[HashName(aEntryName, len)];
 | |
|     while (item) {
 | |
|       if ((len == item->nameLength) &&
 | |
|           (!memcmp(aEntryName, item->Name(), len))) {
 | |
|         // Successful GetItem() is a good indicator that the file is about to be
 | |
|         // read
 | |
|         if (mUseZipLog && mURI.Length()) {
 | |
|           zipLog.Write(mURI, aEntryName);
 | |
|         }
 | |
|         return item;  //-- found it
 | |
|       }
 | |
|       item = item->next;
 | |
|     }
 | |
|     MMAP_FAULT_HANDLER_CATCH(nullptr)
 | |
|   }
 | |
|   return nullptr;
 | |
| }
 | |
| 
 | |
| //---------------------------------------------
 | |
| // nsZipArchive::ExtractFile
 | |
| // This extracts the item to the filehandle provided.
 | |
| // If 'aFd' is null, it only tests the extraction.
 | |
| // On extraction error(s) it removes the file.
 | |
| //---------------------------------------------
 | |
| nsresult nsZipArchive::ExtractFile(nsZipItem* item, nsIFile* outFile,
 | |
|                                    PRFileDesc* aFd) {
 | |
|   if (!item) return NS_ERROR_ILLEGAL_VALUE;
 | |
|   if (!mFd) return NS_ERROR_FAILURE;
 | |
| 
 | |
|   // Directory extraction is handled in nsJAR::Extract,
 | |
|   // so the item to be extracted should never be a directory
 | |
|   MOZ_ASSERT(!item->IsDirectory());
 | |
| 
 | |
|   Bytef outbuf[ZIP_BUFLEN];
 | |
| 
 | |
|   nsZipCursor cursor(item, this, outbuf, ZIP_BUFLEN, true);
 | |
| 
 | |
|   nsresult rv = NS_OK;
 | |
| 
 | |
|   while (true) {
 | |
|     uint32_t count = 0;
 | |
|     uint8_t* buf = cursor.Read(&count);
 | |
|     if (!buf) {
 | |
|       rv = NS_ERROR_FILE_CORRUPTED;
 | |
|       break;
 | |
|     }
 | |
|     if (count == 0) {
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     if (aFd && PR_Write(aFd, buf, count) < (READTYPE)count) {
 | |
|       rv = NS_ERROR_FILE_NO_DEVICE_SPACE;
 | |
|       break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //-- delete the file on errors
 | |
|   if (aFd) {
 | |
|     PR_Close(aFd);
 | |
|     if (NS_FAILED(rv) && outFile) {
 | |
|       outFile->Remove(false);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return rv;
 | |
| }
 | |
| 
 | |
| //---------------------------------------------
 | |
| // nsZipArchive::FindInit
 | |
| //---------------------------------------------
 | |
| nsresult nsZipArchive::FindInit(const char* aPattern, nsZipFind** aFind) {
 | |
|   if (!aFind) return NS_ERROR_ILLEGAL_VALUE;
 | |
| 
 | |
|   MutexAutoLock lock(mLock);
 | |
| 
 | |
|   // null out param in case an error happens
 | |
|   *aFind = nullptr;
 | |
| 
 | |
|   bool regExp = false;
 | |
|   char* pattern = 0;
 | |
| 
 | |
|   // Create synthetic directory entries on demand
 | |
|   nsresult rv = BuildSynthetics();
 | |
|   if (rv != NS_OK) return rv;
 | |
| 
 | |
|   // validate the pattern
 | |
|   if (aPattern) {
 | |
|     switch (NS_WildCardValid((char*)aPattern)) {
 | |
|       case INVALID_SXP:
 | |
|         return NS_ERROR_ILLEGAL_VALUE;
 | |
| 
 | |
|       case NON_SXP:
 | |
|         regExp = false;
 | |
|         break;
 | |
| 
 | |
|       case VALID_SXP:
 | |
|         regExp = true;
 | |
|         break;
 | |
| 
 | |
|       default:
 | |
|         // undocumented return value from RegExpValid!
 | |
|         MOZ_ASSERT(false);
 | |
|         return NS_ERROR_ILLEGAL_VALUE;
 | |
|     }
 | |
| 
 | |
|     pattern = strdup(aPattern);
 | |
|     if (!pattern) return NS_ERROR_OUT_OF_MEMORY;
 | |
|   }
 | |
| 
 | |
|   *aFind = new nsZipFind(this, pattern, regExp);
 | |
|   if (!*aFind) {
 | |
|     free(pattern);
 | |
|     return NS_ERROR_OUT_OF_MEMORY;
 | |
|   }
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| //---------------------------------------------
 | |
| // nsZipFind::FindNext
 | |
| //---------------------------------------------
 | |
| nsresult nsZipFind::FindNext(const char** aResult, uint16_t* aNameLen) {
 | |
|   if (!mArchive || !aResult || !aNameLen) return NS_ERROR_ILLEGAL_VALUE;
 | |
| 
 | |
|   MutexAutoLock lock(mArchive->mLock);
 | |
|   *aResult = 0;
 | |
|   *aNameLen = 0;
 | |
|   MMAP_FAULT_HANDLER_BEGIN_HANDLE(mArchive->GetFD())
 | |
|   // we start from last match, look for next
 | |
|   while (mSlot < ZIP_TABSIZE) {
 | |
|     // move to next in current chain, or move to new slot
 | |
|     mItem = mItem ? mItem->next : mArchive->mFiles[mSlot];
 | |
| 
 | |
|     bool found = false;
 | |
|     if (!mItem)
 | |
|       ++mSlot;  // no more in this chain, move to next slot
 | |
|     else if (!mPattern)
 | |
|       found = true;  // always match
 | |
|     else if (mRegExp) {
 | |
|       char buf[kMaxNameLength + 1];
 | |
|       memcpy(buf, mItem->Name(), mItem->nameLength);
 | |
|       buf[mItem->nameLength] = '\0';
 | |
|       found = (NS_WildCardMatch(buf, mPattern, false) == MATCH);
 | |
|     } else
 | |
|       found = ((mItem->nameLength == strlen(mPattern)) &&
 | |
|                (memcmp(mItem->Name(), mPattern, mItem->nameLength) == 0));
 | |
|     if (found) {
 | |
|       // Need also to return the name length, as it is NOT zero-terminatdd...
 | |
|       *aResult = mItem->Name();
 | |
|       *aNameLen = mItem->nameLength;
 | |
|       return NS_OK;
 | |
|     }
 | |
|   }
 | |
|   MMAP_FAULT_HANDLER_CATCH(NS_ERROR_FAILURE)
 | |
|   return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST;
 | |
| }
 | |
| 
 | |
| //***********************************************************
 | |
| //      nsZipArchive  --  private implementation
 | |
| //***********************************************************
 | |
| 
 | |
| //---------------------------------------------
 | |
| //  nsZipArchive::CreateZipItem
 | |
| //---------------------------------------------
 | |
| nsZipItem* nsZipArchive::CreateZipItem() {
 | |
|   // Arena allocate the nsZipItem
 | |
|   return (nsZipItem*)mArena.Allocate(sizeof(nsZipItem), mozilla::fallible);
 | |
| }
 | |
| 
 | |
| //---------------------------------------------
 | |
| //  nsZipArchive::BuildFileList
 | |
| //---------------------------------------------
 | |
| nsresult nsZipArchive::BuildFileList(PRFileDesc* aFd) {
 | |
|   MutexAutoLock lock(mLock);
 | |
| 
 | |
|   // Get archive size using end pos
 | |
|   const uint8_t* buf;
 | |
|   const uint8_t* startp = mFd->mFileData;
 | |
|   const uint8_t* endp = startp + mFd->mLen;
 | |
|   MMAP_FAULT_HANDLER_BEGIN_HANDLE(mFd)
 | |
|   uint32_t centralOffset = 4;
 | |
|   // Only perform readahead in the parent process. Children processes
 | |
|   // don't need readahead when the file has already been readahead by
 | |
|   // the parent process, and readahead only really happens for omni.ja,
 | |
|   // which is used in the parent process.
 | |
|   if (XRE_IsParentProcess() && mFd->mLen > ZIPCENTRAL_SIZE &&
 | |
|       xtolong(startp + centralOffset) == CENTRALSIG) {
 | |
|     // Success means optimized jar layout from bug 559961 is in effect
 | |
|     uint32_t readaheadLength = xtolong(startp);
 | |
|     mozilla::PrefetchMemory(const_cast<uint8_t*>(startp), readaheadLength);
 | |
|   } else {
 | |
|     for (buf = endp - ZIPEND_SIZE; buf > startp; buf--) {
 | |
|       if (xtolong(buf) == ENDSIG) {
 | |
|         centralOffset = xtolong(((ZipEnd*)buf)->offset_central_dir);
 | |
|         break;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (!centralOffset) {
 | |
|     return NS_ERROR_FILE_CORRUPTED;
 | |
|   }
 | |
| 
 | |
|   buf = startp + centralOffset;
 | |
| 
 | |
|   // avoid overflow of startp + centralOffset.
 | |
|   if (buf < startp) {
 | |
|     return NS_ERROR_FILE_CORRUPTED;
 | |
|   }
 | |
| 
 | |
|   //-- Read the central directory headers
 | |
|   uint32_t sig = 0;
 | |
|   while ((buf + int32_t(sizeof(uint32_t)) > buf) &&
 | |
|          (buf + int32_t(sizeof(uint32_t)) <= endp) &&
 | |
|          ((sig = xtolong(buf)) == CENTRALSIG)) {
 | |
|     // Make sure there is enough data available.
 | |
|     if ((buf > endp) || (endp - buf < ZIPCENTRAL_SIZE)) {
 | |
|       return NS_ERROR_FILE_CORRUPTED;
 | |
|     }
 | |
| 
 | |
|     // Read the fixed-size data.
 | |
|     ZipCentral* central = (ZipCentral*)buf;
 | |
| 
 | |
|     uint16_t namelen = xtoint(central->filename_len);
 | |
|     uint16_t extralen = xtoint(central->extrafield_len);
 | |
|     uint16_t commentlen = xtoint(central->commentfield_len);
 | |
|     uint32_t diff = ZIPCENTRAL_SIZE + namelen + extralen + commentlen;
 | |
| 
 | |
|     // Sanity check variable sizes and refuse to deal with
 | |
|     // anything too big: it's likely a corrupt archive.
 | |
|     if (namelen < 1 || namelen > kMaxNameLength) {
 | |
|       return NS_ERROR_FILE_CORRUPTED;
 | |
|     }
 | |
|     if (buf >= buf + diff ||  // No overflow
 | |
|         buf >= endp - diff) {
 | |
|       return NS_ERROR_FILE_CORRUPTED;
 | |
|     }
 | |
| 
 | |
|     // Point to the next item at the top of loop
 | |
|     buf += diff;
 | |
| 
 | |
|     nsZipItem* item = CreateZipItem();
 | |
|     if (!item) return NS_ERROR_OUT_OF_MEMORY;
 | |
| 
 | |
|     item->central = central;
 | |
|     item->nameLength = namelen;
 | |
|     item->isSynthetic = false;
 | |
| 
 | |
|     // Add item to file table
 | |
|     uint32_t hash = HashName(item->Name(), namelen);
 | |
|     item->next = mFiles[hash];
 | |
|     mFiles[hash] = item;
 | |
| 
 | |
|     sig = 0;
 | |
|   } /* while reading central directory records */
 | |
| 
 | |
|   if (sig != ENDSIG) {
 | |
|     return NS_ERROR_FILE_CORRUPTED;
 | |
|   }
 | |
| 
 | |
|   // Make the comment available for consumers.
 | |
|   if ((endp >= buf) && (endp - buf >= ZIPEND_SIZE)) {
 | |
|     ZipEnd* zipend = (ZipEnd*)buf;
 | |
| 
 | |
|     buf += ZIPEND_SIZE;
 | |
|     uint16_t commentlen = xtoint(zipend->commentfield_len);
 | |
|     if (endp - buf >= commentlen) {
 | |
|       mCommentPtr = (const char*)buf;
 | |
|       mCommentLen = commentlen;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   MMAP_FAULT_HANDLER_CATCH(NS_ERROR_FAILURE)
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| //---------------------------------------------
 | |
| //  nsZipArchive::BuildSynthetics
 | |
| //---------------------------------------------
 | |
| nsresult nsZipArchive::BuildSynthetics() {
 | |
|   mLock.AssertCurrentThreadOwns();
 | |
| 
 | |
|   if (mBuiltSynthetics) return NS_OK;
 | |
|   mBuiltSynthetics = true;
 | |
| 
 | |
|   MMAP_FAULT_HANDLER_BEGIN_HANDLE(mFd)
 | |
|   // Create synthetic entries for any missing directories.
 | |
|   // Do this when all ziptable has scanned to prevent double entries.
 | |
|   for (auto* item : mFiles) {
 | |
|     for (; item != nullptr; item = item->next) {
 | |
|       if (item->isSynthetic) continue;
 | |
| 
 | |
|       //-- add entries for directories in the current item's path
 | |
|       //-- go from end to beginning, because then we can stop trying
 | |
|       //-- to create diritems if we find that the diritem we want to
 | |
|       //-- create already exists
 | |
|       //-- start just before the last char so as to not add the item
 | |
|       //-- twice if it's a directory
 | |
|       uint16_t namelen = item->nameLength;
 | |
|       MOZ_ASSERT(namelen > 0,
 | |
|                  "Attempt to build synthetic for zero-length entry name!");
 | |
|       const char* name = item->Name();
 | |
|       for (uint16_t dirlen = namelen - 1; dirlen > 0; dirlen--) {
 | |
|         if (name[dirlen - 1] != '/') continue;
 | |
| 
 | |
|         // The character before this is '/', so if this is also '/' then we
 | |
|         // have an empty path component. Skip it.
 | |
|         if (name[dirlen] == '/') continue;
 | |
| 
 | |
|         // Is the directory already in the file table?
 | |
|         uint32_t hash = HashName(item->Name(), dirlen);
 | |
|         bool found = false;
 | |
|         for (nsZipItem* zi = mFiles[hash]; zi != nullptr; zi = zi->next) {
 | |
|           if ((dirlen == zi->nameLength) &&
 | |
|               (0 == memcmp(item->Name(), zi->Name(), dirlen))) {
 | |
|             // we've already added this dir and all its parents
 | |
|             found = true;
 | |
|             break;
 | |
|           }
 | |
|         }
 | |
|         // if the directory was found, break out of the directory
 | |
|         // creation loop now that we know all implicit directories
 | |
|         // are there -- otherwise, start creating the zip item
 | |
|         if (found) break;
 | |
| 
 | |
|         nsZipItem* diritem = CreateZipItem();
 | |
|         if (!diritem) return NS_ERROR_OUT_OF_MEMORY;
 | |
| 
 | |
|         // Point to the central record of the original item for the name part.
 | |
|         diritem->central = item->central;
 | |
|         diritem->nameLength = dirlen;
 | |
|         diritem->isSynthetic = true;
 | |
| 
 | |
|         // add diritem to the file table
 | |
|         diritem->next = mFiles[hash];
 | |
|         mFiles[hash] = diritem;
 | |
|       } /* end processing of dirs in item's name */
 | |
|     }
 | |
|   }
 | |
|   MMAP_FAULT_HANDLER_CATCH(NS_ERROR_FAILURE)
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsZipHandle* nsZipArchive::GetFD() {
 | |
|   if (!mFd) return nullptr;
 | |
|   return mFd.get();
 | |
| }
 | |
| 
 | |
| //---------------------------------------------
 | |
| // nsZipArchive::GetDataOffset
 | |
| //---------------------------------------------
 | |
| uint32_t nsZipArchive::GetDataOffset(nsZipItem* aItem) {
 | |
|   MOZ_ASSERT(aItem);
 | |
| 
 | |
|   uint32_t offset;
 | |
|   MMAP_FAULT_HANDLER_BEGIN_HANDLE(mFd)
 | |
|   //-- read local header to get variable length values and calculate
 | |
|   //-- the real data offset
 | |
|   uint32_t len = mFd->mLen;
 | |
|   const uint8_t* data = mFd->mFileData;
 | |
|   offset = aItem->LocalOffset();
 | |
|   if (len < ZIPLOCAL_SIZE || offset > len - ZIPLOCAL_SIZE) return 0;
 | |
| 
 | |
|   // -- check signature before using the structure, in case the zip file is
 | |
|   // corrupt
 | |
|   ZipLocal* Local = (ZipLocal*)(data + offset);
 | |
|   if ((xtolong(Local->signature) != LOCALSIG)) return 0;
 | |
| 
 | |
|   //-- NOTE: extralen is different in central header and local header
 | |
|   //--       for archives created using the Unix "zip" utility. To set
 | |
|   //--       the offset accurately we need the _local_ extralen.
 | |
|   offset += ZIPLOCAL_SIZE + xtoint(Local->filename_len) +
 | |
|             xtoint(Local->extrafield_len);
 | |
| 
 | |
|   MMAP_FAULT_HANDLER_CATCH(0)
 | |
|   return offset;
 | |
| }
 | |
| 
 | |
| //---------------------------------------------
 | |
| // nsZipArchive::GetData
 | |
| //---------------------------------------------
 | |
| const uint8_t* nsZipArchive::GetData(nsZipItem* aItem) {
 | |
|   MOZ_ASSERT(aItem);
 | |
|   uint32_t offset = GetDataOffset(aItem);
 | |
| 
 | |
|   MMAP_FAULT_HANDLER_BEGIN_HANDLE(mFd)
 | |
|   // -- check if there is enough source data in the file
 | |
|   if (!offset || mFd->mLen < aItem->Size() ||
 | |
|       offset > mFd->mLen - aItem->Size() ||
 | |
|       (aItem->Compression() == STORED && aItem->Size() != aItem->RealSize())) {
 | |
|     return nullptr;
 | |
|   }
 | |
|   MMAP_FAULT_HANDLER_CATCH(nullptr)
 | |
| 
 | |
|   return mFd->mFileData + offset;
 | |
| }
 | |
| 
 | |
| // nsZipArchive::GetComment
 | |
| bool nsZipArchive::GetComment(nsACString& aComment) {
 | |
|   MMAP_FAULT_HANDLER_BEGIN_BUFFER(mCommentPtr, mCommentLen)
 | |
|   aComment.Assign(mCommentPtr, mCommentLen);
 | |
|   MMAP_FAULT_HANDLER_CATCH(false)
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| //---------------------------------------------
 | |
| // nsZipArchive::SizeOfMapping
 | |
| //---------------------------------------------
 | |
| int64_t nsZipArchive::SizeOfMapping() { return mFd ? mFd->SizeOfMapping() : 0; }
 | |
| 
 | |
| //------------------------------------------
 | |
| // nsZipArchive constructor and destructor
 | |
| //------------------------------------------
 | |
| 
 | |
| nsZipArchive::nsZipArchive()
 | |
|     : mRefCnt(0),
 | |
|       mCommentPtr(nullptr),
 | |
|       mCommentLen(0),
 | |
|       mBuiltSynthetics(false),
 | |
|       mUseZipLog(false) {
 | |
|   // initialize the table to nullptr
 | |
|   memset(mFiles, 0, sizeof(mFiles));
 | |
| }
 | |
| 
 | |
| NS_IMPL_ADDREF(nsZipArchive)
 | |
| NS_IMPL_RELEASE(nsZipArchive)
 | |
| 
 | |
| nsZipArchive::~nsZipArchive() {
 | |
|   CloseArchive();
 | |
| 
 | |
|   if (mUseZipLog) {
 | |
|     zipLog.Release();
 | |
|   }
 | |
| }
 | |
| 
 | |
| //------------------------------------------
 | |
| // nsZipFind constructor and destructor
 | |
| //------------------------------------------
 | |
| 
 | |
| nsZipFind::nsZipFind(nsZipArchive* aZip, char* aPattern, bool aRegExp)
 | |
|     : mArchive(aZip),
 | |
|       mPattern(aPattern),
 | |
|       mItem(nullptr),
 | |
|       mSlot(0),
 | |
|       mRegExp(aRegExp) {
 | |
|   MOZ_COUNT_CTOR(nsZipFind);
 | |
| }
 | |
| 
 | |
| nsZipFind::~nsZipFind() {
 | |
|   free(mPattern);
 | |
| 
 | |
|   MOZ_COUNT_DTOR(nsZipFind);
 | |
| }
 | |
| 
 | |
| //------------------------------------------
 | |
| // helper functions
 | |
| //------------------------------------------
 | |
| 
 | |
| /*
 | |
|  * HashName
 | |
|  *
 | |
|  * returns a hash key for the entry name
 | |
|  */
 | |
| MOZ_NO_SANITIZE_UNSIGNED_OVERFLOW
 | |
| static uint32_t HashName(const char* aName, uint16_t len) {
 | |
|   MOZ_ASSERT(aName != 0);
 | |
| 
 | |
|   const uint8_t* p = (const uint8_t*)aName;
 | |
|   const uint8_t* endp = p + len;
 | |
|   uint32_t val = 0;
 | |
|   while (p != endp) {
 | |
|     val = val * 37 + *p++;
 | |
|   }
 | |
| 
 | |
|   return (val % ZIP_TABSIZE);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  *  x t o i n t
 | |
|  *
 | |
|  *  Converts a two byte ugly endianed integer
 | |
|  *  to our platform's integer.
 | |
|  */
 | |
| static uint16_t xtoint(const uint8_t* ii) {
 | |
|   return (uint16_t)((ii[0]) | (ii[1] << 8));
 | |
| }
 | |
| 
 | |
| /*
 | |
|  *  x t o l o n g
 | |
|  *
 | |
|  *  Converts a four byte ugly endianed integer
 | |
|  *  to our platform's integer.
 | |
|  */
 | |
| static uint32_t xtolong(const uint8_t* ll) {
 | |
|   return (uint32_t)((ll[0] << 0) | (ll[1] << 8) | (ll[2] << 16) |
 | |
|                     (ll[3] << 24));
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * GetModTime
 | |
|  *
 | |
|  * returns last modification time in microseconds
 | |
|  */
 | |
| static PRTime GetModTime(uint16_t aDate, uint16_t aTime) {
 | |
|   // Note that on DST shift we can't handle correctly the hour that is valid
 | |
|   // in both DST zones
 | |
|   PRExplodedTime time;
 | |
| 
 | |
|   time.tm_usec = 0;
 | |
| 
 | |
|   time.tm_hour = (aTime >> 11) & 0x1F;
 | |
|   time.tm_min = (aTime >> 5) & 0x3F;
 | |
|   time.tm_sec = (aTime & 0x1F) * 2;
 | |
| 
 | |
|   time.tm_year = (aDate >> 9) + 1980;
 | |
|   time.tm_month = ((aDate >> 5) & 0x0F) - 1;
 | |
|   time.tm_mday = aDate & 0x1F;
 | |
| 
 | |
|   time.tm_params.tp_gmt_offset = 0;
 | |
|   time.tm_params.tp_dst_offset = 0;
 | |
| 
 | |
|   PR_NormalizeTime(&time, PR_GMTParameters);
 | |
|   time.tm_params.tp_gmt_offset = PR_LocalTimeParameters(&time).tp_gmt_offset;
 | |
|   PR_NormalizeTime(&time, PR_GMTParameters);
 | |
|   time.tm_params.tp_dst_offset = PR_LocalTimeParameters(&time).tp_dst_offset;
 | |
| 
 | |
|   return PR_ImplodeTime(&time);
 | |
| }
 | |
| 
 | |
| nsZipItem::nsZipItem()
 | |
|     : next(nullptr), central(nullptr), nameLength(0), isSynthetic(false) {}
 | |
| 
 | |
| uint32_t nsZipItem::LocalOffset() { return xtolong(central->localhdr_offset); }
 | |
| 
 | |
| uint32_t nsZipItem::Size() { return isSynthetic ? 0 : xtolong(central->size); }
 | |
| 
 | |
| uint32_t nsZipItem::RealSize() {
 | |
|   return isSynthetic ? 0 : xtolong(central->orglen);
 | |
| }
 | |
| 
 | |
| uint32_t nsZipItem::CRC32() {
 | |
|   return isSynthetic ? 0 : xtolong(central->crc32);
 | |
| }
 | |
| 
 | |
| uint16_t nsZipItem::Date() {
 | |
|   return isSynthetic ? kSyntheticDate : xtoint(central->date);
 | |
| }
 | |
| 
 | |
| uint16_t nsZipItem::Time() {
 | |
|   return isSynthetic ? kSyntheticTime : xtoint(central->time);
 | |
| }
 | |
| 
 | |
| uint16_t nsZipItem::Compression() {
 | |
|   return isSynthetic ? STORED : xtoint(central->method);
 | |
| }
 | |
| 
 | |
| bool nsZipItem::IsDirectory() {
 | |
|   return isSynthetic || ((nameLength > 0) && ('/' == Name()[nameLength - 1]));
 | |
| }
 | |
| 
 | |
| uint16_t nsZipItem::Mode() {
 | |
|   if (isSynthetic) return 0755;
 | |
|   return ((uint16_t)(central->external_attributes[2]) | 0x100);
 | |
| }
 | |
| 
 | |
| const uint8_t* nsZipItem::GetExtraField(uint16_t aTag, uint16_t* aBlockSize) {
 | |
|   if (isSynthetic) return nullptr;
 | |
| 
 | |
|   const unsigned char* buf =
 | |
|       ((const unsigned char*)central) + ZIPCENTRAL_SIZE + nameLength;
 | |
|   uint32_t buflen;
 | |
| 
 | |
|   MMAP_FAULT_HANDLER_BEGIN_BUFFER(central, ZIPCENTRAL_SIZE + nameLength)
 | |
|   buflen = (uint32_t)xtoint(central->extrafield_len);
 | |
|   MMAP_FAULT_HANDLER_CATCH(nullptr)
 | |
| 
 | |
|   uint32_t pos = 0;
 | |
|   uint16_t tag, blocksize;
 | |
| 
 | |
|   MMAP_FAULT_HANDLER_BEGIN_BUFFER(buf, buflen)
 | |
|   while (buf && (pos + 4) <= buflen) {
 | |
|     tag = xtoint(buf + pos);
 | |
|     blocksize = xtoint(buf + pos + 2);
 | |
| 
 | |
|     if (aTag == tag && (pos + 4 + blocksize) <= buflen) {
 | |
|       *aBlockSize = blocksize;
 | |
|       return buf + pos;
 | |
|     }
 | |
| 
 | |
|     pos += blocksize + 4;
 | |
|   }
 | |
|   MMAP_FAULT_HANDLER_CATCH(nullptr)
 | |
| 
 | |
|   return nullptr;
 | |
| }
 | |
| 
 | |
| PRTime nsZipItem::LastModTime() {
 | |
|   if (isSynthetic) return GetModTime(kSyntheticDate, kSyntheticTime);
 | |
| 
 | |
|   // Try to read timestamp from extra field
 | |
|   uint16_t blocksize;
 | |
|   const uint8_t* tsField = GetExtraField(EXTENDED_TIMESTAMP_FIELD, &blocksize);
 | |
|   if (tsField && blocksize >= 5 && tsField[4] & EXTENDED_TIMESTAMP_MODTIME) {
 | |
|     return (PRTime)(xtolong(tsField + 5)) * PR_USEC_PER_SEC;
 | |
|   }
 | |
| 
 | |
|   return GetModTime(Date(), Time());
 | |
| }
 | |
| 
 | |
| nsZipCursor::nsZipCursor(nsZipItem* item, nsZipArchive* aZip, uint8_t* aBuf,
 | |
|                          uint32_t aBufSize, bool doCRC)
 | |
|     : mItem(item),
 | |
|       mBuf(aBuf),
 | |
|       mBufSize(aBufSize),
 | |
|       mZs(),
 | |
|       mCRC(0),
 | |
|       mDoCRC(doCRC) {
 | |
|   if (mItem->Compression() == DEFLATED) {
 | |
| #ifdef DEBUG
 | |
|     nsresult status =
 | |
| #endif
 | |
|         gZlibInit(&mZs);
 | |
|     NS_ASSERTION(status == NS_OK, "Zlib failed to initialize");
 | |
|     NS_ASSERTION(aBuf, "Must pass in a buffer for DEFLATED nsZipItem");
 | |
|   }
 | |
| 
 | |
|   mZs.avail_in = item->Size();
 | |
|   mZs.next_in = (Bytef*)aZip->GetData(item);
 | |
| 
 | |
|   if (doCRC) mCRC = crc32(0L, Z_NULL, 0);
 | |
| }
 | |
| 
 | |
| nsZipCursor::~nsZipCursor() {
 | |
|   if (mItem->Compression() == DEFLATED) {
 | |
|     inflateEnd(&mZs);
 | |
|   }
 | |
| }
 | |
| 
 | |
| uint8_t* nsZipCursor::ReadOrCopy(uint32_t* aBytesRead, bool aCopy) {
 | |
|   int zerr;
 | |
|   uint8_t* buf = nullptr;
 | |
|   bool verifyCRC = true;
 | |
| 
 | |
|   if (!mZs.next_in) return nullptr;
 | |
|   MMAP_FAULT_HANDLER_BEGIN_BUFFER(mZs.next_in, mZs.avail_in)
 | |
|   switch (mItem->Compression()) {
 | |
|     case STORED:
 | |
|       if (!aCopy) {
 | |
|         *aBytesRead = mZs.avail_in;
 | |
|         buf = mZs.next_in;
 | |
|         mZs.next_in += mZs.avail_in;
 | |
|         mZs.avail_in = 0;
 | |
|       } else {
 | |
|         *aBytesRead = mZs.avail_in > mBufSize ? mBufSize : mZs.avail_in;
 | |
|         memcpy(mBuf, mZs.next_in, *aBytesRead);
 | |
|         mZs.avail_in -= *aBytesRead;
 | |
|         mZs.next_in += *aBytesRead;
 | |
|       }
 | |
|       break;
 | |
|     case DEFLATED:
 | |
|       buf = mBuf;
 | |
|       mZs.next_out = buf;
 | |
|       mZs.avail_out = mBufSize;
 | |
| 
 | |
|       zerr = inflate(&mZs, Z_PARTIAL_FLUSH);
 | |
|       if (zerr != Z_OK && zerr != Z_STREAM_END) return nullptr;
 | |
| 
 | |
|       *aBytesRead = mZs.next_out - buf;
 | |
|       verifyCRC = (zerr == Z_STREAM_END);
 | |
|       break;
 | |
|     default:
 | |
|       return nullptr;
 | |
|   }
 | |
| 
 | |
|   if (mDoCRC) {
 | |
|     mCRC = crc32(mCRC, (const unsigned char*)buf, *aBytesRead);
 | |
|     if (verifyCRC && mCRC != mItem->CRC32()) return nullptr;
 | |
|   }
 | |
|   MMAP_FAULT_HANDLER_CATCH(nullptr)
 | |
|   return buf;
 | |
| }
 | |
| 
 | |
| nsZipItemPtr_base::nsZipItemPtr_base(nsZipArchive* aZip, const char* aEntryName,
 | |
|                                      bool doCRC)
 | |
|     : mReturnBuf(nullptr), mReadlen(0) {
 | |
|   // make sure the ziparchive hangs around
 | |
|   mZipHandle = aZip->GetFD();
 | |
| 
 | |
|   nsZipItem* item = aZip->GetItem(aEntryName);
 | |
|   if (!item) return;
 | |
| 
 | |
|   uint32_t size = 0;
 | |
|   bool compressed = (item->Compression() == DEFLATED);
 | |
|   if (compressed) {
 | |
|     size = item->RealSize();
 | |
|     mAutoBuf = MakeUniqueFallible<uint8_t[]>(size);
 | |
|     if (!mAutoBuf) {
 | |
|       return;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   nsZipCursor cursor(item, aZip, mAutoBuf.get(), size, doCRC);
 | |
|   mReturnBuf = cursor.Read(&mReadlen);
 | |
|   if (!mReturnBuf) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (mReadlen != item->RealSize()) {
 | |
|     NS_ASSERTION(mReadlen == item->RealSize(), "nsZipCursor underflow");
 | |
|     mReturnBuf = nullptr;
 | |
|     return;
 | |
|   }
 | |
| }
 | 
