Coverage Report

Created: 2018-09-25 14:53

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