Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/modules/libjar/nsJAR.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
#include <string.h>
7
#include "nsJARInputStream.h"
8
#include "nsJAR.h"
9
#include "nsIFile.h"
10
#include "nsIConsoleService.h"
11
#include "mozilla/DebugOnly.h"
12
#include "mozilla/Omnijar.h"
13
#include "mozilla/Unused.h"
14
15
#ifdef XP_UNIX
16
  #include <sys/stat.h>
17
#elif defined (XP_WIN)
18
  #include <io.h>
19
#endif
20
21
using namespace mozilla;
22
23
//----------------------------------------------
24
// nsJAR constructor/destructor
25
//----------------------------------------------
26
27
// The following initialization makes a guess of 10 entries per jarfile.
28
nsJAR::nsJAR(): mZip(new nsZipArchive()),
29
                mReleaseTime(PR_INTERVAL_NO_TIMEOUT),
30
                mCache(nullptr),
31
                mLock("nsJAR::mLock"),
32
                mMtime(0),
33
                mOpened(false),
34
                mIsOmnijar(false)
35
1
{
36
1
}
37
38
nsJAR::~nsJAR()
39
0
{
40
0
  Close();
41
0
}
42
43
NS_IMPL_QUERY_INTERFACE(nsJAR, nsIZipReader)
44
NS_IMPL_ADDREF(nsJAR)
45
46
// Custom Release method works with nsZipReaderCache...
47
// Release might be called from multi-thread, we have to
48
// take this function carefully to avoid delete-after-use.
49
MozExternalRefCountType nsJAR::Release(void)
50
10
{
51
10
  nsrefcnt count;
52
10
  MOZ_ASSERT(0 != mRefCnt, "dup release");
53
10
54
10
  RefPtr<nsZipReaderCache> cache;
55
10
  if (mRefCnt == 2) { // don't use a lock too frequently
56
5
    // Use a mutex here to guarantee mCache is not racing and the target instance
57
5
    // is still valid to increase ref-count.
58
5
    MutexAutoLock lock(mLock);
59
5
    cache = mCache;
60
5
    mCache = nullptr;
61
5
  }
62
10
  if (cache) {
63
1
    DebugOnly<nsresult> rv = cache->ReleaseZip(this);
64
1
    MOZ_ASSERT(NS_SUCCEEDED(rv), "failed to release zip file");
65
1
  }
66
10
67
10
  count = --mRefCnt; // don't access any member variable after this line
68
10
  NS_LOG_RELEASE(this, count, "nsJAR");
69
10
  if (0 == count) {
70
0
    mRefCnt = 1; /* stabilize */
71
0
    /* enable this to find non-threadsafe destructors: */
72
0
    /* NS_ASSERT_OWNINGTHREAD(nsJAR); */
73
0
    delete this;
74
0
    return 0;
75
0
  }
76
10
77
10
  return count;
78
10
}
79
80
//----------------------------------------------
81
// nsIZipReader implementation
82
//----------------------------------------------
83
84
NS_IMETHODIMP
85
nsJAR::Open(nsIFile* zipFile)
86
1
{
87
1
  NS_ENSURE_ARG_POINTER(zipFile);
88
1
  if (mOpened) return NS_ERROR_FAILURE; // Already open!
89
1
90
1
  mZipFile = zipFile;
91
1
  mOuterZipEntry.Truncate();
92
1
  mOpened = true;
93
1
94
1
  // The omnijar is special, it is opened early on and closed late
95
1
  // this avoids reopening it
96
1
  RefPtr<nsZipArchive> zip = mozilla::Omnijar::GetReader(zipFile);
97
1
  if (zip) {
98
1
    mZip = zip;
99
1
    mIsOmnijar = true;
100
1
    return NS_OK;
101
1
  }
102
0
  return mZip->OpenArchive(zipFile);
103
0
}
104
105
NS_IMETHODIMP
106
nsJAR::OpenInner(nsIZipReader *aZipReader, const nsACString &aZipEntry)
107
0
{
108
0
  NS_ENSURE_ARG_POINTER(aZipReader);
109
0
  if (mOpened) return NS_ERROR_FAILURE; // Already open!
110
0
111
0
  bool exist;
112
0
  nsresult rv = aZipReader->HasEntry(aZipEntry, &exist);
113
0
  NS_ENSURE_SUCCESS(rv, rv);
114
0
  NS_ENSURE_TRUE(exist, NS_ERROR_FILE_NOT_FOUND);
115
0
116
0
  rv = aZipReader->GetFile(getter_AddRefs(mZipFile));
117
0
  NS_ENSURE_SUCCESS(rv, rv);
118
0
119
0
  mOpened = true;
120
0
121
0
  mOuterZipEntry.Assign(aZipEntry);
122
0
123
0
  RefPtr<nsZipHandle> handle;
124
0
  rv = nsZipHandle::Init(static_cast<nsJAR*>(aZipReader)->mZip.get(), PromiseFlatCString(aZipEntry).get(),
125
0
                         getter_AddRefs(handle));
126
0
  if (NS_FAILED(rv))
127
0
    return rv;
128
0
129
0
  return mZip->OpenArchive(handle);
130
0
}
131
132
NS_IMETHODIMP
133
nsJAR::OpenMemory(void* aData, uint32_t aLength)
134
0
{
135
0
  NS_ENSURE_ARG_POINTER(aData);
136
0
  if (mOpened) return NS_ERROR_FAILURE; // Already open!
137
0
138
0
  mOpened = true;
139
0
140
0
  RefPtr<nsZipHandle> handle;
141
0
  nsresult rv = nsZipHandle::Init(static_cast<uint8_t*>(aData), aLength,
142
0
                                  getter_AddRefs(handle));
143
0
  if (NS_FAILED(rv))
144
0
    return rv;
145
0
146
0
  return mZip->OpenArchive(handle);
147
0
}
148
149
NS_IMETHODIMP
150
nsJAR::GetFile(nsIFile* *result)
151
0
{
152
0
  *result = mZipFile;
153
0
  NS_IF_ADDREF(*result);
154
0
  return NS_OK;
155
0
}
156
157
NS_IMETHODIMP
158
nsJAR::Close()
159
0
{
160
0
  if (!mOpened) {
161
0
    return NS_ERROR_FAILURE; // Never opened or already closed.
162
0
  }
163
0
164
0
  mOpened = false;
165
0
166
0
  if (mIsOmnijar) {
167
0
    // Reset state, but don't close the omnijar because we did not open it.
168
0
    mIsOmnijar = false;
169
0
    mZip = new nsZipArchive();
170
0
    return NS_OK;
171
0
  }
172
0
173
0
  return mZip->CloseArchive();
174
0
}
175
176
NS_IMETHODIMP
177
nsJAR::Test(const nsACString &aEntryName)
178
0
{
179
0
  return mZip->Test(aEntryName.IsEmpty()? nullptr : PromiseFlatCString(aEntryName).get());
180
0
}
181
182
NS_IMETHODIMP
183
nsJAR::Extract(const nsACString &aEntryName, nsIFile* outFile)
184
0
{
185
0
  // nsZipArchive and zlib are not thread safe
186
0
  // we need to use a lock to prevent bug #51267
187
0
  MutexAutoLock lock(mLock);
188
0
189
0
  nsZipItem *item = mZip->GetItem(PromiseFlatCString(aEntryName).get());
190
0
  NS_ENSURE_TRUE(item, NS_ERROR_FILE_TARGET_DOES_NOT_EXIST);
191
0
192
0
  // Remove existing file or directory so we set permissions correctly.
193
0
  // If it's a directory that already exists and contains files, throw
194
0
  // an exception and return.
195
0
196
0
  nsresult rv = outFile->Remove(false);
197
0
  if (rv == NS_ERROR_FILE_DIR_NOT_EMPTY ||
198
0
      rv == NS_ERROR_FAILURE)
199
0
    return rv;
200
0
201
0
  if (item->IsDirectory())
202
0
  {
203
0
    rv = outFile->Create(nsIFile::DIRECTORY_TYPE, item->Mode());
204
0
    //XXX Do this in nsZipArchive?  It would be nice to keep extraction
205
0
    //XXX code completely there, but that would require a way to get a
206
0
    //XXX PRDir from outFile.
207
0
  }
208
0
  else
209
0
  {
210
0
    PRFileDesc* fd;
211
0
    rv = outFile->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE, item->Mode(), &fd);
212
0
    if (NS_FAILED(rv)) return rv;
213
0
214
0
    // ExtractFile also closes the fd handle and resolves the symlink if needed
215
0
    rv = mZip->ExtractFile(item, outFile, fd);
216
0
  }
