modules/libjar/nsZipArchive.cpp

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

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

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

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

mercurial