1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/modules/libjar/nsZipArchive.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1148 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +/* 1.10 + * This module implements a simple archive extractor for the PKZIP format. 1.11 + * 1.12 + * The underlying nsZipArchive is NOT thread-safe. Do not pass references 1.13 + * or pointers to it across thread boundaries. 1.14 + */ 1.15 + 1.16 +// This must be the first include in the file in order for the 1.17 +// PL_ARENA_CONST_ALIGN_MASK macro to be effective. 1.18 +#define PL_ARENA_CONST_ALIGN_MASK (sizeof(void*)-1) 1.19 +#include "plarena.h" 1.20 + 1.21 +#define READTYPE int32_t 1.22 +#include "zlib.h" 1.23 +#include "nsISupportsUtils.h" 1.24 +#include "prio.h" 1.25 +#include "plstr.h" 1.26 +#include "prlog.h" 1.27 +#include "stdlib.h" 1.28 +#include "nsWildCard.h" 1.29 +#include "nsZipArchive.h" 1.30 +#include "nsString.h" 1.31 +#include "prenv.h" 1.32 +#if defined(XP_WIN) 1.33 +#include <windows.h> 1.34 +#endif 1.35 + 1.36 +// For placement new used for arena allocations of zip file list 1.37 +#include <new> 1.38 +#define ZIP_ARENABLOCKSIZE (1*1024) 1.39 + 1.40 +#ifdef XP_UNIX 1.41 + #include <sys/mman.h> 1.42 + #include <sys/types.h> 1.43 + #include <sys/stat.h> 1.44 + #include <limits.h> 1.45 + #include <unistd.h> 1.46 +#elif defined(XP_WIN) 1.47 + #include <io.h> 1.48 +#endif 1.49 + 1.50 +#ifdef __SYMBIAN32__ 1.51 + #include <sys/syslimits.h> 1.52 +#endif /*__SYMBIAN32__*/ 1.53 + 1.54 + 1.55 +#ifndef XP_UNIX /* we need some constants defined in limits.h and unistd.h */ 1.56 +# ifndef S_IFMT 1.57 +# define S_IFMT 0170000 1.58 +# endif 1.59 +# ifndef S_IFLNK 1.60 +# define S_IFLNK 0120000 1.61 +# endif 1.62 +# ifndef PATH_MAX 1.63 +# define PATH_MAX 1024 1.64 +# endif 1.65 +#endif /* XP_UNIX */ 1.66 + 1.67 +#ifdef XP_WIN 1.68 +#include "private/pprio.h" // To get PR_ImportFile 1.69 +#endif 1.70 + 1.71 +using namespace mozilla; 1.72 + 1.73 +static const uint32_t kMaxNameLength = PATH_MAX; /* Maximum name length */ 1.74 +// For synthetic zip entries. Date/time corresponds to 1980-01-01 00:00. 1.75 +static const uint16_t kSyntheticTime = 0; 1.76 +static const uint16_t kSyntheticDate = (1 + (1 << 5) + (0 << 9)); 1.77 + 1.78 +static uint16_t xtoint(const uint8_t *ii); 1.79 +static uint32_t xtolong(const uint8_t *ll); 1.80 +static uint32_t HashName(const char* aName, uint16_t nameLen); 1.81 +#ifdef XP_UNIX 1.82 +static nsresult ResolveSymlink(const char *path); 1.83 +#endif 1.84 + 1.85 +class ZipArchiveLogger { 1.86 +public: 1.87 + void Write(const nsACString &zip, const char *entry) const { 1.88 + if (!fd) { 1.89 + char *env = PR_GetEnv("MOZ_JAR_LOG_FILE"); 1.90 + if (!env) 1.91 + return; 1.92 + 1.93 + nsCOMPtr<nsIFile> logFile; 1.94 + nsresult rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(env), false, getter_AddRefs(logFile)); 1.95 + if (NS_FAILED(rv)) 1.96 + return; 1.97 + 1.98 + // Create the log file and its parent directory (in case it doesn't exist) 1.99 + logFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644); 1.100 + 1.101 + PRFileDesc* file; 1.102 +#ifdef XP_WIN 1.103 + // PR_APPEND is racy on Windows, so open a handle ourselves with flags that 1.104 + // will work, and use PR_ImportFile to make it a PRFileDesc. 1.105 + // This can go away when bug 840435 is fixed. 1.106 + nsAutoString path; 1.107 + logFile->GetPath(path); 1.108 + if (path.IsEmpty()) 1.109 + return; 1.110 + HANDLE handle = CreateFileW(path.get(), FILE_APPEND_DATA, FILE_SHARE_WRITE, 1.111 + nullptr, OPEN_ALWAYS, 0, nullptr); 1.112 + if (handle == INVALID_HANDLE_VALUE) 1.113 + return; 1.114 + file = PR_ImportFile((PROsfd)handle); 1.115 + if (!file) 1.116 + return; 1.117 +#else 1.118 + rv = logFile->OpenNSPRFileDesc(PR_WRONLY|PR_CREATE_FILE|PR_APPEND, 0644, &file); 1.119 + if (NS_FAILED(rv)) 1.120 + return; 1.121 +#endif 1.122 + fd = file; 1.123 + } 1.124 + nsCString buf(zip); 1.125 + buf.Append(" "); 1.126 + buf.Append(entry); 1.127 + buf.Append('\n'); 1.128 + PR_Write(fd, buf.get(), buf.Length()); 1.129 + } 1.130 + 1.131 + void AddRef() { 1.132 + MOZ_ASSERT(refCnt >= 0); 1.133 + ++refCnt; 1.134 + } 1.135 + 1.136 + void Release() { 1.137 + MOZ_ASSERT(refCnt > 0); 1.138 + if ((0 == --refCnt) && fd) { 1.139 + PR_Close(fd); 1.140 + fd = nullptr; 1.141 + } 1.142 + } 1.143 +private: 1.144 + int refCnt; 1.145 + mutable PRFileDesc *fd; 1.146 +}; 1.147 + 1.148 +static ZipArchiveLogger zipLog; 1.149 + 1.150 +//*********************************************************** 1.151 +// For every inflation the following allocations are done: 1.152 +// malloc(1 * 9520) 1.153 +// malloc(32768 * 1) 1.154 +//*********************************************************** 1.155 + 1.156 +nsresult gZlibInit(z_stream *zs) 1.157 +{ 1.158 + memset(zs, 0, sizeof(z_stream)); 1.159 + int zerr = inflateInit2(zs, -MAX_WBITS); 1.160 + if (zerr != Z_OK) return NS_ERROR_OUT_OF_MEMORY; 1.161 + 1.162 + return NS_OK; 1.163 +} 1.164 + 1.165 +nsZipHandle::nsZipHandle() 1.166 + : mFileData(nullptr) 1.167 + , mLen(0) 1.168 + , mMap(nullptr) 1.169 + , mRefCnt(0) 1.170 +{ 1.171 + MOZ_COUNT_CTOR(nsZipHandle); 1.172 +} 1.173 + 1.174 +NS_IMPL_ADDREF(nsZipHandle) 1.175 +NS_IMPL_RELEASE(nsZipHandle) 1.176 + 1.177 +nsresult nsZipHandle::Init(nsIFile *file, nsZipHandle **ret, PRFileDesc **aFd) 1.178 +{ 1.179 + mozilla::AutoFDClose fd; 1.180 + int32_t flags = PR_RDONLY; 1.181 +#if defined(XP_WIN) 1.182 + flags |= nsIFile::OS_READAHEAD; 1.183 +#endif 1.184 + nsresult rv = file->OpenNSPRFileDesc(flags, 0000, &fd.rwget()); 1.185 + if (NS_FAILED(rv)) 1.186 + return rv; 1.187 + 1.188 + int64_t size = PR_Available64(fd); 1.189 + if (size >= INT32_MAX) 1.190 + return NS_ERROR_FILE_TOO_BIG; 1.191 + 1.192 + PRFileMap *map = PR_CreateFileMap(fd, size, PR_PROT_READONLY); 1.193 + if (!map) 1.194 + return NS_ERROR_FAILURE; 1.195 + 1.196 + uint8_t *buf = (uint8_t*) PR_MemMap(map, 0, (uint32_t) size); 1.197 + // Bug 525755: PR_MemMap fails when fd points at something other than a normal file. 1.198 + if (!buf) { 1.199 + PR_CloseFileMap(map); 1.200 + return NS_ERROR_FAILURE; 1.201 + } 1.202 + 1.203 + nsRefPtr<nsZipHandle> handle = new nsZipHandle(); 1.204 + if (!handle) { 1.205 + PR_MemUnmap(buf, (uint32_t) size); 1.206 + PR_CloseFileMap(map); 1.207 + return NS_ERROR_OUT_OF_MEMORY; 1.208 + } 1.209 + 1.210 +#if defined(XP_WIN) 1.211 + if (aFd) { 1.212 + *aFd = fd.forget(); 1.213 + } 1.214 +#endif 1.215 + handle->mMap = map; 1.216 + handle->mFile.Init(file); 1.217 + handle->mLen = (uint32_t) size; 1.218 + handle->mFileData = buf; 1.219 + handle.forget(ret); 1.220 + return NS_OK; 1.221 +} 1.222 + 1.223 +nsresult nsZipHandle::Init(nsZipArchive *zip, const char *entry, 1.224 + nsZipHandle **ret) 1.225 +{ 1.226 + nsRefPtr<nsZipHandle> handle = new nsZipHandle(); 1.227 + if (!handle) 1.228 + return NS_ERROR_OUT_OF_MEMORY; 1.229 + 1.230 + handle->mBuf = new nsZipItemPtr<uint8_t>(zip, entry); 1.231 + if (!handle->mBuf) 1.232 + return NS_ERROR_OUT_OF_MEMORY; 1.233 + 1.234 + if (!handle->mBuf->Buffer()) 1.235 + return NS_ERROR_UNEXPECTED; 1.236 + 1.237 + handle->mMap = nullptr; 1.238 + handle->mFile.Init(zip, entry); 1.239 + handle->mLen = handle->mBuf->Length(); 1.240 + handle->mFileData = handle->mBuf->Buffer(); 1.241 + handle.forget(ret); 1.242 + return NS_OK; 1.243 +} 1.244 + 1.245 +int64_t nsZipHandle::SizeOfMapping() 1.246 +{ 1.247 + return mLen; 1.248 +} 1.249 + 1.250 +nsZipHandle::~nsZipHandle() 1.251 +{ 1.252 + if (mMap) { 1.253 + PR_MemUnmap((void *)mFileData, mLen); 1.254 + PR_CloseFileMap(mMap); 1.255 + } 1.256 + mFileData = nullptr; 1.257 + mMap = nullptr; 1.258 + mBuf = nullptr; 1.259 + MOZ_COUNT_DTOR(nsZipHandle); 1.260 +} 1.261 + 1.262 +//*********************************************************** 1.263 +// nsZipArchive -- public methods 1.264 +//*********************************************************** 1.265 + 1.266 +//--------------------------------------------- 1.267 +// nsZipArchive::OpenArchive 1.268 +//--------------------------------------------- 1.269 +nsresult nsZipArchive::OpenArchive(nsZipHandle *aZipHandle, PRFileDesc *aFd) 1.270 +{ 1.271 + mFd = aZipHandle; 1.272 + 1.273 + // Initialize our arena 1.274 + PL_INIT_ARENA_POOL(&mArena, "ZipArena", ZIP_ARENABLOCKSIZE); 1.275 + 1.276 + //-- get table of contents for archive 1.277 + nsresult rv = BuildFileList(aFd); 1.278 + if (NS_SUCCEEDED(rv)) { 1.279 + if (aZipHandle->mFile) 1.280 + aZipHandle->mFile.GetURIString(mURI); 1.281 + } 1.282 + return rv; 1.283 +} 1.284 + 1.285 +nsresult nsZipArchive::OpenArchive(nsIFile *aFile) 1.286 +{ 1.287 + nsRefPtr<nsZipHandle> handle; 1.288 +#if defined(XP_WIN) 1.289 + mozilla::AutoFDClose fd; 1.290 + nsresult rv = nsZipHandle::Init(aFile, getter_AddRefs(handle), &fd.rwget()); 1.291 +#else 1.292 + nsresult rv = nsZipHandle::Init(aFile, getter_AddRefs(handle)); 1.293 +#endif 1.294 + if (NS_FAILED(rv)) 1.295 + return rv; 1.296 + 1.297 +#if defined(XP_WIN) 1.298 + return OpenArchive(handle, fd.get()); 1.299 +#else 1.300 + return OpenArchive(handle); 1.301 +#endif 1.302 +} 1.303 + 1.304 +//--------------------------------------------- 1.305 +// nsZipArchive::Test 1.306 +//--------------------------------------------- 1.307 +nsresult nsZipArchive::Test(const char *aEntryName) 1.308 +{ 1.309 + nsZipItem* currItem; 1.310 + 1.311 + if (aEntryName) // only test specified item 1.312 + { 1.313 + currItem = GetItem(aEntryName); 1.314 + if (!currItem) 1.315 + return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST; 1.316 + //-- don't test (synthetic) directory items 1.317 + if (currItem->IsDirectory()) 1.318 + return NS_OK; 1.319 + return ExtractFile(currItem, 0, 0); 1.320 + } 1.321 + 1.322 + // test all items in archive 1.323 + for (int i = 0; i < ZIP_TABSIZE; i++) { 1.324 + for (currItem = mFiles[i]; currItem; currItem = currItem->next) { 1.325 + //-- don't test (synthetic) directory items 1.326 + if (currItem->IsDirectory()) 1.327 + continue; 1.328 + nsresult rv = ExtractFile(currItem, 0, 0); 1.329 + if (rv != NS_OK) 1.330 + return rv; 1.331 + } 1.332 + } 1.333 + 1.334 + return NS_OK; 1.335 +} 1.336 + 1.337 +//--------------------------------------------- 1.338 +// nsZipArchive::CloseArchive 1.339 +//--------------------------------------------- 1.340 +nsresult nsZipArchive::CloseArchive() 1.341 +{ 1.342 + if (mFd) { 1.343 + PL_FinishArenaPool(&mArena); 1.344 + mFd = nullptr; 1.345 + } 1.346 + 1.347 + // CAUTION: 1.348 + // We don't need to delete each of the nsZipItem as the memory for 1.349 + // the zip item and the filename it holds are both allocated from the Arena. 1.350 + // Hence, destroying the Arena is like destroying all the memory 1.351 + // for all the nsZipItem in one shot. But if the ~nsZipItem is doing 1.352 + // anything more than cleaning up memory, we should start calling it. 1.353 + // Let us also cleanup the mFiles table for re-use on the next 'open' call 1.354 + memset(mFiles, 0, sizeof(mFiles)); 1.355 + mBuiltSynthetics = false; 1.356 + return NS_OK; 1.357 +} 1.358 + 1.359 +//--------------------------------------------- 1.360 +// nsZipArchive::GetItem 1.361 +//--------------------------------------------- 1.362 +nsZipItem* nsZipArchive::GetItem(const char * aEntryName) 1.363 +{ 1.364 + if (aEntryName) { 1.365 + uint32_t len = strlen(aEntryName); 1.366 + //-- If the request is for a directory, make sure that synthetic entries 1.367 + //-- are created for the directories without their own entry. 1.368 + if (!mBuiltSynthetics) { 1.369 + if ((len > 0) && (aEntryName[len-1] == '/')) { 1.370 + if (BuildSynthetics() != NS_OK) 1.371 + return 0; 1.372 + } 1.373 + } 1.374 +MOZ_WIN_MEM_TRY_BEGIN 1.375 + nsZipItem* item = mFiles[ HashName(aEntryName, len) ]; 1.376 + while (item) { 1.377 + if ((len == item->nameLength) && 1.378 + (!memcmp(aEntryName, item->Name(), len))) { 1.379 + 1.380 + // Successful GetItem() is a good indicator that the file is about to be read 1.381 + zipLog.Write(mURI, aEntryName); 1.382 + return item; //-- found it 1.383 + } 1.384 + item = item->next; 1.385 + } 1.386 +MOZ_WIN_MEM_TRY_CATCH(return nullptr) 1.387 + } 1.388 + return nullptr; 1.389 +} 1.390 + 1.391 +//--------------------------------------------- 1.392 +// nsZipArchive::ExtractFile 1.393 +// This extracts the item to the filehandle provided. 1.394 +// If 'aFd' is null, it only tests the extraction. 1.395 +// On extraction error(s) it removes the file. 1.396 +// When needed, it also resolves the symlink. 1.397 +//--------------------------------------------- 1.398 +nsresult nsZipArchive::ExtractFile(nsZipItem *item, const char *outname, 1.399 + PRFileDesc* aFd) 1.400 +{ 1.401 + if (!item) 1.402 + return NS_ERROR_ILLEGAL_VALUE; 1.403 + if (!mFd) 1.404 + return NS_ERROR_FAILURE; 1.405 + 1.406 + // Directory extraction is handled in nsJAR::Extract, 1.407 + // so the item to be extracted should never be a directory 1.408 + PR_ASSERT(!item->IsDirectory()); 1.409 + 1.410 + Bytef outbuf[ZIP_BUFLEN]; 1.411 + 1.412 + nsZipCursor cursor(item, this, outbuf, ZIP_BUFLEN, true); 1.413 + 1.414 + nsresult rv = NS_OK; 1.415 + 1.416 + while (true) { 1.417 + uint32_t count = 0; 1.418 + uint8_t* buf = cursor.Read(&count); 1.419 + if (!buf) { 1.420 + rv = NS_ERROR_FILE_CORRUPTED; 1.421 + break; 1.422 + } else if (count == 0) { 1.423 + break; 1.424 + } 1.425 + 1.426 + if (aFd && PR_Write(aFd, buf, count) < (READTYPE)count) { 1.427 + rv = NS_ERROR_FILE_DISK_FULL; 1.428 + break; 1.429 + } 1.430 + } 1.431 + 1.432 + //-- delete the file on errors, or resolve symlink if needed 1.433 + if (aFd) { 1.434 + PR_Close(aFd); 1.435 + if (rv != NS_OK) 1.436 + PR_Delete(outname); 1.437 +#ifdef XP_UNIX 1.438 + else if (item->IsSymlink()) 1.439 + rv = ResolveSymlink(outname); 1.440 +#endif 1.441 + } 1.442 + 1.443 + return rv; 1.444 +} 1.445 + 1.446 +//--------------------------------------------- 1.447 +// nsZipArchive::FindInit 1.448 +//--------------------------------------------- 1.449 +nsresult 1.450 +nsZipArchive::FindInit(const char * aPattern, nsZipFind **aFind) 1.451 +{ 1.452 + if (!aFind) 1.453 + return NS_ERROR_ILLEGAL_VALUE; 1.454 + 1.455 + // null out param in case an error happens 1.456 + *aFind = nullptr; 1.457 + 1.458 + bool regExp = false; 1.459 + char* pattern = 0; 1.460 + 1.461 + // Create synthetic directory entries on demand 1.462 + nsresult rv = BuildSynthetics(); 1.463 + if (rv != NS_OK) 1.464 + return rv; 1.465 + 1.466 + // validate the pattern 1.467 + if (aPattern) 1.468 + { 1.469 + switch (NS_WildCardValid((char*)aPattern)) 1.470 + { 1.471 + case INVALID_SXP: 1.472 + return NS_ERROR_ILLEGAL_VALUE; 1.473 + 1.474 + case NON_SXP: 1.475 + regExp = false; 1.476 + break; 1.477 + 1.478 + case VALID_SXP: 1.479 + regExp = true; 1.480 + break; 1.481 + 1.482 + default: 1.483 + // undocumented return value from RegExpValid! 1.484 + PR_ASSERT(false); 1.485 + return NS_ERROR_ILLEGAL_VALUE; 1.486 + } 1.487 + 1.488 + pattern = PL_strdup(aPattern); 1.489 + if (!pattern) 1.490 + return NS_ERROR_OUT_OF_MEMORY; 1.491 + } 1.492 + 1.493 + *aFind = new nsZipFind(this, pattern, regExp); 1.494 + if (!*aFind) { 1.495 + PL_strfree(pattern); 1.496 + return NS_ERROR_OUT_OF_MEMORY; 1.497 + } 1.498 + 1.499 + return NS_OK; 1.500 +} 1.501 + 1.502 + 1.503 + 1.504 +//--------------------------------------------- 1.505 +// nsZipFind::FindNext 1.506 +//--------------------------------------------- 1.507 +nsresult nsZipFind::FindNext(const char ** aResult, uint16_t *aNameLen) 1.508 +{ 1.509 + if (!mArchive || !aResult || !aNameLen) 1.510 + return NS_ERROR_ILLEGAL_VALUE; 1.511 + 1.512 + *aResult = 0; 1.513 + *aNameLen = 0; 1.514 +MOZ_WIN_MEM_TRY_BEGIN 1.515 + // we start from last match, look for next 1.516 + while (mSlot < ZIP_TABSIZE) 1.517 + { 1.518 + // move to next in current chain, or move to new slot 1.519 + mItem = mItem ? mItem->next : mArchive->mFiles[mSlot]; 1.520 + 1.521 + bool found = false; 1.522 + if (!mItem) 1.523 + ++mSlot; // no more in this chain, move to next slot 1.524 + else if (!mPattern) 1.525 + found = true; // always match 1.526 + else if (mRegExp) 1.527 + { 1.528 + char buf[kMaxNameLength+1]; 1.529 + memcpy(buf, mItem->Name(), mItem->nameLength); 1.530 + buf[mItem->nameLength]='\0'; 1.531 + found = (NS_WildCardMatch(buf, mPattern, false) == MATCH); 1.532 + } 1.533 + else 1.534 + found = ((mItem->nameLength == strlen(mPattern)) && 1.535 + (memcmp(mItem->Name(), mPattern, mItem->nameLength) == 0)); 1.536 + if (found) { 1.537 + // Need also to return the name length, as it is NOT zero-terminatdd... 1.538 + *aResult = mItem->Name(); 1.539 + *aNameLen = mItem->nameLength; 1.540 + return NS_OK; 1.541 + } 1.542 + } 1.543 +MOZ_WIN_MEM_TRY_CATCH(return NS_ERROR_FAILURE) 1.544 + return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST; 1.545 +} 1.546 + 1.547 +#ifdef XP_UNIX 1.548 +//--------------------------------------------- 1.549 +// ResolveSymlink 1.550 +//--------------------------------------------- 1.551 +static nsresult ResolveSymlink(const char *path) 1.552 +{ 1.553 + PRFileDesc * fIn = PR_Open(path, PR_RDONLY, 0000); 1.554 + if (!fIn) 1.555 + return NS_ERROR_FILE_DISK_FULL; 1.556 + 1.557 + char buf[PATH_MAX+1]; 1.558 + int32_t length = PR_Read(fIn, (void*)buf, PATH_MAX); 1.559 + PR_Close(fIn); 1.560 + 1.561 + if ( (length <= 0) 1.562 + || ((buf[length] = 0, PR_Delete(path)) != 0) 1.563 + || (symlink(buf, path) != 0)) 1.564 + { 1.565 + return NS_ERROR_FILE_DISK_FULL; 1.566 + } 1.567 + return NS_OK; 1.568 +} 1.569 +#endif 1.570 + 1.571 +//*********************************************************** 1.572 +// nsZipArchive -- private implementation 1.573 +//*********************************************************** 1.574 + 1.575 +//--------------------------------------------- 1.576 +// nsZipArchive::CreateZipItem 1.577 +//--------------------------------------------- 1.578 +nsZipItem* nsZipArchive::CreateZipItem() 1.579 +{ 1.580 + // Arena allocate the nsZipItem 1.581 + void *mem; 1.582 + PL_ARENA_ALLOCATE(mem, &mArena, sizeof(nsZipItem)); 1.583 + return (nsZipItem*)mem; 1.584 +} 1.585 + 1.586 +//--------------------------------------------- 1.587 +// nsZipArchive::BuildFileList 1.588 +//--------------------------------------------- 1.589 +nsresult nsZipArchive::BuildFileList(PRFileDesc *aFd) 1.590 +{ 1.591 + // Get archive size using end pos 1.592 + const uint8_t* buf; 1.593 + const uint8_t* startp = mFd->mFileData; 1.594 + const uint8_t* endp = startp + mFd->mLen; 1.595 +MOZ_WIN_MEM_TRY_BEGIN 1.596 + uint32_t centralOffset = 4; 1.597 + if (mFd->mLen > ZIPCENTRAL_SIZE && xtolong(startp + centralOffset) == CENTRALSIG) { 1.598 + // Success means optimized jar layout from bug 559961 is in effect 1.599 + uint32_t readaheadLength = xtolong(startp); 1.600 + if (readaheadLength) { 1.601 +#if defined(XP_UNIX) 1.602 + madvise(const_cast<uint8_t*>(startp), readaheadLength, MADV_WILLNEED); 1.603 +#elif defined(XP_WIN) 1.604 + if (aFd) { 1.605 + HANDLE hFile = (HANDLE) PR_FileDesc2NativeHandle(aFd); 1.606 + mozilla::ReadAhead(hFile, 0, readaheadLength); 1.607 + } 1.608 +#endif 1.609 + } 1.610 + } else { 1.611 + for (buf = endp - ZIPEND_SIZE; buf > startp; buf--) 1.612 + { 1.613 + if (xtolong(buf) == ENDSIG) { 1.614 + centralOffset = xtolong(((ZipEnd *)buf)->offset_central_dir); 1.615 + break; 1.616 + } 1.617 + } 1.618 + } 1.619 + 1.620 + if (!centralOffset) 1.621 + return NS_ERROR_FILE_CORRUPTED; 1.622 + 1.623 + //-- Read the central directory headers 1.624 + buf = startp + centralOffset; 1.625 + uint32_t sig = 0; 1.626 + while (buf + int32_t(sizeof(uint32_t)) <= endp && 1.627 + (sig = xtolong(buf)) == CENTRALSIG) { 1.628 + // Make sure there is enough data available. 1.629 + if (endp - buf < ZIPCENTRAL_SIZE) 1.630 + return NS_ERROR_FILE_CORRUPTED; 1.631 + 1.632 + // Read the fixed-size data. 1.633 + ZipCentral* central = (ZipCentral*)buf; 1.634 + 1.635 + uint16_t namelen = xtoint(central->filename_len); 1.636 + uint16_t extralen = xtoint(central->extrafield_len); 1.637 + uint16_t commentlen = xtoint(central->commentfield_len); 1.638 + 1.639 + // Point to the next item at the top of loop 1.640 + buf += ZIPCENTRAL_SIZE + namelen + extralen + commentlen; 1.641 + 1.642 + // Sanity check variable sizes and refuse to deal with 1.643 + // anything too big: it's likely a corrupt archive. 1.644 + if (namelen < 1 || 1.645 + namelen > kMaxNameLength || 1.646 + buf >= endp) { 1.647 + return NS_ERROR_FILE_CORRUPTED; 1.648 + } 1.649 + 1.650 + nsZipItem* item = CreateZipItem(); 1.651 + if (!item) 1.652 + return NS_ERROR_OUT_OF_MEMORY; 1.653 + 1.654 + item->central = central; 1.655 + item->nameLength = namelen; 1.656 + item->isSynthetic = false; 1.657 + 1.658 + // Add item to file table 1.659 + uint32_t hash = HashName(item->Name(), namelen); 1.660 + item->next = mFiles[hash]; 1.661 + mFiles[hash] = item; 1.662 + 1.663 + sig = 0; 1.664 + } /* while reading central directory records */ 1.665 + 1.666 + if (sig != ENDSIG) 1.667 + return NS_ERROR_FILE_CORRUPTED; 1.668 + 1.669 + // Make the comment available for consumers. 1.670 + if (endp - buf >= ZIPEND_SIZE) { 1.671 + ZipEnd *zipend = (ZipEnd *)buf; 1.672 + 1.673 + buf += ZIPEND_SIZE; 1.674 + uint16_t commentlen = xtoint(zipend->commentfield_len); 1.675 + if (endp - buf >= commentlen) { 1.676 + mCommentPtr = (const char *)buf; 1.677 + mCommentLen = commentlen; 1.678 + } 1.679 + } 1.680 + 1.681 +MOZ_WIN_MEM_TRY_CATCH(return NS_ERROR_FAILURE) 1.682 + return NS_OK; 1.683 +} 1.684 + 1.685 +//--------------------------------------------- 1.686 +// nsZipArchive::BuildSynthetics 1.687 +//--------------------------------------------- 1.688 +nsresult nsZipArchive::BuildSynthetics() 1.689 +{ 1.690 + if (mBuiltSynthetics) 1.691 + return NS_OK; 1.692 + mBuiltSynthetics = true; 1.693 + 1.694 +MOZ_WIN_MEM_TRY_BEGIN 1.695 + // Create synthetic entries for any missing directories. 1.696 + // Do this when all ziptable has scanned to prevent double entries. 1.697 + for (int i = 0; i < ZIP_TABSIZE; ++i) 1.698 + { 1.699 + for (nsZipItem* item = mFiles[i]; item != nullptr; item = item->next) 1.700 + { 1.701 + if (item->isSynthetic) 1.702 + continue; 1.703 + 1.704 + //-- add entries for directories in the current item's path 1.705 + //-- go from end to beginning, because then we can stop trying 1.706 + //-- to create diritems if we find that the diritem we want to 1.707 + //-- create already exists 1.708 + //-- start just before the last char so as to not add the item 1.709 + //-- twice if it's a directory 1.710 + uint16_t namelen = item->nameLength; 1.711 + MOZ_ASSERT(namelen > 0, "Attempt to build synthetic for zero-length entry name!"); 1.712 + const char *name = item->Name(); 1.713 + for (uint16_t dirlen = namelen - 1; dirlen > 0; dirlen--) 1.714 + { 1.715 + if (name[dirlen-1] != '/') 1.716 + continue; 1.717 + 1.718 + // The character before this is '/', so if this is also '/' then we 1.719 + // have an empty path component. Skip it. 1.720 + if (name[dirlen] == '/') 1.721 + continue; 1.722 + 1.723 + // Is the directory already in the file table? 1.724 + uint32_t hash = HashName(item->Name(), dirlen); 1.725 + bool found = false; 1.726 + for (nsZipItem* zi = mFiles[hash]; zi != nullptr; zi = zi->next) 1.727 + { 1.728 + if ((dirlen == zi->nameLength) && 1.729 + (0 == memcmp(item->Name(), zi->Name(), dirlen))) 1.730 + { 1.731 + // we've already added this dir and all its parents 1.732 + found = true; 1.733 + break; 1.734 + } 1.735 + } 1.736 + // if the directory was found, break out of the directory 1.737 + // creation loop now that we know all implicit directories 1.738 + // are there -- otherwise, start creating the zip item 1.739 + if (found) 1.740 + break; 1.741 + 1.742 + nsZipItem* diritem = CreateZipItem(); 1.743 + if (!diritem) 1.744 + return NS_ERROR_OUT_OF_MEMORY; 1.745 + 1.746 + // Point to the central record of the original item for the name part. 1.747 + diritem->central = item->central; 1.748 + diritem->nameLength = dirlen; 1.749 + diritem->isSynthetic = true; 1.750 + 1.751 + // add diritem to the file table 1.752 + diritem->next = mFiles[hash]; 1.753 + mFiles[hash] = diritem; 1.754 + } /* end processing of dirs in item's name */ 1.755 + } 1.756 + } 1.757 +MOZ_WIN_MEM_TRY_CATCH(return NS_ERROR_FAILURE) 1.758 + return NS_OK; 1.759 +} 1.760 + 1.761 +nsZipHandle* nsZipArchive::GetFD() 1.762 +{ 1.763 + if (!mFd) 1.764 + return nullptr; 1.765 + return mFd.get(); 1.766 +} 1.767 + 1.768 +//--------------------------------------------- 1.769 +// nsZipArchive::GetData 1.770 +//--------------------------------------------- 1.771 +const uint8_t* nsZipArchive::GetData(nsZipItem* aItem) 1.772 +{ 1.773 + PR_ASSERT (aItem); 1.774 +MOZ_WIN_MEM_TRY_BEGIN 1.775 + //-- read local header to get variable length values and calculate 1.776 + //-- the real data offset 1.777 + uint32_t len = mFd->mLen; 1.778 + const uint8_t* data = mFd->mFileData; 1.779 + uint32_t offset = aItem->LocalOffset(); 1.780 + if (offset + ZIPLOCAL_SIZE > len) 1.781 + return nullptr; 1.782 + 1.783 + // -- check signature before using the structure, in case the zip file is corrupt 1.784 + ZipLocal* Local = (ZipLocal*)(data + offset); 1.785 + if ((xtolong(Local->signature) != LOCALSIG)) 1.786 + return nullptr; 1.787 + 1.788 + //-- NOTE: extralen is different in central header and local header 1.789 + //-- for archives created using the Unix "zip" utility. To set 1.790 + //-- the offset accurately we need the _local_ extralen. 1.791 + offset += ZIPLOCAL_SIZE + 1.792 + xtoint(Local->filename_len) + 1.793 + xtoint(Local->extrafield_len); 1.794 + 1.795 + // -- check if there is enough source data in the file 1.796 + if (offset + aItem->Size() > len) 1.797 + return nullptr; 1.798 + 1.799 + return data + offset; 1.800 +MOZ_WIN_MEM_TRY_CATCH(return nullptr) 1.801 +} 1.802 + 1.803 +// nsZipArchive::GetComment 1.804 +bool nsZipArchive::GetComment(nsACString &aComment) 1.805 +{ 1.806 +MOZ_WIN_MEM_TRY_BEGIN 1.807 + aComment.Assign(mCommentPtr, mCommentLen); 1.808 +MOZ_WIN_MEM_TRY_CATCH(return false) 1.809 + return true; 1.810 +} 1.811 + 1.812 +//--------------------------------------------- 1.813 +// nsZipArchive::SizeOfMapping 1.814 +//--------------------------------------------- 1.815 +int64_t nsZipArchive::SizeOfMapping() 1.816 +{ 1.817 + return mFd ? mFd->SizeOfMapping() : 0; 1.818 +} 1.819 + 1.820 +//------------------------------------------ 1.821 +// nsZipArchive constructor and destructor 1.822 +//------------------------------------------ 1.823 + 1.824 +nsZipArchive::nsZipArchive() 1.825 + : mRefCnt(0) 1.826 + , mBuiltSynthetics(false) 1.827 +{ 1.828 + zipLog.AddRef(); 1.829 + 1.830 + MOZ_COUNT_CTOR(nsZipArchive); 1.831 + 1.832 + // initialize the table to nullptr 1.833 + memset(mFiles, 0, sizeof(mFiles)); 1.834 +} 1.835 + 1.836 +NS_IMPL_ADDREF(nsZipArchive) 1.837 +NS_IMPL_RELEASE(nsZipArchive) 1.838 + 1.839 +nsZipArchive::~nsZipArchive() 1.840 +{ 1.841 + CloseArchive(); 1.842 + 1.843 + MOZ_COUNT_DTOR(nsZipArchive); 1.844 + 1.845 + zipLog.Release(); 1.846 +} 1.847 + 1.848 + 1.849 +//------------------------------------------ 1.850 +// nsZipFind constructor and destructor 1.851 +//------------------------------------------ 1.852 + 1.853 +nsZipFind::nsZipFind(nsZipArchive* aZip, char* aPattern, bool aRegExp) : 1.854 + mArchive(aZip), 1.855 + mPattern(aPattern), 1.856 + mItem(0), 1.857 + mSlot(0), 1.858 + mRegExp(aRegExp) 1.859 +{ 1.860 + MOZ_COUNT_CTOR(nsZipFind); 1.861 +} 1.862 + 1.863 +nsZipFind::~nsZipFind() 1.864 +{ 1.865 + PL_strfree(mPattern); 1.866 + 1.867 + MOZ_COUNT_DTOR(nsZipFind); 1.868 +} 1.869 + 1.870 +//------------------------------------------ 1.871 +// helper functions 1.872 +//------------------------------------------ 1.873 + 1.874 +/* 1.875 + * HashName 1.876 + * 1.877 + * returns a hash key for the entry name 1.878 + */ 1.879 +static uint32_t HashName(const char* aName, uint16_t len) 1.880 +{ 1.881 + PR_ASSERT(aName != 0); 1.882 + 1.883 + const uint8_t* p = (const uint8_t*)aName; 1.884 + const uint8_t* endp = p + len; 1.885 + uint32_t val = 0; 1.886 + while (p != endp) { 1.887 + val = val*37 + *p++; 1.888 + } 1.889 + 1.890 + return (val % ZIP_TABSIZE); 1.891 +} 1.892 + 1.893 +/* 1.894 + * x t o i n t 1.895 + * 1.896 + * Converts a two byte ugly endianed integer 1.897 + * to our platform's integer. 1.898 + */ 1.899 +static uint16_t xtoint (const uint8_t *ii) 1.900 +{ 1.901 + return (uint16_t) ((ii [0]) | (ii [1] << 8)); 1.902 +} 1.903 + 1.904 +/* 1.905 + * x t o l o n g 1.906 + * 1.907 + * Converts a four byte ugly endianed integer 1.908 + * to our platform's integer. 1.909 + */ 1.910 +static uint32_t xtolong (const uint8_t *ll) 1.911 +{ 1.912 + return (uint32_t)( (ll [0] << 0) | 1.913 + (ll [1] << 8) | 1.914 + (ll [2] << 16) | 1.915 + (ll [3] << 24) ); 1.916 +} 1.917 + 1.918 +/* 1.919 + * GetModTime 1.920 + * 1.921 + * returns last modification time in microseconds 1.922 + */ 1.923 +static PRTime GetModTime(uint16_t aDate, uint16_t aTime) 1.924 +{ 1.925 + // Note that on DST shift we can't handle correctly the hour that is valid 1.926 + // in both DST zones 1.927 + PRExplodedTime time; 1.928 + 1.929 + time.tm_usec = 0; 1.930 + 1.931 + time.tm_hour = (aTime >> 11) & 0x1F; 1.932 + time.tm_min = (aTime >> 5) & 0x3F; 1.933 + time.tm_sec = (aTime & 0x1F) * 2; 1.934 + 1.935 + time.tm_year = (aDate >> 9) + 1980; 1.936 + time.tm_month = ((aDate >> 5) & 0x0F) - 1; 1.937 + time.tm_mday = aDate & 0x1F; 1.938 + 1.939 + time.tm_params.tp_gmt_offset = 0; 1.940 + time.tm_params.tp_dst_offset = 0; 1.941 + 1.942 + PR_NormalizeTime(&time, PR_GMTParameters); 1.943 + time.tm_params.tp_gmt_offset = PR_LocalTimeParameters(&time).tp_gmt_offset; 1.944 + PR_NormalizeTime(&time, PR_GMTParameters); 1.945 + time.tm_params.tp_dst_offset = PR_LocalTimeParameters(&time).tp_dst_offset; 1.946 + 1.947 + return PR_ImplodeTime(&time); 1.948 +} 1.949 + 1.950 +uint32_t nsZipItem::LocalOffset() 1.951 +{ 1.952 + return xtolong(central->localhdr_offset); 1.953 +} 1.954 + 1.955 +uint32_t nsZipItem::Size() 1.956 +{ 1.957 + return isSynthetic ? 0 : xtolong(central->size); 1.958 +} 1.959 + 1.960 +uint32_t nsZipItem::RealSize() 1.961 +{ 1.962 + return isSynthetic ? 0 : xtolong(central->orglen); 1.963 +} 1.964 + 1.965 +uint32_t nsZipItem::CRC32() 1.966 +{ 1.967 + return isSynthetic ? 0 : xtolong(central->crc32); 1.968 +} 1.969 + 1.970 +uint16_t nsZipItem::Date() 1.971 +{ 1.972 + return isSynthetic ? kSyntheticDate : xtoint(central->date); 1.973 +} 1.974 + 1.975 +uint16_t nsZipItem::Time() 1.976 +{ 1.977 + return isSynthetic ? kSyntheticTime : xtoint(central->time); 1.978 +} 1.979 + 1.980 +uint16_t nsZipItem::Compression() 1.981 +{ 1.982 + return isSynthetic ? STORED : xtoint(central->method); 1.983 +} 1.984 + 1.985 +bool nsZipItem::IsDirectory() 1.986 +{ 1.987 + return isSynthetic || ((nameLength > 0) && ('/' == Name()[nameLength - 1])); 1.988 +} 1.989 + 1.990 +uint16_t nsZipItem::Mode() 1.991 +{ 1.992 + if (isSynthetic) return 0755; 1.993 + return ((uint16_t)(central->external_attributes[2]) | 0x100); 1.994 +} 1.995 + 1.996 +const uint8_t * nsZipItem::GetExtraField(uint16_t aTag, uint16_t *aBlockSize) 1.997 +{ 1.998 + if (isSynthetic) return nullptr; 1.999 +MOZ_WIN_MEM_TRY_BEGIN 1.1000 + const unsigned char *buf = ((const unsigned char*)central) + ZIPCENTRAL_SIZE + 1.1001 + nameLength; 1.1002 + uint32_t buflen = (uint32_t)xtoint(central->extrafield_len); 1.1003 + uint32_t pos = 0; 1.1004 + uint16_t tag, blocksize; 1.1005 + 1.1006 + while (buf && (pos + 4) <= buflen) { 1.1007 + tag = xtoint(buf + pos); 1.1008 + blocksize = xtoint(buf + pos + 2); 1.1009 + 1.1010 + if (aTag == tag && (pos + 4 + blocksize) <= buflen) { 1.1011 + *aBlockSize = blocksize; 1.1012 + return buf + pos; 1.1013 + } 1.1014 + 1.1015 + pos += blocksize + 4; 1.1016 + } 1.1017 + 1.1018 +MOZ_WIN_MEM_TRY_CATCH(return nullptr) 1.1019 + return nullptr; 1.1020 +} 1.1021 + 1.1022 + 1.1023 +PRTime nsZipItem::LastModTime() 1.1024 +{ 1.1025 + if (isSynthetic) return GetModTime(kSyntheticDate, kSyntheticTime); 1.1026 + 1.1027 + // Try to read timestamp from extra field 1.1028 + uint16_t blocksize; 1.1029 + const uint8_t *tsField = GetExtraField(EXTENDED_TIMESTAMP_FIELD, &blocksize); 1.1030 + if (tsField && blocksize >= 5 && tsField[4] & EXTENDED_TIMESTAMP_MODTIME) { 1.1031 + return (PRTime)(xtolong(tsField + 5)) * PR_USEC_PER_SEC; 1.1032 + } 1.1033 + 1.1034 + return GetModTime(Date(), Time()); 1.1035 +} 1.1036 + 1.1037 +#ifdef XP_UNIX 1.1038 +bool nsZipItem::IsSymlink() 1.1039 +{ 1.1040 + if (isSynthetic) return false; 1.1041 + return (xtoint(central->external_attributes+2) & S_IFMT) == S_IFLNK; 1.1042 +} 1.1043 +#endif 1.1044 + 1.1045 +nsZipCursor::nsZipCursor(nsZipItem *item, nsZipArchive *aZip, uint8_t* aBuf, uint32_t aBufSize, bool doCRC) : 1.1046 + mItem(item), 1.1047 + mBuf(aBuf), 1.1048 + mBufSize(aBufSize), 1.1049 + mDoCRC(doCRC) 1.1050 +{ 1.1051 + if (mItem->Compression() == DEFLATED) { 1.1052 +#ifdef DEBUG 1.1053 + nsresult status = 1.1054 +#endif 1.1055 + gZlibInit(&mZs); 1.1056 + NS_ASSERTION(status == NS_OK, "Zlib failed to initialize"); 1.1057 + NS_ASSERTION(aBuf, "Must pass in a buffer for DEFLATED nsZipItem"); 1.1058 + } 1.1059 + 1.1060 + mZs.avail_in = item->Size(); 1.1061 + mZs.next_in = (Bytef*)aZip->GetData(item); 1.1062 + 1.1063 + if (doCRC) 1.1064 + mCRC = crc32(0L, Z_NULL, 0); 1.1065 +} 1.1066 + 1.1067 +nsZipCursor::~nsZipCursor() 1.1068 +{ 1.1069 + if (mItem->Compression() == DEFLATED) { 1.1070 + inflateEnd(&mZs); 1.1071 + } 1.1072 +} 1.1073 + 1.1074 +uint8_t* nsZipCursor::ReadOrCopy(uint32_t *aBytesRead, bool aCopy) { 1.1075 + int zerr; 1.1076 + uint8_t *buf = nullptr; 1.1077 + bool verifyCRC = true; 1.1078 + 1.1079 + if (!mZs.next_in) 1.1080 + return nullptr; 1.1081 +MOZ_WIN_MEM_TRY_BEGIN 1.1082 + switch (mItem->Compression()) { 1.1083 + case STORED: 1.1084 + if (!aCopy) { 1.1085 + *aBytesRead = mZs.avail_in; 1.1086 + buf = mZs.next_in; 1.1087 + mZs.next_in += mZs.avail_in; 1.1088 + mZs.avail_in = 0; 1.1089 + } else { 1.1090 + *aBytesRead = mZs.avail_in > mBufSize ? mBufSize : mZs.avail_in; 1.1091 + memcpy(mBuf, mZs.next_in, *aBytesRead); 1.1092 + mZs.avail_in -= *aBytesRead; 1.1093 + mZs.next_in += *aBytesRead; 1.1094 + } 1.1095 + break; 1.1096 + case DEFLATED: 1.1097 + buf = mBuf; 1.1098 + mZs.next_out = buf; 1.1099 + mZs.avail_out = mBufSize; 1.1100 + 1.1101 + zerr = inflate(&mZs, Z_PARTIAL_FLUSH); 1.1102 + if (zerr != Z_OK && zerr != Z_STREAM_END) 1.1103 + return nullptr; 1.1104 + 1.1105 + *aBytesRead = mZs.next_out - buf; 1.1106 + verifyCRC = (zerr == Z_STREAM_END); 1.1107 + break; 1.1108 + default: 1.1109 + return nullptr; 1.1110 + } 1.1111 + 1.1112 + if (mDoCRC) { 1.1113 + mCRC = crc32(mCRC, (const unsigned char*)buf, *aBytesRead); 1.1114 + if (verifyCRC && mCRC != mItem->CRC32()) 1.1115 + return nullptr; 1.1116 + } 1.1117 +MOZ_WIN_MEM_TRY_CATCH(return nullptr) 1.1118 + return buf; 1.1119 +} 1.1120 + 1.1121 +nsZipItemPtr_base::nsZipItemPtr_base(nsZipArchive *aZip, const char * aEntryName, bool doCRC) : 1.1122 + mReturnBuf(nullptr) 1.1123 +{ 1.1124 + // make sure the ziparchive hangs around 1.1125 + mZipHandle = aZip->GetFD(); 1.1126 + 1.1127 + nsZipItem* item = aZip->GetItem(aEntryName); 1.1128 + if (!item) 1.1129 + return; 1.1130 + 1.1131 + uint32_t size = 0; 1.1132 + if (item->Compression() == DEFLATED) { 1.1133 + size = item->RealSize(); 1.1134 + mAutoBuf = new ((fallible_t())) uint8_t[size]; 1.1135 + if (!mAutoBuf) { 1.1136 + return; 1.1137 + } 1.1138 + } 1.1139 + 1.1140 + nsZipCursor cursor(item, aZip, mAutoBuf, size, doCRC); 1.1141 + mReturnBuf = cursor.Read(&mReadlen); 1.1142 + if (!mReturnBuf) { 1.1143 + return; 1.1144 + } 1.1145 + 1.1146 + if (mReadlen != item->RealSize()) { 1.1147 + NS_ASSERTION(mReadlen == item->RealSize(), "nsZipCursor underflow"); 1.1148 + mReturnBuf = nullptr; 1.1149 + return; 1.1150 + } 1.1151 +}