217
0
  if (NS_FAILED(rv)) return rv;
218
0
219
0
  // nsIFile needs milliseconds, while prtime is in microseconds.
220
0
  // non-fatal if this fails, ignore errors
221
0
  outFile->SetLastModifiedTime(item->LastModTime() / PR_USEC_PER_MSEC);
222
0
223
0
  return NS_OK;
224
0
}
225
226
NS_IMETHODIMP
227
nsJAR::GetEntry(const nsACString &aEntryName, nsIZipEntry* *result)
228
0
{
229
0
  nsZipItem* zipItem = mZip->GetItem(PromiseFlatCString(aEntryName).get());
230
0
  NS_ENSURE_TRUE(zipItem, NS_ERROR_FILE_TARGET_DOES_NOT_EXIST);
231
0
232
0
  nsJARItem* jarItem = new nsJARItem(zipItem);
233
0
234
0
  NS_ADDREF(*result = jarItem);
235
0
  return NS_OK;
236
0
}
237
238
NS_IMETHODIMP
239
nsJAR::HasEntry(const nsACString &aEntryName, bool *result)
240
0
{
241
0
  *result = mZip->GetItem(PromiseFlatCString(aEntryName).get()) != nullptr;
242
0
  return NS_OK;
243
0
}
244
245
NS_IMETHODIMP
246
nsJAR::FindEntries(const nsACString &aPattern, nsIUTF8StringEnumerator **result)
247
0
{
248
0
  NS_ENSURE_ARG_POINTER(result);
249
0
250
0
  nsZipFind *find;
251
0
  nsresult rv = mZip->FindInit(aPattern.IsEmpty()? nullptr : PromiseFlatCString(aPattern).get(), &find);
252
0
  NS_ENSURE_SUCCESS(rv, rv);
253
0
254
0
  nsIUTF8StringEnumerator *zipEnum = new nsJAREnumerator(find);
255
0
256
0
  NS_ADDREF(*result = zipEnum);
257
0
  return NS_OK;
258
0
}
259
260
NS_IMETHODIMP
261
nsJAR::GetInputStream(const nsACString &aFilename, nsIInputStream** result)
262
5
{
263
5
  return GetInputStreamWithSpec(EmptyCString(), aFilename, result);
264
5
}
265
266
NS_IMETHODIMP
267
nsJAR::GetInputStreamWithSpec(const nsACString& aJarDirSpec,
268
                          const nsACString &aEntryName, nsIInputStream** result)
