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(¤t))); 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