Line data Source code
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 "CacheFileIOManager.h"
7 :
8 : #include "../cache/nsCacheUtils.h"
9 : #include "CacheHashUtils.h"
10 : #include "CacheStorageService.h"
11 : #include "CacheIndex.h"
12 : #include "CacheFileUtils.h"
13 : #include "nsThreadUtils.h"
14 : #include "CacheFile.h"
15 : #include "CacheObserver.h"
16 : #include "nsIFile.h"
17 : #include "CacheFileContextEvictor.h"
18 : #include "nsITimer.h"
19 : #include "nsISimpleEnumerator.h"
20 : #include "nsIDirectoryEnumerator.h"
21 : #include "nsIObserverService.h"
22 : #include "nsICacheStorageVisitor.h"
23 : #include "nsISizeOf.h"
24 : #include "mozilla/Telemetry.h"
25 : #include "mozilla/DebugOnly.h"
26 : #include "mozilla/Services.h"
27 : #include "nsDirectoryServiceUtils.h"
28 : #include "nsAppDirectoryServiceDefs.h"
29 : #include "private/pprio.h"
30 : #include "mozilla/IntegerPrintfMacros.h"
31 : #include "mozilla/Preferences.h"
32 : #include "nsNetUtil.h"
33 :
34 : // include files for ftruncate (or equivalent)
35 : #if defined(XP_UNIX)
36 : #include <unistd.h>
37 : #elif defined(XP_WIN)
38 : #include <windows.h>
39 : #undef CreateFile
40 : #undef CREATE_NEW
41 : #else
42 : // XXX add necessary include file for ftruncate (or equivalent)
43 : #endif
44 :
45 :
46 : namespace mozilla {
47 : namespace net {
48 :
49 : #define kOpenHandlesLimit 128
50 : #define kMetadataWriteDelay 5000
51 : #define kRemoveTrashStartDelay 60000 // in milliseconds
52 : #define kSmartSizeUpdateInterval 60000 // in milliseconds
53 :
54 : #ifdef ANDROID
55 : const uint32_t kMaxCacheSizeKB = 200*1024; // 200 MB
56 : #else
57 : const uint32_t kMaxCacheSizeKB = 350*1024; // 350 MB
58 : #endif
59 :
60 : bool
61 39 : CacheFileHandle::DispatchRelease()
62 : {
63 39 : if (CacheFileIOManager::IsOnIOThreadOrCeased()) {
64 39 : return false;
65 : }
66 :
67 0 : nsCOMPtr<nsIEventTarget> ioTarget = CacheFileIOManager::IOTarget();
68 0 : if (!ioTarget) {
69 0 : return false;
70 : }
71 :
72 0 : nsresult rv = ioTarget->Dispatch(
73 0 : NewNonOwningRunnableMethod(
74 : "net::CacheFileHandle::Release", this, &CacheFileHandle::Release),
75 0 : nsIEventTarget::DISPATCH_NORMAL);
76 0 : if (NS_FAILED(rv)) {
77 0 : return false;
78 : }
79 :
80 0 : return true;
81 : }
82 :
83 49 : NS_IMPL_ADDREF(CacheFileHandle)
84 : NS_IMETHODIMP_(MozExternalRefCountType)
85 39 : CacheFileHandle::Release()
86 : {
87 39 : nsrefcnt count = mRefCnt - 1;
88 39 : if (DispatchRelease()) {
89 : // Redispatched to the IO thread.
90 0 : return count;
91 : }
92 :
93 39 : MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
94 :
95 39 : LOG(("CacheFileHandle::Release() [this=%p, refcnt=%" PRIuPTR "]", this, mRefCnt.get()));
96 39 : NS_PRECONDITION(0 != mRefCnt, "dup release");
97 39 : count = --mRefCnt;
98 39 : NS_LOG_RELEASE(this, count, "CacheFileHandle");
99 :
100 39 : if (0 == count) {
101 0 : mRefCnt = 1;
102 0 : delete (this);
103 0 : return 0;
104 : }
105 :
106 39 : return count;
107 : }
108 :
109 0 : NS_INTERFACE_MAP_BEGIN(CacheFileHandle)
110 0 : NS_INTERFACE_MAP_ENTRY(nsISupports)
111 0 : NS_INTERFACE_MAP_END_THREADSAFE
112 :
113 5 : CacheFileHandle::CacheFileHandle(const SHA1Sum::Hash *aHash, bool aPriority, PinningStatus aPinning)
114 : : mHash(aHash)
115 : , mIsDoomed(false)
116 : , mClosed(false)
117 : , mPriority(aPriority)
118 : , mSpecialFile(false)
119 : , mInvalid(false)
120 : , mFileExists(false)
121 : , mDoomWhenFoundPinned(false)
122 : , mDoomWhenFoundNonPinned(false)
123 : , mKilled(false)
124 : , mPinning(aPinning)
125 : , mFileSize(-1)
126 5 : , mFD(nullptr)
127 : {
128 : // If we initialize mDoomed in the initialization list, that initialization is
129 : // not guaranteeded to be atomic. Whereas this assignment here is guaranteed
130 : // to be atomic. TSan will see this (atomic) assignment and be satisfied
131 : // that cross-thread accesses to mIsDoomed are properly synchronized.
132 5 : mIsDoomed = false;
133 5 : LOG(("CacheFileHandle::CacheFileHandle() [this=%p, hash=%08x%08x%08x%08x%08x]"
134 : , this, LOGSHA1(aHash)));
135 5 : }
136 :
137 0 : CacheFileHandle::CacheFileHandle(const nsACString &aKey, bool aPriority, PinningStatus aPinning)
138 : : mHash(nullptr)
139 : , mIsDoomed(false)
140 : , mClosed(false)
141 : , mPriority(aPriority)
142 : , mSpecialFile(true)
143 : , mInvalid(false)
144 : , mFileExists(false)
145 : , mDoomWhenFoundPinned(false)
146 : , mDoomWhenFoundNonPinned(false)
147 : , mKilled(false)
148 : , mPinning(aPinning)
149 : , mFileSize(-1)
150 : , mFD(nullptr)
151 0 : , mKey(aKey)
152 : {
153 : // See comment above about the initialization of mIsDoomed.
154 0 : mIsDoomed = false;
155 0 : LOG(("CacheFileHandle::CacheFileHandle() [this=%p, key=%s]", this,
156 : PromiseFlatCString(aKey).get()));
157 0 : }
158 :
159 0 : CacheFileHandle::~CacheFileHandle()
160 : {
161 0 : LOG(("CacheFileHandle::~CacheFileHandle() [this=%p]", this));
162 :
163 0 : MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
164 :
165 0 : RefPtr<CacheFileIOManager> ioMan = CacheFileIOManager::gInstance;
166 0 : if (!IsClosed() && ioMan) {
167 0 : ioMan->CloseHandleInternal(this);
168 : }
169 0 : }
170 :
171 : void
172 0 : CacheFileHandle::Log()
173 : {
174 0 : nsAutoCString leafName;
175 0 : if (mFile) {
176 0 : mFile->GetNativeLeafName(leafName);
177 : }
178 :
179 0 : if (mSpecialFile) {
180 0 : LOG(("CacheFileHandle::Log() - special file [this=%p, "
181 : "isDoomed=%d, priority=%d, closed=%d, invalid=%d, "
182 : "pinning=%" PRIu32 ", fileExists=%d, fileSize=%" PRId64 ", leafName=%s, key=%s]",
183 : this,
184 : bool(mIsDoomed), bool(mPriority), bool(mClosed), bool(mInvalid),
185 : static_cast<uint32_t>(mPinning), bool(mFileExists), mFileSize, leafName.get(),
186 : mKey.get()));
187 : } else {
188 0 : LOG(("CacheFileHandle::Log() - entry file [this=%p, hash=%08x%08x%08x%08x%08x, "
189 : "isDoomed=%d, priority=%d, closed=%d, invalid=%d, "
190 : "pinning=%" PRIu32 ", fileExists=%d, fileSize=%" PRId64 ", leafName=%s, key=%s]",
191 : this, LOGSHA1(mHash),
192 : bool(mIsDoomed), bool(mPriority), bool(mClosed), bool(mInvalid),
193 : static_cast<uint32_t>(mPinning), bool(mFileExists), mFileSize, leafName.get(),
194 : mKey.get()));
195 : }
196 0 : }
197 :
198 : uint32_t
199 19 : CacheFileHandle::FileSizeInK() const
200 : {
201 19 : MOZ_ASSERT(mFileSize != -1);
202 19 : uint64_t size64 = mFileSize;
203 :
204 19 : size64 += 0x3FF;
205 19 : size64 >>= 10;
206 :
207 : uint32_t size;
208 19 : if (size64 >> 32) {
209 : NS_WARNING("CacheFileHandle::FileSizeInK() - FileSize is too large, "
210 0 : "truncating to PR_UINT32_MAX");
211 0 : size = PR_UINT32_MAX;
212 : } else {
213 19 : size = static_cast<uint32_t>(size64);
214 : }
215 :
216 19 : return size;
217 : }
218 :
219 : bool
220 5 : CacheFileHandle::SetPinned(bool aPinned)
221 : {
222 5 : LOG(("CacheFileHandle::SetPinned [this=%p, pinned=%d]", this, aPinned));
223 :
224 5 : MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
225 :
226 5 : mPinning = aPinned
227 5 : ? PinningStatus::PINNED
228 : : PinningStatus::NON_PINNED;
229 :
230 10 : if ((MOZ_UNLIKELY(mDoomWhenFoundPinned) && aPinned) ||
231 5 : (MOZ_UNLIKELY(mDoomWhenFoundNonPinned) && !aPinned)) {
232 :
233 0 : LOG((" dooming, when: pinned=%d, non-pinned=%d, found: pinned=%d",
234 : bool(mDoomWhenFoundPinned), bool(mDoomWhenFoundNonPinned), aPinned));
235 :
236 0 : mDoomWhenFoundPinned = false;
237 0 : mDoomWhenFoundNonPinned = false;
238 :
239 0 : return false;
240 : }
241 :
242 5 : return true;
243 : }
244 :
245 : // Memory reporting
246 :
247 : size_t
248 0 : CacheFileHandle::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
249 : {
250 0 : size_t n = 0;
251 0 : nsCOMPtr<nsISizeOf> sizeOf;
252 :
253 0 : sizeOf = do_QueryInterface(mFile);
254 0 : if (sizeOf) {
255 0 : n += sizeOf->SizeOfIncludingThis(mallocSizeOf);
256 : }
257 :
258 0 : n += mallocSizeOf(mFD);
259 0 : n += mKey.SizeOfExcludingThisIfUnshared(mallocSizeOf);
260 0 : return n;
261 : }
262 :
263 : size_t
264 0 : CacheFileHandle::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
265 : {
266 0 : return mallocSizeOf(this) + SizeOfExcludingThis(mallocSizeOf);
267 : }
268 :
269 : /******************************************************************************
270 : * CacheFileHandles::HandleHashKey
271 : *****************************************************************************/
272 :
273 : void
274 5 : CacheFileHandles::HandleHashKey::AddHandle(CacheFileHandle* aHandle)
275 : {
276 5 : MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
277 :
278 5 : mHandles.InsertElementAt(0, aHandle);
279 5 : }
280 :
281 : void
282 0 : CacheFileHandles::HandleHashKey::RemoveHandle(CacheFileHandle* aHandle)
283 : {
284 0 : MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
285 :
286 0 : DebugOnly<bool> found;
287 0 : found = mHandles.RemoveElement(aHandle);
288 0 : MOZ_ASSERT(found);
289 0 : }
290 :
291 : already_AddRefed<CacheFileHandle>
292 0 : CacheFileHandles::HandleHashKey::GetNewestHandle()
293 : {
294 0 : MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
295 :
296 0 : RefPtr<CacheFileHandle> handle;
297 0 : if (mHandles.Length()) {
298 0 : handle = mHandles[0];
299 : }
300 :
301 0 : return handle.forget();
302 : }
303 :
304 : void
305 0 : CacheFileHandles::HandleHashKey::GetHandles(nsTArray<RefPtr<CacheFileHandle> > &aResult)
306 : {
307 0 : MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
308 :
309 0 : for (uint32_t i = 0; i < mHandles.Length(); ++i) {
310 0 : CacheFileHandle* handle = mHandles[i];
311 0 : aResult.AppendElement(handle);
312 : }
313 0 : }
314 :
315 : #ifdef DEBUG
316 :
317 : void
318 5 : CacheFileHandles::HandleHashKey::AssertHandlesState()
319 : {
320 5 : for (uint32_t i = 0; i < mHandles.Length(); ++i) {
321 0 : CacheFileHandle* handle = mHandles[i];
322 0 : MOZ_ASSERT(handle->IsDoomed());
323 : }
324 5 : }
325 :
326 : #endif
327 :
328 : size_t
329 0 : CacheFileHandles::HandleHashKey::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
330 : {
331 0 : MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
332 :
333 0 : size_t n = 0;
334 0 : n += mallocSizeOf(mHash.get());
335 0 : for (uint32_t i = 0; i < mHandles.Length(); ++i) {
336 0 : n += mHandles[i]->SizeOfIncludingThis(mallocSizeOf);
337 : }
338 :
339 0 : return n;
340 : }
341 :
342 : /******************************************************************************
343 : * CacheFileHandles
344 : *****************************************************************************/
345 :
346 1 : CacheFileHandles::CacheFileHandles()
347 : {
348 1 : LOG(("CacheFileHandles::CacheFileHandles() [this=%p]", this));
349 1 : MOZ_COUNT_CTOR(CacheFileHandles);
350 1 : }
351 :
352 0 : CacheFileHandles::~CacheFileHandles()
353 : {
354 0 : LOG(("CacheFileHandles::~CacheFileHandles() [this=%p]", this));
355 0 : MOZ_COUNT_DTOR(CacheFileHandles);
356 0 : }
357 :
358 : nsresult
359 6 : CacheFileHandles::GetHandle(const SHA1Sum::Hash *aHash,
360 : CacheFileHandle **_retval)
361 : {
362 6 : MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
363 6 : MOZ_ASSERT(aHash);
364 :
365 : #ifdef DEBUG_HANDLES
366 : LOG(("CacheFileHandles::GetHandle() [hash=%08x%08x%08x%08x%08x]",
367 : LOGSHA1(aHash)));
368 : #endif
369 :
370 : // find hash entry for key
371 6 : HandleHashKey *entry = mTable.GetEntry(*aHash);
372 6 : if (!entry) {
373 6 : LOG(("CacheFileHandles::GetHandle() hash=%08x%08x%08x%08x%08x "
374 : "no handle entries found", LOGSHA1(aHash)));
375 6 : return NS_ERROR_NOT_AVAILABLE;
376 : }
377 :
378 : #ifdef DEBUG_HANDLES
379 : Log(entry);
380 : #endif
381 :
382 : // Check if the entry is doomed
383 0 : RefPtr<CacheFileHandle> handle = entry->GetNewestHandle();
384 0 : if (!handle) {
385 0 : LOG(("CacheFileHandles::GetHandle() hash=%08x%08x%08x%08x%08x "
386 : "no handle found %p, entry %p", LOGSHA1(aHash), handle.get(), entry));
387 0 : return NS_ERROR_NOT_AVAILABLE;
388 : }
389 :
390 0 : if (handle->IsDoomed()) {
391 0 : LOG(("CacheFileHandles::GetHandle() hash=%08x%08x%08x%08x%08x "
392 : "found doomed handle %p, entry %p", LOGSHA1(aHash), handle.get(), entry));
393 0 : return NS_ERROR_NOT_AVAILABLE;
394 : }
395 :
396 0 : LOG(("CacheFileHandles::GetHandle() hash=%08x%08x%08x%08x%08x "
397 : "found handle %p, entry %p", LOGSHA1(aHash), handle.get(), entry));
398 :
399 0 : handle.forget(_retval);
400 0 : return NS_OK;
401 : }
402 :
403 :
404 : nsresult
405 5 : CacheFileHandles::NewHandle(const SHA1Sum::Hash *aHash,
406 : bool aPriority, CacheFileHandle::PinningStatus aPinning,
407 : CacheFileHandle **_retval)
408 : {
409 5 : MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
410 5 : MOZ_ASSERT(aHash);
411 :
412 : #ifdef DEBUG_HANDLES
413 : LOG(("CacheFileHandles::NewHandle() [hash=%08x%08x%08x%08x%08x]", LOGSHA1(aHash)));
414 : #endif
415 :
416 : // find hash entry for key
417 5 : HandleHashKey *entry = mTable.PutEntry(*aHash);
418 :
419 : #ifdef DEBUG_HANDLES
420 : Log(entry);
421 : #endif
422 :
423 : #ifdef DEBUG
424 5 : entry->AssertHandlesState();
425 : #endif
426 :
427 15 : RefPtr<CacheFileHandle> handle = new CacheFileHandle(entry->Hash(), aPriority, aPinning);
428 5 : entry->AddHandle(handle);
429 :
430 5 : LOG(("CacheFileHandles::NewHandle() hash=%08x%08x%08x%08x%08x "
431 : "created new handle %p, entry=%p", LOGSHA1(aHash), handle.get(), entry));
432 :
433 5 : handle.forget(_retval);
434 10 : return NS_OK;
435 : }
436 :
437 : void
438 0 : CacheFileHandles::RemoveHandle(CacheFileHandle *aHandle)
439 : {
440 0 : MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
441 0 : MOZ_ASSERT(aHandle);
442 :
443 0 : if (!aHandle) {
444 0 : return;
445 : }
446 :
447 : #ifdef DEBUG_HANDLES
448 : LOG(("CacheFileHandles::RemoveHandle() [handle=%p, hash=%08x%08x%08x%08x%08x]"
449 : , aHandle, LOGSHA1(aHandle->Hash())));
450 : #endif
451 :
452 : // find hash entry for key
453 0 : HandleHashKey *entry = mTable.GetEntry(*aHandle->Hash());
454 0 : if (!entry) {
455 0 : MOZ_ASSERT(CacheFileIOManager::IsShutdown(),
456 : "Should find entry when removing a handle before shutdown");
457 :
458 0 : LOG(("CacheFileHandles::RemoveHandle() hash=%08x%08x%08x%08x%08x "
459 : "no entries found", LOGSHA1(aHandle->Hash())));
460 0 : return;
461 : }
462 :
463 : #ifdef DEBUG_HANDLES
464 : Log(entry);
465 : #endif
466 :
467 0 : LOG(("CacheFileHandles::RemoveHandle() hash=%08x%08x%08x%08x%08x "
468 : "removing handle %p", LOGSHA1(entry->Hash()), aHandle));
469 0 : entry->RemoveHandle(aHandle);
470 :
471 0 : if (entry->IsEmpty()) {
472 0 : LOG(("CacheFileHandles::RemoveHandle() hash=%08x%08x%08x%08x%08x "
473 : "list is empty, removing entry %p", LOGSHA1(entry->Hash()), entry));
474 0 : mTable.RemoveEntry(entry);
475 : }
476 : }
477 :
478 : void
479 0 : CacheFileHandles::GetAllHandles(nsTArray<RefPtr<CacheFileHandle> > *_retval)
480 : {
481 0 : MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
482 0 : for (auto iter = mTable.Iter(); !iter.Done(); iter.Next()) {
483 0 : iter.Get()->GetHandles(*_retval);
484 : }
485 0 : }
486 :
487 : void
488 0 : CacheFileHandles::GetActiveHandles(
489 : nsTArray<RefPtr<CacheFileHandle> > *_retval)
490 : {
491 0 : MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
492 0 : for (auto iter = mTable.Iter(); !iter.Done(); iter.Next()) {
493 0 : RefPtr<CacheFileHandle> handle = iter.Get()->GetNewestHandle();
494 0 : MOZ_ASSERT(handle);
495 :
496 0 : if (!handle->IsDoomed()) {
497 0 : _retval->AppendElement(handle);
498 : }
499 : }
500 0 : }
501 :
502 : void
503 0 : CacheFileHandles::ClearAll()
504 : {
505 0 : MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
506 0 : mTable.Clear();
507 0 : }
508 :
509 : uint32_t
510 0 : CacheFileHandles::HandleCount()
511 : {
512 0 : return mTable.Count();
513 : }
514 :
515 : #ifdef DEBUG_HANDLES
516 : void
517 : CacheFileHandles::Log(CacheFileHandlesEntry *entry)
518 : {
519 : LOG(("CacheFileHandles::Log() BEGIN [entry=%p]", entry));
520 :
521 : nsTArray<RefPtr<CacheFileHandle> > array;
522 : aEntry->GetHandles(array);
523 :
524 : for (uint32_t i = 0; i < array.Length(); ++i) {
525 : CacheFileHandle *handle = array[i];
526 : handle->Log();
527 : }
528 :
529 : LOG(("CacheFileHandles::Log() END [entry=%p]", entry));
530 : }
531 : #endif
532 :
533 : // Memory reporting
534 :
535 : size_t
536 0 : CacheFileHandles::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
537 : {
538 0 : MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
539 :
540 0 : return mTable.SizeOfExcludingThis(mallocSizeOf);
541 : }
542 :
543 : // Events
544 :
545 : class ShutdownEvent : public Runnable {
546 : public:
547 0 : ShutdownEvent()
548 0 : : Runnable("net::ShutdownEvent")
549 : , mMonitor("ShutdownEvent.mMonitor")
550 0 : , mNotified(false)
551 : {
552 0 : }
553 :
554 : protected:
555 0 : ~ShutdownEvent()
556 0 : {
557 0 : }
558 :
559 : public:
560 0 : NS_IMETHOD Run() override
561 : {
562 0 : MonitorAutoLock mon(mMonitor);
563 :
564 0 : CacheFileIOManager::gInstance->ShutdownInternal();
565 :
566 0 : mNotified = true;
567 0 : mon.Notify();
568 :
569 0 : return NS_OK;
570 : }
571 :
572 0 : void PostAndWait()
573 : {
574 0 : MonitorAutoLock mon(mMonitor);
575 :
576 0 : DebugOnly<nsresult> rv;
577 0 : rv = CacheFileIOManager::gInstance->mIOThread->Dispatch(
578 0 : this, CacheIOThread::WRITE); // When writes and closing of handles is done
579 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
580 :
581 0 : PRIntervalTime const waitTime = PR_MillisecondsToInterval(1000);
582 0 : while (!mNotified) {
583 0 : mon.Wait(waitTime);
584 0 : if (!mNotified) {
585 : // If there is any IO blocking on the IO thread, this will
586 : // try to cancel it. Returns no later than after two seconds.
587 0 : MonitorAutoUnlock unmon(mMonitor); // Prevent delays
588 0 : CacheFileIOManager::gInstance->mIOThread->CancelBlockingIO();
589 : }
590 : }
591 0 : }
592 :
593 : protected:
594 : mozilla::Monitor mMonitor;
595 : bool mNotified;
596 : };
597 :
598 : // Class responsible for reporting IO performance stats
599 : class IOPerfReportEvent {
600 : public:
601 19 : explicit IOPerfReportEvent(CacheFileUtils::CachePerfStats::EDataType aType)
602 19 : : mType(aType)
603 19 : , mEventCounter(0)
604 : {
605 19 : }
606 :
607 16 : void Start(CacheIOThread *aIOThread)
608 : {
609 16 : mStartTime = TimeStamp::Now();
610 16 : mEventCounter = aIOThread->EventCounter();
611 16 : }
612 :
613 16 : void Report(CacheIOThread *aIOThread)
614 : {
615 16 : if (mStartTime.IsNull()) {
616 0 : return;
617 : }
618 :
619 : // Single IO operations can take less than 1ms. So we use microseconds to
620 : // keep a good resolution of data.
621 16 : uint32_t duration = (TimeStamp::Now() - mStartTime).ToMicroseconds();
622 :
623 : // This is a simple prefiltering of values that might differ a lot from the
624 : // average value. Do not add the value to the filtered stats when the event
625 : // had to wait in a long queue.
626 16 : uint32_t eventCounter = aIOThread->EventCounter();
627 16 : bool shortOnly = eventCounter - mEventCounter < 5 ? false : true;
628 :
629 16 : CacheFileUtils::CachePerfStats::AddValue(mType, duration, shortOnly);
630 : }
631 :
632 : protected:
633 : CacheFileUtils::CachePerfStats::EDataType mType;
634 : TimeStamp mStartTime;
635 : uint32_t mEventCounter;
636 : };
637 :
638 : class OpenFileEvent : public Runnable
639 : , public IOPerfReportEvent {
640 : public:
641 8 : OpenFileEvent(const nsACString &aKey, uint32_t aFlags,
642 : CacheFileIOListener *aCallback)
643 8 : : Runnable("net::OpenFileEvent")
644 : , IOPerfReportEvent(CacheFileUtils::CachePerfStats::IO_OPEN)
645 : , mFlags(aFlags)
646 : , mCallback(aCallback)
647 8 : , mKey(aKey)
648 : {
649 8 : mIOMan = CacheFileIOManager::gInstance;
650 8 : if (!(mFlags & CacheFileIOManager::SPECIAL_FILE)) {
651 5 : Start(mIOMan->mIOThread);
652 : }
653 8 : }
654 :
655 : protected:
656 16 : ~OpenFileEvent()
657 8 : {
658 24 : }
659 :
660 : public:
661 8 : NS_IMETHOD Run() override
662 : {
663 8 : nsresult rv = NS_OK;
664 :
665 8 : if (!(mFlags & CacheFileIOManager::SPECIAL_FILE)) {
666 5 : SHA1Sum sum;
667 5 : sum.update(mKey.BeginReading(), mKey.Length());
668 5 : sum.finish(mHash);
669 : }
670 :
671 8 : if (!mIOMan) {
672 0 : rv = NS_ERROR_NOT_INITIALIZED;
673 : } else {
674 8 : if (mFlags & CacheFileIOManager::SPECIAL_FILE) {
675 3 : rv = mIOMan->OpenSpecialFileInternal(mKey, mFlags,
676 6 : getter_AddRefs(mHandle));
677 : } else {
678 5 : rv = mIOMan->OpenFileInternal(&mHash, mKey, mFlags,
679 10 : getter_AddRefs(mHandle));
680 5 : if (NS_SUCCEEDED(rv)) {
681 5 : Report(mIOMan->mIOThread);
682 : }
683 : }
684 8 : mIOMan = nullptr;
685 8 : if (mHandle) {
686 5 : if (mHandle->Key().IsEmpty()) {
687 5 : mHandle->Key() = mKey;
688 : }
689 : }
690 : }
691 :
692 8 : mCallback->OnFileOpened(mHandle, rv);
693 8 : return NS_OK;
694 : }
695 :
696 : protected:
697 : SHA1Sum::Hash mHash;
698 : uint32_t mFlags;
699 : nsCOMPtr<CacheFileIOListener> mCallback;
700 : RefPtr<CacheFileIOManager> mIOMan;
701 : RefPtr<CacheFileHandle> mHandle;
702 : nsCString mKey;
703 : };
704 :
705 : class ReadEvent : public Runnable
706 : , public IOPerfReportEvent {
707 : public:
708 5 : ReadEvent(CacheFileHandle *aHandle, int64_t aOffset, char *aBuf,
709 : int32_t aCount, CacheFileIOListener *aCallback)
710 5 : : Runnable("net::ReadEvent")
711 : , IOPerfReportEvent(CacheFileUtils::CachePerfStats::IO_READ)
712 : , mHandle(aHandle)
713 : , mOffset(aOffset)
714 : , mBuf(aBuf)
715 : , mCount(aCount)
716 5 : , mCallback(aCallback)
717 : {
718 5 : if (!mHandle->IsSpecialFile()) {
719 5 : Start(CacheFileIOManager::gInstance->mIOThread);
720 : }
721 5 : }
722 :
723 : protected:
724 10 : ~ReadEvent()
725 5 : {
726 15 : }
727 :
728 : public:
729 5 : NS_IMETHOD Run() override
730 : {
731 : nsresult rv;
732 :
733 5 : if (mHandle->IsClosed() || (mCallback && mCallback->IsKilled())) {
734 0 : rv = NS_ERROR_NOT_INITIALIZED;
735 : } else {
736 5 : rv = CacheFileIOManager::gInstance->ReadInternal(
737 5 : mHandle, mOffset, mBuf, mCount);
738 5 : if (NS_SUCCEEDED(rv)) {
739 5 : Report(CacheFileIOManager::gInstance->mIOThread);
740 : }
741 : }
742 :
743 5 : mCallback->OnDataRead(mHandle, mBuf, rv);
744 5 : return NS_OK;
745 : }
746 :
747 : protected:
748 : RefPtr<CacheFileHandle> mHandle;
749 : int64_t mOffset;
750 : char *mBuf;
751 : int32_t mCount;
752 : nsCOMPtr<CacheFileIOListener> mCallback;
753 : };
754 :
755 : class WriteEvent : public Runnable
756 : , public IOPerfReportEvent {
757 : public:
758 6 : WriteEvent(CacheFileHandle *aHandle, int64_t aOffset, const char *aBuf,
759 : int32_t aCount, bool aValidate, bool aTruncate,
760 : CacheFileIOListener *aCallback)
761 6 : : Runnable("net::WriteEvent")
762 : , IOPerfReportEvent(CacheFileUtils::CachePerfStats::IO_WRITE)
763 : , mHandle(aHandle)
764 : , mOffset(aOffset)
765 : , mBuf(aBuf)
766 : , mCount(aCount)
767 : , mValidate(aValidate)
768 : , mTruncate(aTruncate)
769 6 : , mCallback(aCallback)
770 : {
771 6 : if (!mHandle->IsSpecialFile()) {
772 6 : Start(CacheFileIOManager::gInstance->mIOThread);
773 : }
774 6 : }
775 :
776 : protected:
777 12 : ~WriteEvent()
778 12 : {
779 6 : if (!mCallback && mBuf) {
780 0 : free(const_cast<char *>(mBuf));
781 : }
782 18 : }
783 :
784 : public:
785 6 : NS_IMETHOD Run() override
786 : {
787 : nsresult rv;
788 :
789 6 : if (mHandle->IsClosed() || (mCallback && mCallback->IsKilled())) {
790 : // We usually get here only after the internal shutdown
791 : // (i.e. mShuttingDown == true). Pretend write has succeeded
792 : // to avoid any past-shutdown file dooming.
793 0 : rv = (CacheObserver::IsPastShutdownIOLag() ||
794 0 : CacheFileIOManager::gInstance->mShuttingDown)
795 0 : ? NS_OK
796 : : NS_ERROR_NOT_INITIALIZED;
797 : } else {
798 12 : rv = CacheFileIOManager::gInstance->WriteInternal(
799 18 : mHandle, mOffset, mBuf, mCount, mValidate, mTruncate);
800 6 : if (NS_SUCCEEDED(rv)) {
801 6 : Report(CacheFileIOManager::gInstance->mIOThread);
802 : }
803 6 : if (NS_FAILED(rv) && !mCallback) {
804 : // No listener is going to handle the error, doom the file
805 0 : CacheFileIOManager::gInstance->DoomFileInternal(mHandle);
806 : }
807 : }
808 6 : if (mCallback) {
809 6 : mCallback->OnDataWritten(mHandle, mBuf, rv);
810 : } else {
811 0 : free(const_cast<char *>(mBuf));
812 0 : mBuf = nullptr;
813 : }
814 :
815 6 : return NS_OK;
816 : }
817 :
818 : protected:
819 : RefPtr<CacheFileHandle> mHandle;
820 : int64_t mOffset;
821 : const char *mBuf;
822 : int32_t mCount;
823 : bool mValidate : 1;
824 : bool mTruncate : 1;
825 : nsCOMPtr<CacheFileIOListener> mCallback;
826 : };
827 :
828 : class DoomFileEvent : public Runnable {
829 : public:
830 0 : DoomFileEvent(CacheFileHandle* aHandle, CacheFileIOListener* aCallback)
831 0 : : Runnable("net::DoomFileEvent")
832 : , mCallback(aCallback)
833 0 : , mHandle(aHandle)
834 : {
835 0 : }
836 :
837 : protected:
838 0 : ~DoomFileEvent()
839 0 : {
840 0 : }
841 :
842 : public:
843 0 : NS_IMETHOD Run() override
844 : {
845 : nsresult rv;
846 :
847 0 : if (mHandle->IsClosed()) {
848 0 : rv = NS_ERROR_NOT_INITIALIZED;
849 : } else {
850 0 : rv = CacheFileIOManager::gInstance->DoomFileInternal(mHandle);
851 : }
852 :
853 0 : if (mCallback) {
854 0 : mCallback->OnFileDoomed(mHandle, rv);
855 : }
856 :
857 0 : return NS_OK;
858 : }
859 :
860 : protected:
861 : nsCOMPtr<CacheFileIOListener> mCallback;
862 : nsCOMPtr<nsIEventTarget> mTarget;
863 : RefPtr<CacheFileHandle> mHandle;
864 : };
865 :
866 : class DoomFileByKeyEvent : public Runnable {
867 : public:
868 1 : DoomFileByKeyEvent(const nsACString& aKey, CacheFileIOListener* aCallback)
869 1 : : Runnable("net::DoomFileByKeyEvent")
870 1 : , mCallback(aCallback)
871 : {
872 1 : SHA1Sum sum;
873 1 : sum.update(aKey.BeginReading(), aKey.Length());
874 1 : sum.finish(mHash);
875 :
876 1 : mIOMan = CacheFileIOManager::gInstance;
877 1 : }
878 :
879 : protected:
880 2 : ~DoomFileByKeyEvent()
881 1 : {
882 3 : }
883 :
884 : public:
885 1 : NS_IMETHOD Run() override
886 : {
887 : nsresult rv;
888 :
889 1 : if (!mIOMan) {
890 0 : rv = NS_ERROR_NOT_INITIALIZED;
891 : } else {
892 1 : rv = mIOMan->DoomFileByKeyInternal(&mHash);
893 1 : mIOMan = nullptr;
894 : }
895 :
896 1 : if (mCallback) {
897 1 : mCallback->OnFileDoomed(nullptr, rv);
898 : }
899 :
900 1 : return NS_OK;
901 : }
902 :
903 : protected:
904 : SHA1Sum::Hash mHash;
905 : nsCOMPtr<CacheFileIOListener> mCallback;
906 : RefPtr<CacheFileIOManager> mIOMan;
907 : };
908 :
909 : class ReleaseNSPRHandleEvent : public Runnable {
910 : public:
911 4 : explicit ReleaseNSPRHandleEvent(CacheFileHandle* aHandle)
912 4 : : Runnable("net::ReleaseNSPRHandleEvent")
913 4 : , mHandle(aHandle)
914 : {
915 4 : }
916 :
917 : protected:
918 8 : ~ReleaseNSPRHandleEvent()
919 4 : {
920 12 : }
921 :
922 : public:
923 4 : NS_IMETHOD Run() override
924 : {
925 4 : if (!mHandle->IsClosed()) {
926 4 : CacheFileIOManager::gInstance->MaybeReleaseNSPRHandleInternal(mHandle);
927 : }
928 :
929 4 : return NS_OK;
930 : }
931 :
932 : protected:
933 : RefPtr<CacheFileHandle> mHandle;
934 : };
935 :
936 : class TruncateSeekSetEOFEvent : public Runnable {
937 : public:
938 0 : TruncateSeekSetEOFEvent(CacheFileHandle* aHandle,
939 : int64_t aTruncatePos,
940 : int64_t aEOFPos,
941 : CacheFileIOListener* aCallback)
942 0 : : Runnable("net::TruncateSeekSetEOFEvent")
943 : , mHandle(aHandle)
944 : , mTruncatePos(aTruncatePos)
945 : , mEOFPos(aEOFPos)
946 0 : , mCallback(aCallback)
947 : {
948 0 : }
949 :
950 : protected:
951 0 : ~TruncateSeekSetEOFEvent()
952 0 : {
953 0 : }
954 :
955 : public:
956 0 : NS_IMETHOD Run() override
957 : {
958 : nsresult rv;
959 :
960 0 : if (mHandle->IsClosed() || (mCallback && mCallback->IsKilled())) {
961 0 : rv = NS_ERROR_NOT_INITIALIZED;
962 : } else {
963 0 : rv = CacheFileIOManager::gInstance->TruncateSeekSetEOFInternal(
964 0 : mHandle, mTruncatePos, mEOFPos);
965 : }
966 :
967 0 : if (mCallback) {
968 0 : mCallback->OnEOFSet(mHandle, rv);
969 : }
970 :
971 0 : return NS_OK;
972 : }
973 :
974 : protected:
975 : RefPtr<CacheFileHandle> mHandle;
976 : int64_t mTruncatePos;
977 : int64_t mEOFPos;
978 : nsCOMPtr<CacheFileIOListener> mCallback;
979 : };
980 :
981 : class RenameFileEvent : public Runnable {
982 : public:
983 0 : RenameFileEvent(CacheFileHandle* aHandle,
984 : const nsACString& aNewName,
985 : CacheFileIOListener* aCallback)
986 0 : : Runnable("net::RenameFileEvent")
987 : , mHandle(aHandle)
988 : , mNewName(aNewName)
989 0 : , mCallback(aCallback)
990 : {
991 0 : }
992 :
993 : protected:
994 0 : ~RenameFileEvent()
995 0 : {
996 0 : }
997 :
998 : public:
999 0 : NS_IMETHOD Run() override
1000 : {
1001 : nsresult rv;
1002 :
1003 0 : if (mHandle->IsClosed()) {
1004 0 : rv = NS_ERROR_NOT_INITIALIZED;
1005 : } else {
1006 0 : rv = CacheFileIOManager::gInstance->RenameFileInternal(mHandle,
1007 0 : mNewName);
1008 : }
1009 :
1010 0 : if (mCallback) {
1011 0 : mCallback->OnFileRenamed(mHandle, rv);
1012 : }
1013 :
1014 0 : return NS_OK;
1015 : }
1016 :
1017 : protected:
1018 : RefPtr<CacheFileHandle> mHandle;
1019 : nsCString mNewName;
1020 : nsCOMPtr<CacheFileIOListener> mCallback;
1021 : };
1022 :
1023 : class InitIndexEntryEvent : public Runnable {
1024 : public:
1025 5 : InitIndexEntryEvent(CacheFileHandle* aHandle,
1026 : OriginAttrsHash aOriginAttrsHash,
1027 : bool aAnonymous,
1028 : bool aPinning)
1029 5 : : Runnable("net::InitIndexEntryEvent")
1030 : , mHandle(aHandle)
1031 : , mOriginAttrsHash(aOriginAttrsHash)
1032 : , mAnonymous(aAnonymous)
1033 5 : , mPinning(aPinning)
1034 : {
1035 5 : }
1036 :
1037 : protected:
1038 10 : ~InitIndexEntryEvent()
1039 5 : {
1040 15 : }
1041 :
1042 : public:
1043 5 : NS_IMETHOD Run() override
1044 : {
1045 5 : if (mHandle->IsClosed() || mHandle->IsDoomed()) {
1046 0 : return NS_OK;
1047 : }
1048 :
1049 5 : CacheIndex::InitEntry(mHandle->Hash(), mOriginAttrsHash, mAnonymous,
1050 10 : mPinning);
1051 :
1052 : // We cannot set the filesize before we init the entry. If we're opening
1053 : // an existing entry file, frecency and expiration time will be set after
1054 : // parsing the entry file, but we must set the filesize here since nobody is
1055 : // going to set it if there is no write to the file.
1056 5 : uint32_t sizeInK = mHandle->FileSizeInK();
1057 5 : CacheIndex::UpdateEntry(mHandle->Hash(), nullptr, nullptr, nullptr, nullptr,
1058 5 : nullptr, &sizeInK);
1059 :
1060 5 : return NS_OK;
1061 : }
1062 :
1063 : protected:
1064 : RefPtr<CacheFileHandle> mHandle;
1065 : OriginAttrsHash mOriginAttrsHash;
1066 : bool mAnonymous;
1067 : bool mPinning;
1068 : };
1069 :
1070 : class UpdateIndexEntryEvent : public Runnable {
1071 : public:
1072 14 : UpdateIndexEntryEvent(CacheFileHandle* aHandle,
1073 : const uint32_t* aFrecency,
1074 : const uint32_t* aExpirationTime,
1075 : const bool* aHasAltData,
1076 : const uint16_t* aOnStartTime,
1077 : const uint16_t* aOnStopTime)
1078 14 : : Runnable("net::UpdateIndexEntryEvent")
1079 : , mHandle(aHandle)
1080 : , mHasFrecency(false)
1081 : , mHasExpirationTime(false)
1082 : , mHasHasAltData(false)
1083 : , mHasOnStartTime(false)
1084 14 : , mHasOnStopTime(false)
1085 : {
1086 14 : if (aFrecency) {
1087 11 : mHasFrecency = true;
1088 11 : mFrecency = *aFrecency;
1089 : }
1090 14 : if (aExpirationTime) {
1091 7 : mHasExpirationTime = true;
1092 7 : mExpirationTime = *aExpirationTime;
1093 : }
1094 14 : if (aHasAltData) {
1095 5 : mHasHasAltData = true;
1096 5 : mHasAltData = *aHasAltData;
1097 : }
1098 14 : if (aOnStartTime) {
1099 6 : mHasOnStartTime = true;
1100 6 : mOnStartTime = *aOnStartTime;
1101 : }
1102 14 : if (aOnStopTime) {
1103 6 : mHasOnStopTime = true;
1104 6 : mOnStopTime = *aOnStopTime;
1105 : }
1106 14 : }
1107 :
1108 : protected:
1109 28 : ~UpdateIndexEntryEvent()
1110 14 : {
1111 42 : }
1112 :
1113 : public:
1114 14 : NS_IMETHOD Run() override
1115 : {
1116 14 : if (mHandle->IsClosed() || mHandle->IsDoomed()) {
1117 0 : return NS_OK;
1118 : }
1119 :
1120 70 : CacheIndex::UpdateEntry(mHandle->Hash(),
1121 14 : mHasFrecency ? &mFrecency : nullptr,
1122 14 : mHasExpirationTime ? &mExpirationTime : nullptr,
1123 14 : mHasHasAltData ? &mHasAltData : nullptr,
1124 14 : mHasOnStartTime ? &mOnStartTime : nullptr,
1125 14 : mHasOnStopTime ? &mOnStopTime : nullptr,
1126 14 : nullptr);
1127 14 : return NS_OK;
1128 : }
1129 :
1130 : protected:
1131 : RefPtr<CacheFileHandle> mHandle;
1132 :
1133 : bool mHasFrecency;
1134 : bool mHasExpirationTime;
1135 : bool mHasHasAltData;
1136 : bool mHasOnStartTime;
1137 : bool mHasOnStopTime;
1138 :
1139 : uint32_t mFrecency;
1140 : uint32_t mExpirationTime;
1141 : bool mHasAltData;
1142 : uint16_t mOnStartTime;
1143 : uint16_t mOnStopTime;
1144 : };
1145 :
1146 : class MetadataWriteScheduleEvent : public Runnable
1147 : {
1148 : public:
1149 : enum EMode {
1150 : SCHEDULE,
1151 : UNSCHEDULE,
1152 : SHUTDOWN
1153 : } mMode;
1154 :
1155 : RefPtr<CacheFile> mFile;
1156 : RefPtr<CacheFileIOManager> mIOMan;
1157 :
1158 36 : MetadataWriteScheduleEvent(CacheFileIOManager* aManager,
1159 : CacheFile* aFile,
1160 : EMode aMode)
1161 36 : : Runnable("net::MetadataWriteScheduleEvent")
1162 : , mMode(aMode)
1163 : , mFile(aFile)
1164 36 : , mIOMan(aManager)
1165 36 : { }
1166 :
1167 108 : virtual ~MetadataWriteScheduleEvent() { }
1168 :
1169 36 : NS_IMETHOD Run() override
1170 : {
1171 72 : RefPtr<CacheFileIOManager> ioMan = CacheFileIOManager::gInstance;
1172 36 : if (!ioMan) {
1173 0 : NS_WARNING("CacheFileIOManager already gone in MetadataWriteScheduleEvent::Run()");
1174 0 : return NS_OK;
1175 : }
1176 :
1177 36 : switch (mMode)
1178 : {
1179 : case SCHEDULE:
1180 32 : ioMan->ScheduleMetadataWriteInternal(mFile);
1181 32 : break;
1182 : case UNSCHEDULE:
1183 4 : ioMan->UnscheduleMetadataWriteInternal(mFile);
1184 4 : break;
1185 : case SHUTDOWN:
1186 0 : ioMan->ShutdownMetadataWriteSchedulingInternal();
1187 0 : break;
1188 : }
1189 36 : return NS_OK;
1190 : }
1191 : };
1192 :
1193 3 : StaticRefPtr<CacheFileIOManager> CacheFileIOManager::gInstance;
1194 :
1195 728 : NS_IMPL_ISUPPORTS(CacheFileIOManager, nsITimerCallback)
1196 :
1197 1 : CacheFileIOManager::CacheFileIOManager()
1198 : : mShuttingDown(false)
1199 : , mTreeCreated(false)
1200 : , mTreeCreationFailed(false)
1201 : , mOverLimitEvicting(false)
1202 : , mCacheSizeOnHardLimit(false)
1203 1 : , mRemovingTrashDirs(false)
1204 : {
1205 1 : LOG(("CacheFileIOManager::CacheFileIOManager [this=%p]", this));
1206 1 : MOZ_ASSERT(!gInstance, "multiple CacheFileIOManager instances!");
1207 1 : }
1208 :
1209 0 : CacheFileIOManager::~CacheFileIOManager()
1210 : {
1211 0 : LOG(("CacheFileIOManager::~CacheFileIOManager [this=%p]", this));
1212 0 : }
1213 :
1214 : // static
1215 : nsresult
1216 2 : CacheFileIOManager::Init()
1217 : {
1218 2 : LOG(("CacheFileIOManager::Init()"));
1219 :
1220 2 : MOZ_ASSERT(NS_IsMainThread());
1221 :
1222 2 : if (gInstance) {
1223 1 : return NS_ERROR_ALREADY_INITIALIZED;
1224 : }
1225 :
1226 2 : RefPtr<CacheFileIOManager> ioMan = new CacheFileIOManager();
1227 :
1228 1 : nsresult rv = ioMan->InitInternal();
1229 1 : NS_ENSURE_SUCCESS(rv, rv);
1230 :
1231 1 : gInstance = ioMan.forget();
1232 1 : return NS_OK;
1233 : }
1234 :
1235 : nsresult
1236 1 : CacheFileIOManager::InitInternal()
1237 : {
1238 : nsresult rv;
1239 :
1240 1 : mIOThread = new CacheIOThread();
1241 :
1242 1 : rv = mIOThread->Init();
1243 1 : MOZ_ASSERT(NS_SUCCEEDED(rv), "Can't create background thread");
1244 1 : NS_ENSURE_SUCCESS(rv, rv);
1245 :
1246 1 : mStartTime = TimeStamp::NowLoRes();
1247 :
1248 1 : return NS_OK;
1249 : }
1250 :
1251 : // static
1252 : nsresult
1253 0 : CacheFileIOManager::Shutdown()
1254 : {
1255 0 : LOG(("CacheFileIOManager::Shutdown() [gInstance=%p]", gInstance.get()));
1256 :
1257 0 : MOZ_ASSERT(NS_IsMainThread());
1258 :
1259 0 : if (!gInstance) {
1260 0 : return NS_ERROR_NOT_INITIALIZED;
1261 : }
1262 :
1263 0 : Telemetry::AutoTimer<Telemetry::NETWORK_DISK_CACHE_SHUTDOWN_V2> shutdownTimer;
1264 :
1265 0 : CacheIndex::PreShutdown();
1266 :
1267 0 : ShutdownMetadataWriteScheduling();
1268 :
1269 0 : RefPtr<ShutdownEvent> ev = new ShutdownEvent();
1270 0 : ev->PostAndWait();
1271 :
1272 0 : MOZ_ASSERT(gInstance->mHandles.HandleCount() == 0);
1273 0 : MOZ_ASSERT(gInstance->mHandlesByLastUsed.Length() == 0);
1274 :
1275 0 : if (gInstance->mIOThread) {
1276 0 : gInstance->mIOThread->Shutdown();
1277 : }
1278 :
1279 0 : CacheIndex::Shutdown();
1280 :
1281 0 : if (CacheObserver::ClearCacheOnShutdown()) {
1282 0 : Telemetry::AutoTimer<Telemetry::NETWORK_DISK_CACHE2_SHUTDOWN_CLEAR_PRIVATE> totalTimer;
1283 0 : gInstance->SyncRemoveAllCacheFiles();
1284 : }
1285 :
1286 0 : gInstance = nullptr;
1287 :
1288 0 : return NS_OK;
1289 : }
1290 :
1291 : nsresult
1292 0 : CacheFileIOManager::ShutdownInternal()
1293 : {
1294 0 : LOG(("CacheFileIOManager::ShutdownInternal() [this=%p]", this));
1295 :
1296 0 : MOZ_ASSERT(mIOThread->IsCurrentThread());
1297 :
1298 : // No new handles can be created after this flag is set
1299 0 : mShuttingDown = true;
1300 :
1301 0 : if (mTrashTimer) {
1302 0 : mTrashTimer->Cancel();
1303 0 : mTrashTimer = nullptr;
1304 : }
1305 :
1306 : // close all handles and delete all associated files
1307 0 : nsTArray<RefPtr<CacheFileHandle> > handles;
1308 0 : mHandles.GetAllHandles(&handles);
1309 0 : handles.AppendElements(mSpecialHandles);
1310 :
1311 0 : for (uint32_t i=0 ; i<handles.Length() ; i++) {
1312 0 : CacheFileHandle *h = handles[i];
1313 0 : h->mClosed = true;
1314 :
1315 0 : h->Log();
1316 :
1317 : // Close completely written files.
1318 0 : MaybeReleaseNSPRHandleInternal(h);
1319 : // Don't bother removing invalid and/or doomed files to improve
1320 : // shutdown perfomrance.
1321 : // Doomed files are already in the doomed directory from which
1322 : // we never reuse files and delete the dir on next session startup.
1323 : // Invalid files don't have metadata and thus won't load anyway
1324 : // (hashes won't match).
1325 :
1326 0 : if (!h->IsSpecialFile() && !h->mIsDoomed && !h->mFileExists) {
1327 0 : CacheIndex::RemoveEntry(h->Hash());
1328 : }
1329 :
1330 : // Remove the handle from mHandles/mSpecialHandles
1331 0 : if (h->IsSpecialFile()) {
1332 0 : mSpecialHandles.RemoveElement(h);
1333 : } else {
1334 0 : mHandles.RemoveHandle(h);
1335 : }
1336 :
1337 : // Pointer to the hash is no longer valid once the last handle with the
1338 : // given hash is released. Null out the pointer so that we crash if there
1339 : // is a bug in this code and we dereference the pointer after this point.
1340 0 : if (!h->IsSpecialFile()) {
1341 0 : h->mHash = nullptr;
1342 : }
1343 : }
1344 :
1345 : // Assert the table is empty. When we are here, no new handles can be added
1346 : // and handles will no longer remove them self from this table and we don't
1347 : // want to keep invalid handles here. Also, there is no lookup after this
1348 : // point to happen.
1349 0 : MOZ_ASSERT(mHandles.HandleCount() == 0);
1350 :
1351 : // Release trash directory enumerator
1352 0 : if (mTrashDirEnumerator) {
1353 0 : mTrashDirEnumerator->Close();
1354 0 : mTrashDirEnumerator = nullptr;
1355 : }
1356 :
1357 0 : return NS_OK;
1358 : }
1359 :
1360 : // static
1361 : nsresult
1362 1 : CacheFileIOManager::OnProfile()
1363 : {
1364 1 : LOG(("CacheFileIOManager::OnProfile() [gInstance=%p]", gInstance.get()));
1365 :
1366 2 : RefPtr<CacheFileIOManager> ioMan = gInstance;
1367 1 : if (!ioMan) {
1368 : // CacheFileIOManager::Init() failed, probably could not create the IO
1369 : // thread, just go with it...
1370 0 : return NS_ERROR_NOT_INITIALIZED;
1371 : }
1372 :
1373 : nsresult rv;
1374 :
1375 2 : nsCOMPtr<nsIFile> directory;
1376 :
1377 1 : CacheObserver::ParentDirOverride(getter_AddRefs(directory));
1378 :
1379 : #if defined(MOZ_WIDGET_ANDROID)
1380 : nsCOMPtr<nsIFile> profilelessDirectory;
1381 : char* cachePath = getenv("CACHE_DIRECTORY");
1382 : if (!directory && cachePath && *cachePath) {
1383 : rv = NS_NewNativeLocalFile(nsDependentCString(cachePath),
1384 : true, getter_AddRefs(directory));
1385 : if (NS_SUCCEEDED(rv)) {
1386 : // Save this directory as the profileless path.
1387 : rv = directory->Clone(getter_AddRefs(profilelessDirectory));
1388 : NS_ENSURE_SUCCESS(rv, rv);
1389 :
1390 : // Add profile leaf name to the directory name to distinguish
1391 : // multiple profiles Fennec supports.
1392 : nsCOMPtr<nsIFile> profD;
1393 : rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
1394 : getter_AddRefs(profD));
1395 :
1396 : nsAutoCString leafName;
1397 : if (NS_SUCCEEDED(rv)) {
1398 : rv = profD->GetNativeLeafName(leafName);
1399 : }
1400 : if (NS_SUCCEEDED(rv)) {
1401 : rv = directory->AppendNative(leafName);
1402 : }
1403 : if (NS_FAILED(rv)) {
1404 : directory = nullptr;
1405 : }
1406 : }
1407 : }
1408 : #endif
1409 :
1410 1 : if (!directory) {
1411 1 : rv = NS_GetSpecialDirectory(NS_APP_CACHE_PARENT_DIR,
1412 2 : getter_AddRefs(directory));
1413 : }
1414 :
1415 1 : if (!directory) {
1416 1 : rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR,
1417 2 : getter_AddRefs(directory));
1418 : }
1419 :
1420 1 : if (directory) {
1421 1 : rv = directory->Append(NS_LITERAL_STRING("cache2"));
1422 1 : NS_ENSURE_SUCCESS(rv, rv);
1423 : }
1424 :
1425 : // All functions return a clone.
1426 1 : ioMan->mCacheDirectory.swap(directory);
1427 :
1428 : #if defined(MOZ_WIDGET_ANDROID)
1429 : if (profilelessDirectory) {
1430 : rv = profilelessDirectory->Append(NS_LITERAL_STRING("cache2"));
1431 : NS_ENSURE_SUCCESS(rv, rv);
1432 : }
1433 :
1434 : ioMan->mCacheProfilelessDirectory.swap(profilelessDirectory);
1435 : #endif
1436 :
1437 1 : if (ioMan->mCacheDirectory) {
1438 1 : CacheIndex::Init(ioMan->mCacheDirectory);
1439 : }
1440 :
1441 1 : return NS_OK;
1442 : }
1443 :
1444 : // static
1445 : already_AddRefed<nsIEventTarget>
1446 114 : CacheFileIOManager::IOTarget()
1447 : {
1448 228 : nsCOMPtr<nsIEventTarget> target;
1449 114 : if (gInstance && gInstance->mIOThread) {
1450 114 : target = gInstance->mIOThread->Target();
1451 : }
1452 :
1453 228 : return target.forget();
1454 : }
1455 :
1456 : // static
1457 : already_AddRefed<CacheIOThread>
1458 18 : CacheFileIOManager::IOThread()
1459 : {
1460 36 : RefPtr<CacheIOThread> thread;
1461 18 : if (gInstance) {
1462 18 : thread = gInstance->mIOThread;
1463 : }
1464 :
1465 36 : return thread.forget();
1466 : }
1467 :
1468 : // static
1469 : bool
1470 44 : CacheFileIOManager::IsOnIOThread()
1471 : {
1472 88 : RefPtr<CacheFileIOManager> ioMan = gInstance;
1473 44 : if (ioMan && ioMan->mIOThread) {
1474 44 : return ioMan->mIOThread->IsCurrentThread();
1475 : }
1476 :
1477 0 : return false;
1478 : }
1479 :
1480 : // static
1481 : bool
1482 153 : CacheFileIOManager::IsOnIOThreadOrCeased()
1483 : {
1484 306 : RefPtr<CacheFileIOManager> ioMan = gInstance;
1485 153 : if (ioMan && ioMan->mIOThread) {
1486 153 : return ioMan->mIOThread->IsCurrentThread();
1487 : }
1488 :
1489 : // Ceased...
1490 0 : return true;
1491 : }
1492 :
1493 : // static
1494 : bool
1495 0 : CacheFileIOManager::IsShutdown()
1496 : {
1497 0 : if (!gInstance) {
1498 0 : return true;
1499 : }
1500 0 : return gInstance->mShuttingDown;
1501 : }
1502 :
1503 : // static
1504 : nsresult
1505 32 : CacheFileIOManager::ScheduleMetadataWrite(CacheFile * aFile)
1506 : {
1507 64 : RefPtr<CacheFileIOManager> ioMan = gInstance;
1508 32 : NS_ENSURE_TRUE(ioMan, NS_ERROR_NOT_INITIALIZED);
1509 :
1510 32 : NS_ENSURE_TRUE(!ioMan->mShuttingDown, NS_ERROR_NOT_INITIALIZED);
1511 :
1512 : RefPtr<MetadataWriteScheduleEvent> event = new MetadataWriteScheduleEvent(
1513 96 : ioMan, aFile, MetadataWriteScheduleEvent::SCHEDULE);
1514 64 : nsCOMPtr<nsIEventTarget> target = ioMan->IOTarget();
1515 32 : NS_ENSURE_TRUE(target, NS_ERROR_UNEXPECTED);
1516 32 : return target->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
1517 : }
1518 :
1519 : nsresult
1520 32 : CacheFileIOManager::ScheduleMetadataWriteInternal(CacheFile * aFile)
1521 : {
1522 32 : MOZ_ASSERT(IsOnIOThreadOrCeased());
1523 :
1524 : nsresult rv;
1525 :
1526 32 : if (!mMetadataWritesTimer) {
1527 1 : mMetadataWritesTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
1528 1 : NS_ENSURE_SUCCESS(rv, rv);
1529 :
1530 2 : rv = mMetadataWritesTimer->InitWithCallback(
1531 1 : this, kMetadataWriteDelay, nsITimer::TYPE_ONE_SHOT);
1532 1 : NS_ENSURE_SUCCESS(rv, rv);
1533 : }
1534 :
1535 32 : if (mScheduledMetadataWrites.IndexOf(aFile) !=
1536 : mScheduledMetadataWrites.NoIndex) {
1537 26 : return NS_OK;
1538 : }
1539 :
1540 6 : mScheduledMetadataWrites.AppendElement(aFile);
1541 :
1542 6 : return NS_OK;
1543 : }
1544 :
1545 : // static
1546 : nsresult
1547 4 : CacheFileIOManager::UnscheduleMetadataWrite(CacheFile * aFile)
1548 : {
1549 8 : RefPtr<CacheFileIOManager> ioMan = gInstance;
1550 4 : NS_ENSURE_TRUE(ioMan, NS_ERROR_NOT_INITIALIZED);
1551 :
1552 4 : NS_ENSURE_TRUE(!ioMan->mShuttingDown, NS_ERROR_NOT_INITIALIZED);
1553 :
1554 : RefPtr<MetadataWriteScheduleEvent> event = new MetadataWriteScheduleEvent(
1555 12 : ioMan, aFile, MetadataWriteScheduleEvent::UNSCHEDULE);
1556 8 : nsCOMPtr<nsIEventTarget> target = ioMan->IOTarget();
1557 4 : NS_ENSURE_TRUE(target, NS_ERROR_UNEXPECTED);
1558 4 : return target->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
1559 : }
1560 :
1561 : nsresult
1562 4 : CacheFileIOManager::UnscheduleMetadataWriteInternal(CacheFile * aFile)
1563 : {
1564 4 : MOZ_ASSERT(IsOnIOThreadOrCeased());
1565 :
1566 4 : mScheduledMetadataWrites.RemoveElement(aFile);
1567 :
1568 4 : if (mScheduledMetadataWrites.Length() == 0 &&
1569 0 : mMetadataWritesTimer) {
1570 0 : mMetadataWritesTimer->Cancel();
1571 0 : mMetadataWritesTimer = nullptr;
1572 : }
1573 :
1574 4 : return NS_OK;
1575 : }
1576 :
1577 : // static
1578 : nsresult
1579 0 : CacheFileIOManager::ShutdownMetadataWriteScheduling()
1580 : {
1581 0 : RefPtr<CacheFileIOManager> ioMan = gInstance;
1582 0 : NS_ENSURE_TRUE(ioMan, NS_ERROR_NOT_INITIALIZED);
1583 :
1584 : RefPtr<MetadataWriteScheduleEvent> event = new MetadataWriteScheduleEvent(
1585 0 : ioMan, nullptr, MetadataWriteScheduleEvent::SHUTDOWN);
1586 0 : nsCOMPtr<nsIEventTarget> target = ioMan->IOTarget();
1587 0 : NS_ENSURE_TRUE(target, NS_ERROR_UNEXPECTED);
1588 0 : return target->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
1589 : }
1590 :
1591 : nsresult
1592 0 : CacheFileIOManager::ShutdownMetadataWriteSchedulingInternal()
1593 : {
1594 0 : MOZ_ASSERT(IsOnIOThreadOrCeased());
1595 :
1596 0 : nsTArray<RefPtr<CacheFile> > files;
1597 0 : files.SwapElements(mScheduledMetadataWrites);
1598 0 : for (uint32_t i = 0; i < files.Length(); ++i) {
1599 0 : CacheFile * file = files[i];
1600 0 : file->WriteMetadataIfNeeded();
1601 : }
1602 :
1603 0 : if (mMetadataWritesTimer) {
1604 0 : mMetadataWritesTimer->Cancel();
1605 0 : mMetadataWritesTimer = nullptr;
1606 : }
1607 :
1608 0 : return NS_OK;
1609 : }
1610 :
1611 : NS_IMETHODIMP
1612 0 : CacheFileIOManager::Notify(nsITimer * aTimer)
1613 : {
1614 0 : MOZ_ASSERT(IsOnIOThreadOrCeased());
1615 0 : MOZ_ASSERT(mMetadataWritesTimer == aTimer);
1616 :
1617 0 : mMetadataWritesTimer = nullptr;
1618 :
1619 0 : nsTArray<RefPtr<CacheFile> > files;
1620 0 : files.SwapElements(mScheduledMetadataWrites);
1621 0 : for (uint32_t i = 0; i < files.Length(); ++i) {
1622 0 : CacheFile * file = files[i];
1623 0 : file->WriteMetadataIfNeeded();
1624 : }
1625 :
1626 0 : return NS_OK;
1627 : }
1628 :
1629 : // static
1630 : nsresult
1631 8 : CacheFileIOManager::OpenFile(const nsACString &aKey,
1632 : uint32_t aFlags, CacheFileIOListener *aCallback)
1633 : {
1634 8 : LOG(("CacheFileIOManager::OpenFile() [key=%s, flags=%d, listener=%p]",
1635 : PromiseFlatCString(aKey).get(), aFlags, aCallback));
1636 :
1637 : nsresult rv;
1638 16 : RefPtr<CacheFileIOManager> ioMan = gInstance;
1639 :
1640 8 : if (!ioMan) {
1641 0 : return NS_ERROR_NOT_INITIALIZED;
1642 : }
1643 :
1644 8 : bool priority = aFlags & CacheFileIOManager::PRIORITY;
1645 16 : RefPtr<OpenFileEvent> ev = new OpenFileEvent(aKey, aFlags, aCallback);
1646 16 : rv = ioMan->mIOThread->Dispatch(ev, priority
1647 : ? CacheIOThread::OPEN_PRIORITY
1648 16 : : CacheIOThread::OPEN);
1649 8 : NS_ENSURE_SUCCESS(rv, rv);
1650 :
1651 8 : return NS_OK;
1652 : }
1653 :
1654 : nsresult
1655 5 : CacheFileIOManager::OpenFileInternal(const SHA1Sum::Hash *aHash,
1656 : const nsACString &aKey,
1657 : uint32_t aFlags,
1658 : CacheFileHandle **_retval)
1659 : {
1660 5 : LOG(("CacheFileIOManager::OpenFileInternal() [hash=%08x%08x%08x%08x%08x, "
1661 : "key=%s, flags=%d]", LOGSHA1(aHash), PromiseFlatCString(aKey).get(),
1662 : aFlags));
1663 :
1664 5 : MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
1665 :
1666 : nsresult rv;
1667 :
1668 5 : if (mShuttingDown) {
1669 0 : return NS_ERROR_NOT_INITIALIZED;
1670 : }
1671 :
1672 10 : CacheIOThread::Cancelable cancelable(true /* never called for special handles */);
1673 :
1674 5 : if (!mTreeCreated) {
1675 0 : rv = CreateCacheTree();
1676 0 : if (NS_FAILED(rv)) return rv;
1677 : }
1678 :
1679 5 : CacheFileHandle::PinningStatus pinning = aFlags & PINNED
1680 5 : ? CacheFileHandle::PinningStatus::PINNED
1681 5 : : CacheFileHandle::PinningStatus::NON_PINNED;
1682 :
1683 10 : nsCOMPtr<nsIFile> file;
1684 5 : rv = GetFile(aHash, getter_AddRefs(file));
1685 5 : NS_ENSURE_SUCCESS(rv, rv);
1686 :
1687 10 : RefPtr<CacheFileHandle> handle;
1688 5 : mHandles.GetHandle(aHash, getter_AddRefs(handle));
1689 :
1690 5 : if ((aFlags & (OPEN | CREATE | CREATE_NEW)) == CREATE_NEW) {
1691 0 : if (handle) {
1692 0 : rv = DoomFileInternal(handle);
1693 0 : NS_ENSURE_SUCCESS(rv, rv);
1694 0 : handle = nullptr;
1695 : }
1696 :
1697 0 : rv = mHandles.NewHandle(aHash, aFlags & PRIORITY, pinning, getter_AddRefs(handle));
1698 0 : NS_ENSURE_SUCCESS(rv, rv);
1699 :
1700 : bool exists;
1701 0 : rv = file->Exists(&exists);
1702 0 : NS_ENSURE_SUCCESS(rv, rv);
1703 :
1704 0 : if (exists) {
1705 0 : CacheIndex::RemoveEntry(aHash);
1706 :
1707 0 : LOG(("CacheFileIOManager::OpenFileInternal() - Removing old file from "
1708 : "disk"));
1709 0 : rv = file->Remove(false);
1710 0 : if (NS_FAILED(rv)) {
1711 0 : NS_WARNING("Cannot remove old entry from the disk");
1712 0 : LOG(("CacheFileIOManager::OpenFileInternal() - Removing old file failed"
1713 : ". [rv=0x%08" PRIx32 "]", static_cast<uint32_t>(rv)));
1714 : }
1715 : }
1716 :
1717 0 : CacheIndex::AddEntry(aHash);
1718 0 : handle->mFile.swap(file);
1719 0 : handle->mFileSize = 0;
1720 : }
1721 :
1722 5 : if (handle) {
1723 0 : handle.swap(*_retval);
1724 0 : return NS_OK;
1725 : }
1726 :
1727 5 : bool exists, evictedAsPinned = false, evictedAsNonPinned = false;
1728 5 : rv = file->Exists(&exists);
1729 5 : NS_ENSURE_SUCCESS(rv, rv);
1730 :
1731 5 : if (exists && mContextEvictor) {
1732 0 : if (mContextEvictor->ContextsCount() == 0) {
1733 0 : mContextEvictor = nullptr;
1734 : } else {
1735 0 : mContextEvictor->WasEvicted(aKey, file, &evictedAsPinned, &evictedAsNonPinned);
1736 : }
1737 : }
1738 :
1739 5 : if (!exists && (aFlags & (OPEN | CREATE | CREATE_NEW)) == OPEN) {
1740 0 : return NS_ERROR_NOT_AVAILABLE;
1741 : }
1742 :
1743 5 : if (exists) {
1744 : // For existing files we determine the pinning status later, after the metadata gets parsed.
1745 3 : pinning = CacheFileHandle::PinningStatus::UNKNOWN;
1746 : }
1747 :
1748 5 : rv = mHandles.NewHandle(aHash, aFlags & PRIORITY, pinning, getter_AddRefs(handle));
1749 5 : NS_ENSURE_SUCCESS(rv, rv);
1750 :
1751 5 : if (exists) {
1752 : // If this file has been found evicted through the context file evictor above for
1753 : // any of pinned or non-pinned state, these calls ensure we doom the handle ASAP
1754 : // we know the real pinning state after metadta has been parsed. DoomFileInternal
1755 : // on the |handle| doesn't doom right now, since the pinning state is unknown
1756 : // and we pass down a pinning restriction.
1757 3 : if (evictedAsPinned) {
1758 0 : rv = DoomFileInternal(handle, DOOM_WHEN_PINNED);
1759 0 : MOZ_ASSERT(!handle->IsDoomed() && NS_SUCCEEDED(rv));
1760 : }
1761 3 : if (evictedAsNonPinned) {
1762 0 : rv = DoomFileInternal(handle, DOOM_WHEN_NON_PINNED);
1763 0 : MOZ_ASSERT(!handle->IsDoomed() && NS_SUCCEEDED(rv));
1764 : }
1765 :
1766 3 : rv = file->GetFileSize(&handle->mFileSize);
1767 3 : NS_ENSURE_SUCCESS(rv, rv);
1768 :
1769 3 : handle->mFileExists = true;
1770 :
1771 3 : CacheIndex::EnsureEntryExists(aHash);
1772 : } else {
1773 2 : handle->mFileSize = 0;
1774 :
1775 2 : CacheIndex::AddEntry(aHash);
1776 : }
1777 :
1778 5 : handle->mFile.swap(file);
1779 5 : handle.swap(*_retval);
1780 5 : return NS_OK;
1781 : }
1782 :
1783 : nsresult
1784 3 : CacheFileIOManager::OpenSpecialFileInternal(const nsACString &aKey,
1785 : uint32_t aFlags,
1786 : CacheFileHandle **_retval)
1787 : {
1788 3 : LOG(("CacheFileIOManager::OpenSpecialFileInternal() [key=%s, flags=%d]",
1789 : PromiseFlatCString(aKey).get(), aFlags));
1790 :
1791 3 : MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
1792 :
1793 : nsresult rv;
1794 :
1795 3 : if (mShuttingDown) {
1796 0 : return NS_ERROR_NOT_INITIALIZED;
1797 : }
1798 :
1799 3 : if (!mTreeCreated) {
1800 1 : rv = CreateCacheTree();
1801 1 : if (NS_FAILED(rv)) return rv;
1802 : }
1803 :
1804 6 : nsCOMPtr<nsIFile> file;
1805 3 : rv = GetSpecialFile(aKey, getter_AddRefs(file));
1806 3 : NS_ENSURE_SUCCESS(rv, rv);
1807 :
1808 6 : RefPtr<CacheFileHandle> handle;
1809 3 : for (uint32_t i = 0 ; i < mSpecialHandles.Length() ; i++) {
1810 0 : if (!mSpecialHandles[i]->IsDoomed() && mSpecialHandles[i]->Key() == aKey) {
1811 0 : handle = mSpecialHandles[i];
1812 0 : break;
1813 : }
1814 : }
1815 :
1816 3 : if ((aFlags & (OPEN | CREATE | CREATE_NEW)) == CREATE_NEW) {
1817 0 : if (handle) {
1818 0 : rv = DoomFileInternal(handle);
1819 0 : NS_ENSURE_SUCCESS(rv, rv);
1820 0 : handle = nullptr;
1821 : }
1822 :
1823 0 : handle = new CacheFileHandle(aKey, aFlags & PRIORITY, CacheFileHandle::PinningStatus::NON_PINNED);
1824 0 : mSpecialHandles.AppendElement(handle);
1825 :
1826 : bool exists;
1827 0 : rv = file->Exists(&exists);
1828 0 : NS_ENSURE_SUCCESS(rv, rv);
1829 :
1830 0 : if (exists) {
1831 0 : LOG(("CacheFileIOManager::OpenSpecialFileInternal() - Removing file from "
1832 : "disk"));
1833 0 : rv = file->Remove(false);
1834 0 : if (NS_FAILED(rv)) {
1835 0 : NS_WARNING("Cannot remove old entry from the disk");
1836 0 : LOG(("CacheFileIOManager::OpenSpecialFileInternal() - Removing file "
1837 : "failed. [rv=0x%08" PRIx32 "]", static_cast<uint32_t>(rv)));
1838 : }
1839 : }
1840 :
1841 0 : handle->mFile.swap(file);
1842 0 : handle->mFileSize = 0;
1843 : }
1844 :
1845 3 : if (handle) {
1846 0 : handle.swap(*_retval);
1847 0 : return NS_OK;
1848 : }
1849 :
1850 : bool exists;
1851 3 : rv = file->Exists(&exists);
1852 3 : NS_ENSURE_SUCCESS(rv, rv);
1853 :
1854 3 : if (!exists && (aFlags & (OPEN | CREATE | CREATE_NEW)) == OPEN) {
1855 3 : return NS_ERROR_NOT_AVAILABLE;
1856 : }
1857 :
1858 0 : handle = new CacheFileHandle(aKey, aFlags & PRIORITY, CacheFileHandle::PinningStatus::NON_PINNED);
1859 0 : mSpecialHandles.AppendElement(handle);
1860 :
1861 0 : if (exists) {
1862 0 : rv = file->GetFileSize(&handle->mFileSize);
1863 0 : NS_ENSURE_SUCCESS(rv, rv);
1864 :
1865 0 : handle->mFileExists = true;
1866 : } else {
1867 0 : handle->mFileSize = 0;
1868 : }
1869 :
1870 0 : handle->mFile.swap(file);
1871 0 : handle.swap(*_retval);
1872 0 : return NS_OK;
1873 : }
1874 :
1875 : nsresult
1876 0 : CacheFileIOManager::CloseHandleInternal(CacheFileHandle *aHandle)
1877 : {
1878 : nsresult rv;
1879 :
1880 0 : LOG(("CacheFileIOManager::CloseHandleInternal() [handle=%p]", aHandle));
1881 :
1882 0 : MOZ_ASSERT(!aHandle->IsClosed());
1883 :
1884 0 : aHandle->Log();
1885 :
1886 0 : MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
1887 :
1888 0 : CacheIOThread::Cancelable cancelable(!aHandle->IsSpecialFile());
1889 :
1890 : // Maybe close file handle (can be legally bypassed after shutdown)
1891 0 : rv = MaybeReleaseNSPRHandleInternal(aHandle);
1892 :
1893 : // Delete the file if the entry was doomed or invalid and
1894 : // filedesc properly closed
1895 0 : if ((aHandle->mIsDoomed || aHandle->mInvalid) && aHandle->mFileExists &&
1896 0 : NS_SUCCEEDED(rv)) {
1897 0 : LOG(("CacheFileIOManager::CloseHandleInternal() - Removing file from "
1898 : "disk"));
1899 :
1900 0 : rv = aHandle->mFile->Remove(false);
1901 0 : if (NS_SUCCEEDED(rv)) {
1902 0 : aHandle->mFileExists = false;
1903 : } else {
1904 0 : LOG((" failed to remove the file [rv=0x%08" PRIx32 "]",
1905 : static_cast<uint32_t>(rv)));
1906 : }
1907 : }
1908 :
1909 0 : if (!aHandle->IsSpecialFile() && !aHandle->mIsDoomed &&
1910 0 : (aHandle->mInvalid || !aHandle->mFileExists)) {
1911 0 : CacheIndex::RemoveEntry(aHandle->Hash());
1912 : }
1913 :
1914 : // Don't remove handles after shutdown
1915 0 : if (!mShuttingDown) {
1916 0 : if (aHandle->IsSpecialFile()) {
1917 0 : mSpecialHandles.RemoveElement(aHandle);
1918 : } else {
1919 0 : mHandles.RemoveHandle(aHandle);
1920 : }
1921 : }
1922 :
1923 0 : return NS_OK;
1924 : }
1925 :
1926 : // static
1927 : nsresult
1928 5 : CacheFileIOManager::Read(CacheFileHandle *aHandle, int64_t aOffset,
1929 : char *aBuf, int32_t aCount,
1930 : CacheFileIOListener *aCallback)
1931 : {
1932 5 : LOG(("CacheFileIOManager::Read() [handle=%p, offset=%" PRId64 ", count=%d, "
1933 : "listener=%p]", aHandle, aOffset, aCount, aCallback));
1934 :
1935 5 : if (CacheObserver::ShuttingDown()) {
1936 0 : LOG((" no reads after shutdown"));
1937 0 : return NS_ERROR_NOT_INITIALIZED;
1938 : }
1939 :
1940 : nsresult rv;
1941 10 : RefPtr<CacheFileIOManager> ioMan = gInstance;
1942 :
1943 5 : if (aHandle->IsClosed() || !ioMan) {
1944 0 : return NS_ERROR_NOT_INITIALIZED;
1945 : }
1946 :
1947 : RefPtr<ReadEvent> ev = new ReadEvent(aHandle, aOffset, aBuf, aCount,
1948 10 : aCallback);
1949 10 : rv = ioMan->mIOThread->Dispatch(ev, aHandle->IsPriority()
1950 : ? CacheIOThread::READ_PRIORITY
1951 10 : : CacheIOThread::READ);
1952 5 : NS_ENSURE_SUCCESS(rv, rv);
1953 :
1954 5 : return NS_OK;
1955 : }
1956 :
1957 : nsresult
1958 5 : CacheFileIOManager::ReadInternal(CacheFileHandle *aHandle, int64_t aOffset,
1959 : char *aBuf, int32_t aCount)
1960 : {
1961 5 : LOG(("CacheFileIOManager::ReadInternal() [handle=%p, offset=%" PRId64 ", count=%d]",
1962 : aHandle, aOffset, aCount));
1963 :
1964 : nsresult rv;
1965 :
1966 5 : if (CacheObserver::ShuttingDown()) {
1967 0 : LOG((" no reads after shutdown"));
1968 0 : return NS_ERROR_NOT_INITIALIZED;
1969 : }
1970 :
1971 5 : if (!aHandle->mFileExists) {
1972 0 : NS_WARNING("Trying to read from non-existent file");
1973 0 : return NS_ERROR_NOT_AVAILABLE;
1974 : }
1975 :
1976 10 : CacheIOThread::Cancelable cancelable(!aHandle->IsSpecialFile());
1977 :
1978 5 : if (!aHandle->mFD) {
1979 3 : rv = OpenNSPRHandle(aHandle);
1980 3 : NS_ENSURE_SUCCESS(rv, rv);
1981 : } else {
1982 2 : NSPRHandleUsed(aHandle);
1983 : }
1984 :
1985 : // Check again, OpenNSPRHandle could figure out the file was gone.
1986 5 : if (!aHandle->mFileExists) {
1987 0 : NS_WARNING("Trying to read from non-existent file");
1988 0 : return NS_ERROR_NOT_AVAILABLE;
1989 : }
1990 :
1991 5 : int64_t offset = PR_Seek64(aHandle->mFD, aOffset, PR_SEEK_SET);
1992 5 : if (offset == -1) {
1993 0 : return NS_ERROR_FAILURE;
1994 : }
1995 :
1996 5 : int32_t bytesRead = PR_Read(aHandle->mFD, aBuf, aCount);
1997 5 : if (bytesRead != aCount) {
1998 0 : return NS_ERROR_FAILURE;
1999 : }
2000 :
2001 5 : return NS_OK;
2002 : }
2003 :
2004 : // static
2005 : nsresult
2006 6 : CacheFileIOManager::Write(CacheFileHandle *aHandle, int64_t aOffset,
2007 : const char *aBuf, int32_t aCount, bool aValidate,
2008 : bool aTruncate, CacheFileIOListener *aCallback)
2009 : {
2010 6 : LOG(("CacheFileIOManager::Write() [handle=%p, offset=%" PRId64 ", count=%d, "
2011 : "validate=%d, truncate=%d, listener=%p]", aHandle, aOffset, aCount,
2012 : aValidate, aTruncate, aCallback));
2013 :
2014 : nsresult rv;
2015 12 : RefPtr<CacheFileIOManager> ioMan = gInstance;
2016 :
2017 6 : if (aHandle->IsClosed() || (aCallback && aCallback->IsKilled()) || !ioMan) {
2018 0 : if (!aCallback) {
2019 : // When no callback is provided, CacheFileIOManager is responsible for
2020 : // releasing the buffer. We must release it even in case of failure.
2021 0 : free(const_cast<char *>(aBuf));
2022 : }
2023 0 : return NS_ERROR_NOT_INITIALIZED;
2024 : }
2025 :
2026 : RefPtr<WriteEvent> ev = new WriteEvent(aHandle, aOffset, aBuf, aCount,
2027 12 : aValidate, aTruncate, aCallback);
2028 12 : rv = ioMan->mIOThread->Dispatch(ev, aHandle->mPriority
2029 : ? CacheIOThread::WRITE_PRIORITY
2030 12 : : CacheIOThread::WRITE);
2031 6 : NS_ENSURE_SUCCESS(rv, rv);
2032 :
2033 6 : return NS_OK;
2034 : }
2035 :
2036 : static nsresult
2037 4 : TruncFile(PRFileDesc *aFD, int64_t aEOF)
2038 : {
2039 : #if defined(XP_UNIX)
2040 4 : if (ftruncate(PR_FileDesc2NativeHandle(aFD), aEOF) != 0) {
2041 0 : NS_ERROR("ftruncate failed");
2042 0 : return NS_ERROR_FAILURE;
2043 : }
2044 : #elif defined(XP_WIN)
2045 : int64_t cnt = PR_Seek64(aFD, aEOF, PR_SEEK_SET);
2046 : if (cnt == -1) {
2047 : return NS_ERROR_FAILURE;
2048 : }
2049 : if (!SetEndOfFile((HANDLE) PR_FileDesc2NativeHandle(aFD))) {
2050 : NS_ERROR("SetEndOfFile failed");
2051 : return NS_ERROR_FAILURE;
2052 : }
2053 : #else
2054 : MOZ_ASSERT(false, "Not implemented!");
2055 : return NS_ERROR_NOT_IMPLEMENTED;
2056 : #endif
2057 :
2058 4 : return NS_OK;
2059 : }
2060 :
2061 : nsresult
2062 6 : CacheFileIOManager::WriteInternal(CacheFileHandle *aHandle, int64_t aOffset,
2063 : const char *aBuf, int32_t aCount,
2064 : bool aValidate, bool aTruncate)
2065 : {
2066 6 : LOG(("CacheFileIOManager::WriteInternal() [handle=%p, offset=%" PRId64 ", count=%d, "
2067 : "validate=%d, truncate=%d]", aHandle, aOffset, aCount, aValidate,
2068 : aTruncate));
2069 :
2070 : nsresult rv;
2071 :
2072 6 : if (aHandle->mKilled) {
2073 0 : LOG((" handle already killed, nothing written"));
2074 0 : return NS_OK;
2075 : }
2076 :
2077 6 : if (CacheObserver::ShuttingDown() && (!aValidate || !aHandle->mFD)) {
2078 0 : aHandle->mKilled = true;
2079 0 : LOG((" killing the handle, nothing written"));
2080 0 : return NS_OK;
2081 : }
2082 :
2083 6 : if (CacheObserver::IsPastShutdownIOLag()) {
2084 0 : LOG((" past the shutdown I/O lag, nothing written"));
2085 : // Pretend the write has succeeded, otherwise upper layers will doom
2086 : // the file and we end up with I/O anyway.
2087 0 : return NS_OK;
2088 : }
2089 :
2090 12 : CacheIOThread::Cancelable cancelable(!aHandle->IsSpecialFile());
2091 :
2092 6 : if (!aHandle->mFileExists) {
2093 2 : rv = CreateFile(aHandle);
2094 2 : NS_ENSURE_SUCCESS(rv, rv);
2095 : }
2096 :
2097 6 : if (!aHandle->mFD) {
2098 0 : rv = OpenNSPRHandle(aHandle);
2099 0 : NS_ENSURE_SUCCESS(rv, rv);
2100 : } else {
2101 6 : NSPRHandleUsed(aHandle);
2102 : }
2103 :
2104 : // Check again, OpenNSPRHandle could figure out the file was gone.
2105 6 : if (!aHandle->mFileExists) {
2106 0 : return NS_ERROR_NOT_AVAILABLE;
2107 : }
2108 :
2109 : // When this operation would increase cache size, check whether the cache size
2110 : // reached the hard limit and whether it would cause critical low disk space.
2111 6 : if (aHandle->mFileSize < aOffset + aCount) {
2112 4 : if (mOverLimitEvicting && mCacheSizeOnHardLimit) {
2113 0 : LOG(("CacheFileIOManager::WriteInternal() - failing because cache size "
2114 : "reached hard limit!"));
2115 0 : return NS_ERROR_FILE_DISK_FULL;
2116 : }
2117 :
2118 4 : int64_t freeSpace = -1;
2119 4 : rv = mCacheDirectory->GetDiskSpaceAvailable(&freeSpace);
2120 4 : if (NS_WARN_IF(NS_FAILED(rv))) {
2121 0 : LOG(("CacheFileIOManager::WriteInternal() - GetDiskSpaceAvailable() "
2122 : "failed! [rv=0x%08" PRIx32 "]", static_cast<uint32_t>(rv)));
2123 : } else {
2124 4 : uint32_t limit = CacheObserver::DiskFreeSpaceHardLimit();
2125 4 : if (freeSpace - aOffset - aCount + aHandle->mFileSize < limit) {
2126 0 : LOG(("CacheFileIOManager::WriteInternal() - Low free space, refusing "
2127 : "to write! [freeSpace=%" PRId64 ", limit=%u]", freeSpace, limit));
2128 0 : return NS_ERROR_FILE_DISK_FULL;
2129 : }
2130 : }
2131 : }
2132 :
2133 : // Write invalidates the entry by default
2134 6 : aHandle->mInvalid = true;
2135 :
2136 6 : int64_t offset = PR_Seek64(aHandle->mFD, aOffset, PR_SEEK_SET);
2137 6 : if (offset == -1) {
2138 0 : return NS_ERROR_FAILURE;
2139 : }
2140 :
2141 6 : int32_t bytesWritten = PR_Write(aHandle->mFD, aBuf, aCount);
2142 :
2143 6 : if (bytesWritten != -1) {
2144 6 : uint32_t oldSizeInK = aHandle->FileSizeInK();
2145 6 : int64_t writeEnd = aOffset + bytesWritten;
2146 :
2147 6 : if (aTruncate) {
2148 4 : rv = TruncFile(aHandle->mFD, writeEnd);
2149 4 : NS_ENSURE_SUCCESS(rv, rv);
2150 :
2151 4 : aHandle->mFileSize = writeEnd;
2152 : } else {
2153 2 : if (aHandle->mFileSize < writeEnd) {
2154 2 : aHandle->mFileSize = writeEnd;
2155 : }
2156 : }
2157 :
2158 6 : uint32_t newSizeInK = aHandle->FileSizeInK();
2159 :
2160 9 : if (oldSizeInK != newSizeInK && !aHandle->IsDoomed() &&
2161 3 : !aHandle->IsSpecialFile()) {
2162 3 : CacheIndex::UpdateEntry(aHandle->Hash(), nullptr, nullptr, nullptr,
2163 3 : nullptr, nullptr, &newSizeInK);
2164 :
2165 3 : if (oldSizeInK < newSizeInK) {
2166 3 : EvictIfOverLimitInternal();
2167 : }
2168 : }
2169 : }
2170 :
2171 6 : if (bytesWritten != aCount) {
2172 0 : return NS_ERROR_FAILURE;
2173 : }
2174 :
2175 : // Write was successful and this write validates the entry (i.e. metadata)
2176 6 : if (aValidate) {
2177 4 : aHandle->mInvalid = false;
2178 : }
2179 :
2180 6 : return NS_OK;
2181 : }
2182 :
2183 : // static
2184 : nsresult
2185 0 : CacheFileIOManager::DoomFile(CacheFileHandle *aHandle,
2186 : CacheFileIOListener *aCallback)
2187 : {
2188 0 : LOG(("CacheFileIOManager::DoomFile() [handle=%p, listener=%p]",
2189 : aHandle, aCallback));
2190 :
2191 : nsresult rv;
2192 0 : RefPtr<CacheFileIOManager> ioMan = gInstance;
2193 :
2194 0 : if (aHandle->IsClosed() || !ioMan) {
2195 0 : return NS_ERROR_NOT_INITIALIZED;
2196 : }
2197 :
2198 0 : RefPtr<DoomFileEvent> ev = new DoomFileEvent(aHandle, aCallback);
2199 0 : rv = ioMan->mIOThread->Dispatch(ev, aHandle->IsPriority()
2200 : ? CacheIOThread::OPEN_PRIORITY
2201 0 : : CacheIOThread::OPEN);
2202 0 : NS_ENSURE_SUCCESS(rv, rv);
2203 :
2204 0 : return NS_OK;
2205 : }
2206 :
2207 : nsresult
2208 0 : CacheFileIOManager::DoomFileInternal(CacheFileHandle *aHandle,
2209 : PinningDoomRestriction aPinningDoomRestriction)
2210 : {
2211 0 : LOG(("CacheFileIOManager::DoomFileInternal() [handle=%p]", aHandle));
2212 0 : aHandle->Log();
2213 :
2214 0 : MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
2215 :
2216 : nsresult rv;
2217 :
2218 0 : if (aHandle->IsDoomed()) {
2219 0 : return NS_OK;
2220 : }
2221 :
2222 0 : CacheIOThread::Cancelable cancelable(!aHandle->IsSpecialFile());
2223 :
2224 0 : if (aPinningDoomRestriction > NO_RESTRICTION) {
2225 0 : switch (aHandle->mPinning) {
2226 : case CacheFileHandle::PinningStatus::NON_PINNED:
2227 0 : if (MOZ_LIKELY(aPinningDoomRestriction != DOOM_WHEN_NON_PINNED)) {
2228 0 : LOG((" not dooming, it's a non-pinned handle"));
2229 0 : return NS_OK;
2230 : }
2231 : // Doom now
2232 0 : break;
2233 :
2234 : case CacheFileHandle::PinningStatus::PINNED:
2235 0 : if (MOZ_UNLIKELY(aPinningDoomRestriction != DOOM_WHEN_PINNED)) {
2236 0 : LOG((" not dooming, it's a pinned handle"));
2237 0 : return NS_OK;
2238 : }
2239 : // Doom now
2240 0 : break;
2241 :
2242 : case CacheFileHandle::PinningStatus::UNKNOWN:
2243 0 : if (MOZ_LIKELY(aPinningDoomRestriction == DOOM_WHEN_NON_PINNED)) {
2244 0 : LOG((" doom when non-pinned set"));
2245 0 : aHandle->mDoomWhenFoundNonPinned = true;
2246 0 : } else if (MOZ_UNLIKELY(aPinningDoomRestriction == DOOM_WHEN_PINNED)) {
2247 0 : LOG((" doom when pinned set"));
2248 0 : aHandle->mDoomWhenFoundPinned = true;
2249 : }
2250 :
2251 0 : LOG((" pinning status not known, deferring doom decision"));
2252 0 : return NS_OK;
2253 : }
2254 : }
2255 :
2256 0 : if (aHandle->mFileExists) {
2257 : // we need to move the current file to the doomed directory
2258 0 : rv = MaybeReleaseNSPRHandleInternal(aHandle, true);
2259 0 : NS_ENSURE_SUCCESS(rv, rv);
2260 :
2261 : // find unused filename
2262 0 : nsCOMPtr<nsIFile> file;
2263 0 : rv = GetDoomedFile(getter_AddRefs(file));
2264 0 : NS_ENSURE_SUCCESS(rv, rv);
2265 :
2266 0 : nsCOMPtr<nsIFile> parentDir;
2267 0 : rv = file->GetParent(getter_AddRefs(parentDir));
2268 0 : NS_ENSURE_SUCCESS(rv, rv);
2269 :
2270 0 : nsAutoCString leafName;
2271 0 : rv = file->GetNativeLeafName(leafName);
2272 0 : NS_ENSURE_SUCCESS(rv, rv);
2273 :
2274 0 : rv = aHandle->mFile->MoveToNative(parentDir, leafName);
2275 0 : if (NS_ERROR_FILE_NOT_FOUND == rv || NS_ERROR_FILE_TARGET_DOES_NOT_EXIST == rv) {
2276 0 : LOG((" file already removed under our hands"));
2277 0 : aHandle->mFileExists = false;
2278 0 : rv = NS_OK;
2279 : } else {
2280 0 : NS_ENSURE_SUCCESS(rv, rv);
2281 0 : aHandle->mFile.swap(file);
2282 : }
2283 : }
2284 :
2285 0 : if (!aHandle->IsSpecialFile()) {
2286 0 : CacheIndex::RemoveEntry(aHandle->Hash());
2287 : }
2288 :
2289 0 : aHandle->mIsDoomed = true;
2290 :
2291 0 : if (!aHandle->IsSpecialFile()) {
2292 0 : RefPtr<CacheStorageService> storageService = CacheStorageService::Self();
2293 0 : if (storageService) {
2294 0 : nsAutoCString idExtension, url;
2295 : nsCOMPtr<nsILoadContextInfo> info =
2296 0 : CacheFileUtils::ParseKey(aHandle->Key(), &idExtension, &url);
2297 0 : MOZ_ASSERT(info);
2298 0 : if (info) {
2299 0 : storageService->CacheFileDoomed(info, idExtension, url);
2300 : }
2301 : }
2302 : }
2303 :
2304 0 : return NS_OK;
2305 : }
2306 :
2307 : // static
2308 : nsresult
2309 1 : CacheFileIOManager::DoomFileByKey(const nsACString &aKey,
2310 : CacheFileIOListener *aCallback)
2311 : {
2312 1 : LOG(("CacheFileIOManager::DoomFileByKey() [key=%s, listener=%p]",
2313 : PromiseFlatCString(aKey).get(), aCallback));
2314 :
2315 : nsresult rv;
2316 2 : RefPtr<CacheFileIOManager> ioMan = gInstance;
2317 :
2318 1 : if (!ioMan) {
2319 0 : return NS_ERROR_NOT_INITIALIZED;
2320 : }
2321 :
2322 2 : RefPtr<DoomFileByKeyEvent> ev = new DoomFileByKeyEvent(aKey, aCallback);
2323 1 : rv = ioMan->mIOThread->DispatchAfterPendingOpens(ev);
2324 1 : NS_ENSURE_SUCCESS(rv, rv);
2325 :
2326 1 : return NS_OK;
2327 : }
2328 :
2329 : nsresult
2330 1 : CacheFileIOManager::DoomFileByKeyInternal(const SHA1Sum::Hash *aHash)
2331 : {
2332 1 : LOG(("CacheFileIOManager::DoomFileByKeyInternal() [hash=%08x%08x%08x%08x%08x]"
2333 : , LOGSHA1(aHash)));
2334 :
2335 1 : MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
2336 :
2337 : nsresult rv;
2338 :
2339 1 : if (mShuttingDown) {
2340 0 : return NS_ERROR_NOT_INITIALIZED;
2341 : }
2342 :
2343 1 : if (!mCacheDirectory) {
2344 0 : return NS_ERROR_FILE_INVALID_PATH;
2345 : }
2346 :
2347 : // Find active handle
2348 2 : RefPtr<CacheFileHandle> handle;
2349 1 : mHandles.GetHandle(aHash, getter_AddRefs(handle));
2350 :
2351 1 : if (handle) {
2352 0 : handle->Log();
2353 :
2354 0 : return DoomFileInternal(handle);
2355 : }
2356 :
2357 2 : CacheIOThread::Cancelable cancelable(true);
2358 :
2359 : // There is no handle for this file, delete the file if exists
2360 2 : nsCOMPtr<nsIFile> file;
2361 1 : rv = GetFile(aHash, getter_AddRefs(file));
2362 1 : NS_ENSURE_SUCCESS(rv, rv);
2363 :
2364 : bool exists;
2365 1 : rv = file->Exists(&exists);
2366 1 : NS_ENSURE_SUCCESS(rv, rv);
2367 :
2368 1 : if (!exists) {
2369 1 : return NS_ERROR_NOT_AVAILABLE;
2370 : }
2371 :
2372 0 : LOG(("CacheFileIOManager::DoomFileByKeyInternal() - Removing file from "
2373 : "disk"));
2374 0 : rv = file->Remove(false);
2375 0 : if (NS_FAILED(rv)) {
2376 0 : NS_WARNING("Cannot remove old entry from the disk");
2377 0 : LOG(("CacheFileIOManager::DoomFileByKeyInternal() - Removing file failed. "
2378 : "[rv=0x%08" PRIx32 "]", static_cast<uint32_t>(rv)));
2379 : }
2380 :
2381 0 : CacheIndex::RemoveEntry(aHash);
2382 :
2383 0 : return NS_OK;
2384 : }
2385 :
2386 : // static
2387 : nsresult
2388 4 : CacheFileIOManager::ReleaseNSPRHandle(CacheFileHandle *aHandle)
2389 : {
2390 4 : LOG(("CacheFileIOManager::ReleaseNSPRHandle() [handle=%p]", aHandle));
2391 :
2392 : nsresult rv;
2393 8 : RefPtr<CacheFileIOManager> ioMan = gInstance;
2394 :
2395 4 : if (aHandle->IsClosed() || !ioMan) {
2396 0 : return NS_ERROR_NOT_INITIALIZED;
2397 : }
2398 :
2399 8 : RefPtr<ReleaseNSPRHandleEvent> ev = new ReleaseNSPRHandleEvent(aHandle);
2400 8 : rv = ioMan->mIOThread->Dispatch(ev, aHandle->mPriority
2401 : ? CacheIOThread::WRITE_PRIORITY
2402 8 : : CacheIOThread::WRITE);
2403 4 : NS_ENSURE_SUCCESS(rv, rv);
2404 :
2405 4 : return NS_OK;
2406 : }
2407 :
2408 : nsresult
2409 4 : CacheFileIOManager::MaybeReleaseNSPRHandleInternal(CacheFileHandle *aHandle,
2410 : bool aIgnoreShutdownLag)
2411 : {
2412 4 : LOG(("CacheFileIOManager::MaybeReleaseNSPRHandleInternal() [handle=%p, ignore shutdown=%d]",
2413 : aHandle, aIgnoreShutdownLag));
2414 :
2415 4 : MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
2416 :
2417 4 : if (aHandle->mFD) {
2418 8 : DebugOnly<bool> found;
2419 4 : found = mHandlesByLastUsed.RemoveElement(aHandle);
2420 4 : MOZ_ASSERT(found);
2421 : }
2422 :
2423 4 : PRFileDesc *fd = aHandle->mFD;
2424 4 : aHandle->mFD = nullptr;
2425 :
2426 : // Leak invalid (w/o metadata) and doomed handles immediately after shutdown.
2427 : // Leak other handles when past the shutdown time maximum lag.
2428 8 : if (
2429 : #ifndef DEBUG
2430 : ((aHandle->mInvalid || aHandle->mIsDoomed) &&
2431 : MOZ_UNLIKELY(CacheObserver::ShuttingDown())) ||
2432 : #endif
2433 8 : MOZ_UNLIKELY(!aIgnoreShutdownLag &&
2434 : CacheObserver::IsPastShutdownIOLag())) {
2435 : // Don't bother closing this file. Return a failure code from here will
2436 : // cause any following IO operation on the file (mainly removal) to be
2437 : // bypassed, which is what we want.
2438 : // For mInvalid == true the entry will never be used, since it doesn't
2439 : // have correct metadata, thus we don't need to worry about removing it.
2440 : // For mIsDoomed == true the file is already in the doomed sub-dir and
2441 : // will be removed on next session start.
2442 0 : LOG((" past the shutdown I/O lag, leaking file handle"));
2443 0 : return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
2444 : }
2445 :
2446 4 : if (!fd) {
2447 : // The filedesc has already been closed before, just let go.
2448 0 : return NS_OK;
2449 : }
2450 :
2451 8 : CacheIOThread::Cancelable cancelable(!aHandle->IsSpecialFile());
2452 :
2453 4 : PRStatus status = PR_Close(fd);
2454 4 : if (status != PR_SUCCESS) {
2455 0 : LOG(("CacheFileIOManager::MaybeReleaseNSPRHandleInternal() "
2456 : "failed to close [handle=%p, status=%u]", aHandle, status));
2457 0 : return NS_ERROR_FAILURE;
2458 : }
2459 :
2460 4 : LOG(("CacheFileIOManager::MaybeReleaseNSPRHandleInternal() DONE"));
2461 :
2462 4 : return NS_OK;
2463 : }
2464 :
2465 : // static
2466 : nsresult
2467 0 : CacheFileIOManager::TruncateSeekSetEOF(CacheFileHandle *aHandle,
2468 : int64_t aTruncatePos, int64_t aEOFPos,
2469 : CacheFileIOListener *aCallback)
2470 : {
2471 0 : LOG(("CacheFileIOManager::TruncateSeekSetEOF() [handle=%p, truncatePos=%" PRId64 ", "
2472 : "EOFPos=%" PRId64 ", listener=%p]", aHandle, aTruncatePos, aEOFPos, aCallback));
2473 :
2474 : nsresult rv;
2475 0 : RefPtr<CacheFileIOManager> ioMan = gInstance;
2476 :
2477 0 : if (aHandle->IsClosed() || (aCallback && aCallback->IsKilled()) || !ioMan) {
2478 0 : return NS_ERROR_NOT_INITIALIZED;
2479 : }
2480 :
2481 : RefPtr<TruncateSeekSetEOFEvent> ev = new TruncateSeekSetEOFEvent(
2482 : aHandle, aTruncatePos, aEOFPos,
2483 0 : aCallback);
2484 0 : rv = ioMan->mIOThread->Dispatch(ev, aHandle->mPriority
2485 : ? CacheIOThread::WRITE_PRIORITY
2486 0 : : CacheIOThread::WRITE);
2487 0 : NS_ENSURE_SUCCESS(rv, rv);
2488 :
2489 0 : return NS_OK;
2490 : }
2491 :
2492 : // static
2493 1 : void CacheFileIOManager::GetCacheDirectory(nsIFile** result)
2494 : {
2495 1 : *result = nullptr;
2496 :
2497 2 : RefPtr<CacheFileIOManager> ioMan = gInstance;
2498 1 : if (!ioMan || !ioMan->mCacheDirectory) {
2499 0 : return;
2500 : }
2501 :
2502 1 : ioMan->mCacheDirectory->Clone(result);
2503 : }
2504 :
2505 : #if defined(MOZ_WIDGET_ANDROID)
2506 :
2507 : // static
2508 : void CacheFileIOManager::GetProfilelessCacheDirectory(nsIFile** result)
2509 : {
2510 : *result = nullptr;
2511 :
2512 : RefPtr<CacheFileIOManager> ioMan = gInstance;
2513 : if (!ioMan || !ioMan->mCacheProfilelessDirectory) {
2514 : return;
2515 : }
2516 :
2517 : ioMan->mCacheProfilelessDirectory->Clone(result);
2518 : }
2519 :
2520 : #endif
2521 :
2522 : // static
2523 : nsresult
2524 0 : CacheFileIOManager::GetEntryInfo(const SHA1Sum::Hash *aHash,
2525 : CacheStorageService::EntryInfoCallback *aCallback)
2526 : {
2527 0 : MOZ_ASSERT(CacheFileIOManager::IsOnIOThread());
2528 :
2529 : nsresult rv;
2530 :
2531 0 : RefPtr<CacheFileIOManager> ioMan = gInstance;
2532 0 : if (!ioMan) {
2533 0 : return NS_ERROR_NOT_INITIALIZED;
2534 : }
2535 :
2536 0 : nsAutoCString enhanceId;
2537 0 : nsAutoCString uriSpec;
2538 :
2539 0 : RefPtr<CacheFileHandle> handle;
2540 0 : ioMan->mHandles.GetHandle(aHash, getter_AddRefs(handle));
2541 0 : if (handle) {
2542 : RefPtr<nsILoadContextInfo> info =
2543 0 : CacheFileUtils::ParseKey(handle->Key(), &enhanceId, &uriSpec);
2544 :
2545 0 : MOZ_ASSERT(info);
2546 0 : if (!info) {
2547 0 : return NS_OK; // ignore
2548 : }
2549 :
2550 0 : RefPtr<CacheStorageService> service = CacheStorageService::Self();
2551 0 : if (!service) {
2552 0 : return NS_ERROR_NOT_INITIALIZED;
2553 : }
2554 :
2555 : // Invokes OnCacheEntryInfo when an existing entry is found
2556 0 : if (service->GetCacheEntryInfo(info, enhanceId, uriSpec, aCallback)) {
2557 0 : return NS_OK;
2558 : }
2559 :
2560 : // When we are here, there is no existing entry and we need
2561 : // to synchrnously load metadata from a disk file.
2562 : }
2563 :
2564 : // Locate the actual file
2565 0 : nsCOMPtr<nsIFile> file;
2566 0 : ioMan->GetFile(aHash, getter_AddRefs(file));
2567 :
2568 : // Read metadata from the file synchronously
2569 0 : RefPtr<CacheFileMetadata> metadata = new CacheFileMetadata();
2570 0 : rv = metadata->SyncReadMetadata(file);
2571 0 : if (NS_FAILED(rv)) {
2572 0 : return NS_OK;
2573 : }
2574 :
2575 : // Now get the context + enhance id + URL from the key.
2576 0 : nsAutoCString key;
2577 0 : metadata->GetKey(key);
2578 :
2579 : RefPtr<nsILoadContextInfo> info =
2580 0 : CacheFileUtils::ParseKey(key, &enhanceId, &uriSpec);
2581 0 : MOZ_ASSERT(info);
2582 0 : if (!info) {
2583 0 : return NS_OK;
2584 : }
2585 :
2586 : // Pick all data to pass to the callback.
2587 0 : int64_t dataSize = metadata->Offset();
2588 : uint32_t fetchCount;
2589 0 : if (NS_FAILED(metadata->GetFetchCount(&fetchCount))) {
2590 0 : fetchCount = 0;
2591 : }
2592 : uint32_t expirationTime;
2593 0 : if (NS_FAILED(metadata->GetExpirationTime(&expirationTime))) {
2594 0 : expirationTime = 0;
2595 : }
2596 : uint32_t lastModified;
2597 0 : if (NS_FAILED(metadata->GetLastModified(&lastModified))) {
2598 0 : lastModified = 0;
2599 : }
2600 :
2601 : // Call directly on the callback.
2602 0 : aCallback->OnEntryInfo(uriSpec, enhanceId, dataSize, fetchCount,
2603 0 : lastModified, expirationTime, metadata->Pinned(),
2604 0 : info);
2605 :
2606 0 : return NS_OK;
2607 : }
2608 :
2609 : nsresult
2610 0 : CacheFileIOManager::TruncateSeekSetEOFInternal(CacheFileHandle *aHandle,
2611 : int64_t aTruncatePos,
2612 : int64_t aEOFPos)
2613 : {
2614 0 : LOG(("CacheFileIOManager::TruncateSeekSetEOFInternal() [handle=%p, "
2615 : "truncatePos=%" PRId64 ", EOFPos=%" PRId64 "]", aHandle, aTruncatePos, aEOFPos));
2616 :
2617 : nsresult rv;
2618 :
2619 0 : if (aHandle->mKilled) {
2620 0 : LOG((" handle already killed, file not truncated"));
2621 0 : return NS_OK;
2622 : }
2623 :
2624 0 : if (CacheObserver::ShuttingDown() && !aHandle->mFD) {
2625 0 : aHandle->mKilled = true;
2626 0 : LOG((" killing the handle, file not truncated"));
2627 0 : return NS_OK;
2628 : }
2629 :
2630 0 : CacheIOThread::Cancelable cancelable(!aHandle->IsSpecialFile());
2631 :
2632 0 : if (!aHandle->mFileExists) {
2633 0 : rv = CreateFile(aHandle);
2634 0 : NS_ENSURE_SUCCESS(rv, rv);
2635 : }
2636 :
2637 0 : if (!aHandle->mFD) {
2638 0 : rv = OpenNSPRHandle(aHandle);
2639 0 : NS_ENSURE_SUCCESS(rv, rv);
2640 : } else {
2641 0 : NSPRHandleUsed(aHandle);
2642 : }
2643 :
2644 : // Check again, OpenNSPRHandle could figure out the file was gone.
2645 0 : if (!aHandle->mFileExists) {
2646 0 : return NS_ERROR_NOT_AVAILABLE;
2647 : }
2648 :
2649 : // When this operation would increase cache size, check whether the cache size
2650 : // reached the hard limit and whether it would cause critical low disk space.
2651 0 : if (aHandle->mFileSize < aEOFPos) {
2652 0 : if (mOverLimitEvicting && mCacheSizeOnHardLimit) {
2653 0 : LOG(("CacheFileIOManager::TruncateSeekSetEOFInternal() - failing because "
2654 : "cache size reached hard limit!"));
2655 0 : return NS_ERROR_FILE_DISK_FULL;
2656 : }
2657 :
2658 0 : int64_t freeSpace = -1;
2659 0 : rv = mCacheDirectory->GetDiskSpaceAvailable(&freeSpace);
2660 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2661 0 : LOG(("CacheFileIOManager::TruncateSeekSetEOFInternal() - "
2662 : "GetDiskSpaceAvailable() failed! [rv=0x%08" PRIx32 "]",
2663 : static_cast<uint32_t>(rv)));
2664 : } else {
2665 0 : uint32_t limit = CacheObserver::DiskFreeSpaceHardLimit();
2666 0 : if (freeSpace - aEOFPos + aHandle->mFileSize < limit) {
2667 0 : LOG(("CacheFileIOManager::TruncateSeekSetEOFInternal() - Low free space"
2668 : ", refusing to write! [freeSpace=%" PRId64 ", limit=%u]", freeSpace,
2669 : limit));
2670 0 : return NS_ERROR_FILE_DISK_FULL;
2671 : }
2672 : }
2673 : }
2674 :
2675 : // This operation always invalidates the entry
2676 0 : aHandle->mInvalid = true;
2677 :
2678 0 : rv = TruncFile(aHandle->mFD, aTruncatePos);
2679 0 : NS_ENSURE_SUCCESS(rv, rv);
2680 :
2681 0 : if (aTruncatePos != aEOFPos) {
2682 0 : rv = TruncFile(aHandle->mFD, aEOFPos);
2683 0 : NS_ENSURE_SUCCESS(rv, rv);
2684 : }
2685 :
2686 0 : uint32_t oldSizeInK = aHandle->FileSizeInK();
2687 0 : aHandle->mFileSize = aEOFPos;
2688 0 : uint32_t newSizeInK = aHandle->FileSizeInK();
2689 :
2690 0 : if (oldSizeInK != newSizeInK && !aHandle->IsDoomed() &&
2691 0 : !aHandle->IsSpecialFile()) {
2692 0 : CacheIndex::UpdateEntry(aHandle->Hash(), nullptr, nullptr, nullptr, nullptr,
2693 0 : nullptr, &newSizeInK);
2694 :
2695 0 : if (oldSizeInK < newSizeInK) {
2696 0 : EvictIfOverLimitInternal();
2697 : }
2698 : }
2699 :
2700 0 : return NS_OK;
2701 : }
2702 :
2703 : // static
2704 : nsresult
2705 0 : CacheFileIOManager::RenameFile(CacheFileHandle *aHandle,
2706 : const nsACString &aNewName,
2707 : CacheFileIOListener *aCallback)
2708 : {
2709 0 : LOG(("CacheFileIOManager::RenameFile() [handle=%p, newName=%s, listener=%p]",
2710 : aHandle, PromiseFlatCString(aNewName).get(), aCallback));
2711 :
2712 : nsresult rv;
2713 0 : RefPtr<CacheFileIOManager> ioMan = gInstance;
2714 :
2715 0 : if (aHandle->IsClosed() || !ioMan) {
2716 0 : return NS_ERROR_NOT_INITIALIZED;
2717 : }
2718 :
2719 0 : if (!aHandle->IsSpecialFile()) {
2720 0 : return NS_ERROR_UNEXPECTED;
2721 : }
2722 :
2723 : RefPtr<RenameFileEvent> ev = new RenameFileEvent(aHandle, aNewName,
2724 0 : aCallback);
2725 0 : rv = ioMan->mIOThread->Dispatch(ev, aHandle->mPriority
2726 : ? CacheIOThread::WRITE_PRIORITY
2727 0 : : CacheIOThread::WRITE);
2728 0 : NS_ENSURE_SUCCESS(rv, rv);
2729 :
2730 0 : return NS_OK;
2731 : }
2732 :
2733 : nsresult
2734 0 : CacheFileIOManager::RenameFileInternal(CacheFileHandle *aHandle,
2735 : const nsACString &aNewName)
2736 : {
2737 0 : LOG(("CacheFileIOManager::RenameFileInternal() [handle=%p, newName=%s]",
2738 : aHandle, PromiseFlatCString(aNewName).get()));
2739 :
2740 : nsresult rv;
2741 :
2742 0 : MOZ_ASSERT(aHandle->IsSpecialFile());
2743 :
2744 0 : if (aHandle->IsDoomed()) {
2745 0 : return NS_ERROR_NOT_AVAILABLE;
2746 : }
2747 :
2748 : // Doom old handle if it exists and is not doomed
2749 0 : for (uint32_t i = 0 ; i < mSpecialHandles.Length() ; i++) {
2750 0 : if (!mSpecialHandles[i]->IsDoomed() &&
2751 0 : mSpecialHandles[i]->Key() == aNewName) {
2752 0 : MOZ_ASSERT(aHandle != mSpecialHandles[i]);
2753 0 : rv = DoomFileInternal(mSpecialHandles[i]);
2754 0 : NS_ENSURE_SUCCESS(rv, rv);
2755 0 : break;
2756 : }
2757 : }
2758 :
2759 0 : nsCOMPtr<nsIFile> file;
2760 0 : rv = GetSpecialFile(aNewName, getter_AddRefs(file));
2761 0 : NS_ENSURE_SUCCESS(rv, rv);
2762 :
2763 : bool exists;
2764 0 : rv = file->Exists(&exists);
2765 0 : NS_ENSURE_SUCCESS(rv, rv);
2766 :
2767 0 : if (exists) {
2768 0 : LOG(("CacheFileIOManager::RenameFileInternal() - Removing old file from "
2769 : "disk"));
2770 0 : rv = file->Remove(false);
2771 0 : if (NS_FAILED(rv)) {
2772 0 : NS_WARNING("Cannot remove file from the disk");
2773 0 : LOG(("CacheFileIOManager::RenameFileInternal() - Removing old file failed"
2774 : ". [rv=0x%08" PRIx32 "]", static_cast<uint32_t>(rv)));
2775 : }
2776 : }
2777 :
2778 0 : if (!aHandle->FileExists()) {
2779 0 : aHandle->mKey = aNewName;
2780 0 : return NS_OK;
2781 : }
2782 :
2783 0 : rv = MaybeReleaseNSPRHandleInternal(aHandle, true);
2784 0 : NS_ENSURE_SUCCESS(rv, rv);
2785 :
2786 0 : rv = aHandle->mFile->MoveToNative(nullptr, aNewName);
2787 0 : NS_ENSURE_SUCCESS(rv, rv);
2788 :
2789 0 : aHandle->mKey = aNewName;
2790 0 : return NS_OK;
2791 : }
2792 :
2793 : // static
2794 : nsresult
2795 1 : CacheFileIOManager::EvictIfOverLimit()
2796 : {
2797 1 : LOG(("CacheFileIOManager::EvictIfOverLimit()"));
2798 :
2799 : nsresult rv;
2800 2 : RefPtr<CacheFileIOManager> ioMan = gInstance;
2801 :
2802 1 : if (!ioMan) {
2803 0 : return NS_ERROR_NOT_INITIALIZED;
2804 : }
2805 :
2806 2 : nsCOMPtr<nsIRunnable> ev;
2807 2 : ev = NewRunnableMethod("net::CacheFileIOManager::EvictIfOverLimitInternal",
2808 : ioMan,
2809 1 : &CacheFileIOManager::EvictIfOverLimitInternal);
2810 :
2811 1 : rv = ioMan->mIOThread->Dispatch(ev, CacheIOThread::EVICT);
2812 1 : NS_ENSURE_SUCCESS(rv, rv);
2813 :
2814 1 : return NS_OK;
2815 : }
2816 :
2817 : nsresult
2818 4 : CacheFileIOManager::EvictIfOverLimitInternal()
2819 : {
2820 4 : LOG(("CacheFileIOManager::EvictIfOverLimitInternal()"));
2821 :
2822 : nsresult rv;
2823 :
2824 4 : MOZ_ASSERT(mIOThread->IsCurrentThread());
2825 :
2826 4 : if (mShuttingDown) {
2827 0 : return NS_ERROR_NOT_INITIALIZED;
2828 : }
2829 :
2830 4 : if (mOverLimitEvicting) {
2831 0 : LOG(("CacheFileIOManager::EvictIfOverLimitInternal() - Eviction already "
2832 : "running."));
2833 0 : return NS_OK;
2834 : }
2835 :
2836 8 : CacheIOThread::Cancelable cancelable(true);
2837 :
2838 : int64_t freeSpace;
2839 4 : rv = mCacheDirectory->GetDiskSpaceAvailable(&freeSpace);
2840 4 : if (NS_WARN_IF(NS_FAILED(rv))) {
2841 0 : freeSpace = -1;
2842 :
2843 : // Do not change smart size.
2844 0 : LOG(("CacheFileIOManager::EvictIfOverLimitInternal() - "
2845 : "GetDiskSpaceAvailable() failed! [rv=0x%08" PRIx32 "]",
2846 : static_cast<uint32_t>(rv)));
2847 : } else {
2848 4 : UpdateSmartCacheSize(freeSpace);
2849 : }
2850 :
2851 : uint32_t cacheUsage;
2852 4 : rv = CacheIndex::GetCacheSize(&cacheUsage);
2853 4 : NS_ENSURE_SUCCESS(rv, rv);
2854 :
2855 4 : uint32_t cacheLimit = CacheObserver::DiskCacheCapacity() >> 10;
2856 4 : uint32_t freeSpaceLimit = CacheObserver::DiskFreeSpaceSoftLimit();
2857 :
2858 8 : if (cacheUsage <= cacheLimit &&
2859 8 : (freeSpace == -1 || freeSpace >= freeSpaceLimit)) {
2860 4 : LOG(("CacheFileIOManager::EvictIfOverLimitInternal() - Cache size and free "
2861 : "space in limits. [cacheSize=%ukB, cacheSizeLimit=%ukB, "
2862 : "freeSpace=%" PRId64 ", freeSpaceLimit=%u]", cacheUsage, cacheLimit,
2863 : freeSpace, freeSpaceLimit));
2864 4 : return NS_OK;
2865 : }
2866 :
2867 0 : LOG(("CacheFileIOManager::EvictIfOverLimitInternal() - Cache size exceeded "
2868 : "limit. Starting overlimit eviction. [cacheSize=%u, limit=%u]",
2869 : cacheUsage, cacheLimit));
2870 :
2871 0 : nsCOMPtr<nsIRunnable> ev;
2872 0 : ev = NewRunnableMethod("net::CacheFileIOManager::OverLimitEvictionInternal",
2873 : this,
2874 0 : &CacheFileIOManager::OverLimitEvictionInternal);
2875 :
2876 0 : rv = mIOThread->Dispatch(ev, CacheIOThread::EVICT);
2877 0 : NS_ENSURE_SUCCESS(rv, rv);
2878 :
2879 0 : mOverLimitEvicting = true;
2880 0 : return NS_OK;
2881 : }
2882 :
2883 : nsresult
2884 0 : CacheFileIOManager::OverLimitEvictionInternal()
2885 : {
2886 0 : LOG(("CacheFileIOManager::OverLimitEvictionInternal()"));
2887 :
2888 : nsresult rv;
2889 :
2890 0 : MOZ_ASSERT(mIOThread->IsCurrentThread());
2891 :
2892 : // mOverLimitEvicting is accessed only on IO thread, so we can set it to false
2893 : // here and set it to true again once we dispatch another event that will
2894 : // continue with the eviction. The reason why we do so is that we can fail
2895 : // early anywhere in this method and the variable will contain a correct
2896 : // value. Otherwise we would need to set it to false on every failing place.
2897 0 : mOverLimitEvicting = false;
2898 :
2899 0 : if (mShuttingDown) {
2900 0 : return NS_ERROR_NOT_INITIALIZED;
2901 : }
2902 :
2903 : while (true) {
2904 0 : int64_t freeSpace = -1;
2905 0 : rv = mCacheDirectory->GetDiskSpaceAvailable(&freeSpace);
2906 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2907 : // Do not change smart size.
2908 0 : LOG(("CacheFileIOManager::EvictIfOverLimitInternal() - "
2909 : "GetDiskSpaceAvailable() failed! [rv=0x%08" PRIx32 "]",
2910 : static_cast<uint32_t>(rv)));
2911 : } else {
2912 0 : UpdateSmartCacheSize(freeSpace);
2913 : }
2914 :
2915 : uint32_t cacheUsage;
2916 0 : rv = CacheIndex::GetCacheSize(&cacheUsage);
2917 0 : NS_ENSURE_SUCCESS(rv, rv);
2918 :
2919 0 : uint32_t cacheLimit = CacheObserver::DiskCacheCapacity() >> 10;
2920 0 : uint32_t freeSpaceLimit = CacheObserver::DiskFreeSpaceSoftLimit();
2921 :
2922 0 : if (cacheUsage > cacheLimit) {
2923 0 : LOG(("CacheFileIOManager::OverLimitEvictionInternal() - Cache size over "
2924 : "limit. [cacheSize=%u, limit=%u]", cacheUsage, cacheLimit));
2925 :
2926 : // We allow cache size to go over the specified limit. Eviction should
2927 : // keep the size within the limit in a long run, but in case the eviction
2928 : // is too slow, the cache could go way over the limit. To prevent this we
2929 : // set flag mCacheSizeOnHardLimit when the size reaches 105% of the limit
2930 : // and WriteInternal() and TruncateSeekSetEOFInternal() fail to cache
2931 : // additional data.
2932 0 : if ((cacheUsage - cacheLimit) > (cacheLimit / 20)) {
2933 0 : LOG(("CacheFileIOManager::OverLimitEvictionInternal() - Cache size "
2934 : "reached hard limit."));
2935 0 : mCacheSizeOnHardLimit = true;
2936 : } else {
2937 0 : mCacheSizeOnHardLimit = false;
2938 : }
2939 0 : } else if (freeSpace != 1 && freeSpace < freeSpaceLimit) {
2940 0 : LOG(("CacheFileIOManager::OverLimitEvictionInternal() - Free space under "
2941 : "limit. [freeSpace=%" PRId64 ", freeSpaceLimit=%u]", freeSpace,
2942 : freeSpaceLimit));
2943 : } else {
2944 0 : LOG(("CacheFileIOManager::OverLimitEvictionInternal() - Cache size and "
2945 : "free space in limits. [cacheSize=%ukB, cacheSizeLimit=%ukB, "
2946 : "freeSpace=%" PRId64 ", freeSpaceLimit=%u]", cacheUsage, cacheLimit,
2947 : freeSpace, freeSpaceLimit));
2948 :
2949 0 : mCacheSizeOnHardLimit = false;
2950 0 : return NS_OK;
2951 : }
2952 :
2953 0 : if (CacheIOThread::YieldAndRerun()) {
2954 0 : LOG(("CacheFileIOManager::OverLimitEvictionInternal() - Breaking loop "
2955 : "for higher level events."));
2956 0 : mOverLimitEvicting = true;
2957 0 : return NS_OK;
2958 : }
2959 :
2960 : SHA1Sum::Hash hash;
2961 : uint32_t cnt;
2962 : static uint32_t consecutiveFailures = 0;
2963 0 : rv = CacheIndex::GetEntryForEviction(false, &hash, &cnt);
2964 0 : NS_ENSURE_SUCCESS(rv, rv);
2965 :
2966 0 : rv = DoomFileByKeyInternal(&hash);
2967 0 : if (NS_SUCCEEDED(rv)) {
2968 0 : consecutiveFailures = 0;
2969 0 : } else if (rv == NS_ERROR_NOT_AVAILABLE) {
2970 0 : LOG(("CacheFileIOManager::OverLimitEvictionInternal() - "
2971 : "DoomFileByKeyInternal() failed. [rv=0x%08" PRIx32 "]",
2972 : static_cast<uint32_t>(rv)));
2973 : // TODO index is outdated, start update
2974 :
2975 : // Make sure index won't return the same entry again
2976 0 : CacheIndex::RemoveEntry(&hash);
2977 0 : consecutiveFailures = 0;
2978 : } else {
2979 : // This shouldn't normally happen, but the eviction must not fail
2980 : // completely if we ever encounter this problem.
2981 : NS_WARNING("CacheFileIOManager::OverLimitEvictionInternal() - Unexpected "
2982 0 : "failure of DoomFileByKeyInternal()");
2983 :
2984 0 : LOG(("CacheFileIOManager::OverLimitEvictionInternal() - "
2985 : "DoomFileByKeyInternal() failed. [rv=0x%08" PRIx32 "]",
2986 : static_cast<uint32_t>(rv)));
2987 :
2988 : // Normally, CacheIndex::UpdateEntry() is called only to update newly
2989 : // created/opened entries which are always fresh and UpdateEntry() expects
2990 : // and checks this flag. The way we use UpdateEntry() here is a kind of
2991 : // hack and we must make sure the flag is set by calling
2992 : // EnsureEntryExists().
2993 0 : rv = CacheIndex::EnsureEntryExists(&hash);
2994 0 : NS_ENSURE_SUCCESS(rv, rv);
2995 :
2996 : // Move the entry at the end of both lists to make sure we won't end up
2997 : // failing on one entry forever.
2998 0 : uint32_t frecency = 0;
2999 0 : uint32_t expTime = nsICacheEntry::NO_EXPIRATION_TIME;
3000 : rv = CacheIndex::UpdateEntry(&hash, &frecency, &expTime, nullptr, nullptr,
3001 0 : nullptr, nullptr);
3002 0 : NS_ENSURE_SUCCESS(rv, rv);
3003 :
3004 0 : consecutiveFailures++;
3005 0 : if (consecutiveFailures >= cnt) {
3006 : // This doesn't necessarily mean that we've tried to doom every entry
3007 : // but we've reached a sane number of tries. It is likely that another
3008 : // eviction will start soon. And as said earlier, this normally doesn't
3009 : // happen at all.
3010 0 : return NS_OK;
3011 : }
3012 : }
3013 0 : }
3014 :
3015 : NS_NOTREACHED("We should never get here");
3016 : return NS_OK;
3017 : }
3018 :
3019 : // static
3020 : nsresult
3021 0 : CacheFileIOManager::EvictAll()
3022 : {
3023 0 : LOG(("CacheFileIOManager::EvictAll()"));
3024 :
3025 : nsresult rv;
3026 0 : RefPtr<CacheFileIOManager> ioMan = gInstance;
3027 :
3028 0 : if (!ioMan) {
3029 0 : return NS_ERROR_NOT_INITIALIZED;
3030 : }
3031 :
3032 0 : nsCOMPtr<nsIRunnable> ev;
3033 0 : ev = NewRunnableMethod("net::CacheFileIOManager::EvictAllInternal",
3034 : ioMan,
3035 0 : &CacheFileIOManager::EvictAllInternal);
3036 :
3037 0 : rv = ioMan->mIOThread->DispatchAfterPendingOpens(ev);
3038 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3039 0 : return rv;
3040 : }
3041 :
3042 0 : return NS_OK;
3043 : }
3044 :
3045 : namespace {
3046 :
3047 0 : class EvictionNotifierRunnable : public Runnable
3048 : {
3049 : public:
3050 0 : EvictionNotifierRunnable() : Runnable("EvictionNotifierRunnable") {}
3051 : NS_DECL_NSIRUNNABLE
3052 : };
3053 :
3054 : NS_IMETHODIMP
3055 0 : EvictionNotifierRunnable::Run()
3056 : {
3057 0 : nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
3058 0 : if (obsSvc) {
3059 0 : obsSvc->NotifyObservers(nullptr, "cacheservice:empty-cache", nullptr);
3060 : }
3061 0 : return NS_OK;
3062 : }
3063 :
3064 : } // namespace
3065 :
3066 : nsresult
3067 0 : CacheFileIOManager::EvictAllInternal()
3068 : {
3069 0 : LOG(("CacheFileIOManager::EvictAllInternal()"));
3070 :
3071 : nsresult rv;
3072 :
3073 0 : MOZ_ASSERT(mIOThread->IsCurrentThread());
3074 :
3075 0 : RefPtr<EvictionNotifierRunnable> r = new EvictionNotifierRunnable();
3076 :
3077 0 : if (!mCacheDirectory) {
3078 : // This is a kind of hack. Somebody called EvictAll() without a profile.
3079 : // This happens in xpcshell tests that use cache without profile. We need
3080 : // to notify observers in this case since the tests are waiting for it.
3081 0 : NS_DispatchToMainThread(r);
3082 0 : return NS_ERROR_FILE_INVALID_PATH;
3083 : }
3084 :
3085 0 : if (mShuttingDown) {
3086 0 : return NS_ERROR_NOT_INITIALIZED;
3087 : }
3088 :
3089 0 : if (!mTreeCreated) {
3090 0 : rv = CreateCacheTree();
3091 0 : if (NS_FAILED(rv)) {
3092 0 : return rv;
3093 : }
3094 : }
3095 :
3096 : // Doom all active handles
3097 0 : nsTArray<RefPtr<CacheFileHandle> > handles;
3098 0 : mHandles.GetActiveHandles(&handles);
3099 :
3100 0 : for (uint32_t i = 0; i < handles.Length(); ++i) {
3101 0 : rv = DoomFileInternal(handles[i]);
3102 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3103 0 : LOG(("CacheFileIOManager::EvictAllInternal() - Cannot doom handle "
3104 : "[handle=%p]", handles[i].get()));
3105 : }
3106 : }
3107 :
3108 0 : nsCOMPtr<nsIFile> file;
3109 0 : rv = mCacheDirectory->Clone(getter_AddRefs(file));
3110 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3111 0 : return rv;
3112 : }
3113 :
3114 0 : rv = file->AppendNative(NS_LITERAL_CSTRING(ENTRIES_DIR));
3115 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3116 0 : return rv;
3117 : }
3118 :
3119 : // Trash current entries directory
3120 0 : rv = TrashDirectory(file);
3121 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3122 0 : return rv;
3123 : }
3124 :
3125 : // Files are now inaccessible in entries directory, notify observers.
3126 0 : NS_DispatchToMainThread(r);
3127 :
3128 : // Create a new empty entries directory
3129 0 : rv = CheckAndCreateDir(mCacheDirectory, ENTRIES_DIR, false);
3130 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3131 0 : return rv;
3132 : }
3133 :
3134 0 : CacheIndex::RemoveAll();
3135 :
3136 0 : return NS_OK;
3137 : }
3138 :
3139 : // static
3140 : nsresult
3141 0 : CacheFileIOManager::EvictByContext(nsILoadContextInfo *aLoadContextInfo, bool aPinned)
3142 : {
3143 0 : LOG(("CacheFileIOManager::EvictByContext() [loadContextInfo=%p]",
3144 : aLoadContextInfo));
3145 :
3146 : nsresult rv;
3147 0 : RefPtr<CacheFileIOManager> ioMan = gInstance;
3148 :
3149 0 : if (!ioMan) {
3150 0 : return NS_ERROR_NOT_INITIALIZED;
3151 : }
3152 :
3153 0 : nsCOMPtr<nsIRunnable> ev;
3154 0 : ev = NewRunnableMethod<nsCOMPtr<nsILoadContextInfo>, bool>(
3155 : "net::CacheFileIOManager::EvictByContextInternal",
3156 : ioMan,
3157 : &CacheFileIOManager::EvictByContextInternal,
3158 : aLoadContextInfo,
3159 0 : aPinned);
3160 :
3161 0 : rv = ioMan->mIOThread->DispatchAfterPendingOpens(ev);
3162 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3163 0 : return rv;
3164 : }
3165 :
3166 0 : return NS_OK;
3167 : }
3168 :
3169 : nsresult
3170 0 : CacheFileIOManager::EvictByContextInternal(nsILoadContextInfo *aLoadContextInfo, bool aPinned)
3171 : {
3172 0 : LOG(("CacheFileIOManager::EvictByContextInternal() [loadContextInfo=%p, pinned=%d]",
3173 : aLoadContextInfo, aPinned));
3174 :
3175 : nsresult rv;
3176 :
3177 0 : if (aLoadContextInfo) {
3178 0 : nsAutoCString suffix;
3179 0 : aLoadContextInfo->OriginAttributesPtr()->CreateSuffix(suffix);
3180 0 : LOG((" anonymous=%u, suffix=%s]", aLoadContextInfo->IsAnonymous(), suffix.get()));
3181 :
3182 0 : MOZ_ASSERT(mIOThread->IsCurrentThread());
3183 :
3184 0 : MOZ_ASSERT(!aLoadContextInfo->IsPrivate());
3185 0 : if (aLoadContextInfo->IsPrivate()) {
3186 0 : return NS_ERROR_INVALID_ARG;
3187 : }
3188 : }
3189 :
3190 0 : if (!mCacheDirectory) {
3191 : // This is a kind of hack. Somebody called EvictAll() without a profile.
3192 : // This happens in xpcshell tests that use cache without profile. We need
3193 : // to notify observers in this case since the tests are waiting for it.
3194 : // Also notify for aPinned == true, those are interested as well.
3195 0 : if (!aLoadContextInfo) {
3196 0 : RefPtr<EvictionNotifierRunnable> r = new EvictionNotifierRunnable();
3197 0 : NS_DispatchToMainThread(r);
3198 : }
3199 0 : return NS_ERROR_FILE_INVALID_PATH;
3200 : }
3201 :
3202 0 : if (mShuttingDown) {
3203 0 : return NS_ERROR_NOT_INITIALIZED;
3204 : }
3205 :
3206 0 : if (!mTreeCreated) {
3207 0 : rv = CreateCacheTree();
3208 0 : if (NS_FAILED(rv)) {
3209 0 : return rv;
3210 : }
3211 : }
3212 :
3213 : // Doom all active handles that matches the load context
3214 0 : nsTArray<RefPtr<CacheFileHandle> > handles;
3215 0 : mHandles.GetActiveHandles(&handles);
3216 :
3217 0 : for (uint32_t i = 0; i < handles.Length(); ++i) {
3218 0 : CacheFileHandle* handle = handles[i];
3219 :
3220 0 : if (aLoadContextInfo) {
3221 : bool equals;
3222 0 : rv = CacheFileUtils::KeyMatchesLoadContextInfo(handle->Key(),
3223 : aLoadContextInfo,
3224 0 : &equals);
3225 0 : if (NS_FAILED(rv)) {
3226 0 : LOG(("CacheFileIOManager::EvictByContextInternal() - Cannot parse key in "
3227 : "handle! [handle=%p, key=%s]", handle, handle->Key().get()));
3228 0 : MOZ_CRASH("Unexpected error!");
3229 : }
3230 :
3231 0 : if (!equals) {
3232 0 : continue;
3233 : }
3234 : }
3235 :
3236 : // handle will be doomed only when pinning status is known and equal or
3237 : // doom decision will be deferred until pinning status is determined.
3238 0 : rv = DoomFileInternal(handle, aPinned
3239 : ? CacheFileIOManager::DOOM_WHEN_PINNED
3240 0 : : CacheFileIOManager::DOOM_WHEN_NON_PINNED);
3241 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3242 0 : LOG(("CacheFileIOManager::EvictByContextInternal() - Cannot doom handle"
3243 : " [handle=%p]", handle));
3244 : }
3245 : }
3246 :
3247 0 : if (!aLoadContextInfo) {
3248 0 : RefPtr<EvictionNotifierRunnable> r = new EvictionNotifierRunnable();
3249 0 : NS_DispatchToMainThread(r);
3250 : }
3251 :
3252 0 : if (!mContextEvictor) {
3253 0 : mContextEvictor = new CacheFileContextEvictor();
3254 0 : mContextEvictor->Init(mCacheDirectory);
3255 : }
3256 :
3257 0 : mContextEvictor->AddContext(aLoadContextInfo, aPinned);
3258 :
3259 0 : return NS_OK;
3260 : }
3261 :
3262 : // static
3263 : nsresult
3264 2 : CacheFileIOManager::CacheIndexStateChanged()
3265 : {
3266 2 : LOG(("CacheFileIOManager::CacheIndexStateChanged()"));
3267 :
3268 : nsresult rv;
3269 :
3270 : // CacheFileIOManager lives longer than CacheIndex so gInstance must be
3271 : // non-null here.
3272 2 : MOZ_ASSERT(gInstance);
3273 :
3274 : // We have to re-distatch even if we are on IO thread to prevent reentering
3275 : // the lock in CacheIndex
3276 4 : nsCOMPtr<nsIRunnable> ev;
3277 : ev =
3278 6 : NewRunnableMethod("net::CacheFileIOManager::CacheIndexStateChangedInternal",
3279 4 : gInstance.get(),
3280 2 : &CacheFileIOManager::CacheIndexStateChangedInternal);
3281 :
3282 4 : nsCOMPtr<nsIEventTarget> ioTarget = IOTarget();
3283 2 : MOZ_ASSERT(ioTarget);
3284 :
3285 2 : rv = ioTarget->Dispatch(ev, nsIEventTarget::DISPATCH_NORMAL);
3286 2 : if (NS_WARN_IF(NS_FAILED(rv))) {
3287 0 : return rv;
3288 : }
3289 :
3290 2 : return NS_OK;
3291 : }
3292 :
3293 : nsresult
3294 2 : CacheFileIOManager::CacheIndexStateChangedInternal()
3295 : {
3296 2 : if (mShuttingDown) {
3297 : // ignore notification during shutdown
3298 0 : return NS_OK;
3299 : }
3300 :
3301 2 : if (!mContextEvictor) {
3302 2 : return NS_OK;
3303 : }
3304 :
3305 0 : mContextEvictor->CacheIndexStateChanged();
3306 0 : return NS_OK;
3307 : }
3308 :
3309 : nsresult
3310 0 : CacheFileIOManager::TrashDirectory(nsIFile *aFile)
3311 : {
3312 0 : nsAutoCString path;
3313 0 : aFile->GetNativePath(path);
3314 0 : LOG(("CacheFileIOManager::TrashDirectory() [file=%s]", path.get()));
3315 :
3316 : nsresult rv;
3317 :
3318 0 : MOZ_ASSERT(mIOThread->IsCurrentThread());
3319 0 : MOZ_ASSERT(mCacheDirectory);
3320 :
3321 : // When the directory is empty, it is cheaper to remove it directly instead of
3322 : // using the trash mechanism.
3323 : bool isEmpty;
3324 0 : rv = IsEmptyDirectory(aFile, &isEmpty);
3325 0 : NS_ENSURE_SUCCESS(rv, rv);
3326 :
3327 0 : if (isEmpty) {
3328 0 : rv = aFile->Remove(false);
3329 0 : LOG(("CacheFileIOManager::TrashDirectory() - Directory removed [rv=0x%08" PRIx32 "]",
3330 : static_cast<uint32_t>(rv)));
3331 0 : return rv;
3332 : }
3333 :
3334 : #ifdef DEBUG
3335 0 : nsCOMPtr<nsIFile> dirCheck;
3336 0 : rv = aFile->GetParent(getter_AddRefs(dirCheck));
3337 0 : NS_ENSURE_SUCCESS(rv, rv);
3338 :
3339 0 : bool equals = false;
3340 0 : rv = dirCheck->Equals(mCacheDirectory, &equals);
3341 0 : NS_ENSURE_SUCCESS(rv, rv);
3342 :
3343 0 : MOZ_ASSERT(equals);
3344 : #endif
3345 :
3346 0 : nsCOMPtr<nsIFile> dir, trash;
3347 0 : nsAutoCString leaf;
3348 :
3349 0 : rv = aFile->Clone(getter_AddRefs(dir));
3350 0 : NS_ENSURE_SUCCESS(rv, rv);
3351 :
3352 0 : rv = aFile->Clone(getter_AddRefs(trash));
3353 0 : NS_ENSURE_SUCCESS(rv, rv);
3354 :
3355 0 : const int32_t kMaxTries = 16;
3356 0 : srand(static_cast<unsigned>(PR_Now()));
3357 0 : for (int32_t triesCount = 0; ; ++triesCount) {
3358 0 : leaf = TRASH_DIR;
3359 0 : leaf.AppendInt(rand());
3360 0 : rv = trash->SetNativeLeafName(leaf);
3361 0 : NS_ENSURE_SUCCESS(rv, rv);
3362 :
3363 : bool exists;
3364 0 : if (NS_SUCCEEDED(trash->Exists(&exists)) && !exists) {
3365 0 : break;
3366 : }
3367 :
3368 0 : LOG(("CacheFileIOManager::TrashDirectory() - Trash directory already "
3369 : "exists [leaf=%s]", leaf.get()));
3370 :
3371 0 : if (triesCount == kMaxTries) {
3372 0 : LOG(("CacheFileIOManager::TrashDirectory() - Could not find unused trash "
3373 : "directory in %d tries.", kMaxTries));
3374 0 : return NS_ERROR_FAILURE;
3375 : }
3376 0 : }
3377 :
3378 0 : LOG(("CacheFileIOManager::TrashDirectory() - Renaming directory [leaf=%s]",
3379 : leaf.get()));
3380 :
3381 0 : rv = dir->MoveToNative(nullptr, leaf);
3382 0 : NS_ENSURE_SUCCESS(rv, rv);
3383 :
3384 0 : StartRemovingTrash();
3385 0 : return NS_OK;
3386 : }
3387 :
3388 : // static
3389 : void
3390 0 : CacheFileIOManager::OnTrashTimer(nsITimer *aTimer, void *aClosure)
3391 : {
3392 0 : LOG(("CacheFileIOManager::OnTrashTimer() [timer=%p, closure=%p]", aTimer,
3393 : aClosure));
3394 :
3395 0 : RefPtr<CacheFileIOManager> ioMan = gInstance;
3396 :
3397 0 : if (!ioMan) {
3398 0 : return;
3399 : }
3400 :
3401 0 : ioMan->mTrashTimer = nullptr;
3402 0 : ioMan->StartRemovingTrash();
3403 : }
3404 :
3405 : nsresult
3406 1 : CacheFileIOManager::StartRemovingTrash()
3407 : {
3408 1 : LOG(("CacheFileIOManager::StartRemovingTrash()"));
3409 :
3410 : nsresult rv;
3411 :
3412 1 : MOZ_ASSERT(mIOThread->IsCurrentThread());
3413 :
3414 1 : if (mShuttingDown) {
3415 0 : return NS_ERROR_NOT_INITIALIZED;
3416 : }
3417 :
3418 1 : if (!mCacheDirectory) {
3419 0 : return NS_ERROR_FILE_INVALID_PATH;
3420 : }
3421 :
3422 1 : if (mTrashTimer) {
3423 0 : LOG(("CacheFileIOManager::StartRemovingTrash() - Trash timer exists."));
3424 0 : return NS_OK;
3425 : }
3426 :
3427 1 : if (mRemovingTrashDirs) {
3428 0 : LOG(("CacheFileIOManager::StartRemovingTrash() - Trash removing in "
3429 : "progress."));
3430 0 : return NS_OK;
3431 : }
3432 :
3433 1 : uint32_t elapsed = (TimeStamp::NowLoRes() - mStartTime).ToMilliseconds();
3434 1 : if (elapsed < kRemoveTrashStartDelay) {
3435 2 : nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1", &rv);
3436 1 : NS_ENSURE_SUCCESS(rv, rv);
3437 :
3438 2 : nsCOMPtr<nsIEventTarget> ioTarget = IOTarget();
3439 1 : MOZ_ASSERT(ioTarget);
3440 :
3441 1 : rv = timer->SetTarget(ioTarget);
3442 1 : NS_ENSURE_SUCCESS(rv, rv);
3443 :
3444 2 : rv = timer->InitWithNamedFuncCallback(
3445 : CacheFileIOManager::OnTrashTimer,
3446 : nullptr,
3447 : kRemoveTrashStartDelay - elapsed,
3448 : nsITimer::TYPE_ONE_SHOT,
3449 1 : "net::CacheFileIOManager::StartRemovingTrash");
3450 1 : NS_ENSURE_SUCCESS(rv, rv);
3451 :
3452 1 : mTrashTimer.swap(timer);
3453 1 : return NS_OK;
3454 : }
3455 :
3456 0 : nsCOMPtr<nsIRunnable> ev;
3457 0 : ev = NewRunnableMethod("net::CacheFileIOManager::RemoveTrashInternal",
3458 : this,
3459 0 : &CacheFileIOManager::RemoveTrashInternal);
3460 :
3461 0 : rv = mIOThread->Dispatch(ev, CacheIOThread::EVICT);
3462 0 : NS_ENSURE_SUCCESS(rv, rv);
3463 :
3464 0 : mRemovingTrashDirs = true;
3465 0 : return NS_OK;
3466 : }
3467 :
3468 : nsresult
3469 0 : CacheFileIOManager::RemoveTrashInternal()
3470 : {
3471 0 : LOG(("CacheFileIOManager::RemoveTrashInternal()"));
3472 :
3473 : nsresult rv;
3474 :
3475 0 : MOZ_ASSERT(mIOThread->IsCurrentThread());
3476 :
3477 0 : if (mShuttingDown) {
3478 0 : return NS_ERROR_NOT_INITIALIZED;
3479 : }
3480 :
3481 0 : CacheIOThread::Cancelable cancelable(true);
3482 :
3483 0 : MOZ_ASSERT(!mTrashTimer);
3484 0 : MOZ_ASSERT(mRemovingTrashDirs);
3485 :
3486 0 : if (!mTreeCreated) {
3487 0 : rv = CreateCacheTree();
3488 0 : if (NS_FAILED(rv)) {
3489 0 : return rv;
3490 : }
3491 : }
3492 :
3493 : // mRemovingTrashDirs is accessed only on IO thread, so we can drop the flag
3494 : // here and set it again once we dispatch a continuation event. By doing so,
3495 : // we don't have to drop the flag on any possible early return.
3496 0 : mRemovingTrashDirs = false;
3497 :
3498 : while (true) {
3499 0 : if (CacheIOThread::YieldAndRerun()) {
3500 0 : LOG(("CacheFileIOManager::RemoveTrashInternal() - Breaking loop for "
3501 : "higher level events."));
3502 0 : mRemovingTrashDirs = true;
3503 0 : return NS_OK;
3504 : }
3505 :
3506 : // Find some trash directory
3507 0 : if (!mTrashDir) {
3508 0 : MOZ_ASSERT(!mTrashDirEnumerator);
3509 :
3510 0 : rv = FindTrashDirToRemove();
3511 0 : if (rv == NS_ERROR_NOT_AVAILABLE) {
3512 0 : LOG(("CacheFileIOManager::RemoveTrashInternal() - No trash directory "
3513 : "found."));
3514 0 : return NS_OK;
3515 : }
3516 0 : NS_ENSURE_SUCCESS(rv, rv);
3517 :
3518 0 : nsCOMPtr<nsISimpleEnumerator> enumerator;
3519 0 : rv = mTrashDir->GetDirectoryEntries(getter_AddRefs(enumerator));
3520 0 : if (NS_SUCCEEDED(rv)) {
3521 0 : mTrashDirEnumerator = do_QueryInterface(enumerator, &rv);
3522 0 : NS_ENSURE_SUCCESS(rv, rv);
3523 : }
3524 :
3525 0 : continue; // check elapsed time
3526 : }
3527 :
3528 : // We null out mTrashDirEnumerator once we remove all files in the
3529 : // directory, so remove the trash directory if we don't have enumerator.
3530 0 : if (!mTrashDirEnumerator) {
3531 0 : rv = mTrashDir->Remove(false);
3532 0 : if (NS_FAILED(rv)) {
3533 : // There is no reason why removing an empty directory should fail, but
3534 : // if it does, we should continue and try to remove all other trash
3535 : // directories.
3536 0 : nsAutoCString leafName;
3537 0 : mTrashDir->GetNativeLeafName(leafName);
3538 0 : mFailedTrashDirs.AppendElement(leafName);
3539 0 : LOG(("CacheFileIOManager::RemoveTrashInternal() - Cannot remove "
3540 : "trashdir. [name=%s]", leafName.get()));
3541 : }
3542 :
3543 0 : mTrashDir = nullptr;
3544 0 : continue; // check elapsed time
3545 : }
3546 :
3547 0 : nsCOMPtr<nsIFile> file;
3548 0 : rv = mTrashDirEnumerator->GetNextFile(getter_AddRefs(file));
3549 0 : if (!file) {
3550 0 : mTrashDirEnumerator->Close();
3551 0 : mTrashDirEnumerator = nullptr;
3552 0 : continue; // check elapsed time
3553 : } else {
3554 0 : bool isDir = false;
3555 0 : file->IsDirectory(&isDir);
3556 0 : if (isDir) {
3557 : NS_WARNING("Found a directory in a trash directory! It will be removed "
3558 0 : "recursively, but this can block IO thread for a while!");
3559 0 : if (LOG_ENABLED()) {
3560 0 : nsAutoCString path;
3561 0 : file->GetNativePath(path);
3562 0 : LOG(("CacheFileIOManager::RemoveTrashInternal() - Found a directory in a trash "
3563 : "directory! It will be removed recursively, but this can block IO "
3564 : "thread for a while! [file=%s]", path.get()));
3565 : }
3566 : }
3567 0 : file->Remove(isDir);
3568 : }
3569 0 : }
3570 :
3571 : NS_NOTREACHED("We should never get here");
3572 : return NS_OK;
3573 : }
3574 :
3575 : nsresult
3576 0 : CacheFileIOManager::FindTrashDirToRemove()
3577 : {
3578 0 : LOG(("CacheFileIOManager::FindTrashDirToRemove()"));
3579 :
3580 : nsresult rv;
3581 :
3582 : // We call this method on the main thread during shutdown when user wants to
3583 : // remove all cache files.
3584 0 : MOZ_ASSERT(mIOThread->IsCurrentThread() || mShuttingDown);
3585 :
3586 0 : nsCOMPtr<nsISimpleEnumerator> iter;
3587 0 : rv = mCacheDirectory->GetDirectoryEntries(getter_AddRefs(iter));
3588 0 : NS_ENSURE_SUCCESS(rv, rv);
3589 :
3590 : bool more;
3591 0 : nsCOMPtr<nsISupports> elem;
3592 :
3593 0 : while (NS_SUCCEEDED(iter->HasMoreElements(&more)) && more) {
3594 0 : rv = iter->GetNext(getter_AddRefs(elem));
3595 0 : if (NS_FAILED(rv)) {
3596 0 : continue;
3597 : }
3598 :
3599 0 : nsCOMPtr<nsIFile> file = do_QueryInterface(elem);
3600 0 : if (!file) {
3601 0 : continue;
3602 : }
3603 :
3604 0 : bool isDir = false;
3605 0 : file->IsDirectory(&isDir);
3606 0 : if (!isDir) {
3607 0 : continue;
3608 : }
3609 :
3610 0 : nsAutoCString leafName;
3611 0 : rv = file->GetNativeLeafName(leafName);
3612 0 : if (NS_FAILED(rv)) {
3613 0 : continue;
3614 : }
3615 :
3616 0 : if (leafName.Length() < strlen(TRASH_DIR)) {
3617 0 : continue;
3618 : }
3619 :
3620 0 : if (!StringBeginsWith(leafName, NS_LITERAL_CSTRING(TRASH_DIR))) {
3621 0 : continue;
3622 : }
3623 :
3624 0 : if (mFailedTrashDirs.Contains(leafName)) {
3625 0 : continue;
3626 : }
3627 :
3628 0 : LOG(("CacheFileIOManager::FindTrashDirToRemove() - Returning directory %s",
3629 : leafName.get()));
3630 :
3631 0 : mTrashDir = file;
3632 0 : return NS_OK;
3633 : }
3634 :
3635 : // When we're here we've tried to delete all trash directories. Clear
3636 : // mFailedTrashDirs so we will try to delete them again when we start removing
3637 : // trash directories next time.
3638 0 : mFailedTrashDirs.Clear();
3639 0 : return NS_ERROR_NOT_AVAILABLE;
3640 : }
3641 :
3642 : // static
3643 : nsresult
3644 5 : CacheFileIOManager::InitIndexEntry(CacheFileHandle *aHandle,
3645 : OriginAttrsHash aOriginAttrsHash,
3646 : bool aAnonymous,
3647 : bool aPinning)
3648 : {
3649 5 : LOG(("CacheFileIOManager::InitIndexEntry() [handle=%p, originAttrsHash=%" PRIx64 ", "
3650 : "anonymous=%d, pinning=%d]", aHandle, aOriginAttrsHash, aAnonymous,
3651 : aPinning));
3652 :
3653 : nsresult rv;
3654 10 : RefPtr<CacheFileIOManager> ioMan = gInstance;
3655 :
3656 5 : if (aHandle->IsClosed() || !ioMan) {
3657 0 : return NS_ERROR_NOT_INITIALIZED;
3658 : }
3659 :
3660 5 : if (aHandle->IsSpecialFile()) {
3661 0 : return NS_ERROR_UNEXPECTED;
3662 : }
3663 :
3664 : RefPtr<InitIndexEntryEvent> ev =
3665 10 : new InitIndexEntryEvent(aHandle, aOriginAttrsHash, aAnonymous, aPinning);
3666 10 : rv = ioMan->mIOThread->Dispatch(ev, aHandle->mPriority
3667 : ? CacheIOThread::WRITE_PRIORITY
3668 10 : : CacheIOThread::WRITE);
3669 5 : NS_ENSURE_SUCCESS(rv, rv);
3670 :
3671 5 : return NS_OK;
3672 : }
3673 :
3674 : // static
3675 : nsresult
3676 14 : CacheFileIOManager::UpdateIndexEntry(CacheFileHandle *aHandle,
3677 : const uint32_t *aFrecency,
3678 : const uint32_t *aExpirationTime,
3679 : const bool *aHasAltData,
3680 : const uint16_t *aOnStartTime,
3681 : const uint16_t *aOnStopTime)
3682 : {
3683 14 : LOG(("CacheFileIOManager::UpdateIndexEntry() [handle=%p, frecency=%s, "
3684 : "expirationTime=%s, hasAltData=%s, onStartTime=%s, onStopTime=%s]", aHandle,
3685 : aFrecency ? nsPrintfCString("%u", *aFrecency).get() : "",
3686 : aExpirationTime ? nsPrintfCString("%u", *aExpirationTime).get() : "",
3687 : aHasAltData ? (*aHasAltData ? "true" : "false") : "",
3688 : aOnStartTime ? nsPrintfCString("%u", *aOnStartTime).get() : "",
3689 : aOnStopTime ? nsPrintfCString("%u", *aOnStopTime).get() : ""));
3690 :
3691 : nsresult rv;
3692 28 : RefPtr<CacheFileIOManager> ioMan = gInstance;
3693 :
3694 14 : if (aHandle->IsClosed() || !ioMan) {
3695 0 : return NS_ERROR_NOT_INITIALIZED;
3696 : }
3697 :
3698 14 : if (aHandle->IsSpecialFile()) {
3699 0 : return NS_ERROR_UNEXPECTED;
3700 : }
3701 :
3702 : RefPtr<UpdateIndexEntryEvent> ev =
3703 : new UpdateIndexEntryEvent(aHandle, aFrecency, aExpirationTime, aHasAltData,
3704 28 : aOnStartTime, aOnStopTime);
3705 28 : rv = ioMan->mIOThread->Dispatch(ev, aHandle->mPriority
3706 : ? CacheIOThread::WRITE_PRIORITY
3707 28 : : CacheIOThread::WRITE);
3708 14 : NS_ENSURE_SUCCESS(rv, rv);
3709 :
3710 14 : return NS_OK;
3711 : }
3712 :
3713 : nsresult
3714 2 : CacheFileIOManager::CreateFile(CacheFileHandle *aHandle)
3715 : {
3716 2 : MOZ_ASSERT(!aHandle->mFD);
3717 2 : MOZ_ASSERT(aHandle->mFile);
3718 :
3719 : nsresult rv;
3720 :
3721 2 : if (aHandle->IsDoomed()) {
3722 0 : nsCOMPtr<nsIFile> file;
3723 :
3724 0 : rv = GetDoomedFile(getter_AddRefs(file));
3725 0 : NS_ENSURE_SUCCESS(rv, rv);
3726 :
3727 0 : aHandle->mFile.swap(file);
3728 : } else {
3729 : bool exists;
3730 2 : if (NS_SUCCEEDED(aHandle->mFile->Exists(&exists)) && exists) {
3731 0 : NS_WARNING("Found a file that should not exist!");
3732 : }
3733 : }
3734 :
3735 2 : rv = OpenNSPRHandle(aHandle, true);
3736 2 : NS_ENSURE_SUCCESS(rv, rv);
3737 :
3738 2 : aHandle->mFileSize = 0;
3739 2 : return NS_OK;
3740 : }
3741 :
3742 : // static
3743 : void
3744 6 : CacheFileIOManager::HashToStr(const SHA1Sum::Hash *aHash, nsACString &_retval)
3745 : {
3746 6 : _retval.Truncate();
3747 : const char hexChars[] = {'0', '1', '2', '3', '4', '5', '6', '7',
3748 6 : '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
3749 126 : for (uint32_t i=0 ; i<sizeof(SHA1Sum::Hash) ; i++) {
3750 120 : _retval.Append(hexChars[(*aHash)[i] >> 4]);
3751 120 : _retval.Append(hexChars[(*aHash)[i] & 0xF]);
3752 : }
3753 6 : }
3754 :
3755 : // static
3756 : nsresult
3757 0 : CacheFileIOManager::StrToHash(const nsACString &aHash, SHA1Sum::Hash *_retval)
3758 : {
3759 0 : if (aHash.Length() != 2*sizeof(SHA1Sum::Hash)) {
3760 0 : return NS_ERROR_INVALID_ARG;
3761 : }
3762 :
3763 0 : for (uint32_t i=0 ; i<aHash.Length() ; i++) {
3764 : uint8_t value;
3765 :
3766 0 : if (aHash[i] >= '0' && aHash[i] <= '9') {
3767 0 : value = aHash[i] - '0';
3768 0 : } else if (aHash[i] >= 'A' && aHash[i] <= 'F') {
3769 0 : value = aHash[i] - 'A' + 10;
3770 0 : } else if (aHash[i] >= 'a' && aHash[i] <= 'f') {
3771 0 : value = aHash[i] - 'a' + 10;
3772 : } else {
3773 0 : return NS_ERROR_INVALID_ARG;
3774 : }
3775 :
3776 0 : if (i%2 == 0) {
3777 0 : (reinterpret_cast<uint8_t *>(_retval))[i/2] = value << 4;
3778 : } else {
3779 0 : (reinterpret_cast<uint8_t *>(_retval))[i/2] += value;
3780 : }
3781 : }
3782 :
3783 0 : return NS_OK;
3784 : }
3785 :
3786 : nsresult
3787 6 : CacheFileIOManager::GetFile(const SHA1Sum::Hash *aHash, nsIFile **_retval)
3788 : {
3789 : nsresult rv;
3790 12 : nsCOMPtr<nsIFile> file;
3791 6 : rv = mCacheDirectory->Clone(getter_AddRefs(file));
3792 6 : NS_ENSURE_SUCCESS(rv, rv);
3793 :
3794 6 : rv = file->AppendNative(NS_LITERAL_CSTRING(ENTRIES_DIR));
3795 6 : NS_ENSURE_SUCCESS(rv, rv);
3796 :
3797 12 : nsAutoCString leafName;
3798 6 : HashToStr(aHash, leafName);
3799 :
3800 6 : rv = file->AppendNative(leafName);
3801 6 : NS_ENSURE_SUCCESS(rv, rv);
3802 :
3803 6 : file.swap(*_retval);
3804 6 : return NS_OK;
3805 : }
3806 :
3807 : nsresult
3808 3 : CacheFileIOManager::GetSpecialFile(const nsACString &aKey, nsIFile **_retval)
3809 : {
3810 : nsresult rv;
3811 6 : nsCOMPtr<nsIFile> file;
3812 3 : rv = mCacheDirectory->Clone(getter_AddRefs(file));
3813 3 : NS_ENSURE_SUCCESS(rv, rv);
3814 :
3815 3 : rv = file->AppendNative(aKey);
3816 3 : NS_ENSURE_SUCCESS(rv, rv);
3817 :
3818 3 : file.swap(*_retval);
3819 3 : return NS_OK;
3820 : }
3821 :
3822 : nsresult
3823 0 : CacheFileIOManager::GetDoomedFile(nsIFile **_retval)
3824 : {
3825 : nsresult rv;
3826 0 : nsCOMPtr<nsIFile> file;
3827 0 : rv = mCacheDirectory->Clone(getter_AddRefs(file));
3828 0 : NS_ENSURE_SUCCESS(rv, rv);
3829 :
3830 0 : rv = file->AppendNative(NS_LITERAL_CSTRING(DOOMED_DIR));
3831 0 : NS_ENSURE_SUCCESS(rv, rv);
3832 :
3833 0 : rv = file->AppendNative(NS_LITERAL_CSTRING("dummyleaf"));
3834 0 : NS_ENSURE_SUCCESS(rv, rv);
3835 :
3836 0 : const int32_t kMaxTries = 64;
3837 0 : srand(static_cast<unsigned>(PR_Now()));
3838 0 : nsAutoCString leafName;
3839 0 : for (int32_t triesCount = 0; ; ++triesCount) {
3840 0 : leafName.AppendInt(rand());
3841 0 : rv = file->SetNativeLeafName(leafName);
3842 0 : NS_ENSURE_SUCCESS(rv, rv);
3843 :
3844 : bool exists;
3845 0 : if (NS_SUCCEEDED(file->Exists(&exists)) && !exists) {
3846 0 : break;
3847 : }
3848 :
3849 0 : if (triesCount == kMaxTries) {
3850 0 : LOG(("CacheFileIOManager::GetDoomedFile() - Could not find unused file "
3851 : "name in %d tries.", kMaxTries));
3852 0 : return NS_ERROR_FAILURE;
3853 : }
3854 :
3855 0 : leafName.Truncate();
3856 0 : }
3857 :
3858 0 : file.swap(*_retval);
3859 0 : return NS_OK;
3860 : }
3861 :
3862 : nsresult
3863 1 : CacheFileIOManager::IsEmptyDirectory(nsIFile *aFile, bool *_retval)
3864 : {
3865 1 : MOZ_ASSERT(mIOThread->IsCurrentThread());
3866 :
3867 : nsresult rv;
3868 :
3869 2 : nsCOMPtr<nsISimpleEnumerator> enumerator;
3870 1 : rv = aFile->GetDirectoryEntries(getter_AddRefs(enumerator));
3871 1 : NS_ENSURE_SUCCESS(rv, rv);
3872 :
3873 1 : bool hasMoreElements = false;
3874 1 : rv = enumerator->HasMoreElements(&hasMoreElements);
3875 1 : NS_ENSURE_SUCCESS(rv, rv);
3876 :
3877 1 : *_retval = !hasMoreElements;
3878 1 : return NS_OK;
3879 : }
3880 :
3881 : nsresult
3882 4 : CacheFileIOManager::CheckAndCreateDir(nsIFile *aFile, const char *aDir,
3883 : bool aEnsureEmptyDir)
3884 : {
3885 : nsresult rv;
3886 :
3887 8 : nsCOMPtr<nsIFile> file;
3888 4 : if (!aDir) {
3889 2 : file = aFile;
3890 : } else {
3891 4 : nsAutoCString dir(aDir);
3892 2 : rv = aFile->Clone(getter_AddRefs(file));
3893 2 : NS_ENSURE_SUCCESS(rv, rv);
3894 2 : rv = file->AppendNative(dir);
3895 2 : NS_ENSURE_SUCCESS(rv, rv);
3896 : }
3897 :
3898 4 : bool exists = false;
3899 4 : rv = file->Exists(&exists);
3900 4 : if (NS_SUCCEEDED(rv) && exists) {
3901 4 : bool isDirectory = false;
3902 4 : rv = file->IsDirectory(&isDirectory);
3903 4 : if (NS_FAILED(rv) || !isDirectory) {
3904 : // Try to remove the file
3905 0 : rv = file->Remove(false);
3906 0 : if (NS_SUCCEEDED(rv)) {
3907 0 : exists = false;
3908 : }
3909 : }
3910 4 : NS_ENSURE_SUCCESS(rv, rv);
3911 : }
3912 :
3913 4 : if (aEnsureEmptyDir && NS_SUCCEEDED(rv) && exists) {
3914 : bool isEmpty;
3915 1 : rv = IsEmptyDirectory(file, &isEmpty);
3916 1 : NS_ENSURE_SUCCESS(rv, rv);
3917 :
3918 1 : if (!isEmpty) {
3919 : // Don't check the result, if this fails, it's OK. We do this
3920 : // only for the doomed directory that doesn't need to be deleted
3921 : // for the cost of completely disabling the whole browser.
3922 0 : TrashDirectory(file);
3923 : }
3924 : }
3925 :
3926 4 : if (NS_SUCCEEDED(rv) && !exists) {
3927 0 : rv = file->Create(nsIFile::DIRECTORY_TYPE, 0700);
3928 : }
3929 4 : if (NS_FAILED(rv)) {
3930 0 : NS_WARNING("Cannot create directory");
3931 0 : return NS_ERROR_FAILURE;
3932 : }
3933 :
3934 4 : return NS_OK;
3935 : }
3936 :
3937 : nsresult
3938 1 : CacheFileIOManager::CreateCacheTree()
3939 : {
3940 1 : MOZ_ASSERT(mIOThread->IsCurrentThread());
3941 1 : MOZ_ASSERT(!mTreeCreated);
3942 :
3943 1 : if (!mCacheDirectory || mTreeCreationFailed) {
3944 0 : return NS_ERROR_FILE_INVALID_PATH;
3945 : }
3946 :
3947 : nsresult rv;
3948 :
3949 : // Set the flag here and clear it again below when the tree is created
3950 : // successfully.
3951 1 : mTreeCreationFailed = true;
3952 :
3953 : // ensure parent directory exists
3954 2 : nsCOMPtr<nsIFile> parentDir;
3955 1 : rv = mCacheDirectory->GetParent(getter_AddRefs(parentDir));
3956 1 : NS_ENSURE_SUCCESS(rv, rv);
3957 1 : rv = CheckAndCreateDir(parentDir, nullptr, false);
3958 1 : NS_ENSURE_SUCCESS(rv, rv);
3959 :
3960 : // ensure cache directory exists
3961 1 : rv = CheckAndCreateDir(mCacheDirectory, nullptr, false);
3962 1 : NS_ENSURE_SUCCESS(rv, rv);
3963 :
3964 : // ensure entries directory exists
3965 1 : rv = CheckAndCreateDir(mCacheDirectory, ENTRIES_DIR, false);
3966 1 : NS_ENSURE_SUCCESS(rv, rv);
3967 :
3968 : // ensure doomed directory exists
3969 1 : rv = CheckAndCreateDir(mCacheDirectory, DOOMED_DIR, true);
3970 1 : NS_ENSURE_SUCCESS(rv, rv);
3971 :
3972 1 : mTreeCreated = true;
3973 1 : mTreeCreationFailed = false;
3974 :
3975 1 : if (!mContextEvictor) {
3976 2 : RefPtr<CacheFileContextEvictor> contextEvictor;
3977 1 : contextEvictor = new CacheFileContextEvictor();
3978 :
3979 : // Init() method will try to load unfinished contexts from the disk. Store
3980 : // the evictor as a member only when there is some unfinished job.
3981 1 : contextEvictor->Init(mCacheDirectory);
3982 1 : if (contextEvictor->ContextsCount()) {
3983 0 : contextEvictor.swap(mContextEvictor);
3984 : }
3985 : }
3986 :
3987 1 : StartRemovingTrash();
3988 :
3989 1 : if (!CacheObserver::CacheFSReported()) {
3990 1 : uint32_t fsType = 4; // Other OS
3991 :
3992 : #ifdef XP_WIN
3993 : nsAutoString target;
3994 : nsresult rv = mCacheDirectory->GetTarget(target);
3995 : if (NS_FAILED(rv)) {
3996 : return NS_OK;
3997 : }
3998 :
3999 : wchar_t volume_path[MAX_PATH + 1] = { 0 };
4000 : if (!::GetVolumePathNameW(target.get(),
4001 : volume_path,
4002 : mozilla::ArrayLength(volume_path))) {
4003 : return NS_OK;
4004 : }
4005 :
4006 : wchar_t fsName[6] = { 0 };
4007 : if (!::GetVolumeInformationW(volume_path, nullptr, 0, nullptr, nullptr,
4008 : nullptr, fsName,
4009 : mozilla::ArrayLength(fsName))) {
4010 : return NS_OK;
4011 : }
4012 :
4013 : if (wcscmp(fsName, L"NTFS") == 0) {
4014 : fsType = 0;
4015 : } else if (wcscmp(fsName, L"FAT32") == 0) {
4016 : fsType = 1;
4017 : } else if (wcscmp(fsName, L"FAT") == 0) {
4018 : fsType = 2;
4019 : } else {
4020 : fsType = 3;
4021 : }
4022 : #endif
4023 :
4024 1 : Telemetry::Accumulate(Telemetry::NETWORK_CACHE_FS_TYPE, fsType);
4025 1 : CacheObserver::SetCacheFSReported();
4026 : }
4027 :
4028 1 : return NS_OK;
4029 : }
4030 :
4031 : nsresult
4032 5 : CacheFileIOManager::OpenNSPRHandle(CacheFileHandle *aHandle, bool aCreate)
4033 : {
4034 5 : LOG(("CacheFileIOManager::OpenNSPRHandle BEGIN, handle=%p", aHandle));
4035 :
4036 5 : MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
4037 5 : MOZ_ASSERT(!aHandle->mFD);
4038 5 : MOZ_ASSERT(mHandlesByLastUsed.IndexOf(aHandle) == mHandlesByLastUsed.NoIndex);
4039 5 : MOZ_ASSERT(mHandlesByLastUsed.Length() <= kOpenHandlesLimit);
4040 5 : MOZ_ASSERT((aCreate && !aHandle->mFileExists) ||
4041 : (!aCreate && aHandle->mFileExists));
4042 :
4043 : nsresult rv;
4044 :
4045 5 : if (mHandlesByLastUsed.Length() == kOpenHandlesLimit) {
4046 : // close handle that hasn't been used for the longest time
4047 0 : rv = MaybeReleaseNSPRHandleInternal(mHandlesByLastUsed[0], true);
4048 0 : NS_ENSURE_SUCCESS(rv, rv);
4049 : }
4050 :
4051 5 : if (aCreate) {
4052 4 : rv = aHandle->mFile->OpenNSPRFileDesc(
4053 4 : PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE, 0600, &aHandle->mFD);
4054 2 : if (rv == NS_ERROR_FILE_ALREADY_EXISTS || // error from nsLocalFileWin
4055 : rv == NS_ERROR_FILE_NO_DEVICE_SPACE) { // error from nsLocalFileUnix
4056 0 : LOG(("CacheFileIOManager::OpenNSPRHandle() - Cannot create a new file, we"
4057 : " might reached a limit on FAT32. Will evict a single entry and try "
4058 : "again. [hash=%08x%08x%08x%08x%08x]", LOGSHA1(aHandle->Hash())));
4059 :
4060 : SHA1Sum::Hash hash;
4061 : uint32_t cnt;
4062 :
4063 0 : rv = CacheIndex::GetEntryForEviction(true, &hash, &cnt);
4064 0 : if (NS_SUCCEEDED(rv)) {
4065 0 : rv = DoomFileByKeyInternal(&hash);
4066 : }
4067 0 : if (NS_SUCCEEDED(rv)) {
4068 0 : rv = aHandle->mFile->OpenNSPRFileDesc(
4069 0 : PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE, 0600, &aHandle->mFD);
4070 0 : LOG(("CacheFileIOManager::OpenNSPRHandle() - Successfully evicted entry"
4071 : " with hash %08x%08x%08x%08x%08x. %s to create the new file.",
4072 : LOGSHA1(&hash), NS_SUCCEEDED(rv) ? "Succeeded" : "Failed"));
4073 :
4074 : // Report the full size only once per session
4075 : static bool sSizeReported = false;
4076 0 : if (!sSizeReported) {
4077 : uint32_t cacheUsage;
4078 0 : if (NS_SUCCEEDED(CacheIndex::GetCacheSize(&cacheUsage))) {
4079 0 : cacheUsage >>= 10;
4080 : Telemetry::Accumulate(Telemetry::NETWORK_CACHE_SIZE_FULL_FAT,
4081 0 : cacheUsage);
4082 0 : sSizeReported = true;
4083 : }
4084 : }
4085 : } else {
4086 0 : LOG(("CacheFileIOManager::OpenNSPRHandle() - Couldn't evict an existing"
4087 : " entry."));
4088 0 : rv = NS_ERROR_FILE_NO_DEVICE_SPACE;
4089 : }
4090 : }
4091 2 : if (NS_FAILED(rv)) {
4092 0 : LOG(("CacheFileIOManager::OpenNSPRHandle() Create failed with 0x%08" PRIx32,
4093 : static_cast<uint32_t>(rv)));
4094 : }
4095 2 : NS_ENSURE_SUCCESS(rv, rv);
4096 :
4097 2 : aHandle->mFileExists = true;
4098 : } else {
4099 3 : rv = aHandle->mFile->OpenNSPRFileDesc(PR_RDWR, 0600, &aHandle->mFD);
4100 3 : if (NS_ERROR_FILE_NOT_FOUND == rv) {
4101 0 : LOG((" file doesn't exists"));
4102 0 : aHandle->mFileExists = false;
4103 0 : return DoomFileInternal(aHandle);
4104 : }
4105 3 : if (NS_FAILED(rv)) {
4106 0 : LOG(("CacheFileIOManager::OpenNSPRHandle() Open failed with 0x%08" PRIx32,
4107 : static_cast<uint32_t>(rv)));
4108 : }
4109 3 : NS_ENSURE_SUCCESS(rv, rv);
4110 : }
4111 :
4112 5 : mHandlesByLastUsed.AppendElement(aHandle);
4113 :
4114 5 : LOG(("CacheFileIOManager::OpenNSPRHandle END, handle=%p", aHandle));
4115 :
4116 5 : return NS_OK;
4117 : }
4118 :
4119 : void
4120 8 : CacheFileIOManager::NSPRHandleUsed(CacheFileHandle *aHandle)
4121 : {
4122 8 : MOZ_ASSERT(CacheFileIOManager::IsOnIOThreadOrCeased());
4123 8 : MOZ_ASSERT(aHandle->mFD);
4124 :
4125 16 : DebugOnly<bool> found;
4126 8 : found = mHandlesByLastUsed.RemoveElement(aHandle);
4127 8 : MOZ_ASSERT(found);
4128 :
4129 8 : mHandlesByLastUsed.AppendElement(aHandle);
4130 8 : }
4131 :
4132 : nsresult
4133 0 : CacheFileIOManager::SyncRemoveDir(nsIFile *aFile, const char *aDir)
4134 : {
4135 : nsresult rv;
4136 0 : nsCOMPtr<nsIFile> file;
4137 :
4138 0 : if (!aDir) {
4139 0 : file = aFile;
4140 : } else {
4141 0 : rv = aFile->Clone(getter_AddRefs(file));
4142 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4143 0 : return rv;
4144 : }
4145 :
4146 0 : rv = file->AppendNative(nsDependentCString(aDir));
4147 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4148 0 : return rv;
4149 : }
4150 : }
4151 :
4152 0 : if (LOG_ENABLED()) {
4153 0 : nsAutoCString path;
4154 0 : file->GetNativePath(path);
4155 0 : LOG(("CacheFileIOManager::SyncRemoveDir() - Removing directory %s",
4156 : path.get()));
4157 : }
4158 :
4159 0 : rv = file->Remove(true);
4160 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4161 0 : LOG(("CacheFileIOManager::SyncRemoveDir() - Removing failed! [rv=0x%08" PRIx32 "]",
4162 : static_cast<uint32_t>(rv)));
4163 : }
4164 :
4165 0 : return rv;
4166 : }
4167 :
4168 : void
4169 0 : CacheFileIOManager::SyncRemoveAllCacheFiles()
4170 : {
4171 0 : LOG(("CacheFileIOManager::SyncRemoveAllCacheFiles()"));
4172 :
4173 : nsresult rv;
4174 :
4175 0 : SyncRemoveDir(mCacheDirectory, ENTRIES_DIR);
4176 0 : SyncRemoveDir(mCacheDirectory, DOOMED_DIR);
4177 :
4178 : // Clear any intermediate state of trash dir enumeration.
4179 0 : mFailedTrashDirs.Clear();
4180 0 : mTrashDir = nullptr;
4181 :
4182 : while (true) {
4183 : // FindTrashDirToRemove() fills mTrashDir if there is any trash directory.
4184 0 : rv = FindTrashDirToRemove();
4185 0 : if (rv == NS_ERROR_NOT_AVAILABLE) {
4186 0 : LOG(("CacheFileIOManager::SyncRemoveAllCacheFiles() - No trash directory "
4187 : "found."));
4188 0 : break;
4189 : }
4190 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4191 0 : LOG(("CacheFileIOManager::SyncRemoveAllCacheFiles() - "
4192 : "FindTrashDirToRemove() returned an unexpected error. [rv=0x%08" PRIx32 "]",
4193 : static_cast<uint32_t>(rv)));
4194 0 : break;
4195 : }
4196 :
4197 0 : rv = SyncRemoveDir(mTrashDir, nullptr);
4198 0 : if (NS_FAILED(rv)) {
4199 0 : nsAutoCString leafName;
4200 0 : mTrashDir->GetNativeLeafName(leafName);
4201 0 : mFailedTrashDirs.AppendElement(leafName);
4202 : }
4203 0 : }
4204 0 : }
4205 :
4206 : // Returns default ("smart") size (in KB) of cache, given available disk space
4207 : // (also in KB)
4208 : static uint32_t
4209 0 : SmartCacheSize(const uint32_t availKB)
4210 : {
4211 0 : uint32_t maxSize = kMaxCacheSizeKB;
4212 :
4213 0 : if (availKB > 100 * 1024 * 1024) {
4214 0 : return maxSize; // skip computing if we're over 100 GB
4215 : }
4216 :
4217 : // Grow/shrink in 10 MB units, deliberately, so that in the common case we
4218 : // don't shrink cache and evict items every time we startup (it's important
4219 : // that we don't slow down startup benchmarks).
4220 0 : uint32_t sz10MBs = 0;
4221 0 : uint32_t avail10MBs = availKB / (1024*10);
4222 :
4223 : // .5% of space above 25 GB
4224 0 : if (avail10MBs > 2500) {
4225 0 : sz10MBs += static_cast<uint32_t>((avail10MBs - 2500)*.005);
4226 0 : avail10MBs = 2500;
4227 : }
4228 : // 1% of space between 7GB -> 25 GB
4229 0 : if (avail10MBs > 700) {
4230 0 : sz10MBs += static_cast<uint32_t>((avail10MBs - 700)*.01);
4231 0 : avail10MBs = 700;
4232 : }
4233 : // 5% of space between 500 MB -> 7 GB
4234 0 : if (avail10MBs > 50) {
4235 0 : sz10MBs += static_cast<uint32_t>((avail10MBs - 50)*.05);
4236 0 : avail10MBs = 50;
4237 : }
4238 :
4239 : #ifdef ANDROID
4240 : // On Android, smaller/older devices may have very little storage and
4241 : // device owners may be sensitive to storage footprint: Use a smaller
4242 : // percentage of available space and a smaller minimum.
4243 :
4244 : // 20% of space up to 500 MB (10 MB min)
4245 : sz10MBs += std::max<uint32_t>(1, static_cast<uint32_t>(avail10MBs * .2));
4246 : #else
4247 : // 40% of space up to 500 MB (50 MB min)
4248 0 : sz10MBs += std::max<uint32_t>(5, static_cast<uint32_t>(avail10MBs * .4));
4249 : #endif
4250 :
4251 0 : return std::min<uint32_t>(maxSize, sz10MBs * 10 * 1024);
4252 : }
4253 :
4254 : nsresult
4255 4 : CacheFileIOManager::UpdateSmartCacheSize(int64_t aFreeSpace)
4256 : {
4257 4 : MOZ_ASSERT(mIOThread->IsCurrentThread());
4258 :
4259 : nsresult rv;
4260 :
4261 4 : if (!CacheObserver::UseNewCache()) {
4262 0 : return NS_ERROR_NOT_AVAILABLE;
4263 : }
4264 :
4265 4 : if (!CacheObserver::SmartCacheSizeEnabled()) {
4266 4 : return NS_ERROR_NOT_AVAILABLE;
4267 : }
4268 :
4269 : // Wait at least kSmartSizeUpdateInterval before recomputing smart size.
4270 : static const TimeDuration kUpdateLimit =
4271 0 : TimeDuration::FromMilliseconds(kSmartSizeUpdateInterval);
4272 0 : if (!mLastSmartSizeTime.IsNull() &&
4273 0 : (TimeStamp::NowLoRes() - mLastSmartSizeTime) < kUpdateLimit) {
4274 0 : return NS_OK;
4275 : }
4276 :
4277 : // Do not compute smart size when cache size is not reliable.
4278 0 : bool isUpToDate = false;
4279 0 : CacheIndex::IsUpToDate(&isUpToDate);
4280 0 : if (!isUpToDate) {
4281 0 : return NS_ERROR_NOT_AVAILABLE;
4282 : }
4283 :
4284 : uint32_t cacheUsage;
4285 0 : rv = CacheIndex::GetCacheSize(&cacheUsage);
4286 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4287 0 : LOG(("CacheFileIOManager::UpdateSmartCacheSize() - Cannot get cacheUsage! "
4288 : "[rv=0x%08" PRIx32 "]", static_cast<uint32_t>(rv)));
4289 0 : return rv;
4290 : }
4291 :
4292 0 : mLastSmartSizeTime = TimeStamp::NowLoRes();
4293 :
4294 0 : uint32_t smartSize = SmartCacheSize(static_cast<uint32_t>(aFreeSpace / 1024) +
4295 0 : cacheUsage);
4296 :
4297 0 : if (smartSize == (CacheObserver::DiskCacheCapacity() >> 10)) {
4298 : // Smart size has not changed.
4299 0 : return NS_OK;
4300 : }
4301 :
4302 0 : CacheObserver::SetDiskCacheCapacity(smartSize << 10);
4303 :
4304 0 : return NS_OK;
4305 : }
4306 :
4307 : // Memory reporting
4308 :
4309 : namespace {
4310 :
4311 : // A helper class that dispatches and waits for an event that gets result of
4312 : // CacheFileIOManager->mHandles.SizeOfExcludingThis() on the I/O thread
4313 : // to safely get handles memory report.
4314 : // We must do this, since the handle list is only accessed and managed w/o
4315 : // locking on the I/O thread. That is by design.
4316 0 : class SizeOfHandlesRunnable : public Runnable
4317 : {
4318 : public:
4319 0 : SizeOfHandlesRunnable(mozilla::MallocSizeOf mallocSizeOf,
4320 : CacheFileHandles const& handles,
4321 : nsTArray<CacheFileHandle*> const& specialHandles)
4322 0 : : Runnable("net::SizeOfHandlesRunnable")
4323 : , mMonitor("SizeOfHandlesRunnable.mMonitor")
4324 : , mMallocSizeOf(mallocSizeOf)
4325 : , mHandles(handles)
4326 0 : , mSpecialHandles(specialHandles)
4327 : {
4328 0 : }
4329 :
4330 0 : size_t Get(CacheIOThread* thread)
4331 : {
4332 0 : nsCOMPtr<nsIEventTarget> target = thread->Target();
4333 0 : if (!target) {
4334 0 : NS_ERROR("If we have the I/O thread we also must have the I/O target");
4335 0 : return 0;
4336 : }
4337 :
4338 0 : mozilla::MonitorAutoLock mon(mMonitor);
4339 0 : mMonitorNotified = false;
4340 0 : nsresult rv = target->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
4341 0 : if (NS_FAILED(rv)) {
4342 0 : NS_ERROR("Dispatch failed, cannot do memory report of CacheFileHandles");
4343 0 : return 0;
4344 : }
4345 :
4346 0 : while (!mMonitorNotified) {
4347 0 : mon.Wait();
4348 : }
4349 0 : return mSize;
4350 : }
4351 :
4352 0 : NS_IMETHOD Run() override
4353 : {
4354 0 : mozilla::MonitorAutoLock mon(mMonitor);
4355 : // Excluding this since the object itself is a member of CacheFileIOManager
4356 : // reported in CacheFileIOManager::SizeOfIncludingThis as part of |this|.
4357 0 : mSize = mHandles.SizeOfExcludingThis(mMallocSizeOf);
4358 0 : for (uint32_t i = 0; i < mSpecialHandles.Length(); ++i) {
4359 0 : mSize += mSpecialHandles[i]->SizeOfIncludingThis(mMallocSizeOf);
4360 : }
4361 :
4362 0 : mMonitorNotified = true;
4363 0 : mon.Notify();
4364 0 : return NS_OK;
4365 : }
4366 :
4367 : private:
4368 : mozilla::Monitor mMonitor;
4369 : bool mMonitorNotified;
4370 : mozilla::MallocSizeOf mMallocSizeOf;
4371 : CacheFileHandles const &mHandles;
4372 : nsTArray<CacheFileHandle *> const &mSpecialHandles;
4373 : size_t mSize;
4374 : };
4375 :
4376 : } // namespace
4377 :
4378 : size_t
4379 0 : CacheFileIOManager::SizeOfExcludingThisInternal(mozilla::MallocSizeOf mallocSizeOf) const
4380 : {
4381 0 : size_t n = 0;
4382 0 : nsCOMPtr<nsISizeOf> sizeOf;
4383 :
4384 0 : if (mIOThread) {
4385 0 : n += mIOThread->SizeOfIncludingThis(mallocSizeOf);
4386 :
4387 : // mHandles and mSpecialHandles must be accessed only on the I/O thread,
4388 : // must sync dispatch.
4389 : RefPtr<SizeOfHandlesRunnable> sizeOfHandlesRunnable =
4390 0 : new SizeOfHandlesRunnable(mallocSizeOf, mHandles, mSpecialHandles);
4391 0 : n += sizeOfHandlesRunnable->Get(mIOThread);
4392 : }
4393 :
4394 : // mHandlesByLastUsed just refers handles reported by mHandles.
4395 :
4396 0 : sizeOf = do_QueryInterface(mCacheDirectory);
4397 0 : if (sizeOf)
4398 0 : n += sizeOf->SizeOfIncludingThis(mallocSizeOf);
4399 :
4400 0 : sizeOf = do_QueryInterface(mMetadataWritesTimer);
4401 0 : if (sizeOf)
4402 0 : n += sizeOf->SizeOfIncludingThis(mallocSizeOf);
4403 :
4404 0 : sizeOf = do_QueryInterface(mTrashTimer);
4405 0 : if (sizeOf)
4406 0 : n += sizeOf->SizeOfIncludingThis(mallocSizeOf);
4407 :
4408 0 : sizeOf = do_QueryInterface(mTrashDir);
4409 0 : if (sizeOf)
4410 0 : n += sizeOf->SizeOfIncludingThis(mallocSizeOf);
4411 :
4412 0 : for (uint32_t i = 0; i < mFailedTrashDirs.Length(); ++i) {
4413 0 : n += mFailedTrashDirs[i].SizeOfExcludingThisIfUnshared(mallocSizeOf);
4414 : }
4415 :
4416 0 : return n;
4417 : }
4418 :
4419 : // static
4420 : size_t
4421 0 : CacheFileIOManager::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
4422 : {
4423 0 : if (!gInstance)
4424 0 : return 0;
4425 :
4426 0 : return gInstance->SizeOfExcludingThisInternal(mallocSizeOf);
4427 : }
4428 :
4429 : // static
4430 : size_t
4431 0 : CacheFileIOManager::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)
4432 : {
4433 0 : return mallocSizeOf(gInstance) + SizeOfExcludingThis(mallocSizeOf);
4434 : }
4435 :
4436 : } // namespace net
4437 : } // namespace mozilla
|