269
5
{
270
5
  NS_ENSURE_ARG_POINTER(result);
271
5
272
5
  // Watch out for the jar:foo.zip!/ (aDir is empty) top-level special case!
273
5
  nsZipItem *item = nullptr;
274
5
  const nsCString& entry = PromiseFlatCString(aEntryName);
275
5
  if (*entry.get()) {
276
5
    // First check if item exists in jar
277
5
    item = mZip->GetItem(entry.get());
278
5
    if (!item) return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST;
279
5
  }
280
5
  nsJARInputStream* jis = new nsJARInputStream();
281
5
  // addref now so we can call InitFile/InitDirectory()
282
5
  NS_ADDREF(*result = jis);
283
5
284
5
  nsresult rv = NS_OK;
285
5
  if (!item || item->IsDirectory()) {
286
0
    rv = jis->InitDirectory(this, aJarDirSpec, entry.get());
287
5
  } else {
288
5
    rv = jis->InitFile(this, item);
289
5
  }
290
5
  if (NS_FAILED(rv)) {
291
0
    NS_RELEASE(*result);
292
0
  }
293
5
  return rv;
294
5
}
295
296
nsresult
297
nsJAR::GetJarPath(nsACString& aResult)
298
0
{
299
0
  NS_ENSURE_ARG_POINTER(mZipFile);
300
0
301
0
  return mZipFile->GetPersistentDescriptor(aResult);
302
0
}
303
304
nsresult
305
nsJAR::GetNSPRFileDesc(PRFileDesc** aNSPRFileDesc)
306
0
{
307
0
  if (!aNSPRFileDesc) {
308
0
    return NS_ERROR_ILLEGAL_VALUE;
309
0
  }
310
0
  *aNSPRFileDesc = nullptr;
311
0
312
0
  if (!mZip) {
313
0
    return NS_ERROR_FAILURE;
314
0
  }
315
0
316
0
  RefPtr<nsZipHandle> handle = mZip->GetFD();
317
0
  if (!handle) {
318
0
    return NS_ERROR_FAILURE;
319
0
  }
320
0
321
0
  return handle->GetNSPRFileDesc(aNSPRFileDesc);
322
0
}
323
324
//----------------------------------------------
325
// nsJAR private implementation
326
//----------------------------------------------
327
nsresult
328
nsJAR::LoadEntry(const nsACString& aFilename, nsCString& aBuf)
329
0
{
330
0
  //-- Get a stream for reading the file
331
0
  nsresult rv;
332
0
  nsCOMPtr<nsIInputStream> manifestStream;
333
0
  rv = GetInputStream(aFilename, getter_AddRefs(manifestStream));
334
0
  if (NS_FAILED(rv)) return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST;
335
0
336
0
  //-- Read the manifest file into memory
337
0
  char* buf;
338
0
  uint64_t len64;
339
0
  rv = manifestStream->Available(&len64);
340
0
  if (NS_FAILED(rv)) return rv;
341
0
  if (len64 >= UINT32_MAX) { // bug 164695
342
0
    nsZipArchive::sFileCorruptedReason = "nsJAR: invalid manifest size";
343
0
    return NS_ERROR_FILE_CORRUPTED;
344
0
  }
345
0
  uint32_t len = (uint32_t)len64;
346
0
  buf = (char*)malloc(len+1);
347
0
  if (!buf) return NS_ERROR_OUT_OF_MEMORY;
348
0
  uint32_t bytesRead;
349
0
  rv = manifestStream->Read(buf, len, &bytesRead);
350
0
  if (bytesRead != len) {
351
0
    nsZipArchive::sFileCorruptedReason = "nsJAR: manifest too small";
352
0
    rv = NS_ERROR_FILE_CORRUPTED;
353
0
  }
354
0
  if (NS_FAILED(rv)) {
355
0
    free(buf);
356
0
    return rv;
357
0
  }
358
0
  buf[len] = '\0'; //Null-terminate the buffer
359
0
  aBuf.Adopt(buf, len);
360
0
  return NS_OK;
361
0
}
362
363
364
int32_t
365
nsJAR::ReadLine(const char** src)
366
0
{
367
0
  if (!*src) {
368
0
    return 0;
369
0
  }
370
0
371
0
  //--Moves pointer to beginning of next line and returns line length
372
0
  //  not including CR/LF.
373
0
  int32_t length;
374
0
  char* eol = PL_strpbrk(*src, "\r\n");
375
0
376
0
  if (eol == nullptr) // Probably reached end of file before newline
377
0
  {
378
0
    length = strlen(*src);
379
0
    if (length == 0) // immediate end-of-file
380
0
      *src = nullptr;
381
0
    else             // some data left on this line
382
0
      *src += length;
383
0
  }
384
0
  else
385
0
  {
386
0
    length = eol - *src;
387
0
    if (eol[0] == '\r' && eol[1] == '\n')      // CR LF, so skip 2
388
0
      *src = eol+2;
389
0
    else                                       // Either CR or LF, so skip 1
390
0
      *src = eol+1;
391
0
  }
392
0
  return length;
393
0
}
394
395
NS_IMPL_ISUPPORTS(nsJAREnumerator, nsIUTF8StringEnumerator,
396
                  nsIStringEnumerator)
