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
This commit is contained in:
Jan Varga 2023-09-25 16:28:39 +00:00
parent 72434a1513
commit 0806548c36
3 changed files with 126 additions and 131 deletions

View File

@ -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<OriginMetadata, nsresult> QuotaManager::GetOriginMetadata(
maybePersistenceType.value()};
}
Result<Ok, nsresult> 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 <typename OriginFunc>
nsresult QuotaManager::InitializeRepository(PersistenceType aPersistenceType,
OriginFunc&& aOriginFunc) {

View File

@ -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<nsCOMPtr<nsIFile>, 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<Ok, QMResult> 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<nsIFile> 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<nsIFile>&& file) -> mozilla::Result<Ok, nsresult> {
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<nsIFile>&& file) -> mozilla::Result<Ok, nsresult> {
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<OriginMetadata> {
return Some(std::move(metadata));
}),
// Predicate.
IsSpecificError<NS_ERROR_MALFORMED_URI>,
// Fallback.
ErrToDefaultOk<Maybe<OriginMetadata>>));
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<OriginMetadata> {
return Some(std::move(metadata));
}),
// Predicate.
IsSpecificError<NS_ERROR_MALFORMED_URI>,
// Fallback.
ErrToDefaultOk<Maybe<OriginMetadata>>));
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<nsCOMPtr<nsIFile>>{})) {
QM_WARNONLY_TRY(
RemoveOrMoveToDir(*file, toBeRemovedDir),
aQuotaManager.RemoveOriginDirectory(*file),
([&directoriesForRemovalRetry, &file](const auto&) {
directoriesForRemovalRetry.AppendElement(std::move(file));
}));

View File

@ -262,6 +262,8 @@ class QuotaManager final : public BackgroundThreadObject {
Result<OriginMetadata, nsresult> GetOriginMetadata(nsIFile* aDirectory);
Result<Ok, nsresult> RemoveOriginDirectory(nsIFile& aDirectory);
RefPtr<UniversalDirectoryLockPromise> OpenStorageDirectory(
const Nullable<PersistenceType>& aPersistenceType,
const OriginScope& aOriginScope,
@ -747,6 +749,7 @@ class QuotaManager final : public BackgroundThreadObject {
LazyInitializedOnce<const nsString> mTemporaryStoragePath;
LazyInitializedOnce<const nsString> mDefaultStoragePath;
LazyInitializedOnce<const nsString> mPrivateStoragePath;
LazyInitializedOnce<const nsString> mToBeRemovedStoragePath;
uint64_t mTemporaryStorageLimit;
uint64_t mTemporaryStorageUsage;