Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/netwerk/cache2/CacheFileContextEvictor.cpp
Line
Count
Source (jump to first uncovered line)
1
/* This Source Code Form is subject to the terms of the Mozilla Public
2
 * License, v. 2.0. If a copy of the MPL was not distributed with this
3
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5
#include "CacheLog.h"
6
#include "CacheFileContextEvictor.h"
7
#include "CacheFileIOManager.h"
8
#include "CacheIndex.h"
9
#include "CacheIndexIterator.h"
10
#include "CacheFileUtils.h"
11
#include "nsIFile.h"
12
#include "LoadContextInfo.h"
13
#include "nsThreadUtils.h"
14
#include "nsString.h"
15
#include "nsISimpleEnumerator.h"
16
#include "nsIDirectoryEnumerator.h"
17
#include "mozilla/Base64.h"
18
#include "mozilla/IntegerPrintfMacros.h"
19
#include "mozilla/net/MozURL.h"
20
21
22
namespace mozilla {
23
namespace net {
24
25
0
#define CONTEXT_EVICTION_PREFIX "ce_"
26
const uint32_t kContextEvictionPrefixLength =
27
  sizeof(CONTEXT_EVICTION_PREFIX) - 1;
28
29
bool CacheFileContextEvictor::sDiskAlreadySearched = false;
30
31
CacheFileContextEvictor::CacheFileContextEvictor()
32
  : mEvicting(false)
33
  , mIndexIsUpToDate(false)
34
0
{
35
0
  LOG(("CacheFileContextEvictor::CacheFileContextEvictor() [this=%p]", this));
36
0
}
37
38
CacheFileContextEvictor::~CacheFileContextEvictor()
39
0
{
40
0
  LOG(("CacheFileContextEvictor::~CacheFileContextEvictor() [this=%p]", this));
41
0
}
42
43
nsresult
44
CacheFileContextEvictor::Init(nsIFile *aCacheDirectory)
45
0
{
46
0
  LOG(("CacheFileContextEvictor::Init()"));
47
0
48
0
  nsresult rv;
49
0
50
0
  MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
51
0
52
0
  CacheIndex::IsUpToDate(&mIndexIsUpToDate);
53
0
54
0
  mCacheDirectory = aCacheDirectory;
55
0
56
0
  rv = aCacheDirectory->Clone(getter_AddRefs(mEntriesDir));
57
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
58
0
    return rv;
59
0
  }
60
0
61
0
  rv = mEntriesDir->AppendNative(NS_LITERAL_CSTRING(ENTRIES_DIR));
62
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
63
0
    return rv;
64
0
  }
65
0
66
0
  if (!sDiskAlreadySearched) {
67
0
    LoadEvictInfoFromDisk();
68
0
    if ((mEntries.Length() != 0) && mIndexIsUpToDate) {
69
0
      CreateIterators();
70
0
      StartEvicting();
71
0
    }
72
0
  }
73
0
74
0
  return NS_OK;
75
0
}
76
77
void
78
CacheFileContextEvictor::Shutdown()
79
0
{
80
0
  LOG(("CacheFileContextEvictor::Shutdown()"));
81
0
82
0
  MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
83
0
84
0
  CloseIterators();
85
0
}
86
87
uint32_t
88
CacheFileContextEvictor::ContextsCount()
89
0
{
90
0
  MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
91
0
92
0
  return mEntries.Length();
93
0
}
94
95
nsresult
96
CacheFileContextEvictor::AddContext(nsILoadContextInfo *aLoadContextInfo,
97
                                    bool aPinned,
98
                                    const nsAString& aOrigin)
99
0
{
100
0
  LOG(("CacheFileContextEvictor::AddContext() [this=%p, loadContextInfo=%p, pinned=%d]",
101
0
       this, aLoadContextInfo, aPinned));
102
0
103
0
  nsresult rv;
104
0
105
0
  MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
106
0
107
0
  CacheFileContextEvictorEntry *entry = nullptr;
108
0
  if (aLoadContextInfo) {
109
0
    for (uint32_t i = 0; i < mEntries.Length(); ++i) {
110
0
      if (mEntries[i]->mInfo &&
111
0
          mEntries[i]->mInfo->Equals(aLoadContextInfo) &&
112
0
          mEntries[i]->mPinned == aPinned &&
113
0
          mEntries[i]->mOrigin.Equals(aOrigin)) {
114
0
        entry = mEntries[i];
115
0
        break;
116
0
      }
117
0
    }
118
0
  } else {
119
0
    // Not providing load context info means we want to delete everything,
120
0
    // so let's not bother with any currently running context cleanups
121
0
    // for the same pinning state.
122
0
    for (uint32_t i = mEntries.Length(); i > 0;) {
123
0
      --i;
124
0
      if (mEntries[i]->mInfo && mEntries[i]->mPinned == aPinned) {
125
0
        RemoveEvictInfoFromDisk(mEntries[i]->mInfo, mEntries[i]->mPinned,
126
0
                                mEntries[i]->mOrigin);
127
0
        mEntries.RemoveElementAt(i);
128
0
      }
129
0
    }
130
0
  }
131
0
132
0
  if (!entry) {
133
0
    entry = new CacheFileContextEvictorEntry();
134
0
    entry->mInfo = aLoadContextInfo;
135
0
    entry->mPinned = aPinned;
136
0
    entry->mOrigin = aOrigin;
137
0
    mEntries.AppendElement(entry);
138
0
  }
139
0
140
0
  entry->mTimeStamp = PR_Now() / PR_USEC_PER_MSEC;
141
0
142
0
  PersistEvictionInfoToDisk(aLoadContextInfo, aPinned, aOrigin);
143
0
144
0
  if (mIndexIsUpToDate) {
145
0
    // Already existing context could be added again, in this case the iterator
146
0
    // would be recreated. Close the old iterator explicitely.
147
0
    if (entry->mIterator) {
148
0
      entry->mIterator->Close();
149
0
      entry->mIterator = nullptr;
150
0
    }
151
0
152
0
    rv = CacheIndex::GetIterator(aLoadContextInfo, false,
153
0
                                 getter_AddRefs(entry->mIterator));
154
0
    if (NS_FAILED(rv)) {
155
0
      // This could probably happen during shutdown. Remove the entry from
156
0
      // the array, but leave the info on the disk. No entry can be opened
157
0
      // during shutdown and we'll load the eviction info on next start.
158
0
      LOG(("CacheFileContextEvictor::AddContext() - Cannot get an iterator. "
159
0
           "[rv=0x%08" PRIx32 "]", static_cast<uint32_t>(rv)));
160
0
      mEntries.RemoveElement(entry);
161
0
      return rv;
162
0
    }
163
0
164
0
    StartEvicting();
165
0
  }
166
0
167
0
  return NS_OK;
168
0
}
169
170
nsresult
171
CacheFileContextEvictor::CacheIndexStateChanged()
172
0
{
173
0
  LOG(("CacheFileContextEvictor::CacheIndexStateChanged() [this=%p]", this));
174
0
175
0
  MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
176
0
177
0
  bool isUpToDate = false;
178
0
  CacheIndex::IsUpToDate(&isUpToDate);
179
0
  if (mEntries.Length() == 0) {
180
0
    // Just save the state and exit, since there is nothing to do
181
0
    mIndexIsUpToDate = isUpToDate;
182
0
    return NS_OK;
183
0
  }
184
0
185
0
  if (!isUpToDate && !mIndexIsUpToDate) {
186
0
    // Index is outdated and status has not changed, nothing to do.
187
0
    return NS_OK;
188
0
  }
189
0
190
0
  if (isUpToDate && mIndexIsUpToDate) {
191
0
    // Status has not changed, but make sure the eviction is running.
192
0
    if (mEvicting) {
193
0
      return NS_OK;
194
0
    }
195
0
196
0
    // We're not evicting, but we should be evicting?!
197
0
    LOG(("CacheFileContextEvictor::CacheIndexStateChanged() - Index is up to "
198
0
         "date, we have some context to evict but eviction is not running! "
199
0
         "Starting now."));
200
0
  }
201
0
202
0
  mIndexIsUpToDate = isUpToDate;
203
0
204
0
  if (mIndexIsUpToDate) {
205
0
    CreateIterators();
206
0
    StartEvicting();
207
0
  } else {
208
0
    CloseIterators();
209
0
  }
210
0
211
0
  return NS_OK;
212
0
}
213
214
nsresult
215
CacheFileContextEvictor::WasEvicted(const nsACString &aKey, nsIFile *aFile,
216
                                    bool *aEvictedAsPinned, bool *aEvictedAsNonPinned)
217
0
{
218
0
  LOG(("CacheFileContextEvictor::WasEvicted() [key=%s]",
219
0
       PromiseFlatCString(aKey).get()));
220
0
221
0
  nsresult rv;
222
0
223
0
  *aEvictedAsPinned = false;
224
0
  *aEvictedAsNonPinned = false;
225
0
226
0
  MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
227
0
228
0
  nsCOMPtr<nsILoadContextInfo> info = CacheFileUtils::ParseKey(aKey);
229
0
  MOZ_ASSERT(info);
230
0
  if (!info) {
231
0
    LOG(("CacheFileContextEvictor::WasEvicted() - Cannot parse key!"));
232
0
    return NS_OK;
233
0
  }
234
0
235
0
  for (uint32_t i = 0; i < mEntries.Length(); ++i) {
236
0
    CacheFileContextEvictorEntry *entry = mEntries[i];
237
0
238
0
    if (entry->mInfo && !info->Equals(entry->mInfo)) {
239
0
      continue;
240
0
    }
241
0
242
0
    PRTime lastModifiedTime;
243
0
    rv = aFile->GetLastModifiedTime(&lastModifiedTime);
244
0
    if (NS_FAILED(rv)) {
245
0
      LOG(("CacheFileContextEvictor::WasEvicted() - Cannot get last modified time"
246
0
            ", returning false."));
247
0
      return NS_OK;
248
0
    }
249
0
250
0
    if (lastModifiedTime > entry->mTimeStamp) {
251
0
      // File has been modified since context eviction.
252
0
      continue;
253
0
    }
254
0
255
0
    LOG(("CacheFileContextEvictor::WasEvicted() - evicted [pinning=%d, "
256
0
         "mTimeStamp=%" PRId64 ", lastModifiedTime=%" PRId64 "]",
257
0
         entry->mPinned, entry->mTimeStamp, lastModifiedTime));
258
0
259
0
    if (entry->mPinned) {
260
0
      *aEvictedAsPinned = true;
261
0
    } else {
262
0
      *aEvictedAsNonPinned = true;
263
0
    }
264
0
  }
265
0
266
0
  return NS_OK;
267
0
}
268
269
nsresult
270
CacheFileContextEvictor::PersistEvictionInfoToDisk(
271
  nsILoadContextInfo *aLoadContextInfo, bool aPinned,
272
  const nsAString& aOrigin)
273
0
{
274
0
  LOG(("CacheFileContextEvictor::PersistEvictionInfoToDisk() [this=%p, "
275
0
       "loadContextInfo=%p]", this, aLoadContextInfo));
276
0
277
0
  nsresult rv;
278
0
279
0
  MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
280
0
281
0
  nsCOMPtr<nsIFile> file;
282
0
  rv = GetContextFile(aLoadContextInfo, aPinned, aOrigin, getter_AddRefs(file));
283
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
284
0
    return rv;
285
0
  }
286
0
287
0
  nsCString path = file->HumanReadablePath();
288
0
289
0
  PRFileDesc *fd;
290
0
  rv = file->OpenNSPRFileDesc(PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE, 0600,
291
0
                              &fd);
292
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
293
0
    LOG(("CacheFileContextEvictor::PersistEvictionInfoToDisk() - Creating file "
294
0
         "failed! [path=%s, rv=0x%08" PRIx32 "]", path.get(), static_cast<uint32_t>(rv)));
295
0
    return rv;
296
0
  }
297
0
298
0
  PR_Close(fd);
299
0
300
0
  LOG(("CacheFileContextEvictor::PersistEvictionInfoToDisk() - Successfully "
301
0
       "created file. [path=%s]", path.get()));
302
0
303
0
  return NS_OK;
304
0
}
305
306
nsresult
307
CacheFileContextEvictor::RemoveEvictInfoFromDisk(
308
  nsILoadContextInfo *aLoadContextInfo, bool aPinned, const nsAString& aOrigin)
309
0
{
310
0
  LOG(("CacheFileContextEvictor::RemoveEvictInfoFromDisk() [this=%p, "
311
0
       "loadContextInfo=%p]", this, aLoadContextInfo));
312
0
313
0
  nsresult rv;
314
0
315
0
  MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
316
0
317
0
  nsCOMPtr<nsIFile> file;
318
0
  rv = GetContextFile(aLoadContextInfo, aPinned, aOrigin, getter_AddRefs(file));
319
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
320
0
    return rv;
321
0
  }
322
0
323
0
  nsCString path = file->HumanReadablePath();
324
0
325
0
  rv = file->Remove(false);
326
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
327
0
    LOG(("CacheFileContextEvictor::RemoveEvictionInfoFromDisk() - Removing file"
328
0
         " failed! [path=%s, rv=0x%08" PRIx32 "]", path.get(), static_cast<uint32_t>(rv)));
329
0
    return rv;
330
0
  }
331
0
332
0
  LOG(("CacheFileContextEvictor::RemoveEvictionInfoFromDisk() - Successfully "
333
0
       "removed file. [path=%s]", path.get()));
334
0
335
0
  return NS_OK;
336
0
}
337
338
nsresult
339
CacheFileContextEvictor::LoadEvictInfoFromDisk()
340
0
{
341
0
  LOG(("CacheFileContextEvictor::LoadEvictInfoFromDisk() [this=%p]", this));
342
0
343
0
  nsresult rv;
344
0
345
0
  MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
346
0
347
0
  sDiskAlreadySearched = true;
348
0
349
0
  nsCOMPtr<nsIDirectoryEnumerator> dirEnum;
350
0
  rv = mCacheDirectory->GetDirectoryEntries(getter_AddRefs(dirEnum));
351
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
352
0
    return rv;
353
0
  }
354
0
355
0
  while (true) {
356
0
    nsCOMPtr<nsIFile> file;
357
0
    rv = dirEnum->GetNextFile(getter_AddRefs(file));
358
0
    if (!file) {
359
0
      break;
360
0
    }
361
0
362
0
    bool isDir = false;
363
0
    file->IsDirectory(&isDir);
364
0
    if (isDir) {
365
0
      continue;
366
0
    }
367
0
368
0
    nsAutoCString leaf;
369
0
    rv = file->GetNativeLeafName(leaf);
370
0
    if (NS_FAILED(rv)) {
371
0
      LOG(("CacheFileContextEvictor::LoadEvictInfoFromDisk() - "
372
0
           "GetNativeLeafName() failed! Skipping file."));
373
0
      continue;
374
0
    }
375
0
376
0
    if (leaf.Length() < kContextEvictionPrefixLength) {
377
0
      continue;
378
0
    }
379
0
380
0
    if (!StringBeginsWith(leaf, NS_LITERAL_CSTRING(CONTEXT_EVICTION_PREFIX))) {
381
0
      continue;
382
0
    }
383
0
384
0
    nsAutoCString encoded;
385
0
    encoded = Substring(leaf, kContextEvictionPrefixLength);
386
0
    encoded.ReplaceChar('-', '/');
387
0
388
0
    nsAutoCString decoded;
389
0
    rv = Base64Decode(encoded, decoded);
390
0
    if (NS_FAILED(rv)) {
391
0
      LOG(("CacheFileContextEvictor::LoadEvictInfoFromDisk() - Base64 decoding "
392
0
           "failed. Removing the file. [file=%s]", leaf.get()));
393
0
      file->Remove(false);
394
0
      continue;
395
0
    }
396
0
397
0
    bool pinned = decoded[0] == '\t';
398
0
    if (pinned) {
399
0
      decoded = Substring(decoded, 1);
400
0
    }
401
0
402
0
    // Let's see if we have an origin.
403
0
    nsAutoCString origin;
404
0
    if (decoded.Contains('\t')) {
405
0
      auto split = decoded.Split('\t');
406
0
      MOZ_ASSERT(decoded.CountChar('\t') == 2);
407
0
408
0
      origin = split.Get(0);
409
0
      decoded = split.Get(1);
410
0
    }
411
0
412
0
    nsCOMPtr<nsILoadContextInfo> info;
413
0
    if (!NS_LITERAL_CSTRING("*").Equals(decoded)) {
414
0
      // "*" is indication of 'delete all', info left null will pass
415
0
      // to CacheFileContextEvictor::AddContext and clear all the cache data.
416
0
      info = CacheFileUtils::ParseKey(decoded);
417
0
      if (!info) {
418
0
        LOG(("CacheFileContextEvictor::LoadEvictInfoFromDisk() - Cannot parse "
419
0
             "context key, removing file. [contextKey=%s, file=%s]",
420
0
             decoded.get(), leaf.get()));
421
0
        file->Remove(false);
422
0
        continue;
423
0
      }
424
0
    }
425
0
426
0
427
0
    PRTime lastModifiedTime;
428
0
    rv = file->GetLastModifiedTime(&lastModifiedTime);
429
0
    if (NS_FAILED(rv)) {
430
0
      continue;
431
0
    }
432
0
433
0
    CacheFileContextEvictorEntry *entry = new CacheFileContextEvictorEntry();
434
0
    entry->mInfo = info;
435
0
    entry->mPinned = pinned;
436
0
    CopyUTF8toUTF16(origin, entry->mOrigin);
437
0
    entry->mTimeStamp = lastModifiedTime;
438
0
    mEntries.AppendElement(entry);
439
0
  }
440
0
441
0
  return NS_OK;
442
0
}
443
444
nsresult
445
CacheFileContextEvictor::GetContextFile(nsILoadContextInfo *aLoadContextInfo,
446
                                        bool aPinned,
447
                                        const nsAString& aOrigin,
448
                                        nsIFile **_retval)
449
0
{
450
0
  nsresult rv;
451
0
452
0
  nsAutoCString leafName;
453
0
  leafName.AssignLiteral(CONTEXT_EVICTION_PREFIX);
454
0
455
0
  nsAutoCString keyPrefix;
456
0
  if (aPinned) {
457
0
    // Mark pinned context files with a tab char at the start.
458
0
    // Tab is chosen because it can never be used as a context key tag.
459
0
    keyPrefix.Append('\t');
460
0
  }
461
0
  if (aLoadContextInfo) {
462
0
    CacheFileUtils::AppendKeyPrefix(aLoadContextInfo, keyPrefix);
463
0
  } else {
464
0
    keyPrefix.Append('*');
465
0
  }
466
0
  if (!aOrigin.IsEmpty()) {
467
0
    keyPrefix.Append('\t');
468
0
    keyPrefix.Append(NS_ConvertUTF16toUTF8(aOrigin));
469
0
  }
470
0
471
0
  nsAutoCString data64;
472
0
  rv = Base64Encode(keyPrefix, data64);
473
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
474
0
    return rv;
475
0
  }
476
0
477
0
  // Replace '/' with '-' since '/' cannot be part of the filename.
478
0
  data64.ReplaceChar('/', '-');
479
0
480
0
  leafName.Append(data64);
481
0
482
0
  nsCOMPtr<nsIFile> file;
483
0
  rv = mCacheDirectory->Clone(getter_AddRefs(file));
484
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
485
0
    return rv;
486
0
  }
487
0
488
0
  rv = file->AppendNative(leafName);
489
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
490
0
    return rv;
491
0
  }
492
0
493
0
  file.swap(*_retval);
494
0
  return NS_OK;
495
0
}
496
497
void
498
CacheFileContextEvictor::CreateIterators()
499
0
{
500
0
  LOG(("CacheFileContextEvictor::CreateIterators() [this=%p]", this));
501
0
502
0
  CloseIterators();
503
0
504
0
  nsresult rv;
505
0
506
0
  for (uint32_t i = 0; i < mEntries.Length(); ) {
507
0
    rv = CacheIndex::GetIterator(mEntries[i]->mInfo, false,
508
0
                                 getter_AddRefs(mEntries[i]->mIterator));
509
0
    if (NS_FAILED(rv)) {
510
0
      LOG(("CacheFileContextEvictor::CreateIterators() - Cannot get an iterator"
511
0
           ". [rv=0x%08" PRIx32 "]", static_cast<uint32_t>(rv)));
512
0
      mEntries.RemoveElementAt(i);
513
0
      continue;
514
0
    }
515
0
516
0
    ++i;
517
0
  }
518
0
}
519
520
void
521
CacheFileContextEvictor::CloseIterators()
522
0
{
523
0
  LOG(("CacheFileContextEvictor::CloseIterators() [this=%p]", this));
524
0
525
0
  for (uint32_t i = 0; i < mEntries.Length(); ++i) {
526
0
    if (mEntries[i]->mIterator) {
527
0
      mEntries[i]->mIterator->Close();
528
0
      mEntries[i]->mIterator = nullptr;
529
0
    }
530
0
  }
531
0
}
532
533
void
534
CacheFileContextEvictor::StartEvicting()
535
0
{
536
0
  LOG(("CacheFileContextEvictor::StartEvicting() [this=%p]", this));
537
0
538
0
  MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
539
0
540
0
  if (mEvicting) {
541
0
    LOG(("CacheFileContextEvictor::StartEvicting() - already evicting."));
542
0
    return;
543
0
  }
544
0
545
0
  if (mEntries.Length() == 0) {
546
0
    LOG(("CacheFileContextEvictor::StartEvicting() - no context to evict."));
547
0
    return;
548
0
  }
549
0
550
0
  nsCOMPtr<nsIRunnable> ev;
551
0
  ev = NewRunnableMethod("net::CacheFileContextEvictor::EvictEntries",
552
0
                         this,
553
0
                         &CacheFileContextEvictor::EvictEntries);
554
0
555
0
  RefPtr<CacheIOThread> ioThread = CacheFileIOManager::IOThread();
556
0
557
0
  nsresult rv = ioThread->Dispatch(ev, CacheIOThread::EVICT);
558
0
  if (NS_FAILED(rv)) {
559
0
    LOG(("CacheFileContextEvictor::StartEvicting() - Cannot dispatch event to "
560
0
         "IO thread. [rv=0x%08" PRIx32 "]", static_cast<uint32_t>(rv)));
561
0
  }
562
0
563
0
  mEvicting = true;
564
0
}
565
566
nsresult
567
CacheFileContextEvictor::EvictEntries()
568
0
{
569
0
  LOG(("CacheFileContextEvictor::EvictEntries()"));
570
0
571
0
  nsresult rv;
572
0
573
0
  MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
574
0
575
0
  mEvicting = false;
576
0
577
0
  if (!mIndexIsUpToDate) {
578
0
    LOG(("CacheFileContextEvictor::EvictEntries() - Stopping evicting due to "
579
0
         "outdated index."));
580
0
    return NS_OK;
581
0
  }
582
0
583
0
  while (true) {
584
0
    if (CacheObserver::ShuttingDown()) {
585
0
      LOG(("CacheFileContextEvictor::EvictEntries() - Stopping evicting due to "
586
0
           "shutdown."));
587
0
      mEvicting = true; // We don't want to start eviction again during shutdown
588
0
                        // process. Setting this flag to true ensures it.
589
0
      return NS_OK;
590
0
    }
591
0
592
0
    if (CacheIOThread::YieldAndRerun()) {
593
0
      LOG(("CacheFileContextEvictor::EvictEntries() - Breaking loop for higher "
594
0
           "level events."));
595
0
      mEvicting = true;
596
0
      return NS_OK;
597
0
    }
598
0
599
0
    if (mEntries.Length() == 0) {
600
0
      LOG(("CacheFileContextEvictor::EvictEntries() - Stopping evicting, there "
601
0
           "is no context to evict."));
602
0
603
0
      // Allow index to notify AsyncGetDiskConsumption callbacks.  The size is
604
0
      // actual again.
605
0
      CacheIndex::OnAsyncEviction(false);
606
0
      return NS_OK;
607
0
    }
608
0
609
0
    SHA1Sum::Hash hash;
610
0
    rv = mEntries[0]->mIterator->GetNextHash(&hash);
611
0
    if (rv == NS_ERROR_NOT_AVAILABLE) {
612
0
      LOG(("CacheFileContextEvictor::EvictEntries() - No more entries left in "
613
0
           "iterator. [iterator=%p, info=%p]", mEntries[0]->mIterator.get(),
614
0
           mEntries[0]->mInfo.get()));
615
0
      RemoveEvictInfoFromDisk(mEntries[0]->mInfo, mEntries[0]->mPinned,
616
0
                              mEntries[0]->mOrigin);
617
0
      mEntries.RemoveElementAt(0);
618
0
      continue;
619
0
    } else if (NS_FAILED(rv)) {
620
0
      LOG(("CacheFileContextEvictor::EvictEntries() - Iterator failed to "
621
0
           "provide next hash (shutdown?), keeping eviction info on disk."
622
0
           " [iterator=%p, info=%p]", mEntries[0]->mIterator.get(),
623
0
           mEntries[0]->mInfo.get()));
624
0
      mEntries.RemoveElementAt(0);
625
0
      continue;
626
0
    }
627
0
628
0
    LOG(("CacheFileContextEvictor::EvictEntries() - Processing hash. "
629
0
         "[hash=%08x%08x%08x%08x%08x, iterator=%p, info=%p]", LOGSHA1(&hash),
630
0
         mEntries[0]->mIterator.get(), mEntries[0]->mInfo.get()));
631
0
632
0
    RefPtr<CacheFileHandle> handle;
633
0
    CacheFileIOManager::gInstance->mHandles.GetHandle(&hash,
634
0
                                                      getter_AddRefs(handle));
635
0
    if (handle) {
636
0
      // We doom any active handle in CacheFileIOManager::EvictByContext(), so
637
0
      // this must be a new one. Skip it.
638
0
      LOG(("CacheFileContextEvictor::EvictEntries() - Skipping entry since we "
639
0
           "found an active handle. [handle=%p]", handle.get()));
640
0
      continue;
641
0
    }
642
0
643
0
    CacheIndex::EntryStatus status;
644
0
    bool pinned = false;
645
0
    auto callback = [&pinned](const CacheIndexEntry * aEntry) {
646
0
      pinned = aEntry->IsPinned();
647
0
    };
648
0
    rv = CacheIndex::HasEntry(hash, &status, callback);
649
0
    // This must never fail, since eviction (this code) happens only when the index
650
0
    // is up-to-date and thus the informatin is known.
651
0
    MOZ_ASSERT(NS_SUCCEEDED(rv));
652
0
653
0
    if (pinned != mEntries[0]->mPinned) {
654
0
      LOG(("CacheFileContextEvictor::EvictEntries() - Skipping entry since pinning "
655
0
           "doesn't match [evicting pinned=%d, entry pinned=%d]",
656
0
           mEntries[0]->mPinned, pinned));
657
0
      continue;
658
0
    }
659
0
660
0
    if (!mEntries[0]->mOrigin.IsEmpty()) {
661
0
      nsCOMPtr<nsIFile> file;
662
0
      CacheFileIOManager::gInstance->GetFile(&hash, getter_AddRefs(file));
663
0
664
0
      // Read metadata from the file synchronously
665
0
      RefPtr<CacheFileMetadata> metadata = new CacheFileMetadata();
666
0
      rv = metadata->SyncReadMetadata(file);
667
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
668
0
        continue;
669
0
      }
670
0
671
0
      // Now get the context + enhance id + URL from the key.
672
0
      nsAutoCString key;
673
0
      metadata->GetKey(key);
674
0
675
0
      nsAutoCString uriSpec;
676
0
677
0
      RefPtr<nsILoadContextInfo> info =
678
0
        CacheFileUtils::ParseKey(key, nullptr, &uriSpec);
679
0
      MOZ_ASSERT(info);
680
0
      if (!info) {
681
0
        continue;
682
0
      }
683
0
684
0
      RefPtr<MozURL> url;
685
0
      rv = MozURL::Init(getter_AddRefs(url), uriSpec);
686
0
      if (NS_FAILED(rv)) {
687
0
        LOG(("CacheFileContextEvictor::EvictEntries() - Skipping entry since MozURL "
688
0
             "fails in the parsing of the uriSpec"));
689
0
        continue;
690
0
      }
691
0
692
0
      nsAutoCString urlOrigin;
693
0
      url->Origin(urlOrigin);
694
0
      if (urlOrigin.Equals(NS_ConvertUTF16toUTF8(mEntries[0]->mOrigin))) {
695
0
        LOG(("CacheFileContextEvictor::EvictEntries() - Skipping entry since origin "
696
0
             "doesn't match"));
697
0
        continue;
698
0
      }
699
0
    }
700
0
701
0
    nsAutoCString leafName;
702
0
    CacheFileIOManager::HashToStr(&hash, leafName);
703
0
704
0
    PRTime lastModifiedTime;
705
0
    nsCOMPtr<nsIFile> file;
706
0
    rv = mEntriesDir->Clone(getter_AddRefs(file));
707
0
    if (NS_SUCCEEDED(rv)) {
708
0
      rv = file->AppendNative(leafName);
709
0
    }
710
0
    if (NS_SUCCEEDED(rv)) {
711
0
      rv = file->GetLastModifiedTime(&lastModifiedTime);
712
0
    }
713
0
    if (NS_FAILED(rv)) {
714
0
      LOG(("CacheFileContextEvictor::EvictEntries() - Cannot get last modified "
715
0
           "time, skipping entry."));
716
0
      continue;
717
0
    }
718
0
719
0
    if (lastModifiedTime > mEntries[0]->mTimeStamp) {
720
0
      LOG(("CacheFileContextEvictor::EvictEntries() - Skipping newer entry. "
721
0
           "[mTimeStamp=%" PRId64 ", lastModifiedTime=%" PRId64 "]", mEntries[0]->mTimeStamp,
722
0
           lastModifiedTime));
723
0
      continue;
724
0
    }
725
0
726
0
    LOG(("CacheFileContextEvictor::EvictEntries - Removing entry."));
727
0
    file->Remove(false);
728
0
    CacheIndex::RemoveEntry(&hash);
729
0
  }
730
0
731
0
  MOZ_ASSERT_UNREACHABLE("We should never get here");
732
0
  return NS_OK;
733
0
}
734
735
} // namespace net
736
} // namespace mozilla