397
398
//----------------------------------------------
399
// nsJAREnumerator::HasMore
400
//----------------------------------------------
401
NS_IMETHODIMP
402
nsJAREnumerator::HasMore(bool* aResult)
403
0
{
404
0
    // try to get the next element
405
0
    if (!mName) {
406
0
        NS_ASSERTION(mFind, "nsJAREnumerator: Missing zipFind.");
407
0
        nsresult rv = mFind->FindNext( &mName, &mNameLen );
408
0
        if (rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST) {
409
0
            *aResult = false;                    // No more matches available
410
0
            return NS_OK;
411
0
        }
412
0
        NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);    // no error translation
413
0
    }
414
0
415
0
    *aResult = true;
416
0
    return NS_OK;
417
0
}
418
419
//----------------------------------------------
420
// nsJAREnumerator::GetNext
421
//----------------------------------------------
422
NS_IMETHODIMP
423
nsJAREnumerator::GetNext(nsACString& aResult)
424
0
{
425
0
    // check if the current item is "stale"
426
0
    if (!mName) {
427
0
        bool     bMore;
428
0
        nsresult rv = HasMore(&bMore);
429
0
        if (NS_FAILED(rv) || !bMore)
430
0
            return NS_ERROR_FAILURE; // no error translation
431
0
    }
432
0
    aResult.Assign(mName, mNameLen);
433
0
    mName = 0; // we just gave this one away
434
0
    return NS_OK;
435
0
}
436
437
438
NS_IMPL_ISUPPORTS(nsJARItem, nsIZipEntry)
439
440
nsJARItem::nsJARItem(nsZipItem* aZipItem)
441
    : mSize(aZipItem->Size()),
442
      mRealsize(aZipItem->RealSize()),
443
      mCrc32(aZipItem->CRC32()),
444
      mLastModTime(aZipItem->LastModTime()),
