Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 : * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "ActorsParent.h"
8 :
9 : #include "mozIStorageConnection.h"
10 : #include "mozIStorageService.h"
11 : #include "nsIBinaryInputStream.h"
12 : #include "nsIBinaryOutputStream.h"
13 : #include "nsIFile.h"
14 : #include "nsIFileStreams.h"
15 : #include "nsIObserverService.h"
16 : #include "nsIPermissionManager.h"
17 : #include "nsIPrincipal.h"
18 : #include "nsIRunnable.h"
19 : #include "nsISimpleEnumerator.h"
20 : #include "nsIScriptObjectPrincipal.h"
21 : #include "nsIScriptSecurityManager.h"
22 : #include "nsISupportsPrimitives.h"
23 : #include "nsITimer.h"
24 : #include "nsIURI.h"
25 : #include "nsPIDOMWindow.h"
26 :
27 : #include <algorithm>
28 : #include "GeckoProfiler.h"
29 : #include "mozilla/Atomics.h"
30 : #include "mozilla/BasePrincipal.h"
31 : #include "mozilla/CondVar.h"
32 : #include "mozilla/dom/PContent.h"
33 : #include "mozilla/dom/asmjscache/AsmJSCache.h"
34 : #include "mozilla/dom/cache/QuotaClient.h"
35 : #include "mozilla/dom/indexedDB/ActorsParent.h"
36 : #include "mozilla/dom/quota/PQuotaParent.h"
37 : #include "mozilla/dom/quota/PQuotaRequestParent.h"
38 : #include "mozilla/dom/quota/PQuotaUsageRequestParent.h"
39 : #include "mozilla/ipc/BackgroundParent.h"
40 : #include "mozilla/ipc/BackgroundUtils.h"
41 : #include "mozilla/IntegerRange.h"
42 : #include "mozilla/Mutex.h"
43 : #include "mozilla/LazyIdleThread.h"
44 : #include "mozilla/Preferences.h"
45 : #include "mozilla/Services.h"
46 : #include "mozilla/StaticPtr.h"
47 : #include "mozilla/TypeTraits.h"
48 : #include "mozilla/Unused.h"
49 : #include "mozStorageCID.h"
50 : #include "mozStorageHelper.h"
51 : #include "nsAppDirectoryServiceDefs.h"
52 : #include "nsComponentManagerUtils.h"
53 : #include "nsAboutProtocolUtils.h"
54 : #include "nsCharSeparatedTokenizer.h"
55 : #include "nsContentUtils.h"
56 : #include "nsCRTGlue.h"
57 : #include "nsDirectoryServiceUtils.h"
58 : #include "nsEscape.h"
59 : #include "nsNetUtil.h"
60 : #include "nsPrintfCString.h"
61 : #include "nsScriptSecurityManager.h"
62 : #include "nsThreadUtils.h"
63 : #include "nsXULAppAPI.h"
64 : #include "prio.h"
65 : #include "xpcpublic.h"
66 :
67 : #include "OriginScope.h"
68 : #include "QuotaManager.h"
69 : #include "QuotaManagerService.h"
70 : #include "QuotaObject.h"
71 : #include "UsageInfo.h"
72 :
73 : #define DISABLE_ASSERTS_FOR_FUZZING 0
74 :
75 : #if DISABLE_ASSERTS_FOR_FUZZING
76 : #define ASSERT_UNLESS_FUZZING(...) do { } while (0)
77 : #else
78 : #define ASSERT_UNLESS_FUZZING(...) MOZ_ASSERT(false, __VA_ARGS__)
79 : #endif
80 :
81 : #define UNKNOWN_FILE_WARNING(_leafName) \
82 : QM_WARNING("Something (%s) in the directory that doesn't belong!", \
83 : NS_ConvertUTF16toUTF8(leafName).get())
84 :
85 : // The amount of time, in milliseconds, that our IO thread will stay alive
86 : // after the last event it processes.
87 : #define DEFAULT_THREAD_TIMEOUT_MS 30000
88 :
89 : // The amount of time, in milliseconds, that we will wait for active storage
90 : // transactions on shutdown before aborting them.
91 : #define DEFAULT_SHUTDOWN_TIMER_MS 30000
92 :
93 : // Preference that users can set to override temporary storage smart limit
94 : // calculation.
95 : #define PREF_FIXED_LIMIT "dom.quotaManager.temporaryStorage.fixedLimit"
96 : #define PREF_CHUNK_SIZE "dom.quotaManager.temporaryStorage.chunkSize"
97 :
98 : // Preference that is used to enable testing features
99 : #define PREF_TESTING_FEATURES "dom.quotaManager.testing"
100 :
101 : // profile-before-change, when we need to shut down quota manager
102 : #define PROFILE_BEFORE_CHANGE_QM_OBSERVER_ID "profile-before-change-qm"
103 :
104 : #define KB * 1024ULL
105 : #define MB * 1024ULL KB
106 : #define GB * 1024ULL MB
107 :
108 : namespace mozilla {
109 : namespace dom {
110 : namespace quota {
111 :
112 : using namespace mozilla::ipc;
113 :
114 : // We want profiles to be platform-independent so we always need to replace
115 : // the same characters on every platform. Windows has the most extensive set
116 : // of illegal characters so we use its FILE_ILLEGAL_CHARACTERS and
117 : // FILE_PATH_SEPARATOR.
118 : const char QuotaManager::kReplaceChars[] = CONTROL_CHARACTERS "/:*?\"<>|\\";
119 :
120 : namespace {
121 :
122 : /*******************************************************************************
123 : * Constants
124 : ******************************************************************************/
125 :
126 : const uint32_t kSQLitePageSizeOverride = 512;
127 :
128 : // Major storage version. Bump for backwards-incompatible changes.
129 : const uint32_t kMajorStorageVersion = 2;
130 :
131 : // Minor storage version. Bump for backwards-compatible changes.
132 : const uint32_t kMinorStorageVersion = 0;
133 :
134 : // The storage version we store in the SQLite database is a (signed) 32-bit
135 : // integer. The major version is left-shifted 16 bits so the max value is
136 : // 0xFFFF. The minor version occupies the lower 16 bits and its max is 0xFFFF.
137 : static_assert(kMajorStorageVersion <= 0xFFFF,
138 : "Major version needs to fit in 16 bits.");
139 : static_assert(kMinorStorageVersion <= 0xFFFF,
140 : "Minor version needs to fit in 16 bits.");
141 :
142 : const int32_t kStorageVersion =
143 : int32_t((kMajorStorageVersion << 16) + kMinorStorageVersion);
144 :
145 : static_assert(
146 : static_cast<uint32_t>(StorageType::Persistent) ==
147 : static_cast<uint32_t>(PERSISTENCE_TYPE_PERSISTENT),
148 : "Enum values should match.");
149 :
150 : static_assert(
151 : static_cast<uint32_t>(StorageType::Temporary) ==
152 : static_cast<uint32_t>(PERSISTENCE_TYPE_TEMPORARY),
153 : "Enum values should match.");
154 :
155 : static_assert(
156 : static_cast<uint32_t>(StorageType::Default) ==
157 : static_cast<uint32_t>(PERSISTENCE_TYPE_DEFAULT),
158 : "Enum values should match.");
159 :
160 : const char kChromeOrigin[] = "chrome";
161 : const char kAboutHomeOriginPrefix[] = "moz-safe-about:home";
162 : const char kIndexedDBOriginPrefix[] = "indexeddb://";
163 : const char kResourceOriginPrefix[] = "resource://";
164 :
165 : #define INDEXEDDB_DIRECTORY_NAME "indexedDB"
166 : #define STORAGE_DIRECTORY_NAME "storage"
167 : #define PERSISTENT_DIRECTORY_NAME "persistent"
168 : #define PERMANENT_DIRECTORY_NAME "permanent"
169 : #define TEMPORARY_DIRECTORY_NAME "temporary"
170 : #define DEFAULT_DIRECTORY_NAME "default"
171 :
172 : enum AppId {
173 : kNoAppId = nsIScriptSecurityManager::NO_APP_ID,
174 : kUnknownAppId = nsIScriptSecurityManager::UNKNOWN_APP_ID
175 : };
176 :
177 : #define STORAGE_FILE_NAME "storage.sqlite"
178 :
179 : // The name of the file that we use to load/save the last access time of an
180 : // origin.
181 : // XXX We should get rid of old metadata files at some point, bug 1343576.
182 : #define METADATA_FILE_NAME ".metadata"
183 : #define METADATA_TMP_FILE_NAME ".metadata-tmp"
184 : #define METADATA_V2_FILE_NAME ".metadata-v2"
185 : #define METADATA_V2_TMP_FILE_NAME ".metadata-v2-tmp"
186 :
187 : /******************************************************************************
188 : * SQLite functions
189 : ******************************************************************************/
190 :
191 : int32_t
192 0 : MakeStorageVersion(uint32_t aMajorStorageVersion,
193 : uint32_t aMinorStorageVersion)
194 : {
195 0 : return int32_t((aMajorStorageVersion << 16) + aMinorStorageVersion);
196 : }
197 :
198 : uint32_t
199 0 : GetMajorStorageVersion(int32_t aStorageVersion)
200 : {
201 0 : return uint32_t(aStorageVersion >> 16);
202 :
203 : }
204 :
205 : nsresult
206 0 : CreateTables(mozIStorageConnection* aConnection)
207 : {
208 0 : AssertIsOnIOThread();
209 0 : MOZ_ASSERT(aConnection);
210 :
211 : // The database doesn't have any tables for now. It's only used for storage
212 : // version checking.
213 : // However, this is the place where any future tables should be created.
214 :
215 : nsresult rv;
216 :
217 : #ifdef DEBUG
218 : {
219 : int32_t storageVersion;
220 0 : rv = aConnection->GetSchemaVersion(&storageVersion);
221 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
222 0 : return rv;
223 : }
224 :
225 0 : MOZ_ASSERT(storageVersion == 0);
226 : }
227 : #endif
228 :
229 0 : rv = aConnection->SetSchemaVersion(kStorageVersion);
230 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
231 0 : return rv;
232 : }
233 :
234 0 : return NS_OK;
235 : }
236 :
237 : /******************************************************************************
238 : * Quota manager class declarations
239 : ******************************************************************************/
240 :
241 : } // namespace
242 :
243 : class DirectoryLockImpl final
244 : : public DirectoryLock
245 : {
246 : RefPtr<QuotaManager> mQuotaManager;
247 :
248 : const Nullable<PersistenceType> mPersistenceType;
249 : const nsCString mGroup;
250 : const OriginScope mOriginScope;
251 : const Nullable<Client::Type> mClientType;
252 : RefPtr<OpenDirectoryListener> mOpenListener;
253 :
254 : nsTArray<DirectoryLockImpl*> mBlocking;
255 : nsTArray<DirectoryLockImpl*> mBlockedOn;
256 :
257 : const bool mExclusive;
258 :
259 : // Internal quota manager operations use this flag to prevent directory lock
260 : // registraction/unregistration from updating origin access time, etc.
261 : const bool mInternal;
262 :
263 : bool mInvalidated;
264 :
265 : public:
266 : DirectoryLockImpl(QuotaManager* aQuotaManager,
267 : const Nullable<PersistenceType>& aPersistenceType,
268 : const nsACString& aGroup,
269 : const OriginScope& aOriginScope,
270 : const Nullable<Client::Type>& aClientType,
271 : bool aExclusive,
272 : bool aInternal,
273 : OpenDirectoryListener* aOpenListener);
274 :
275 : void
276 : AssertIsOnOwningThread() const
277 : #ifdef DEBUG
278 : ;
279 : #else
280 : { }
281 : #endif
282 :
283 : const Nullable<PersistenceType>&
284 0 : GetPersistenceType() const
285 : {
286 0 : return mPersistenceType;
287 : }
288 :
289 : const nsACString&
290 0 : GetGroup() const
291 : {
292 0 : return mGroup;
293 : }
294 :
295 : const OriginScope&
296 0 : GetOriginScope() const
297 : {
298 0 : return mOriginScope;
299 : }
300 :
301 : const Nullable<Client::Type>&
302 0 : GetClientType() const
303 : {
304 0 : return mClientType;
305 : }
306 :
307 : bool
308 0 : IsInternal() const
309 : {
310 0 : return mInternal;
311 : }
312 :
313 : bool
314 0 : ShouldUpdateLockTable()
315 : {
316 0 : return !mInternal &&
317 0 : mPersistenceType.Value() != PERSISTENCE_TYPE_PERSISTENT;
318 : }
319 :
320 : // Test whether this DirectoryLock needs to wait for the given lock.
321 : bool
322 : MustWaitFor(const DirectoryLockImpl& aLock);
323 :
324 : void
325 0 : AddBlockingLock(DirectoryLockImpl* aLock)
326 : {
327 0 : AssertIsOnOwningThread();
328 :
329 0 : mBlocking.AppendElement(aLock);
330 0 : }
331 :
332 : const nsTArray<DirectoryLockImpl*>&
333 0 : GetBlockedOnLocks()
334 : {
335 0 : return mBlockedOn;
336 : }
337 :
338 : void
339 0 : AddBlockedOnLock(DirectoryLockImpl* aLock)
340 : {
341 0 : AssertIsOnOwningThread();
342 :
343 0 : mBlockedOn.AppendElement(aLock);
344 0 : }
345 :
346 : void
347 0 : MaybeUnblock(DirectoryLockImpl* aLock)
348 : {
349 0 : AssertIsOnOwningThread();
350 :
351 0 : mBlockedOn.RemoveElement(aLock);
352 0 : if (mBlockedOn.IsEmpty()) {
353 0 : NotifyOpenListener();
354 : }
355 0 : }
356 :
357 : void
358 : NotifyOpenListener();
359 :
360 : void
361 0 : Invalidate()
362 : {
363 0 : AssertIsOnOwningThread();
364 :
365 0 : mInvalidated = true;
366 0 : }
367 :
368 0 : NS_INLINE_DECL_REFCOUNTING(DirectoryLockImpl)
369 :
370 : private:
371 : ~DirectoryLockImpl();
372 : };
373 :
374 : class QuotaObject::StoragePressureRunnable final
375 : : public Runnable
376 : {
377 : const uint64_t mUsage;
378 :
379 : public:
380 0 : explicit StoragePressureRunnable(uint64_t aUsage)
381 0 : : Runnable("dom::quota::QuotaObject::StoragePressureRunnable")
382 0 : , mUsage(aUsage)
383 0 : { }
384 :
385 : private:
386 0 : ~StoragePressureRunnable()
387 0 : { }
388 :
389 : NS_DECL_NSIRUNNABLE
390 : };
391 :
392 : class QuotaManager::CreateRunnable final
393 : : public BackgroundThreadObject
394 : , public Runnable
395 : {
396 : nsTArray<nsCOMPtr<nsIRunnable>> mCallbacks;
397 : nsString mBaseDirPath;
398 : RefPtr<QuotaManager> mManager;
399 : nsresult mResultCode;
400 :
401 : enum class State
402 : {
403 : Initial,
404 : CreatingManager,
405 : RegisteringObserver,
406 : CallingCallbacks,
407 : Completed
408 : };
409 :
410 : State mState;
411 :
412 : public:
413 0 : CreateRunnable()
414 0 : : Runnable("dom::quota::QuotaManager::CreateRunnable")
415 : , mResultCode(NS_OK)
416 0 : , mState(State::Initial)
417 : {
418 0 : AssertIsOnBackgroundThread();
419 0 : }
420 :
421 : void
422 0 : AddCallback(nsIRunnable* aCallback)
423 : {
424 0 : AssertIsOnOwningThread();
425 0 : MOZ_ASSERT(aCallback);
426 :
427 0 : mCallbacks.AppendElement(aCallback);
428 0 : }
429 :
430 : private:
431 0 : ~CreateRunnable()
432 0 : { }
433 :
434 : nsresult
435 : Init();
436 :
437 : nsresult
438 : CreateManager();
439 :
440 : nsresult
441 : RegisterObserver();
442 :
443 : void
444 : CallCallbacks();
445 :
446 : State
447 : GetNextState(nsCOMPtr<nsIEventTarget>& aThread);
448 :
449 : NS_DECL_NSIRUNNABLE
450 : };
451 :
452 : class QuotaManager::ShutdownRunnable final
453 : : public Runnable
454 : {
455 : // Only touched on the main thread.
456 : bool& mDone;
457 :
458 : public:
459 0 : explicit ShutdownRunnable(bool& aDone)
460 0 : : Runnable("dom::quota::QuotaManager::ShutdownRunnable")
461 0 : , mDone(aDone)
462 : {
463 0 : MOZ_ASSERT(NS_IsMainThread());
464 0 : }
465 :
466 : private:
467 0 : ~ShutdownRunnable()
468 0 : { }
469 :
470 : NS_DECL_NSIRUNNABLE
471 : };
472 :
473 : class QuotaManager::ShutdownObserver final
474 : : public nsIObserver
475 : {
476 : nsCOMPtr<nsIEventTarget> mBackgroundThread;
477 :
478 : public:
479 0 : explicit ShutdownObserver(nsIEventTarget* aBackgroundThread)
480 0 : : mBackgroundThread(aBackgroundThread)
481 : {
482 0 : MOZ_ASSERT(NS_IsMainThread());
483 0 : }
484 :
485 : NS_DECL_ISUPPORTS
486 : NS_DECL_NSIOBSERVER
487 :
488 : private:
489 0 : ~ShutdownObserver()
490 0 : {
491 0 : MOZ_ASSERT(NS_IsMainThread());
492 0 : }
493 : };
494 :
495 : namespace {
496 :
497 : /*******************************************************************************
498 : * Local class declarations
499 : ******************************************************************************/
500 :
501 : } // namespace
502 :
503 : class OriginInfo final
504 : {
505 : friend class GroupInfo;
506 : friend class QuotaManager;
507 : friend class QuotaObject;
508 :
509 : public:
510 : OriginInfo(GroupInfo* aGroupInfo, const nsACString& aOrigin,
511 : uint64_t aUsage, int64_t aAccessTime, bool aPersisted);
512 :
513 0 : NS_INLINE_DECL_THREADSAFE_REFCOUNTING(OriginInfo)
514 :
515 : int64_t
516 0 : LockedAccessTime() const
517 : {
518 0 : AssertCurrentThreadOwnsQuotaMutex();
519 :
520 0 : return mAccessTime;
521 : }
522 :
523 : bool
524 0 : LockedPersisted() const
525 : {
526 0 : AssertCurrentThreadOwnsQuotaMutex();
527 :
528 0 : return mPersisted;
529 : }
530 :
531 : private:
532 : // Private destructor, to discourage deletion outside of Release():
533 0 : ~OriginInfo()
534 0 : {
535 0 : MOZ_COUNT_DTOR(OriginInfo);
536 :
537 0 : MOZ_ASSERT(!mQuotaObjects.Count());
538 0 : }
539 :
540 : void
541 : LockedDecreaseUsage(int64_t aSize);
542 :
543 : void
544 0 : LockedUpdateAccessTime(int64_t aAccessTime)
545 : {
546 0 : AssertCurrentThreadOwnsQuotaMutex();
547 :
548 0 : mAccessTime = aAccessTime;
549 0 : }
550 :
551 : void
552 : LockedPersist();
553 :
554 : nsDataHashtable<nsStringHashKey, QuotaObject*> mQuotaObjects;
555 :
556 : GroupInfo* mGroupInfo;
557 : const nsCString mOrigin;
558 : uint64_t mUsage;
559 : int64_t mAccessTime;
560 : bool mPersisted;
561 : };
562 :
563 : class OriginInfoLRUComparator
564 : {
565 : public:
566 : bool
567 0 : Equals(const OriginInfo* a, const OriginInfo* b) const
568 : {
569 0 : return a && b ?
570 0 : a->LockedAccessTime() == b->LockedAccessTime() :
571 0 : !a && !b ? true : false;
572 : }
573 :
574 : bool
575 0 : LessThan(const OriginInfo* a, const OriginInfo* b) const
576 : {
577 : return
578 0 : a && b ? a->LockedAccessTime() < b->LockedAccessTime() : b ? true : false;
579 : }
580 : };
581 :
582 : class GroupInfo final
583 : {
584 : friend class GroupInfoPair;
585 : friend class OriginInfo;
586 : friend class QuotaManager;
587 : friend class QuotaObject;
588 :
589 : public:
590 0 : GroupInfo(GroupInfoPair* aGroupInfoPair, PersistenceType aPersistenceType,
591 : const nsACString& aGroup)
592 0 : : mGroupInfoPair(aGroupInfoPair), mPersistenceType(aPersistenceType),
593 0 : mGroup(aGroup), mUsage(0)
594 : {
595 0 : MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_PERSISTENT);
596 :
597 0 : MOZ_COUNT_CTOR(GroupInfo);
598 0 : }
599 :
600 0 : NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GroupInfo)
601 :
602 : private:
603 : // Private destructor, to discourage deletion outside of Release():
604 0 : ~GroupInfo()
605 0 : {
606 0 : MOZ_COUNT_DTOR(GroupInfo);
607 0 : }
608 :
609 : already_AddRefed<OriginInfo>
610 : LockedGetOriginInfo(const nsACString& aOrigin);
611 :
612 : void
613 : LockedAddOriginInfo(OriginInfo* aOriginInfo);
614 :
615 : void
616 : LockedRemoveOriginInfo(const nsACString& aOrigin);
617 :
618 : void
619 : LockedRemoveOriginInfos();
620 :
621 : bool
622 0 : LockedHasOriginInfos()
623 : {
624 0 : AssertCurrentThreadOwnsQuotaMutex();
625 :
626 0 : return !mOriginInfos.IsEmpty();
627 : }
628 :
629 : nsTArray<RefPtr<OriginInfo> > mOriginInfos;
630 :
631 : GroupInfoPair* mGroupInfoPair;
632 : PersistenceType mPersistenceType;
633 : nsCString mGroup;
634 : uint64_t mUsage;
635 : };
636 :
637 : class GroupInfoPair
638 : {
639 : friend class QuotaManager;
640 : friend class QuotaObject;
641 :
642 : public:
643 0 : GroupInfoPair()
644 0 : {
645 0 : MOZ_COUNT_CTOR(GroupInfoPair);
646 0 : }
647 :
648 0 : ~GroupInfoPair()
649 0 : {
650 0 : MOZ_COUNT_DTOR(GroupInfoPair);
651 0 : }
652 :
653 : private:
654 : already_AddRefed<GroupInfo>
655 0 : LockedGetGroupInfo(PersistenceType aPersistenceType)
656 : {
657 0 : AssertCurrentThreadOwnsQuotaMutex();
658 0 : MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_PERSISTENT);
659 :
660 : RefPtr<GroupInfo> groupInfo =
661 0 : GetGroupInfoForPersistenceType(aPersistenceType);
662 0 : return groupInfo.forget();
663 : }
664 :
665 : void
666 0 : LockedSetGroupInfo(PersistenceType aPersistenceType, GroupInfo* aGroupInfo)
667 : {
668 0 : AssertCurrentThreadOwnsQuotaMutex();
669 0 : MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_PERSISTENT);
670 :
671 : RefPtr<GroupInfo>& groupInfo =
672 0 : GetGroupInfoForPersistenceType(aPersistenceType);
673 0 : groupInfo = aGroupInfo;
674 0 : }
675 :
676 : void
677 0 : LockedClearGroupInfo(PersistenceType aPersistenceType)
678 : {
679 0 : AssertCurrentThreadOwnsQuotaMutex();
680 0 : MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_PERSISTENT);
681 :
682 : RefPtr<GroupInfo>& groupInfo =
683 0 : GetGroupInfoForPersistenceType(aPersistenceType);
684 0 : groupInfo = nullptr;
685 0 : }
686 :
687 : bool
688 0 : LockedHasGroupInfos()
689 : {
690 0 : AssertCurrentThreadOwnsQuotaMutex();
691 :
692 0 : return mTemporaryStorageGroupInfo || mDefaultStorageGroupInfo;
693 : }
694 :
695 : RefPtr<GroupInfo>&
696 : GetGroupInfoForPersistenceType(PersistenceType aPersistenceType);
697 :
698 : RefPtr<GroupInfo> mTemporaryStorageGroupInfo;
699 : RefPtr<GroupInfo> mDefaultStorageGroupInfo;
700 : };
701 :
702 : namespace {
703 :
704 : class CollectOriginsHelper final
705 : : public Runnable
706 : {
707 : uint64_t mMinSizeToBeFreed;
708 :
709 : Mutex& mMutex;
710 : CondVar mCondVar;
711 :
712 : // The members below are protected by mMutex.
713 : nsTArray<RefPtr<DirectoryLockImpl>> mLocks;
714 : uint64_t mSizeToBeFreed;
715 : bool mWaiting;
716 :
717 : public:
718 : CollectOriginsHelper(mozilla::Mutex& aMutex,
719 : uint64_t aMinSizeToBeFreed);
720 :
721 : // Blocks the current thread until origins are collected on the main thread.
722 : // The returned value contains an aggregate size of those origins.
723 : int64_t
724 : BlockAndReturnOriginsForEviction(
725 : nsTArray<RefPtr<DirectoryLockImpl>>& aLocks);
726 :
727 : private:
728 0 : ~CollectOriginsHelper()
729 0 : { }
730 :
731 : NS_IMETHOD
732 : Run() override;
733 : };
734 :
735 : class OriginOperationBase
736 : : public BackgroundThreadObject
737 : , public Runnable
738 : {
739 : protected:
740 : nsresult mResultCode;
741 :
742 : enum State {
743 : // Not yet run.
744 : State_Initial,
745 :
746 : // Running initialization on the main thread.
747 : State_Initializing,
748 :
749 : // Running initialization on the owning thread.
750 : State_FinishingInit,
751 :
752 : // Running quota manager initialization on the owning thread.
753 : State_CreatingQuotaManager,
754 :
755 : // Running on the owning thread in the listener for OpenDirectory.
756 : State_DirectoryOpenPending,
757 :
758 : // Running on the IO thread.
759 : State_DirectoryWorkOpen,
760 :
761 : // Running on the owning thread after all work is done.
762 : State_UnblockingOpen,
763 :
764 : // All done.
765 : State_Complete
766 : };
767 :
768 : private:
769 : State mState;
770 : bool mActorDestroyed;
771 :
772 : protected:
773 : bool mNeedsMainThreadInit;
774 : bool mNeedsQuotaManagerInit;
775 :
776 : public:
777 : void
778 0 : NoteActorDestroyed()
779 : {
780 0 : AssertIsOnOwningThread();
781 :
782 0 : mActorDestroyed = true;
783 0 : }
784 :
785 : bool
786 0 : IsActorDestroyed() const
787 : {
788 0 : AssertIsOnOwningThread();
789 :
790 0 : return mActorDestroyed;
791 : }
792 :
793 : protected:
794 0 : explicit OriginOperationBase(
795 : nsIEventTarget* aOwningThread = GetCurrentThreadEventTarget())
796 0 : : BackgroundThreadObject(aOwningThread)
797 : , Runnable("dom::quota::OriginOperationBase")
798 : , mResultCode(NS_OK)
799 : , mState(State_Initial)
800 : , mActorDestroyed(false)
801 : , mNeedsMainThreadInit(false)
802 0 : , mNeedsQuotaManagerInit(false)
803 0 : { }
804 :
805 : // Reference counted.
806 0 : virtual ~OriginOperationBase()
807 0 : {
808 0 : MOZ_ASSERT(mState == State_Complete);
809 0 : MOZ_ASSERT(mActorDestroyed);
810 0 : }
811 :
812 : #ifdef DEBUG
813 : State
814 0 : GetState() const
815 : {
816 0 : return mState;
817 : }
818 : #endif
819 :
820 : void
821 0 : SetState(State aState)
822 : {
823 0 : MOZ_ASSERT(mState == State_Initial);
824 0 : mState = aState;
825 0 : }
826 :
827 : void
828 0 : AdvanceState()
829 : {
830 0 : switch (mState) {
831 : case State_Initial:
832 0 : mState = State_Initializing;
833 0 : return;
834 : case State_Initializing:
835 0 : mState = State_FinishingInit;
836 0 : return;
837 : case State_FinishingInit:
838 0 : mState = State_CreatingQuotaManager;
839 0 : return;
840 : case State_CreatingQuotaManager:
841 0 : mState = State_DirectoryOpenPending;
842 0 : return;
843 : case State_DirectoryOpenPending:
844 0 : mState = State_DirectoryWorkOpen;
845 0 : return;
846 : case State_DirectoryWorkOpen:
847 0 : mState = State_UnblockingOpen;
848 0 : return;
849 : case State_UnblockingOpen:
850 0 : mState = State_Complete;
851 0 : return;
852 : default:
853 0 : MOZ_CRASH("Bad state!");
854 : }
855 : }
856 :
857 : NS_IMETHOD
858 : Run() override;
859 :
860 : virtual nsresult
861 0 : DoInitOnMainThread()
862 : {
863 0 : return NS_OK;
864 : }
865 :
866 : virtual void
867 : Open() = 0;
868 :
869 : nsresult
870 : DirectoryOpen();
871 :
872 : virtual nsresult
873 : DoDirectoryWork(QuotaManager* aQuotaManager) = 0;
874 :
875 : void
876 : Finish(nsresult aResult);
877 :
878 : virtual void
879 : UnblockOpen() = 0;
880 :
881 : private:
882 : nsresult
883 : Init();
884 :
885 : nsresult
886 : InitOnMainThread();
887 :
888 : nsresult
889 : FinishInit();
890 :
891 : nsresult
892 : QuotaManagerOpen();
893 :
894 : nsresult
895 : DirectoryWork();
896 : };
897 :
898 : class FinalizeOriginEvictionOp
899 : : public OriginOperationBase
900 : {
901 : nsTArray<RefPtr<DirectoryLockImpl>> mLocks;
902 :
903 : public:
904 0 : FinalizeOriginEvictionOp(nsIEventTarget* aBackgroundThread,
905 : nsTArray<RefPtr<DirectoryLockImpl>>& aLocks)
906 0 : : OriginOperationBase(aBackgroundThread)
907 : {
908 0 : MOZ_ASSERT(!NS_IsMainThread());
909 :
910 0 : mLocks.SwapElements(aLocks);
911 0 : }
912 :
913 : void
914 : Dispatch();
915 :
916 : void
917 : RunOnIOThreadImmediately();
918 :
919 : private:
920 0 : ~FinalizeOriginEvictionOp()
921 0 : { }
922 :
923 : virtual void
924 : Open() override;
925 :
926 : virtual nsresult
927 : DoDirectoryWork(QuotaManager* aQuotaManager) override;
928 :
929 : virtual void
930 : UnblockOpen() override;
931 : };
932 :
933 : class NormalOriginOperationBase
934 : : public OriginOperationBase
935 : , public OpenDirectoryListener
936 : {
937 : RefPtr<DirectoryLock> mDirectoryLock;
938 :
939 : protected:
940 : Nullable<PersistenceType> mPersistenceType;
941 : OriginScope mOriginScope;
942 : mozilla::Atomic<bool> mCanceled;
943 : const bool mExclusive;
944 :
945 : public:
946 : void
947 0 : RunImmediately()
948 : {
949 0 : MOZ_ASSERT(GetState() == State_Initial);
950 :
951 0 : MOZ_ALWAYS_SUCCEEDS(this->Run());
952 0 : }
953 :
954 : protected:
955 0 : NormalOriginOperationBase(const Nullable<PersistenceType>& aPersistenceType,
956 : const OriginScope& aOriginScope,
957 : bool aExclusive)
958 0 : : mPersistenceType(aPersistenceType)
959 : , mOriginScope(aOriginScope)
960 0 : , mExclusive(aExclusive)
961 : {
962 0 : AssertIsOnOwningThread();
963 0 : }
964 :
965 0 : ~NormalOriginOperationBase()
966 0 : { }
967 :
968 : private:
969 : NS_DECL_ISUPPORTS_INHERITED
970 :
971 : virtual void
972 : Open() override;
973 :
974 : virtual void
975 : UnblockOpen() override;
976 :
977 : // OpenDirectoryListener overrides.
978 : virtual void
979 : DirectoryLockAcquired(DirectoryLock* aLock) override;
980 :
981 : virtual void
982 : DirectoryLockFailed() override;
983 :
984 : // Used to send results before unblocking open.
985 : virtual void
986 : SendResults() = 0;
987 : };
988 :
989 : class SaveOriginAccessTimeOp
990 : : public NormalOriginOperationBase
991 : {
992 : int64_t mTimestamp;
993 :
994 : public:
995 0 : SaveOriginAccessTimeOp(PersistenceType aPersistenceType,
996 : const nsACString& aOrigin,
997 : int64_t aTimestamp)
998 0 : : NormalOriginOperationBase(Nullable<PersistenceType>(aPersistenceType),
999 0 : OriginScope::FromOrigin(aOrigin),
1000 : /* aExclusive */ false)
1001 0 : , mTimestamp(aTimestamp)
1002 : {
1003 0 : AssertIsOnOwningThread();
1004 0 : }
1005 :
1006 : private:
1007 0 : ~SaveOriginAccessTimeOp()
1008 0 : { }
1009 :
1010 : virtual nsresult
1011 : DoDirectoryWork(QuotaManager* aQuotaManager) override;
1012 :
1013 : virtual void
1014 : SendResults() override;
1015 : };
1016 :
1017 : /*******************************************************************************
1018 : * Actor class declarations
1019 : ******************************************************************************/
1020 :
1021 : class Quota final
1022 : : public PQuotaParent
1023 : {
1024 : #ifdef DEBUG
1025 : bool mActorDestroyed;
1026 : #endif
1027 :
1028 : public:
1029 : Quota();
1030 :
1031 0 : NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::quota::Quota)
1032 :
1033 : private:
1034 : ~Quota();
1035 :
1036 : void
1037 : StartIdleMaintenance();
1038 :
1039 : // IPDL methods.
1040 : virtual void
1041 : ActorDestroy(ActorDestroyReason aWhy) override;
1042 :
1043 : virtual PQuotaUsageRequestParent*
1044 : AllocPQuotaUsageRequestParent(const UsageRequestParams& aParams) override;
1045 :
1046 : virtual mozilla::ipc::IPCResult
1047 : RecvPQuotaUsageRequestConstructor(PQuotaUsageRequestParent* aActor,
1048 : const UsageRequestParams& aParams) override;
1049 :
1050 : virtual bool
1051 : DeallocPQuotaUsageRequestParent(PQuotaUsageRequestParent* aActor) override;
1052 :
1053 : virtual PQuotaRequestParent*
1054 : AllocPQuotaRequestParent(const RequestParams& aParams) override;
1055 :
1056 : virtual mozilla::ipc::IPCResult
1057 : RecvPQuotaRequestConstructor(PQuotaRequestParent* aActor,
1058 : const RequestParams& aParams) override;
1059 :
1060 : virtual bool
1061 : DeallocPQuotaRequestParent(PQuotaRequestParent* aActor) override;
1062 :
1063 : virtual mozilla::ipc::IPCResult
1064 : RecvStartIdleMaintenance() override;
1065 :
1066 : virtual mozilla::ipc::IPCResult
1067 : RecvStopIdleMaintenance() override;
1068 : };
1069 :
1070 0 : class QuotaUsageRequestBase
1071 : : public NormalOriginOperationBase
1072 : , public PQuotaUsageRequestParent
1073 : {
1074 : public:
1075 : // May be overridden by subclasses if they need to perform work on the
1076 : // background thread before being run.
1077 : virtual bool
1078 : Init(Quota* aQuota);
1079 :
1080 : protected:
1081 0 : QuotaUsageRequestBase()
1082 0 : : NormalOriginOperationBase(Nullable<PersistenceType>(),
1083 0 : OriginScope::FromNull(),
1084 0 : /* aExclusive */ false)
1085 0 : { }
1086 :
1087 : nsresult
1088 : GetUsageForOrigin(QuotaManager* aQuotaManager,
1089 : PersistenceType aPersistenceType,
1090 : const nsACString& aGroup,
1091 : const nsACString& aOrigin,
1092 : UsageInfo* aUsageInfo);
1093 :
1094 : // Subclasses use this override to set the IPDL response value.
1095 : virtual void
1096 : GetResponse(UsageRequestResponse& aResponse) = 0;
1097 :
1098 : private:
1099 : void
1100 : SendResults() override;
1101 :
1102 : // IPDL methods.
1103 : void
1104 : ActorDestroy(ActorDestroyReason aWhy) override;
1105 :
1106 : mozilla::ipc::IPCResult
1107 : RecvCancel() override;
1108 : };
1109 :
1110 : class GetUsageOp final
1111 : : public QuotaUsageRequestBase
1112 : {
1113 : nsTArray<OriginUsage> mOriginUsages;
1114 : nsDataHashtable<nsCStringHashKey, uint32_t> mOriginUsagesIndex;
1115 :
1116 : bool mGetAll;
1117 :
1118 : public:
1119 : explicit GetUsageOp(const UsageRequestParams& aParams);
1120 :
1121 : private:
1122 0 : ~GetUsageOp()
1123 0 : { }
1124 :
1125 : nsresult
1126 : TraverseRepository(QuotaManager* aQuotaManager,
1127 : PersistenceType aPersistenceType);
1128 :
1129 : nsresult
1130 : DoDirectoryWork(QuotaManager* aQuotaManager) override;
1131 :
1132 : void
1133 : GetResponse(UsageRequestResponse& aResponse) override;
1134 : };
1135 :
1136 : class GetOriginUsageOp final
1137 : : public QuotaUsageRequestBase
1138 : {
1139 : // If mGetGroupUsage is false, we use mUsageInfo to record the origin usage
1140 : // and the file usage. Otherwise, we use it to record the group usage and the
1141 : // limit.
1142 : UsageInfo mUsageInfo;
1143 :
1144 : const OriginUsageParams mParams;
1145 : nsCString mSuffix;
1146 : nsCString mGroup;
1147 : bool mGetGroupUsage;
1148 :
1149 : public:
1150 : explicit GetOriginUsageOp(const UsageRequestParams& aParams);
1151 :
1152 : MOZ_IS_CLASS_INIT bool
1153 : Init(Quota* aQuota) override;
1154 :
1155 : private:
1156 0 : ~GetOriginUsageOp()
1157 0 : { }
1158 :
1159 : MOZ_IS_CLASS_INIT virtual nsresult
1160 : DoInitOnMainThread() override;
1161 :
1162 : virtual nsresult
1163 : DoDirectoryWork(QuotaManager* aQuotaManager) override;
1164 :
1165 : void
1166 : GetResponse(UsageRequestResponse& aResponse) override;
1167 : };
1168 :
1169 0 : class QuotaRequestBase
1170 : : public NormalOriginOperationBase
1171 : , public PQuotaRequestParent
1172 : {
1173 : public:
1174 : // May be overridden by subclasses if they need to perform work on the
1175 : // background thread before being run.
1176 : virtual bool
1177 : Init(Quota* aQuota);
1178 :
1179 : protected:
1180 0 : explicit QuotaRequestBase(bool aExclusive)
1181 0 : : NormalOriginOperationBase(Nullable<PersistenceType>(),
1182 0 : OriginScope::FromNull(),
1183 0 : aExclusive)
1184 0 : { }
1185 :
1186 : // Subclasses use this override to set the IPDL response value.
1187 : virtual void
1188 : GetResponse(RequestResponse& aResponse) = 0;
1189 :
1190 : private:
1191 : virtual void
1192 : SendResults() override;
1193 :
1194 : // IPDL methods.
1195 : virtual void
1196 : ActorDestroy(ActorDestroyReason aWhy) override;
1197 : };
1198 :
1199 : class InitOp final
1200 : : public QuotaRequestBase
1201 : {
1202 : public:
1203 0 : InitOp()
1204 0 : : QuotaRequestBase(/* aExclusive */ false)
1205 : {
1206 0 : AssertIsOnOwningThread();
1207 0 : }
1208 :
1209 : private:
1210 0 : ~InitOp()
1211 0 : { }
1212 :
1213 : nsresult
1214 : DoDirectoryWork(QuotaManager* aQuotaManager) override;
1215 :
1216 : void
1217 : GetResponse(RequestResponse& aResponse) override;
1218 : };
1219 :
1220 : class InitOriginOp final
1221 : : public QuotaRequestBase
1222 : {
1223 : const InitOriginParams mParams;
1224 : nsCString mSuffix;
1225 : nsCString mGroup;
1226 : bool mCreated;
1227 :
1228 : public:
1229 : explicit InitOriginOp(const RequestParams& aParams);
1230 :
1231 : bool
1232 : Init(Quota* aQuota) override;
1233 :
1234 : private:
1235 0 : ~InitOriginOp()
1236 0 : { }
1237 :
1238 : nsresult
1239 : DoInitOnMainThread() override;
1240 :
1241 : nsresult
1242 : DoDirectoryWork(QuotaManager* aQuotaManager) override;
1243 :
1244 : void
1245 : GetResponse(RequestResponse& aResponse) override;
1246 : };
1247 :
1248 : class ResetOrClearOp final
1249 : : public QuotaRequestBase
1250 : {
1251 : const bool mClear;
1252 :
1253 : public:
1254 0 : explicit ResetOrClearOp(bool aClear)
1255 0 : : QuotaRequestBase(/* aExclusive */ true)
1256 0 : , mClear(aClear)
1257 : {
1258 0 : AssertIsOnOwningThread();
1259 0 : }
1260 :
1261 : private:
1262 0 : ~ResetOrClearOp()
1263 0 : { }
1264 :
1265 : void
1266 : DeleteFiles(QuotaManager* aQuotaManager);
1267 :
1268 : virtual nsresult
1269 : DoDirectoryWork(QuotaManager* aQuotaManager) override;
1270 :
1271 : virtual void
1272 : GetResponse(RequestResponse& aResponse) override;
1273 : };
1274 :
1275 0 : class ClearRequestBase
1276 : : public QuotaRequestBase
1277 : {
1278 : protected:
1279 0 : explicit ClearRequestBase(bool aExclusive)
1280 0 : : QuotaRequestBase(aExclusive)
1281 : {
1282 0 : AssertIsOnOwningThread();
1283 0 : }
1284 :
1285 : void
1286 : DeleteFiles(QuotaManager* aQuotaManager,
1287 : PersistenceType aPersistenceType);
1288 :
1289 : nsresult
1290 : DoDirectoryWork(QuotaManager* aQuotaManager) override;
1291 : };
1292 :
1293 : class ClearOriginOp final
1294 : : public ClearRequestBase
1295 : {
1296 : const ClearOriginParams mParams;
1297 :
1298 : public:
1299 : explicit ClearOriginOp(const RequestParams& aParams);
1300 :
1301 : bool
1302 : Init(Quota* aQuota) override;
1303 :
1304 : private:
1305 0 : ~ClearOriginOp()
1306 0 : { }
1307 :
1308 : nsresult
1309 : DoInitOnMainThread() override;
1310 :
1311 : void
1312 : GetResponse(RequestResponse& aResponse) override;
1313 : };
1314 :
1315 : class ClearDataOp final
1316 : : public ClearRequestBase
1317 : {
1318 : const ClearDataParams mParams;
1319 :
1320 : public:
1321 : explicit ClearDataOp(const RequestParams& aParams);
1322 :
1323 : bool
1324 : Init(Quota* aQuota) override;
1325 :
1326 : private:
1327 0 : ~ClearDataOp()
1328 0 : { }
1329 :
1330 : nsresult
1331 : DoInitOnMainThread() override;
1332 :
1333 : void
1334 : GetResponse(RequestResponse& aResponse) override;
1335 : };
1336 :
1337 0 : class PersistRequestBase
1338 : : public QuotaRequestBase
1339 : {
1340 : const PrincipalInfo mPrincipalInfo;
1341 :
1342 : protected:
1343 : nsCString mSuffix;
1344 : nsCString mGroup;
1345 :
1346 : public:
1347 : bool
1348 : Init(Quota* aQuota) override;
1349 :
1350 : protected:
1351 : explicit PersistRequestBase(const PrincipalInfo& aPrincipalInfo);
1352 :
1353 : private:
1354 : nsresult
1355 : DoInitOnMainThread() override;
1356 : };
1357 :
1358 : class PersistedOp final
1359 : : public PersistRequestBase
1360 : {
1361 : bool mPersisted;
1362 :
1363 : public:
1364 : explicit PersistedOp(const RequestParams& aParams);
1365 :
1366 : private:
1367 0 : ~PersistedOp()
1368 0 : { }
1369 :
1370 : nsresult
1371 : DoDirectoryWork(QuotaManager* aQuotaManager) override;
1372 :
1373 : void
1374 : GetResponse(RequestResponse& aResponse) override;
1375 : };
1376 :
1377 : class PersistOp final
1378 : : public PersistRequestBase
1379 : {
1380 : public:
1381 : explicit PersistOp(const RequestParams& aParams);
1382 :
1383 : private:
1384 0 : ~PersistOp()
1385 0 : { }
1386 :
1387 : nsresult
1388 : DoDirectoryWork(QuotaManager* aQuotaManager) override;
1389 :
1390 : void
1391 : GetResponse(RequestResponse& aResponse) override;
1392 : };
1393 :
1394 : /*******************************************************************************
1395 : * Helper Functions
1396 : ******************************************************************************/
1397 :
1398 : template <typename T, bool = mozilla::IsUnsigned<T>::value>
1399 : struct IntChecker
1400 : {
1401 : static void
1402 0 : Assert(T aInt)
1403 : {
1404 : static_assert(mozilla::IsIntegral<T>::value, "Not an integer!");
1405 0 : MOZ_ASSERT(aInt >= 0);
1406 0 : }
1407 : };
1408 :
1409 : template <typename T>
1410 : struct IntChecker<T, true>
1411 : {
1412 : static void
1413 0 : Assert(T aInt)
1414 : {
1415 : static_assert(mozilla::IsIntegral<T>::value, "Not an integer!");
1416 0 : }
1417 : };
1418 :
1419 : template <typename T>
1420 : void
1421 0 : AssertNoOverflow(uint64_t aDest, T aArg)
1422 : {
1423 0 : IntChecker<T>::Assert(aDest);
1424 0 : IntChecker<T>::Assert(aArg);
1425 0 : MOZ_ASSERT(UINT64_MAX - aDest >= uint64_t(aArg));
1426 0 : }
1427 :
1428 : template <typename T, typename U>
1429 : void
1430 0 : AssertNoUnderflow(T aDest, U aArg)
1431 : {
1432 0 : IntChecker<T>::Assert(aDest);
1433 0 : IntChecker<T>::Assert(aArg);
1434 0 : MOZ_ASSERT(uint64_t(aDest) >= uint64_t(aArg));
1435 0 : }
1436 :
1437 : bool
1438 0 : IsOSMetadata(const nsAString& aFileName)
1439 : {
1440 0 : return aFileName.EqualsLiteral(DSSTORE_FILE_NAME);
1441 : }
1442 :
1443 : bool
1444 0 : IsOriginMetadata(const nsAString& aFileName)
1445 : {
1446 0 : return aFileName.EqualsLiteral(METADATA_FILE_NAME) ||
1447 0 : aFileName.EqualsLiteral(METADATA_V2_FILE_NAME) ||
1448 0 : IsOSMetadata(aFileName);
1449 : }
1450 :
1451 : bool
1452 0 : IsTempMetadata(const nsAString& aFileName)
1453 : {
1454 0 : return aFileName.EqualsLiteral(METADATA_TMP_FILE_NAME) ||
1455 0 : aFileName.EqualsLiteral(METADATA_V2_TMP_FILE_NAME);
1456 : }
1457 :
1458 : } // namespace
1459 :
1460 0 : BackgroundThreadObject::BackgroundThreadObject()
1461 0 : : mOwningThread(GetCurrentThreadEventTarget())
1462 : {
1463 0 : AssertIsOnOwningThread();
1464 0 : }
1465 :
1466 0 : BackgroundThreadObject::BackgroundThreadObject(nsIEventTarget* aOwningThread)
1467 0 : : mOwningThread(aOwningThread)
1468 : {
1469 0 : }
1470 :
1471 : #ifdef DEBUG
1472 :
1473 : void
1474 0 : BackgroundThreadObject::AssertIsOnOwningThread() const
1475 : {
1476 0 : AssertIsOnBackgroundThread();
1477 0 : MOZ_ASSERT(mOwningThread);
1478 : bool current;
1479 0 : MOZ_ASSERT(NS_SUCCEEDED(mOwningThread->IsOnCurrentThread(¤t)));
1480 0 : MOZ_ASSERT(current);
1481 0 : }
1482 :
1483 : #endif // DEBUG
1484 :
1485 : nsIEventTarget*
1486 0 : BackgroundThreadObject::OwningThread() const
1487 : {
1488 0 : MOZ_ASSERT(mOwningThread);
1489 0 : return mOwningThread;
1490 : }
1491 :
1492 : bool
1493 0 : IsOnIOThread()
1494 : {
1495 0 : QuotaManager* quotaManager = QuotaManager::Get();
1496 0 : NS_ASSERTION(quotaManager, "Must have a manager here!");
1497 :
1498 : bool currentThread;
1499 0 : return NS_SUCCEEDED(quotaManager->IOThread()->
1500 0 : IsOnCurrentThread(¤tThread)) && currentThread;
1501 : }
1502 :
1503 : void
1504 0 : AssertIsOnIOThread()
1505 : {
1506 0 : NS_ASSERTION(IsOnIOThread(), "Running on the wrong thread!");
1507 0 : }
1508 :
1509 : void
1510 0 : AssertCurrentThreadOwnsQuotaMutex()
1511 : {
1512 : #ifdef DEBUG
1513 0 : QuotaManager* quotaManager = QuotaManager::Get();
1514 0 : NS_ASSERTION(quotaManager, "Must have a manager here!");
1515 :
1516 0 : quotaManager->AssertCurrentThreadOwnsQuotaMutex();
1517 : #endif
1518 0 : }
1519 :
1520 : void
1521 0 : ReportInternalError(const char* aFile, uint32_t aLine, const char* aStr)
1522 : {
1523 : // Get leaf of file path
1524 0 : for (const char* p = aFile; *p; ++p) {
1525 0 : if (*p == '/' && *(p + 1)) {
1526 0 : aFile = p + 1;
1527 : }
1528 : }
1529 :
1530 : nsContentUtils::LogSimpleConsoleError(
1531 0 : NS_ConvertUTF8toUTF16(nsPrintfCString(
1532 : "Quota %s: %s:%" PRIu32, aStr, aFile, aLine)),
1533 0 : "quota");
1534 0 : }
1535 :
1536 : namespace {
1537 :
1538 3 : StaticRefPtr<QuotaManager> gInstance;
1539 : bool gCreateFailed = false;
1540 3 : StaticRefPtr<QuotaManager::CreateRunnable> gCreateRunnable;
1541 : mozilla::Atomic<bool> gShutdown(false);
1542 :
1543 : // Constants for temporary storage limit computing.
1544 : static const int32_t kDefaultFixedLimitKB = -1;
1545 : static const uint32_t kDefaultChunkSizeKB = 10 * 1024;
1546 : int32_t gFixedLimitKB = kDefaultFixedLimitKB;
1547 : uint32_t gChunkSizeKB = kDefaultChunkSizeKB;
1548 :
1549 : bool gTestingEnabled = false;
1550 :
1551 : class StorageDirectoryHelper
1552 : : public Runnable
1553 : {
1554 : mozilla::Mutex mMutex;
1555 : mozilla::CondVar mCondVar;
1556 : nsresult mMainThreadResultCode;
1557 : bool mWaiting;
1558 :
1559 : protected:
1560 : struct OriginProps;
1561 :
1562 : nsTArray<OriginProps> mOriginProps;
1563 :
1564 : nsCOMPtr<nsIFile> mDirectory;
1565 :
1566 : const bool mPersistent;
1567 :
1568 : public:
1569 0 : StorageDirectoryHelper(nsIFile* aDirectory, bool aPersistent)
1570 0 : : Runnable("dom::quota::StorageDirectoryHelper")
1571 : , mMutex("StorageDirectoryHelper::mMutex")
1572 : , mCondVar(mMutex, "StorageDirectoryHelper::mCondVar")
1573 : , mMainThreadResultCode(NS_OK)
1574 : , mWaiting(true)
1575 : , mDirectory(aDirectory)
1576 0 : , mPersistent(aPersistent)
1577 : {
1578 0 : AssertIsOnIOThread();
1579 0 : }
1580 :
1581 : protected:
1582 0 : ~StorageDirectoryHelper()
1583 0 : { }
1584 :
1585 : nsresult
1586 : GetDirectoryMetadata(nsIFile* aDirectory,
1587 : int64_t& aTimestamp,
1588 : nsACString& aGroup,
1589 : nsACString& aOrigin,
1590 : Nullable<bool>& aIsApp);
1591 :
1592 : // Upgrade helper to load the contents of ".metadata-v2" files from previous
1593 : // schema versions. Although QuotaManager has a similar GetDirectoryMetadata2
1594 : // method, it is only intended to read current version ".metadata-v2" files.
1595 : // And unlike the old ".metadata" files, the ".metadata-v2" format can evolve
1596 : // because our "storage.sqlite" lets us track the overall version of the
1597 : // storage directory.
1598 : nsresult
1599 : GetDirectoryMetadata2(nsIFile* aDirectory,
1600 : int64_t& aTimestamp,
1601 : nsACString& aSuffix,
1602 : nsACString& aGroup,
1603 : nsACString& aOrigin,
1604 : bool& aIsApp);
1605 :
1606 : nsresult
1607 : RemoveObsoleteOrigin(const OriginProps& aOriginProps);
1608 :
1609 : nsresult
1610 : ProcessOriginDirectories();
1611 :
1612 : virtual nsresult
1613 : ProcessOriginDirectory(const OriginProps& aOriginProps) = 0;
1614 :
1615 : private:
1616 : nsresult
1617 : RunOnMainThread();
1618 :
1619 : NS_IMETHOD
1620 : Run() override;
1621 : };
1622 :
1623 0 : struct StorageDirectoryHelper::OriginProps
1624 : {
1625 : enum Type
1626 : {
1627 : eChrome,
1628 : eContent,
1629 : eObsolete
1630 : };
1631 :
1632 : nsCOMPtr<nsIFile> mDirectory;
1633 : nsString mLeafName;
1634 : nsCString mSpec;
1635 : OriginAttributes mAttrs;
1636 : int64_t mTimestamp;
1637 : nsCString mSuffix;
1638 : nsCString mGroup;
1639 : nsCString mOrigin;
1640 :
1641 : Type mType;
1642 : bool mNeedsRestore;
1643 : bool mNeedsRestore2;
1644 : bool mIgnore;
1645 :
1646 : public:
1647 0 : explicit OriginProps()
1648 0 : : mTimestamp(0)
1649 : , mType(eContent)
1650 : , mNeedsRestore(false)
1651 : , mNeedsRestore2(false)
1652 0 : , mIgnore(false)
1653 0 : { }
1654 :
1655 : nsresult
1656 : Init(nsIFile* aDirectory);
1657 : };
1658 :
1659 0 : class MOZ_STACK_CLASS OriginParser final
1660 : {
1661 : public:
1662 : enum ResultType {
1663 : InvalidOrigin,
1664 : ObsoleteOrigin,
1665 : ValidOrigin
1666 : };
1667 :
1668 : private:
1669 : static bool
1670 0 : IgnoreWhitespace(char16_t /* aChar */)
1671 : {
1672 0 : return false;
1673 : }
1674 :
1675 : typedef nsCCharSeparatedTokenizerTemplate<IgnoreWhitespace> Tokenizer;
1676 :
1677 : enum SchemeType {
1678 : eNone,
1679 : eFile,
1680 : eAbout
1681 : };
1682 :
1683 : enum State {
1684 : eExpectingAppIdOrScheme,
1685 : eExpectingInMozBrowser,
1686 : eExpectingScheme,
1687 : eExpectingEmptyToken1,
1688 : eExpectingEmptyToken2,
1689 : eExpectingEmptyToken3,
1690 : eExpectingHost,
1691 : eExpectingPort,
1692 : eExpectingEmptyTokenOrDriveLetterOrPathnameComponent,
1693 : eExpectingEmptyTokenOrPathnameComponent,
1694 : eComplete,
1695 : eHandledTrailingSeparator
1696 : };
1697 :
1698 : const nsCString mOrigin;
1699 : const OriginAttributes mOriginAttributes;
1700 : Tokenizer mTokenizer;
1701 :
1702 : uint32_t mAppId;
1703 : nsCString mScheme;
1704 : nsCString mHost;
1705 : Nullable<uint32_t> mPort;
1706 : nsTArray<nsCString> mPathnameComponents;
1707 : nsCString mHandledTokens;
1708 :
1709 : SchemeType mSchemeType;
1710 : State mState;
1711 : bool mInIsolatedMozBrowser;
1712 : bool mMaybeDriveLetter;
1713 : bool mError;
1714 :
1715 : public:
1716 0 : OriginParser(const nsACString& aOrigin,
1717 : const OriginAttributes& aOriginAttributes)
1718 0 : : mOrigin(aOrigin)
1719 : , mOriginAttributes(aOriginAttributes)
1720 : , mTokenizer(aOrigin, '+')
1721 : , mAppId(kNoAppId)
1722 : , mPort()
1723 : , mSchemeType(eNone)
1724 : , mState(eExpectingAppIdOrScheme)
1725 : , mInIsolatedMozBrowser(false)
1726 : , mMaybeDriveLetter(false)
1727 0 : , mError(false)
1728 0 : { }
1729 :
1730 : static ResultType
1731 : ParseOrigin(const nsACString& aOrigin,
1732 : nsCString& aSpec,
1733 : OriginAttributes* aAttrs);
1734 :
1735 : ResultType
1736 : Parse(nsACString& aSpec, OriginAttributes* aAttrs);
1737 :
1738 : private:
1739 : void
1740 : HandleScheme(const nsDependentCSubstring& aToken);
1741 :
1742 : void
1743 : HandlePathnameComponent(const nsDependentCSubstring& aToken);
1744 :
1745 : void
1746 : HandleToken(const nsDependentCSubstring& aToken);
1747 :
1748 : void
1749 : HandleTrailingSeparator();
1750 : };
1751 :
1752 0 : class CreateOrUpgradeDirectoryMetadataHelper final
1753 : : public StorageDirectoryHelper
1754 : {
1755 : nsCOMPtr<nsIFile> mPermanentStorageDir;
1756 :
1757 : public:
1758 0 : CreateOrUpgradeDirectoryMetadataHelper(nsIFile* aDirectory,
1759 : bool aPersistent)
1760 0 : : StorageDirectoryHelper(aDirectory, aPersistent)
1761 0 : { }
1762 :
1763 : nsresult
1764 : CreateOrUpgradeMetadataFiles();
1765 :
1766 : private:
1767 : nsresult
1768 : MaybeUpgradeOriginDirectory(nsIFile* aDirectory);
1769 :
1770 : nsresult
1771 : ProcessOriginDirectory(const OriginProps& aOriginProps) override;
1772 : };
1773 :
1774 0 : class UpgradeStorageFrom0_0To1_0Helper final
1775 : : public StorageDirectoryHelper
1776 : {
1777 : public:
1778 0 : UpgradeStorageFrom0_0To1_0Helper(nsIFile* aDirectory,
1779 : bool aPersistent)
1780 0 : : StorageDirectoryHelper(aDirectory, aPersistent)
1781 0 : { }
1782 :
1783 : nsresult
1784 : DoUpgrade();
1785 :
1786 : private:
1787 : nsresult
1788 : ProcessOriginDirectory(const OriginProps& aOriginProps) override;
1789 : };
1790 :
1791 0 : class UpgradeStorageFrom1_0To2_0Helper final
1792 : : public StorageDirectoryHelper
1793 : {
1794 : public:
1795 0 : UpgradeStorageFrom1_0To2_0Helper(nsIFile* aDirectory,
1796 : bool aPersistent)
1797 0 : : StorageDirectoryHelper(aDirectory, aPersistent)
1798 0 : { }
1799 :
1800 : nsresult
1801 : DoUpgrade();
1802 :
1803 : private:
1804 : nsresult
1805 : MaybeUpgradeClients(const OriginProps& aOriginProps);
1806 :
1807 : nsresult
1808 : MaybeRemoveAppsData(const OriginProps& aOriginProps,
1809 : bool* aRemoved);
1810 :
1811 : nsresult
1812 : MaybeStripObsoleteOriginAttributes(const OriginProps& aOriginProps,
1813 : bool* aStripped);
1814 :
1815 : nsresult
1816 : ProcessOriginDirectory(const OriginProps& aOriginProps) override;
1817 : };
1818 :
1819 0 : class RestoreDirectoryMetadata2Helper final
1820 : : public StorageDirectoryHelper
1821 : {
1822 : public:
1823 0 : RestoreDirectoryMetadata2Helper(nsIFile* aDirectory,
1824 : bool aPersistent)
1825 0 : : StorageDirectoryHelper(aDirectory, aPersistent)
1826 0 : { }
1827 :
1828 : nsresult
1829 : RestoreMetadata2File();
1830 :
1831 : private:
1832 : nsresult
1833 : ProcessOriginDirectory(const OriginProps& aOriginProps) override;
1834 : };
1835 :
1836 : void
1837 0 : SanitizeOriginString(nsCString& aOrigin)
1838 : {
1839 :
1840 : #ifdef XP_WIN
1841 : NS_ASSERTION(!strcmp(QuotaManager::kReplaceChars,
1842 : FILE_ILLEGAL_CHARACTERS FILE_PATH_SEPARATOR),
1843 : "Illegal file characters have changed!");
1844 : #endif
1845 :
1846 0 : aOrigin.ReplaceChar(QuotaManager::kReplaceChars, '+');
1847 0 : }
1848 :
1849 : nsresult
1850 0 : CloneStoragePath(nsIFile* aBaseDir,
1851 : const nsAString& aStorageName,
1852 : nsAString& aStoragePath)
1853 : {
1854 : nsresult rv;
1855 :
1856 0 : nsCOMPtr<nsIFile> storageDir;
1857 0 : rv = aBaseDir->Clone(getter_AddRefs(storageDir));
1858 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1859 0 : return rv;
1860 : }
1861 :
1862 0 : rv = storageDir->Append(aStorageName);
1863 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1864 0 : return rv;
1865 : }
1866 :
1867 0 : rv = storageDir->GetPath(aStoragePath);
1868 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1869 0 : return rv;
1870 : }
1871 :
1872 0 : return NS_OK;
1873 : }
1874 :
1875 : int64_t
1876 0 : GetLastModifiedTime(nsIFile* aFile, bool aPersistent)
1877 : {
1878 0 : AssertIsOnIOThread();
1879 0 : MOZ_ASSERT(aFile);
1880 :
1881 : class MOZ_STACK_CLASS Helper final
1882 : {
1883 : public:
1884 : static nsresult
1885 0 : GetLastModifiedTime(nsIFile* aFile, int64_t* aTimestamp)
1886 : {
1887 0 : AssertIsOnIOThread();
1888 0 : MOZ_ASSERT(aFile);
1889 0 : MOZ_ASSERT(aTimestamp);
1890 :
1891 : bool isDirectory;
1892 0 : nsresult rv = aFile->IsDirectory(&isDirectory);
1893 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1894 0 : return rv;
1895 : }
1896 :
1897 0 : if (!isDirectory) {
1898 0 : nsString leafName;
1899 0 : rv = aFile->GetLeafName(leafName);
1900 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1901 0 : return rv;
1902 : }
1903 :
1904 0 : if (IsOriginMetadata(leafName) ||
1905 0 : IsTempMetadata(leafName)) {
1906 0 : return NS_OK;
1907 : }
1908 :
1909 : int64_t timestamp;
1910 0 : rv = aFile->GetLastModifiedTime(×tamp);
1911 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1912 0 : return rv;
1913 : }
1914 :
1915 : // Need to convert from milliseconds to microseconds.
1916 0 : MOZ_ASSERT((INT64_MAX / PR_USEC_PER_MSEC) > timestamp);
1917 0 : timestamp *= int64_t(PR_USEC_PER_MSEC);
1918 :
1919 0 : if (timestamp > *aTimestamp) {
1920 0 : *aTimestamp = timestamp;
1921 : }
1922 0 : return NS_OK;
1923 : }
1924 :
1925 0 : nsCOMPtr<nsISimpleEnumerator> entries;
1926 0 : rv = aFile->GetDirectoryEntries(getter_AddRefs(entries));
1927 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1928 0 : return rv;
1929 : }
1930 :
1931 : bool hasMore;
1932 0 : while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) {
1933 0 : nsCOMPtr<nsISupports> entry;
1934 0 : rv = entries->GetNext(getter_AddRefs(entry));
1935 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1936 0 : return rv;
1937 : }
1938 :
1939 0 : nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
1940 0 : MOZ_ASSERT(file);
1941 :
1942 0 : rv = GetLastModifiedTime(file, aTimestamp);
1943 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1944 0 : return rv;
1945 : }
1946 : }
1947 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1948 0 : return rv;
1949 : }
1950 :
1951 0 : return NS_OK;
1952 : }
1953 : };
1954 :
1955 0 : if (aPersistent) {
1956 0 : return PR_Now();
1957 : }
1958 :
1959 0 : int64_t timestamp = INT64_MIN;
1960 0 : nsresult rv = Helper::GetLastModifiedTime(aFile, ×tamp);
1961 0 : if (NS_FAILED(rv)) {
1962 0 : timestamp = PR_Now();
1963 : }
1964 :
1965 0 : return timestamp;
1966 : }
1967 :
1968 : nsresult
1969 0 : EnsureDirectory(nsIFile* aDirectory, bool* aCreated)
1970 : {
1971 0 : AssertIsOnIOThread();
1972 :
1973 0 : nsresult rv = aDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755);
1974 0 : if (rv == NS_ERROR_FILE_ALREADY_EXISTS) {
1975 : bool isDirectory;
1976 0 : rv = aDirectory->IsDirectory(&isDirectory);
1977 0 : NS_ENSURE_SUCCESS(rv, rv);
1978 0 : NS_ENSURE_TRUE(isDirectory, NS_ERROR_UNEXPECTED);
1979 :
1980 0 : *aCreated = false;
1981 : }
1982 : else {
1983 0 : NS_ENSURE_SUCCESS(rv, rv);
1984 :
1985 0 : *aCreated = true;
1986 : }
1987 :
1988 0 : return NS_OK;
1989 : }
1990 :
1991 : nsresult
1992 0 : EnsureOriginDirectory(nsIFile* aDirectory, bool* aCreated)
1993 : {
1994 0 : AssertIsOnIOThread();
1995 :
1996 : nsresult rv;
1997 :
1998 : #ifndef RELEASE_OR_BETA
1999 : bool exists;
2000 0 : rv = aDirectory->Exists(&exists);
2001 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2002 0 : return rv;
2003 : }
2004 :
2005 0 : if (!exists) {
2006 0 : nsString leafName;
2007 0 : nsresult rv = aDirectory->GetLeafName(leafName);
2008 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2009 0 : return rv;
2010 : }
2011 :
2012 0 : if (!leafName.EqualsLiteral(kChromeOrigin)) {
2013 0 : nsCString spec;
2014 0 : OriginAttributes attrs;
2015 : OriginParser::ResultType result =
2016 0 : OriginParser::ParseOrigin(NS_ConvertUTF16toUTF8(leafName),
2017 : spec,
2018 0 : &attrs);
2019 0 : if (NS_WARN_IF(result != OriginParser::ValidOrigin)) {
2020 0 : QM_WARNING("Preventing creation of a new origin directory which is not "
2021 : "supported by our origin parser or is obsolete!");
2022 :
2023 0 : return NS_ERROR_FAILURE;
2024 : }
2025 : }
2026 : }
2027 : #endif
2028 :
2029 0 : rv = EnsureDirectory(aDirectory, aCreated);
2030 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2031 0 : return rv;
2032 : }
2033 :
2034 0 : return NS_OK;
2035 : }
2036 :
2037 : enum FileFlag {
2038 : kTruncateFileFlag,
2039 : kUpdateFileFlag,
2040 : kAppendFileFlag
2041 : };
2042 :
2043 : nsresult
2044 0 : GetOutputStream(nsIFile* aFile,
2045 : FileFlag aFileFlag,
2046 : nsIOutputStream** aStream)
2047 : {
2048 0 : AssertIsOnIOThread();
2049 :
2050 : nsresult rv;
2051 :
2052 0 : nsCOMPtr<nsIOutputStream> outputStream;
2053 0 : switch (aFileFlag) {
2054 : case kTruncateFileFlag: {
2055 0 : rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream),
2056 0 : aFile);
2057 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2058 0 : return rv;
2059 : }
2060 :
2061 0 : break;
2062 : }
2063 :
2064 : case kUpdateFileFlag: {
2065 : bool exists;
2066 0 : rv = aFile->Exists(&exists);
2067 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2068 0 : return rv;
2069 : }
2070 :
2071 0 : if (!exists) {
2072 0 : *aStream = nullptr;
2073 0 : return NS_OK;
2074 : }
2075 :
2076 0 : nsCOMPtr<nsIFileStream> stream;
2077 0 : rv = NS_NewLocalFileStream(getter_AddRefs(stream), aFile);
2078 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2079 0 : return rv;
2080 : }
2081 :
2082 0 : outputStream = do_QueryInterface(stream);
2083 0 : if (NS_WARN_IF(!outputStream)) {
2084 0 : return NS_ERROR_FAILURE;
2085 : }
2086 :
2087 0 : break;
2088 : }
2089 :
2090 : case kAppendFileFlag: {
2091 0 : rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream),
2092 : aFile,
2093 0 : PR_WRONLY | PR_CREATE_FILE | PR_APPEND);
2094 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2095 0 : return rv;
2096 : }
2097 :
2098 0 : break;
2099 : }
2100 :
2101 : default:
2102 0 : MOZ_CRASH("Should never get here!");
2103 : }
2104 :
2105 0 : outputStream.forget(aStream);
2106 0 : return NS_OK;
2107 : }
2108 :
2109 : nsresult
2110 0 : GetBinaryOutputStream(nsIFile* aFile,
2111 : FileFlag aFileFlag,
2112 : nsIBinaryOutputStream** aStream)
2113 : {
2114 0 : nsCOMPtr<nsIOutputStream> outputStream;
2115 0 : nsresult rv = GetOutputStream(aFile,
2116 : aFileFlag,
2117 0 : getter_AddRefs(outputStream));
2118 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2119 0 : return rv;
2120 : }
2121 :
2122 : nsCOMPtr<nsIBinaryOutputStream> binaryStream =
2123 0 : do_CreateInstance("@mozilla.org/binaryoutputstream;1");
2124 0 : if (NS_WARN_IF(!binaryStream)) {
2125 0 : return NS_ERROR_FAILURE;
2126 : }
2127 :
2128 0 : rv = binaryStream->SetOutputStream(outputStream);
2129 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2130 0 : return rv;
2131 : }
2132 :
2133 0 : binaryStream.forget(aStream);
2134 0 : return NS_OK;
2135 : }
2136 :
2137 : void
2138 0 : GetJarPrefix(uint32_t aAppId,
2139 : bool aInIsolatedMozBrowser,
2140 : nsACString& aJarPrefix)
2141 : {
2142 0 : MOZ_ASSERT(aAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID);
2143 :
2144 0 : if (aAppId == nsIScriptSecurityManager::UNKNOWN_APP_ID) {
2145 0 : aAppId = nsIScriptSecurityManager::NO_APP_ID;
2146 : }
2147 :
2148 0 : aJarPrefix.Truncate();
2149 :
2150 : // Fallback.
2151 0 : if (aAppId == nsIScriptSecurityManager::NO_APP_ID && !aInIsolatedMozBrowser) {
2152 0 : return;
2153 : }
2154 :
2155 : // aJarPrefix = appId + "+" + { 't', 'f' } + "+";
2156 0 : aJarPrefix.AppendInt(aAppId);
2157 0 : aJarPrefix.Append('+');
2158 0 : aJarPrefix.Append(aInIsolatedMozBrowser ? 't' : 'f');
2159 0 : aJarPrefix.Append('+');
2160 : }
2161 :
2162 : nsresult
2163 0 : CreateDirectoryMetadata(nsIFile* aDirectory, int64_t aTimestamp,
2164 : const nsACString& aSuffix, const nsACString& aGroup,
2165 : const nsACString& aOrigin)
2166 : {
2167 0 : AssertIsOnIOThread();
2168 :
2169 0 : OriginAttributes groupAttributes;
2170 :
2171 0 : nsCString groupNoSuffix;
2172 0 : bool ok = groupAttributes.PopulateFromOrigin(aGroup, groupNoSuffix);
2173 0 : if (!ok) {
2174 0 : return NS_ERROR_FAILURE;
2175 : }
2176 :
2177 0 : nsCString groupPrefix;
2178 0 : GetJarPrefix(groupAttributes.mAppId,
2179 0 : groupAttributes.mInIsolatedMozBrowser,
2180 0 : groupPrefix);
2181 :
2182 0 : nsCString group = groupPrefix + groupNoSuffix;
2183 :
2184 0 : OriginAttributes originAttributes;
2185 :
2186 0 : nsCString originNoSuffix;
2187 0 : ok = originAttributes.PopulateFromOrigin(aOrigin, originNoSuffix);
2188 0 : if (!ok) {
2189 0 : return NS_ERROR_FAILURE;
2190 : }
2191 :
2192 0 : nsCString originPrefix;
2193 0 : GetJarPrefix(originAttributes.mAppId,
2194 0 : originAttributes.mInIsolatedMozBrowser,
2195 0 : originPrefix);
2196 :
2197 0 : nsCString origin = originPrefix + originNoSuffix;
2198 :
2199 0 : MOZ_ASSERT(groupPrefix == originPrefix);
2200 :
2201 0 : nsCOMPtr<nsIFile> file;
2202 0 : nsresult rv = aDirectory->Clone(getter_AddRefs(file));
2203 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2204 0 : return rv;
2205 : }
2206 :
2207 0 : rv = file->Append(NS_LITERAL_STRING(METADATA_TMP_FILE_NAME));
2208 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2209 0 : return rv;
2210 : }
2211 :
2212 0 : nsCOMPtr<nsIBinaryOutputStream> stream;
2213 0 : rv = GetBinaryOutputStream(file, kTruncateFileFlag, getter_AddRefs(stream));
2214 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2215 0 : return rv;
2216 : }
2217 :
2218 0 : MOZ_ASSERT(stream);
2219 :
2220 0 : rv = stream->Write64(aTimestamp);
2221 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2222 0 : return rv;
2223 : }
2224 :
2225 0 : rv = stream->WriteStringZ(group.get());
2226 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2227 0 : return rv;
2228 : }
2229 :
2230 0 : rv = stream->WriteStringZ(origin.get());
2231 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2232 0 : return rv;
2233 : }
2234 :
2235 : // Currently unused (used to be isApp).
2236 0 : rv = stream->WriteBoolean(false);
2237 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2238 0 : return rv;
2239 : }
2240 :
2241 0 : rv = stream->Flush();
2242 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2243 0 : return rv;
2244 : }
2245 :
2246 0 : rv = stream->Close();
2247 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2248 0 : return rv;
2249 : }
2250 :
2251 0 : rv = file->RenameTo(nullptr, NS_LITERAL_STRING(METADATA_FILE_NAME));
2252 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2253 0 : return rv;
2254 : }
2255 :
2256 0 : return NS_OK;
2257 : }
2258 :
2259 : nsresult
2260 0 : CreateDirectoryMetadata2(nsIFile* aDirectory,
2261 : int64_t aTimestamp,
2262 : bool aPersisted,
2263 : const nsACString& aSuffix,
2264 : const nsACString& aGroup,
2265 : const nsACString& aOrigin)
2266 : {
2267 0 : AssertIsOnIOThread();
2268 0 : MOZ_ASSERT(aDirectory);
2269 :
2270 0 : nsCOMPtr<nsIFile> file;
2271 0 : nsresult rv = aDirectory->Clone(getter_AddRefs(file));
2272 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2273 0 : return rv;
2274 : }
2275 :
2276 0 : rv = file->Append(NS_LITERAL_STRING(METADATA_V2_TMP_FILE_NAME));
2277 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2278 0 : return rv;
2279 : }
2280 :
2281 0 : nsCOMPtr<nsIBinaryOutputStream> stream;
2282 0 : rv = GetBinaryOutputStream(file, kTruncateFileFlag, getter_AddRefs(stream));
2283 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2284 0 : return rv;
2285 : }
2286 :
2287 0 : MOZ_ASSERT(stream);
2288 :
2289 0 : rv = stream->Write64(aTimestamp);
2290 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2291 0 : return rv;
2292 : }
2293 :
2294 0 : rv = stream->WriteBoolean(aPersisted);
2295 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2296 0 : return rv;
2297 : }
2298 :
2299 : // Reserved data 1
2300 0 : rv = stream->Write32(0);
2301 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2302 0 : return rv;
2303 : }
2304 :
2305 : // Reserved data 2
2306 0 : rv = stream->Write32(0);
2307 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2308 0 : return rv;
2309 : }
2310 :
2311 : // The suffix isn't used right now, but we might need it in future. It's
2312 : // a bit of redundancy we can live with given how painful is to upgrade
2313 : // metadata files.
2314 0 : rv = stream->WriteStringZ(PromiseFlatCString(aSuffix).get());
2315 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2316 0 : return rv;
2317 : }
2318 :
2319 0 : rv = stream->WriteStringZ(PromiseFlatCString(aGroup).get());
2320 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2321 0 : return rv;
2322 : }
2323 :
2324 0 : rv = stream->WriteStringZ(PromiseFlatCString(aOrigin).get());
2325 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2326 0 : return rv;
2327 : }
2328 :
2329 : // Currently unused (used to be isApp).
2330 0 : rv = stream->WriteBoolean(false);
2331 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2332 0 : return rv;
2333 : }
2334 :
2335 0 : rv = stream->Flush();
2336 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2337 0 : return rv;
2338 : }
2339 :
2340 0 : rv = stream->Close();
2341 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2342 0 : return rv;
2343 : }
2344 :
2345 0 : rv = file->RenameTo(nullptr, NS_LITERAL_STRING(METADATA_V2_FILE_NAME));
2346 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2347 0 : return rv;
2348 : }
2349 :
2350 0 : return NS_OK;
2351 : }
2352 :
2353 : nsresult
2354 0 : CreateDirectoryMetadataFiles(nsIFile* aDirectory,
2355 : bool aPersisted,
2356 : const nsACString& aSuffix,
2357 : const nsACString& aGroup,
2358 : const nsACString& aOrigin,
2359 : int64_t* aTimestamp)
2360 : {
2361 0 : AssertIsOnIOThread();
2362 :
2363 0 : int64_t timestamp = PR_Now();
2364 :
2365 : nsresult rv = CreateDirectoryMetadata(aDirectory,
2366 : timestamp,
2367 : aSuffix,
2368 : aGroup,
2369 0 : aOrigin);
2370 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2371 0 : return rv;
2372 : }
2373 :
2374 0 : rv = CreateDirectoryMetadata2(aDirectory,
2375 : timestamp,
2376 : aPersisted,
2377 : aSuffix,
2378 : aGroup,
2379 0 : aOrigin);
2380 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2381 0 : return rv;
2382 : }
2383 :
2384 0 : if (aTimestamp) {
2385 0 : *aTimestamp = timestamp;
2386 : }
2387 0 : return NS_OK;
2388 : }
2389 :
2390 : nsresult
2391 0 : GetBinaryInputStream(nsIFile* aDirectory,
2392 : const nsAString& aFilename,
2393 : nsIBinaryInputStream** aStream)
2394 : {
2395 0 : MOZ_ASSERT(!NS_IsMainThread());
2396 0 : MOZ_ASSERT(aDirectory);
2397 0 : MOZ_ASSERT(aStream);
2398 :
2399 0 : nsCOMPtr<nsIFile> file;
2400 0 : nsresult rv = aDirectory->Clone(getter_AddRefs(file));
2401 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2402 0 : return rv;
2403 : }
2404 :
2405 0 : rv = file->Append(aFilename);
2406 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2407 0 : return rv;
2408 : }
2409 :
2410 0 : nsCOMPtr<nsIInputStream> stream;
2411 0 : rv = NS_NewLocalFileInputStream(getter_AddRefs(stream), file);
2412 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2413 0 : return rv;
2414 : }
2415 :
2416 0 : nsCOMPtr<nsIInputStream> bufferedStream;
2417 0 : rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream), stream, 512);
2418 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2419 0 : return rv;
2420 : }
2421 :
2422 : nsCOMPtr<nsIBinaryInputStream> binaryStream =
2423 0 : do_CreateInstance("@mozilla.org/binaryinputstream;1");
2424 0 : if (NS_WARN_IF(!binaryStream)) {
2425 0 : return NS_ERROR_FAILURE;
2426 : }
2427 :
2428 0 : rv = binaryStream->SetInputStream(bufferedStream);
2429 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2430 0 : return rv;
2431 : }
2432 :
2433 0 : binaryStream.forget(aStream);
2434 0 : return NS_OK;
2435 : }
2436 :
2437 : // This method computes and returns our best guess for the temporary storage
2438 : // limit (in bytes), based on the amount of space users have free on their hard
2439 : // drive and on given temporary storage usage (also in bytes).
2440 : nsresult
2441 0 : GetTemporaryStorageLimit(nsIFile* aDirectory, uint64_t aCurrentUsage,
2442 : uint64_t* aLimit)
2443 : {
2444 : // Check for free space on device where temporary storage directory lives.
2445 : int64_t bytesAvailable;
2446 0 : nsresult rv = aDirectory->GetDiskSpaceAvailable(&bytesAvailable);
2447 0 : NS_ENSURE_SUCCESS(rv, rv);
2448 :
2449 0 : NS_ASSERTION(bytesAvailable >= 0, "Negative bytes available?!");
2450 :
2451 : uint64_t availableKB =
2452 0 : static_cast<uint64_t>((bytesAvailable + aCurrentUsage) / 1024);
2453 :
2454 : // Grow/shrink in gChunkSizeKB units, deliberately, so that in the common case
2455 : // we don't shrink temporary storage and evict origin data every time we
2456 : // initialize.
2457 0 : availableKB = (availableKB / gChunkSizeKB) * gChunkSizeKB;
2458 :
2459 : // Allow temporary storage to consume up to half the available space.
2460 0 : uint64_t resultKB = availableKB * .50;
2461 :
2462 0 : *aLimit = resultKB * 1024;
2463 0 : return NS_OK;
2464 : }
2465 :
2466 : } // namespace
2467 :
2468 : /*******************************************************************************
2469 : * Exported functions
2470 : ******************************************************************************/
2471 :
2472 : PQuotaParent*
2473 0 : AllocPQuotaParent()
2474 : {
2475 0 : AssertIsOnBackgroundThread();
2476 :
2477 0 : if (NS_WARN_IF(QuotaManager::IsShuttingDown())) {
2478 0 : return nullptr;
2479 : }
2480 :
2481 0 : RefPtr<Quota> actor = new Quota();
2482 :
2483 0 : return actor.forget().take();
2484 : }
2485 :
2486 : bool
2487 0 : DeallocPQuotaParent(PQuotaParent* aActor)
2488 : {
2489 0 : AssertIsOnBackgroundThread();
2490 0 : MOZ_ASSERT(aActor);
2491 :
2492 0 : RefPtr<Quota> actor = dont_AddRef(static_cast<Quota*>(aActor));
2493 0 : return true;
2494 : }
2495 :
2496 : /*******************************************************************************
2497 : * Directory lock
2498 : ******************************************************************************/
2499 :
2500 0 : DirectoryLockImpl::DirectoryLockImpl(QuotaManager* aQuotaManager,
2501 : const Nullable<PersistenceType>& aPersistenceType,
2502 : const nsACString& aGroup,
2503 : const OriginScope& aOriginScope,
2504 : const Nullable<Client::Type>& aClientType,
2505 : bool aExclusive,
2506 : bool aInternal,
2507 0 : OpenDirectoryListener* aOpenListener)
2508 : : mQuotaManager(aQuotaManager)
2509 : , mPersistenceType(aPersistenceType)
2510 : , mGroup(aGroup)
2511 : , mOriginScope(aOriginScope)
2512 : , mClientType(aClientType)
2513 : , mOpenListener(aOpenListener)
2514 : , mExclusive(aExclusive)
2515 : , mInternal(aInternal)
2516 0 : , mInvalidated(false)
2517 : {
2518 0 : AssertIsOnOwningThread();
2519 0 : MOZ_ASSERT(aQuotaManager);
2520 0 : MOZ_ASSERT_IF(aOriginScope.IsOrigin(), !aOriginScope.GetOrigin().IsEmpty());
2521 0 : MOZ_ASSERT_IF(!aInternal, !aPersistenceType.IsNull());
2522 0 : MOZ_ASSERT_IF(!aInternal,
2523 : aPersistenceType.Value() != PERSISTENCE_TYPE_INVALID);
2524 0 : MOZ_ASSERT_IF(!aInternal, !aGroup.IsEmpty());
2525 0 : MOZ_ASSERT_IF(!aInternal, aOriginScope.IsOrigin());
2526 0 : MOZ_ASSERT_IF(!aInternal, !aClientType.IsNull());
2527 0 : MOZ_ASSERT_IF(!aInternal, aClientType.Value() != Client::TYPE_MAX);
2528 0 : MOZ_ASSERT_IF(!aInternal, aOpenListener);
2529 0 : }
2530 :
2531 0 : DirectoryLockImpl::~DirectoryLockImpl()
2532 : {
2533 0 : AssertIsOnOwningThread();
2534 0 : MOZ_ASSERT(mQuotaManager);
2535 :
2536 0 : for (DirectoryLockImpl* blockingLock : mBlocking) {
2537 0 : blockingLock->MaybeUnblock(this);
2538 : }
2539 :
2540 0 : mBlocking.Clear();
2541 :
2542 0 : mQuotaManager->UnregisterDirectoryLock(this);
2543 0 : }
2544 :
2545 : #ifdef DEBUG
2546 :
2547 : void
2548 0 : DirectoryLockImpl::AssertIsOnOwningThread() const
2549 : {
2550 0 : MOZ_ASSERT(mQuotaManager);
2551 0 : mQuotaManager->AssertIsOnOwningThread();
2552 0 : }
2553 :
2554 : #endif // DEBUG
2555 :
2556 : bool
2557 0 : DirectoryLockImpl::MustWaitFor(const DirectoryLockImpl& aExistingLock)
2558 : {
2559 0 : AssertIsOnOwningThread();
2560 :
2561 : // Waiting is never required if the ops in comparison represent shared locks.
2562 0 : if (!aExistingLock.mExclusive && !mExclusive) {
2563 0 : return false;
2564 : }
2565 :
2566 : // If the persistence types don't overlap, the op can proceed.
2567 0 : if (!aExistingLock.mPersistenceType.IsNull() && !mPersistenceType.IsNull() &&
2568 0 : aExistingLock.mPersistenceType.Value() != mPersistenceType.Value()) {
2569 0 : return false;
2570 : }
2571 :
2572 : // If the origin scopes don't overlap, the op can proceed.
2573 0 : bool match = aExistingLock.mOriginScope.Matches(mOriginScope);
2574 0 : if (!match) {
2575 0 : return false;
2576 : }
2577 :
2578 : // If the client types don't overlap, the op can proceed.
2579 0 : if (!aExistingLock.mClientType.IsNull() && !mClientType.IsNull() &&
2580 0 : aExistingLock.mClientType.Value() != mClientType.Value()) {
2581 0 : return false;
2582 : }
2583 :
2584 : // Otherwise, when all attributes overlap (persistence type, origin scope and
2585 : // client type) the op must wait.
2586 0 : return true;
2587 : }
2588 :
2589 : void
2590 0 : DirectoryLockImpl::NotifyOpenListener()
2591 : {
2592 0 : AssertIsOnOwningThread();
2593 0 : MOZ_ASSERT(mQuotaManager);
2594 0 : MOZ_ASSERT(mOpenListener);
2595 :
2596 0 : if (mInvalidated) {
2597 0 : mOpenListener->DirectoryLockFailed();
2598 : } else {
2599 0 : mOpenListener->DirectoryLockAcquired(this);
2600 : }
2601 :
2602 0 : mOpenListener = nullptr;
2603 :
2604 0 : mQuotaManager->RemovePendingDirectoryLock(this);
2605 0 : }
2606 :
2607 : nsresult
2608 0 : QuotaManager::
2609 : CreateRunnable::Init()
2610 : {
2611 0 : MOZ_ASSERT(NS_IsMainThread());
2612 0 : MOZ_ASSERT(mState == State::Initial);
2613 :
2614 : nsresult rv;
2615 :
2616 0 : nsCOMPtr<nsIFile> baseDir;
2617 0 : rv = NS_GetSpecialDirectory(NS_APP_INDEXEDDB_PARENT_DIR,
2618 0 : getter_AddRefs(baseDir));
2619 0 : if (NS_FAILED(rv)) {
2620 0 : rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
2621 0 : getter_AddRefs(baseDir));
2622 : }
2623 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2624 0 : return rv;
2625 : }
2626 :
2627 0 : rv = baseDir->GetPath(mBaseDirPath);
2628 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2629 0 : return rv;
2630 : }
2631 :
2632 0 : return NS_OK;
2633 : }
2634 :
2635 : nsresult
2636 0 : QuotaManager::
2637 : CreateRunnable::CreateManager()
2638 : {
2639 0 : AssertIsOnOwningThread();
2640 0 : MOZ_ASSERT(mState == State::CreatingManager);
2641 :
2642 0 : mManager = new QuotaManager();
2643 :
2644 0 : nsresult rv = mManager->Init(mBaseDirPath);
2645 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2646 0 : return rv;
2647 : }
2648 :
2649 0 : return NS_OK;
2650 : }
2651 :
2652 : nsresult
2653 0 : QuotaManager::
2654 : CreateRunnable::RegisterObserver()
2655 : {
2656 0 : MOZ_ASSERT(NS_IsMainThread());
2657 0 : MOZ_ASSERT(mState == State::RegisteringObserver);
2658 :
2659 0 : if (NS_FAILED(Preferences::AddIntVarCache(&gFixedLimitKB, PREF_FIXED_LIMIT,
2660 0 : kDefaultFixedLimitKB)) ||
2661 0 : NS_FAILED(Preferences::AddUintVarCache(&gChunkSizeKB,
2662 : PREF_CHUNK_SIZE,
2663 : kDefaultChunkSizeKB))) {
2664 0 : NS_WARNING("Unable to respond to temp storage pref changes!");
2665 : }
2666 :
2667 0 : if (NS_FAILED(Preferences::AddBoolVarCache(&gTestingEnabled,
2668 : PREF_TESTING_FEATURES, false))) {
2669 0 : NS_WARNING("Unable to respond to testing pref changes!");
2670 : }
2671 :
2672 : nsCOMPtr<nsIObserverService> observerService =
2673 0 : mozilla::services::GetObserverService();
2674 0 : if (NS_WARN_IF(!observerService)) {
2675 0 : return NS_ERROR_FAILURE;
2676 : }
2677 :
2678 0 : nsCOMPtr<nsIObserver> observer = new ShutdownObserver(mOwningThread);
2679 :
2680 : nsresult rv =
2681 0 : observerService->AddObserver(observer,
2682 : PROFILE_BEFORE_CHANGE_QM_OBSERVER_ID,
2683 0 : false);
2684 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2685 0 : return rv;
2686 : }
2687 :
2688 : // This service has to be started on the main thread currently.
2689 : nsCOMPtr<mozIStorageService> ss =
2690 0 : do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv);
2691 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2692 0 : return rv;
2693 : }
2694 :
2695 0 : QuotaManagerService* qms = QuotaManagerService::GetOrCreate();
2696 0 : if (NS_WARN_IF(!qms)) {
2697 0 : return rv;
2698 : }
2699 :
2700 0 : qms->NoteLiveManager(mManager);
2701 :
2702 0 : for (RefPtr<Client>& client : mManager->mClients) {
2703 0 : client->DidInitialize(mManager);
2704 : }
2705 :
2706 0 : return NS_OK;
2707 : }
2708 :
2709 : void
2710 0 : QuotaManager::
2711 : CreateRunnable::CallCallbacks()
2712 : {
2713 0 : AssertIsOnOwningThread();
2714 0 : MOZ_ASSERT(mState == State::CallingCallbacks);
2715 :
2716 0 : gCreateRunnable = nullptr;
2717 :
2718 0 : if (NS_FAILED(mResultCode)) {
2719 0 : gCreateFailed = true;
2720 : } else {
2721 0 : gInstance = mManager;
2722 : }
2723 :
2724 0 : mManager = nullptr;
2725 :
2726 0 : nsTArray<nsCOMPtr<nsIRunnable>> callbacks;
2727 0 : mCallbacks.SwapElements(callbacks);
2728 :
2729 0 : for (nsCOMPtr<nsIRunnable>& callback : callbacks) {
2730 0 : Unused << callback->Run();
2731 : }
2732 0 : }
2733 :
2734 : auto
2735 0 : QuotaManager::
2736 : CreateRunnable::GetNextState(nsCOMPtr<nsIEventTarget>& aThread) -> State
2737 : {
2738 0 : switch (mState) {
2739 : case State::Initial:
2740 0 : aThread = mOwningThread;
2741 0 : return State::CreatingManager;
2742 : case State::CreatingManager:
2743 0 : aThread = GetMainThreadEventTarget();
2744 0 : return State::RegisteringObserver;
2745 : case State::RegisteringObserver:
2746 0 : aThread = mOwningThread;
2747 0 : return State::CallingCallbacks;
2748 : case State::CallingCallbacks:
2749 0 : aThread = nullptr;
2750 0 : return State::Completed;
2751 : default:
2752 0 : MOZ_CRASH("Bad state!");
2753 : }
2754 : }
2755 :
2756 : NS_IMETHODIMP
2757 0 : QuotaManager::
2758 : CreateRunnable::Run()
2759 : {
2760 : nsresult rv;
2761 :
2762 0 : switch (mState) {
2763 : case State::Initial:
2764 0 : rv = Init();
2765 0 : break;
2766 :
2767 : case State::CreatingManager:
2768 0 : rv = CreateManager();
2769 0 : break;
2770 :
2771 : case State::RegisteringObserver:
2772 0 : rv = RegisterObserver();
2773 0 : break;
2774 :
2775 : case State::CallingCallbacks:
2776 0 : CallCallbacks();
2777 0 : rv = NS_OK;
2778 0 : break;
2779 :
2780 : case State::Completed:
2781 : default:
2782 0 : MOZ_CRASH("Bad state!");
2783 : }
2784 :
2785 0 : nsCOMPtr<nsIEventTarget> thread;
2786 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2787 0 : if (NS_SUCCEEDED(mResultCode)) {
2788 0 : mResultCode = rv;
2789 : }
2790 :
2791 0 : mState = State::CallingCallbacks;
2792 0 : thread = mOwningThread;
2793 : } else {
2794 0 : mState = GetNextState(thread);
2795 : }
2796 :
2797 0 : if (thread) {
2798 0 : MOZ_ALWAYS_SUCCEEDS(thread->Dispatch(this, NS_DISPATCH_NORMAL));
2799 : }
2800 :
2801 0 : return NS_OK;
2802 : }
2803 :
2804 : NS_IMETHODIMP
2805 0 : QuotaManager::
2806 : ShutdownRunnable::Run()
2807 : {
2808 0 : if (NS_IsMainThread()) {
2809 0 : mDone = true;
2810 :
2811 0 : return NS_OK;
2812 : }
2813 :
2814 0 : AssertIsOnBackgroundThread();
2815 :
2816 0 : RefPtr<QuotaManager> quotaManager = gInstance.get();
2817 0 : if (quotaManager) {
2818 0 : quotaManager->Shutdown();
2819 :
2820 0 : gInstance = nullptr;
2821 : }
2822 :
2823 0 : MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(this));
2824 :
2825 0 : return NS_OK;
2826 : }
2827 :
2828 0 : NS_IMPL_ISUPPORTS(QuotaManager::ShutdownObserver, nsIObserver)
2829 :
2830 : NS_IMETHODIMP
2831 0 : QuotaManager::
2832 : ShutdownObserver::Observe(nsISupports* aSubject,
2833 : const char* aTopic,
2834 : const char16_t* aData)
2835 : {
2836 0 : MOZ_ASSERT(NS_IsMainThread());
2837 0 : MOZ_ASSERT(!strcmp(aTopic, PROFILE_BEFORE_CHANGE_QM_OBSERVER_ID));
2838 0 : MOZ_ASSERT(gInstance);
2839 :
2840 : nsCOMPtr<nsIObserverService> observerService =
2841 0 : mozilla::services::GetObserverService();
2842 0 : if (NS_WARN_IF(!observerService)) {
2843 0 : return NS_ERROR_FAILURE;
2844 : }
2845 :
2846 : // Unregister ourselves from the observer service first to make sure the
2847 : // nested event loop below will not cause re-entrancy issues.
2848 : Unused <<
2849 0 : observerService->RemoveObserver(this,
2850 0 : PROFILE_BEFORE_CHANGE_QM_OBSERVER_ID);
2851 :
2852 0 : QuotaManagerService* qms = QuotaManagerService::Get();
2853 0 : MOZ_ASSERT(qms);
2854 :
2855 0 : qms->NoteShuttingDownManager();
2856 :
2857 0 : for (RefPtr<Client>& client : gInstance->mClients) {
2858 0 : client->WillShutdown();
2859 : }
2860 :
2861 0 : bool done = false;
2862 :
2863 0 : RefPtr<ShutdownRunnable> shutdownRunnable = new ShutdownRunnable(done);
2864 0 : MOZ_ALWAYS_SUCCEEDS(
2865 : mBackgroundThread->Dispatch(shutdownRunnable, NS_DISPATCH_NORMAL));
2866 :
2867 0 : MOZ_ALWAYS_TRUE(SpinEventLoopUntil([&]() { return done; }));
2868 :
2869 0 : return NS_OK;
2870 : }
2871 :
2872 : /*******************************************************************************
2873 : * Quota object
2874 : ******************************************************************************/
2875 :
2876 : NS_IMETHODIMP
2877 0 : QuotaObject::
2878 : StoragePressureRunnable::Run()
2879 : {
2880 0 : MOZ_ASSERT(NS_IsMainThread());
2881 :
2882 0 : nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
2883 0 : if (NS_WARN_IF(!obsSvc)) {
2884 0 : return NS_ERROR_FAILURE;
2885 : }
2886 :
2887 : nsCOMPtr<nsISupportsPRUint64> wrapper =
2888 0 : do_CreateInstance(NS_SUPPORTS_PRUINT64_CONTRACTID);
2889 0 : if (NS_WARN_IF(!wrapper)) {
2890 0 : return NS_ERROR_FAILURE;
2891 : }
2892 :
2893 0 : wrapper->SetData(mUsage);
2894 :
2895 0 : obsSvc->NotifyObservers(wrapper, "QuotaManager::StoragePressure", u"");
2896 :
2897 0 : return NS_OK;
2898 : }
2899 :
2900 : void
2901 0 : QuotaObject::AddRef()
2902 : {
2903 0 : QuotaManager* quotaManager = QuotaManager::Get();
2904 0 : if (!quotaManager) {
2905 0 : NS_ERROR("Null quota manager, this shouldn't happen, possible leak!");
2906 :
2907 0 : ++mRefCnt;
2908 :
2909 0 : return;
2910 : }
2911 :
2912 0 : MutexAutoLock lock(quotaManager->mQuotaMutex);
2913 :
2914 0 : ++mRefCnt;
2915 : }
2916 :
2917 : void
2918 0 : QuotaObject::Release()
2919 : {
2920 0 : QuotaManager* quotaManager = QuotaManager::Get();
2921 0 : if (!quotaManager) {
2922 0 : NS_ERROR("Null quota manager, this shouldn't happen, possible leak!");
2923 :
2924 0 : nsrefcnt count = --mRefCnt;
2925 0 : if (count == 0) {
2926 0 : mRefCnt = 1;
2927 0 : delete this;
2928 : }
2929 :
2930 0 : return;
2931 : }
2932 :
2933 : {
2934 0 : MutexAutoLock lock(quotaManager->mQuotaMutex);
2935 :
2936 0 : --mRefCnt;
2937 :
2938 0 : if (mRefCnt > 0) {
2939 0 : return;
2940 : }
2941 :
2942 0 : if (mOriginInfo) {
2943 0 : mOriginInfo->mQuotaObjects.Remove(mPath);
2944 : }
2945 : }
2946 :
2947 0 : delete this;
2948 : }
2949 :
2950 : bool
2951 0 : QuotaObject::MaybeUpdateSize(int64_t aSize, bool aTruncate)
2952 : {
2953 0 : QuotaManager* quotaManager = QuotaManager::Get();
2954 0 : MOZ_ASSERT(quotaManager);
2955 :
2956 0 : MutexAutoLock lock(quotaManager->mQuotaMutex);
2957 :
2958 0 : if (mQuotaCheckDisabled) {
2959 0 : return true;
2960 : }
2961 :
2962 0 : if (mSize == aSize) {
2963 0 : return true;
2964 : }
2965 :
2966 0 : if (!mOriginInfo) {
2967 0 : mSize = aSize;
2968 0 : return true;
2969 : }
2970 :
2971 0 : GroupInfo* groupInfo = mOriginInfo->mGroupInfo;
2972 0 : MOZ_ASSERT(groupInfo);
2973 :
2974 0 : if (mSize > aSize) {
2975 0 : if (aTruncate) {
2976 0 : const int64_t delta = mSize - aSize;
2977 :
2978 0 : AssertNoUnderflow(quotaManager->mTemporaryStorageUsage, delta);
2979 0 : quotaManager->mTemporaryStorageUsage -= delta;
2980 :
2981 0 : if (!mOriginInfo->LockedPersisted()) {
2982 0 : AssertNoUnderflow(groupInfo->mUsage, delta);
2983 0 : groupInfo->mUsage -= delta;
2984 : }
2985 :
2986 0 : AssertNoUnderflow(mOriginInfo->mUsage, delta);
2987 0 : mOriginInfo->mUsage -= delta;
2988 :
2989 0 : mSize = aSize;
2990 : }
2991 0 : return true;
2992 : }
2993 :
2994 0 : MOZ_ASSERT(mSize < aSize);
2995 :
2996 : RefPtr<GroupInfo> complementaryGroupInfo =
2997 0 : groupInfo->mGroupInfoPair->LockedGetGroupInfo(
2998 0 : ComplementaryPersistenceType(groupInfo->mPersistenceType));
2999 :
3000 0 : uint64_t delta = aSize - mSize;
3001 :
3002 0 : AssertNoOverflow(mOriginInfo->mUsage, delta);
3003 0 : uint64_t newUsage = mOriginInfo->mUsage + delta;
3004 :
3005 : // Temporary storage has no limit for origin usage (there's a group and the
3006 : // global limit though).
3007 :
3008 0 : uint64_t newGroupUsage = groupInfo->mUsage;
3009 0 : if (!mOriginInfo->LockedPersisted()) {
3010 0 : AssertNoOverflow(groupInfo->mUsage, delta);
3011 0 : newGroupUsage += delta;
3012 :
3013 0 : uint64_t groupUsage = groupInfo->mUsage;
3014 0 : if (complementaryGroupInfo) {
3015 0 : AssertNoOverflow(groupUsage, complementaryGroupInfo->mUsage);
3016 0 : groupUsage += complementaryGroupInfo->mUsage;
3017 : }
3018 :
3019 : // Temporary storage has a hard limit for group usage (20 % of the global
3020 : // limit).
3021 0 : AssertNoOverflow(groupUsage, delta);
3022 0 : if (groupUsage + delta > quotaManager->GetGroupLimit()) {
3023 0 : return false;
3024 : }
3025 : }
3026 :
3027 0 : AssertNoOverflow(quotaManager->mTemporaryStorageUsage, delta);
3028 0 : uint64_t newTemporaryStorageUsage = quotaManager->mTemporaryStorageUsage +
3029 0 : delta;
3030 :
3031 0 : if (newTemporaryStorageUsage > quotaManager->mTemporaryStorageLimit) {
3032 : // This will block the thread without holding the lock while waitting.
3033 :
3034 0 : AutoTArray<RefPtr<DirectoryLockImpl>, 10> locks;
3035 :
3036 : uint64_t sizeToBeFreed =
3037 0 : quotaManager->LockedCollectOriginsForEviction(delta, locks);
3038 :
3039 0 : if (!sizeToBeFreed) {
3040 0 : MutexAutoUnlock autoUnlock(quotaManager->mQuotaMutex);
3041 :
3042 : // Notify pressure event.
3043 : RefPtr<StoragePressureRunnable> storagePressureRunnable =
3044 0 : new StoragePressureRunnable(quotaManager->mTemporaryStorageUsage);
3045 :
3046 0 : MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(storagePressureRunnable));
3047 :
3048 0 : return false;
3049 : }
3050 :
3051 0 : NS_ASSERTION(sizeToBeFreed >= delta, "Huh?");
3052 :
3053 : {
3054 0 : MutexAutoUnlock autoUnlock(quotaManager->mQuotaMutex);
3055 :
3056 0 : for (RefPtr<DirectoryLockImpl>& lock : locks) {
3057 0 : MOZ_ASSERT(!lock->GetPersistenceType().IsNull());
3058 0 : MOZ_ASSERT(lock->GetOriginScope().IsOrigin());
3059 0 : MOZ_ASSERT(!lock->GetOriginScope().GetOrigin().IsEmpty());
3060 :
3061 0 : quotaManager->DeleteFilesForOrigin(lock->GetPersistenceType().Value(),
3062 0 : lock->GetOriginScope().GetOrigin());
3063 : }
3064 : }
3065 :
3066 : // Relocked.
3067 :
3068 0 : NS_ASSERTION(mOriginInfo, "How come?!");
3069 :
3070 0 : for (DirectoryLockImpl* lock : locks) {
3071 0 : MOZ_ASSERT(!lock->GetPersistenceType().IsNull());
3072 0 : MOZ_ASSERT(!lock->GetGroup().IsEmpty());
3073 0 : MOZ_ASSERT(lock->GetOriginScope().IsOrigin());
3074 0 : MOZ_ASSERT(!lock->GetOriginScope().GetOrigin().IsEmpty());
3075 0 : MOZ_ASSERT(lock->GetOriginScope().GetOrigin() != mOriginInfo->mOrigin,
3076 : "Deleted itself!");
3077 :
3078 0 : quotaManager->LockedRemoveQuotaForOrigin(
3079 0 : lock->GetPersistenceType().Value(),
3080 : lock->GetGroup(),
3081 0 : lock->GetOriginScope().GetOrigin());
3082 : }
3083 :
3084 : // We unlocked and relocked several times so we need to recompute all the
3085 : // essential variables and recheck the group limit.
3086 :
3087 0 : AssertNoUnderflow(aSize, mSize);
3088 0 : delta = aSize - mSize;
3089 :
3090 0 : AssertNoOverflow(mOriginInfo->mUsage, delta);
3091 0 : newUsage = mOriginInfo->mUsage + delta;
3092 :
3093 0 : newGroupUsage = groupInfo->mUsage;
3094 0 : if (!mOriginInfo->LockedPersisted()) {
3095 0 : AssertNoOverflow(groupInfo->mUsage, delta);
3096 0 : newGroupUsage += delta;
3097 :
3098 0 : uint64_t groupUsage = groupInfo->mUsage;
3099 0 : if (complementaryGroupInfo) {
3100 0 : AssertNoOverflow(groupUsage, complementaryGroupInfo->mUsage);
3101 0 : groupUsage += complementaryGroupInfo->mUsage;
3102 : }
3103 :
3104 0 : AssertNoOverflow(groupUsage, delta);
3105 0 : if (groupUsage + delta > quotaManager->GetGroupLimit()) {
3106 : // Unfortunately some other thread increased the group usage in the
3107 : // meantime and we are not below the group limit anymore.
3108 :
3109 : // However, the origin eviction must be finalized in this case too.
3110 0 : MutexAutoUnlock autoUnlock(quotaManager->mQuotaMutex);
3111 :
3112 0 : quotaManager->FinalizeOriginEviction(locks);
3113 :
3114 0 : return false;
3115 : }
3116 : }
3117 :
3118 0 : AssertNoOverflow(quotaManager->mTemporaryStorageUsage, delta);
3119 0 : newTemporaryStorageUsage = quotaManager->mTemporaryStorageUsage + delta;
3120 :
3121 0 : NS_ASSERTION(newTemporaryStorageUsage <=
3122 : quotaManager->mTemporaryStorageLimit, "How come?!");
3123 :
3124 : // Ok, we successfully freed enough space and the operation can continue
3125 : // without throwing the quota error.
3126 0 : mOriginInfo->mUsage = newUsage;
3127 0 : if (!mOriginInfo->LockedPersisted()) {
3128 0 : groupInfo->mUsage = newGroupUsage;
3129 : }
3130 0 : quotaManager->mTemporaryStorageUsage = newTemporaryStorageUsage;;
3131 :
3132 : // Some other thread could increase the size in the meantime, but no more
3133 : // than this one.
3134 0 : MOZ_ASSERT(mSize < aSize);
3135 0 : mSize = aSize;
3136 :
3137 : // Finally, release IO thread only objects and allow next synchronized
3138 : // ops for the evicted origins.
3139 0 : MutexAutoUnlock autoUnlock(quotaManager->mQuotaMutex);
3140 :
3141 0 : quotaManager->FinalizeOriginEviction(locks);
3142 :
3143 0 : return true;
3144 : }
3145 :
3146 0 : mOriginInfo->mUsage = newUsage;
3147 0 : if (!mOriginInfo->LockedPersisted()) {
3148 0 : groupInfo->mUsage = newGroupUsage;
3149 : }
3150 0 : quotaManager->mTemporaryStorageUsage = newTemporaryStorageUsage;
3151 :
3152 0 : mSize = aSize;
3153 :
3154 0 : return true;
3155 : }
3156 :
3157 : void
3158 0 : QuotaObject::DisableQuotaCheck()
3159 : {
3160 0 : QuotaManager* quotaManager = QuotaManager::Get();
3161 0 : MOZ_ASSERT(quotaManager);
3162 :
3163 0 : MutexAutoLock lock(quotaManager->mQuotaMutex);
3164 :
3165 0 : mQuotaCheckDisabled = true;
3166 0 : }
3167 :
3168 : void
3169 0 : QuotaObject::EnableQuotaCheck()
3170 : {
3171 0 : QuotaManager* quotaManager = QuotaManager::Get();
3172 0 : MOZ_ASSERT(quotaManager);
3173 :
3174 0 : MutexAutoLock lock(quotaManager->mQuotaMutex);
3175 :
3176 0 : mQuotaCheckDisabled = false;
3177 0 : }
3178 :
3179 : /*******************************************************************************
3180 : * Quota manager
3181 : ******************************************************************************/
3182 :
3183 0 : QuotaManager::QuotaManager()
3184 : : mQuotaMutex("QuotaManager.mQuotaMutex"),
3185 : mTemporaryStorageLimit(0),
3186 : mTemporaryStorageUsage(0),
3187 : mTemporaryStorageInitialized(false),
3188 0 : mStorageInitialized(false)
3189 : {
3190 0 : AssertIsOnOwningThread();
3191 0 : MOZ_ASSERT(!gInstance);
3192 0 : }
3193 :
3194 0 : QuotaManager::~QuotaManager()
3195 : {
3196 0 : AssertIsOnOwningThread();
3197 0 : MOZ_ASSERT(!gInstance || gInstance == this);
3198 0 : }
3199 :
3200 : void
3201 0 : QuotaManager::GetOrCreate(nsIRunnable* aCallback)
3202 : {
3203 0 : AssertIsOnBackgroundThread();
3204 :
3205 0 : if (IsShuttingDown()) {
3206 0 : MOZ_ASSERT(false, "Calling GetOrCreate() after shutdown!");
3207 : return;
3208 : }
3209 :
3210 0 : if (gInstance || gCreateFailed) {
3211 0 : MOZ_ASSERT(!gCreateRunnable);
3212 0 : MOZ_ASSERT_IF(gCreateFailed, !gInstance);
3213 :
3214 0 : MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(aCallback));
3215 : } else {
3216 0 : if (!gCreateRunnable) {
3217 0 : gCreateRunnable = new CreateRunnable();
3218 0 : MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(gCreateRunnable));
3219 : }
3220 :
3221 0 : gCreateRunnable->AddCallback(aCallback);
3222 : }
3223 0 : }
3224 :
3225 : // static
3226 : QuotaManager*
3227 1 : QuotaManager::Get()
3228 : {
3229 : // Does not return an owning reference.
3230 1 : return gInstance;
3231 : }
3232 :
3233 : // static
3234 : bool
3235 0 : QuotaManager::IsShuttingDown()
3236 : {
3237 0 : return gShutdown;
3238 : }
3239 :
3240 : auto
3241 0 : QuotaManager::CreateDirectoryLock(const Nullable<PersistenceType>& aPersistenceType,
3242 : const nsACString& aGroup,
3243 : const OriginScope& aOriginScope,
3244 : const Nullable<Client::Type>& aClientType,
3245 : bool aExclusive,
3246 : bool aInternal,
3247 : OpenDirectoryListener* aOpenListener)
3248 : -> already_AddRefed<DirectoryLockImpl>
3249 : {
3250 0 : AssertIsOnOwningThread();
3251 0 : MOZ_ASSERT_IF(aOriginScope.IsOrigin(), !aOriginScope.GetOrigin().IsEmpty());
3252 0 : MOZ_ASSERT_IF(!aInternal, !aPersistenceType.IsNull());
3253 0 : MOZ_ASSERT_IF(!aInternal,
3254 : aPersistenceType.Value() != PERSISTENCE_TYPE_INVALID);
3255 0 : MOZ_ASSERT_IF(!aInternal, !aGroup.IsEmpty());
3256 0 : MOZ_ASSERT_IF(!aInternal, aOriginScope.IsOrigin());
3257 0 : MOZ_ASSERT_IF(!aInternal, !aClientType.IsNull());
3258 0 : MOZ_ASSERT_IF(!aInternal, aClientType.Value() != Client::TYPE_MAX);
3259 0 : MOZ_ASSERT_IF(!aInternal, aOpenListener);
3260 :
3261 : RefPtr<DirectoryLockImpl> lock = new DirectoryLockImpl(this,
3262 : aPersistenceType,
3263 : aGroup,
3264 : aOriginScope,
3265 : aClientType,
3266 : aExclusive,
3267 : aInternal,
3268 0 : aOpenListener);
3269 :
3270 0 : mPendingDirectoryLocks.AppendElement(lock);
3271 :
3272 : // See if this lock needs to wait.
3273 0 : bool blocked = false;
3274 0 : for (uint32_t index = mDirectoryLocks.Length(); index > 0; index--) {
3275 0 : DirectoryLockImpl* existingLock = mDirectoryLocks[index - 1];
3276 0 : if (lock->MustWaitFor(*existingLock)) {
3277 0 : existingLock->AddBlockingLock(lock);
3278 0 : lock->AddBlockedOnLock(existingLock);
3279 0 : blocked = true;
3280 : }
3281 : }
3282 :
3283 0 : RegisterDirectoryLock(lock);
3284 :
3285 : // Otherwise, notify the open listener immediately.
3286 0 : if (!blocked) {
3287 0 : lock->NotifyOpenListener();
3288 : }
3289 :
3290 0 : return lock.forget();
3291 : }
3292 :
3293 : auto
3294 0 : QuotaManager::CreateDirectoryLockForEviction(PersistenceType aPersistenceType,
3295 : const nsACString& aGroup,
3296 : const nsACString& aOrigin)
3297 : -> already_AddRefed<DirectoryLockImpl>
3298 : {
3299 0 : AssertIsOnOwningThread();
3300 0 : MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_INVALID);
3301 0 : MOZ_ASSERT(!aOrigin.IsEmpty());
3302 :
3303 : RefPtr<DirectoryLockImpl> lock =
3304 : new DirectoryLockImpl(this,
3305 0 : Nullable<PersistenceType>(aPersistenceType),
3306 : aGroup,
3307 0 : OriginScope::FromOrigin(aOrigin),
3308 0 : Nullable<Client::Type>(),
3309 : /* aExclusive */ true,
3310 : /* aInternal */ true,
3311 0 : nullptr);
3312 :
3313 : #ifdef DEBUG
3314 0 : for (uint32_t index = mDirectoryLocks.Length(); index > 0; index--) {
3315 0 : DirectoryLockImpl* existingLock = mDirectoryLocks[index - 1];
3316 0 : MOZ_ASSERT(!lock->MustWaitFor(*existingLock));
3317 : }
3318 : #endif
3319 :
3320 0 : RegisterDirectoryLock(lock);
3321 :
3322 0 : return lock.forget();
3323 : }
3324 :
3325 : void
3326 0 : QuotaManager::RegisterDirectoryLock(DirectoryLockImpl* aLock)
3327 : {
3328 0 : AssertIsOnOwningThread();
3329 0 : MOZ_ASSERT(aLock);
3330 :
3331 0 : mDirectoryLocks.AppendElement(aLock);
3332 :
3333 0 : if (aLock->ShouldUpdateLockTable()) {
3334 : const Nullable<PersistenceType>& persistenceType =
3335 0 : aLock->GetPersistenceType();
3336 0 : const OriginScope& originScope = aLock->GetOriginScope();
3337 :
3338 0 : MOZ_ASSERT(!persistenceType.IsNull());
3339 0 : MOZ_ASSERT(!aLock->GetGroup().IsEmpty());
3340 0 : MOZ_ASSERT(originScope.IsOrigin());
3341 0 : MOZ_ASSERT(!originScope.GetOrigin().IsEmpty());
3342 :
3343 : DirectoryLockTable& directoryLockTable =
3344 0 : GetDirectoryLockTable(persistenceType.Value());
3345 :
3346 : nsTArray<DirectoryLockImpl*>* array;
3347 0 : if (!directoryLockTable.Get(originScope.GetOrigin(), &array)) {
3348 0 : array = new nsTArray<DirectoryLockImpl*>();
3349 0 : directoryLockTable.Put(originScope.GetOrigin(), array);
3350 :
3351 0 : if (!IsShuttingDown()) {
3352 0 : UpdateOriginAccessTime(persistenceType.Value(),
3353 : aLock->GetGroup(),
3354 0 : originScope.GetOrigin());
3355 : }
3356 : }
3357 0 : array->AppendElement(aLock);
3358 : }
3359 0 : }
3360 :
3361 : void
3362 0 : QuotaManager::UnregisterDirectoryLock(DirectoryLockImpl* aLock)
3363 : {
3364 0 : AssertIsOnOwningThread();
3365 :
3366 0 : MOZ_ALWAYS_TRUE(mDirectoryLocks.RemoveElement(aLock));
3367 :
3368 0 : if (aLock->ShouldUpdateLockTable()) {
3369 : const Nullable<PersistenceType>& persistenceType =
3370 0 : aLock->GetPersistenceType();
3371 0 : const OriginScope& originScope = aLock->GetOriginScope();
3372 :
3373 0 : MOZ_ASSERT(!persistenceType.IsNull());
3374 0 : MOZ_ASSERT(!aLock->GetGroup().IsEmpty());
3375 0 : MOZ_ASSERT(originScope.IsOrigin());
3376 0 : MOZ_ASSERT(!originScope.GetOrigin().IsEmpty());
3377 :
3378 : DirectoryLockTable& directoryLockTable =
3379 0 : GetDirectoryLockTable(persistenceType.Value());
3380 :
3381 : nsTArray<DirectoryLockImpl*>* array;
3382 0 : MOZ_ALWAYS_TRUE(directoryLockTable.Get(originScope.GetOrigin(), &array));
3383 :
3384 0 : MOZ_ALWAYS_TRUE(array->RemoveElement(aLock));
3385 0 : if (array->IsEmpty()) {
3386 0 : directoryLockTable.Remove(originScope.GetOrigin());
3387 :
3388 0 : if (!IsShuttingDown()) {
3389 0 : UpdateOriginAccessTime(persistenceType.Value(),
3390 : aLock->GetGroup(),
3391 0 : originScope.GetOrigin());
3392 : }
3393 : }
3394 : }
3395 0 : }
3396 :
3397 : void
3398 0 : QuotaManager::RemovePendingDirectoryLock(DirectoryLockImpl* aLock)
3399 : {
3400 0 : AssertIsOnOwningThread();
3401 0 : MOZ_ASSERT(aLock);
3402 :
3403 0 : MOZ_ALWAYS_TRUE(mPendingDirectoryLocks.RemoveElement(aLock));
3404 0 : }
3405 :
3406 : uint64_t
3407 0 : QuotaManager::CollectOriginsForEviction(
3408 : uint64_t aMinSizeToBeFreed,
3409 : nsTArray<RefPtr<DirectoryLockImpl>>& aLocks)
3410 : {
3411 0 : AssertIsOnOwningThread();
3412 0 : MOZ_ASSERT(aLocks.IsEmpty());
3413 :
3414 : struct MOZ_STACK_CLASS Helper final
3415 : {
3416 : static void
3417 0 : GetInactiveOriginInfos(nsTArray<RefPtr<OriginInfo>>& aOriginInfos,
3418 : nsTArray<DirectoryLockImpl*>& aLocks,
3419 : nsTArray<OriginInfo*>& aInactiveOriginInfos)
3420 : {
3421 0 : for (OriginInfo* originInfo : aOriginInfos) {
3422 0 : MOZ_ASSERT(originInfo->mGroupInfo->mPersistenceType !=
3423 : PERSISTENCE_TYPE_PERSISTENT);
3424 :
3425 0 : if (originInfo->LockedPersisted()) {
3426 0 : continue;
3427 : }
3428 :
3429 0 : OriginScope originScope = OriginScope::FromOrigin(originInfo->mOrigin);
3430 :
3431 0 : bool match = false;
3432 0 : for (uint32_t j = aLocks.Length(); j > 0; j--) {
3433 0 : DirectoryLockImpl* lock = aLocks[j - 1];
3434 0 : if (originScope.Matches(lock->GetOriginScope())) {
3435 0 : match = true;
3436 0 : break;
3437 : }
3438 : }
3439 :
3440 0 : if (!match) {
3441 0 : MOZ_ASSERT(!originInfo->mQuotaObjects.Count(),
3442 : "Inactive origin shouldn't have open files!");
3443 0 : aInactiveOriginInfos.InsertElementSorted(originInfo,
3444 0 : OriginInfoLRUComparator());
3445 : }
3446 : }
3447 0 : }
3448 : };
3449 :
3450 : // Split locks into separate arrays and filter out locks for persistent
3451 : // storage, they can't block us.
3452 0 : nsTArray<DirectoryLockImpl*> temporaryStorageLocks;
3453 0 : nsTArray<DirectoryLockImpl*> defaultStorageLocks;
3454 0 : for (DirectoryLockImpl* lock : mDirectoryLocks) {
3455 : const Nullable<PersistenceType>& persistenceType =
3456 0 : lock->GetPersistenceType();
3457 :
3458 0 : if (persistenceType.IsNull()) {
3459 0 : temporaryStorageLocks.AppendElement(lock);
3460 0 : defaultStorageLocks.AppendElement(lock);
3461 0 : } else if (persistenceType.Value() == PERSISTENCE_TYPE_TEMPORARY) {
3462 0 : temporaryStorageLocks.AppendElement(lock);
3463 0 : } else if (persistenceType.Value() == PERSISTENCE_TYPE_DEFAULT) {
3464 0 : defaultStorageLocks.AppendElement(lock);
3465 : } else {
3466 0 : MOZ_ASSERT(persistenceType.Value() == PERSISTENCE_TYPE_PERSISTENT);
3467 :
3468 : // Do nothing here, persistent origins don't need to be collected ever.
3469 : }
3470 : }
3471 :
3472 0 : nsTArray<OriginInfo*> inactiveOrigins;
3473 :
3474 : // Enumerate and process inactive origins. This must be protected by the
3475 : // mutex.
3476 0 : MutexAutoLock lock(mQuotaMutex);
3477 :
3478 0 : for (auto iter = mGroupInfoPairs.Iter(); !iter.Done(); iter.Next()) {
3479 0 : GroupInfoPair* pair = iter.UserData();
3480 :
3481 0 : MOZ_ASSERT(!iter.Key().IsEmpty());
3482 0 : MOZ_ASSERT(pair);
3483 :
3484 : RefPtr<GroupInfo> groupInfo =
3485 0 : pair->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY);
3486 0 : if (groupInfo) {
3487 0 : Helper::GetInactiveOriginInfos(groupInfo->mOriginInfos,
3488 : temporaryStorageLocks,
3489 0 : inactiveOrigins);
3490 : }
3491 :
3492 0 : groupInfo = pair->LockedGetGroupInfo(PERSISTENCE_TYPE_DEFAULT);
3493 0 : if (groupInfo) {
3494 0 : Helper::GetInactiveOriginInfos(groupInfo->mOriginInfos,
3495 : defaultStorageLocks,
3496 0 : inactiveOrigins);
3497 : }
3498 : }
3499 :
3500 : #ifdef DEBUG
3501 : // Make sure the array is sorted correctly.
3502 0 : for (uint32_t index = inactiveOrigins.Length(); index > 1; index--) {
3503 0 : MOZ_ASSERT(inactiveOrigins[index - 1]->mAccessTime >=
3504 : inactiveOrigins[index - 2]->mAccessTime);
3505 : }
3506 : #endif
3507 :
3508 : // Create a list of inactive and the least recently used origins
3509 : // whose aggregate size is greater or equals the minimal size to be freed.
3510 0 : uint64_t sizeToBeFreed = 0;
3511 0 : for(uint32_t count = inactiveOrigins.Length(), index = 0;
3512 0 : index < count;
3513 : index++) {
3514 0 : if (sizeToBeFreed >= aMinSizeToBeFreed) {
3515 0 : inactiveOrigins.TruncateLength(index);
3516 0 : break;
3517 : }
3518 :
3519 0 : sizeToBeFreed += inactiveOrigins[index]->mUsage;
3520 : }
3521 :
3522 0 : if (sizeToBeFreed >= aMinSizeToBeFreed) {
3523 : // Success, add directory locks for these origins, so any other
3524 : // operations for them will be delayed (until origin eviction is finalized).
3525 :
3526 0 : for (OriginInfo* originInfo : inactiveOrigins) {
3527 : RefPtr<DirectoryLockImpl> lock =
3528 0 : CreateDirectoryLockForEviction(originInfo->mGroupInfo->mPersistenceType,
3529 0 : originInfo->mGroupInfo->mGroup,
3530 0 : originInfo->mOrigin);
3531 0 : aLocks.AppendElement(lock.forget());
3532 : }
3533 :
3534 0 : return sizeToBeFreed;
3535 : }
3536 :
3537 0 : return 0;
3538 : }
3539 :
3540 : nsresult
3541 0 : QuotaManager::Init(const nsAString& aBasePath)
3542 : {
3543 : nsresult rv;
3544 :
3545 0 : mBasePath = aBasePath;
3546 :
3547 0 : nsCOMPtr<nsIFile> baseDir = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
3548 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3549 0 : return rv;
3550 : }
3551 :
3552 0 : rv = baseDir->InitWithPath(aBasePath);
3553 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3554 0 : return rv;
3555 : }
3556 :
3557 0 : rv = CloneStoragePath(baseDir,
3558 0 : NS_LITERAL_STRING(INDEXEDDB_DIRECTORY_NAME),
3559 0 : mIndexedDBPath);
3560 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3561 0 : return rv;
3562 : }
3563 :
3564 0 : rv = baseDir->Append(NS_LITERAL_STRING(STORAGE_DIRECTORY_NAME));
3565 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3566 0 : return rv;
3567 : }
3568 :
3569 0 : rv = baseDir->GetPath(mStoragePath);
3570 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3571 0 : return rv;
3572 : }
3573 :
3574 0 : rv = CloneStoragePath(baseDir,
3575 0 : NS_LITERAL_STRING(PERMANENT_DIRECTORY_NAME),
3576 0 : mPermanentStoragePath);
3577 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3578 0 : return rv;
3579 : }
3580 :
3581 0 : rv = CloneStoragePath(baseDir,
3582 0 : NS_LITERAL_STRING(TEMPORARY_DIRECTORY_NAME),
3583 0 : mTemporaryStoragePath);
3584 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3585 0 : return rv;
3586 : }
3587 :
3588 0 : rv = CloneStoragePath(baseDir,
3589 0 : NS_LITERAL_STRING(DEFAULT_DIRECTORY_NAME),
3590 0 : mDefaultStoragePath);
3591 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3592 0 : return rv;
3593 : }
3594 :
3595 : // Make a lazy thread for any IO we need (like clearing or enumerating the
3596 : // contents of storage directories).
3597 : mIOThread = new LazyIdleThread(DEFAULT_THREAD_TIMEOUT_MS,
3598 0 : NS_LITERAL_CSTRING("Storage I/O"),
3599 0 : LazyIdleThread::ManualShutdown);
3600 :
3601 : // Make a timer here to avoid potential failures later. We don't actually
3602 : // initialize the timer until shutdown.
3603 0 : mShutdownTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
3604 0 : if (NS_WARN_IF(!mShutdownTimer)) {
3605 0 : return NS_ERROR_FAILURE;
3606 : }
3607 :
3608 : static_assert(Client::IDB == 0 && Client::ASMJS == 1 && Client::DOMCACHE == 2 &&
3609 : Client::TYPE_MAX == 3, "Fix the registration!");
3610 :
3611 0 : MOZ_ASSERT(mClients.Capacity() == Client::TYPE_MAX,
3612 : "Should be using an auto array with correct capacity!");
3613 :
3614 : // Register clients.
3615 0 : mClients.AppendElement(indexedDB::CreateQuotaClient());
3616 0 : mClients.AppendElement(asmjscache::CreateClient());
3617 0 : mClients.AppendElement(cache::CreateQuotaClient());
3618 :
3619 0 : return NS_OK;
3620 : }
3621 :
3622 : void
3623 0 : QuotaManager::Shutdown()
3624 : {
3625 0 : AssertIsOnOwningThread();
3626 :
3627 : // Setting this flag prevents the service from being recreated and prevents
3628 : // further storagess from being created.
3629 0 : if (gShutdown.exchange(true)) {
3630 0 : NS_ERROR("Shutdown more than once?!");
3631 : }
3632 :
3633 0 : StopIdleMaintenance();
3634 :
3635 : // Kick off the shutdown timer.
3636 0 : MOZ_ALWAYS_SUCCEEDS(
3637 : mShutdownTimer->InitWithNamedFuncCallback(&ShutdownTimerCallback,
3638 : this,
3639 : DEFAULT_SHUTDOWN_TIMER_MS,
3640 : nsITimer::TYPE_ONE_SHOT,
3641 : "QuotaManager::ShutdownTimerCallback"));
3642 :
3643 : // Each client will spin the event loop while we wait on all the threads
3644 : // to close. Our timer may fire during that loop.
3645 0 : for (uint32_t index = 0; index < Client::TYPE_MAX; index++) {
3646 0 : mClients[index]->ShutdownWorkThreads();
3647 : }
3648 :
3649 : // Cancel the timer regardless of whether it actually fired.
3650 0 : if (NS_FAILED(mShutdownTimer->Cancel())) {
3651 0 : NS_WARNING("Failed to cancel shutdown timer!");
3652 : }
3653 :
3654 : // NB: It's very important that runnable is destroyed on this thread
3655 : // (i.e. after we join the IO thread) because we can't release the
3656 : // QuotaManager on the IO thread. This should probably use
3657 : // NewNonOwningRunnableMethod ...
3658 : RefPtr<Runnable> runnable =
3659 0 : NewRunnableMethod("dom::quota::QuotaManager::ReleaseIOThreadObjects",
3660 : this,
3661 0 : &QuotaManager::ReleaseIOThreadObjects);
3662 0 : MOZ_ASSERT(runnable);
3663 :
3664 : // Give clients a chance to cleanup IO thread only objects.
3665 0 : if (NS_FAILED(mIOThread->Dispatch(runnable, NS_DISPATCH_NORMAL))) {
3666 0 : NS_WARNING("Failed to dispatch runnable!");
3667 : }
3668 :
3669 : // Make sure to join with our IO thread.
3670 0 : if (NS_FAILED(mIOThread->Shutdown())) {
3671 0 : NS_WARNING("Failed to shutdown IO thread!");
3672 : }
3673 :
3674 0 : for (RefPtr<DirectoryLockImpl>& lock : mPendingDirectoryLocks) {
3675 0 : lock->Invalidate();
3676 : }
3677 0 : }
3678 :
3679 : void
3680 0 : QuotaManager::InitQuotaForOrigin(PersistenceType aPersistenceType,
3681 : const nsACString& aGroup,
3682 : const nsACString& aOrigin,
3683 : uint64_t aUsageBytes,
3684 : int64_t aAccessTime,
3685 : bool aPersisted)
3686 : {
3687 0 : AssertIsOnIOThread();
3688 0 : MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_PERSISTENT);
3689 :
3690 0 : MutexAutoLock lock(mQuotaMutex);
3691 :
3692 : GroupInfoPair* pair;
3693 0 : if (!mGroupInfoPairs.Get(aGroup, &pair)) {
3694 0 : pair = new GroupInfoPair();
3695 0 : mGroupInfoPairs.Put(aGroup, pair);
3696 : // The hashtable is now responsible to delete the GroupInfoPair.
3697 : }
3698 :
3699 0 : RefPtr<GroupInfo> groupInfo = pair->LockedGetGroupInfo(aPersistenceType);
3700 0 : if (!groupInfo) {
3701 0 : groupInfo = new GroupInfo(pair, aPersistenceType, aGroup);
3702 0 : pair->LockedSetGroupInfo(aPersistenceType, groupInfo);
3703 : }
3704 :
3705 : RefPtr<OriginInfo> originInfo =
3706 0 : new OriginInfo(groupInfo, aOrigin, aUsageBytes, aAccessTime, aPersisted);
3707 0 : groupInfo->LockedAddOriginInfo(originInfo);
3708 0 : }
3709 :
3710 : void
3711 0 : QuotaManager::DecreaseUsageForOrigin(PersistenceType aPersistenceType,
3712 : const nsACString& aGroup,
3713 : const nsACString& aOrigin,
3714 : int64_t aSize)
3715 : {
3716 0 : MOZ_ASSERT(!NS_IsMainThread());
3717 0 : MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_PERSISTENT);
3718 :
3719 0 : MutexAutoLock lock(mQuotaMutex);
3720 :
3721 : GroupInfoPair* pair;
3722 0 : if (!mGroupInfoPairs.Get(aGroup, &pair)) {
3723 0 : return;
3724 : }
3725 :
3726 0 : RefPtr<GroupInfo> groupInfo = pair->LockedGetGroupInfo(aPersistenceType);
3727 0 : if (!groupInfo) {
3728 0 : return;
3729 : }
3730 :
3731 0 : RefPtr<OriginInfo> originInfo = groupInfo->LockedGetOriginInfo(aOrigin);
3732 0 : if (originInfo) {
3733 0 : originInfo->LockedDecreaseUsage(aSize);
3734 : }
3735 : }
3736 :
3737 : void
3738 0 : QuotaManager::UpdateOriginAccessTime(PersistenceType aPersistenceType,
3739 : const nsACString& aGroup,
3740 : const nsACString& aOrigin)
3741 : {
3742 0 : AssertIsOnOwningThread();
3743 0 : MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_PERSISTENT);
3744 :
3745 0 : MutexAutoLock lock(mQuotaMutex);
3746 :
3747 : GroupInfoPair* pair;
3748 0 : if (!mGroupInfoPairs.Get(aGroup, &pair)) {
3749 0 : return;
3750 : }
3751 :
3752 0 : RefPtr<GroupInfo> groupInfo = pair->LockedGetGroupInfo(aPersistenceType);
3753 0 : if (!groupInfo) {
3754 0 : return;
3755 : }
3756 :
3757 0 : RefPtr<OriginInfo> originInfo = groupInfo->LockedGetOriginInfo(aOrigin);
3758 0 : if (originInfo) {
3759 0 : int64_t timestamp = PR_Now();
3760 0 : originInfo->LockedUpdateAccessTime(timestamp);
3761 :
3762 0 : MutexAutoUnlock autoUnlock(mQuotaMutex);
3763 :
3764 : RefPtr<SaveOriginAccessTimeOp> op =
3765 0 : new SaveOriginAccessTimeOp(aPersistenceType, aOrigin, timestamp);
3766 :
3767 0 : op->RunImmediately();
3768 : }
3769 : }
3770 :
3771 : void
3772 0 : QuotaManager::RemoveQuota()
3773 : {
3774 0 : AssertIsOnIOThread();
3775 :
3776 0 : MutexAutoLock lock(mQuotaMutex);
3777 :
3778 0 : for (auto iter = mGroupInfoPairs.Iter(); !iter.Done(); iter.Next()) {
3779 0 : nsAutoPtr<GroupInfoPair>& pair = iter.Data();
3780 :
3781 0 : MOZ_ASSERT(!iter.Key().IsEmpty(), "Empty key!");
3782 0 : MOZ_ASSERT(pair, "Null pointer!");
3783 :
3784 : RefPtr<GroupInfo> groupInfo =
3785 0 : pair->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY);
3786 0 : if (groupInfo) {
3787 0 : groupInfo->LockedRemoveOriginInfos();
3788 : }
3789 :
3790 0 : groupInfo = pair->LockedGetGroupInfo(PERSISTENCE_TYPE_DEFAULT);
3791 0 : if (groupInfo) {
3792 0 : groupInfo->LockedRemoveOriginInfos();
3793 : }
3794 :
3795 0 : iter.Remove();
3796 : }
3797 :
3798 0 : NS_ASSERTION(mTemporaryStorageUsage == 0, "Should be zero!");
3799 0 : }
3800 :
3801 : already_AddRefed<QuotaObject>
3802 0 : QuotaManager::GetQuotaObject(PersistenceType aPersistenceType,
3803 : const nsACString& aGroup,
3804 : const nsACString& aOrigin,
3805 : nsIFile* aFile)
3806 : {
3807 0 : NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
3808 :
3809 0 : if (aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) {
3810 0 : return nullptr;
3811 : }
3812 :
3813 0 : nsString path;
3814 0 : nsresult rv = aFile->GetPath(path);
3815 0 : NS_ENSURE_SUCCESS(rv, nullptr);
3816 :
3817 : int64_t fileSize;
3818 :
3819 : bool exists;
3820 0 : rv = aFile->Exists(&exists);
3821 0 : NS_ENSURE_SUCCESS(rv, nullptr);
3822 :
3823 0 : if (exists) {
3824 0 : rv = aFile->GetFileSize(&fileSize);
3825 0 : NS_ENSURE_SUCCESS(rv, nullptr);
3826 : }
3827 : else {
3828 0 : fileSize = 0;
3829 : }
3830 :
3831 : // Re-escape our parameters above to make sure we get the right quota group.
3832 0 : nsAutoCString group;
3833 0 : rv = NS_EscapeURL(aGroup, esc_Query, group, fallible);
3834 0 : NS_ENSURE_SUCCESS(rv, nullptr);
3835 :
3836 0 : nsAutoCString origin;
3837 0 : rv = NS_EscapeURL(aOrigin, esc_Query, origin, fallible);
3838 0 : NS_ENSURE_SUCCESS(rv, nullptr);
3839 :
3840 0 : RefPtr<QuotaObject> result;
3841 : {
3842 0 : MutexAutoLock lock(mQuotaMutex);
3843 :
3844 : GroupInfoPair* pair;
3845 0 : if (!mGroupInfoPairs.Get(group, &pair)) {
3846 0 : return nullptr;
3847 : }
3848 :
3849 : RefPtr<GroupInfo> groupInfo =
3850 0 : pair->LockedGetGroupInfo(aPersistenceType);
3851 :
3852 0 : if (!groupInfo) {
3853 0 : return nullptr;
3854 : }
3855 :
3856 0 : RefPtr<OriginInfo> originInfo = groupInfo->LockedGetOriginInfo(origin);
3857 :
3858 0 : if (!originInfo) {
3859 0 : return nullptr;
3860 : }
3861 :
3862 : // We need this extra raw pointer because we can't assign to the smart
3863 : // pointer directly since QuotaObject::AddRef would try to acquire the same
3864 : // mutex.
3865 : QuotaObject* quotaObject;
3866 0 : if (!originInfo->mQuotaObjects.Get(path, "aObject)) {
3867 : // Create a new QuotaObject.
3868 0 : quotaObject = new QuotaObject(originInfo, path, fileSize);
3869 :
3870 : // Put it to the hashtable. The hashtable is not responsible to delete
3871 : // the QuotaObject.
3872 0 : originInfo->mQuotaObjects.Put(path, quotaObject);
3873 : }
3874 :
3875 : // Addref the QuotaObject and move the ownership to the result. This must
3876 : // happen before we unlock!
3877 0 : result = quotaObject->LockedAddRef();
3878 : }
3879 :
3880 : // The caller becomes the owner of the QuotaObject, that is, the caller is
3881 : // is responsible to delete it when the last reference is removed.
3882 0 : return result.forget();
3883 : }
3884 :
3885 : already_AddRefed<QuotaObject>
3886 0 : QuotaManager::GetQuotaObject(PersistenceType aPersistenceType,
3887 : const nsACString& aGroup,
3888 : const nsACString& aOrigin,
3889 : const nsAString& aPath)
3890 : {
3891 : nsresult rv;
3892 0 : nsCOMPtr<nsIFile> file = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
3893 0 : NS_ENSURE_SUCCESS(rv, nullptr);
3894 :
3895 0 : rv = file->InitWithPath(aPath);
3896 0 : NS_ENSURE_SUCCESS(rv, nullptr);
3897 :
3898 0 : return GetQuotaObject(aPersistenceType, aGroup, aOrigin, file);
3899 : }
3900 :
3901 : Nullable<bool>
3902 0 : QuotaManager::OriginPersisted(const nsACString& aGroup,
3903 : const nsACString& aOrigin)
3904 : {
3905 0 : AssertIsOnIOThread();
3906 :
3907 0 : MutexAutoLock lock(mQuotaMutex);
3908 :
3909 0 : RefPtr<OriginInfo> originInfo = LockedGetOriginInfo(PERSISTENCE_TYPE_DEFAULT,
3910 : aGroup,
3911 0 : aOrigin);
3912 0 : if (originInfo) {
3913 0 : return Nullable<bool>(originInfo->LockedPersisted());
3914 : }
3915 :
3916 0 : return Nullable<bool>();
3917 : }
3918 :
3919 : void
3920 0 : QuotaManager::PersistOrigin(const nsACString& aGroup,
3921 : const nsACString& aOrigin)
3922 : {
3923 0 : AssertIsOnIOThread();
3924 :
3925 0 : MutexAutoLock lock(mQuotaMutex);
3926 :
3927 0 : RefPtr<OriginInfo> originInfo = LockedGetOriginInfo(PERSISTENCE_TYPE_DEFAULT,
3928 : aGroup,
3929 0 : aOrigin);
3930 0 : if (originInfo && !originInfo->LockedPersisted()) {
3931 0 : originInfo->LockedPersist();
3932 : }
3933 0 : }
3934 :
3935 : void
3936 0 : QuotaManager::AbortOperationsForProcess(ContentParentId aContentParentId)
3937 : {
3938 0 : AssertIsOnOwningThread();
3939 :
3940 0 : for (RefPtr<Client>& client : mClients) {
3941 0 : client->AbortOperationsForProcess(aContentParentId);
3942 : }
3943 0 : }
3944 :
3945 : nsresult
3946 0 : QuotaManager::GetDirectoryForOrigin(PersistenceType aPersistenceType,
3947 : const nsACString& aASCIIOrigin,
3948 : nsIFile** aDirectory) const
3949 : {
3950 : nsresult rv;
3951 : nsCOMPtr<nsIFile> directory =
3952 0 : do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
3953 0 : NS_ENSURE_SUCCESS(rv, rv);
3954 :
3955 0 : rv = directory->InitWithPath(GetStoragePath(aPersistenceType));
3956 0 : NS_ENSURE_SUCCESS(rv, rv);
3957 :
3958 0 : nsAutoCString originSanitized(aASCIIOrigin);
3959 0 : SanitizeOriginString(originSanitized);
3960 :
3961 0 : rv = directory->Append(NS_ConvertASCIItoUTF16(originSanitized));
3962 0 : NS_ENSURE_SUCCESS(rv, rv);
3963 :
3964 0 : directory.forget(aDirectory);
3965 0 : return NS_OK;
3966 : }
3967 :
3968 : nsresult
3969 0 : QuotaManager::RestoreDirectoryMetadata2(nsIFile* aDirectory, bool aPersistent)
3970 : {
3971 0 : AssertIsOnIOThread();
3972 0 : MOZ_ASSERT(aDirectory);
3973 0 : MOZ_ASSERT(mStorageInitialized);
3974 :
3975 : RefPtr<RestoreDirectoryMetadata2Helper> helper =
3976 0 : new RestoreDirectoryMetadata2Helper(aDirectory, aPersistent);
3977 :
3978 0 : nsresult rv = helper->RestoreMetadata2File();
3979 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3980 0 : return rv;
3981 : }
3982 :
3983 0 : return NS_OK;
3984 : }
3985 :
3986 : nsresult
3987 0 : QuotaManager::GetDirectoryMetadata2(nsIFile* aDirectory,
3988 : int64_t* aTimestamp,
3989 : bool* aPersisted,
3990 : nsACString& aSuffix,
3991 : nsACString& aGroup,
3992 : nsACString& aOrigin)
3993 : {
3994 0 : MOZ_ASSERT(!NS_IsMainThread());
3995 0 : MOZ_ASSERT(aDirectory);
3996 0 : MOZ_ASSERT(aTimestamp);
3997 0 : MOZ_ASSERT(aPersisted);
3998 0 : MOZ_ASSERT(mStorageInitialized);
3999 :
4000 0 : nsCOMPtr<nsIBinaryInputStream> binaryStream;
4001 0 : nsresult rv = GetBinaryInputStream(aDirectory,
4002 0 : NS_LITERAL_STRING(METADATA_V2_FILE_NAME),
4003 0 : getter_AddRefs(binaryStream));
4004 0 : NS_ENSURE_SUCCESS(rv, rv);
4005 :
4006 : uint64_t timestamp;
4007 0 : rv = binaryStream->Read64(×tamp);
4008 0 : NS_ENSURE_SUCCESS(rv, rv);
4009 :
4010 : bool persisted;
4011 0 : rv = binaryStream->ReadBoolean(&persisted);
4012 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4013 0 : return rv;
4014 : }
4015 :
4016 : uint32_t reservedData1;
4017 0 : rv = binaryStream->Read32(&reservedData1);
4018 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4019 0 : return rv;
4020 : }
4021 :
4022 : uint32_t reservedData2;
4023 0 : rv = binaryStream->Read32(&reservedData2);
4024 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4025 0 : return rv;
4026 : }
4027 :
4028 0 : nsCString suffix;
4029 0 : rv = binaryStream->ReadCString(suffix);
4030 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4031 0 : return rv;
4032 : }
4033 :
4034 0 : nsCString group;
4035 0 : rv = binaryStream->ReadCString(group);
4036 0 : NS_ENSURE_SUCCESS(rv, rv);
4037 :
4038 0 : nsCString origin;
4039 0 : rv = binaryStream->ReadCString(origin);
4040 0 : NS_ENSURE_SUCCESS(rv, rv);
4041 :
4042 : // Currently unused (used to be isApp).
4043 : bool dummy;
4044 0 : rv = binaryStream->ReadBoolean(&dummy);
4045 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4046 0 : return rv;
4047 : }
4048 :
4049 0 : *aTimestamp = timestamp;
4050 0 : *aPersisted = persisted;
4051 0 : aSuffix = suffix;
4052 0 : aGroup = group;
4053 0 : aOrigin = origin;
4054 0 : return NS_OK;
4055 : }
4056 :
4057 : nsresult
4058 0 : QuotaManager::GetDirectoryMetadata2WithRestore(nsIFile* aDirectory,
4059 : bool aPersistent,
4060 : int64_t* aTimestamp,
4061 : bool* aPersisted,
4062 : nsACString& aSuffix,
4063 : nsACString& aGroup,
4064 : nsACString& aOrigin)
4065 : {
4066 : nsresult rv = GetDirectoryMetadata2(aDirectory,
4067 : aTimestamp,
4068 : aPersisted,
4069 : aSuffix,
4070 : aGroup,
4071 0 : aOrigin);
4072 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4073 0 : rv = RestoreDirectoryMetadata2(aDirectory, aPersistent);
4074 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4075 0 : return rv;
4076 : }
4077 :
4078 : rv = GetDirectoryMetadata2(aDirectory,
4079 : aTimestamp,
4080 : aPersisted,
4081 : aSuffix,
4082 : aGroup,
4083 0 : aOrigin);
4084 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4085 0 : return rv;
4086 : }
4087 : }
4088 :
4089 0 : return NS_OK;
4090 : }
4091 :
4092 : nsresult
4093 0 : QuotaManager::GetDirectoryMetadata2(nsIFile* aDirectory,
4094 : int64_t* aTimestamp,
4095 : bool* aPersisted)
4096 : {
4097 0 : AssertIsOnIOThread();
4098 0 : MOZ_ASSERT(aDirectory);
4099 0 : MOZ_ASSERT(aTimestamp || aPersisted);
4100 0 : MOZ_ASSERT(mStorageInitialized);
4101 :
4102 0 : nsCOMPtr<nsIBinaryInputStream> binaryStream;
4103 0 : nsresult rv = GetBinaryInputStream(aDirectory,
4104 0 : NS_LITERAL_STRING(METADATA_V2_FILE_NAME),
4105 0 : getter_AddRefs(binaryStream));
4106 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4107 0 : return rv;
4108 : }
4109 :
4110 : uint64_t timestamp;
4111 0 : rv = binaryStream->Read64(×tamp);
4112 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4113 0 : return rv;
4114 : }
4115 :
4116 : bool persisted;
4117 0 : if (aPersisted) {
4118 0 : rv = binaryStream->ReadBoolean(&persisted);
4119 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4120 0 : return rv;
4121 : }
4122 : }
4123 :
4124 0 : if (aTimestamp) {
4125 0 : *aTimestamp = timestamp;
4126 : }
4127 0 : if (aPersisted) {
4128 0 : *aPersisted = persisted;
4129 : }
4130 0 : return NS_OK;
4131 : }
4132 :
4133 : nsresult
4134 0 : QuotaManager::GetDirectoryMetadata2WithRestore(nsIFile* aDirectory,
4135 : bool aPersistent,
4136 : int64_t* aTimestamp,
4137 : bool* aPersisted)
4138 : {
4139 0 : nsresult rv = GetDirectoryMetadata2(aDirectory, aTimestamp, aPersisted);
4140 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4141 0 : rv = RestoreDirectoryMetadata2(aDirectory, aPersistent);
4142 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4143 0 : return rv;
4144 : }
4145 :
4146 0 : rv = GetDirectoryMetadata2(aDirectory, aTimestamp, aPersisted);
4147 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4148 0 : return rv;
4149 : }
4150 : }
4151 :
4152 0 : return NS_OK;
4153 : }
4154 :
4155 : nsresult
4156 0 : QuotaManager::InitializeRepository(PersistenceType aPersistenceType)
4157 : {
4158 0 : MOZ_ASSERT(aPersistenceType == PERSISTENCE_TYPE_TEMPORARY ||
4159 : aPersistenceType == PERSISTENCE_TYPE_DEFAULT);
4160 :
4161 : nsresult rv;
4162 :
4163 : nsCOMPtr<nsIFile> directory =
4164 0 : do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
4165 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4166 0 : return rv;
4167 : }
4168 :
4169 0 : rv = directory->InitWithPath(GetStoragePath(aPersistenceType));
4170 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4171 0 : return rv;
4172 : }
4173 :
4174 : bool created;
4175 0 : rv = EnsureDirectory(directory, &created);
4176 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4177 0 : return rv;
4178 : }
4179 :
4180 0 : nsCOMPtr<nsISimpleEnumerator> entries;
4181 0 : rv = directory->GetDirectoryEntries(getter_AddRefs(entries));
4182 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4183 0 : return rv;
4184 : }
4185 :
4186 : bool hasMore;
4187 0 : while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) {
4188 0 : nsCOMPtr<nsISupports> entry;
4189 0 : rv = entries->GetNext(getter_AddRefs(entry));
4190 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4191 0 : return rv;
4192 : }
4193 :
4194 0 : nsCOMPtr<nsIFile> childDirectory = do_QueryInterface(entry);
4195 0 : MOZ_ASSERT(childDirectory);
4196 :
4197 : bool isDirectory;
4198 0 : rv = childDirectory->IsDirectory(&isDirectory);
4199 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4200 0 : return rv;
4201 : }
4202 :
4203 0 : if (!isDirectory) {
4204 0 : nsString leafName;
4205 0 : rv = childDirectory->GetLeafName(leafName);
4206 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4207 0 : return rv;
4208 : }
4209 :
4210 0 : if (IsOSMetadata(leafName)) {
4211 0 : continue;
4212 : }
4213 :
4214 0 : UNKNOWN_FILE_WARNING(leafName);
4215 0 : return NS_ERROR_UNEXPECTED;
4216 : }
4217 :
4218 : int64_t timestamp;
4219 : bool persisted;
4220 0 : nsCString suffix;
4221 0 : nsCString group;
4222 0 : nsCString origin;
4223 0 : rv = GetDirectoryMetadata2WithRestore(childDirectory,
4224 : /* aPersistent */ false,
4225 : ×tamp,
4226 : &persisted,
4227 : suffix,
4228 : group,
4229 : origin);
4230 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4231 0 : return rv;
4232 : }
4233 :
4234 0 : rv = InitializeOrigin(aPersistenceType, group, origin, timestamp, persisted,
4235 : childDirectory);
4236 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4237 0 : return rv;
4238 : }
4239 : }
4240 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4241 0 : return rv;
4242 : }
4243 :
4244 0 : return NS_OK;
4245 : }
4246 :
4247 : nsresult
4248 0 : QuotaManager::InitializeOrigin(PersistenceType aPersistenceType,
4249 : const nsACString& aGroup,
4250 : const nsACString& aOrigin,
4251 : int64_t aAccessTime,
4252 : bool aPersisted,
4253 : nsIFile* aDirectory)
4254 : {
4255 0 : AssertIsOnIOThread();
4256 :
4257 : nsresult rv;
4258 :
4259 0 : bool trackQuota = aPersistenceType != PERSISTENCE_TYPE_PERSISTENT;
4260 :
4261 : // We need to initialize directories of all clients if they exists and also
4262 : // get the total usage to initialize the quota.
4263 0 : nsAutoPtr<UsageInfo> usageInfo;
4264 0 : if (trackQuota) {
4265 0 : usageInfo = new UsageInfo();
4266 : }
4267 :
4268 0 : nsCOMPtr<nsISimpleEnumerator> entries;
4269 0 : rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries));
4270 0 : NS_ENSURE_SUCCESS(rv, rv);
4271 :
4272 : bool hasMore;
4273 0 : while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) {
4274 0 : nsCOMPtr<nsISupports> entry;
4275 0 : rv = entries->GetNext(getter_AddRefs(entry));
4276 0 : NS_ENSURE_SUCCESS(rv, rv);
4277 :
4278 0 : nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
4279 0 : NS_ENSURE_TRUE(file, NS_NOINTERFACE);
4280 :
4281 : bool isDirectory;
4282 0 : rv = file->IsDirectory(&isDirectory);
4283 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4284 0 : return rv;
4285 : }
4286 :
4287 0 : nsString leafName;
4288 0 : rv = file->GetLeafName(leafName);
4289 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4290 0 : return rv;
4291 : }
4292 :
4293 0 : if (!isDirectory) {
4294 0 : if (IsOriginMetadata(leafName)) {
4295 0 : continue;
4296 : }
4297 :
4298 0 : if (IsTempMetadata(leafName)) {
4299 0 : rv = file->Remove(/* recursive */ false);
4300 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4301 0 : return rv;
4302 : }
4303 :
4304 0 : continue;
4305 : }
4306 :
4307 0 : UNKNOWN_FILE_WARNING(leafName);
4308 0 : return NS_ERROR_UNEXPECTED;
4309 : }
4310 :
4311 : Client::Type clientType;
4312 0 : rv = Client::TypeFromText(leafName, clientType);
4313 0 : if (NS_FAILED(rv)) {
4314 0 : UNKNOWN_FILE_WARNING(leafName);
4315 0 : return NS_ERROR_UNEXPECTED;
4316 : }
4317 :
4318 0 : Atomic<bool> dummy(false);
4319 0 : rv = mClients[clientType]->InitOrigin(aPersistenceType,
4320 : aGroup,
4321 : aOrigin,
4322 : /* aCanceled */ dummy,
4323 0 : usageInfo);
4324 0 : NS_ENSURE_SUCCESS(rv, rv);
4325 : }
4326 :
4327 0 : if (trackQuota) {
4328 0 : InitQuotaForOrigin(aPersistenceType, aGroup, aOrigin,
4329 0 : usageInfo->TotalUsage(), aAccessTime, aPersisted);
4330 : }
4331 :
4332 0 : return NS_OK;
4333 : }
4334 :
4335 : nsresult
4336 0 : QuotaManager::MaybeUpgradeIndexedDBDirectory()
4337 : {
4338 0 : AssertIsOnIOThread();
4339 :
4340 : nsresult rv;
4341 :
4342 : nsCOMPtr<nsIFile> indexedDBDir =
4343 0 : do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
4344 0 : NS_ENSURE_SUCCESS(rv, rv);
4345 :
4346 0 : rv = indexedDBDir->InitWithPath(mIndexedDBPath);
4347 0 : NS_ENSURE_SUCCESS(rv, rv);
4348 :
4349 : bool exists;
4350 0 : rv = indexedDBDir->Exists(&exists);
4351 0 : NS_ENSURE_SUCCESS(rv, rv);
4352 :
4353 0 : if (!exists) {
4354 : // Nothing to upgrade.
4355 0 : return NS_OK;
4356 : }
4357 :
4358 : bool isDirectory;
4359 0 : rv = indexedDBDir->IsDirectory(&isDirectory);
4360 0 : NS_ENSURE_SUCCESS(rv, rv);
4361 :
4362 0 : if (!isDirectory) {
4363 0 : NS_WARNING("indexedDB entry is not a directory!");
4364 0 : return NS_OK;
4365 : }
4366 :
4367 : nsCOMPtr<nsIFile> persistentStorageDir =
4368 0 : do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
4369 0 : NS_ENSURE_SUCCESS(rv, rv);
4370 :
4371 0 : rv = persistentStorageDir->InitWithPath(mStoragePath);
4372 0 : NS_ENSURE_SUCCESS(rv, rv);
4373 :
4374 0 : rv = persistentStorageDir->Append(NS_LITERAL_STRING(PERSISTENT_DIRECTORY_NAME));
4375 0 : NS_ENSURE_SUCCESS(rv, rv);
4376 :
4377 0 : rv = persistentStorageDir->Exists(&exists);
4378 0 : NS_ENSURE_SUCCESS(rv, rv);
4379 :
4380 0 : if (exists) {
4381 0 : NS_WARNING("indexedDB directory shouldn't exist after the upgrade!");
4382 0 : return NS_OK;
4383 : }
4384 :
4385 0 : nsCOMPtr<nsIFile> storageDir;
4386 0 : rv = persistentStorageDir->GetParent(getter_AddRefs(storageDir));
4387 0 : NS_ENSURE_SUCCESS(rv, rv);
4388 :
4389 : // MoveTo() is atomic if the move happens on the same volume which should
4390 : // be our case, so even if we crash in the middle of the operation nothing
4391 : // breaks next time we try to initialize.
4392 : // However there's a theoretical possibility that the indexedDB directory
4393 : // is on different volume, but it should be rare enough that we don't have
4394 : // to worry about it.
4395 0 : rv = indexedDBDir->MoveTo(storageDir, NS_LITERAL_STRING(PERSISTENT_DIRECTORY_NAME));
4396 0 : NS_ENSURE_SUCCESS(rv, rv);
4397 :
4398 0 : return NS_OK;
4399 : }
4400 :
4401 : nsresult
4402 0 : QuotaManager::MaybeUpgradePersistentStorageDirectory()
4403 : {
4404 0 : AssertIsOnIOThread();
4405 :
4406 : nsresult rv;
4407 :
4408 : nsCOMPtr<nsIFile> persistentStorageDir =
4409 0 : do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
4410 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4411 0 : return rv;
4412 : }
4413 :
4414 0 : rv = persistentStorageDir->InitWithPath(mStoragePath);
4415 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4416 0 : return rv;
4417 : }
4418 :
4419 0 : rv = persistentStorageDir->Append(NS_LITERAL_STRING(PERSISTENT_DIRECTORY_NAME));
4420 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4421 0 : return rv;
4422 : }
4423 :
4424 : bool exists;
4425 0 : rv = persistentStorageDir->Exists(&exists);
4426 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4427 0 : return rv;
4428 : }
4429 :
4430 0 : if (!exists) {
4431 : // Nothing to upgrade.
4432 0 : return NS_OK;
4433 : }
4434 :
4435 : bool isDirectory;
4436 0 : rv = persistentStorageDir->IsDirectory(&isDirectory);
4437 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4438 0 : return rv;
4439 : }
4440 :
4441 0 : if (!isDirectory) {
4442 0 : NS_WARNING("persistent entry is not a directory!");
4443 0 : return NS_OK;
4444 : }
4445 :
4446 : nsCOMPtr<nsIFile> defaultStorageDir =
4447 0 : do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
4448 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4449 0 : return rv;
4450 : }
4451 :
4452 0 : rv = defaultStorageDir->InitWithPath(mDefaultStoragePath);
4453 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4454 0 : return rv;
4455 : }
4456 :
4457 0 : rv = defaultStorageDir->Exists(&exists);
4458 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4459 0 : return rv;
4460 : }
4461 :
4462 0 : if (exists) {
4463 0 : NS_WARNING("storage/persistent shouldn't exist after the upgrade!");
4464 0 : return NS_OK;
4465 : }
4466 :
4467 : // Create real metadata files for origin directories in persistent storage.
4468 : RefPtr<CreateOrUpgradeDirectoryMetadataHelper> helper =
4469 : new CreateOrUpgradeDirectoryMetadataHelper(persistentStorageDir,
4470 0 : /* aPersistent */ true);
4471 :
4472 0 : rv = helper->CreateOrUpgradeMetadataFiles();
4473 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4474 0 : return rv;
4475 : }
4476 :
4477 : // Upgrade metadata files for origin directories in temporary storage.
4478 : nsCOMPtr<nsIFile> temporaryStorageDir =
4479 0 : do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
4480 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4481 0 : return rv;
4482 : }
4483 :
4484 0 : rv = temporaryStorageDir->InitWithPath(mTemporaryStoragePath);
4485 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4486 0 : return rv;
4487 : }
4488 :
4489 0 : rv = temporaryStorageDir->Exists(&exists);
4490 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4491 0 : return rv;
4492 : }
4493 :
4494 0 : if (exists) {
4495 0 : rv = temporaryStorageDir->IsDirectory(&isDirectory);
4496 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4497 0 : return rv;
4498 : }
4499 :
4500 0 : if (!isDirectory) {
4501 0 : NS_WARNING("temporary entry is not a directory!");
4502 0 : return NS_OK;
4503 : }
4504 :
4505 : helper =
4506 : new CreateOrUpgradeDirectoryMetadataHelper(temporaryStorageDir,
4507 0 : /* aPersistent */ false);
4508 :
4509 0 : rv = helper->CreateOrUpgradeMetadataFiles();
4510 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4511 0 : return rv;
4512 : }
4513 : }
4514 :
4515 : // And finally rename persistent to default.
4516 0 : rv = persistentStorageDir->RenameTo(nullptr, NS_LITERAL_STRING(DEFAULT_DIRECTORY_NAME));
4517 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4518 0 : return rv;
4519 : }
4520 :
4521 0 : return NS_OK;
4522 : }
4523 :
4524 : nsresult
4525 0 : QuotaManager::MaybeRemoveOldDirectories()
4526 : {
4527 0 : AssertIsOnIOThread();
4528 :
4529 : nsresult rv;
4530 :
4531 : nsCOMPtr<nsIFile> indexedDBDir =
4532 0 : do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
4533 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4534 0 : return rv;
4535 : }
4536 :
4537 0 : rv = indexedDBDir->InitWithPath(mIndexedDBPath);
4538 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4539 0 : return rv;
4540 : }
4541 :
4542 : bool exists;
4543 0 : rv = indexedDBDir->Exists(&exists);
4544 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4545 0 : return rv;
4546 : }
4547 :
4548 0 : if (exists) {
4549 0 : QM_WARNING("Deleting old <profile>/indexedDB directory!");
4550 :
4551 0 : rv = indexedDBDir->Remove(/* aRecursive */ true);
4552 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4553 0 : return rv;
4554 : }
4555 : }
4556 :
4557 : nsCOMPtr<nsIFile> persistentStorageDir =
4558 0 : do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
4559 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4560 0 : return rv;
4561 : }
4562 :
4563 0 : rv = persistentStorageDir->InitWithPath(mStoragePath);
4564 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4565 0 : return rv;
4566 : }
4567 :
4568 0 : rv = persistentStorageDir->Append(NS_LITERAL_STRING(PERSISTENT_DIRECTORY_NAME));
4569 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4570 0 : return rv;
4571 : }
4572 :
4573 0 : rv = persistentStorageDir->Exists(&exists);
4574 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4575 0 : return rv;
4576 : }
4577 :
4578 0 : if (exists) {
4579 0 : QM_WARNING("Deleting old <profile>/storage/persistent directory!");
4580 :
4581 0 : rv = persistentStorageDir->Remove(/* aRecursive */ true);
4582 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4583 0 : return rv;
4584 : }
4585 : }
4586 :
4587 0 : return NS_OK;
4588 : }
4589 :
4590 : nsresult
4591 0 : QuotaManager::UpgradeStorageFrom0_0To1_0(mozIStorageConnection* aConnection)
4592 : {
4593 0 : AssertIsOnIOThread();
4594 0 : MOZ_ASSERT(aConnection);
4595 :
4596 0 : nsresult rv = MaybeUpgradeIndexedDBDirectory();
4597 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4598 0 : return rv;
4599 : }
4600 :
4601 0 : rv = MaybeUpgradePersistentStorageDirectory();
4602 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4603 0 : return rv;
4604 : }
4605 :
4606 0 : rv = MaybeRemoveOldDirectories();
4607 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4608 0 : return rv;
4609 : }
4610 :
4611 0 : for (const PersistenceType persistenceType : kAllPersistenceTypes) {
4612 : nsCOMPtr<nsIFile> directory =
4613 0 : do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
4614 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4615 0 : return rv;
4616 : }
4617 :
4618 0 : rv = directory->InitWithPath(GetStoragePath(persistenceType));
4619 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4620 0 : return rv;
4621 : }
4622 :
4623 0 : bool persistent = persistenceType == PERSISTENCE_TYPE_PERSISTENT;
4624 : RefPtr<UpgradeStorageFrom0_0To1_0Helper> helper =
4625 0 : new UpgradeStorageFrom0_0To1_0Helper(directory, persistent);
4626 :
4627 0 : rv = helper->DoUpgrade();
4628 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4629 0 : return rv;
4630 : }
4631 : }
4632 :
4633 : #ifdef DEBUG
4634 : {
4635 : int32_t storageVersion;
4636 0 : rv = aConnection->GetSchemaVersion(&storageVersion);
4637 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4638 0 : return rv;
4639 : }
4640 :
4641 0 : MOZ_ASSERT(storageVersion == 0);
4642 : }
4643 : #endif
4644 :
4645 0 : rv = aConnection->SetSchemaVersion(MakeStorageVersion(1, 0));
4646 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4647 0 : return rv;
4648 : }
4649 :
4650 0 : return NS_OK;
4651 : }
4652 :
4653 : nsresult
4654 0 : QuotaManager::UpgradeStorageFrom1_0To2_0(mozIStorageConnection* aConnection)
4655 : {
4656 0 : AssertIsOnIOThread();
4657 0 : MOZ_ASSERT(aConnection);
4658 :
4659 : // The upgrade consists of a number of logically distinct bugs that
4660 : // intentionally got fixed at the same time to trigger just one major
4661 : // version bump.
4662 : //
4663 : //
4664 : // Morgue directory cleanup
4665 : // [Feature/Bug]:
4666 : // The original bug that added "on demand" morgue cleanup is 1165119.
4667 : //
4668 : // [Mutations]:
4669 : // Morgue directories are removed from all origin directories during the
4670 : // upgrade process. Origin initialization and usage calculation doesn't try
4671 : // to remove morgue directories anymore.
4672 : //
4673 : // [Downgrade-incompatible changes]:
4674 : // Morgue directories can reappear if user runs an already upgraded profile
4675 : // in an older version of Firefox. Morgue directories then prevent current
4676 : // Firefox from initializing and using the storage.
4677 : //
4678 : //
4679 : // App data removal
4680 : // [Feature/Bug]:
4681 : // The bug that removes isApp flags is 1311057.
4682 : //
4683 : // [Mutations]:
4684 : // Origin directories with appIds are removed during the upgrade process.
4685 : //
4686 : // [Downgrade-incompatible changes]:
4687 : // Origin directories with appIds can reappear if user runs an already
4688 : // upgraded profile in an older version of Firefox. Origin directories with
4689 : // appIds don't prevent current Firefox from initializing and using the
4690 : // storage, but they wouldn't ever be removed again, potentially causing
4691 : // problems once appId is removed from origin attributes.
4692 : //
4693 : //
4694 : // Strip obsolete origin attributes
4695 : // [Feature/Bug]:
4696 : // The bug that strips obsolete origin attributes is 1314361.
4697 : //
4698 : // [Mutations]:
4699 : // Origin directories with obsolete origin attributes are renamed and their
4700 : // metadata files are updated during the upgrade process.
4701 : //
4702 : // [Downgrade-incompatible changes]:
4703 : // Origin directories with obsolete origin attributes can reappear if user
4704 : // runs an already upgraded profile in an older version of Firefox. Origin
4705 : // directories with obsolete origin attributes don't prevent current Firefox
4706 : // from initializing and using the storage, but they wouldn't ever be upgraded
4707 : // again, potentially causing problems in future.
4708 : //
4709 : //
4710 : // File manager directory renaming (client specific)
4711 : // [Feature/Bug]:
4712 : // The original bug that added "on demand" file manager directory renaming is
4713 : // 1056939.
4714 : //
4715 : // [Mutations]:
4716 : // All file manager directories are renamed to contain the ".files" suffix.
4717 : //
4718 : // [Downgrade-incompatible changes]:
4719 : // File manager directories with the ".files" suffix prevent older versions of
4720 : // Firefox from initializing and using the storage.
4721 : // File manager directories without the ".files" suffix can appear if user
4722 : // runs an already upgraded profile in an older version of Firefox. File
4723 : // manager directories without the ".files" suffix then prevent current
4724 : // Firefox from initializing and using the storage.
4725 :
4726 : nsresult rv;
4727 :
4728 0 : for (const PersistenceType persistenceType : kAllPersistenceTypes) {
4729 : nsCOMPtr<nsIFile> directory =
4730 0 : do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
4731 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4732 0 : return rv;
4733 : }
4734 :
4735 0 : rv = directory->InitWithPath(GetStoragePath(persistenceType));
4736 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4737 0 : return rv;
4738 : }
4739 :
4740 : bool exists;
4741 0 : rv = directory->Exists(&exists);
4742 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4743 0 : return rv;
4744 : }
4745 :
4746 0 : if (!exists) {
4747 0 : continue;
4748 : }
4749 :
4750 0 : bool persistent = persistenceType == PERSISTENCE_TYPE_PERSISTENT;
4751 : RefPtr<UpgradeStorageFrom1_0To2_0Helper> helper =
4752 0 : new UpgradeStorageFrom1_0To2_0Helper(directory, persistent);
4753 :
4754 0 : rv = helper->DoUpgrade();
4755 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4756 0 : return rv;
4757 : }
4758 : }
4759 :
4760 : #ifdef DEBUG
4761 : {
4762 : int32_t storageVersion;
4763 0 : rv = aConnection->GetSchemaVersion(&storageVersion);
4764 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4765 0 : return rv;
4766 : }
4767 :
4768 0 : MOZ_ASSERT(storageVersion == MakeStorageVersion(1, 0));
4769 : }
4770 : #endif
4771 :
4772 0 : rv = aConnection->SetSchemaVersion(MakeStorageVersion(2, 0));
4773 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4774 0 : return rv;
4775 : }
4776 :
4777 0 : return NS_OK;
4778 : }
4779 :
4780 : #ifdef DEBUG
4781 :
4782 : void
4783 0 : QuotaManager::AssertStorageIsInitialized() const
4784 : {
4785 0 : AssertIsOnIOThread();
4786 0 : MOZ_ASSERT(mStorageInitialized);
4787 0 : }
4788 :
4789 : #endif // DEBUG
4790 :
4791 : nsresult
4792 0 : QuotaManager::EnsureStorageIsInitialized()
4793 : {
4794 0 : AssertIsOnIOThread();
4795 :
4796 0 : if (mStorageInitialized) {
4797 0 : return NS_OK;
4798 : }
4799 :
4800 : nsresult rv;
4801 :
4802 : nsCOMPtr<nsIFile> storageFile =
4803 0 : do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
4804 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4805 0 : return rv;
4806 : }
4807 :
4808 0 : rv = storageFile->InitWithPath(mBasePath);
4809 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4810 0 : return rv;
4811 : }
4812 :
4813 0 : rv = storageFile->Append(NS_LITERAL_STRING(STORAGE_FILE_NAME));
4814 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4815 0 : return rv;
4816 : }
4817 :
4818 : nsCOMPtr<mozIStorageService> ss =
4819 0 : do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv);
4820 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4821 0 : return rv;
4822 : }
4823 :
4824 0 : nsCOMPtr<mozIStorageConnection> connection;
4825 0 : rv = ss->OpenUnsharedDatabase(storageFile, getter_AddRefs(connection));
4826 0 : if (rv == NS_ERROR_FILE_CORRUPTED) {
4827 : // Nuke the database file.
4828 0 : rv = storageFile->Remove(false);
4829 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4830 0 : return rv;
4831 : }
4832 :
4833 0 : rv = ss->OpenUnsharedDatabase(storageFile, getter_AddRefs(connection));
4834 : }
4835 :
4836 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4837 0 : return rv;
4838 : }
4839 :
4840 : // We want extra durability for this important file.
4841 0 : rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
4842 : "PRAGMA synchronous = EXTRA;"
4843 0 : ));
4844 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4845 0 : return rv;
4846 : }
4847 :
4848 : // Check to make sure that the storage version is correct.
4849 : int32_t storageVersion;
4850 0 : rv = connection->GetSchemaVersion(&storageVersion);
4851 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4852 0 : return rv;
4853 : }
4854 :
4855 0 : if (GetMajorStorageVersion(storageVersion) > kMajorStorageVersion) {
4856 0 : NS_WARNING("Unable to initialize storage, version is too high!");
4857 0 : return NS_ERROR_FAILURE;
4858 : }
4859 :
4860 0 : if (storageVersion < kStorageVersion) {
4861 0 : const bool newDatabase = !storageVersion;
4862 :
4863 : nsCOMPtr<nsIFile> storageDir =
4864 0 : do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
4865 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4866 0 : return rv;
4867 : }
4868 :
4869 0 : rv = storageDir->InitWithPath(mStoragePath);
4870 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4871 0 : return rv;
4872 : }
4873 :
4874 : bool exists;
4875 0 : rv = storageDir->Exists(&exists);
4876 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4877 0 : return rv;
4878 : }
4879 :
4880 0 : if (!exists) {
4881 : nsCOMPtr<nsIFile> indexedDBDir =
4882 0 : do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
4883 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4884 0 : return rv;
4885 : }
4886 :
4887 0 : rv = indexedDBDir->InitWithPath(mIndexedDBPath);
4888 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4889 0 : return rv;
4890 : }
4891 :
4892 0 : rv = indexedDBDir->Exists(&exists);
4893 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4894 0 : return rv;
4895 : }
4896 : }
4897 :
4898 0 : const bool newDirectory = !exists;
4899 :
4900 0 : if (newDatabase) {
4901 : // Set the page size first.
4902 : if (kSQLitePageSizeOverride) {
4903 0 : rv = connection->ExecuteSimpleSQL(
4904 0 : nsPrintfCString("PRAGMA page_size = %" PRIu32 ";", kSQLitePageSizeOverride)
4905 0 : );
4906 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4907 0 : return rv;
4908 : }
4909 : }
4910 : }
4911 :
4912 : mozStorageTransaction transaction(connection, false,
4913 0 : mozIStorageConnection::TRANSACTION_IMMEDIATE);
4914 :
4915 : // An upgrade method can upgrade the database, the storage or both.
4916 : // The upgrade loop below can only be avoided when there's no database and
4917 : // no storage yet (e.g. new profile).
4918 0 : if (newDatabase && newDirectory) {
4919 0 : rv = CreateTables(connection);
4920 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4921 0 : return rv;
4922 : }
4923 :
4924 0 : MOZ_ASSERT(NS_SUCCEEDED(connection->GetSchemaVersion(&storageVersion)));
4925 0 : MOZ_ASSERT(storageVersion == kStorageVersion);
4926 : } else {
4927 : // This logic needs to change next time we change the storage!
4928 : static_assert(kStorageVersion == int32_t((2 << 16) + 0),
4929 : "Upgrade function needed due to storage version increase.");
4930 :
4931 0 : while (storageVersion != kStorageVersion) {
4932 0 : if (storageVersion == 0) {
4933 0 : rv = UpgradeStorageFrom0_0To1_0(connection);
4934 0 : } else if (storageVersion == MakeStorageVersion(1, 0)) {
4935 0 : rv = UpgradeStorageFrom1_0To2_0(connection);
4936 : } else {
4937 : NS_WARNING("Unable to initialize storage, no upgrade path is "
4938 0 : "available!");
4939 0 : return NS_ERROR_FAILURE;
4940 : }
4941 :
4942 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4943 0 : return rv;
4944 : }
4945 :
4946 0 : rv = connection->GetSchemaVersion(&storageVersion);
4947 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4948 0 : return rv;
4949 : }
4950 : }
4951 :
4952 0 : MOZ_ASSERT(storageVersion == kStorageVersion);
4953 : }
4954 :
4955 0 : rv = transaction.Commit();
4956 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4957 0 : return rv;
4958 : }
4959 : }
4960 :
4961 0 : mStorageInitialized = true;
4962 :
4963 0 : return NS_OK;
4964 : }
4965 :
4966 : void
4967 0 : QuotaManager::OpenDirectory(PersistenceType aPersistenceType,
4968 : const nsACString& aGroup,
4969 : const nsACString& aOrigin,
4970 : Client::Type aClientType,
4971 : bool aExclusive,
4972 : OpenDirectoryListener* aOpenListener)
4973 : {
4974 0 : AssertIsOnOwningThread();
4975 :
4976 : RefPtr<DirectoryLockImpl> lock =
4977 0 : CreateDirectoryLock(Nullable<PersistenceType>(aPersistenceType),
4978 : aGroup,
4979 0 : OriginScope::FromOrigin(aOrigin),
4980 0 : Nullable<Client::Type>(aClientType),
4981 : aExclusive,
4982 : false,
4983 0 : aOpenListener);
4984 0 : MOZ_ASSERT(lock);
4985 0 : }
4986 :
4987 : void
4988 0 : QuotaManager::OpenDirectoryInternal(const Nullable<PersistenceType>& aPersistenceType,
4989 : const OriginScope& aOriginScope,
4990 : const Nullable<Client::Type>& aClientType,
4991 : bool aExclusive,
4992 : OpenDirectoryListener* aOpenListener)
4993 : {
4994 0 : AssertIsOnOwningThread();
4995 :
4996 : RefPtr<DirectoryLockImpl> lock =
4997 0 : CreateDirectoryLock(aPersistenceType,
4998 0 : EmptyCString(),
4999 : aOriginScope,
5000 0 : Nullable<Client::Type>(aClientType),
5001 : aExclusive,
5002 : true,
5003 0 : aOpenListener);
5004 0 : MOZ_ASSERT(lock);
5005 :
5006 0 : if (!aExclusive) {
5007 0 : return;
5008 : }
5009 :
5010 : // All the locks that block this new exclusive lock need to be invalidated.
5011 : // We also need to notify clients to abort operations for them.
5012 : AutoTArray<nsAutoPtr<nsTHashtable<nsCStringHashKey>>,
5013 0 : Client::TYPE_MAX> origins;
5014 0 : origins.SetLength(Client::TYPE_MAX);
5015 :
5016 : const nsTArray<DirectoryLockImpl*>& blockedOnLocks =
5017 0 : lock->GetBlockedOnLocks();
5018 :
5019 0 : for (DirectoryLockImpl* blockedOnLock : blockedOnLocks) {
5020 0 : blockedOnLock->Invalidate();
5021 :
5022 0 : if (!blockedOnLock->IsInternal()) {
5023 0 : MOZ_ASSERT(!blockedOnLock->GetClientType().IsNull());
5024 0 : Client::Type clientType = blockedOnLock->GetClientType().Value();
5025 0 : MOZ_ASSERT(clientType < Client::TYPE_MAX);
5026 :
5027 0 : const OriginScope& originScope = blockedOnLock->GetOriginScope();
5028 0 : MOZ_ASSERT(originScope.IsOrigin());
5029 0 : MOZ_ASSERT(!originScope.GetOrigin().IsEmpty());
5030 :
5031 0 : nsAutoPtr<nsTHashtable<nsCStringHashKey>>& origin = origins[clientType];
5032 0 : if (!origin) {
5033 0 : origin = new nsTHashtable<nsCStringHashKey>();
5034 : }
5035 0 : origin->PutEntry(originScope.GetOrigin());
5036 : }
5037 : }
5038 :
5039 0 : for (uint32_t index : IntegerRange(uint32_t(Client::TYPE_MAX))) {
5040 0 : if (origins[index]) {
5041 0 : for (auto iter = origins[index]->Iter(); !iter.Done(); iter.Next()) {
5042 0 : MOZ_ASSERT(mClients[index]);
5043 :
5044 0 : mClients[index]->AbortOperations(iter.Get()->GetKey());
5045 : }
5046 : }
5047 : }
5048 : }
5049 :
5050 : nsresult
5051 0 : QuotaManager::EnsureOriginIsInitialized(PersistenceType aPersistenceType,
5052 : const nsACString& aSuffix,
5053 : const nsACString& aGroup,
5054 : const nsACString& aOrigin,
5055 : nsIFile** aDirectory)
5056 : {
5057 0 : AssertIsOnIOThread();
5058 0 : MOZ_ASSERT(aDirectory);
5059 :
5060 0 : nsCOMPtr<nsIFile> directory;
5061 : bool created;
5062 0 : nsresult rv = EnsureOriginIsInitializedInternal(aPersistenceType,
5063 : aSuffix,
5064 : aGroup,
5065 : aOrigin,
5066 0 : getter_AddRefs(directory),
5067 0 : &created);
5068 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
5069 0 : return rv;
5070 : }
5071 :
5072 0 : directory.forget(aDirectory);
5073 0 : return NS_OK;
5074 : }
5075 :
5076 : nsresult
5077 0 : QuotaManager::EnsureOriginIsInitializedInternal(
5078 : PersistenceType aPersistenceType,
5079 : const nsACString& aSuffix,
5080 : const nsACString& aGroup,
5081 : const nsACString& aOrigin,
5082 : nsIFile** aDirectory,
5083 : bool* aCreated)
5084 : {
5085 0 : AssertIsOnIOThread();
5086 0 : MOZ_ASSERT(aDirectory);
5087 0 : MOZ_ASSERT(aCreated);
5088 :
5089 0 : nsresult rv = EnsureStorageIsInitialized();
5090 0 : NS_ENSURE_SUCCESS(rv, rv);
5091 :
5092 : // Get directory for this origin and persistence type.
5093 0 : nsCOMPtr<nsIFile> directory;
5094 0 : rv = GetDirectoryForOrigin(aPersistenceType, aOrigin,
5095 0 : getter_AddRefs(directory));
5096 0 : NS_ENSURE_SUCCESS(rv, rv);
5097 :
5098 0 : if (aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) {
5099 0 : if (mInitializedOrigins.Contains(aOrigin)) {
5100 0 : directory.forget(aDirectory);
5101 0 : *aCreated = false;
5102 0 : return NS_OK;
5103 : }
5104 0 : } else if (!mTemporaryStorageInitialized) {
5105 0 : rv = InitializeRepository(aPersistenceType);
5106 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
5107 : // We have to cleanup partially initialized quota.
5108 0 : RemoveQuota();
5109 :
5110 0 : return rv;
5111 : }
5112 :
5113 0 : rv = InitializeRepository(ComplementaryPersistenceType(aPersistenceType));
5114 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
5115 : // We have to cleanup partially initialized quota.
5116 0 : RemoveQuota();
5117 :
5118 0 : return rv;
5119 : }
5120 :
5121 0 : if (gFixedLimitKB >= 0) {
5122 0 : mTemporaryStorageLimit = static_cast<uint64_t>(gFixedLimitKB) * 1024;
5123 : }
5124 : else {
5125 : nsCOMPtr<nsIFile> storageDir =
5126 0 : do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
5127 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
5128 0 : return rv;
5129 : }
5130 :
5131 0 : rv = storageDir->InitWithPath(GetStoragePath());
5132 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
5133 0 : return rv;
5134 : }
5135 :
5136 0 : rv = GetTemporaryStorageLimit(storageDir, mTemporaryStorageUsage,
5137 : &mTemporaryStorageLimit);
5138 0 : NS_ENSURE_SUCCESS(rv, rv);
5139 : }
5140 :
5141 0 : mTemporaryStorageInitialized = true;
5142 :
5143 0 : CheckTemporaryStorageLimits();
5144 : }
5145 :
5146 : bool created;
5147 0 : rv = EnsureOriginDirectory(directory, &created);
5148 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
5149 0 : return rv;
5150 : }
5151 :
5152 : int64_t timestamp;
5153 0 : if (aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) {
5154 0 : if (created) {
5155 0 : rv = CreateDirectoryMetadataFiles(directory,
5156 : /* aPersisted */ true,
5157 : aSuffix,
5158 : aGroup,
5159 : aOrigin,
5160 : ×tamp);
5161 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
5162 0 : return rv;
5163 : }
5164 : } else {
5165 0 : rv = GetDirectoryMetadata2WithRestore(directory,
5166 : /* aPersistent */ true,
5167 : ×tamp,
5168 : /* aPersisted */ nullptr);
5169 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
5170 0 : return rv;
5171 : }
5172 :
5173 0 : MOZ_ASSERT(timestamp <= PR_Now());
5174 : }
5175 :
5176 0 : rv = InitializeOrigin(aPersistenceType, aGroup, aOrigin, timestamp,
5177 : /* aPersisted */ true, directory);
5178 0 : NS_ENSURE_SUCCESS(rv, rv);
5179 :
5180 0 : mInitializedOrigins.AppendElement(aOrigin);
5181 0 : } else if (created) {
5182 0 : rv = CreateDirectoryMetadataFiles(directory,
5183 : /* aPersisted */ false,
5184 : aSuffix,
5185 : aGroup,
5186 : aOrigin,
5187 : ×tamp);
5188 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
5189 0 : return rv;
5190 : }
5191 :
5192 : // Don't need to traverse the directory, since it's empty.
5193 : InitQuotaForOrigin(aPersistenceType,
5194 : aGroup,
5195 : aOrigin,
5196 : /* aUsageBytes */ 0,
5197 : timestamp,
5198 0 : /* aPersisted */ false);
5199 : }
5200 :
5201 0 : directory.forget(aDirectory);
5202 0 : *aCreated = created;
5203 0 : return NS_OK;
5204 : }
5205 :
5206 : void
5207 0 : QuotaManager::OriginClearCompleted(PersistenceType aPersistenceType,
5208 : const nsACString& aOrigin)
5209 : {
5210 0 : AssertIsOnIOThread();
5211 :
5212 0 : if (aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) {
5213 0 : mInitializedOrigins.RemoveElement(aOrigin);
5214 : }
5215 :
5216 0 : for (uint32_t index = 0; index < Client::TYPE_MAX; index++) {
5217 0 : mClients[index]->OnOriginClearCompleted(aPersistenceType, aOrigin);
5218 : }
5219 0 : }
5220 :
5221 : void
5222 0 : QuotaManager::ResetOrClearCompleted()
5223 : {
5224 0 : AssertIsOnIOThread();
5225 :
5226 0 : mInitializedOrigins.Clear();
5227 0 : mTemporaryStorageInitialized = false;
5228 0 : mStorageInitialized = false;
5229 :
5230 0 : ReleaseIOThreadObjects();
5231 0 : }
5232 :
5233 : Client*
5234 0 : QuotaManager::GetClient(Client::Type aClientType)
5235 : {
5236 0 : MOZ_ASSERT(aClientType >= Client::IDB);
5237 0 : MOZ_ASSERT(aClientType < Client::TYPE_MAX);
5238 :
5239 0 : return mClients.ElementAt(aClientType);
5240 : }
5241 :
5242 : uint64_t
5243 0 : QuotaManager::GetGroupLimit() const
5244 : {
5245 0 : MOZ_ASSERT(mTemporaryStorageInitialized);
5246 :
5247 : // To avoid one group evicting all the rest, limit the amount any one group
5248 : // can use to 20%. To prevent individual sites from using exorbitant amounts
5249 : // of storage where there is a lot of free space, cap the group limit to 2GB.
5250 0 : uint64_t x = std::min<uint64_t>(mTemporaryStorageLimit * .20, 2 GB);
5251 :
5252 : // In low-storage situations, make an exception (while not exceeding the total
5253 : // storage limit).
5254 : return std::min<uint64_t>(mTemporaryStorageLimit,
5255 0 : std::max<uint64_t>(x, 10 MB));
5256 : }
5257 :
5258 : void
5259 0 : QuotaManager::GetGroupUsageAndLimit(const nsACString& aGroup,
5260 : UsageInfo* aUsageInfo)
5261 : {
5262 0 : AssertIsOnIOThread();
5263 0 : MOZ_ASSERT(aUsageInfo);
5264 :
5265 : {
5266 0 : MutexAutoLock lock(mQuotaMutex);
5267 :
5268 0 : aUsageInfo->SetLimit(GetGroupLimit());
5269 0 : aUsageInfo->ResetUsage();
5270 :
5271 : GroupInfoPair* pair;
5272 0 : if (!mGroupInfoPairs.Get(aGroup, &pair)) {
5273 0 : return;
5274 : }
5275 :
5276 : // Calculate temporary group usage
5277 : RefPtr<GroupInfo> temporaryGroupInfo =
5278 0 : pair->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY);
5279 0 : if (temporaryGroupInfo) {
5280 0 : aUsageInfo->AppendToDatabaseUsage(temporaryGroupInfo->mUsage);
5281 : }
5282 :
5283 : // Calculate default group usage
5284 : RefPtr<GroupInfo> defaultGroupInfo =
5285 0 : pair->LockedGetGroupInfo(PERSISTENCE_TYPE_DEFAULT);
5286 0 : if (defaultGroupInfo) {
5287 0 : aUsageInfo->AppendToDatabaseUsage(defaultGroupInfo->mUsage);
5288 : }
5289 : }
5290 : }
5291 :
5292 : // static
5293 : void
5294 0 : QuotaManager::GetStorageId(PersistenceType aPersistenceType,
5295 : const nsACString& aOrigin,
5296 : Client::Type aClientType,
5297 : nsACString& aDatabaseId)
5298 : {
5299 0 : nsAutoCString str;
5300 0 : str.AppendInt(aPersistenceType);
5301 0 : str.Append('*');
5302 0 : str.Append(aOrigin);
5303 0 : str.Append('*');
5304 0 : str.AppendInt(aClientType);
5305 :
5306 0 : aDatabaseId = str;
5307 0 : }
5308 :
5309 : // static
5310 : nsresult
5311 0 : QuotaManager::GetInfoFromPrincipal(nsIPrincipal* aPrincipal,
5312 : nsACString* aSuffix,
5313 : nsACString* aGroup,
5314 : nsACString* aOrigin)
5315 : {
5316 0 : MOZ_ASSERT(NS_IsMainThread());
5317 0 : MOZ_ASSERT(aPrincipal);
5318 :
5319 0 : if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
5320 0 : GetInfoForChrome(aSuffix, aGroup, aOrigin);
5321 0 : return NS_OK;
5322 : }
5323 :
5324 :
5325 0 : if (aPrincipal->GetIsNullPrincipal()) {
5326 0 : NS_WARNING("IndexedDB not supported from this principal!");
5327 0 : return NS_ERROR_FAILURE;
5328 : }
5329 :
5330 0 : nsCString origin;
5331 0 : nsresult rv = aPrincipal->GetOrigin(origin);
5332 0 : NS_ENSURE_SUCCESS(rv, rv);
5333 :
5334 0 : if (origin.EqualsLiteral(kChromeOrigin)) {
5335 0 : NS_WARNING("Non-chrome principal can't use chrome origin!");
5336 0 : return NS_ERROR_FAILURE;
5337 : }
5338 :
5339 0 : nsCString suffix;
5340 0 : aPrincipal->OriginAttributesRef().CreateSuffix(suffix);
5341 :
5342 0 : if (aSuffix)
5343 : {
5344 0 : aSuffix->Assign(suffix);
5345 : }
5346 :
5347 0 : if (aGroup) {
5348 0 : nsCString baseDomain;
5349 0 : rv = aPrincipal->GetBaseDomain(baseDomain);
5350 0 : if (NS_FAILED(rv)) {
5351 : // A hack for JetPack.
5352 :
5353 0 : nsCOMPtr<nsIURI> uri;
5354 0 : rv = aPrincipal->GetURI(getter_AddRefs(uri));
5355 0 : NS_ENSURE_SUCCESS(rv, rv);
5356 :
5357 0 : bool isIndexedDBURI = false;
5358 0 : rv = uri->SchemeIs("indexedDB", &isIndexedDBURI);
5359 0 : NS_ENSURE_SUCCESS(rv, rv);
5360 :
5361 0 : if (isIndexedDBURI) {
5362 0 : rv = NS_OK;
5363 : }
5364 : }
5365 0 : NS_ENSURE_SUCCESS(rv, rv);
5366 :
5367 0 : if (baseDomain.IsEmpty()) {
5368 0 : aGroup->Assign(origin);
5369 : } else {
5370 0 : aGroup->Assign(baseDomain + suffix);
5371 : }
5372 : }
5373 :
5374 0 : if (aOrigin) {
5375 0 : aOrigin->Assign(origin);
5376 : }
5377 :
5378 0 : return NS_OK;
5379 : }
5380 :
5381 : // static
5382 : nsresult
5383 0 : QuotaManager::GetInfoFromWindow(nsPIDOMWindowOuter* aWindow,
5384 : nsACString* aSuffix,
5385 : nsACString* aGroup,
5386 : nsACString* aOrigin)
5387 : {
5388 0 : MOZ_ASSERT(NS_IsMainThread());
5389 0 : MOZ_ASSERT(aWindow);
5390 :
5391 0 : nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(aWindow);
5392 0 : NS_ENSURE_TRUE(sop, NS_ERROR_FAILURE);
5393 :
5394 0 : nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal();
5395 0 : NS_ENSURE_TRUE(principal, NS_ERROR_FAILURE);
5396 :
5397 : nsresult rv =
5398 0 : GetInfoFromPrincipal(principal, aSuffix, aGroup, aOrigin);
5399 0 : NS_ENSURE_SUCCESS(rv, rv);
5400 :
5401 0 : return NS_OK;
5402 : }
5403 :
5404 : // static
5405 : void
5406 0 : QuotaManager::GetInfoForChrome(nsACString* aSuffix,
5407 : nsACString* aGroup,
5408 : nsACString* aOrigin)
5409 : {
5410 0 : MOZ_ASSERT(NS_IsMainThread());
5411 0 : MOZ_ASSERT(nsContentUtils::LegacyIsCallerChromeOrNativeCode());
5412 :
5413 0 : if (aSuffix) {
5414 0 : aSuffix->Assign(EmptyCString());
5415 : }
5416 0 : if (aGroup) {
5417 0 : ChromeOrigin(*aGroup);
5418 : }
5419 0 : if (aOrigin) {
5420 0 : ChromeOrigin(*aOrigin);
5421 : }
5422 0 : }
5423 :
5424 : // static
5425 : bool
5426 0 : QuotaManager::IsOriginInternal(const nsACString& aOrigin)
5427 : {
5428 : // The first prompt is not required for these origins.
5429 0 : if (aOrigin.EqualsLiteral(kChromeOrigin) ||
5430 0 : StringBeginsWith(aOrigin, nsDependentCString(kAboutHomeOriginPrefix)) ||
5431 0 : StringBeginsWith(aOrigin, nsDependentCString(kIndexedDBOriginPrefix)) ||
5432 0 : StringBeginsWith(aOrigin, nsDependentCString(kResourceOriginPrefix))) {
5433 0 : return true;
5434 : }
5435 :
5436 0 : return false;
5437 : }
5438 :
5439 : // static
5440 : void
5441 0 : QuotaManager::ChromeOrigin(nsACString& aOrigin)
5442 : {
5443 0 : aOrigin.AssignLiteral(kChromeOrigin);
5444 0 : }
5445 :
5446 : // static
5447 : bool
5448 0 : QuotaManager::AreOriginsEqualOnDisk(nsACString& aOrigin1,
5449 : nsACString& aOrigin2)
5450 : {
5451 0 : nsCString origin1Sanitized(aOrigin1);
5452 0 : SanitizeOriginString(origin1Sanitized);
5453 :
5454 0 : nsCString origin2Sanitized(aOrigin2);
5455 0 : SanitizeOriginString(origin2Sanitized);
5456 :
5457 0 : return origin1Sanitized == origin2Sanitized;
5458 : }
5459 :
5460 : uint64_t
5461 0 : QuotaManager::LockedCollectOriginsForEviction(
5462 : uint64_t aMinSizeToBeFreed,
5463 : nsTArray<RefPtr<DirectoryLockImpl>>& aLocks)
5464 : {
5465 0 : mQuotaMutex.AssertCurrentThreadOwns();
5466 :
5467 : RefPtr<CollectOriginsHelper> helper =
5468 0 : new CollectOriginsHelper(mQuotaMutex, aMinSizeToBeFreed);
5469 :
5470 : // Unlock while calling out to XPCOM (code behind the dispatch method needs
5471 : // to acquire its own lock which can potentially lead to a deadlock and it
5472 : // also calls an observer that can do various stuff like IO, so it's better
5473 : // to not hold our mutex while that happens).
5474 : {
5475 0 : MutexAutoUnlock autoUnlock(mQuotaMutex);
5476 :
5477 0 : MOZ_ALWAYS_SUCCEEDS(mOwningThread->Dispatch(helper, NS_DISPATCH_NORMAL));
5478 : }
5479 :
5480 0 : return helper->BlockAndReturnOriginsForEviction(aLocks);
5481 : }
5482 :
5483 : void
5484 0 : QuotaManager::LockedRemoveQuotaForOrigin(PersistenceType aPersistenceType,
5485 : const nsACString& aGroup,
5486 : const nsACString& aOrigin)
5487 : {
5488 0 : mQuotaMutex.AssertCurrentThreadOwns();
5489 0 : MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_PERSISTENT);
5490 :
5491 : GroupInfoPair* pair;
5492 0 : mGroupInfoPairs.Get(aGroup, &pair);
5493 :
5494 0 : if (!pair) {
5495 0 : return;
5496 : }
5497 :
5498 0 : RefPtr<GroupInfo> groupInfo = pair->LockedGetGroupInfo(aPersistenceType);
5499 0 : if (groupInfo) {
5500 0 : groupInfo->LockedRemoveOriginInfo(aOrigin);
5501 :
5502 0 : if (!groupInfo->LockedHasOriginInfos()) {
5503 0 : pair->LockedClearGroupInfo(aPersistenceType);
5504 :
5505 0 : if (!pair->LockedHasGroupInfos()) {
5506 0 : mGroupInfoPairs.Remove(aGroup);
5507 : }
5508 : }
5509 : }
5510 : }
5511 :
5512 : already_AddRefed<OriginInfo>
5513 0 : QuotaManager::LockedGetOriginInfo(PersistenceType aPersistenceType,
5514 : const nsACString& aGroup,
5515 : const nsACString& aOrigin)
5516 : {
5517 0 : mQuotaMutex.AssertCurrentThreadOwns();
5518 0 : MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_PERSISTENT);
5519 :
5520 : GroupInfoPair* pair;
5521 0 : if (mGroupInfoPairs.Get(aGroup, &pair)) {
5522 0 : RefPtr<GroupInfo> groupInfo = pair->LockedGetGroupInfo(aPersistenceType);
5523 0 : if (groupInfo) {
5524 0 : return groupInfo->LockedGetOriginInfo(aOrigin);
5525 : }
5526 : }
5527 :
5528 0 : return nullptr;
5529 : }
5530 :
5531 : void
5532 0 : QuotaManager::CheckTemporaryStorageLimits()
5533 : {
5534 0 : AssertIsOnIOThread();
5535 :
5536 0 : nsTArray<OriginInfo*> doomedOriginInfos;
5537 : {
5538 0 : MutexAutoLock lock(mQuotaMutex);
5539 :
5540 0 : for (auto iter = mGroupInfoPairs.Iter(); !iter.Done(); iter.Next()) {
5541 0 : GroupInfoPair* pair = iter.UserData();
5542 :
5543 0 : MOZ_ASSERT(!iter.Key().IsEmpty(), "Empty key!");
5544 0 : MOZ_ASSERT(pair, "Null pointer!");
5545 :
5546 0 : uint64_t groupUsage = 0;
5547 :
5548 : RefPtr<GroupInfo> temporaryGroupInfo =
5549 0 : pair->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY);
5550 0 : if (temporaryGroupInfo) {
5551 0 : groupUsage += temporaryGroupInfo->mUsage;
5552 : }
5553 :
5554 : RefPtr<GroupInfo> defaultGroupInfo =
5555 0 : pair->LockedGetGroupInfo(PERSISTENCE_TYPE_DEFAULT);
5556 0 : if (defaultGroupInfo) {
5557 0 : groupUsage += defaultGroupInfo->mUsage;
5558 : }
5559 :
5560 0 : if (groupUsage > 0) {
5561 0 : QuotaManager* quotaManager = QuotaManager::Get();
5562 0 : MOZ_ASSERT(quotaManager, "Shouldn't be null!");
5563 :
5564 0 : if (groupUsage > quotaManager->GetGroupLimit()) {
5565 0 : nsTArray<OriginInfo*> originInfos;
5566 0 : if (temporaryGroupInfo) {
5567 0 : originInfos.AppendElements(temporaryGroupInfo->mOriginInfos);
5568 : }
5569 0 : if (defaultGroupInfo) {
5570 0 : originInfos.AppendElements(defaultGroupInfo->mOriginInfos);
5571 : }
5572 0 : originInfos.Sort(OriginInfoLRUComparator());
5573 :
5574 0 : for (uint32_t i = 0; i < originInfos.Length(); i++) {
5575 0 : OriginInfo* originInfo = originInfos[i];
5576 0 : if (originInfo->LockedPersisted()) {
5577 0 : continue;
5578 : }
5579 :
5580 0 : doomedOriginInfos.AppendElement(originInfo);
5581 0 : groupUsage -= originInfo->mUsage;
5582 :
5583 0 : if (groupUsage <= quotaManager->GetGroupLimit()) {
5584 0 : break;
5585 : }
5586 : }
5587 : }
5588 : }
5589 : }
5590 :
5591 0 : uint64_t usage = 0;
5592 0 : for (uint32_t index = 0; index < doomedOriginInfos.Length(); index++) {
5593 0 : usage += doomedOriginInfos[index]->mUsage;
5594 : }
5595 :
5596 0 : if (mTemporaryStorageUsage - usage > mTemporaryStorageLimit) {
5597 0 : nsTArray<OriginInfo*> originInfos;
5598 :
5599 0 : for (auto iter = mGroupInfoPairs.Iter(); !iter.Done(); iter.Next()) {
5600 0 : GroupInfoPair* pair = iter.UserData();
5601 :
5602 0 : MOZ_ASSERT(!iter.Key().IsEmpty(), "Empty key!");
5603 0 : MOZ_ASSERT(pair, "Null pointer!");
5604 :
5605 : RefPtr<GroupInfo> groupInfo =
5606 0 : pair->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY);
5607 0 : if (groupInfo) {
5608 0 : originInfos.AppendElements(groupInfo->mOriginInfos);
5609 : }
5610 :
5611 0 : groupInfo = pair->LockedGetGroupInfo(PERSISTENCE_TYPE_DEFAULT);
5612 0 : if (groupInfo) {
5613 0 : originInfos.AppendElements(groupInfo->mOriginInfos);
5614 : }
5615 : }
5616 :
5617 0 : for (uint32_t index = originInfos.Length(); index > 0; index--) {
5618 0 : if (doomedOriginInfos.Contains(originInfos[index - 1]) ||
5619 0 : originInfos[index - 1]->LockedPersisted()) {
5620 0 : originInfos.RemoveElementAt(index - 1);
5621 : }
5622 : }
5623 :
5624 0 : originInfos.Sort(OriginInfoLRUComparator());
5625 :
5626 0 : for (uint32_t i = 0; i < originInfos.Length(); i++) {
5627 0 : if (mTemporaryStorageUsage - usage <= mTemporaryStorageLimit) {
5628 0 : originInfos.TruncateLength(i);
5629 0 : break;
5630 : }
5631 :
5632 0 : usage += originInfos[i]->mUsage;
5633 : }
5634 :
5635 0 : doomedOriginInfos.AppendElements(originInfos);
5636 : }
5637 : }
5638 :
5639 0 : for (uint32_t index = 0; index < doomedOriginInfos.Length(); index++) {
5640 0 : OriginInfo* doomedOriginInfo = doomedOriginInfos[index];
5641 :
5642 : #ifdef DEBUG
5643 : {
5644 0 : MutexAutoLock lock(mQuotaMutex);
5645 0 : MOZ_ASSERT(!doomedOriginInfo->LockedPersisted());
5646 : }
5647 : #endif
5648 :
5649 0 : DeleteFilesForOrigin(doomedOriginInfo->mGroupInfo->mPersistenceType,
5650 0 : doomedOriginInfo->mOrigin);
5651 : }
5652 :
5653 0 : nsTArray<OriginParams> doomedOrigins;
5654 : {
5655 0 : MutexAutoLock lock(mQuotaMutex);
5656 :
5657 0 : for (uint32_t index = 0; index < doomedOriginInfos.Length(); index++) {
5658 0 : OriginInfo* doomedOriginInfo = doomedOriginInfos[index];
5659 :
5660 : PersistenceType persistenceType =
5661 0 : doomedOriginInfo->mGroupInfo->mPersistenceType;
5662 0 : nsCString group = doomedOriginInfo->mGroupInfo->mGroup;
5663 0 : nsCString origin = doomedOriginInfo->mOrigin;
5664 0 : LockedRemoveQuotaForOrigin(persistenceType, group, origin);
5665 :
5666 : #ifdef DEBUG
5667 0 : doomedOriginInfos[index] = nullptr;
5668 : #endif
5669 :
5670 0 : doomedOrigins.AppendElement(OriginParams(persistenceType, origin));
5671 : }
5672 : }
5673 :
5674 0 : for (const OriginParams& doomedOrigin : doomedOrigins) {
5675 0 : OriginClearCompleted(doomedOrigin.mPersistenceType,
5676 0 : doomedOrigin.mOrigin);
5677 : }
5678 0 : }
5679 :
5680 : void
5681 0 : QuotaManager::DeleteFilesForOrigin(PersistenceType aPersistenceType,
5682 : const nsACString& aOrigin)
5683 : {
5684 0 : nsCOMPtr<nsIFile> directory;
5685 0 : nsresult rv = GetDirectoryForOrigin(aPersistenceType, aOrigin,
5686 0 : getter_AddRefs(directory));
5687 0 : NS_ENSURE_SUCCESS_VOID(rv);
5688 :
5689 0 : rv = directory->Remove(true);
5690 0 : if (rv != NS_ERROR_FILE_TARGET_DOES_NOT_EXIST &&
5691 0 : rv != NS_ERROR_FILE_NOT_FOUND && NS_FAILED(rv)) {
5692 : // This should never fail if we've closed all storage connections
5693 : // correctly...
5694 0 : NS_ERROR("Failed to remove directory!");
5695 : }
5696 : }
5697 :
5698 : void
5699 0 : QuotaManager::FinalizeOriginEviction(
5700 : nsTArray<RefPtr<DirectoryLockImpl>>& aLocks)
5701 : {
5702 0 : NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
5703 :
5704 : RefPtr<FinalizeOriginEvictionOp> op =
5705 0 : new FinalizeOriginEvictionOp(mOwningThread, aLocks);
5706 :
5707 0 : if (IsOnIOThread()) {
5708 0 : op->RunOnIOThreadImmediately();
5709 : } else {
5710 0 : op->Dispatch();
5711 : }
5712 0 : }
5713 :
5714 : void
5715 0 : QuotaManager::ShutdownTimerCallback(nsITimer* aTimer, void* aClosure)
5716 : {
5717 0 : AssertIsOnBackgroundThread();
5718 :
5719 0 : auto quotaManager = static_cast<QuotaManager*>(aClosure);
5720 0 : MOZ_ASSERT(quotaManager);
5721 :
5722 : NS_WARNING("Some storage operations are taking longer than expected "
5723 0 : "during shutdown and will be aborted!");
5724 :
5725 : // Abort all operations.
5726 0 : for (RefPtr<Client>& client : quotaManager->mClients) {
5727 0 : client->AbortOperations(NullCString());
5728 : }
5729 0 : }
5730 :
5731 : auto
5732 0 : QuotaManager::GetDirectoryLockTable(PersistenceType aPersistenceType)
5733 : -> DirectoryLockTable&
5734 : {
5735 0 : switch (aPersistenceType) {
5736 : case PERSISTENCE_TYPE_TEMPORARY:
5737 0 : return mTemporaryDirectoryLockTable;
5738 : case PERSISTENCE_TYPE_DEFAULT:
5739 0 : return mDefaultDirectoryLockTable;
5740 :
5741 : case PERSISTENCE_TYPE_PERSISTENT:
5742 : case PERSISTENCE_TYPE_INVALID:
5743 : default:
5744 0 : MOZ_CRASH("Bad persistence type value!");
5745 : }
5746 : }
5747 :
5748 : /*******************************************************************************
5749 : * Local class implementations
5750 : ******************************************************************************/
5751 :
5752 0 : OriginInfo::OriginInfo(GroupInfo* aGroupInfo, const nsACString& aOrigin,
5753 0 : uint64_t aUsage, int64_t aAccessTime, bool aPersisted)
5754 : : mGroupInfo(aGroupInfo), mOrigin(aOrigin), mUsage(aUsage),
5755 0 : mAccessTime(aAccessTime), mPersisted(aPersisted)
5756 : {
5757 0 : MOZ_ASSERT(aGroupInfo);
5758 0 : MOZ_ASSERT_IF(aPersisted,
5759 : aGroupInfo->mPersistenceType == PERSISTENCE_TYPE_DEFAULT);
5760 :
5761 0 : MOZ_COUNT_CTOR(OriginInfo);
5762 0 : }
5763 :
5764 : void
5765 0 : OriginInfo::LockedDecreaseUsage(int64_t aSize)
5766 : {
5767 0 : AssertCurrentThreadOwnsQuotaMutex();
5768 :
5769 0 : AssertNoUnderflow(mUsage, aSize);
5770 0 : mUsage -= aSize;
5771 :
5772 0 : if (!LockedPersisted()) {
5773 0 : AssertNoUnderflow(mGroupInfo->mUsage, aSize);
5774 0 : mGroupInfo->mUsage -= aSize;
5775 : }
5776 :
5777 0 : QuotaManager* quotaManager = QuotaManager::Get();
5778 0 : MOZ_ASSERT(quotaManager);
5779 :
5780 0 : AssertNoUnderflow(quotaManager->mTemporaryStorageUsage, aSize);
5781 0 : quotaManager->mTemporaryStorageUsage -= aSize;
5782 0 : }
5783 :
5784 : void
5785 0 : OriginInfo::LockedPersist()
5786 : {
5787 0 : AssertCurrentThreadOwnsQuotaMutex();
5788 0 : MOZ_ASSERT(mGroupInfo->mPersistenceType == PERSISTENCE_TYPE_DEFAULT);
5789 0 : MOZ_ASSERT(!mPersisted);
5790 :
5791 0 : mPersisted = true;
5792 :
5793 : // Remove Usage from GroupInfo
5794 0 : AssertNoUnderflow(mGroupInfo->mUsage, mUsage);
5795 0 : mGroupInfo->mUsage -= mUsage;
5796 0 : }
5797 :
5798 : already_AddRefed<OriginInfo>
5799 0 : GroupInfo::LockedGetOriginInfo(const nsACString& aOrigin)
5800 : {
5801 0 : AssertCurrentThreadOwnsQuotaMutex();
5802 :
5803 0 : for (RefPtr<OriginInfo>& originInfo : mOriginInfos) {
5804 0 : if (originInfo->mOrigin == aOrigin) {
5805 0 : RefPtr<OriginInfo> result = originInfo;
5806 0 : return result.forget();
5807 : }
5808 : }
5809 :
5810 0 : return nullptr;
5811 : }
5812 :
5813 : void
5814 0 : GroupInfo::LockedAddOriginInfo(OriginInfo* aOriginInfo)
5815 : {
5816 0 : AssertCurrentThreadOwnsQuotaMutex();
5817 :
5818 0 : NS_ASSERTION(!mOriginInfos.Contains(aOriginInfo),
5819 : "Replacing an existing entry!");
5820 0 : mOriginInfos.AppendElement(aOriginInfo);
5821 :
5822 0 : if (!aOriginInfo->LockedPersisted()) {
5823 0 : AssertNoOverflow(mUsage, aOriginInfo->mUsage);
5824 0 : mUsage += aOriginInfo->mUsage;
5825 : }
5826 :
5827 0 : QuotaManager* quotaManager = QuotaManager::Get();
5828 0 : MOZ_ASSERT(quotaManager);
5829 :
5830 0 : AssertNoOverflow(quotaManager->mTemporaryStorageUsage, aOriginInfo->mUsage);
5831 0 : quotaManager->mTemporaryStorageUsage += aOriginInfo->mUsage;
5832 0 : }
5833 :
5834 : void
5835 0 : GroupInfo::LockedRemoveOriginInfo(const nsACString& aOrigin)
5836 : {
5837 0 : AssertCurrentThreadOwnsQuotaMutex();
5838 :
5839 0 : for (uint32_t index = 0; index < mOriginInfos.Length(); index++) {
5840 0 : if (mOriginInfos[index]->mOrigin == aOrigin) {
5841 0 : if (!mOriginInfos[index]->LockedPersisted()) {
5842 0 : AssertNoUnderflow(mUsage, mOriginInfos[index]->mUsage);
5843 0 : mUsage -= mOriginInfos[index]->mUsage;
5844 : }
5845 :
5846 0 : QuotaManager* quotaManager = QuotaManager::Get();
5847 0 : MOZ_ASSERT(quotaManager);
5848 :
5849 0 : AssertNoUnderflow(quotaManager->mTemporaryStorageUsage,
5850 0 : mOriginInfos[index]->mUsage);
5851 0 : quotaManager->mTemporaryStorageUsage -= mOriginInfos[index]->mUsage;
5852 :
5853 0 : mOriginInfos.RemoveElementAt(index);
5854 :
5855 0 : return;
5856 : }
5857 : }
5858 : }
5859 :
5860 : void
5861 0 : GroupInfo::LockedRemoveOriginInfos()
5862 : {
5863 0 : AssertCurrentThreadOwnsQuotaMutex();
5864 :
5865 0 : QuotaManager* quotaManager = QuotaManager::Get();
5866 0 : MOZ_ASSERT(quotaManager);
5867 :
5868 0 : for (uint32_t index = mOriginInfos.Length(); index > 0; index--) {
5869 0 : OriginInfo* originInfo = mOriginInfos[index - 1];
5870 :
5871 0 : if (!originInfo->LockedPersisted()) {
5872 0 : AssertNoUnderflow(mUsage, originInfo->mUsage);
5873 0 : mUsage -= originInfo->mUsage;
5874 : }
5875 :
5876 0 : AssertNoUnderflow(quotaManager->mTemporaryStorageUsage, originInfo->mUsage);
5877 0 : quotaManager->mTemporaryStorageUsage -= originInfo->mUsage;
5878 :
5879 0 : mOriginInfos.RemoveElementAt(index - 1);
5880 : }
5881 0 : }
5882 :
5883 : RefPtr<GroupInfo>&
5884 0 : GroupInfoPair::GetGroupInfoForPersistenceType(PersistenceType aPersistenceType)
5885 : {
5886 0 : switch (aPersistenceType) {
5887 : case PERSISTENCE_TYPE_TEMPORARY:
5888 0 : return mTemporaryStorageGroupInfo;
5889 : case PERSISTENCE_TYPE_DEFAULT:
5890 0 : return mDefaultStorageGroupInfo;
5891 :
5892 : case PERSISTENCE_TYPE_PERSISTENT:
5893 : case PERSISTENCE_TYPE_INVALID:
5894 : default:
5895 0 : MOZ_CRASH("Bad persistence type value!");
5896 : }
5897 : }
5898 :
5899 0 : CollectOriginsHelper::CollectOriginsHelper(mozilla::Mutex& aMutex,
5900 0 : uint64_t aMinSizeToBeFreed)
5901 : : Runnable("dom::quota::CollectOriginsHelper")
5902 : , mMinSizeToBeFreed(aMinSizeToBeFreed)
5903 : , mMutex(aMutex)
5904 : , mCondVar(aMutex, "CollectOriginsHelper::mCondVar")
5905 : , mSizeToBeFreed(0)
5906 0 : , mWaiting(true)
5907 : {
5908 0 : MOZ_ASSERT(!NS_IsMainThread(), "Wrong thread!");
5909 0 : mMutex.AssertCurrentThreadOwns();
5910 0 : }
5911 :
5912 : int64_t
5913 0 : CollectOriginsHelper::BlockAndReturnOriginsForEviction(
5914 : nsTArray<RefPtr<DirectoryLockImpl>>& aLocks)
5915 : {
5916 0 : MOZ_ASSERT(!NS_IsMainThread(), "Wrong thread!");
5917 0 : mMutex.AssertCurrentThreadOwns();
5918 :
5919 0 : while (mWaiting) {
5920 0 : mCondVar.Wait();
5921 : }
5922 :
5923 0 : mLocks.SwapElements(aLocks);
5924 0 : return mSizeToBeFreed;
5925 : }
5926 :
5927 : NS_IMETHODIMP
5928 0 : CollectOriginsHelper::Run()
5929 : {
5930 0 : AssertIsOnBackgroundThread();
5931 :
5932 0 : QuotaManager* quotaManager = QuotaManager::Get();
5933 0 : NS_ASSERTION(quotaManager, "Shouldn't be null!");
5934 :
5935 : // We use extra stack vars here to avoid race detector warnings (the same
5936 : // memory accessed with and without the lock held).
5937 0 : nsTArray<RefPtr<DirectoryLockImpl>> locks;
5938 : uint64_t sizeToBeFreed =
5939 0 : quotaManager->CollectOriginsForEviction(mMinSizeToBeFreed, locks);
5940 :
5941 0 : MutexAutoLock lock(mMutex);
5942 :
5943 0 : NS_ASSERTION(mWaiting, "Huh?!");
5944 :
5945 0 : mLocks.SwapElements(locks);
5946 0 : mSizeToBeFreed = sizeToBeFreed;
5947 0 : mWaiting = false;
5948 0 : mCondVar.Notify();
5949 :
5950 0 : return NS_OK;
5951 : }
5952 :
5953 : /*******************************************************************************
5954 : * OriginOperationBase
5955 : ******************************************************************************/
5956 :
5957 : NS_IMETHODIMP
5958 0 : OriginOperationBase::Run()
5959 : {
5960 : nsresult rv;
5961 :
5962 0 : switch (mState) {
5963 : case State_Initial: {
5964 0 : rv = Init();
5965 0 : break;
5966 : }
5967 :
5968 : case State_Initializing: {
5969 0 : rv = InitOnMainThread();
5970 0 : break;
5971 : }
5972 :
5973 : case State_FinishingInit: {
5974 0 : rv = FinishInit();
5975 0 : break;
5976 : }
5977 :
5978 : case State_CreatingQuotaManager: {
5979 0 : rv = QuotaManagerOpen();
5980 0 : break;
5981 : }
5982 :
5983 : case State_DirectoryOpenPending: {
5984 0 : rv = DirectoryOpen();
5985 0 : break;
5986 : }
5987 :
5988 : case State_DirectoryWorkOpen: {
5989 0 : rv = DirectoryWork();
5990 0 : break;
5991 : }
5992 :
5993 : case State_UnblockingOpen: {
5994 0 : UnblockOpen();
5995 0 : return NS_OK;
5996 : }
5997 :
5998 : default:
5999 0 : MOZ_CRASH("Bad state!");
6000 : }
6001 :
6002 0 : if (NS_WARN_IF(NS_FAILED(rv)) && mState != State_UnblockingOpen) {
6003 0 : Finish(rv);
6004 : }
6005 :
6006 0 : return NS_OK;
6007 : }
6008 :
6009 : nsresult
6010 0 : OriginOperationBase::DirectoryOpen()
6011 : {
6012 0 : AssertIsOnOwningThread();
6013 0 : MOZ_ASSERT(mState == State_DirectoryOpenPending);
6014 :
6015 0 : QuotaManager* quotaManager = QuotaManager::Get();
6016 0 : if (NS_WARN_IF(!quotaManager)) {
6017 0 : return NS_ERROR_FAILURE;
6018 : }
6019 :
6020 : // Must set this before dispatching otherwise we will race with the IO thread.
6021 0 : AdvanceState();
6022 :
6023 0 : nsresult rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL);
6024 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
6025 0 : return NS_ERROR_FAILURE;
6026 : }
6027 :
6028 0 : return NS_OK;
6029 : }
6030 :
6031 : void
6032 0 : OriginOperationBase::Finish(nsresult aResult)
6033 : {
6034 0 : if (NS_SUCCEEDED(mResultCode)) {
6035 0 : mResultCode = aResult;
6036 : }
6037 :
6038 : // Must set mState before dispatching otherwise we will race with the main
6039 : // thread.
6040 0 : mState = State_UnblockingOpen;
6041 :
6042 0 : MOZ_ALWAYS_SUCCEEDS(mOwningThread->Dispatch(this, NS_DISPATCH_NORMAL));
6043 0 : }
6044 :
6045 : nsresult
6046 0 : OriginOperationBase::Init()
6047 : {
6048 0 : AssertIsOnOwningThread();
6049 0 : MOZ_ASSERT(mState == State_Initial);
6050 :
6051 0 : AdvanceState();
6052 :
6053 0 : if (mNeedsMainThreadInit) {
6054 0 : MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(this));
6055 : } else {
6056 0 : AdvanceState();
6057 0 : MOZ_ALWAYS_SUCCEEDS(Run());
6058 : }
6059 :
6060 0 : return NS_OK;
6061 : }
6062 :
6063 : nsresult
6064 0 : OriginOperationBase::InitOnMainThread()
6065 : {
6066 0 : MOZ_ASSERT(NS_IsMainThread());
6067 0 : MOZ_ASSERT(mState == State_Initializing);
6068 :
6069 0 : nsresult rv = DoInitOnMainThread();
6070 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
6071 0 : return rv;
6072 : }
6073 :
6074 0 : AdvanceState();
6075 :
6076 0 : MOZ_ALWAYS_SUCCEEDS(mOwningThread->Dispatch(this, NS_DISPATCH_NORMAL));
6077 :
6078 0 : return NS_OK;
6079 : }
6080 :
6081 : nsresult
6082 0 : OriginOperationBase::FinishInit()
6083 : {
6084 0 : AssertIsOnOwningThread();
6085 0 : MOZ_ASSERT(mState == State_FinishingInit);
6086 :
6087 0 : if (QuotaManager::IsShuttingDown()) {
6088 0 : return NS_ERROR_FAILURE;
6089 : }
6090 :
6091 0 : AdvanceState();
6092 :
6093 0 : if (mNeedsQuotaManagerInit && !QuotaManager::Get()) {
6094 0 : QuotaManager::GetOrCreate(this);
6095 : } else {
6096 0 : Open();
6097 : }
6098 :
6099 0 : return NS_OK;
6100 : }
6101 :
6102 : nsresult
6103 0 : OriginOperationBase::QuotaManagerOpen()
6104 : {
6105 0 : AssertIsOnOwningThread();
6106 0 : MOZ_ASSERT(mState == State_CreatingQuotaManager);
6107 :
6108 0 : if (NS_WARN_IF(!QuotaManager::Get())) {
6109 0 : return NS_ERROR_FAILURE;
6110 : }
6111 :
6112 0 : Open();
6113 :
6114 0 : return NS_OK;
6115 : }
6116 :
6117 : nsresult
6118 0 : OriginOperationBase::DirectoryWork()
6119 : {
6120 0 : AssertIsOnIOThread();
6121 0 : MOZ_ASSERT(mState == State_DirectoryWorkOpen);
6122 :
6123 0 : QuotaManager* quotaManager = QuotaManager::Get();
6124 0 : if (NS_WARN_IF(!quotaManager)) {
6125 0 : return NS_ERROR_FAILURE;
6126 : }
6127 :
6128 : nsresult rv;
6129 :
6130 0 : if (mNeedsQuotaManagerInit) {
6131 0 : rv = quotaManager->EnsureStorageIsInitialized();
6132 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
6133 0 : return rv;
6134 : }
6135 : }
6136 :
6137 0 : rv = DoDirectoryWork(quotaManager);
6138 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
6139 0 : return rv;
6140 : }
6141 :
6142 : // Must set mState before dispatching otherwise we will race with the owning
6143 : // thread.
6144 0 : AdvanceState();
6145 :
6146 0 : MOZ_ALWAYS_SUCCEEDS(mOwningThread->Dispatch(this, NS_DISPATCH_NORMAL));
6147 :
6148 0 : return NS_OK;
6149 : }
6150 :
6151 : void
6152 0 : FinalizeOriginEvictionOp::Dispatch()
6153 : {
6154 0 : MOZ_ASSERT(!NS_IsMainThread());
6155 0 : MOZ_ASSERT(GetState() == State_Initial);
6156 :
6157 0 : SetState(State_DirectoryOpenPending);
6158 :
6159 0 : MOZ_ALWAYS_SUCCEEDS(mOwningThread->Dispatch(this, NS_DISPATCH_NORMAL));
6160 0 : }
6161 :
6162 : void
6163 0 : FinalizeOriginEvictionOp::RunOnIOThreadImmediately()
6164 : {
6165 0 : AssertIsOnIOThread();
6166 0 : MOZ_ASSERT(GetState() == State_Initial);
6167 :
6168 0 : SetState(State_DirectoryWorkOpen);
6169 :
6170 0 : MOZ_ALWAYS_SUCCEEDS(this->Run());
6171 0 : }
6172 :
6173 : void
6174 0 : FinalizeOriginEvictionOp::Open()
6175 : {
6176 0 : MOZ_CRASH("Shouldn't get here!");
6177 : }
6178 :
6179 : nsresult
6180 0 : FinalizeOriginEvictionOp::DoDirectoryWork(QuotaManager* aQuotaManager)
6181 : {
6182 0 : AssertIsOnIOThread();
6183 :
6184 0 : AUTO_PROFILER_LABEL("FinalizeOriginEvictionOp::DoDirectoryWork", OTHER);
6185 :
6186 0 : for (RefPtr<DirectoryLockImpl>& lock : mLocks) {
6187 0 : aQuotaManager->OriginClearCompleted(lock->GetPersistenceType().Value(),
6188 0 : lock->GetOriginScope().GetOrigin());
6189 : }
6190 :
6191 0 : return NS_OK;
6192 : }
6193 :
6194 : void
6195 0 : FinalizeOriginEvictionOp::UnblockOpen()
6196 : {
6197 0 : AssertIsOnOwningThread();
6198 0 : MOZ_ASSERT(GetState() == State_UnblockingOpen);
6199 :
6200 : #ifdef DEBUG
6201 0 : NoteActorDestroyed();
6202 : #endif
6203 :
6204 0 : mLocks.Clear();
6205 :
6206 0 : AdvanceState();
6207 0 : }
6208 :
6209 0 : NS_IMPL_ISUPPORTS_INHERITED0(NormalOriginOperationBase, Runnable)
6210 :
6211 : void
6212 0 : NormalOriginOperationBase::Open()
6213 : {
6214 0 : AssertIsOnOwningThread();
6215 0 : MOZ_ASSERT(GetState() == State_CreatingQuotaManager);
6216 0 : MOZ_ASSERT(QuotaManager::Get());
6217 :
6218 0 : AdvanceState();
6219 :
6220 0 : QuotaManager::Get()->OpenDirectoryInternal(mPersistenceType,
6221 : mOriginScope,
6222 0 : Nullable<Client::Type>(),
6223 0 : mExclusive,
6224 0 : this);
6225 0 : }
6226 :
6227 : void
6228 0 : NormalOriginOperationBase::UnblockOpen()
6229 : {
6230 0 : AssertIsOnOwningThread();
6231 0 : MOZ_ASSERT(GetState() == State_UnblockingOpen);
6232 :
6233 0 : SendResults();
6234 :
6235 0 : mDirectoryLock = nullptr;
6236 :
6237 0 : AdvanceState();
6238 0 : }
6239 :
6240 : void
6241 0 : NormalOriginOperationBase::DirectoryLockAcquired(DirectoryLock* aLock)
6242 : {
6243 0 : AssertIsOnOwningThread();
6244 0 : MOZ_ASSERT(aLock);
6245 0 : MOZ_ASSERT(GetState() == State_DirectoryOpenPending);
6246 0 : MOZ_ASSERT(!mDirectoryLock);
6247 :
6248 0 : mDirectoryLock = aLock;
6249 :
6250 0 : nsresult rv = DirectoryOpen();
6251 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
6252 0 : Finish(rv);
6253 0 : return;
6254 : }
6255 : }
6256 :
6257 : void
6258 0 : NormalOriginOperationBase::DirectoryLockFailed()
6259 : {
6260 0 : AssertIsOnOwningThread();
6261 0 : MOZ_ASSERT(GetState() == State_DirectoryOpenPending);
6262 0 : MOZ_ASSERT(!mDirectoryLock);
6263 :
6264 0 : Finish(NS_ERROR_FAILURE);
6265 0 : }
6266 :
6267 : nsresult
6268 0 : SaveOriginAccessTimeOp::DoDirectoryWork(QuotaManager* aQuotaManager)
6269 : {
6270 0 : AssertIsOnIOThread();
6271 0 : MOZ_ASSERT(!mPersistenceType.IsNull());
6272 0 : MOZ_ASSERT(mOriginScope.IsOrigin());
6273 :
6274 0 : AUTO_PROFILER_LABEL("SaveOriginAccessTimeOp::DoDirectoryWork", OTHER);
6275 :
6276 0 : nsCOMPtr<nsIFile> file;
6277 : nsresult rv =
6278 0 : aQuotaManager->GetDirectoryForOrigin(mPersistenceType.Value(),
6279 : mOriginScope.GetOrigin(),
6280 0 : getter_AddRefs(file));
6281 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
6282 0 : return rv;
6283 : }
6284 :
6285 0 : rv = file->Append(NS_LITERAL_STRING(METADATA_V2_FILE_NAME));
6286 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
6287 0 : return rv;
6288 : }
6289 :
6290 0 : nsCOMPtr<nsIBinaryOutputStream> stream;
6291 0 : rv = GetBinaryOutputStream(file, kUpdateFileFlag, getter_AddRefs(stream));
6292 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
6293 0 : return rv;
6294 : }
6295 :
6296 : // The origin directory may not exist anymore.
6297 0 : if (stream) {
6298 0 : rv = stream->Write64(mTimestamp);
6299 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
6300 0 : return rv;
6301 : }
6302 : }
6303 :
6304 0 : return NS_OK;
6305 : }
6306 :
6307 : void
6308 0 : SaveOriginAccessTimeOp::SendResults()
6309 : {
6310 : #ifdef DEBUG
6311 0 : NoteActorDestroyed();
6312 : #endif
6313 0 : }
6314 :
6315 : /*******************************************************************************
6316 : * Quota
6317 : ******************************************************************************/
6318 :
6319 0 : Quota::Quota()
6320 : #ifdef DEBUG
6321 0 : : mActorDestroyed(false)
6322 : #endif
6323 : {
6324 0 : }
6325 :
6326 0 : Quota::~Quota()
6327 : {
6328 0 : MOZ_ASSERT(mActorDestroyed);
6329 0 : }
6330 :
6331 : void
6332 0 : Quota::StartIdleMaintenance()
6333 : {
6334 0 : AssertIsOnBackgroundThread();
6335 0 : MOZ_ASSERT(!QuotaManager::IsShuttingDown());
6336 :
6337 0 : QuotaManager* quotaManager = QuotaManager::Get();
6338 0 : if (NS_WARN_IF(!quotaManager)) {
6339 0 : return;
6340 : }
6341 :
6342 0 : quotaManager->StartIdleMaintenance();
6343 : }
6344 :
6345 : void
6346 0 : Quota::ActorDestroy(ActorDestroyReason aWhy)
6347 : {
6348 0 : AssertIsOnBackgroundThread();
6349 : #ifdef DEBUG
6350 0 : MOZ_ASSERT(!mActorDestroyed);
6351 0 : mActorDestroyed = true;
6352 : #endif
6353 0 : }
6354 :
6355 : PQuotaUsageRequestParent*
6356 0 : Quota::AllocPQuotaUsageRequestParent(const UsageRequestParams& aParams)
6357 : {
6358 0 : AssertIsOnBackgroundThread();
6359 0 : MOZ_ASSERT(aParams.type() != UsageRequestParams::T__None);
6360 :
6361 0 : RefPtr<QuotaUsageRequestBase> actor;
6362 :
6363 0 : switch (aParams.type()) {
6364 : case UsageRequestParams::TAllUsageParams:
6365 0 : actor = new GetUsageOp(aParams);
6366 0 : break;
6367 :
6368 : case UsageRequestParams::TOriginUsageParams:
6369 0 : actor = new GetOriginUsageOp(aParams);
6370 0 : break;
6371 :
6372 : default:
6373 0 : MOZ_CRASH("Should never get here!");
6374 : }
6375 :
6376 0 : MOZ_ASSERT(actor);
6377 :
6378 : // Transfer ownership to IPDL.
6379 0 : return actor.forget().take();
6380 : }
6381 :
6382 : mozilla::ipc::IPCResult
6383 0 : Quota::RecvPQuotaUsageRequestConstructor(PQuotaUsageRequestParent* aActor,
6384 : const UsageRequestParams& aParams)
6385 : {
6386 0 : AssertIsOnBackgroundThread();
6387 0 : MOZ_ASSERT(aActor);
6388 0 : MOZ_ASSERT(aParams.type() != UsageRequestParams::T__None);
6389 :
6390 0 : auto* op = static_cast<QuotaUsageRequestBase*>(aActor);
6391 :
6392 0 : if (NS_WARN_IF(!op->Init(this))) {
6393 0 : return IPC_FAIL_NO_REASON(this);
6394 : }
6395 :
6396 0 : op->RunImmediately();
6397 0 : return IPC_OK();
6398 : }
6399 :
6400 : bool
6401 0 : Quota::DeallocPQuotaUsageRequestParent(PQuotaUsageRequestParent* aActor)
6402 : {
6403 0 : AssertIsOnBackgroundThread();
6404 0 : MOZ_ASSERT(aActor);
6405 :
6406 : // Transfer ownership back from IPDL.
6407 : RefPtr<QuotaUsageRequestBase> actor =
6408 0 : dont_AddRef(static_cast<QuotaUsageRequestBase*>(aActor));
6409 0 : return true;
6410 : }
6411 :
6412 : PQuotaRequestParent*
6413 0 : Quota::AllocPQuotaRequestParent(const RequestParams& aParams)
6414 : {
6415 0 : AssertIsOnBackgroundThread();
6416 0 : MOZ_ASSERT(aParams.type() != RequestParams::T__None);
6417 :
6418 0 : if (aParams.type() == RequestParams::TClearDataParams) {
6419 0 : PBackgroundParent* actor = Manager();
6420 0 : MOZ_ASSERT(actor);
6421 :
6422 0 : if (BackgroundParent::IsOtherProcessActor(actor)) {
6423 0 : ASSERT_UNLESS_FUZZING();
6424 : return nullptr;
6425 : }
6426 : }
6427 :
6428 0 : RefPtr<QuotaRequestBase> actor;
6429 :
6430 0 : switch (aParams.type()) {
6431 : case RequestParams::TInitParams:
6432 0 : actor = new InitOp();
6433 0 : break;
6434 :
6435 : case RequestParams::TInitOriginParams:
6436 0 : actor = new InitOriginOp(aParams);
6437 0 : break;
6438 :
6439 : case RequestParams::TClearOriginParams:
6440 0 : actor = new ClearOriginOp(aParams);
6441 0 : break;
6442 :
6443 : case RequestParams::TClearDataParams:
6444 0 : actor = new ClearDataOp(aParams);
6445 0 : break;
6446 :
6447 : case RequestParams::TClearAllParams:
6448 0 : actor = new ResetOrClearOp(/* aClear */ true);
6449 0 : break;
6450 :
6451 : case RequestParams::TResetAllParams:
6452 0 : actor = new ResetOrClearOp(/* aClear */ false);
6453 0 : break;
6454 :
6455 : case RequestParams::TPersistedParams:
6456 0 : actor = new PersistedOp(aParams);
6457 0 : break;
6458 :
6459 : case RequestParams::TPersistParams:
6460 0 : actor = new PersistOp(aParams);
6461 0 : break;
6462 :
6463 : default:
6464 0 : MOZ_CRASH("Should never get here!");
6465 : }
6466 :
6467 0 : MOZ_ASSERT(actor);
6468 :
6469 : // Transfer ownership to IPDL.
6470 0 : return actor.forget().take();
6471 : }
6472 :
6473 : mozilla::ipc::IPCResult
6474 0 : Quota::RecvPQuotaRequestConstructor(PQuotaRequestParent* aActor,
6475 : const RequestParams& aParams)
6476 : {
6477 0 : AssertIsOnBackgroundThread();
6478 0 : MOZ_ASSERT(aActor);
6479 0 : MOZ_ASSERT(aParams.type() != RequestParams::T__None);
6480 :
6481 0 : auto* op = static_cast<QuotaRequestBase*>(aActor);
6482 :
6483 0 : if (NS_WARN_IF(!op->Init(this))) {
6484 0 : return IPC_FAIL_NO_REASON(this);
6485 : }
6486 :
6487 0 : op->RunImmediately();
6488 0 : return IPC_OK();
6489 : }
6490 :
6491 : bool
6492 0 : Quota::DeallocPQuotaRequestParent(PQuotaRequestParent* aActor)
6493 : {
6494 0 : AssertIsOnBackgroundThread();
6495 0 : MOZ_ASSERT(aActor);
6496 :
6497 : // Transfer ownership back from IPDL.
6498 : RefPtr<QuotaRequestBase> actor =
6499 0 : dont_AddRef(static_cast<QuotaRequestBase*>(aActor));
6500 0 : return true;
6501 : }
6502 :
6503 : mozilla::ipc::IPCResult
6504 0 : Quota::RecvStartIdleMaintenance()
6505 : {
6506 0 : AssertIsOnBackgroundThread();
6507 :
6508 0 : PBackgroundParent* actor = Manager();
6509 0 : MOZ_ASSERT(actor);
6510 :
6511 0 : if (BackgroundParent::IsOtherProcessActor(actor)) {
6512 0 : ASSERT_UNLESS_FUZZING();
6513 : return IPC_FAIL_NO_REASON(this);
6514 : }
6515 :
6516 0 : if (QuotaManager::IsShuttingDown()) {
6517 0 : return IPC_OK();
6518 : }
6519 :
6520 0 : QuotaManager* quotaManager = QuotaManager::Get();
6521 0 : if (!quotaManager) {
6522 : nsCOMPtr<nsIRunnable> callback =
6523 0 : NewRunnableMethod("dom::quota::Quota::StartIdleMaintenance",
6524 : this,
6525 0 : &Quota::StartIdleMaintenance);
6526 :
6527 0 : QuotaManager::GetOrCreate(callback);
6528 0 : return IPC_OK();
6529 : }
6530 :
6531 0 : quotaManager->StartIdleMaintenance();
6532 :
6533 0 : return IPC_OK();
6534 : }
6535 :
6536 : mozilla::ipc::IPCResult
6537 0 : Quota::RecvStopIdleMaintenance()
6538 : {
6539 0 : AssertIsOnBackgroundThread();
6540 :
6541 0 : PBackgroundParent* actor = Manager();
6542 0 : MOZ_ASSERT(actor);
6543 :
6544 0 : if (BackgroundParent::IsOtherProcessActor(actor)) {
6545 0 : ASSERT_UNLESS_FUZZING();
6546 : return IPC_FAIL_NO_REASON(this);
6547 : }
6548 :
6549 0 : if (QuotaManager::IsShuttingDown()) {
6550 0 : return IPC_OK();
6551 : }
6552 :
6553 0 : QuotaManager* quotaManager = QuotaManager::Get();
6554 0 : if (!quotaManager) {
6555 0 : return IPC_OK();
6556 : }
6557 :
6558 0 : quotaManager->StopIdleMaintenance();
6559 :
6560 0 : return IPC_OK();
6561 : }
6562 :
6563 : bool
6564 0 : QuotaUsageRequestBase::Init(Quota* aQuota)
6565 : {
6566 0 : AssertIsOnOwningThread();
6567 0 : MOZ_ASSERT(aQuota);
6568 :
6569 0 : mNeedsQuotaManagerInit = true;
6570 :
6571 0 : return true;
6572 : }
6573 :
6574 : nsresult
6575 0 : QuotaUsageRequestBase::GetUsageForOrigin(QuotaManager* aQuotaManager,
6576 : PersistenceType aPersistenceType,
6577 : const nsACString& aGroup,
6578 : const nsACString& aOrigin,
6579 : UsageInfo* aUsageInfo)
6580 : {
6581 0 : AssertIsOnIOThread();
6582 0 : MOZ_ASSERT(aQuotaManager);
6583 0 : MOZ_ASSERT(aUsageInfo);
6584 0 : MOZ_ASSERT(aUsageInfo->TotalUsage() == 0);
6585 :
6586 0 : nsCOMPtr<nsIFile> directory;
6587 0 : nsresult rv = aQuotaManager->GetDirectoryForOrigin(aPersistenceType,
6588 : aOrigin,
6589 0 : getter_AddRefs(directory));
6590 0 : NS_ENSURE_SUCCESS(rv, rv);
6591 :
6592 : bool exists;
6593 0 : rv = directory->Exists(&exists);
6594 0 : NS_ENSURE_SUCCESS(rv, rv);
6595 :
6596 : // If the directory exists then enumerate all the files inside, adding up
6597 : // the sizes to get the final usage statistic.
6598 0 : if (exists && !mCanceled) {
6599 : bool initialized;
6600 :
6601 0 : if (aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) {
6602 0 : initialized = aQuotaManager->IsOriginInitialized(aOrigin);
6603 : } else {
6604 0 : initialized = aQuotaManager->IsTemporaryStorageInitialized();
6605 : }
6606 :
6607 0 : nsCOMPtr<nsISimpleEnumerator> entries;
6608 0 : rv = directory->GetDirectoryEntries(getter_AddRefs(entries));
6609 0 : NS_ENSURE_SUCCESS(rv, rv);
6610 :
6611 : bool hasMore;
6612 0 : while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) &&
6613 0 : hasMore && !mCanceled) {
6614 0 : nsCOMPtr<nsISupports> entry;
6615 0 : rv = entries->GetNext(getter_AddRefs(entry));
6616 0 : NS_ENSURE_SUCCESS(rv, rv);
6617 :
6618 0 : nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
6619 0 : NS_ENSURE_TRUE(file, NS_NOINTERFACE);
6620 :
6621 : bool isDirectory;
6622 0 : rv = file->IsDirectory(&isDirectory);
6623 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
6624 0 : return rv;
6625 : }
6626 :
6627 0 : nsString leafName;
6628 0 : rv = file->GetLeafName(leafName);
6629 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
6630 0 : return rv;
6631 : }
6632 :
6633 0 : if (!isDirectory) {
6634 : // We are maintaining existing behavior here (failing if the origin is
6635 : // not yet initialized or just continuing otherwise).
6636 : // This can possibly be used by developers to add temporary backups into
6637 : // origin directories without losing get usage functionality.
6638 0 : if (IsOriginMetadata(leafName)) {
6639 0 : continue;
6640 : }
6641 :
6642 0 : if (IsTempMetadata(leafName)) {
6643 0 : if (!initialized) {
6644 0 : rv = file->Remove(/* recursive */ false);
6645 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
6646 0 : return rv;
6647 : }
6648 : }
6649 :
6650 0 : continue;
6651 : }
6652 :
6653 0 : UNKNOWN_FILE_WARNING(leafName);
6654 0 : if (!initialized) {
6655 0 : return NS_ERROR_UNEXPECTED;
6656 : }
6657 0 : continue;
6658 : }
6659 :
6660 : Client::Type clientType;
6661 0 : rv = Client::TypeFromText(leafName, clientType);
6662 0 : if (NS_FAILED(rv)) {
6663 0 : UNKNOWN_FILE_WARNING(leafName);
6664 0 : if (!initialized) {
6665 0 : return NS_ERROR_UNEXPECTED;
6666 : }
6667 0 : continue;
6668 : }
6669 :
6670 0 : Client* client = aQuotaManager->GetClient(clientType);
6671 0 : MOZ_ASSERT(client);
6672 :
6673 0 : if (initialized) {
6674 0 : rv = client->GetUsageForOrigin(aPersistenceType,
6675 : aGroup,
6676 : aOrigin,
6677 : mCanceled,
6678 0 : aUsageInfo);
6679 : }
6680 : else {
6681 0 : rv = client->InitOrigin(aPersistenceType,
6682 : aGroup,
6683 : aOrigin,
6684 : mCanceled,
6685 0 : aUsageInfo);
6686 : }
6687 0 : NS_ENSURE_SUCCESS(rv, rv);
6688 : }
6689 : }
6690 :
6691 0 : return NS_OK;
6692 : }
6693 :
6694 : void
6695 0 : QuotaUsageRequestBase::SendResults()
6696 : {
6697 0 : AssertIsOnOwningThread();
6698 :
6699 0 : if (IsActorDestroyed()) {
6700 0 : if (NS_SUCCEEDED(mResultCode)) {
6701 0 : mResultCode = NS_ERROR_FAILURE;
6702 : }
6703 : } else {
6704 0 : if (mCanceled) {
6705 0 : mResultCode = NS_ERROR_FAILURE;
6706 : }
6707 :
6708 0 : UsageRequestResponse response;
6709 :
6710 0 : if (NS_SUCCEEDED(mResultCode)) {
6711 0 : GetResponse(response);
6712 : } else {
6713 0 : response = mResultCode;
6714 : }
6715 :
6716 0 : Unused << PQuotaUsageRequestParent::Send__delete__(this, response);
6717 : }
6718 0 : }
6719 :
6720 : void
6721 0 : QuotaUsageRequestBase::ActorDestroy(ActorDestroyReason aWhy)
6722 : {
6723 0 : AssertIsOnOwningThread();
6724 :
6725 0 : NoteActorDestroyed();
6726 0 : }
6727 :
6728 : mozilla::ipc::IPCResult
6729 0 : QuotaUsageRequestBase::RecvCancel()
6730 : {
6731 0 : AssertIsOnOwningThread();
6732 :
6733 0 : if (mCanceled.exchange(true)) {
6734 0 : NS_WARNING("Canceled more than once?!");
6735 0 : return IPC_FAIL_NO_REASON(this);
6736 : }
6737 :
6738 0 : return IPC_OK();
6739 : }
6740 :
6741 0 : GetUsageOp::GetUsageOp(const UsageRequestParams& aParams)
6742 0 : : mGetAll(aParams.get_AllUsageParams().getAll())
6743 : {
6744 0 : AssertIsOnOwningThread();
6745 0 : MOZ_ASSERT(aParams.type() == UsageRequestParams::TAllUsageParams);
6746 0 : }
6747 :
6748 : nsresult
6749 0 : GetUsageOp::TraverseRepository(QuotaManager* aQuotaManager,
6750 : PersistenceType aPersistenceType)
6751 : {
6752 0 : AssertIsOnIOThread();
6753 0 : MOZ_ASSERT(aQuotaManager);
6754 :
6755 : nsresult rv;
6756 :
6757 : nsCOMPtr<nsIFile> directory =
6758 0 : do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
6759 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
6760 0 : return rv;
6761 : }
6762 :
6763 0 : rv = directory->InitWithPath(aQuotaManager->GetStoragePath(aPersistenceType));
6764 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
6765 0 : return rv;
6766 : }
6767 :
6768 : bool exists;
6769 0 : rv = directory->Exists(&exists);
6770 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
6771 0 : return rv;
6772 : }
6773 :
6774 0 : if (!exists) {
6775 0 : return NS_OK;
6776 : }
6777 :
6778 0 : nsCOMPtr<nsISimpleEnumerator> entries;
6779 0 : rv = directory->GetDirectoryEntries(getter_AddRefs(entries));
6780 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
6781 0 : return rv;
6782 : }
6783 :
6784 0 : bool persistent = aPersistenceType == PERSISTENCE_TYPE_PERSISTENT;
6785 :
6786 : bool hasMore;
6787 0 : while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) &&
6788 0 : hasMore && !mCanceled) {
6789 0 : nsCOMPtr<nsISupports> entry;
6790 0 : rv = entries->GetNext(getter_AddRefs(entry));
6791 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
6792 0 : return rv;
6793 : }
6794 :
6795 0 : nsCOMPtr<nsIFile> originDir = do_QueryInterface(entry);
6796 0 : MOZ_ASSERT(originDir);
6797 :
6798 : bool isDirectory;
6799 0 : rv = originDir->IsDirectory(&isDirectory);
6800 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
6801 0 : return rv;
6802 : }
6803 :
6804 0 : if (!isDirectory) {
6805 0 : nsString leafName;
6806 0 : rv = originDir->GetLeafName(leafName);
6807 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
6808 0 : return rv;
6809 : }
6810 :
6811 0 : if (!IsOSMetadata(leafName)) {
6812 0 : UNKNOWN_FILE_WARNING(leafName);
6813 : }
6814 0 : continue;
6815 : }
6816 :
6817 : int64_t timestamp;
6818 : bool persisted;
6819 0 : nsCString suffix;
6820 0 : nsCString group;
6821 0 : nsCString origin;
6822 0 : rv = aQuotaManager->GetDirectoryMetadata2WithRestore(originDir,
6823 : persistent,
6824 : ×tamp,
6825 : &persisted,
6826 : suffix,
6827 : group,
6828 : origin);
6829 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
6830 0 : return rv;
6831 : }
6832 :
6833 0 : if (!mGetAll && aQuotaManager->IsOriginInternal(origin)) {
6834 0 : continue;
6835 : }
6836 :
6837 : OriginUsage* originUsage;
6838 :
6839 : // We can't store pointers to OriginUsage objects in the hashtable
6840 : // since AppendElement() reallocates its internal array buffer as number
6841 : // of elements grows.
6842 : uint32_t index;
6843 0 : if (mOriginUsagesIndex.Get(origin, &index)) {
6844 0 : originUsage = &mOriginUsages[index];
6845 : } else {
6846 0 : index = mOriginUsages.Length();
6847 :
6848 0 : originUsage = mOriginUsages.AppendElement();
6849 :
6850 0 : originUsage->origin() = origin;
6851 0 : originUsage->persisted() = false;
6852 0 : originUsage->usage() = 0;
6853 :
6854 0 : mOriginUsagesIndex.Put(origin, index);
6855 : }
6856 :
6857 0 : if (aPersistenceType == PERSISTENCE_TYPE_DEFAULT) {
6858 0 : originUsage->persisted() = persisted;
6859 : }
6860 :
6861 0 : UsageInfo usageInfo;
6862 0 : rv = GetUsageForOrigin(aQuotaManager,
6863 : aPersistenceType,
6864 : group,
6865 : origin,
6866 : &usageInfo);
6867 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
6868 0 : return rv;
6869 : }
6870 :
6871 0 : originUsage->usage() = originUsage->usage() + usageInfo.TotalUsage();
6872 : }
6873 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
6874 0 : return rv;
6875 : }
6876 :
6877 0 : return NS_OK;
6878 : }
6879 :
6880 : nsresult
6881 0 : GetUsageOp::DoDirectoryWork(QuotaManager* aQuotaManager)
6882 : {
6883 0 : AssertIsOnIOThread();
6884 :
6885 0 : AUTO_PROFILER_LABEL("GetUsageOp::DoDirectoryWork", OTHER);
6886 :
6887 : nsresult rv;
6888 :
6889 0 : for (const PersistenceType type : kAllPersistenceTypes) {
6890 0 : rv = TraverseRepository(aQuotaManager, type);
6891 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
6892 0 : return rv;
6893 : }
6894 : }
6895 :
6896 0 : return NS_OK;
6897 : }
6898 :
6899 : void
6900 0 : GetUsageOp::GetResponse(UsageRequestResponse& aResponse)
6901 : {
6902 0 : AssertIsOnOwningThread();
6903 :
6904 0 : aResponse = AllUsageResponse();
6905 :
6906 0 : if (!mOriginUsages.IsEmpty()) {
6907 : nsTArray<OriginUsage>& originUsages =
6908 0 : aResponse.get_AllUsageResponse().originUsages();
6909 :
6910 0 : mOriginUsages.SwapElements(originUsages);
6911 : }
6912 0 : }
6913 :
6914 0 : GetOriginUsageOp::GetOriginUsageOp(const UsageRequestParams& aParams)
6915 0 : : mParams(aParams.get_OriginUsageParams())
6916 0 : , mGetGroupUsage(aParams.get_OriginUsageParams().getGroupUsage())
6917 : {
6918 0 : AssertIsOnOwningThread();
6919 0 : MOZ_ASSERT(aParams.type() == UsageRequestParams::TOriginUsageParams);
6920 0 : }
6921 :
6922 : bool
6923 0 : GetOriginUsageOp::Init(Quota* aQuota)
6924 : {
6925 0 : AssertIsOnOwningThread();
6926 0 : MOZ_ASSERT(aQuota);
6927 :
6928 0 : if (NS_WARN_IF(!QuotaUsageRequestBase::Init(aQuota))) {
6929 0 : return false;
6930 : }
6931 :
6932 0 : mNeedsMainThreadInit = true;
6933 :
6934 0 : return true;
6935 : }
6936 :
6937 : nsresult
6938 0 : GetOriginUsageOp::DoInitOnMainThread()
6939 : {
6940 0 : MOZ_ASSERT(NS_IsMainThread());
6941 0 : MOZ_ASSERT(GetState() == State_Initializing);
6942 0 : MOZ_ASSERT(mNeedsMainThreadInit);
6943 :
6944 0 : const PrincipalInfo& principalInfo = mParams.principalInfo();
6945 :
6946 : nsresult rv;
6947 : nsCOMPtr<nsIPrincipal> principal =
6948 0 : PrincipalInfoToPrincipal(principalInfo, &rv);
6949 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
6950 0 : return rv;
6951 : }
6952 :
6953 : // Figure out which origin we're dealing with.
6954 0 : nsCString origin;
6955 0 : rv = QuotaManager::GetInfoFromPrincipal(principal, &mSuffix, &mGroup,
6956 : &origin);
6957 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
6958 0 : return rv;
6959 : }
6960 :
6961 0 : mOriginScope.SetFromOrigin(origin);
6962 :
6963 0 : return NS_OK;
6964 : }
6965 :
6966 : nsresult
6967 0 : GetOriginUsageOp::DoDirectoryWork(QuotaManager* aQuotaManager)
6968 : {
6969 0 : AssertIsOnIOThread();
6970 0 : MOZ_ASSERT(mUsageInfo.TotalUsage() == 0);
6971 :
6972 0 : AUTO_PROFILER_LABEL("GetOriginUsageOp::DoDirectoryWork", OTHER);
6973 :
6974 : nsresult rv;
6975 :
6976 0 : if (mGetGroupUsage) {
6977 0 : nsCOMPtr<nsIFile> directory;
6978 :
6979 : // Ensure origin is initialized first. It will initialize all origins for
6980 : // temporary storage including origins belonging to our group.
6981 0 : rv = aQuotaManager->EnsureOriginIsInitialized(PERSISTENCE_TYPE_TEMPORARY,
6982 : mSuffix, mGroup,
6983 : mOriginScope.GetOrigin(),
6984 0 : getter_AddRefs(directory));
6985 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
6986 0 : return rv;
6987 : }
6988 :
6989 : // Get cached usage and limit (the method doesn't have to stat any files).
6990 0 : aQuotaManager->GetGroupUsageAndLimit(mGroup, &mUsageInfo);
6991 :
6992 0 : return NS_OK;
6993 : }
6994 :
6995 : // Add all the persistent/temporary/default storage files we care about.
6996 0 : for (const PersistenceType type : kAllPersistenceTypes) {
6997 0 : UsageInfo usageInfo;
6998 0 : rv = GetUsageForOrigin(aQuotaManager,
6999 : type,
7000 : mGroup,
7001 : mOriginScope.GetOrigin(),
7002 0 : &usageInfo);
7003 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
7004 0 : return rv;
7005 : }
7006 :
7007 0 : mUsageInfo.Append(usageInfo);
7008 : }
7009 :
7010 0 : return NS_OK;
7011 : }
7012 :
7013 : void
7014 0 : GetOriginUsageOp::GetResponse(UsageRequestResponse& aResponse)
7015 : {
7016 0 : AssertIsOnOwningThread();
7017 :
7018 0 : OriginUsageResponse usageResponse;
7019 :
7020 : // We'll get the group usage when mGetGroupUsage is true and get the
7021 : // origin usage when mGetGroupUsage is false.
7022 0 : usageResponse.usage() = mUsageInfo.TotalUsage();
7023 :
7024 0 : if (mGetGroupUsage) {
7025 0 : usageResponse.limit() = mUsageInfo.Limit();
7026 : } else {
7027 0 : usageResponse.fileUsage() = mUsageInfo.FileUsage();
7028 : }
7029 :
7030 0 : aResponse = usageResponse;
7031 0 : }
7032 :
7033 : bool
7034 0 : QuotaRequestBase::Init(Quota* aQuota)
7035 : {
7036 0 : AssertIsOnOwningThread();
7037 0 : MOZ_ASSERT(aQuota);
7038 :
7039 0 : mNeedsQuotaManagerInit = true;
7040 :
7041 0 : return true;
7042 : }
7043 :
7044 : void
7045 0 : QuotaRequestBase::SendResults()
7046 : {
7047 0 : AssertIsOnOwningThread();
7048 :
7049 0 : if (IsActorDestroyed()) {
7050 0 : if (NS_SUCCEEDED(mResultCode)) {
7051 0 : mResultCode = NS_ERROR_FAILURE;
7052 : }
7053 : } else {
7054 0 : RequestResponse response;
7055 :
7056 0 : if (NS_SUCCEEDED(mResultCode)) {
7057 0 : GetResponse(response);
7058 : } else {
7059 0 : response = mResultCode;
7060 : }
7061 :
7062 0 : Unused << PQuotaRequestParent::Send__delete__(this, response);
7063 : }
7064 0 : }
7065 :
7066 : void
7067 0 : QuotaRequestBase::ActorDestroy(ActorDestroyReason aWhy)
7068 : {
7069 0 : AssertIsOnOwningThread();
7070 :
7071 0 : NoteActorDestroyed();
7072 0 : }
7073 :
7074 : nsresult
7075 0 : InitOp::DoDirectoryWork(QuotaManager* aQuotaManager)
7076 : {
7077 0 : AssertIsOnIOThread();
7078 :
7079 0 : AUTO_PROFILER_LABEL("InitOp::DoDirectoryWork", OTHER);
7080 :
7081 0 : aQuotaManager->AssertStorageIsInitialized();
7082 :
7083 0 : return NS_OK;
7084 : }
7085 :
7086 : void
7087 0 : InitOp::GetResponse(RequestResponse& aResponse)
7088 : {
7089 0 : AssertIsOnOwningThread();
7090 :
7091 0 : aResponse = InitResponse();
7092 0 : }
7093 :
7094 0 : InitOriginOp::InitOriginOp(const RequestParams& aParams)
7095 : : QuotaRequestBase(/* aExclusive */ false)
7096 0 : , mParams(aParams.get_InitOriginParams())
7097 0 : , mCreated(false)
7098 : {
7099 0 : AssertIsOnOwningThread();
7100 0 : MOZ_ASSERT(aParams.type() == RequestParams::TInitOriginParams);
7101 0 : }
7102 :
7103 : bool
7104 0 : InitOriginOp::Init(Quota* aQuota)
7105 : {
7106 0 : AssertIsOnOwningThread();
7107 0 : MOZ_ASSERT(aQuota);
7108 :
7109 0 : if (NS_WARN_IF(!QuotaRequestBase::Init(aQuota))) {
7110 0 : return false;
7111 : }
7112 :
7113 0 : MOZ_ASSERT(mParams.persistenceType() != PERSISTENCE_TYPE_INVALID);
7114 :
7115 0 : mPersistenceType.SetValue(mParams.persistenceType());
7116 :
7117 0 : mNeedsMainThreadInit = true;
7118 :
7119 0 : return true;
7120 : }
7121 :
7122 : nsresult
7123 0 : InitOriginOp::DoInitOnMainThread()
7124 : {
7125 0 : MOZ_ASSERT(NS_IsMainThread());
7126 0 : MOZ_ASSERT(GetState() == State_Initializing);
7127 0 : MOZ_ASSERT(mNeedsMainThreadInit);
7128 :
7129 0 : const PrincipalInfo& principalInfo = mParams.principalInfo();
7130 :
7131 : nsresult rv;
7132 : nsCOMPtr<nsIPrincipal> principal =
7133 0 : PrincipalInfoToPrincipal(principalInfo, &rv);
7134 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
7135 0 : return rv;
7136 : }
7137 :
7138 : // Figure out which origin we're dealing with.
7139 0 : nsCString origin;
7140 0 : rv = QuotaManager::GetInfoFromPrincipal(principal, &mSuffix, &mGroup,
7141 : &origin);
7142 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
7143 0 : return rv;
7144 : }
7145 :
7146 0 : mOriginScope.SetFromOrigin(origin);
7147 :
7148 0 : return NS_OK;
7149 : }
7150 :
7151 : nsresult
7152 0 : InitOriginOp::DoDirectoryWork(QuotaManager* aQuotaManager)
7153 : {
7154 0 : AssertIsOnIOThread();
7155 0 : MOZ_ASSERT(!mPersistenceType.IsNull());
7156 :
7157 0 : AUTO_PROFILER_LABEL("InitOriginOp::DoDirectoryWork", OTHER);
7158 :
7159 0 : nsCOMPtr<nsIFile> directory;
7160 : bool created;
7161 : nsresult rv =
7162 0 : aQuotaManager->EnsureOriginIsInitializedInternal(mPersistenceType.Value(),
7163 : mSuffix,
7164 : mGroup,
7165 : mOriginScope.GetOrigin(),
7166 0 : getter_AddRefs(directory),
7167 0 : &created);
7168 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
7169 0 : return rv;
7170 : }
7171 :
7172 0 : mCreated = created;
7173 :
7174 0 : return NS_OK;
7175 : }
7176 :
7177 : void
7178 0 : InitOriginOp::GetResponse(RequestResponse& aResponse)
7179 : {
7180 0 : AssertIsOnOwningThread();
7181 :
7182 0 : InitOriginResponse response;
7183 :
7184 0 : response.created() = mCreated;
7185 :
7186 0 : aResponse = response;
7187 0 : }
7188 :
7189 : void
7190 0 : ResetOrClearOp::DeleteFiles(QuotaManager* aQuotaManager)
7191 : {
7192 0 : AssertIsOnIOThread();
7193 0 : MOZ_ASSERT(aQuotaManager);
7194 :
7195 : nsresult rv;
7196 :
7197 : nsCOMPtr<nsIFile> directory =
7198 0 : do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
7199 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
7200 0 : return;
7201 : }
7202 :
7203 0 : rv = directory->InitWithPath(aQuotaManager->GetStoragePath());
7204 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
7205 0 : return;
7206 : }
7207 :
7208 0 : rv = directory->Remove(true);
7209 0 : if (rv != NS_ERROR_FILE_TARGET_DOES_NOT_EXIST &&
7210 0 : rv != NS_ERROR_FILE_NOT_FOUND && NS_FAILED(rv)) {
7211 : // This should never fail if we've closed all storage connections
7212 : // correctly...
7213 0 : MOZ_ASSERT(false, "Failed to remove storage directory!");
7214 : }
7215 :
7216 : nsCOMPtr<nsIFile> storageFile =
7217 0 : do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
7218 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
7219 0 : return;
7220 : }
7221 :
7222 0 : rv = storageFile->InitWithPath(aQuotaManager->GetBasePath());
7223 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
7224 0 : return;
7225 : }
7226 :
7227 0 : rv = storageFile->Append(NS_LITERAL_STRING(STORAGE_FILE_NAME));
7228 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
7229 0 : return;
7230 : }
7231 :
7232 0 : rv = storageFile->Remove(true);
7233 0 : if (rv != NS_ERROR_FILE_TARGET_DOES_NOT_EXIST &&
7234 0 : rv != NS_ERROR_FILE_NOT_FOUND && NS_FAILED(rv)) {
7235 : // This should never fail if we've closed the storage connection
7236 : // correctly...
7237 0 : MOZ_ASSERT(false, "Failed to remove storage file!");
7238 : }
7239 : }
7240 :
7241 : nsresult
7242 0 : ResetOrClearOp::DoDirectoryWork(QuotaManager* aQuotaManager)
7243 : {
7244 0 : AssertIsOnIOThread();
7245 :
7246 0 : AUTO_PROFILER_LABEL("ResetOrClearOp::DoDirectoryWork", OTHER);
7247 :
7248 0 : if (mClear) {
7249 0 : DeleteFiles(aQuotaManager);
7250 : }
7251 :
7252 0 : aQuotaManager->RemoveQuota();
7253 :
7254 0 : aQuotaManager->ResetOrClearCompleted();
7255 :
7256 0 : return NS_OK;
7257 : }
7258 :
7259 : void
7260 0 : ResetOrClearOp::GetResponse(RequestResponse& aResponse)
7261 : {
7262 0 : AssertIsOnOwningThread();
7263 0 : if (mClear) {
7264 0 : aResponse = ClearAllResponse();
7265 : } else {
7266 0 : aResponse = ResetAllResponse();
7267 : }
7268 0 : }
7269 :
7270 : void
7271 0 : ClearRequestBase::DeleteFiles(QuotaManager* aQuotaManager,
7272 : PersistenceType aPersistenceType)
7273 : {
7274 0 : AssertIsOnIOThread();
7275 0 : MOZ_ASSERT(aQuotaManager);
7276 :
7277 : nsresult rv;
7278 :
7279 : nsCOMPtr<nsIFile> directory =
7280 0 : do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
7281 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
7282 0 : return;
7283 : }
7284 :
7285 0 : rv = directory->InitWithPath(aQuotaManager->GetStoragePath(aPersistenceType));
7286 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
7287 0 : return;
7288 : }
7289 :
7290 0 : nsCOMPtr<nsISimpleEnumerator> entries;
7291 0 : if (NS_WARN_IF(NS_FAILED(
7292 0 : directory->GetDirectoryEntries(getter_AddRefs(entries)))) || !entries) {
7293 0 : return;
7294 : }
7295 :
7296 0 : OriginScope originScope = mOriginScope.Clone();
7297 0 : if (originScope.IsOrigin()) {
7298 0 : nsCString originSanitized(originScope.GetOrigin());
7299 0 : SanitizeOriginString(originSanitized);
7300 0 : originScope.SetOrigin(originSanitized);
7301 0 : } else if (originScope.IsPrefix()) {
7302 0 : nsCString prefixSanitized(originScope.GetPrefix());
7303 0 : SanitizeOriginString(prefixSanitized);
7304 0 : originScope.SetPrefix(prefixSanitized);
7305 : }
7306 :
7307 : bool hasMore;
7308 0 : while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) {
7309 0 : nsCOMPtr<nsISupports> entry;
7310 0 : rv = entries->GetNext(getter_AddRefs(entry));
7311 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
7312 0 : return;
7313 : }
7314 :
7315 0 : nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
7316 0 : MOZ_ASSERT(file);
7317 :
7318 : bool isDirectory;
7319 0 : rv = file->IsDirectory(&isDirectory);
7320 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
7321 0 : return;
7322 : }
7323 :
7324 0 : nsString leafName;
7325 0 : rv = file->GetLeafName(leafName);
7326 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
7327 0 : return;
7328 : }
7329 :
7330 0 : if (!isDirectory) {
7331 : // Unknown files during clearing are allowed. Just warn if we find them.
7332 0 : if (!IsOSMetadata(leafName)) {
7333 0 : UNKNOWN_FILE_WARNING(leafName);
7334 : }
7335 0 : continue;
7336 : }
7337 :
7338 : // Skip the origin directory if it doesn't match the pattern.
7339 0 : if (!originScope.MatchesOrigin(OriginScope::FromOrigin(
7340 0 : NS_ConvertUTF16toUTF8(leafName)))) {
7341 0 : continue;
7342 : }
7343 :
7344 0 : bool persistent = aPersistenceType == PERSISTENCE_TYPE_PERSISTENT;
7345 :
7346 : int64_t timestamp;
7347 0 : nsCString suffix;
7348 0 : nsCString group;
7349 0 : nsCString origin;
7350 : bool persisted;
7351 0 : rv = aQuotaManager->GetDirectoryMetadata2WithRestore(file,
7352 : persistent,
7353 : ×tamp,
7354 : &persisted,
7355 : suffix,
7356 : group,
7357 : origin);
7358 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
7359 0 : return;
7360 : }
7361 :
7362 0 : for (uint32_t index = 0; index < 10; index++) {
7363 : // We can't guarantee that this will always succeed on Windows...
7364 0 : if (NS_SUCCEEDED((rv = file->Remove(true)))) {
7365 0 : break;
7366 : }
7367 :
7368 0 : NS_WARNING("Failed to remove directory, retrying after a short delay.");
7369 :
7370 0 : PR_Sleep(PR_MillisecondsToInterval(200));
7371 : }
7372 :
7373 0 : if (NS_FAILED(rv)) {
7374 0 : NS_WARNING("Failed to remove directory, giving up!");
7375 : }
7376 :
7377 0 : if (aPersistenceType != PERSISTENCE_TYPE_PERSISTENT) {
7378 0 : aQuotaManager->RemoveQuotaForOrigin(aPersistenceType, group, origin);
7379 : }
7380 :
7381 0 : aQuotaManager->OriginClearCompleted(aPersistenceType, origin);
7382 : }
7383 :
7384 : }
7385 :
7386 : nsresult
7387 0 : ClearRequestBase::DoDirectoryWork(QuotaManager* aQuotaManager)
7388 : {
7389 0 : AssertIsOnIOThread();
7390 :
7391 0 : AUTO_PROFILER_LABEL("ClearRequestBase::DoDirectoryWork", OTHER);
7392 :
7393 0 : if (mPersistenceType.IsNull()) {
7394 0 : for (const PersistenceType type : kAllPersistenceTypes) {
7395 0 : DeleteFiles(aQuotaManager, type);
7396 : }
7397 : } else {
7398 0 : DeleteFiles(aQuotaManager, mPersistenceType.Value());
7399 : }
7400 :
7401 0 : return NS_OK;
7402 : }
7403 :
7404 0 : ClearOriginOp::ClearOriginOp(const RequestParams& aParams)
7405 : : ClearRequestBase(/* aExclusive */ true)
7406 0 : , mParams(aParams)
7407 : {
7408 0 : MOZ_ASSERT(aParams.type() == RequestParams::TClearOriginParams);
7409 0 : }
7410 :
7411 : bool
7412 0 : ClearOriginOp::Init(Quota* aQuota)
7413 : {
7414 0 : AssertIsOnOwningThread();
7415 0 : MOZ_ASSERT(aQuota);
7416 :
7417 0 : if (NS_WARN_IF(!QuotaRequestBase::Init(aQuota))) {
7418 0 : return false;
7419 : }
7420 :
7421 0 : if (mParams.persistenceTypeIsExplicit()) {
7422 0 : MOZ_ASSERT(mParams.persistenceType() != PERSISTENCE_TYPE_INVALID);
7423 :
7424 0 : mPersistenceType.SetValue(mParams.persistenceType());
7425 : }
7426 :
7427 0 : mNeedsMainThreadInit = true;
7428 :
7429 0 : return true;
7430 : }
7431 :
7432 : nsresult
7433 0 : ClearOriginOp::DoInitOnMainThread()
7434 : {
7435 0 : MOZ_ASSERT(NS_IsMainThread());
7436 0 : MOZ_ASSERT(GetState() == State_Initializing);
7437 0 : MOZ_ASSERT(mNeedsMainThreadInit);
7438 :
7439 0 : const PrincipalInfo& principalInfo = mParams.principalInfo();
7440 :
7441 : nsresult rv;
7442 : nsCOMPtr<nsIPrincipal> principal =
7443 0 : PrincipalInfoToPrincipal(principalInfo, &rv);
7444 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
7445 0 : return rv;
7446 : }
7447 :
7448 : // Figure out which origin we're dealing with.
7449 0 : nsCString origin;
7450 0 : rv = QuotaManager::GetInfoFromPrincipal(principal, nullptr, nullptr,
7451 : &origin);
7452 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
7453 0 : return rv;
7454 : }
7455 :
7456 0 : if (mParams.clearAll()) {
7457 0 : mOriginScope.SetFromPrefix(origin);
7458 : } else {
7459 0 : mOriginScope.SetFromOrigin(origin);
7460 : }
7461 :
7462 0 : return NS_OK;
7463 : }
7464 :
7465 : void
7466 0 : ClearOriginOp::GetResponse(RequestResponse& aResponse)
7467 : {
7468 0 : AssertIsOnOwningThread();
7469 :
7470 0 : aResponse = ClearOriginResponse();
7471 0 : }
7472 :
7473 0 : ClearDataOp::ClearDataOp(const RequestParams& aParams)
7474 : : ClearRequestBase(/* aExclusive */ true)
7475 0 : , mParams(aParams)
7476 : {
7477 0 : MOZ_ASSERT(aParams.type() == RequestParams::TClearDataParams);
7478 0 : }
7479 :
7480 : bool
7481 0 : ClearDataOp::Init(Quota* aQuota)
7482 : {
7483 0 : AssertIsOnOwningThread();
7484 0 : MOZ_ASSERT(aQuota);
7485 :
7486 0 : if (NS_WARN_IF(!QuotaRequestBase::Init(aQuota))) {
7487 0 : return false;
7488 : }
7489 :
7490 0 : mNeedsMainThreadInit = true;
7491 :
7492 0 : return true;
7493 : }
7494 :
7495 : nsresult
7496 0 : ClearDataOp::DoInitOnMainThread()
7497 : {
7498 0 : MOZ_ASSERT(NS_IsMainThread());
7499 0 : MOZ_ASSERT(GetState() == State_Initializing);
7500 0 : MOZ_ASSERT(mNeedsMainThreadInit);
7501 :
7502 0 : mOriginScope.SetFromJSONPattern(mParams.pattern());
7503 :
7504 0 : return NS_OK;
7505 : }
7506 :
7507 : void
7508 0 : ClearDataOp::GetResponse(RequestResponse& aResponse)
7509 : {
7510 0 : AssertIsOnOwningThread();
7511 :
7512 0 : aResponse = ClearDataResponse();
7513 0 : }
7514 :
7515 0 : PersistRequestBase::PersistRequestBase(const PrincipalInfo& aPrincipalInfo)
7516 : : QuotaRequestBase(/* aExclusive */ false)
7517 0 : , mPrincipalInfo(aPrincipalInfo)
7518 : {
7519 0 : AssertIsOnOwningThread();
7520 0 : }
7521 :
7522 : bool
7523 0 : PersistRequestBase::Init(Quota* aQuota)
7524 : {
7525 0 : AssertIsOnOwningThread();
7526 0 : MOZ_ASSERT(aQuota);
7527 :
7528 0 : if (NS_WARN_IF(!QuotaRequestBase::Init(aQuota))) {
7529 0 : return false;
7530 : }
7531 :
7532 0 : mPersistenceType.SetValue(PERSISTENCE_TYPE_DEFAULT);
7533 :
7534 0 : mNeedsMainThreadInit = true;
7535 :
7536 0 : return true;
7537 : }
7538 :
7539 : nsresult
7540 0 : PersistRequestBase::DoInitOnMainThread()
7541 : {
7542 0 : MOZ_ASSERT(NS_IsMainThread());
7543 0 : MOZ_ASSERT(GetState() == State_Initializing);
7544 0 : MOZ_ASSERT(mNeedsMainThreadInit);
7545 :
7546 : nsresult rv;
7547 : nsCOMPtr<nsIPrincipal> principal =
7548 0 : PrincipalInfoToPrincipal(mPrincipalInfo, &rv);
7549 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
7550 0 : return rv;
7551 : }
7552 :
7553 : // Figure out which origin we're dealing with.
7554 0 : nsCString origin;
7555 0 : rv = QuotaManager::GetInfoFromPrincipal(principal, &mSuffix, &mGroup,
7556 : &origin);
7557 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
7558 0 : return rv;
7559 : }
7560 :
7561 0 : mOriginScope.SetFromOrigin(origin);
7562 :
7563 0 : return NS_OK;
7564 : }
7565 :
7566 0 : PersistedOp::PersistedOp(const RequestParams& aParams)
7567 0 : : PersistRequestBase(aParams.get_PersistedParams().principalInfo())
7568 0 : , mPersisted(false)
7569 : {
7570 0 : MOZ_ASSERT(aParams.type() == RequestParams::TPersistedParams);
7571 0 : }
7572 :
7573 : nsresult
7574 0 : PersistedOp::DoDirectoryWork(QuotaManager* aQuotaManager)
7575 : {
7576 0 : AssertIsOnIOThread();
7577 0 : MOZ_ASSERT(!mPersistenceType.IsNull());
7578 0 : MOZ_ASSERT(mPersistenceType.Value() == PERSISTENCE_TYPE_DEFAULT);
7579 0 : MOZ_ASSERT(mOriginScope.IsOrigin());
7580 :
7581 0 : AUTO_PROFILER_LABEL("PersistedOp::DoDirectoryWork", OTHER);
7582 :
7583 : Nullable<bool> persisted =
7584 0 : aQuotaManager->OriginPersisted(mGroup, mOriginScope.GetOrigin());
7585 :
7586 0 : if (!persisted.IsNull()) {
7587 0 : mPersisted = persisted.Value();
7588 0 : return NS_OK;
7589 : }
7590 :
7591 : // If we get here, it means the origin hasn't been initialized yet.
7592 : // Try to get the persisted flag from directory metadata on disk.
7593 :
7594 0 : nsCOMPtr<nsIFile> directory;
7595 0 : nsresult rv = aQuotaManager->GetDirectoryForOrigin(mPersistenceType.Value(),
7596 : mOriginScope.GetOrigin(),
7597 0 : getter_AddRefs(directory));
7598 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
7599 0 : return rv;
7600 : }
7601 :
7602 : bool exists;
7603 0 : rv = directory->Exists(&exists);
7604 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
7605 0 : return rv;
7606 : }
7607 :
7608 0 : if (exists) {
7609 : // Get the persisted flag.
7610 : bool persisted;
7611 : rv =
7612 0 : aQuotaManager->GetDirectoryMetadata2WithRestore(directory,
7613 : /* aPersistent */ false,
7614 : /* aTimestamp */ nullptr,
7615 0 : &persisted);
7616 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
7617 0 : return rv;
7618 : }
7619 :
7620 0 : mPersisted = persisted;
7621 : } else {
7622 : // The directory has not been created yet.
7623 0 : mPersisted = false;
7624 : }
7625 :
7626 0 : return NS_OK;
7627 : }
7628 :
7629 : void
7630 0 : PersistedOp::GetResponse(RequestResponse& aResponse)
7631 : {
7632 0 : AssertIsOnOwningThread();
7633 :
7634 0 : PersistedResponse persistedResponse;
7635 0 : persistedResponse.persisted() = mPersisted;
7636 :
7637 0 : aResponse = persistedResponse;
7638 0 : }
7639 :
7640 0 : PersistOp::PersistOp(const RequestParams& aParams)
7641 0 : : PersistRequestBase(aParams.get_PersistParams().principalInfo())
7642 : {
7643 0 : MOZ_ASSERT(aParams.type() == RequestParams::TPersistParams);
7644 0 : }
7645 :
7646 : nsresult
7647 0 : PersistOp::DoDirectoryWork(QuotaManager* aQuotaManager)
7648 : {
7649 0 : AssertIsOnIOThread();
7650 0 : MOZ_ASSERT(!mPersistenceType.IsNull());
7651 0 : MOZ_ASSERT(mPersistenceType.Value() == PERSISTENCE_TYPE_DEFAULT);
7652 0 : MOZ_ASSERT(mOriginScope.IsOrigin());
7653 :
7654 0 : AUTO_PROFILER_LABEL("PersistOp::DoDirectoryWork", OTHER);
7655 :
7656 : // Update directory metadata on disk first. Then, create/update the originInfo
7657 : // if needed.
7658 0 : nsCOMPtr<nsIFile> directory;
7659 0 : nsresult rv = aQuotaManager->GetDirectoryForOrigin(mPersistenceType.Value(),
7660 : mOriginScope.GetOrigin(),
7661 0 : getter_AddRefs(directory));
7662 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
7663 0 : return rv;
7664 : }
7665 :
7666 : bool created;
7667 0 : rv = EnsureOriginDirectory(directory, &created);
7668 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
7669 0 : return rv;
7670 : }
7671 :
7672 0 : if (created) {
7673 : int64_t timestamp;
7674 0 : rv = CreateDirectoryMetadataFiles(directory,
7675 : /* aPersisted */ true,
7676 : mSuffix,
7677 : mGroup,
7678 : mOriginScope.GetOrigin(),
7679 0 : ×tamp);
7680 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
7681 0 : return rv;
7682 : }
7683 :
7684 : // Directory metadata has been successfully created.
7685 : // Create OriginInfo too if temporary storage was already initialized.
7686 0 : if (aQuotaManager->IsTemporaryStorageInitialized()) {
7687 0 : aQuotaManager->InitQuotaForOrigin(mPersistenceType.Value(),
7688 : mGroup,
7689 : mOriginScope.GetOrigin(),
7690 : /* aUsageBytes */ 0,
7691 : timestamp,
7692 0 : /* aPersisted */ true);
7693 : }
7694 : } else {
7695 : // Get the persisted flag (restore the metadata file if necessary).
7696 : bool persisted;
7697 : rv =
7698 0 : aQuotaManager->GetDirectoryMetadata2WithRestore(directory,
7699 : /* aPersistent */ false,
7700 : /* aTimestamp */ nullptr,
7701 0 : &persisted);
7702 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
7703 0 : return rv;
7704 : }
7705 :
7706 0 : if (!persisted) {
7707 0 : nsCOMPtr<nsIFile> file;
7708 0 : nsresult rv = directory->Clone(getter_AddRefs(file));
7709 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
7710 0 : return rv;
7711 : }
7712 :
7713 0 : rv = file->Append(NS_LITERAL_STRING(METADATA_V2_FILE_NAME));
7714 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
7715 0 : return rv;
7716 : }
7717 :
7718 0 : nsCOMPtr<nsIBinaryOutputStream> stream;
7719 0 : rv = GetBinaryOutputStream(file, kUpdateFileFlag, getter_AddRefs(stream));
7720 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
7721 0 : return rv;
7722 : }
7723 :
7724 0 : MOZ_ASSERT(stream);
7725 :
7726 : // Update origin access time while we are here.
7727 0 : rv = stream->Write64(PR_Now());
7728 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
7729 0 : return rv;
7730 : }
7731 :
7732 : // Set the persisted flag to true.
7733 0 : rv = stream->WriteBoolean(true);
7734 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
7735 0 : return rv;
7736 : }
7737 : }
7738 :
7739 : // Directory metadata has been successfully updated.
7740 : // Update OriginInfo too if temporary storage was already initialized.
7741 0 : if (aQuotaManager->IsTemporaryStorageInitialized()) {
7742 0 : aQuotaManager->PersistOrigin(mGroup, mOriginScope.GetOrigin());
7743 : }
7744 : }
7745 :
7746 0 : return NS_OK;
7747 : }
7748 :
7749 : void
7750 0 : PersistOp::GetResponse(RequestResponse& aResponse)
7751 : {
7752 0 : AssertIsOnOwningThread();
7753 :
7754 0 : aResponse = PersistResponse();
7755 0 : }
7756 :
7757 : nsresult
7758 0 : StorageDirectoryHelper::GetDirectoryMetadata(nsIFile* aDirectory,
7759 : int64_t& aTimestamp,
7760 : nsACString& aGroup,
7761 : nsACString& aOrigin,
7762 : Nullable<bool>& aIsApp)
7763 : {
7764 0 : AssertIsOnIOThread();
7765 0 : MOZ_ASSERT(aDirectory);
7766 :
7767 0 : nsCOMPtr<nsIBinaryInputStream> binaryStream;
7768 0 : nsresult rv = GetBinaryInputStream(aDirectory,
7769 0 : NS_LITERAL_STRING(METADATA_FILE_NAME),
7770 0 : getter_AddRefs(binaryStream));
7771 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
7772 0 : return rv;
7773 : }
7774 :
7775 : uint64_t timestamp;
7776 0 : rv = binaryStream->Read64(×tamp);
7777 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
7778 0 : return rv;
7779 : }
7780 :
7781 0 : nsCString group;
7782 0 : rv = binaryStream->ReadCString(group);
7783 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
7784 0 : return rv;
7785 : }
7786 :
7787 0 : nsCString origin;
7788 0 : rv = binaryStream->ReadCString(origin);
7789 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
7790 0 : return rv;
7791 : }
7792 :
7793 0 : Nullable<bool> isApp;
7794 : bool value;
7795 0 : if (NS_SUCCEEDED(binaryStream->ReadBoolean(&value))) {
7796 0 : isApp.SetValue(value);
7797 : }
7798 :
7799 0 : aTimestamp = timestamp;
7800 0 : aGroup = group;
7801 0 : aOrigin = origin;
7802 0 : aIsApp = Move(isApp);
7803 0 : return NS_OK;
7804 : }
7805 :
7806 : nsresult
7807 0 : StorageDirectoryHelper::GetDirectoryMetadata2(nsIFile* aDirectory,
7808 : int64_t& aTimestamp,
7809 : nsACString& aSuffix,
7810 : nsACString& aGroup,
7811 : nsACString& aOrigin,
7812 : bool& aIsApp)
7813 : {
7814 0 : AssertIsOnIOThread();
7815 0 : MOZ_ASSERT(aDirectory);
7816 :
7817 0 : nsCOMPtr<nsIBinaryInputStream> binaryStream;
7818 0 : nsresult rv = GetBinaryInputStream(aDirectory,
7819 0 : NS_LITERAL_STRING(METADATA_V2_FILE_NAME),
7820 0 : getter_AddRefs(binaryStream));
7821 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
7822 0 : return rv;
7823 : }
7824 :
7825 : uint64_t timestamp;
7826 0 : rv = binaryStream->Read64(×tamp);
7827 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
7828 0 : return rv;
7829 : }
7830 :
7831 : bool persisted;
7832 0 : rv = binaryStream->ReadBoolean(&persisted);
7833 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
7834 0 : return rv;
7835 : }
7836 :
7837 : uint32_t reservedData1;
7838 0 : rv = binaryStream->Read32(&reservedData1);
7839 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
7840 0 : return rv;
7841 : }
7842 :
7843 : uint32_t reservedData2;
7844 0 : rv = binaryStream->Read32(&reservedData2);
7845 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
7846 0 : return rv;
7847 : }
7848 :
7849 0 : nsCString suffix;
7850 0 : rv = binaryStream->ReadCString(suffix);
7851 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
7852 0 : return rv;
7853 : }
7854 :
7855 0 : nsCString group;
7856 0 : rv = binaryStream->ReadCString(group);
7857 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
7858 0 : return rv;
7859 : }
7860 :
7861 0 : nsCString origin;
7862 0 : rv = binaryStream->ReadCString(origin);
7863 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
7864 0 : return rv;
7865 : }
7866 :
7867 : bool isApp;
7868 0 : rv = binaryStream->ReadBoolean(&isApp);
7869 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
7870 0 : return rv;
7871 : }
7872 :
7873 0 : aTimestamp = timestamp;
7874 0 : aSuffix = suffix;
7875 0 : aGroup = group;
7876 0 : aOrigin = origin;
7877 0 : aIsApp = isApp;
7878 0 : return NS_OK;
7879 : }
7880 :
7881 : nsresult
7882 0 : StorageDirectoryHelper::RemoveObsoleteOrigin(const OriginProps& aOriginProps)
7883 : {
7884 0 : AssertIsOnIOThread();
7885 0 : MOZ_ASSERT(aOriginProps.mDirectory);
7886 :
7887 0 : QM_WARNING("Deleting obsolete %s directory that is no longer a legal "
7888 : "origin!", NS_ConvertUTF16toUTF8(aOriginProps.mLeafName).get());
7889 :
7890 0 : nsresult rv = aOriginProps.mDirectory->Remove(/* recursive */ true);
7891 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
7892 0 : return rv;
7893 : }
7894 :
7895 0 : return NS_OK;
7896 : }
7897 :
7898 : nsresult
7899 0 : StorageDirectoryHelper::ProcessOriginDirectories()
7900 : {
7901 0 : AssertIsOnIOThread();
7902 0 : MOZ_ASSERT(!mOriginProps.IsEmpty());
7903 :
7904 0 : MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(this));
7905 :
7906 : {
7907 0 : mozilla::MutexAutoLock autolock(mMutex);
7908 0 : while (mWaiting) {
7909 0 : mCondVar.Wait();
7910 : }
7911 : }
7912 :
7913 0 : if (NS_WARN_IF(NS_FAILED(mMainThreadResultCode))) {
7914 0 : return mMainThreadResultCode;
7915 : }
7916 :
7917 : // Verify that the bounce to the main thread didn't start the shutdown
7918 : // sequence.
7919 0 : if (NS_WARN_IF(QuotaManager::IsShuttingDown())) {
7920 0 : return NS_ERROR_FAILURE;
7921 : }
7922 :
7923 : nsresult rv;
7924 :
7925 : // Don't try to upgrade obsolete origins, remove them right after we detect
7926 : // them.
7927 0 : for (auto& originProps : mOriginProps) {
7928 0 : if (originProps.mType == OriginProps::eObsolete) {
7929 0 : MOZ_ASSERT(originProps.mSuffix.IsEmpty());
7930 0 : MOZ_ASSERT(originProps.mGroup.IsEmpty());
7931 0 : MOZ_ASSERT(originProps.mOrigin.IsEmpty());
7932 :
7933 0 : rv = RemoveObsoleteOrigin(originProps);
7934 : } else {
7935 0 : MOZ_ASSERT(!originProps.mGroup.IsEmpty());
7936 0 : MOZ_ASSERT(!originProps.mOrigin.IsEmpty());
7937 :
7938 0 : rv = ProcessOriginDirectory(originProps);
7939 : }
7940 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
7941 0 : return rv;
7942 : }
7943 : }
7944 :
7945 0 : return NS_OK;
7946 : }
7947 :
7948 : nsresult
7949 0 : StorageDirectoryHelper::RunOnMainThread()
7950 : {
7951 0 : MOZ_ASSERT(NS_IsMainThread());
7952 0 : MOZ_ASSERT(!mOriginProps.IsEmpty());
7953 :
7954 : nsresult rv;
7955 :
7956 : nsCOMPtr<nsIScriptSecurityManager> secMan =
7957 0 : do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
7958 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
7959 0 : return rv;
7960 : }
7961 :
7962 0 : for (uint32_t count = mOriginProps.Length(), index = 0;
7963 0 : index < count;
7964 : index++) {
7965 0 : OriginProps& originProps = mOriginProps[index];
7966 :
7967 0 : switch (originProps.mType) {
7968 : case OriginProps::eChrome: {
7969 0 : QuotaManager::GetInfoForChrome(&originProps.mSuffix,
7970 : &originProps.mGroup,
7971 0 : &originProps.mOrigin);
7972 0 : break;
7973 : }
7974 :
7975 : case OriginProps::eContent: {
7976 0 : nsCOMPtr<nsIURI> uri;
7977 0 : rv = NS_NewURI(getter_AddRefs(uri), originProps.mSpec);
7978 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
7979 0 : return rv;
7980 : }
7981 :
7982 : nsCOMPtr<nsIPrincipal> principal =
7983 0 : BasePrincipal::CreateCodebasePrincipal(uri, originProps.mAttrs);
7984 0 : if (NS_WARN_IF(!principal)) {
7985 0 : return NS_ERROR_FAILURE;
7986 : }
7987 :
7988 0 : rv = QuotaManager::GetInfoFromPrincipal(principal,
7989 : &originProps.mSuffix,
7990 : &originProps.mGroup,
7991 : &originProps.mOrigin);
7992 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
7993 0 : return rv;
7994 : }
7995 :
7996 0 : break;
7997 : }
7998 :
7999 : case OriginProps::eObsolete: {
8000 : // There's no way to get info for obsolete origins.
8001 0 : break;
8002 : }
8003 :
8004 : default:
8005 0 : MOZ_CRASH("Bad type!");
8006 : }
8007 : }
8008 :
8009 0 : return NS_OK;
8010 : }
8011 :
8012 : NS_IMETHODIMP
8013 0 : StorageDirectoryHelper::Run()
8014 : {
8015 0 : MOZ_ASSERT(NS_IsMainThread());
8016 :
8017 0 : nsresult rv = RunOnMainThread();
8018 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8019 0 : mMainThreadResultCode = rv;
8020 : }
8021 :
8022 0 : MutexAutoLock lock(mMutex);
8023 0 : MOZ_ASSERT(mWaiting);
8024 :
8025 0 : mWaiting = false;
8026 0 : mCondVar.Notify();
8027 :
8028 0 : return NS_OK;
8029 : }
8030 :
8031 : nsresult
8032 0 : StorageDirectoryHelper::
8033 : OriginProps::Init(nsIFile* aDirectory)
8034 : {
8035 0 : AssertIsOnIOThread();
8036 0 : MOZ_ASSERT(aDirectory);
8037 :
8038 0 : nsString leafName;
8039 0 : nsresult rv = aDirectory->GetLeafName(leafName);
8040 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8041 0 : return rv;
8042 : }
8043 :
8044 0 : if (leafName.EqualsLiteral(kChromeOrigin)) {
8045 0 : mDirectory = aDirectory;
8046 0 : mLeafName = leafName;
8047 0 : mSpec = kChromeOrigin;
8048 0 : mType = eChrome;
8049 : } else {
8050 0 : nsCString spec;
8051 0 : OriginAttributes attrs;
8052 : OriginParser::ResultType result =
8053 0 : OriginParser::ParseOrigin(NS_ConvertUTF16toUTF8(leafName), spec, &attrs);
8054 0 : if (NS_WARN_IF(result == OriginParser::InvalidOrigin)) {
8055 0 : return NS_ERROR_FAILURE;
8056 : }
8057 :
8058 0 : mDirectory = aDirectory;
8059 0 : mLeafName = leafName;
8060 0 : mSpec = spec;
8061 0 : mAttrs = attrs;
8062 0 : mType = result == OriginParser::ObsoleteOrigin ? eObsolete : eContent;
8063 : }
8064 :
8065 0 : return NS_OK;
8066 : }
8067 :
8068 : // static
8069 : auto
8070 0 : OriginParser::ParseOrigin(const nsACString& aOrigin,
8071 : nsCString& aSpec,
8072 : OriginAttributes* aAttrs) -> ResultType
8073 : {
8074 0 : MOZ_ASSERT(!aOrigin.IsEmpty());
8075 0 : MOZ_ASSERT(aAttrs);
8076 :
8077 0 : OriginAttributes originAttributes;
8078 :
8079 0 : nsCString originNoSuffix;
8080 0 : bool ok = originAttributes.PopulateFromOrigin(aOrigin, originNoSuffix);
8081 0 : if (!ok) {
8082 0 : return InvalidOrigin;
8083 : }
8084 :
8085 0 : OriginParser parser(originNoSuffix, originAttributes);
8086 0 : return parser.Parse(aSpec, aAttrs);
8087 : }
8088 :
8089 : auto
8090 0 : OriginParser::Parse(nsACString& aSpec, OriginAttributes* aAttrs) -> ResultType
8091 : {
8092 0 : MOZ_ASSERT(aAttrs);
8093 :
8094 0 : while (mTokenizer.hasMoreTokens()) {
8095 0 : const nsDependentCSubstring& token = mTokenizer.nextToken();
8096 :
8097 0 : HandleToken(token);
8098 :
8099 0 : if (mError) {
8100 0 : break;
8101 : }
8102 :
8103 0 : if (!mHandledTokens.IsEmpty()) {
8104 0 : mHandledTokens.Append(NS_LITERAL_CSTRING(", "));
8105 : }
8106 0 : mHandledTokens.Append('\'');
8107 0 : mHandledTokens.Append(token);
8108 0 : mHandledTokens.Append('\'');
8109 : }
8110 :
8111 0 : if (!mError && mTokenizer.separatorAfterCurrentToken()) {
8112 0 : HandleTrailingSeparator();
8113 : }
8114 :
8115 0 : if (mError) {
8116 0 : QM_WARNING("Origin '%s' failed to parse, handled tokens: %s", mOrigin.get(),
8117 : mHandledTokens.get());
8118 :
8119 0 : return InvalidOrigin;
8120 : }
8121 :
8122 0 : MOZ_ASSERT(mState == eComplete || mState == eHandledTrailingSeparator);
8123 :
8124 0 : if (mAppId == kNoAppId) {
8125 0 : *aAttrs = mOriginAttributes;
8126 : } else {
8127 0 : MOZ_ASSERT(mOriginAttributes.mAppId == kNoAppId);
8128 :
8129 0 : *aAttrs = OriginAttributes(mAppId, mInIsolatedMozBrowser);
8130 : }
8131 :
8132 0 : nsAutoCString spec(mScheme);
8133 :
8134 0 : if (mSchemeType == eFile) {
8135 0 : spec.AppendLiteral("://");
8136 :
8137 0 : for (uint32_t count = mPathnameComponents.Length(), index = 0;
8138 0 : index < count;
8139 : index++) {
8140 0 : spec.Append('/');
8141 0 : spec.Append(mPathnameComponents[index]);
8142 : }
8143 :
8144 0 : aSpec = spec;
8145 :
8146 0 : return ValidOrigin;
8147 : }
8148 :
8149 0 : if (mSchemeType == eAbout) {
8150 0 : spec.Append(':');
8151 : } else {
8152 0 : spec.AppendLiteral("://");
8153 : }
8154 :
8155 0 : spec.Append(mHost);
8156 :
8157 0 : if (!mPort.IsNull()) {
8158 0 : spec.Append(':');
8159 0 : spec.AppendInt(mPort.Value());
8160 : }
8161 :
8162 0 : aSpec = spec;
8163 :
8164 0 : return mScheme.EqualsLiteral("app") ? ObsoleteOrigin : ValidOrigin;
8165 : }
8166 :
8167 : void
8168 0 : OriginParser::HandleScheme(const nsDependentCSubstring& aToken)
8169 : {
8170 0 : MOZ_ASSERT(!aToken.IsEmpty());
8171 0 : MOZ_ASSERT(mState == eExpectingAppIdOrScheme || mState == eExpectingScheme);
8172 :
8173 0 : bool isAbout = false;
8174 0 : bool isFile = false;
8175 0 : if (aToken.EqualsLiteral("http") ||
8176 0 : aToken.EqualsLiteral("https") ||
8177 0 : (isAbout = aToken.EqualsLiteral("about") ||
8178 0 : aToken.EqualsLiteral("moz-safe-about")) ||
8179 0 : aToken.EqualsLiteral("indexeddb") ||
8180 0 : (isFile = aToken.EqualsLiteral("file")) ||
8181 0 : aToken.EqualsLiteral("app") ||
8182 0 : aToken.EqualsLiteral("resource") ||
8183 0 : aToken.EqualsLiteral("moz-extension")) {
8184 0 : mScheme = aToken;
8185 :
8186 0 : if (isAbout) {
8187 0 : mSchemeType = eAbout;
8188 0 : mState = eExpectingHost;
8189 : } else {
8190 0 : if (isFile) {
8191 0 : mSchemeType = eFile;
8192 : }
8193 0 : mState = eExpectingEmptyToken1;
8194 : }
8195 :
8196 0 : return;
8197 : }
8198 :
8199 0 : QM_WARNING("'%s' is not a valid scheme!", nsCString(aToken).get());
8200 :
8201 0 : mError = true;
8202 : }
8203 :
8204 : void
8205 0 : OriginParser::HandlePathnameComponent(const nsDependentCSubstring& aToken)
8206 : {
8207 0 : MOZ_ASSERT(!aToken.IsEmpty());
8208 0 : MOZ_ASSERT(mState == eExpectingEmptyTokenOrDriveLetterOrPathnameComponent ||
8209 : mState == eExpectingEmptyTokenOrPathnameComponent);
8210 0 : MOZ_ASSERT(mSchemeType == eFile);
8211 :
8212 0 : mPathnameComponents.AppendElement(aToken);
8213 :
8214 0 : mState = mTokenizer.hasMoreTokens() ? eExpectingEmptyTokenOrPathnameComponent
8215 : : eComplete;
8216 0 : }
8217 :
8218 : void
8219 0 : OriginParser::HandleToken(const nsDependentCSubstring& aToken)
8220 : {
8221 0 : switch (mState) {
8222 : case eExpectingAppIdOrScheme: {
8223 0 : if (aToken.IsEmpty()) {
8224 0 : QM_WARNING("Expected an app id or scheme (not an empty string)!");
8225 :
8226 0 : mError = true;
8227 0 : return;
8228 : }
8229 :
8230 0 : if (NS_IsAsciiDigit(aToken.First())) {
8231 : // nsDependentCSubstring doesn't provice ToInteger()
8232 0 : nsCString token(aToken);
8233 :
8234 : nsresult rv;
8235 0 : uint32_t appId = token.ToInteger(&rv);
8236 0 : if (NS_SUCCEEDED(rv)) {
8237 0 : mAppId = appId;
8238 0 : mState = eExpectingInMozBrowser;
8239 0 : return;
8240 : }
8241 : }
8242 :
8243 0 : HandleScheme(aToken);
8244 :
8245 0 : return;
8246 : }
8247 :
8248 : case eExpectingInMozBrowser: {
8249 0 : if (aToken.Length() != 1) {
8250 0 : QM_WARNING("'%d' is not a valid length for the inMozBrowser flag!",
8251 : aToken.Length());
8252 :
8253 0 : mError = true;
8254 0 : return;
8255 : }
8256 :
8257 0 : if (aToken.First() == 't') {
8258 0 : mInIsolatedMozBrowser = true;
8259 0 : } else if (aToken.First() == 'f') {
8260 0 : mInIsolatedMozBrowser = false;
8261 : } else {
8262 0 : QM_WARNING("'%s' is not a valid value for the inMozBrowser flag!",
8263 : nsCString(aToken).get());
8264 :
8265 0 : mError = true;
8266 0 : return;
8267 : }
8268 :
8269 0 : mState = eExpectingScheme;
8270 :
8271 0 : return;
8272 : }
8273 :
8274 : case eExpectingScheme: {
8275 0 : if (aToken.IsEmpty()) {
8276 0 : QM_WARNING("Expected a scheme (not an empty string)!");
8277 :
8278 0 : mError = true;
8279 0 : return;
8280 : }
8281 :
8282 0 : HandleScheme(aToken);
8283 :
8284 0 : return;
8285 : }
8286 :
8287 : case eExpectingEmptyToken1: {
8288 0 : if (!aToken.IsEmpty()) {
8289 0 : QM_WARNING("Expected the first empty token!");
8290 :
8291 0 : mError = true;
8292 0 : return;
8293 : }
8294 :
8295 0 : mState = eExpectingEmptyToken2;
8296 :
8297 0 : return;
8298 : }
8299 :
8300 : case eExpectingEmptyToken2: {
8301 0 : if (!aToken.IsEmpty()) {
8302 0 : QM_WARNING("Expected the second empty token!");
8303 :
8304 0 : mError = true;
8305 0 : return;
8306 : }
8307 :
8308 0 : if (mSchemeType == eFile) {
8309 0 : mState = eExpectingEmptyToken3;
8310 : } else {
8311 0 : mState = eExpectingHost;
8312 : }
8313 :
8314 0 : return;
8315 : }
8316 :
8317 : case eExpectingEmptyToken3: {
8318 0 : MOZ_ASSERT(mSchemeType == eFile);
8319 :
8320 0 : if (!aToken.IsEmpty()) {
8321 0 : QM_WARNING("Expected the third empty token!");
8322 :
8323 0 : mError = true;
8324 0 : return;
8325 : }
8326 :
8327 0 : mState = mTokenizer.hasMoreTokens()
8328 0 : ? eExpectingEmptyTokenOrDriveLetterOrPathnameComponent
8329 : : eComplete;
8330 :
8331 0 : return;
8332 : }
8333 :
8334 : case eExpectingHost: {
8335 0 : if (aToken.IsEmpty()) {
8336 0 : QM_WARNING("Expected a host (not an empty string)!");
8337 :
8338 0 : mError = true;
8339 0 : return;
8340 : }
8341 :
8342 0 : mHost = aToken;
8343 :
8344 0 : mState = mTokenizer.hasMoreTokens() ? eExpectingPort : eComplete;
8345 :
8346 0 : return;
8347 : }
8348 :
8349 : case eExpectingPort: {
8350 0 : MOZ_ASSERT(mSchemeType == eNone);
8351 :
8352 0 : if (aToken.IsEmpty()) {
8353 0 : QM_WARNING("Expected a port (not an empty string)!");
8354 :
8355 0 : mError = true;
8356 0 : return;
8357 : }
8358 :
8359 : // nsDependentCSubstring doesn't provice ToInteger()
8360 0 : nsCString token(aToken);
8361 :
8362 : nsresult rv;
8363 0 : uint32_t port = token.ToInteger(&rv);
8364 0 : if (NS_SUCCEEDED(rv)) {
8365 0 : mPort.SetValue() = port;
8366 : } else {
8367 0 : QM_WARNING("'%s' is not a valid port number!", token.get());
8368 :
8369 0 : mError = true;
8370 0 : return;
8371 : }
8372 :
8373 0 : mState = eComplete;
8374 :
8375 0 : return;
8376 : }
8377 :
8378 : case eExpectingEmptyTokenOrDriveLetterOrPathnameComponent: {
8379 0 : MOZ_ASSERT(mSchemeType == eFile);
8380 :
8381 0 : if (aToken.IsEmpty()) {
8382 0 : mPathnameComponents.AppendElement(EmptyCString());
8383 :
8384 0 : mState =
8385 0 : mTokenizer.hasMoreTokens() ? eExpectingEmptyTokenOrPathnameComponent
8386 : : eComplete;
8387 :
8388 0 : return;
8389 : }
8390 :
8391 0 : if (aToken.Length() == 1 && NS_IsAsciiAlpha(aToken.First())) {
8392 0 : mMaybeDriveLetter = true;
8393 :
8394 0 : mPathnameComponents.AppendElement(aToken);
8395 :
8396 0 : mState =
8397 0 : mTokenizer.hasMoreTokens() ? eExpectingEmptyTokenOrPathnameComponent
8398 : : eComplete;
8399 :
8400 0 : return;
8401 : }
8402 :
8403 0 : HandlePathnameComponent(aToken);
8404 :
8405 0 : return;
8406 : }
8407 :
8408 : case eExpectingEmptyTokenOrPathnameComponent: {
8409 0 : MOZ_ASSERT(mSchemeType == eFile);
8410 :
8411 0 : if (aToken.IsEmpty()) {
8412 0 : if (mMaybeDriveLetter) {
8413 0 : MOZ_ASSERT(mPathnameComponents.Length() == 1);
8414 :
8415 0 : nsCString& pathnameComponent = mPathnameComponents[0];
8416 0 : pathnameComponent.Append(':');
8417 :
8418 0 : mMaybeDriveLetter = false;
8419 : } else {
8420 0 : mPathnameComponents.AppendElement(EmptyCString());
8421 : }
8422 :
8423 0 : mState =
8424 0 : mTokenizer.hasMoreTokens() ? eExpectingEmptyTokenOrPathnameComponent
8425 : : eComplete;
8426 :
8427 0 : return;
8428 : }
8429 :
8430 0 : HandlePathnameComponent(aToken);
8431 :
8432 0 : return;
8433 : }
8434 :
8435 : default:
8436 0 : MOZ_CRASH("Should never get here!");
8437 : }
8438 : }
8439 :
8440 : void
8441 0 : OriginParser::HandleTrailingSeparator()
8442 : {
8443 0 : MOZ_ASSERT(mState == eComplete);
8444 0 : MOZ_ASSERT(mSchemeType == eFile);
8445 :
8446 0 : mPathnameComponents.AppendElement(EmptyCString());
8447 :
8448 0 : mState = eHandledTrailingSeparator;
8449 0 : }
8450 :
8451 : nsresult
8452 0 : CreateOrUpgradeDirectoryMetadataHelper::CreateOrUpgradeMetadataFiles()
8453 : {
8454 0 : AssertIsOnIOThread();
8455 :
8456 : bool exists;
8457 0 : nsresult rv = mDirectory->Exists(&exists);
8458 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8459 0 : return rv;
8460 : }
8461 :
8462 0 : if (!exists) {
8463 0 : return NS_OK;
8464 : }
8465 :
8466 0 : nsCOMPtr<nsISimpleEnumerator> entries;
8467 0 : rv = mDirectory->GetDirectoryEntries(getter_AddRefs(entries));
8468 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8469 0 : return rv;
8470 : }
8471 :
8472 : bool hasMore;
8473 0 : while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) {
8474 0 : nsCOMPtr<nsISupports> entry;
8475 0 : rv = entries->GetNext(getter_AddRefs(entry));
8476 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8477 0 : return rv;
8478 : }
8479 :
8480 0 : nsCOMPtr<nsIFile> originDir = do_QueryInterface(entry);
8481 0 : MOZ_ASSERT(originDir);
8482 :
8483 0 : nsString leafName;
8484 0 : rv = originDir->GetLeafName(leafName);
8485 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8486 0 : return rv;
8487 : }
8488 :
8489 : bool isDirectory;
8490 0 : rv = originDir->IsDirectory(&isDirectory);
8491 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8492 0 : return rv;
8493 : }
8494 :
8495 0 : if (isDirectory) {
8496 0 : if (leafName.EqualsLiteral("moz-safe-about+++home")) {
8497 : // This directory was accidentally created by a buggy nightly and can
8498 : // be safely removed.
8499 :
8500 0 : QM_WARNING("Deleting accidental moz-safe-about+++home directory!");
8501 :
8502 0 : rv = originDir->Remove(/* aRecursive */ true);
8503 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8504 0 : return rv;
8505 : }
8506 :
8507 0 : continue;
8508 : }
8509 : } else {
8510 : // Unknown files during upgrade are allowed. Just warn if we find them.
8511 0 : if (!IsOSMetadata(leafName)) {
8512 0 : UNKNOWN_FILE_WARNING(leafName);
8513 : }
8514 0 : continue;
8515 : }
8516 :
8517 0 : if (mPersistent) {
8518 0 : rv = MaybeUpgradeOriginDirectory(originDir);
8519 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8520 0 : return rv;
8521 : }
8522 : }
8523 :
8524 0 : OriginProps originProps;
8525 0 : rv = originProps.Init(originDir);
8526 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8527 0 : return rv;
8528 : }
8529 :
8530 0 : if (!mPersistent) {
8531 : int64_t timestamp;
8532 0 : nsCString group;
8533 0 : nsCString origin;
8534 0 : Nullable<bool> isApp;
8535 0 : rv = GetDirectoryMetadata(originDir,
8536 : timestamp,
8537 : group,
8538 : origin,
8539 0 : isApp);
8540 0 : if (NS_FAILED(rv)) {
8541 0 : originProps.mTimestamp = GetLastModifiedTime(originDir, mPersistent);
8542 0 : originProps.mNeedsRestore = true;
8543 0 : } else if (!isApp.IsNull()) {
8544 0 : originProps.mIgnore = true;
8545 : }
8546 : }
8547 : else {
8548 0 : bool persistent = QuotaManager::IsOriginInternal(originProps.mSpec);
8549 0 : originProps.mTimestamp = GetLastModifiedTime(originDir, persistent);
8550 : }
8551 :
8552 0 : mOriginProps.AppendElement(Move(originProps));
8553 : }
8554 :
8555 0 : if (mOriginProps.IsEmpty()) {
8556 0 : return NS_OK;
8557 : }
8558 :
8559 0 : rv = ProcessOriginDirectories();
8560 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8561 0 : return rv;
8562 : }
8563 :
8564 0 : return NS_OK;
8565 : }
8566 :
8567 : nsresult
8568 0 : CreateOrUpgradeDirectoryMetadataHelper::MaybeUpgradeOriginDirectory(
8569 : nsIFile* aDirectory)
8570 : {
8571 0 : AssertIsOnIOThread();
8572 0 : MOZ_ASSERT(aDirectory);
8573 :
8574 0 : nsCOMPtr<nsIFile> metadataFile;
8575 0 : nsresult rv = aDirectory->Clone(getter_AddRefs(metadataFile));
8576 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8577 0 : return rv;
8578 : }
8579 :
8580 0 : rv = metadataFile->Append(NS_LITERAL_STRING(METADATA_FILE_NAME));
8581 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8582 0 : return rv;
8583 : }
8584 :
8585 : bool exists;
8586 0 : rv = metadataFile->Exists(&exists);
8587 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8588 0 : return rv;
8589 : }
8590 :
8591 0 : if (!exists) {
8592 : // Directory structure upgrade needed.
8593 : // Move all files to IDB specific directory.
8594 :
8595 0 : nsString idbDirectoryName;
8596 0 : rv = Client::TypeToText(Client::IDB, idbDirectoryName);
8597 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8598 0 : return rv;
8599 : }
8600 :
8601 0 : nsCOMPtr<nsIFile> idbDirectory;
8602 0 : rv = aDirectory->Clone(getter_AddRefs(idbDirectory));
8603 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8604 0 : return rv;
8605 : }
8606 :
8607 0 : rv = idbDirectory->Append(idbDirectoryName);
8608 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8609 0 : return rv;
8610 : }
8611 :
8612 0 : rv = idbDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755);
8613 0 : if (rv == NS_ERROR_FILE_ALREADY_EXISTS) {
8614 0 : NS_WARNING("IDB directory already exists!");
8615 :
8616 : bool isDirectory;
8617 0 : rv = idbDirectory->IsDirectory(&isDirectory);
8618 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8619 0 : return rv;
8620 : }
8621 :
8622 0 : if (NS_WARN_IF(!isDirectory)) {
8623 0 : return NS_ERROR_UNEXPECTED;
8624 : }
8625 : }
8626 : else {
8627 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8628 0 : return rv;
8629 : }
8630 : }
8631 :
8632 0 : nsCOMPtr<nsISimpleEnumerator> entries;
8633 0 : rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries));
8634 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8635 0 : return rv;
8636 : }
8637 :
8638 : bool hasMore;
8639 0 : while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) {
8640 0 : nsCOMPtr<nsISupports> entry;
8641 0 : rv = entries->GetNext(getter_AddRefs(entry));
8642 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8643 0 : return rv;
8644 : }
8645 :
8646 0 : nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
8647 0 : if (NS_WARN_IF(!file)) {
8648 0 : return rv;
8649 : }
8650 :
8651 0 : nsString leafName;
8652 0 : rv = file->GetLeafName(leafName);
8653 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8654 0 : return rv;
8655 : }
8656 :
8657 0 : if (!leafName.Equals(idbDirectoryName)) {
8658 0 : rv = file->MoveTo(idbDirectory, EmptyString());
8659 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8660 0 : return rv;
8661 : }
8662 : }
8663 : }
8664 :
8665 0 : rv = metadataFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
8666 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8667 0 : return rv;
8668 : }
8669 : }
8670 :
8671 0 : return NS_OK;
8672 : }
8673 :
8674 : nsresult
8675 0 : CreateOrUpgradeDirectoryMetadataHelper::ProcessOriginDirectory(
8676 : const OriginProps& aOriginProps)
8677 : {
8678 0 : AssertIsOnIOThread();
8679 :
8680 : nsresult rv;
8681 :
8682 0 : if (mPersistent) {
8683 0 : rv = CreateDirectoryMetadata(aOriginProps.mDirectory,
8684 0 : aOriginProps.mTimestamp,
8685 : aOriginProps.mSuffix,
8686 : aOriginProps.mGroup,
8687 : aOriginProps.mOrigin);
8688 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8689 0 : return rv;
8690 : }
8691 :
8692 : // Move internal origins to new persistent storage.
8693 0 : if (QuotaManager::IsOriginInternal(aOriginProps.mSpec)) {
8694 0 : if (!mPermanentStorageDir) {
8695 : mPermanentStorageDir =
8696 0 : do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
8697 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8698 0 : return rv;
8699 : }
8700 :
8701 0 : QuotaManager* quotaManager = QuotaManager::Get();
8702 0 : MOZ_ASSERT(quotaManager);
8703 :
8704 : const nsString& permanentStoragePath =
8705 0 : quotaManager->GetStoragePath(PERSISTENCE_TYPE_PERSISTENT);
8706 :
8707 0 : rv = mPermanentStorageDir->InitWithPath(permanentStoragePath);
8708 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8709 0 : return rv;
8710 : }
8711 : }
8712 :
8713 0 : nsString leafName;
8714 0 : rv = aOriginProps.mDirectory->GetLeafName(leafName);
8715 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8716 0 : return rv;
8717 : }
8718 :
8719 0 : nsCOMPtr<nsIFile> newDirectory;
8720 0 : rv = mPermanentStorageDir->Clone(getter_AddRefs(newDirectory));
8721 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8722 0 : return rv;
8723 : }
8724 :
8725 0 : rv = newDirectory->Append(leafName);
8726 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8727 0 : return rv;
8728 : }
8729 :
8730 : bool exists;
8731 0 : rv = newDirectory->Exists(&exists);
8732 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8733 0 : return rv;
8734 : }
8735 :
8736 0 : if (exists) {
8737 0 : QM_WARNING("Found %s in storage/persistent and storage/permanent !",
8738 : NS_ConvertUTF16toUTF8(leafName).get());
8739 :
8740 0 : rv = aOriginProps.mDirectory->Remove(/* recursive */ true);
8741 : } else {
8742 0 : rv = aOriginProps.mDirectory->MoveTo(mPermanentStorageDir,
8743 0 : EmptyString());
8744 : }
8745 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8746 0 : return rv;
8747 : }
8748 : }
8749 0 : } else if (aOriginProps.mNeedsRestore) {
8750 0 : rv = CreateDirectoryMetadata(aOriginProps.mDirectory,
8751 0 : aOriginProps.mTimestamp,
8752 : aOriginProps.mSuffix,
8753 : aOriginProps.mGroup,
8754 : aOriginProps.mOrigin);
8755 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8756 0 : return rv;
8757 : }
8758 0 : } else if (!aOriginProps.mIgnore) {
8759 0 : nsCOMPtr<nsIFile> file;
8760 0 : rv = aOriginProps.mDirectory->Clone(getter_AddRefs(file));
8761 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8762 0 : return rv;
8763 : }
8764 :
8765 0 : rv = file->Append(NS_LITERAL_STRING(METADATA_FILE_NAME));
8766 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8767 0 : return rv;
8768 : }
8769 :
8770 0 : nsCOMPtr<nsIBinaryOutputStream> stream;
8771 0 : rv = GetBinaryOutputStream(file, kAppendFileFlag, getter_AddRefs(stream));
8772 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8773 0 : return rv;
8774 : }
8775 :
8776 0 : MOZ_ASSERT(stream);
8777 :
8778 : // Currently unused (used to be isApp).
8779 0 : rv = stream->WriteBoolean(false);
8780 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8781 0 : return rv;
8782 : }
8783 : }
8784 :
8785 0 : return NS_OK;
8786 : }
8787 :
8788 : nsresult
8789 0 : UpgradeStorageFrom0_0To1_0Helper::DoUpgrade()
8790 : {
8791 0 : AssertIsOnIOThread();
8792 :
8793 : bool exists;
8794 0 : nsresult rv = mDirectory->Exists(&exists);
8795 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8796 0 : return rv;
8797 : }
8798 :
8799 0 : if (!exists) {
8800 0 : return NS_OK;
8801 : }
8802 :
8803 0 : nsCOMPtr<nsISimpleEnumerator> entries;
8804 0 : rv = mDirectory->GetDirectoryEntries(getter_AddRefs(entries));
8805 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8806 0 : return rv;
8807 : }
8808 :
8809 : bool hasMore;
8810 0 : while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) {
8811 0 : nsCOMPtr<nsISupports> entry;
8812 0 : rv = entries->GetNext(getter_AddRefs(entry));
8813 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8814 0 : return rv;
8815 : }
8816 :
8817 0 : nsCOMPtr<nsIFile> originDir = do_QueryInterface(entry);
8818 0 : MOZ_ASSERT(originDir);
8819 :
8820 : bool isDirectory;
8821 0 : rv = originDir->IsDirectory(&isDirectory);
8822 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8823 0 : return rv;
8824 : }
8825 :
8826 0 : if (!isDirectory) {
8827 0 : nsString leafName;
8828 0 : rv = originDir->GetLeafName(leafName);
8829 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8830 0 : return rv;
8831 : }
8832 :
8833 : // Unknown files during upgrade are allowed. Just warn if we find them.
8834 0 : if (!IsOSMetadata(leafName)) {
8835 0 : UNKNOWN_FILE_WARNING(leafName);
8836 : }
8837 0 : continue;
8838 : }
8839 :
8840 0 : OriginProps originProps;
8841 0 : rv = originProps.Init(originDir);
8842 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8843 0 : return rv;
8844 : }
8845 :
8846 : int64_t timestamp;
8847 0 : nsCString group;
8848 0 : nsCString origin;
8849 0 : Nullable<bool> isApp;
8850 0 : nsresult rv = GetDirectoryMetadata(originDir,
8851 : timestamp,
8852 : group,
8853 : origin,
8854 0 : isApp);
8855 0 : if (NS_FAILED(rv) || isApp.IsNull()) {
8856 0 : originProps.mTimestamp = GetLastModifiedTime(originDir, mPersistent);
8857 0 : originProps.mNeedsRestore = true;
8858 : } else {
8859 0 : originProps.mTimestamp = timestamp;
8860 : }
8861 :
8862 0 : mOriginProps.AppendElement(Move(originProps));
8863 : }
8864 :
8865 0 : if (mOriginProps.IsEmpty()) {
8866 0 : return NS_OK;
8867 : }
8868 :
8869 0 : rv = ProcessOriginDirectories();
8870 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8871 0 : return rv;
8872 : }
8873 :
8874 0 : return NS_OK;
8875 : }
8876 :
8877 : nsresult
8878 0 : UpgradeStorageFrom0_0To1_0Helper::ProcessOriginDirectory(
8879 : const OriginProps& aOriginProps)
8880 : {
8881 0 : AssertIsOnIOThread();
8882 :
8883 : nsresult rv;
8884 :
8885 0 : if (aOriginProps.mNeedsRestore) {
8886 0 : rv = CreateDirectoryMetadata(aOriginProps.mDirectory,
8887 0 : aOriginProps.mTimestamp,
8888 : aOriginProps.mSuffix,
8889 : aOriginProps.mGroup,
8890 0 : aOriginProps.mOrigin);
8891 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8892 0 : return rv;
8893 : }
8894 : }
8895 :
8896 0 : rv = CreateDirectoryMetadata2(aOriginProps.mDirectory,
8897 0 : aOriginProps.mTimestamp,
8898 : /* aPersisted */ false,
8899 : aOriginProps.mSuffix,
8900 : aOriginProps.mGroup,
8901 0 : aOriginProps.mOrigin);
8902 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8903 0 : return rv;
8904 : }
8905 :
8906 0 : nsString oldName;
8907 0 : rv = aOriginProps.mDirectory->GetLeafName(oldName);
8908 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8909 0 : return rv;
8910 : }
8911 :
8912 0 : nsAutoCString originSanitized(aOriginProps.mOrigin);
8913 0 : SanitizeOriginString(originSanitized);
8914 :
8915 0 : NS_ConvertASCIItoUTF16 newName(originSanitized);
8916 :
8917 0 : if (!oldName.Equals(newName)) {
8918 0 : rv = aOriginProps.mDirectory->RenameTo(nullptr, newName);
8919 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8920 0 : return rv;
8921 : }
8922 : }
8923 :
8924 0 : return NS_OK;
8925 : }
8926 :
8927 : nsresult
8928 0 : UpgradeStorageFrom1_0To2_0Helper::DoUpgrade()
8929 : {
8930 0 : AssertIsOnIOThread();
8931 :
8932 0 : DebugOnly<bool> exists;
8933 0 : MOZ_ASSERT(NS_SUCCEEDED(mDirectory->Exists(&exists)));
8934 0 : MOZ_ASSERT(exists);
8935 :
8936 0 : nsCOMPtr<nsISimpleEnumerator> entries;
8937 0 : nsresult rv = mDirectory->GetDirectoryEntries(getter_AddRefs(entries));
8938 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8939 0 : return rv;
8940 : }
8941 :
8942 : bool hasMore;
8943 0 : while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) {
8944 0 : nsCOMPtr<nsISupports> entry;
8945 0 : rv = entries->GetNext(getter_AddRefs(entry));
8946 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8947 0 : return rv;
8948 : }
8949 :
8950 0 : nsCOMPtr<nsIFile> originDir = do_QueryInterface(entry);
8951 0 : MOZ_ASSERT(originDir);
8952 :
8953 : bool isDirectory;
8954 0 : rv = originDir->IsDirectory(&isDirectory);
8955 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8956 0 : return rv;
8957 : }
8958 :
8959 0 : if (!isDirectory) {
8960 0 : nsString leafName;
8961 0 : rv = originDir->GetLeafName(leafName);
8962 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8963 0 : return rv;
8964 : }
8965 :
8966 : // Unknown files during upgrade are allowed. Just warn if we find them.
8967 0 : if (!IsOSMetadata(leafName)) {
8968 0 : UNKNOWN_FILE_WARNING(leafName);
8969 : }
8970 0 : continue;
8971 : }
8972 :
8973 0 : OriginProps originProps;
8974 0 : rv = originProps.Init(originDir);
8975 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8976 0 : return rv;
8977 : }
8978 :
8979 0 : rv = MaybeUpgradeClients(originProps);
8980 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8981 0 : return rv;
8982 : }
8983 :
8984 : bool removed;
8985 0 : rv = MaybeRemoveAppsData(originProps, &removed);
8986 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
8987 0 : return rv;
8988 : }
8989 0 : if (removed) {
8990 0 : continue;
8991 : }
8992 :
8993 : int64_t timestamp;
8994 0 : nsCString group;
8995 0 : nsCString origin;
8996 0 : Nullable<bool> isApp;
8997 0 : nsresult rv = GetDirectoryMetadata(originDir,
8998 : timestamp,
8999 : group,
9000 : origin,
9001 0 : isApp);
9002 0 : if (NS_FAILED(rv) || isApp.IsNull()) {
9003 0 : originProps.mNeedsRestore = true;
9004 : }
9005 :
9006 0 : nsCString suffix;
9007 0 : rv = GetDirectoryMetadata2(originDir,
9008 : timestamp,
9009 : suffix,
9010 : group,
9011 : origin,
9012 0 : isApp.SetValue());
9013 0 : if (NS_FAILED(rv)) {
9014 0 : originProps.mTimestamp = GetLastModifiedTime(originDir, mPersistent);
9015 0 : originProps.mNeedsRestore2 = true;
9016 : } else {
9017 0 : originProps.mTimestamp = timestamp;
9018 : }
9019 :
9020 0 : mOriginProps.AppendElement(Move(originProps));
9021 : }
9022 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
9023 0 : return rv;
9024 : }
9025 :
9026 0 : if (mOriginProps.IsEmpty()) {
9027 0 : return NS_OK;
9028 : }
9029 :
9030 0 : rv = ProcessOriginDirectories();
9031 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
9032 0 : return rv;
9033 : }
9034 :
9035 0 : return NS_OK;
9036 : }
9037 :
9038 : nsresult
9039 0 : UpgradeStorageFrom1_0To2_0Helper::MaybeUpgradeClients(
9040 : const OriginProps& aOriginProps)
9041 : {
9042 0 : AssertIsOnIOThread();
9043 0 : MOZ_ASSERT(aOriginProps.mDirectory);
9044 :
9045 0 : QuotaManager* quotaManager = QuotaManager::Get();
9046 0 : MOZ_ASSERT(quotaManager);
9047 :
9048 0 : nsCOMPtr<nsISimpleEnumerator> entries;
9049 : nsresult rv =
9050 0 : aOriginProps.mDirectory->GetDirectoryEntries(getter_AddRefs(entries));
9051 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
9052 0 : return rv;
9053 : }
9054 :
9055 : bool hasMore;
9056 0 : while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) {
9057 0 : nsCOMPtr<nsISupports> entry;
9058 0 : rv = entries->GetNext(getter_AddRefs(entry));
9059 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
9060 0 : return rv;
9061 : }
9062 :
9063 0 : nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
9064 0 : if (NS_WARN_IF(!file)) {
9065 0 : return rv;
9066 : }
9067 :
9068 : bool isDirectory;
9069 0 : rv = file->IsDirectory(&isDirectory);
9070 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
9071 0 : return rv;
9072 : }
9073 :
9074 0 : nsString leafName;
9075 0 : rv = file->GetLeafName(leafName);
9076 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
9077 0 : return rv;
9078 : }
9079 :
9080 0 : if (!isDirectory) {
9081 : // Unknown files during upgrade are allowed. Just warn if we find them.
9082 0 : if (!IsOriginMetadata(leafName) &&
9083 0 : !IsTempMetadata(leafName)) {
9084 0 : UNKNOWN_FILE_WARNING(leafName);
9085 : }
9086 0 : continue;
9087 : }
9088 :
9089 : // The Cache API was creating top level morgue directories by accident for
9090 : // a short time in nightly. This unfortunately prevents all storage from
9091 : // working. So recover these profiles permanently by removing these corrupt
9092 : // directories as part of this upgrade.
9093 0 : if (leafName.EqualsLiteral("morgue")) {
9094 0 : QM_WARNING("Deleting accidental morgue directory!");
9095 :
9096 0 : rv = file->Remove(/* recursive */ true);
9097 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
9098 0 : return rv;
9099 : }
9100 :
9101 0 : continue;
9102 : }
9103 :
9104 : Client::Type clientType;
9105 0 : rv = Client::TypeFromText(leafName, clientType);
9106 0 : if (NS_FAILED(rv)) {
9107 0 : UNKNOWN_FILE_WARNING(leafName);
9108 0 : continue;
9109 : }
9110 :
9111 0 : Client* client = quotaManager->GetClient(clientType);
9112 0 : MOZ_ASSERT(client);
9113 :
9114 0 : rv = client->UpgradeStorageFrom1_0To2_0(file);
9115 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
9116 0 : return rv;
9117 : }
9118 : }
9119 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
9120 0 : return rv;
9121 : }
9122 :
9123 0 : return NS_OK;
9124 : }
9125 :
9126 : nsresult
9127 0 : UpgradeStorageFrom1_0To2_0Helper::MaybeRemoveAppsData(
9128 : const OriginProps& aOriginProps,
9129 : bool* aRemoved)
9130 : {
9131 0 : AssertIsOnIOThread();
9132 :
9133 : // XXX This will need to be reworked as part of bug 1320404 (appId is
9134 : // going to be removed from origin attributes).
9135 0 : if (aOriginProps.mAttrs.mAppId != kNoAppId &&
9136 0 : aOriginProps.mAttrs.mAppId != kUnknownAppId) {
9137 0 : nsresult rv = RemoveObsoleteOrigin(aOriginProps);
9138 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
9139 0 : return rv;
9140 : }
9141 :
9142 0 : *aRemoved = true;
9143 0 : return NS_OK;
9144 : }
9145 :
9146 0 : *aRemoved = false;
9147 0 : return NS_OK;
9148 : }
9149 :
9150 : nsresult
9151 0 : UpgradeStorageFrom1_0To2_0Helper::MaybeStripObsoleteOriginAttributes(
9152 : const OriginProps& aOriginProps,
9153 : bool* aStripped)
9154 : {
9155 0 : AssertIsOnIOThread();
9156 0 : MOZ_ASSERT(aOriginProps.mDirectory);
9157 :
9158 0 : const nsAString& oldLeafName = aOriginProps.mLeafName;
9159 :
9160 0 : nsCString originSanitized(aOriginProps.mOrigin);
9161 0 : SanitizeOriginString(originSanitized);
9162 :
9163 0 : NS_ConvertUTF8toUTF16 newLeafName(originSanitized);
9164 :
9165 0 : if (oldLeafName == newLeafName) {
9166 0 : *aStripped = false;
9167 0 : return NS_OK;
9168 : }
9169 :
9170 0 : nsresult rv = CreateDirectoryMetadata(aOriginProps.mDirectory,
9171 0 : aOriginProps.mTimestamp,
9172 : aOriginProps.mSuffix,
9173 : aOriginProps.mGroup,
9174 0 : aOriginProps.mOrigin);
9175 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
9176 0 : return rv;
9177 : }
9178 :
9179 0 : rv = CreateDirectoryMetadata2(aOriginProps.mDirectory,
9180 0 : aOriginProps.mTimestamp,
9181 : /* aPersisted */ false,
9182 : aOriginProps.mSuffix,
9183 : aOriginProps.mGroup,
9184 0 : aOriginProps.mOrigin);
9185 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
9186 0 : return rv;
9187 : }
9188 :
9189 0 : nsCOMPtr<nsIFile> newFile;
9190 0 : rv = aOriginProps.mDirectory->GetParent(getter_AddRefs(newFile));
9191 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
9192 0 : return rv;
9193 : }
9194 :
9195 0 : rv = newFile->Append(newLeafName);
9196 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
9197 0 : return rv;
9198 : }
9199 :
9200 : bool exists;
9201 0 : rv = newFile->Exists(&exists);
9202 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
9203 0 : return rv;
9204 : }
9205 :
9206 0 : if (exists) {
9207 0 : QM_WARNING("Can't rename %s directory, %s directory already exists, "
9208 : "removing!",
9209 : NS_ConvertUTF16toUTF8(oldLeafName).get(),
9210 : NS_ConvertUTF16toUTF8(newLeafName).get());
9211 :
9212 0 : rv = aOriginProps.mDirectory->Remove(/* recursive */ true);
9213 : } else {
9214 0 : rv = aOriginProps.mDirectory->RenameTo(nullptr, newLeafName);
9215 : }
9216 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
9217 0 : return rv;
9218 : }
9219 :
9220 0 : *aStripped = true;
9221 0 : return NS_OK;
9222 : }
9223 :
9224 : nsresult
9225 0 : UpgradeStorageFrom1_0To2_0Helper::ProcessOriginDirectory(
9226 : const OriginProps& aOriginProps)
9227 : {
9228 0 : AssertIsOnIOThread();
9229 :
9230 : bool stripped;
9231 0 : nsresult rv = MaybeStripObsoleteOriginAttributes(aOriginProps, &stripped);
9232 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
9233 0 : return rv;
9234 : }
9235 0 : if (stripped) {
9236 0 : return NS_OK;
9237 : }
9238 :
9239 0 : if (aOriginProps.mNeedsRestore) {
9240 0 : rv = CreateDirectoryMetadata(aOriginProps.mDirectory,
9241 0 : aOriginProps.mTimestamp,
9242 : aOriginProps.mSuffix,
9243 : aOriginProps.mGroup,
9244 0 : aOriginProps.mOrigin);
9245 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
9246 0 : return rv;
9247 : }
9248 : }
9249 :
9250 0 : if (aOriginProps.mNeedsRestore2) {
9251 0 : rv = CreateDirectoryMetadata2(aOriginProps.mDirectory,
9252 0 : aOriginProps.mTimestamp,
9253 : /* aPersisted */ false,
9254 : aOriginProps.mSuffix,
9255 : aOriginProps.mGroup,
9256 0 : aOriginProps.mOrigin);
9257 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
9258 0 : return rv;
9259 : }
9260 : }
9261 :
9262 0 : return NS_OK;
9263 : }
9264 :
9265 : nsresult
9266 0 : RestoreDirectoryMetadata2Helper::RestoreMetadata2File()
9267 : {
9268 0 : AssertIsOnIOThread();
9269 :
9270 : nsresult rv;
9271 :
9272 0 : OriginProps originProps;
9273 0 : rv = originProps.Init(mDirectory);
9274 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
9275 0 : return rv;
9276 : }
9277 :
9278 0 : originProps.mTimestamp = GetLastModifiedTime(mDirectory, mPersistent);
9279 :
9280 0 : mOriginProps.AppendElement(Move(originProps));
9281 :
9282 0 : rv = ProcessOriginDirectories();
9283 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
9284 0 : return rv;
9285 : }
9286 :
9287 0 : return NS_OK;
9288 : }
9289 :
9290 : nsresult
9291 0 : RestoreDirectoryMetadata2Helper::ProcessOriginDirectory(
9292 : const OriginProps& aOriginProps)
9293 : {
9294 0 : AssertIsOnIOThread();
9295 :
9296 : // We don't have any approach to restore aPersisted, so reset it to false.
9297 0 : nsresult rv = CreateDirectoryMetadata2(aOriginProps.mDirectory,
9298 0 : aOriginProps.mTimestamp,
9299 : /* aPersisted */ false,
9300 : aOriginProps.mSuffix,
9301 : aOriginProps.mGroup,
9302 0 : aOriginProps.mOrigin);
9303 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
9304 0 : return rv;
9305 : }
9306 :
9307 0 : return NS_OK;
9308 : }
9309 :
9310 : } // namespace quota
9311 : } // namespace dom
9312 : } // namespace mozilla
|