tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

FileSystemDataManager.cpp (26890B)


      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
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "FileSystemDataManager.h"
      8 
      9 #include "ErrorList.h"
     10 #include "FileSystemDatabaseManager.h"
     11 #include "FileSystemDatabaseManagerVersion001.h"
     12 #include "FileSystemDatabaseManagerVersion002.h"
     13 #include "FileSystemFileManager.h"
     14 #include "FileSystemHashSource.h"
     15 #include "FileSystemParentTypes.h"
     16 #include "NotifyUtils.h"
     17 #include "ResultStatement.h"
     18 #include "SchemaVersion001.h"
     19 #include "SchemaVersion002.h"
     20 #include "fs/FileSystemConstants.h"
     21 #include "mozIStorageService.h"
     22 #include "mozStorageCID.h"
     23 #include "mozilla/Result.h"
     24 #include "mozilla/StaticPtr.h"
     25 #include "mozilla/dom/FileSystemLog.h"
     26 #include "mozilla/dom/FileSystemManagerParent.h"
     27 #include "mozilla/dom/QMResult.h"
     28 #include "mozilla/dom/quota/ClientDirectoryLock.h"
     29 #include "mozilla/dom/quota/ClientImpl.h"
     30 #include "mozilla/dom/quota/HashKeys.h"
     31 #include "mozilla/dom/quota/QuotaCommon.h"
     32 #include "mozilla/dom/quota/QuotaManager.h"
     33 #include "mozilla/dom/quota/ResultExtensions.h"
     34 #include "mozilla/dom/quota/ThreadUtils.h"
     35 #include "mozilla/dom/quota/UsageInfo.h"
     36 #include "mozilla/ipc/BackgroundParent.h"
     37 #include "nsBaseHashtable.h"
     38 #include "nsCOMPtr.h"
     39 #include "nsHashKeys.h"
     40 #include "nsIFile.h"
     41 #include "nsIFileURL.h"
     42 #include "nsNetCID.h"
     43 #include "nsServiceManagerUtils.h"
     44 #include "nsString.h"
     45 #include "nsThreadUtils.h"
     46 
     47 namespace mozilla::dom::fs::data {
     48 
     49 namespace {
     50 
     51 // When CheckedUnsafePtr's checking is enabled, it's necessary to ensure that
     52 // the hashtable uses the copy constructor instead of memmove for moving entries
     53 // since memmove will break CheckedUnsafePtr in a memory-corrupting way.
     54 
     55 // The assertion type must be the same as the assertion type used for defining
     56 // the base class for FileSystemDataManager in FileSystemDataManager.h!
     57 using FileSystemDataManagerHashKey =
     58    std::conditional<ReleaseAssertEnabled::value,
     59                     quota::nsCStringHashKeyWithDisabledMemmove,
     60                     nsCStringHashKey>::type;
     61 
     62 // Raw (but checked when the diagnostic assert is enabled) references as we
     63 // don't want to keep FileSystemDataManager objects alive forever. When a
     64 // FileSystemDataManager is destroyed it calls RemoveFileSystemDataManager
     65 // to clear itself.
     66 using FileSystemDataManagerHashtable =
     67    nsBaseHashtable<FileSystemDataManagerHashKey,
     68                    NotNull<CheckedUnsafePtr<FileSystemDataManager>>,
     69                    MovingNotNull<CheckedUnsafePtr<FileSystemDataManager>>>;
     70 
     71 // This hashtable isn't protected by any mutex but it is only ever touched on
     72 // the PBackground thread.
     73 StaticAutoPtr<FileSystemDataManagerHashtable> gDataManagers;
     74 
     75 RefPtr<FileSystemDataManager> GetFileSystemDataManager(const Origin& aOrigin) {
     76  ::mozilla::ipc::AssertIsOnBackgroundThread();
     77 
     78  if (gDataManagers) {
     79    auto maybeDataManager = gDataManagers->MaybeGet(aOrigin);
     80    if (maybeDataManager) {
     81      RefPtr<FileSystemDataManager> result(
     82          std::move(*maybeDataManager).unwrapBasePtr());
     83      return result;
     84    }
     85  }
     86 
     87  return nullptr;
     88 }
     89 
     90 void AddFileSystemDataManager(
     91    const Origin& aOrigin, const RefPtr<FileSystemDataManager>& aDataManager) {
     92  ::mozilla::ipc::AssertIsOnBackgroundThread();
     93  MOZ_ASSERT(!quota::QuotaManager::IsShuttingDown());
     94 
     95  if (!gDataManagers) {
     96    gDataManagers = new FileSystemDataManagerHashtable();
     97  }
     98 
     99  MOZ_ASSERT(!gDataManagers->Contains(aOrigin));
    100  gDataManagers->InsertOrUpdate(aOrigin,
    101                                WrapMovingNotNullUnchecked(aDataManager));
    102 }
    103 
    104 void RemoveFileSystemDataManager(const Origin& aOrigin) {
    105  ::mozilla::ipc::AssertIsOnBackgroundThread();
    106 
    107  MOZ_ASSERT(gDataManagers);
    108  const DebugOnly<bool> removed = gDataManagers->Remove(aOrigin);
    109  MOZ_ASSERT(removed);
    110 
    111  if (!gDataManagers->Count()) {
    112    gDataManagers = nullptr;
    113  }
    114 }
    115 
    116 }  // namespace
    117 
    118 Result<ResultConnection, QMResult> GetStorageConnection(
    119    const quota::OriginMetadata& aOriginMetadata,
    120    const int64_t aDirectoryLockId) {
    121  MOZ_ASSERT(aDirectoryLockId >= -1);
    122 
    123  // Ensure that storage is initialized and file system folder exists!
    124  QM_TRY_INSPECT(const auto& dbFileUrl,
    125                 GetDatabaseFileURL(aOriginMetadata, aDirectoryLockId));
    126 
    127  QM_TRY_INSPECT(
    128      const auto& storageService,
    129      QM_TO_RESULT_TRANSFORM(MOZ_TO_RESULT_GET_TYPED(
    130          nsCOMPtr<mozIStorageService>, MOZ_SELECT_OVERLOAD(do_GetService),
    131          MOZ_STORAGE_SERVICE_CONTRACTID)));
    132 
    133  QM_TRY_UNWRAP(auto connection,
    134                QM_TO_RESULT_TRANSFORM(MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
    135                    nsCOMPtr<mozIStorageConnection>, storageService,
    136                    OpenDatabaseWithFileURL, dbFileUrl, ""_ns,
    137                    mozIStorageService::CONNECTION_DEFAULT)));
    138 
    139  ResultConnection result(connection);
    140 
    141  return result;
    142 }
    143 
    144 Result<EntryId, QMResult> GetRootHandle(const Origin& origin) {
    145  MOZ_ASSERT(!origin.IsEmpty());
    146 
    147  return FileSystemHashSource::GenerateHash(origin, kRootString);
    148 }
    149 
    150 Result<EntryId, QMResult> GetEntryHandle(
    151    const FileSystemChildMetadata& aHandle) {
    152  MOZ_ASSERT(!aHandle.parentId().IsEmpty());
    153 
    154  return FileSystemHashSource::GenerateHash(aHandle.parentId(),
    155                                            aHandle.childName());
    156 }
    157 
    158 FileSystemDataManager::FileSystemDataManager(
    159    const quota::OriginMetadata& aOriginMetadata,
    160    RefPtr<quota::QuotaManager> aQuotaManager,
    161    MovingNotNull<nsCOMPtr<nsIEventTarget>> aIOTarget,
    162    MovingNotNull<RefPtr<TaskQueue>> aIOTaskQueue)
    163    : mOriginMetadata(aOriginMetadata),
    164      mQuotaManager(std::move(aQuotaManager)),
    165      mBackgroundTarget(WrapNotNull(GetCurrentSerialEventTarget())),
    166      mIOTarget(std::move(aIOTarget)),
    167      mIOTaskQueue(std::move(aIOTaskQueue)),
    168      mDirectoryLockId(-1),
    169      mRegCount(0),
    170      mVersion(0),
    171      mState(State::Initial) {}
    172 
    173 FileSystemDataManager::~FileSystemDataManager() {
    174  NS_ASSERT_OWNINGTHREAD(FileSystemDataManager);
    175  MOZ_ASSERT(mState == State::Closed);
    176  MOZ_ASSERT(!mDatabaseManager);
    177 }
    178 
    179 RefPtr<FileSystemDataManager::CreatePromise>
    180 FileSystemDataManager::GetOrCreateFileSystemDataManager(
    181    const quota::OriginMetadata& aOriginMetadata) {
    182  if (quota::QuotaManager::IsShuttingDown()) {
    183    return CreatePromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
    184  }
    185 
    186  if (RefPtr<FileSystemDataManager> dataManager =
    187          GetFileSystemDataManager(aOriginMetadata.mOrigin)) {
    188    if (dataManager->IsOpening()) {
    189      // We have to wait for the open to be finished before resolving the
    190      // promise. The manager can't close itself in the meantime because we
    191      // add a new registration in the lambda capture list.
    192      return dataManager->OnOpen()->Then(
    193          GetCurrentSerialEventTarget(), __func__,
    194          [dataManager = Registered<FileSystemDataManager>(dataManager)](
    195              const BoolPromise::ResolveOrRejectValue& aValue) {
    196            if (aValue.IsReject()) {
    197              return CreatePromise::CreateAndReject(aValue.RejectValue(),
    198                                                    __func__);
    199            }
    200            return CreatePromise::CreateAndResolve(dataManager, __func__);
    201          });
    202    }
    203 
    204    if (dataManager->IsClosing()) {
    205      // First, we need to wait for the close to be finished. After that the
    206      // manager is closed and it can't be opened again. The only option is
    207      // to create a new manager and open it. However, all this stuff is
    208      // asynchronous, so it can happen that something else called
    209      // `GetOrCreateFileSystemManager` in the meantime. For that reason, we
    210      // shouldn't try to create a new manager and open it here, a "recursive"
    211      // call to `GetOrCreateFileSystemManager` will handle any new situation.
    212      return dataManager->OnClose()->Then(
    213          GetCurrentSerialEventTarget(), __func__,
    214          [aOriginMetadata](const BoolPromise::ResolveOrRejectValue&) {
    215            return GetOrCreateFileSystemDataManager(aOriginMetadata);
    216          });
    217    }
    218 
    219    return CreatePromise::CreateAndResolve(
    220        Registered<FileSystemDataManager>(std::move(dataManager)), __func__);
    221  }
    222 
    223  RefPtr<quota::QuotaManager> quotaManager = quota::QuotaManager::Get();
    224  MOZ_ASSERT(quotaManager);
    225 
    226  QM_TRY_UNWRAP(auto streamTransportService,
    227                MOZ_TO_RESULT_GET_TYPED(nsCOMPtr<nsIEventTarget>,
    228                                        MOZ_SELECT_OVERLOAD(do_GetService),
    229                                        NS_STREAMTRANSPORTSERVICE_CONTRACTID),
    230                CreatePromise::CreateAndReject(NS_ERROR_FAILURE, __func__));
    231 
    232  nsCString taskQueueName("OPFS "_ns + aOriginMetadata.mOrigin);
    233 
    234  RefPtr<TaskQueue> ioTaskQueue =
    235      TaskQueue::Create(do_AddRef(streamTransportService), taskQueueName.get());
    236 
    237  auto dataManager = MakeRefPtr<FileSystemDataManager>(
    238      aOriginMetadata, std::move(quotaManager),
    239      WrapMovingNotNull(streamTransportService),
    240      WrapMovingNotNull(ioTaskQueue));
    241 
    242  AddFileSystemDataManager(aOriginMetadata.mOrigin, dataManager);
    243 
    244  return dataManager->BeginOpen()->Then(
    245      GetCurrentSerialEventTarget(), __func__,
    246      [dataManager = Registered<FileSystemDataManager>(dataManager)](
    247          const BoolPromise::ResolveOrRejectValue& aValue) {
    248        if (aValue.IsReject()) {
    249          return CreatePromise::CreateAndReject(aValue.RejectValue(), __func__);
    250        }
    251 
    252        return CreatePromise::CreateAndResolve(dataManager, __func__);
    253      });
    254 }
    255 
    256 // static
    257 void FileSystemDataManager::AbortOperationsForLocks(
    258    const quota::Client::DirectoryLockIdTable& aDirectoryLockIds) {
    259  ::mozilla::ipc::AssertIsOnBackgroundThread();
    260 
    261  // XXX Share the iteration code with `InitiateShutdown`, for example by
    262  // creating a helper function which would take a predicate function.
    263 
    264  if (!gDataManagers) {
    265    return;
    266  }
    267 
    268  for (const auto& dataManager : gDataManagers->Values()) {
    269    // Check if the Manager holds an acquired DirectoryLock. Origin clearing
    270    // can't be blocked by this Manager if there is no acquired DirectoryLock.
    271    // If there is an acquired DirectoryLock, check if the table contains the
    272    // lock for the Manager.
    273    if (quota::Client::IsLockForObjectAcquiredAndContainedInLockTable(
    274            *dataManager, aDirectoryLockIds)) {
    275      dataManager->RequestAllowToClose();
    276    }
    277  }
    278 }
    279 
    280 // static
    281 void FileSystemDataManager::InitiateShutdown() {
    282  ::mozilla::ipc::AssertIsOnBackgroundThread();
    283 
    284  if (!gDataManagers) {
    285    return;
    286  }
    287 
    288  for (const auto& dataManager : gDataManagers->Values()) {
    289    dataManager->RequestAllowToClose();
    290  }
    291 }
    292 
    293 // static
    294 bool FileSystemDataManager::IsShutdownCompleted() {
    295  ::mozilla::ipc::AssertIsOnBackgroundThread();
    296 
    297  return !gDataManagers;
    298 }
    299 
    300 void FileSystemDataManager::AssertIsOnIOTarget() const {
    301  DebugOnly<bool> current = false;
    302  MOZ_ASSERT(NS_SUCCEEDED(mIOTarget->IsOnCurrentThread(&current)));
    303  MOZ_ASSERT(current);
    304 }
    305 
    306 void FileSystemDataManager::Register() { mRegCount++; }
    307 
    308 void FileSystemDataManager::Unregister() {
    309  MOZ_ASSERT(mRegCount > 0);
    310 
    311  mRegCount--;
    312 
    313  if (IsInactive()) {
    314    BeginClose();
    315  }
    316 }
    317 
    318 void FileSystemDataManager::RegisterActor(
    319    NotNull<FileSystemManagerParent*> aActor) {
    320  MOZ_ASSERT(!mBackgroundThreadAccessible.Access()->mActors.Contains(aActor));
    321  MOZ_ASSERT(mState == State::Open);
    322  MOZ_ASSERT(mDirectoryLockHandle);
    323 
    324  mBackgroundThreadAccessible.Access()->mActors.Insert(aActor);
    325 
    326  aActor->SetRegistered(true);
    327 
    328  // It can happen that FileSystemDataManager::AbortOperationsForLocks is
    329  // called during async CreateFileSystemManagerParent operation when the actor
    330  // is not yet registered. FileSystemDataManager::RequestAllowToClose is not
    331  // able to propagate the RequestAllowToClose notification to the actor in
    332  // that case. However, one a new actor is registered, we can check the
    333  // directory lock if it has been invalidated and eventually notify the actor
    334  // about the abort.
    335 
    336  if (mDirectoryLockHandle->Invalidated()) {
    337    aActor->RequestAllowToClose();
    338  }
    339 }
    340 
    341 void FileSystemDataManager::UnregisterActor(
    342    NotNull<FileSystemManagerParent*> aActor) {
    343  MOZ_ASSERT(mBackgroundThreadAccessible.Access()->mActors.Contains(aActor));
    344  MOZ_ASSERT(mState == State::Open);
    345  MOZ_ASSERT(mDirectoryLockHandle);
    346 
    347  mBackgroundThreadAccessible.Access()->mActors.Remove(aActor);
    348 
    349  aActor->SetRegistered(false);
    350 
    351  if (IsInactive()) {
    352    BeginClose();
    353  }
    354 }
    355 
    356 void FileSystemDataManager::RegisterAccessHandle(
    357    NotNull<FileSystemAccessHandle*> aAccessHandle) {
    358  MOZ_ASSERT(!mBackgroundThreadAccessible.Access()->mAccessHandles.Contains(
    359      aAccessHandle));
    360 
    361  mBackgroundThreadAccessible.Access()->mAccessHandles.Insert(aAccessHandle);
    362 }
    363 
    364 void FileSystemDataManager::UnregisterAccessHandle(
    365    NotNull<FileSystemAccessHandle*> aAccessHandle) {
    366  MOZ_ASSERT(mBackgroundThreadAccessible.Access()->mAccessHandles.Contains(
    367      aAccessHandle));
    368 
    369  mBackgroundThreadAccessible.Access()->mAccessHandles.Remove(aAccessHandle);
    370 
    371  if (IsInactive()) {
    372    BeginClose();
    373  }
    374 }
    375 
    376 RefPtr<BoolPromise> FileSystemDataManager::OnOpen() {
    377  MOZ_ASSERT(mState == State::Opening);
    378 
    379  return mOpenPromiseHolder.Ensure(__func__);
    380 }
    381 
    382 RefPtr<BoolPromise> FileSystemDataManager::OnClose() {
    383  MOZ_ASSERT(mState == State::Closing);
    384 
    385  return mClosePromiseHolder.Ensure(__func__);
    386 }
    387 
    388 // Note: Input can be temporary or main file id
    389 Result<bool, QMResult> FileSystemDataManager::IsLocked(
    390    const FileId& aFileId) const {
    391  auto checkIfEntryIdIsLocked = [this, &aFileId]() -> Result<bool, QMResult> {
    392    QM_TRY_INSPECT(const EntryId& entryId,
    393                   mDatabaseManager->GetEntryId(aFileId));
    394 
    395    return IsLocked(entryId);
    396  };
    397 
    398  auto valueToSome = [](auto aValue) { return Some(std::move(aValue)); };
    399 
    400  QM_TRY_UNWRAP(Maybe<bool> maybeLocked,
    401                QM_OR_ELSE_LOG_VERBOSE_IF(
    402                    // Expression.
    403                    (checkIfEntryIdIsLocked().map(valueToSome)),
    404                    // Predicate.
    405                    IsSpecificError<NS_ERROR_DOM_NOT_FOUND_ERR>,
    406                    // Fallback.
    407                    ([](const auto&) -> Result<Maybe<bool>, QMResult> {
    408                      return Some(false);  // Non-existent files are not locked.
    409                    })));
    410 
    411  if (!maybeLocked) {
    412    // If the metadata is inaccessible, we block modifications.
    413    return true;
    414  }
    415 
    416  return *maybeLocked;
    417 }
    418 
    419 Result<bool, QMResult> FileSystemDataManager::IsLocked(
    420    const EntryId& aEntryId) const {
    421  return mExclusiveLocks.Contains(aEntryId) || mSharedLocks.Contains(aEntryId);
    422 }
    423 
    424 Result<FileId, QMResult> FileSystemDataManager::LockExclusive(
    425    const EntryId& aEntryId) {
    426  QM_TRY_UNWRAP(const bool isLocked, IsLocked(aEntryId));
    427  if (isLocked) {
    428    return Err(QMResult(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR));
    429  }
    430 
    431  QM_TRY_INSPECT(const FileId& fileId,
    432                 mDatabaseManager->EnsureFileId(aEntryId));
    433 
    434  // If the file has been removed, we should get a file not found error.
    435  // Otherwise, if usage tracking cannot be started because file size is not
    436  // known and attempts to read it are failing, lock is denied to freeze the
    437  // quota usage until the (external) blocker is gone or the file is removed.
    438  QM_TRY(QM_TO_RESULT(mDatabaseManager->BeginUsageTracking(fileId)));
    439 
    440  LOG_VERBOSE(("ExclusiveLock"));
    441  mExclusiveLocks.Insert(aEntryId);
    442 
    443  return fileId;
    444 }
    445 
    446 // TODO: Improve reporting of failures, see bug 1840811.
    447 void FileSystemDataManager::UnlockExclusive(const EntryId& aEntryId) {
    448  MOZ_ASSERT(mExclusiveLocks.Contains(aEntryId));
    449 
    450  LOG_VERBOSE(("ExclusiveUnlock"));
    451  mExclusiveLocks.Remove(aEntryId);
    452 
    453  QM_TRY_INSPECT(const FileId& fileId, mDatabaseManager->GetFileId(aEntryId),
    454                 QM_VOID);
    455 
    456  // On error, usage tracking remains on to prevent writes until usage is
    457  // updated successfully.
    458  QM_TRY(MOZ_TO_RESULT(mDatabaseManager->UpdateUsage(fileId)), QM_VOID);
    459  QM_TRY(MOZ_TO_RESULT(mDatabaseManager->EndUsageTracking(fileId)), QM_VOID);
    460 }
    461 
    462 Result<FileId, QMResult> FileSystemDataManager::LockShared(
    463    const EntryId& aEntryId) {
    464  if (mExclusiveLocks.Contains(aEntryId)) {
    465    return Err(QMResult(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR));
    466  }
    467 
    468  auto& count = mSharedLocks.LookupOrInsert(aEntryId);
    469  if (!(1u + CheckedUint32(count)).isValid()) {  // don't make the count invalid
    470    return Err(QMResult(NS_ERROR_UNEXPECTED));
    471  }
    472 
    473  QM_TRY_INSPECT(const FileId& fileId,
    474                 mDatabaseManager->EnsureTemporaryFileId(aEntryId));
    475 
    476  // If the file has been removed, we should get a file not found error.
    477  // Otherwise, if usage tracking cannot be started because file size is not
    478  // known and attempts to read it are failing, lock is denied to freeze the
    479  // quota usage until the (external) blocker is gone or the file is removed.
    480  QM_TRY(QM_TO_RESULT(mDatabaseManager->BeginUsageTracking(fileId)));
    481 
    482  ++count;
    483  LOG_VERBOSE(("SharedLock %u", count));
    484 
    485  return fileId;
    486 }
    487 
    488 // TODO: Improve reporting of failures, see bug 1840811.
    489 void FileSystemDataManager::UnlockShared(const EntryId& aEntryId,
    490                                         const FileId& aFileId, bool aAbort) {
    491  const bool wasDeprecated = [&]() {
    492    // Someone recreated the file and put an exclusive lock on it
    493    if (mExclusiveLocks.Contains(aEntryId)) {
    494      aAbort = true;
    495    }
    496 
    497    auto entry = mDeprecatedLocks.Lookup(aEntryId);
    498    if (!entry) {
    499      return false;
    500    }
    501 
    502    auto& fileIdData = entry.Data();
    503    auto fileIdIt = fileIdData.IndexOf(aFileId);
    504    if (nsTArray<FileId>::NoIndex == fileIdIt) {
    505      return false;
    506    }
    507 
    508    fileIdData.UnorderedRemoveElementAt(fileIdIt);
    509 
    510    if (fileIdData.IsEmpty()) {
    511      entry.Remove();
    512    }
    513 
    514    return true;
    515  }();
    516 
    517  // Deprecated locks are per file. A file cannot be
    518  // both in active use with a shared lock and deprecated,
    519  // while entries can.
    520  if (!wasDeprecated) {
    521    MOZ_ASSERT(!mExclusiveLocks.Contains(aEntryId));
    522 
    523    auto entry = mSharedLocks.Lookup(aEntryId);
    524    if (!entry) {
    525      return;
    526    }
    527 
    528    MOZ_ASSERT(entry.Data() > 0);
    529    --entry.Data();
    530 
    531    LOG_VERBOSE(("SharedUnlock %u", *entry));
    532 
    533    if (0u == entry.Data()) {
    534      entry.Remove();
    535    }
    536  }
    537 
    538  // If underlying file does not exist but should close, abort instead.
    539  if (!aAbort) {
    540    QM_WARNONLY_TRY_UNWRAP(const Maybe<bool> doesFileExist,
    541                           mDatabaseManager->DoesFileExist(aEntryId));
    542    const bool exists = doesFileExist.isSome() && doesFileExist.ref();
    543    if (!exists) {
    544      aAbort = true;
    545    }
    546  }
    547 
    548  // On error, usage tracking remains on to prevent writes until usage is
    549  // updated successfully.
    550  QM_TRY(MOZ_TO_RESULT(mDatabaseManager->UpdateUsage(aFileId)), QM_VOID);
    551  QM_TRY(MOZ_TO_RESULT(mDatabaseManager->EndUsageTracking(aFileId)), QM_VOID);
    552  QM_TRY(
    553      MOZ_TO_RESULT(mDatabaseManager->MergeFileId(aEntryId, aFileId, aAbort)),
    554      QM_VOID);
    555 }
    556 
    557 void FileSystemDataManager::DeprecateSharedLocks(const EntryId& aEntryId,
    558                                                 const FileId& aFileId) {
    559  auto oldEntry = mSharedLocks.Lookup(aEntryId);
    560  if (!oldEntry) {
    561    return;
    562  }
    563 
    564  auto& deprecatedEntries = mDeprecatedLocks.LookupOrInsert(aEntryId);
    565  MOZ_ASSERT(!deprecatedEntries.Contains(aFileId));
    566  deprecatedEntries.AppendElement(aFileId);
    567 
    568  MOZ_ASSERT(oldEntry.Data() >= 1);
    569  if (oldEntry.Data() == 1) {
    570    oldEntry.Remove();
    571  } else {
    572    --oldEntry.Data();
    573  }
    574 }
    575 
    576 bool FileSystemDataManager::IsLockedWithDeprecatedSharedLock(
    577    const EntryId& aEntryId, const FileId& aFileId) const {
    578  MOZ_ASSERT(!aEntryId.IsEmpty());
    579  MOZ_ASSERT(!aFileId.IsEmpty());
    580 
    581  auto entry = mDeprecatedLocks.Lookup(aEntryId);
    582  if (!entry) {
    583    return false;
    584  }
    585 
    586  return nsTArray<FileId>::NoIndex != entry.Data().IndexOf(aFileId);
    587 }
    588 
    589 FileMode FileSystemDataManager::GetMode(bool aKeepData) const {
    590  if (1 == mVersion) {
    591    return FileMode::EXCLUSIVE;
    592  }
    593 
    594  return aKeepData ? FileMode::SHARED_FROM_COPY : FileMode::SHARED_FROM_EMPTY;
    595 }
    596 
    597 bool FileSystemDataManager::IsInactive() const {
    598  auto data = mBackgroundThreadAccessible.Access();
    599  return !mRegCount && !data->mActors.Count() && !data->mAccessHandles.Count();
    600 }
    601 
    602 void FileSystemDataManager::RequestAllowToClose() {
    603  for (const auto& actor : mBackgroundThreadAccessible.Access()->mActors) {
    604    actor->RequestAllowToClose();
    605  }
    606 }
    607 
    608 RefPtr<BoolPromise> FileSystemDataManager::BeginOpen() {
    609  MOZ_ASSERT(mQuotaManager);
    610  MOZ_ASSERT(mState == State::Initial);
    611 
    612  mState = State::Opening;
    613 
    614  mQuotaManager
    615      ->OpenClientDirectory(
    616          {mOriginMetadata, mozilla::dom::quota::Client::FILESYSTEM})
    617      ->Then(GetCurrentSerialEventTarget(), __func__,
    618             [self = RefPtr<FileSystemDataManager>(this)](
    619                 quota::QuotaManager::ClientDirectoryLockHandlePromise::
    620                     ResolveOrRejectValue&& value) {
    621               if (value.IsReject()) {
    622                 return BoolPromise::CreateAndReject(value.RejectValue(),
    623                                                     __func__);
    624               }
    625 
    626               self->mDirectoryLockHandle = std::move(value.ResolveValue());
    627 
    628               MOZ_ASSERT(self->mDirectoryLockHandle->Id() >= 0);
    629               self->mDirectoryLockId = self->mDirectoryLockHandle->Id();
    630 
    631               if (self->mDirectoryLockHandle->Invalidated()) {
    632                 return BoolPromise::CreateAndReject(NS_ERROR_ABORT, __func__);
    633               }
    634 
    635               NotifyDatabaseWorkStarted();
    636 
    637               return BoolPromise::CreateAndResolve(true, __func__);
    638             })
    639      ->Then(
    640          mQuotaManager->IOThread(), __func__,
    641          [self = RefPtr<FileSystemDataManager>(this)](
    642              const BoolPromise::ResolveOrRejectValue& value) {
    643            if (value.IsReject()) {
    644              return BoolPromise::CreateAndReject(value.RejectValue(),
    645                                                  __func__);
    646            }
    647 
    648            QM_TRY(
    649                MOZ_TO_RESULT(EnsureFileSystemDirectory(self->mOriginMetadata)),
    650                CreateAndRejectBoolPromise);
    651 
    652            quota::SleepIfEnabled(
    653                StaticPrefs::dom_fs_databaseInitialization_pauseOnIOThreadMs());
    654 
    655            return BoolPromise::CreateAndResolve(true, __func__);
    656          })
    657      ->Then(
    658          MutableIOTaskQueuePtr(), __func__,
    659          [self = RefPtr<FileSystemDataManager>(this)](
    660              const BoolPromise::ResolveOrRejectValue& value) {
    661            if (value.IsReject()) {
    662              return BoolPromise::CreateAndReject(value.RejectValue(),
    663                                                  __func__);
    664            }
    665 
    666            QM_TRY_UNWRAP(auto connection,
    667                          GetStorageConnection(self->mOriginMetadata,
    668                                               self->mDirectoryLockId),
    669                          CreateAndRejectBoolPromiseFromQMResult);
    670 
    671            QM_TRY_UNWRAP(UniquePtr<FileSystemFileManager> fmPtr,
    672                          FileSystemFileManager::CreateFileSystemFileManager(
    673                              self->mOriginMetadata),
    674                          CreateAndRejectBoolPromiseFromQMResult);
    675 
    676            QM_TRY_UNWRAP(
    677                self->mVersion,
    678                QM_OR_ELSE_WARN_IF(
    679                    // Expression.
    680                    SchemaVersion002::InitializeConnection(
    681                        connection, *fmPtr, self->mOriginMetadata.mOrigin),
    682                    // Predicate.
    683                    ([](const auto&) { return true; }),
    684                    // Fallback.
    685                    ([&self, &connection](const auto&) {
    686                      QM_TRY_RETURN(SchemaVersion001::InitializeConnection(
    687                          connection, self->mOriginMetadata.mOrigin));
    688                    })),
    689                CreateAndRejectBoolPromiseFromQMResult);
    690 
    691            QM_TRY_UNWRAP(
    692                EntryId rootId,
    693                fs::data::GetRootHandle(self->mOriginMetadata.mOrigin),
    694                CreateAndRejectBoolPromiseFromQMResult);
    695 
    696            switch (self->mVersion) {
    697              case 1: {
    698                self->mDatabaseManager =
    699                    MakeUnique<FileSystemDatabaseManagerVersion001>(
    700                        self, std::move(connection), std::move(fmPtr), rootId);
    701                break;
    702              }
    703 
    704              case 2: {
    705                self->mDatabaseManager =
    706                    MakeUnique<FileSystemDatabaseManagerVersion002>(
    707                        self, std::move(connection), std::move(fmPtr), rootId);
    708                break;
    709              }
    710 
    711              default:
    712                break;
    713            }
    714 
    715            return BoolPromise::CreateAndResolve(true, __func__);
    716          })
    717      ->Then(GetCurrentSerialEventTarget(), __func__,
    718             [self = RefPtr<FileSystemDataManager>(this)](
    719                 const BoolPromise::ResolveOrRejectValue& value) {
    720               if (value.IsReject()) {
    721                 self->mState = State::Initial;
    722 
    723                 self->mOpenPromiseHolder.RejectIfExists(value.RejectValue(),
    724                                                         __func__);
    725 
    726               } else {
    727                 self->mState = State::Open;
    728 
    729                 self->mOpenPromiseHolder.ResolveIfExists(true, __func__);
    730               }
    731             });
    732 
    733  return OnOpen();
    734 }
    735 
    736 RefPtr<BoolPromise> FileSystemDataManager::BeginClose() {
    737  MOZ_ASSERT(mState != State::Closing && mState != State::Closed);
    738  MOZ_ASSERT(IsInactive());
    739 
    740  mState = State::Closing;
    741 
    742  InvokeAsync(MutableIOTaskQueuePtr(), __func__,
    743              [self = RefPtr<FileSystemDataManager>(this)]() {
    744                if (self->mDatabaseManager) {
    745                  self->mDatabaseManager->Close();
    746                  self->mDatabaseManager = nullptr;
    747                }
    748 
    749                return BoolPromise::CreateAndResolve(true, __func__);
    750              })
    751      ->Then(MutableBackgroundTargetPtr(), __func__,
    752             [self = RefPtr<FileSystemDataManager>(this)](
    753                 const BoolPromise::ResolveOrRejectValue&) {
    754               return self->mIOTaskQueue->BeginShutdown();
    755             })
    756      ->Then(MutableBackgroundTargetPtr(), __func__,
    757             [self = RefPtr<FileSystemDataManager>(this)](
    758                 const ShutdownPromise::ResolveOrRejectValue&) {
    759               {
    760                 auto destroyingDirectoryLockHandle =
    761                     std::move(self->mDirectoryLockHandle);
    762               }
    763 
    764               RemoveFileSystemDataManager(self->mOriginMetadata.mOrigin);
    765 
    766               self->mState = State::Closed;
    767 
    768               self->mClosePromiseHolder.ResolveIfExists(true, __func__);
    769             });
    770 
    771  return OnClose();
    772 }
    773 
    774 }  // namespace mozilla::dom::fs::data