445
      mCompression(aZipItem->Compression()),
446
      mPermissions(aZipItem->Mode()),
447
      mIsDirectory(aZipItem->IsDirectory()),
448
      mIsSynthetic(aZipItem->isSynthetic)
449
0
{
450
0
}
451
452
//------------------------------------------
453
// nsJARItem::GetCompression
454
//------------------------------------------
455
NS_IMETHODIMP
456
nsJARItem::GetCompression(uint16_t *aCompression)
457
0
{
458
0
    NS_ENSURE_ARG_POINTER(aCompression);
459
0
460
0
    *aCompression = mCompression;
461
0
    return NS_OK;
462
0
}
463
464
//------------------------------------------
465
// nsJARItem::GetSize
466
//------------------------------------------
467
NS_IMETHODIMP
468
nsJARItem::GetSize(uint32_t *aSize)
469
0
{
470
0
    NS_ENSURE_ARG_POINTER(aSize);
471
0
472
0
    *aSize = mSize;
473
0
    return NS_OK;
474
0
}
475
476
//------------------------------------------
477
// nsJARItem::GetRealSize
478
//------------------------------------------
479
NS_IMETHODIMP
480
nsJARItem::GetRealSize(uint32_t *aRealsize)
481
0
{
482
0
    NS_ENSURE_ARG_POINTER(aRealsize);
483
0
484
0
    *aRealsize = mRealsize;
485
0
    return NS_OK;
486
0
}
487
488
//------------------------------------------
489
// nsJARItem::GetCrc32
490
//------------------------------------------
491
NS_IMETHODIMP
492
nsJARItem::GetCRC32(uint32_t *aCrc32)
493
0
{
494
0
    NS_ENSURE_ARG_POINTER(aCrc32);
495
0
496
0
    *aCrc32 = mCrc32;
497
0
    return NS_OK;
498
0
}
499
500
//------------------------------------------
501
// nsJARItem::GetIsDirectory
502
//------------------------------------------
503
NS_IMETHODIMP
504
nsJARItem::GetIsDirectory(bool *aIsDirectory)
505
0
{
506
0
    NS_ENSURE_ARG_POINTER(aIsDirectory);
507
0
508
0
    *aIsDirectory = mIsDirectory;
509
0
    return NS_OK;
510
0
}
511
512
//------------------------------------------
513
// nsJARItem::GetIsSynthetic
514
//------------------------------------------
515
NS_IMETHODIMP
516
nsJARItem::GetIsSynthetic(bool *aIsSynthetic)
517
0
{
518
0
    NS_ENSURE_ARG_POINTER(aIsSynthetic);
519
0
520
0
    *aIsSynthetic = mIsSynthetic;
521
0
    return NS_OK;
522
0
}
523
524
//------------------------------------------
525
// nsJARItem::GetLastModifiedTime
526
//------------------------------------------
527
NS_IMETHODIMP
528
nsJARItem::GetLastModifiedTime(PRTime* aLastModTime)
529
0
{
530
0
    NS_ENSURE_ARG_POINTER(aLastModTime);
531
0
532
0
    *aLastModTime = mLastModTime;
533
0
    return NS_OK;
534
0
}
535
536
//------------------------------------------
537
// nsJARItem::GetPermissions
538
//------------------------------------------
539
NS_IMETHODIMP
540
nsJARItem::GetPermissions(uint32_t* aPermissions)
541
0
{
542
0
    NS_ENSURE_ARG_POINTER(aPermissions);
543
0
544
0
    *aPermissions = mPermissions;
545
0
    return NS_OK;
546
0
}
547
548
////////////////////////////////////////////////////////////////////////////////
549
// nsIZipReaderCache
550
551
NS_IMPL_ISUPPORTS(nsZipReaderCache, nsIZipReaderCache, nsIObserver, nsISupportsWeakReference)
552
553
nsZipReaderCache::nsZipReaderCache()
554
  : mLock("nsZipReaderCache.mLock")
555
  , mCacheSize(0)
556
  , mZips()
557
#ifdef ZIP_CACHE_HIT_RATE
558
    ,
559
    mZipCacheLookups(0),
560
    mZipCacheHits(0),
561
    mZipCacheFlushes(0),
562
    mZipSyncMisses(0)
