From 0806548c36198cf7e8b9bb31a128b6eed64b9a62 Mon Sep 17 00:00:00 2001 From: Jan Varga Date: Mon, 25 Sep 2023 16:28:39 +0000 Subject: [PATCH] Bug 1733107 - Add QuotaManager::RemoveOriginDirectory; r=dom-storage-reviewers,asuth The removing of an origin directory became a bit more complex because when the app shutdown already started, origin directories shouldn't be fully removed from disk. They need to be only moved to a special directory. All this complexity should be covered by a dedicated QuotaManager method. Changes done in this patch: - added a new method QuotaManager::RemoveOriginDirectory - added a prefilled string for the special to-be-removed directory - adjusted ClearRequestBase::DeleteFiles to use the new QuotaManager::RemoveOriginDirectory method Differential Revision: https://phabricator.services.mozilla.com/D186780 --- dom/quota/ActorsParent.cpp | 32 ++++- dom/quota/OriginOperations.cpp | 222 ++++++++++++++------------------- dom/quota/QuotaManager.h | 3 + 3 files changed, 126 insertions(+), 131 deletions(-) diff --git a/dom/quota/ActorsParent.cpp b/dom/quota/ActorsParent.cpp index 0fe5006f7cd2..d3b4a22ec609 100644 --- a/dom/quota/ActorsParent.cpp +++ b/dom/quota/ActorsParent.cpp @@ -48,6 +48,7 @@ #include "mozStorageCID.h" #include "mozStorageHelper.h" #include "mozilla/AlreadyAddRefed.h" +#include "mozilla/AppShutdown.h" #include "mozilla/Assertions.h" #include "mozilla/Atomics.h" #include "mozilla/Attributes.h" @@ -266,7 +267,8 @@ constexpr auto kStorageName = u"storage"_ns; #define PERMANENT_DIRECTORY_NAME u"permanent" #define TEMPORARY_DIRECTORY_NAME u"temporary" #define DEFAULT_DIRECTORY_NAME u"default" -#define DEFAULT_PRIVATE_DIRECTORY_NAME u"private" +#define PRIVATE_DIRECTORY_NAME u"private" +#define TOBEREMOVED_DIRECTORY_NAME u"to-be-removed" #define WEB_APPS_STORE_FILE_NAME u"webappsstore.sqlite" #define LS_ARCHIVE_FILE_NAME u"ls-archive.sqlite" @@ -2095,9 +2097,13 @@ nsresult QuotaManager::Init() { do_Init(mDefaultStoragePath), GetPathForStorage(*baseDir, nsLiteralString(DEFAULT_DIRECTORY_NAME))); - QM_TRY_UNWRAP(do_Init(mPrivateStoragePath), - GetPathForStorage( - *baseDir, nsLiteralString(DEFAULT_PRIVATE_DIRECTORY_NAME))); + QM_TRY_UNWRAP( + do_Init(mPrivateStoragePath), + GetPathForStorage(*baseDir, nsLiteralString(PRIVATE_DIRECTORY_NAME))); + + QM_TRY_UNWRAP( + do_Init(mToBeRemovedStoragePath), + GetPathForStorage(*baseDir, nsLiteralString(TOBEREMOVED_DIRECTORY_NAME))); QM_TRY_UNWRAP(do_Init(mIOThread), MOZ_TO_RESULT_INVOKE_TYPED( @@ -3481,6 +3487,24 @@ Result QuotaManager::GetOriginMetadata( maybePersistenceType.value()}; } +Result QuotaManager::RemoveOriginDirectory(nsIFile& aDirectory) { + AssertIsOnIOThread(); + + if (!AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownTeardown)) { + QM_TRY_RETURN(MOZ_TO_RESULT(aDirectory.Remove(true))); + } + + QM_TRY_INSPECT(const auto& toBeRemovedStorageDir, + QM_NewLocalFile(*mToBeRemovedStoragePath)); + + QM_TRY_INSPECT(const bool& created, EnsureDirectory(*toBeRemovedStorageDir)); + + (void)created; + + QM_TRY_RETURN(MOZ_TO_RESULT(aDirectory.MoveTo( + toBeRemovedStorageDir, NSID_TrimBracketsUTF16(nsID::GenerateUUID())))); +} + template nsresult QuotaManager::InitializeRepository(PersistenceType aPersistenceType, OriginFunc&& aOriginFunc) { diff --git a/dom/quota/OriginOperations.cpp b/dom/quota/OriginOperations.cpp index 8a068fe52698..84eb2d2b6c2b 100644 --- a/dom/quota/OriginOperations.cpp +++ b/dom/quota/OriginOperations.cpp @@ -14,7 +14,6 @@ #include "FileUtils.h" #include "GroupInfo.h" #include "MainThreadUtils.h" -#include "mozilla/AppShutdown.h" #include "mozilla/Assertions.h" #include "mozilla/Atomics.h" #include "mozilla/Maybe.h" @@ -1709,32 +1708,6 @@ void ClearStorageOp::CloseDirectory() { mDirectoryLock = nullptr; } -static Result, QMResult> OpenToBeRemovedDirectory( - const nsAString& aStoragePath) { - QM_TRY_INSPECT(const auto& dir, - QM_TO_RESULT_TRANSFORM(QM_NewLocalFile(aStoragePath))); - QM_TRY(QM_TO_RESULT(dir->Append(u"to-be-removed"_ns))); - - nsresult rv = dir->Create(nsIFile::DIRECTORY_TYPE, 0700); - if (NS_SUCCEEDED(rv) || rv == NS_ERROR_FILE_ALREADY_EXISTS) { - return dir; - } - return Err(QMResult(rv)); -} - -static Result RemoveOrMoveToDir(nsIFile& aFile, - nsIFile* aMoveTargetDir) { - if (!aMoveTargetDir) { - QM_TRY(QM_TO_RESULT(aFile.Remove(true))); - return Ok(); - } - - nsIDToCString uuid(nsID::GenerateUUID()); - NS_ConvertUTF8toUTF16 subDirName(uuid.get(), NSID_LENGTH - 1); - QM_TRY(QM_TO_RESULT(aFile.MoveTo(aMoveTargetDir, subDirName))); - return Ok(); -} - void ClearRequestBase::DeleteFiles(QuotaManager& aQuotaManager, PersistenceType aPersistenceType, const OriginScope& aOriginScope, @@ -1761,125 +1734,120 @@ void ClearRequestBase::DeleteFiles(QuotaManager& aQuotaManager, aQuotaManager.MaybeRecordQuotaManagerShutdownStep( "ClearRequestBase: Starting deleting files"_ns); - nsCOMPtr toBeRemovedDir; - if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownTeardown)) { - QM_WARNONLY_TRY_UNWRAP( - auto result, OpenToBeRemovedDirectory(aQuotaManager.GetStoragePath())); - toBeRemovedDir = result.valueOr(nullptr); - } - QM_TRY( - CollectEachFile( - *directory, - [&aClientType, &originScope = aOriginScope, aPersistenceType, - &aQuotaManager, &directoriesForRemovalRetry, &toBeRemovedDir]( - nsCOMPtr&& file) -> mozilla::Result { - QM_TRY_INSPECT(const auto& dirEntryKind, GetDirEntryKind(*file)); - QM_TRY_INSPECT(const auto& leafName, - MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsAutoString, file, - GetLeafName)); + QM_TRY(CollectEachFile( + *directory, + [&aClientType, &originScope = aOriginScope, aPersistenceType, + &aQuotaManager, &directoriesForRemovalRetry]( + nsCOMPtr&& file) -> mozilla::Result { + QM_TRY_INSPECT(const auto& dirEntryKind, GetDirEntryKind(*file)); - switch (dirEntryKind) { - case nsIFileKind::ExistsAsDirectory: { - QM_TRY_UNWRAP( - auto maybeMetadata, - QM_OR_ELSE_WARN_IF( - // Expression - aQuotaManager.GetOriginMetadata(file).map( - [](auto metadata) -> Maybe { - return Some(std::move(metadata)); - }), - // Predicate. - IsSpecificError, - // Fallback. - ErrToDefaultOk>)); + QM_TRY_INSPECT(const auto& leafName, + MOZ_TO_RESULT_INVOKE_MEMBER_TYPED( + nsAutoString, file, GetLeafName)); - if (!maybeMetadata) { - // Unknown directories during clearing are allowed. Just warn - // if we find them. - UNKNOWN_FILE_WARNING(leafName); - break; - } + switch (dirEntryKind) { + case nsIFileKind::ExistsAsDirectory: { + QM_TRY_UNWRAP( + auto maybeMetadata, + QM_OR_ELSE_WARN_IF( + // Expression + aQuotaManager.GetOriginMetadata(file).map( + [](auto metadata) -> Maybe { + return Some(std::move(metadata)); + }), + // Predicate. + IsSpecificError, + // Fallback. + ErrToDefaultOk>)); - auto metadata = maybeMetadata.extract(); + if (!maybeMetadata) { + // Unknown directories during clearing are allowed. Just + // warn if we find them. + UNKNOWN_FILE_WARNING(leafName); + break; + } - MOZ_ASSERT(metadata.mPersistenceType == aPersistenceType); + auto metadata = maybeMetadata.extract(); - // Skip the origin directory if it doesn't match the pattern. - if (!originScope.Matches( - OriginScope::FromOrigin(metadata.mOrigin))) { - break; - } + MOZ_ASSERT(metadata.mPersistenceType == aPersistenceType); - if (!aClientType.IsNull()) { - nsAutoString clientDirectoryName; - QM_TRY( - OkIf(Client::TypeToText(aClientType.Value(), - clientDirectoryName, fallible)), - Err(NS_ERROR_FAILURE)); + // Skip the origin directory if it doesn't match the pattern. + if (!originScope.Matches( + OriginScope::FromOrigin(metadata.mOrigin))) { + break; + } - QM_TRY(MOZ_TO_RESULT(file->Append(clientDirectoryName))); + if (!aClientType.IsNull()) { + nsAutoString clientDirectoryName; + QM_TRY(OkIf(Client::TypeToText(aClientType.Value(), + clientDirectoryName, + fallible)), + Err(NS_ERROR_FAILURE)); - QM_TRY_INSPECT(const bool& exists, - MOZ_TO_RESULT_INVOKE_MEMBER(file, Exists)); + QM_TRY(MOZ_TO_RESULT(file->Append(clientDirectoryName))); - if (!exists) { - break; - } - } + QM_TRY_INSPECT(const bool& exists, + MOZ_TO_RESULT_INVOKE_MEMBER(file, Exists)); - // We can't guarantee that this will always succeed on - // Windows... - QM_WARNONLY_TRY( - RemoveOrMoveToDir(*file, toBeRemovedDir), [&](const auto&) { - directoriesForRemovalRetry.AppendElement(std::move(file)); - }); + if (!exists) { + break; + } + } - const bool initialized = - aPersistenceType == PERSISTENCE_TYPE_PERSISTENT - ? aQuotaManager.IsOriginInitialized(metadata.mOrigin) - : aQuotaManager.IsTemporaryStorageInitialized(); + // We can't guarantee that this will always succeed on + // Windows... + QM_WARNONLY_TRY(aQuotaManager.RemoveOriginDirectory(*file), + [&](const auto&) { + directoriesForRemovalRetry.AppendElement( + std::move(file)); + }); - // If it hasn't been initialized, we don't need to update the - // quota and notify the removing client. - if (!initialized) { - break; - } + const bool initialized = + aPersistenceType == PERSISTENCE_TYPE_PERSISTENT + ? aQuotaManager.IsOriginInitialized(metadata.mOrigin) + : aQuotaManager.IsTemporaryStorageInitialized(); - if (aPersistenceType != PERSISTENCE_TYPE_PERSISTENT) { - if (aClientType.IsNull()) { - aQuotaManager.RemoveQuotaForOrigin(aPersistenceType, - metadata); - } else { - aQuotaManager.ResetUsageForClient( - ClientMetadata{metadata, aClientType.Value()}); - } - } + // If it hasn't been initialized, we don't need to update the + // quota and notify the removing client. + if (!initialized) { + break; + } - aQuotaManager.OriginClearCompleted( - aPersistenceType, metadata.mOrigin, aClientType); + if (aPersistenceType != PERSISTENCE_TYPE_PERSISTENT) { + if (aClientType.IsNull()) { + aQuotaManager.RemoveQuotaForOrigin(aPersistenceType, + metadata); + } else { + aQuotaManager.ResetUsageForClient( + ClientMetadata{metadata, aClientType.Value()}); + } + } - break; - } + aQuotaManager.OriginClearCompleted( + aPersistenceType, metadata.mOrigin, aClientType); - case nsIFileKind::ExistsAsFile: { - // Unknown files during clearing are allowed. Just warn if we - // find them. - if (!IsOSMetadata(leafName)) { - UNKNOWN_FILE_WARNING(leafName); - } + break; + } - break; - } + case nsIFileKind::ExistsAsFile: { + // Unknown files during clearing are allowed. Just warn if we + // find them. + if (!IsOSMetadata(leafName)) { + UNKNOWN_FILE_WARNING(leafName); + } - case nsIFileKind::DoesNotExist: - // Ignore files that got removed externally while iterating. - break; - } + break; + } - return Ok{}; - }), - QM_VOID); + case nsIFileKind::DoesNotExist: + // Ignore files that got removed externally while iterating. + break; + } + + return Ok{}; + }), + QM_VOID); // Retry removing any directories that failed to be removed earlier now. // @@ -1896,7 +1864,7 @@ void ClearRequestBase::DeleteFiles(QuotaManager& aQuotaManager, for (auto&& file : std::exchange(directoriesForRemovalRetry, nsTArray>{})) { QM_WARNONLY_TRY( - RemoveOrMoveToDir(*file, toBeRemovedDir), + aQuotaManager.RemoveOriginDirectory(*file), ([&directoriesForRemovalRetry, &file](const auto&) { directoriesForRemovalRetry.AppendElement(std::move(file)); })); diff --git a/dom/quota/QuotaManager.h b/dom/quota/QuotaManager.h index 4b67441caff9..b7048ee5f3df 100644 --- a/dom/quota/QuotaManager.h +++ b/dom/quota/QuotaManager.h @@ -262,6 +262,8 @@ class QuotaManager final : public BackgroundThreadObject { Result GetOriginMetadata(nsIFile* aDirectory); + Result RemoveOriginDirectory(nsIFile& aDirectory); + RefPtr OpenStorageDirectory( const Nullable& aPersistenceType, const OriginScope& aOriginScope, @@ -747,6 +749,7 @@ class QuotaManager final : public BackgroundThreadObject { LazyInitializedOnce mTemporaryStoragePath; LazyInitializedOnce mDefaultStoragePath; LazyInitializedOnce mPrivateStoragePath; + LazyInitializedOnce mToBeRemovedStoragePath; uint64_t mTemporaryStorageLimit; uint64_t mTemporaryStorageUsage;