nsZipArchive.cpp (37550B)
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/. */ 5 6 /* 7 * This module implements a simple archive extractor for the PKZIP format. 8 */ 9 10 #define READTYPE int32_t 11 #include "zlib.h" 12 #include "nsISupportsUtils.h" 13 #include "mozilla/MmapFaultHandler.h" 14 #include "prio.h" 15 #include "mozilla/Attributes.h" 16 #include "mozilla/Logging.h" 17 #include "mozilla/MemUtils.h" 18 #include "mozilla/UniquePtrExtensions.h" 19 #include "mozilla/StaticMutex.h" 20 #include "mozilla/StaticPrefs_network.h" 21 #include "stdlib.h" 22 #include "nsDirectoryService.h" 23 #include "nsWildCard.h" 24 #include "nsXULAppAPI.h" 25 #include "nsZipArchive.h" 26 #include "nsString.h" 27 #include "prenv.h" 28 #if defined(XP_WIN) 29 # include <windows.h> 30 #endif 31 32 // For placement new used for arena allocations of zip file list 33 #include <new> 34 #define ZIP_ARENABLOCKSIZE (1 * 1024) 35 36 #ifdef XP_UNIX 37 # include <sys/mman.h> 38 # include <sys/types.h> 39 # include <sys/stat.h> 40 # include <limits.h> 41 # include <unistd.h> 42 #elif defined(XP_WIN) 43 # include <io.h> 44 #endif 45 46 #ifdef __SYMBIAN32__ 47 # include <sys/syslimits.h> 48 #endif /*__SYMBIAN32__*/ 49 50 #ifndef XP_UNIX /* we need some constants defined in limits.h and unistd.h */ 51 # ifndef S_IFMT 52 # define S_IFMT 0170000 53 # endif 54 # ifndef S_IFLNK 55 # define S_IFLNK 0120000 56 # endif 57 # ifndef PATH_MAX 58 # define PATH_MAX 1024 59 # endif 60 #endif /* XP_UNIX */ 61 62 #ifdef XP_WIN 63 # include "private/pprio.h" // To get PR_ImportFile 64 #endif 65 66 using namespace mozilla; 67 68 static LazyLogModule gZipLog("nsZipArchive"); 69 70 #ifdef LOG 71 # undef LOG 72 #endif 73 #ifdef LOG_ENABLED 74 # undef LOG_ENABLED 75 #endif 76 77 #define LOG(args) MOZ_LOG(gZipLog, mozilla::LogLevel::Debug, args) 78 #define LOG_ENABLED() MOZ_LOG_TEST(gZipLog, mozilla::LogLevel::Debug) 79 80 static const uint32_t kMaxNameLength = PATH_MAX; /* Maximum name length */ 81 // For synthetic zip entries. Date/time corresponds to 1980-01-01 00:00. 82 static const uint16_t kSyntheticTime = 0; 83 static const uint16_t kSyntheticDate = (1 + (1 << 5) + (0 << 9)); 84 85 static uint16_t xtoint(const uint8_t* ii); 86 static uint32_t xtolong(const uint8_t* ll); 87 static uint32_t HashName(const char* aName, uint16_t nameLen); 88 89 class ZipArchiveLogger { 90 public: 91 void Init(const char* env) { 92 StaticMutexAutoLock lock(sLock); 93 94 // AddRef 95 MOZ_ASSERT(mRefCnt >= 0); 96 ++mRefCnt; 97 98 if (!mFd) { 99 nsCOMPtr<nsIFile> logFile; 100 nsresult rv = 101 NS_NewLocalFile(NS_ConvertUTF8toUTF16(env), getter_AddRefs(logFile)); 102 if (NS_FAILED(rv)) return; 103 104 // Create the log file and its parent directory (in case it doesn't exist) 105 rv = logFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644); 106 if (NS_FAILED(rv)) return; 107 108 PRFileDesc* file; 109 #ifdef XP_WIN 110 // PR_APPEND is racy on Windows, so open a handle ourselves with flags 111 // that will work, and use PR_ImportFile to make it a PRFileDesc. This can 112 // go away when bug 840435 is fixed. 113 nsAutoString path; 114 logFile->GetPath(path); 115 if (path.IsEmpty()) return; 116 HANDLE handle = 117 CreateFileW(path.get(), FILE_APPEND_DATA, FILE_SHARE_WRITE, nullptr, 118 OPEN_ALWAYS, 0, nullptr); 119 if (handle == INVALID_HANDLE_VALUE) return; 120 file = PR_ImportFile((PROsfd)handle); 121 if (!file) return; 122 #else 123 rv = logFile->OpenNSPRFileDesc( 124 PR_WRONLY | PR_CREATE_FILE | PR_APPEND | PR_SYNC, 0644, &file); 125 if (NS_FAILED(rv)) return; 126 #endif 127 mFd = file; 128 } 129 } 130 131 void Write(const nsACString& zip, const char* entry) { 132 StaticMutexAutoLock lock(sLock); 133 134 if (mFd) { 135 nsCString buf(zip); 136 buf.Append(' '); 137 buf.Append(entry); 138 buf.Append('\n'); 139 PR_Write(mFd, buf.get(), buf.Length()); 140 } 141 } 142 143 void Release() { 144 StaticMutexAutoLock lock(sLock); 145 146 MOZ_ASSERT(mRefCnt > 0); 147 if ((0 == --mRefCnt) && mFd) { 148 PR_Close(mFd); 149 mFd = nullptr; 150 } 151 } 152 153 private: 154 static StaticMutex sLock; 155 int mRefCnt MOZ_GUARDED_BY(sLock); 156 PRFileDesc* mFd MOZ_GUARDED_BY(sLock); 157 }; 158 159 StaticMutex ZipArchiveLogger::sLock; 160 static ZipArchiveLogger zipLog; 161 162 //*********************************************************** 163 // For every inflation the following allocations are done: 164 // malloc(1 * 9520) 165 // malloc(32768 * 1) 166 //*********************************************************** 167 168 nsresult gZlibInit(z_stream* zs) { 169 memset(zs, 0, sizeof(z_stream)); 170 int zerr = inflateInit2(zs, -MAX_WBITS); 171 if (zerr != Z_OK) return NS_ERROR_OUT_OF_MEMORY; 172 173 return NS_OK; 174 } 175 176 nsZipHandle::nsZipHandle() 177 : mFileData(nullptr), 178 mLen(0), 179 mMap(nullptr), 180 mRefCnt(0), 181 mFileStart(nullptr), 182 mTotalLen(0) {} 183 184 NS_IMPL_ADDREF(nsZipHandle) 185 NS_IMPL_RELEASE(nsZipHandle) 186 187 nsresult nsZipHandle::Init(nsIFile* file, nsZipHandle** ret, PRFileDesc** aFd) { 188 mozilla::AutoFDClose fd; 189 int32_t flags = PR_RDONLY; 190 #if defined(XP_WIN) 191 flags |= nsIFile::OS_READAHEAD; 192 #endif 193 LOG(("ZipHandle::Init %s", file->HumanReadablePath().get())); 194 nsresult rv = file->OpenNSPRFileDesc(flags, 0000, getter_Transfers(fd)); 195 if (NS_FAILED(rv)) return rv; 196 197 int64_t size = PR_Available64(fd.get()); 198 if (size >= INT32_MAX) return NS_ERROR_FILE_TOO_BIG; 199 200 PRFileMap* map = PR_CreateFileMap(fd.get(), size, PR_PROT_READONLY); 201 if (!map) return NS_ERROR_FAILURE; 202 203 uint8_t* buf = (uint8_t*)PR_MemMap(map, 0, (uint32_t)size); 204 // Bug 525755: PR_MemMap fails when fd points at something other than a normal 205 // file. 206 if (!buf) { 207 PR_CloseFileMap(map); 208 return NS_ERROR_FAILURE; 209 } 210 211 RefPtr<nsZipHandle> handle = new nsZipHandle(); 212 if (!handle) { 213 PR_MemUnmap(buf, (uint32_t)size); 214 PR_CloseFileMap(map); 215 return NS_ERROR_OUT_OF_MEMORY; 216 } 217 218 #if defined(XP_WIN) 219 if (aFd) { 220 *aFd = fd.release(); 221 } 222 #else 223 handle->mNSPRFileDesc = std::move(fd); 224 #endif 225 handle->mFile.Init(file); 226 handle->mTotalLen = (uint32_t)size; 227 handle->mFileStart = buf; 228 rv = handle->findDataStart(); 229 if (NS_FAILED(rv)) { 230 PR_MemUnmap(buf, (uint32_t)size); 231 handle->mFileStart = nullptr; 232 PR_CloseFileMap(map); 233 return rv; 234 } 235 handle->mMap = map; 236 handle.forget(ret); 237 return NS_OK; 238 } 239 240 nsresult nsZipHandle::Init(nsZipArchive* zip, const nsACString& entry, 241 nsZipHandle** ret) { 242 RefPtr<nsZipHandle> handle = new nsZipHandle(); 243 if (!handle) return NS_ERROR_OUT_OF_MEMORY; 244 245 LOG(("ZipHandle::Init entry %s", PromiseFlatCString(entry).get())); 246 247 nsZipItem* item = zip->GetItem(entry); 248 if (item && item->Compression() == DEFLATED && 249 StaticPrefs::network_jar_max_entry_size()) { 250 if (item->RealSize() > StaticPrefs::network_jar_max_entry_size()) { 251 return NS_ERROR_OUT_OF_MEMORY; 252 } 253 } 254 255 handle->mBuf = MakeUnique<nsZipItemPtr<uint8_t>>(zip, entry); 256 if (!handle->mBuf) return NS_ERROR_OUT_OF_MEMORY; 257 258 if (!handle->mBuf->Buffer()) return NS_ERROR_UNEXPECTED; 259 260 handle->mMap = nullptr; 261 handle->mFile.Init(zip, entry); 262 handle->mTotalLen = handle->mBuf->Length(); 263 handle->mFileStart = handle->mBuf->Buffer(); 264 nsresult rv = handle->findDataStart(); 265 if (NS_FAILED(rv)) { 266 return rv; 267 } 268 handle.forget(ret); 269 return NS_OK; 270 } 271 272 nsresult nsZipHandle::Init(const uint8_t* aData, uint32_t aLen, 273 nsZipHandle** aRet) { 274 RefPtr<nsZipHandle> handle = new nsZipHandle(); 275 276 handle->mFileStart = aData; 277 handle->mTotalLen = aLen; 278 nsresult rv = handle->findDataStart(); 279 if (NS_FAILED(rv)) { 280 return rv; 281 } 282 handle.forget(aRet); 283 return NS_OK; 284 } 285 286 // This function finds the start of the ZIP data. If the file is a regular ZIP, 287 // this is just the start of the file. If the file is a CRX file, the start of 288 // the data is after the CRX header. 289 // 290 // CRX header reference, version 2: 291 // Header requires little-endian byte ordering with 4-byte alignment. 292 // 32 bits : magicNumber - Defined as a |char m[] = "Cr24"|. 293 // Equivilant to |uint32_t m = 0x34327243|. 294 // 32 bits : version - Unsigned integer representing the CRX file 295 // format version. Currently equal to 2. 296 // 32 bits : pubKeyLength - Unsigned integer representing the length 297 // of the public key in bytes. 298 // 32 bits : sigLength - Unsigned integer representing the length 299 // of the signature in bytes. 300 // pubKeyLength : publicKey - Contents of the author's public key. 301 // sigLength : signature - Signature of the ZIP content. 302 // Signature is created using the RSA 303 // algorithm with the SHA-1 hash function. 304 // 305 // CRX header reference, version 3: 306 // Header requires little-endian byte ordering with 4-byte alignment. 307 // 32 bits : magicNumber - Defined as a |char m[] = "Cr24"|. 308 // Equivilant to |uint32_t m = 0x34327243|. 309 // 32 bits : version - Unsigned integer representing the CRX file 310 // format version. Currently equal to 3. 311 // 32 bits : headerLength - Unsigned integer representing the length 312 // of the CRX header in bytes. 313 // headerLength : header - CRXv3 header. 314 nsresult nsZipHandle::findDataStart() { 315 // In the CRX header, integers are 32 bits. Our pointer to the file is of 316 // type |uint8_t|, which is guaranteed to be 8 bits. 317 const uint32_t CRXIntSize = 4; 318 319 MMAP_FAULT_HANDLER_BEGIN_HANDLE(this) 320 if (mTotalLen > CRXIntSize * 4 && xtolong(mFileStart) == kCRXMagic) { 321 const uint8_t* headerData = mFileStart; 322 headerData += CRXIntSize; // Skip magic number 323 uint32_t version = xtolong(headerData); 324 headerData += CRXIntSize; // Skip version 325 uint32_t headerSize = CRXIntSize * 2; 326 if (version == 3) { 327 uint32_t subHeaderSize = xtolong(headerData); 328 headerSize += CRXIntSize + subHeaderSize; 329 } else if (version < 3) { 330 uint32_t pubKeyLength = xtolong(headerData); 331 headerData += CRXIntSize; 332 uint32_t sigLength = xtolong(headerData); 333 headerSize += CRXIntSize * 2 + pubKeyLength + sigLength; 334 } else { 335 return NS_ERROR_FILE_CORRUPTED; 336 } 337 if (mTotalLen > headerSize) { 338 mLen = mTotalLen - headerSize; 339 mFileData = mFileStart + headerSize; 340 return NS_OK; 341 } 342 return NS_ERROR_FILE_CORRUPTED; 343 } 344 mLen = mTotalLen; 345 mFileData = mFileStart; 346 MMAP_FAULT_HANDLER_CATCH(NS_ERROR_FAILURE) 347 return NS_OK; 348 } 349 350 int64_t nsZipHandle::SizeOfMapping() { return mTotalLen; } 351 352 nsresult nsZipHandle::GetNSPRFileDesc(PRFileDesc** aNSPRFileDesc) { 353 if (!aNSPRFileDesc) { 354 return NS_ERROR_ILLEGAL_VALUE; 355 } 356 357 *aNSPRFileDesc = mNSPRFileDesc.get(); 358 if (!mNSPRFileDesc) { 359 return NS_ERROR_NOT_AVAILABLE; 360 } 361 362 return NS_OK; 363 } 364 365 nsZipHandle::~nsZipHandle() { 366 if (mMap) { 367 PR_MemUnmap((void*)mFileStart, mTotalLen); 368 PR_CloseFileMap(mMap); 369 } 370 mFileStart = nullptr; 371 mFileData = nullptr; 372 mMap = nullptr; 373 mBuf = nullptr; 374 } 375 376 //*********************************************************** 377 // nsZipArchive -- public methods 378 //*********************************************************** 379 380 //--------------------------------------------- 381 // nsZipArchive::OpenArchive 382 //--------------------------------------------- 383 /* static */ 384 already_AddRefed<nsZipArchive> nsZipArchive::OpenArchive( 385 nsZipHandle* aZipHandle, PRFileDesc* aFd) { 386 nsresult rv; 387 RefPtr<nsZipArchive> self(new nsZipArchive(aZipHandle, aFd, rv)); 388 LOG(("ZipHandle::OpenArchive[%p]", self.get())); 389 if (NS_FAILED(rv)) { 390 self = nullptr; 391 } 392 return self.forget(); 393 } 394 395 /* static */ 396 already_AddRefed<nsZipArchive> nsZipArchive::OpenArchive(nsIFile* aFile) { 397 RefPtr<nsZipHandle> handle; 398 #if defined(XP_WIN) 399 mozilla::AutoFDClose fd; 400 nsresult rv = 401 nsZipHandle::Init(aFile, getter_AddRefs(handle), getter_Transfers(fd)); 402 #else 403 nsresult rv = nsZipHandle::Init(aFile, getter_AddRefs(handle)); 404 #endif 405 if (NS_FAILED(rv)) return nullptr; 406 407 #if defined(XP_WIN) 408 return OpenArchive(handle, fd.get()); 409 #else 410 return OpenArchive(handle); 411 #endif 412 } 413 414 //--------------------------------------------- 415 // nsZipArchive::Test 416 //--------------------------------------------- 417 nsresult nsZipArchive::Test(const nsACString& aEntryName) { 418 nsZipItem* currItem; 419 420 if (aEntryName.Length()) // only test specified item 421 { 422 currItem = GetItem(aEntryName); 423 if (!currItem) return NS_ERROR_FILE_NOT_FOUND; 424 //-- don't test (synthetic) directory items 425 if (currItem->IsDirectory()) return NS_OK; 426 return ExtractFile(currItem, 0, 0); 427 } 428 429 // test all items in archive 430 for (auto* item : mFiles) { 431 for (currItem = item; currItem; currItem = currItem->next) { 432 //-- don't test (synthetic) directory items 433 if (currItem->IsDirectory()) continue; 434 nsresult rv = ExtractFile(currItem, 0, 0); 435 if (rv != NS_OK) return rv; 436 } 437 } 438 439 return NS_OK; 440 } 441 442 //--------------------------------------------- 443 // nsZipArchive::GetItem 444 //--------------------------------------------- 445 nsZipItem* nsZipArchive::GetItem(const nsACString& aEntryName) { 446 MutexAutoLock lock(mLock); 447 448 LOG(("ZipHandle::GetItem[%p] %s", this, 449 PromiseFlatCString(aEntryName).get())); 450 if (aEntryName.Length()) { 451 uint32_t len = aEntryName.Length(); 452 //-- If the request is for a directory, make sure that synthetic entries 453 //-- are created for the directories without their own entry. 454 if (!mBuiltSynthetics) { 455 if ((len > 0) && (aEntryName[len - 1] == '/')) { 456 if (BuildSynthetics() != NS_OK) return 0; 457 } 458 } 459 MMAP_FAULT_HANDLER_BEGIN_HANDLE(mFd) 460 nsZipItem* item = mFiles[HashName(aEntryName.BeginReading(), len)]; 461 while (item) { 462 if ((len == item->nameLength) && 463 (!memcmp(aEntryName.BeginReading(), item->Name(), len))) { 464 // Successful GetItem() is a good indicator that the file is about to be 465 // read 466 if (mUseZipLog && mURI.Length()) { 467 zipLog.Write(mURI, aEntryName.BeginReading()); 468 } 469 return item; //-- found it 470 } 471 item = item->next; 472 } 473 MMAP_FAULT_HANDLER_CATCH(nullptr) 474 } 475 return nullptr; 476 } 477 478 //--------------------------------------------- 479 // nsZipArchive::ExtractFile 480 // This extracts the item to the filehandle provided. 481 // If 'aFd' is null, it only tests the extraction. 482 // On extraction error(s) it removes the file. 483 //--------------------------------------------- 484 nsresult nsZipArchive::ExtractFile(nsZipItem* item, nsIFile* outFile, 485 PRFileDesc* aFd) { 486 MutexAutoLock lock(mLock); 487 LOG(("ZipHandle::ExtractFile[%p]", this)); 488 if (!item) return NS_ERROR_ILLEGAL_VALUE; 489 if (!mFd) return NS_ERROR_FAILURE; 490 491 // Directory extraction is handled in nsJAR::Extract, 492 // so the item to be extracted should never be a directory 493 MOZ_ASSERT(!item->IsDirectory()); 494 495 Bytef outbuf[ZIP_BUFLEN]; 496 497 nsZipCursor cursor(item, this, outbuf, ZIP_BUFLEN, true); 498 499 nsresult rv = NS_OK; 500 501 while (true) { 502 uint32_t count = 0; 503 uint8_t* buf = cursor.Read(&count); 504 if (!buf) { 505 rv = NS_ERROR_FILE_CORRUPTED; 506 break; 507 } 508 if (count == 0) { 509 break; 510 } 511 512 if (aFd && PR_Write(aFd, buf, count) < (READTYPE)count) { 513 rv = NS_ERROR_FILE_NO_DEVICE_SPACE; 514 break; 515 } 516 } 517 518 //-- delete the file on errors 519 if (aFd) { 520 PR_Close(aFd); 521 if (NS_FAILED(rv) && outFile) { 522 outFile->Remove(false); 523 } 524 } 525 526 return rv; 527 } 528 529 //--------------------------------------------- 530 // nsZipArchive::FindInit 531 //--------------------------------------------- 532 nsresult nsZipArchive::FindInit(const char* aPattern, nsZipFind** aFind) { 533 if (!aFind) return NS_ERROR_ILLEGAL_VALUE; 534 535 MutexAutoLock lock(mLock); 536 537 LOG(("ZipHandle::FindInit[%p]", this)); 538 // null out param in case an error happens 539 *aFind = nullptr; 540 541 bool regExp = false; 542 char* pattern = 0; 543 544 // Create synthetic directory entries on demand 545 nsresult rv = BuildSynthetics(); 546 if (rv != NS_OK) return rv; 547 548 // validate the pattern 549 if (aPattern) { 550 switch (NS_WildCardValid((char*)aPattern)) { 551 case INVALID_SXP: 552 return NS_ERROR_ILLEGAL_VALUE; 553 554 case NON_SXP: 555 regExp = false; 556 break; 557 558 case VALID_SXP: 559 regExp = true; 560 break; 561 562 default: 563 // undocumented return value from RegExpValid! 564 MOZ_ASSERT(false); 565 return NS_ERROR_ILLEGAL_VALUE; 566 } 567 568 pattern = strdup(aPattern); 569 if (!pattern) return NS_ERROR_OUT_OF_MEMORY; 570 } 571 572 *aFind = new nsZipFind(this, pattern, regExp); 573 if (!*aFind) { 574 free(pattern); 575 return NS_ERROR_OUT_OF_MEMORY; 576 } 577 578 return NS_OK; 579 } 580 581 //--------------------------------------------- 582 // nsZipFind::FindNext 583 //--------------------------------------------- 584 nsresult nsZipFind::FindNext(const char** aResult, uint16_t* aNameLen) { 585 if (!aResult || !aNameLen) return NS_ERROR_ILLEGAL_VALUE; 586 NS_ENSURE_TRUE(mArchive, NS_ERROR_FILE_NOT_FOUND); 587 588 MutexAutoLock lock(mArchive->mLock); 589 *aResult = 0; 590 *aNameLen = 0; 591 MMAP_FAULT_HANDLER_BEGIN_HANDLE(mArchive->GetFD()) 592 // we start from last match, look for next 593 while (mSlot < ZIP_TABSIZE) { 594 // move to next in current chain, or move to new slot 595 mItem = mItem ? mItem->next : mArchive->mFiles[mSlot]; 596 597 bool found = false; 598 if (!mItem) 599 ++mSlot; // no more in this chain, move to next slot 600 else if (!mPattern) 601 found = true; // always match 602 else if (mRegExp) { 603 char buf[kMaxNameLength + 1]; 604 memcpy(buf, mItem->Name(), mItem->nameLength); 605 buf[mItem->nameLength] = '\0'; 606 found = (NS_WildCardMatch(buf, mPattern, false) == MATCH); 607 } else 608 found = ((mItem->nameLength == strlen(mPattern)) && 609 (memcmp(mItem->Name(), mPattern, mItem->nameLength) == 0)); 610 if (found) { 611 // Need also to return the name length, as it is NOT zero-terminatdd... 612 *aResult = mItem->Name(); 613 *aNameLen = mItem->nameLength; 614 LOG(("ZipHandle::FindNext[%p] %s", this, *aResult)); 615 return NS_OK; 616 } 617 } 618 MMAP_FAULT_HANDLER_CATCH(NS_ERROR_FAILURE) 619 LOG(("ZipHandle::FindNext[%p] not found %s", this, mPattern)); 620 // Release the archive. 621 mArchive = nullptr; 622 mItem = nullptr; 623 return NS_ERROR_FILE_NOT_FOUND; 624 } 625 626 //*********************************************************** 627 // nsZipArchive -- private implementation 628 //*********************************************************** 629 630 //--------------------------------------------- 631 // nsZipArchive::CreateZipItem 632 //--------------------------------------------- 633 nsZipItem* nsZipArchive::CreateZipItem() { 634 // Arena allocate the nsZipItem 635 return (nsZipItem*)mArena.Allocate(sizeof(nsZipItem), mozilla::fallible); 636 } 637 638 //--------------------------------------------- 639 // nsZipArchive::BuildFileList 640 //--------------------------------------------- 641 nsresult nsZipArchive::BuildFileList(PRFileDesc* aFd) 642 MOZ_NO_THREAD_SAFETY_ANALYSIS { 643 // We're only called from the constructor, but need to call 644 // CreateZipItem(), which touches locked data, and modify mFiles. Turn 645 // off thread-safety, which normally doesn't apply for constructors 646 // anyways 647 648 // Get archive size using end pos 649 const uint8_t* buf; 650 const uint8_t* startp = mFd->mFileData; 651 const uint8_t* endp = startp + mFd->mLen; 652 MMAP_FAULT_HANDLER_BEGIN_HANDLE(mFd) 653 uint32_t centralOffset = 4; 654 LOG(("ZipHandle::BuildFileList[%p]", this)); 655 // Only perform readahead in the parent process. Children processes 656 // don't need readahead when the file has already been readahead by 657 // the parent process, and readahead only really happens for omni.ja, 658 // which is used in the parent process. 659 if (XRE_IsParentProcess() && mFd->mLen > ZIPCENTRAL_SIZE && 660 xtolong(startp + centralOffset) == CENTRALSIG) { 661 // Success means optimized jar layout from bug 559961 is in effect 662 uint32_t readaheadLength = xtolong(startp); 663 mozilla::PrefetchMemory(const_cast<uint8_t*>(startp), readaheadLength); 664 } else { 665 for (buf = endp - ZIPEND_SIZE; buf > startp; buf--) { 666 if (xtolong(buf) == ENDSIG) { 667 centralOffset = xtolong(((ZipEnd*)buf)->offset_central_dir); 668 break; 669 } 670 } 671 } 672 673 if (!centralOffset) { 674 return NS_ERROR_FILE_CORRUPTED; 675 } 676 677 buf = startp + centralOffset; 678 679 // avoid overflow of startp + centralOffset. 680 if (buf < startp) { 681 return NS_ERROR_FILE_CORRUPTED; 682 } 683 684 //-- Read the central directory headers 685 uint32_t sig = 0; 686 while ((buf + int32_t(sizeof(uint32_t)) > buf) && 687 (buf + int32_t(sizeof(uint32_t)) <= endp) && 688 ((sig = xtolong(buf)) == CENTRALSIG)) { 689 // Make sure there is enough data available. 690 if ((buf > endp) || (endp - buf < ZIPCENTRAL_SIZE)) { 691 return NS_ERROR_FILE_CORRUPTED; 692 } 693 694 // Read the fixed-size data. 695 ZipCentral* central = (ZipCentral*)buf; 696 697 uint16_t namelen = xtoint(central->filename_len); 698 uint16_t extralen = xtoint(central->extrafield_len); 699 uint16_t commentlen = xtoint(central->commentfield_len); 700 uint32_t diff = ZIPCENTRAL_SIZE + namelen + extralen + commentlen; 701 702 // Sanity check variable sizes and refuse to deal with 703 // anything too big: it's likely a corrupt archive. 704 if (namelen < 1 || namelen > kMaxNameLength) { 705 return NS_ERROR_FILE_CORRUPTED; 706 } 707 if (buf >= buf + diff || // No overflow 708 buf >= endp - diff) { 709 return NS_ERROR_FILE_CORRUPTED; 710 } 711 712 // Point to the next item at the top of loop 713 buf += diff; 714 715 nsZipItem* item = CreateZipItem(); 716 if (!item) return NS_ERROR_OUT_OF_MEMORY; 717 718 item->central = central; 719 item->nameLength = namelen; 720 item->isSynthetic = false; 721 722 // Add item to file table 723 #ifdef DEBUG 724 nsDependentCSubstring name(item->Name(), namelen); 725 LOG((" %s", PromiseFlatCString(name).get())); 726 #endif 727 uint32_t hash = HashName(item->Name(), namelen); 728 item->next = mFiles[hash]; 729 mFiles[hash] = item; 730 731 sig = 0; 732 } /* while reading central directory records */ 733 734 if (sig != ENDSIG && sig != ENDSIG64) { 735 return NS_ERROR_FILE_CORRUPTED; 736 } 737 738 MMAP_FAULT_HANDLER_CATCH(NS_ERROR_FAILURE) 739 return NS_OK; 740 } 741 742 //--------------------------------------------- 743 // nsZipArchive::BuildSynthetics 744 //--------------------------------------------- 745 nsresult nsZipArchive::BuildSynthetics() { 746 mLock.AssertCurrentThreadOwns(); 747 748 if (mBuiltSynthetics) return NS_OK; 749 mBuiltSynthetics = true; 750 751 MMAP_FAULT_HANDLER_BEGIN_HANDLE(mFd) 752 // Create synthetic entries for any missing directories. 753 // Do this when all ziptable has scanned to prevent double entries. 754 for (auto* item : mFiles) { 755 for (; item != nullptr; item = item->next) { 756 if (item->isSynthetic) continue; 757 758 //-- add entries for directories in the current item's path 759 //-- go from end to beginning, because then we can stop trying 760 //-- to create diritems if we find that the diritem we want to 761 //-- create already exists 762 //-- start just before the last char so as to not add the item 763 //-- twice if it's a directory 764 uint16_t namelen = item->nameLength; 765 MOZ_ASSERT(namelen > 0, 766 "Attempt to build synthetic for zero-length entry name!"); 767 const char* name = item->Name(); 768 for (uint16_t dirlen = namelen - 1; dirlen > 0; dirlen--) { 769 if (name[dirlen - 1] != '/') continue; 770 771 // The character before this is '/', so if this is also '/' then we 772 // have an empty path component. Skip it. 773 if (name[dirlen] == '/') continue; 774 775 // Is the directory already in the file table? 776 uint32_t hash = HashName(item->Name(), dirlen); 777 bool found = false; 778 for (nsZipItem* zi = mFiles[hash]; zi != nullptr; zi = zi->next) { 779 if ((dirlen == zi->nameLength) && 780 (0 == memcmp(item->Name(), zi->Name(), dirlen))) { 781 // we've already added this dir and all its parents 782 found = true; 783 break; 784 } 785 } 786 // if the directory was found, break out of the directory 787 // creation loop now that we know all implicit directories 788 // are there -- otherwise, start creating the zip item 789 if (found) break; 790 791 nsZipItem* diritem = CreateZipItem(); 792 if (!diritem) return NS_ERROR_OUT_OF_MEMORY; 793 794 // Point to the central record of the original item for the name part. 795 diritem->central = item->central; 796 diritem->nameLength = dirlen; 797 diritem->isSynthetic = true; 798 799 // add diritem to the file table 800 diritem->next = mFiles[hash]; 801 mFiles[hash] = diritem; 802 } /* end processing of dirs in item's name */ 803 } 804 } 805 MMAP_FAULT_HANDLER_CATCH(NS_ERROR_FAILURE) 806 return NS_OK; 807 } 808 809 //--------------------------------------------- 810 // nsZipArchive::GetFD 811 //--------------------------------------------- 812 nsZipHandle* nsZipArchive::GetFD() const { return mFd.get(); } 813 814 //--------------------------------------------- 815 // nsZipArchive::GetDataOffset 816 // Returns 0 on an error; 0 is not a valid result for any success case 817 //--------------------------------------------- 818 uint32_t nsZipArchive::GetDataOffset(nsZipItem* aItem) { 819 MOZ_ASSERT(aItem); 820 MOZ_DIAGNOSTIC_ASSERT(mFd); 821 822 uint32_t offset; 823 MMAP_FAULT_HANDLER_BEGIN_HANDLE(mFd) 824 //-- read local header to get variable length values and calculate 825 //-- the real data offset 826 uint32_t len = mFd->mLen; 827 MOZ_DIAGNOSTIC_ASSERT(len <= UINT32_MAX, "mLen > 2GB"); 828 const uint8_t* data = mFd->mFileData; 829 offset = aItem->LocalOffset(); 830 if (len < ZIPLOCAL_SIZE || offset > len - ZIPLOCAL_SIZE) { 831 return 0; 832 } 833 // Check there's enough space for the signature 834 if (offset > mFd->mLen) { 835 NS_WARNING("Corrupt local offset in JAR file"); 836 return 0; 837 } 838 839 // -- check signature before using the structure, in case the zip file is 840 // corrupt 841 ZipLocal* Local = (ZipLocal*)(data + offset); 842 if ((xtolong(Local->signature) != LOCALSIG)) return 0; 843 844 //-- NOTE: extralen is different in central header and local header 845 //-- for archives created using the Unix "zip" utility. To set 846 //-- the offset accurately we need the _local_ extralen. 847 offset += ZIPLOCAL_SIZE + xtoint(Local->filename_len) + 848 xtoint(Local->extrafield_len); 849 // Check data points inside the file. 850 if (offset > mFd->mLen) { 851 NS_WARNING("Corrupt data offset in JAR file"); 852 return 0; 853 } 854 855 MMAP_FAULT_HANDLER_CATCH(0) 856 // can't be 0 857 return offset; 858 } 859 860 //--------------------------------------------- 861 // nsZipArchive::GetData 862 //--------------------------------------------- 863 const uint8_t* nsZipArchive::GetData(nsZipItem* aItem) { 864 MOZ_DIAGNOSTIC_ASSERT(aItem); 865 if (!aItem) { 866 return nullptr; 867 } 868 uint32_t offset = GetDataOffset(aItem); 869 870 MMAP_FAULT_HANDLER_BEGIN_HANDLE(mFd) 871 // -- check if there is enough source data in the file 872 if (!offset || mFd->mLen < aItem->Size() || 873 offset > mFd->mLen - aItem->Size() || 874 (aItem->Compression() == STORED && aItem->Size() != aItem->RealSize())) { 875 return nullptr; 876 } 877 MMAP_FAULT_HANDLER_CATCH(nullptr) 878 879 return mFd->mFileData + offset; 880 } 881 882 //--------------------------------------------- 883 // nsZipArchive::SizeOfMapping 884 //--------------------------------------------- 885 int64_t nsZipArchive::SizeOfMapping() { return mFd ? mFd->SizeOfMapping() : 0; } 886 887 //------------------------------------------ 888 // nsZipArchive constructor and destructor 889 //------------------------------------------ 890 891 nsZipArchive::nsZipArchive(nsZipHandle* aZipHandle, PRFileDesc* aFd, 892 nsresult& aRv) 893 : mRefCnt(0), mFd(aZipHandle), mUseZipLog(false), mBuiltSynthetics(false) { 894 // initialize the table to nullptr 895 memset(mFiles, 0, sizeof(mFiles)); 896 MOZ_DIAGNOSTIC_ASSERT(aZipHandle); 897 898 //-- get table of contents for archive 899 aRv = BuildFileList(aFd); 900 if (NS_FAILED(aRv)) { 901 return; // whomever created us must destroy us in this case 902 } 903 if (aZipHandle->mFile && XRE_IsParentProcess()) { 904 static char* env = PR_GetEnv("MOZ_JAR_LOG_FILE"); 905 if (env) { 906 mUseZipLog = true; 907 908 zipLog.Init(env); 909 // We only log accesses in jar/zip archives within the NS_GRE_DIR 910 // and/or the APK on Android. For the former, we log the archive path 911 // relative to NS_GRE_DIR, and for the latter, the nested-archive 912 // path within the APK. This makes the path match the path of the 913 // archives relative to the packaged dist/$APP_NAME directory in a 914 // build. 915 if (aZipHandle->mFile.IsZip()) { 916 // Nested archive, likely omni.ja in APK. 917 aZipHandle->mFile.GetPath(mURI); 918 } else if (nsDirectoryService::gService) { 919 // We can reach here through the initialization of Omnijar from 920 // XRE_InitCommandLine, which happens before the directory service 921 // is initialized. When that happens, it means the opened archive is 922 // the APK, and we don't care to log that one, so we just skip 923 // when the directory service is not initialized. 924 nsCOMPtr<nsIFile> dir = aZipHandle->mFile.GetBaseFile(); 925 nsCOMPtr<nsIFile> gre_dir; 926 nsAutoCString path; 927 if (NS_SUCCEEDED(nsDirectoryService::gService->Get( 928 NS_GRE_DIR, NS_GET_IID(nsIFile), getter_AddRefs(gre_dir)))) { 929 nsAutoCString leaf; 930 nsCOMPtr<nsIFile> parent; 931 while (NS_SUCCEEDED(dir->GetNativeLeafName(leaf)) && 932 NS_SUCCEEDED(dir->GetParent(getter_AddRefs(parent)))) { 933 if (!parent) { 934 break; 935 } 936 dir = parent; 937 if (path.Length()) { 938 path.Insert('/', 0); 939 } 940 path.Insert(leaf, 0); 941 bool equals; 942 if (NS_SUCCEEDED(dir->Equals(gre_dir, &equals)) && equals) { 943 mURI.Assign(path); 944 break; 945 } 946 } 947 } 948 } 949 } 950 } 951 } 952 953 NS_IMPL_ADDREF(nsZipArchive) 954 NS_IMPL_RELEASE(nsZipArchive) 955 956 nsZipArchive::~nsZipArchive() { 957 LOG(("Closing nsZipArchive[%p]", this)); 958 if (mUseZipLog) { 959 zipLog.Release(); 960 } 961 } 962 963 //------------------------------------------ 964 // nsZipFind constructor and destructor 965 //------------------------------------------ 966 967 nsZipFind::nsZipFind(nsZipArchive* aZip, char* aPattern, bool aRegExp) 968 : mArchive(aZip), 969 mPattern(aPattern), 970 mItem(nullptr), 971 mSlot(0), 972 mRegExp(aRegExp) { 973 MOZ_COUNT_CTOR(nsZipFind); 974 MOZ_ASSERT(mArchive); 975 } 976 977 nsZipFind::~nsZipFind() { 978 free(mPattern); 979 980 MOZ_COUNT_DTOR(nsZipFind); 981 } 982 983 //------------------------------------------ 984 // helper functions 985 //------------------------------------------ 986 987 /* 988 * HashName 989 * 990 * returns a hash key for the entry name 991 */ 992 MOZ_NO_SANITIZE_UNSIGNED_OVERFLOW 993 static uint32_t HashName(const char* aName, uint16_t len) { 994 MOZ_ASSERT(aName != 0); 995 996 const uint8_t* p = (const uint8_t*)aName; 997 const uint8_t* endp = p + len; 998 uint32_t val = 0; 999 while (p != endp) { 1000 val = val * 37 + *p++; 1001 } 1002 1003 return (val % ZIP_TABSIZE); 1004 } 1005 1006 /* 1007 * x t o i n t 1008 * 1009 * Converts a two byte ugly endianed integer 1010 * to our platform's integer. 1011 */ 1012 static uint16_t xtoint(const uint8_t* ii) { 1013 return (uint16_t)((ii[0]) | (ii[1] << 8)); 1014 } 1015 1016 /* 1017 * x t o l o n g 1018 * 1019 * Converts a four byte ugly endianed integer 1020 * to our platform's integer. 1021 */ 1022 static uint32_t xtolong(const uint8_t* ll) { 1023 return (uint32_t)((ll[0] << 0) | (ll[1] << 8) | (ll[2] << 16) | 1024 (ll[3] << 24)); 1025 } 1026 1027 /* 1028 * GetModTime 1029 * 1030 * returns last modification time in microseconds 1031 */ 1032 static PRTime GetModTime(uint16_t aDate, uint16_t aTime) { 1033 // Note that on DST shift we can't handle correctly the hour that is valid 1034 // in both DST zones 1035 PRExplodedTime time; 1036 1037 time.tm_usec = 0; 1038 1039 time.tm_hour = (aTime >> 11) & 0x1F; 1040 time.tm_min = (aTime >> 5) & 0x3F; 1041 time.tm_sec = (aTime & 0x1F) * 2; 1042 1043 time.tm_year = (aDate >> 9) + 1980; 1044 time.tm_month = ((aDate >> 5) & 0x0F) - 1; 1045 time.tm_mday = aDate & 0x1F; 1046 1047 time.tm_params.tp_gmt_offset = 0; 1048 time.tm_params.tp_dst_offset = 0; 1049 1050 PR_NormalizeTime(&time, PR_GMTParameters); 1051 time.tm_params.tp_gmt_offset = PR_LocalTimeParameters(&time).tp_gmt_offset; 1052 PR_NormalizeTime(&time, PR_GMTParameters); 1053 time.tm_params.tp_dst_offset = PR_LocalTimeParameters(&time).tp_dst_offset; 1054 1055 return PR_ImplodeTime(&time); 1056 } 1057 1058 nsZipItem::nsZipItem() 1059 : next(nullptr), central(nullptr), nameLength(0), isSynthetic(false) {} 1060 1061 uint32_t nsZipItem::LocalOffset() { return xtolong(central->localhdr_offset); } 1062 1063 uint32_t nsZipItem::Size() { return isSynthetic ? 0 : xtolong(central->size); } 1064 1065 uint32_t nsZipItem::RealSize() { 1066 return isSynthetic ? 0 : xtolong(central->orglen); 1067 } 1068 1069 uint32_t nsZipItem::CRC32() { 1070 return isSynthetic ? 0 : xtolong(central->crc32); 1071 } 1072 1073 uint16_t nsZipItem::Date() { 1074 return isSynthetic ? kSyntheticDate : xtoint(central->date); 1075 } 1076 1077 uint16_t nsZipItem::Time() { 1078 return isSynthetic ? kSyntheticTime : xtoint(central->time); 1079 } 1080 1081 uint16_t nsZipItem::Compression() { 1082 return isSynthetic ? STORED : xtoint(central->method); 1083 } 1084 1085 bool nsZipItem::IsDirectory() { 1086 return isSynthetic || ((nameLength > 0) && ('/' == Name()[nameLength - 1])); 1087 } 1088 1089 uint16_t nsZipItem::Mode() { 1090 if (isSynthetic) return 0755; 1091 return ((uint16_t)(central->external_attributes[2]) | 0x100); 1092 } 1093 1094 const uint8_t* nsZipItem::GetExtraField(uint16_t aTag, uint16_t* aBlockSize) { 1095 if (isSynthetic) return nullptr; 1096 1097 const unsigned char* buf = 1098 ((const unsigned char*)central) + ZIPCENTRAL_SIZE + nameLength; 1099 uint32_t buflen; 1100 1101 MMAP_FAULT_HANDLER_BEGIN_BUFFER(central, ZIPCENTRAL_SIZE + nameLength) 1102 buflen = (uint32_t)xtoint(central->extrafield_len); 1103 MMAP_FAULT_HANDLER_CATCH(nullptr) 1104 1105 uint32_t pos = 0; 1106 uint16_t tag, blocksize; 1107 1108 MMAP_FAULT_HANDLER_BEGIN_BUFFER(buf, buflen) 1109 while (buf && (pos + 4) <= buflen) { 1110 tag = xtoint(buf + pos); 1111 blocksize = xtoint(buf + pos + 2); 1112 1113 if (aTag == tag && (pos + 4 + blocksize) <= buflen) { 1114 *aBlockSize = blocksize; 1115 return buf + pos; 1116 } 1117 1118 pos += blocksize + 4; 1119 } 1120 MMAP_FAULT_HANDLER_CATCH(nullptr) 1121 1122 return nullptr; 1123 } 1124 1125 PRTime nsZipItem::LastModTime() { 1126 if (isSynthetic) return GetModTime(kSyntheticDate, kSyntheticTime); 1127 1128 // Try to read timestamp from extra field 1129 uint16_t blocksize; 1130 const uint8_t* tsField = GetExtraField(EXTENDED_TIMESTAMP_FIELD, &blocksize); 1131 if (tsField && blocksize >= 5 && tsField[4] & EXTENDED_TIMESTAMP_MODTIME) { 1132 return (PRTime)(xtolong(tsField + 5)) * PR_USEC_PER_SEC; 1133 } 1134 1135 return GetModTime(Date(), Time()); 1136 } 1137 1138 nsZipCursor::nsZipCursor(nsZipItem* item, nsZipArchive* aZip, uint8_t* aBuf, 1139 uint32_t aBufSize, bool doCRC) 1140 : mItem(item), 1141 mBuf(aBuf), 1142 mBufSize(aBufSize), 1143 mZs(), 1144 mCRC(0), 1145 mDoCRC(doCRC) { 1146 if (mItem->Compression() == DEFLATED) { 1147 #ifdef DEBUG 1148 nsresult status = 1149 #endif 1150 gZlibInit(&mZs); 1151 NS_ASSERTION(status == NS_OK, "Zlib failed to initialize"); 1152 NS_ASSERTION(aBuf, "Must pass in a buffer for DEFLATED nsZipItem"); 1153 } 1154 1155 mZs.avail_in = item->Size(); 1156 mZs.next_in = (Bytef*)aZip->GetData(item); 1157 1158 if (doCRC) mCRC = crc32(0L, Z_NULL, 0); 1159 } 1160 1161 nsZipCursor::~nsZipCursor() { 1162 if (mItem->Compression() == DEFLATED) { 1163 inflateEnd(&mZs); 1164 } 1165 } 1166 1167 uint8_t* nsZipCursor::ReadOrCopy(uint32_t* aBytesRead, bool aCopy) { 1168 int zerr; 1169 uint8_t* buf = nullptr; 1170 bool verifyCRC = true; 1171 1172 if (!mZs.next_in) return nullptr; 1173 MMAP_FAULT_HANDLER_BEGIN_BUFFER(mZs.next_in, mZs.avail_in) 1174 switch (mItem->Compression()) { 1175 case STORED: 1176 if (!aCopy) { 1177 *aBytesRead = mZs.avail_in; 1178 buf = mZs.next_in; 1179 mZs.next_in += mZs.avail_in; 1180 mZs.avail_in = 0; 1181 } else { 1182 *aBytesRead = mZs.avail_in > mBufSize ? mBufSize : mZs.avail_in; 1183 memcpy(mBuf, mZs.next_in, *aBytesRead); 1184 mZs.avail_in -= *aBytesRead; 1185 mZs.next_in += *aBytesRead; 1186 } 1187 break; 1188 case DEFLATED: 1189 buf = mBuf; 1190 mZs.next_out = buf; 1191 mZs.avail_out = mBufSize; 1192 1193 zerr = inflate(&mZs, Z_PARTIAL_FLUSH); 1194 if (zerr != Z_OK && zerr != Z_STREAM_END) return nullptr; 1195 1196 *aBytesRead = mZs.next_out - buf; 1197 verifyCRC = (zerr == Z_STREAM_END); 1198 break; 1199 default: 1200 return nullptr; 1201 } 1202 1203 if (mDoCRC) { 1204 mCRC = crc32(mCRC, (const unsigned char*)buf, *aBytesRead); 1205 if (verifyCRC && mCRC != mItem->CRC32()) return nullptr; 1206 } 1207 MMAP_FAULT_HANDLER_CATCH(nullptr) 1208 return buf; 1209 } 1210 1211 nsZipItemPtr_base::nsZipItemPtr_base(nsZipArchive* aZip, 1212 const nsACString& aEntryName, bool doCRC) 1213 : mReturnBuf(nullptr), mReadlen(0) { 1214 // make sure the ziparchive hangs around 1215 mZipHandle = aZip->GetFD(); 1216 1217 nsZipItem* item = aZip->GetItem(aEntryName); 1218 if (!item) return; 1219 1220 uint32_t size = 0; 1221 bool compressed = (item->Compression() == DEFLATED); 1222 if (compressed) { 1223 size = item->RealSize(); 1224 mAutoBuf = MakeUniqueFallible<uint8_t[]>(size); 1225 if (!mAutoBuf) { 1226 return; 1227 } 1228 } 1229 1230 nsZipCursor cursor(item, aZip, mAutoBuf.get(), size, doCRC); 1231 mReturnBuf = cursor.Read(&mReadlen); 1232 if (!mReturnBuf) { 1233 return; 1234 } 1235 1236 if (mReadlen != item->RealSize()) { 1237 NS_ASSERTION(mReadlen == item->RealSize(), "nsZipCursor underflow"); 1238 mReturnBuf = nullptr; 1239 return; 1240 } 1241 }