563
#endif
564
3
{
565
3
}
566
567
NS_IMETHODIMP
568
nsZipReaderCache::Init(uint32_t cacheSize)
569
3
{
570
3
  mCacheSize = cacheSize;
571
3
572
3
// Register as a memory pressure observer
573
3
  nsCOMPtr<nsIObserverService> os =
574
3
           do_GetService("@mozilla.org/observer-service;1");
575
3
  if (os)
576
3
  {
577
3
    os->AddObserver(this, "memory-pressure", true);
578
3
    os->AddObserver(this, "chrome-flush-caches", true);
579
3
    os->AddObserver(this, "flush-cache-entry", true);
580
3
  }
581
3
// ignore failure of the observer registration.
582
3
583
3
  return NS_OK;
584
3
}
585
586
nsZipReaderCache::~nsZipReaderCache()
587
0
{
588
0
  for (auto iter = mZips.Iter(); !iter.Done(); iter.Next()) {
589
0
    iter.UserData()->SetZipReaderCache(nullptr);
590
0
  }
591
0
592
#ifdef ZIP_CACHE_HIT_RATE
593
  printf("nsZipReaderCache size=%d hits=%d lookups=%d rate=%f%% flushes=%d missed %d\n",
594
         mCacheSize, mZipCacheHits, mZipCacheLookups,
595
         (float)mZipCacheHits / mZipCacheLookups,
596
         mZipCacheFlushes, mZipSyncMisses);
597
#endif
598
}
599
600
NS_IMETHODIMP
601
nsZipReaderCache::IsCached(nsIFile* zipFile, bool* aResult)
602
0
{
603
0
  NS_ENSURE_ARG_POINTER(zipFile);
604
0
  nsresult rv;
605
0
  MutexAutoLock lock(mLock);
606
0
607
0
  nsAutoCString uri;
608
0
  rv = zipFile->GetPersistentDescriptor(uri);
609
0
  if (NS_FAILED(rv))
610
0
    return rv;
611
0
612
0
  uri.InsertLiteral("file:", 0);
613
0
614
0
  *aResult = mZips.Contains(uri);
615
0
  return NS_OK;
616
0
}
617
618
nsresult
619
nsZipReaderCache::GetZip(nsIFile* zipFile, nsIZipReader* *result,
620
                         bool failOnMiss)
621
5
{
622
5
  NS_ENSURE_ARG_POINTER(zipFile);
623
5
  nsresult rv;
624
5
  MutexAutoLock lock(mLock);
625
5
626
#ifdef ZIP_CACHE_HIT_RATE
627
  mZipCacheLookups++;
628
#endif
629
630
5
  nsAutoCString uri;
631
5
  rv = zipFile->GetPersistentDescriptor(uri);
632
5
  if (NS_FAILED(rv)) return rv;
633
5
634
5
  uri.InsertLiteral("file:", 0);
635
5
636
5
  RefPtr<nsJAR> zip;
637
5
  mZips.Get(uri, getter_AddRefs(zip));
638
5
  if (zip) {
639
#ifdef ZIP_CACHE_HIT_RATE
640
    mZipCacheHits++;
641
#endif
642
    zip->ClearReleaseTime();
643
4
  } else {
644
1
    if (failOnMiss) {
645
0
      return NS_ERROR_CACHE_KEY_NOT_FOUND;
646
0
    }
647
1
648
1
    zip = new nsJAR();
649
1
    zip->SetZipReaderCache(this);
650
1
    rv = zip->Open(zipFile);
651
1
    if (NS_FAILED(rv)) {
652
0
      return rv;
653
0
    }
654
1
655
1
    MOZ_ASSERT(!mZips.Contains(uri));
656
1
    mZips.Put(uri, zip);
657
1
  }
658
5
  zip.forget(result);
659
5
  return rv;
660
5
}
661
662
NS_IMETHODIMP
663
nsZipReaderCache::GetZipIfCached(nsIFile* zipFile, nsIZipReader* *result)
664
0
{
665
0
  return GetZip(zipFile, result, true);
666
0
}
667
668
NS_IMETHODIMP
669
nsZipReaderCache::GetZip(nsIFile* zipFile, nsIZipReader* *result)
670
5
{
671
5
  return GetZip(zipFile, result, false);
672
5
}
673
674
NS_IMETHODIMP
675
nsZipReaderCache::GetInnerZip(nsIFile* zipFile, const nsACString &entry,
676
                              nsIZipReader* *result)
