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