677
0
{
678
0
  NS_ENSURE_ARG_POINTER(zipFile);
679
0
680
0
  nsCOMPtr<nsIZipReader> outerZipReader;
681
0
  nsresult rv = GetZip(zipFile, getter_AddRefs(outerZipReader));
682
0
  NS_ENSURE_SUCCESS(rv, rv);
683
0
684
0
  MutexAutoLock lock(mLock);
685
0
686
#ifdef ZIP_CACHE_HIT_RATE
687
  mZipCacheLookups++;
688
#endif
689
690
0
  nsAutoCString uri;
691
0
  rv = zipFile->GetPersistentDescriptor(uri);
692
0
  if (NS_FAILED(rv)) return rv;
693
0
694
0
  uri.InsertLiteral("jar:", 0);
695
0
  uri.AppendLiteral("!/");
696
0
  uri.Append(entry);
697
0
698
0
  RefPtr<nsJAR> zip;
699
0
  mZips.Get(uri, getter_AddRefs(zip));
700
0
  if (zip) {
701
#ifdef ZIP_CACHE_HIT_RATE
702
    mZipCacheHits++;
703
#endif
704
    zip->ClearReleaseTime();
705
0
  } else {
706
0
    zip = new nsJAR();
707
0
    zip->SetZipReaderCache(this);
708
0
709
0
    rv = zip->OpenInner(outerZipReader, entry);
710
0
    if (NS_FAILED(rv)) {
711
0
      return rv;
712
0
    }
713
0
714
0
    MOZ_ASSERT(!mZips.Contains(uri));
715
0
    mZips.Put(uri, zip);
716
0
  }
717
0
  zip.forget(result);
718
0
  return rv;
719
0
}
720
721
NS_IMETHODIMP
722
nsZipReaderCache::GetFd(nsIFile* zipFile, PRFileDesc** aRetVal)
723
0
{
724
#if defined(XP_WIN)
725
  MOZ_CRASH("Not implemented");
726
  return NS_ERROR_NOT_IMPLEMENTED;
727
#else
728
0
  if (!zipFile) {
729
0
    return NS_ERROR_FAILURE;
730
0
  }
731
0
732
0
  nsresult rv;
733
0
  nsAutoCString uri;
734
0
  rv = zipFile->GetPersistentDescriptor(uri);
735
0
  if (NS_FAILED(rv)) {
736
0
    return rv;
737
0
  }
738
0
  uri.InsertLiteral("file:", 0);
739
0
740
0
  MutexAutoLock lock(mLock);
741
0
  RefPtr<nsJAR> zip;
742
0
  mZips.Get(uri, getter_AddRefs(zip));
743
0
  if (!zip) {
744
0
    return NS_ERROR_FAILURE;
745
0
  }
746
0
747
0
  zip->ClearReleaseTime();
748
0
  rv = zip->GetNSPRFileDesc(aRetVal);
749
0
  // Do this to avoid possible deadlock on mLock with ReleaseZip().
750
0
  MutexAutoUnlock unlock(mLock);
751
0
  RefPtr<nsJAR> zipTemp = zip.forget();
752
0
  return rv;
753
0
#endif /* XP_WIN */
754
0
}
755
756
nsresult
757
nsZipReaderCache::ReleaseZip(nsJAR* zip)
758
1
{
759
1
  nsresult rv;
760
1
  MutexAutoLock lock(mLock);
761
1
762
1
  // It is possible that two thread compete for this zip. The dangerous
763
1
  // case is where one thread Releases the zip and discovers that the ref
764
1
  // count has gone to one. Before it can call this ReleaseZip method
765
1
  // another thread calls our GetZip method. The ref count goes to two. That
766
1
  // second thread then Releases the zip and the ref count goes to one. It
767
1
  // then tries to enter this ReleaseZip method and blocks while the first
768
1
  // thread is still here. The first thread continues and remove the zip from
769
1
  // the cache and calls its Release method sending the ref count to 0 and
770
1
  // deleting the zip. However, the second thread is still blocked at the
771
1
  // start of ReleaseZip, but the 'zip' param now hold a reference to a
772
1
  // deleted zip!
773
1
  //
774
1
  // So, we are going to try safeguarding here by searching our hashtable while
775
1
  // locked here for the zip. We return fast if it is not found.
776
1
777
1
  bool found = false;
778
1
  for (auto iter = mZips.Iter(); !iter.Done(); iter.Next()) {
779
1
    if (zip == iter.UserData()) {
780
1
      found = true;
781
1
      break;
782
1
    }
783
1
  }
784
1
785
1
  if (!found) {
786
#ifdef ZIP_CACHE_HIT_RATE
787
    mZipSyncMisses++;
788
#endif
789
    return NS_OK;
790
0
  }
791
1
792
1
  zip->SetReleaseTime();
793
1
794
1
  if (mZips.Count() <= mCacheSize)
795
1
    return NS_OK;
796
0
797
0
  // Find the oldest zip.
798
0
  nsJAR* oldest = nullptr;
799
0
  for (auto iter = mZips.Iter(); !iter.Done(); iter.Next()) {
800
0
    nsJAR* current = iter.UserData();
801
0
    PRIntervalTime currentReleaseTime = current->GetReleaseTime();
802
0
    if (currentReleaseTime != PR_INTERVAL_NO_TIMEOUT) {
803
0
      if (oldest == nullptr ||
804
0
          currentReleaseTime < oldest->GetReleaseTime()) {
805
0
        oldest = current;
806
0
      }
807
0
    }
808
0
  }
809
0
810
0
  // Because of the craziness above it is possible that there is no zip that
811
0
  // needs removing.
812
0
  if (!oldest)
813
0
    return NS_OK;
814
0
815
#ifdef ZIP_CACHE_HIT_RATE
816
    mZipCacheFlushes++;
817
#endif
818
819
0
  // remove from hashtable
820
0
  nsAutoCString uri;
821
0
  rv = oldest->GetJarPath(uri);
822
0
  if (NS_FAILED(rv))
823
0
    return rv;
824
0
825
0
  if (oldest->mOuterZipEntry.IsEmpty()) {
826
0
    uri.InsertLiteral("file:", 0);
827
0
  } else {
828
0
    uri.InsertLiteral("jar:", 0);
829
0
    uri.AppendLiteral("!/");
830
0
    uri.Append(oldest->mOuterZipEntry);
831
0
  }
832
0
833
0
  // Retrieving and removing the JAR must be done without an extra AddRef
834
0
  // and Release, or we'll trigger nsJAR::Release's magic refcount 1 case
835
0
  // an extra time and trigger a deadlock.
836
0
  RefPtr<nsJAR> removed;
837
0
  mZips.Remove(uri, getter_AddRefs(removed));
838
0
  NS_ASSERTION(removed, "botched");
839
0
  NS_ASSERTION(oldest == removed, "removed wrong entry");
840
0
841
0
  if (removed)
842
0
    removed->SetZipReaderCache(nullptr);
843
0
844
0
  return NS_OK;
845
0
}
846
847
NS_IMETHODIMP
848
nsZipReaderCache::Observe(nsISupports *aSubject,
849
                          const char *aTopic,
850
                          const char16_t *aSomeData)
851
0
{
852
0
  if (strcmp(aTopic, "memory-pressure") == 0) {
853
0
    MutexAutoLock lock(mLock);
854
0
    for (auto iter = mZips.Iter(); !iter.Done(); iter.Next()) {
855
0
      RefPtr<nsJAR>& current = iter.Data();
856
0
      if (current->GetReleaseTime() != PR_INTERVAL_NO_TIMEOUT) {
857
0
        current->SetZipReaderCache(nullptr);
858
0
        iter.Remove();
859
0
      }
860
0
    }
861
0
  }
862
0
  else if (strcmp(aTopic, "chrome-flush-caches") == 0) {
863
0
    MutexAutoLock lock(mLock);
864
0
    for (auto iter = mZips.Iter(); !iter.Done(); iter.Next()) {
865
0
      iter.UserData()->SetZipReaderCache(nullptr);
866
0
    }
867
0
    mZips.Clear();
868
0
  }
869
0
  else if (strcmp(aTopic, "flush-cache-entry") == 0) {
870
0
    nsCOMPtr<nsIFile> file;
871
0
    if (aSubject) {
872
0
      file = do_QueryInterface(aSubject);
873
0
    } else if (aSomeData) {
874
0
      nsDependentString fileName(aSomeData);
875
0
      Unused << NS_NewLocalFile(fileName, false, getter_AddRefs(file));
876
0
    }
877
0
878
0
    if (!file)
879
0
      return NS_OK;
880
0
881
0
    nsAutoCString uri;
882
0
    if (NS_FAILED(file->GetPersistentDescriptor(uri)))
883
0
      return NS_OK;
884
0
885
0
    uri.InsertLiteral("file:", 0);
886
0
887
0
    MutexAutoLock lock(mLock);
888
0
889
0
    RefPtr<nsJAR> zip;
890
0
    mZips.Get(uri, getter_AddRefs(zip));
891
0
    if (!zip)
892
0
      return NS_OK;
893
0
894
#ifdef ZIP_CACHE_HIT_RATE
895
    mZipCacheFlushes++;
896
#endif
897
898
0
    zip->SetZipReaderCache(nullptr);
899
0
900
0
    mZips.Remove(uri);
901
0
  }
902
0
  return NS_OK;
903
0
}
904
905
////////////////////////////////////////////////////////////////////////////////