Skip to content

Commit

Permalink
Bug 1722466 - Add an RAII helper class QuotaDatabaseAutoAttacher to e…
Browse files Browse the repository at this point in the history
…nsure database will be detached automatically in scope exit if any error happens before explicitly detach; r=dom-storage-reviewers,janv

This patch add an RAII helper class QuotaDatabaseAutoAttacher, which provides an Attach() and Detach() method, and guarantees that the database will be detached in it's destructor when an instance of this class is out of scope.

Differential Revision: https://phabricator.services.mozilla.com/D128042
  • Loading branch information
Haiyang Xu committed Nov 5, 2021
1 parent d78bdfe commit 35ca020
Show file tree
Hide file tree
Showing 4 changed files with 251 additions and 147 deletions.
274 changes: 127 additions & 147 deletions dom/localstorage/ActorsParent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
#include "mozilla/dom/quota/OriginScope.h"
#include "mozilla/dom/quota/PersistenceType.h"
#include "mozilla/dom/quota/QuotaCommon.h"
#include "mozilla/dom/quota/StorageHelpers.h"
#include "mozilla/dom/quota/QuotaManager.h"
#include "mozilla/dom/quota/QuotaObject.h"
#include "mozilla/dom/quota/UsageInfo.h"
Expand Down Expand Up @@ -727,47 +728,6 @@ CreateArchiveStorageConnection(const nsAString& aStoragePath) {
return connection;
}

nsresult AttachArchiveDatabase(const nsAString& aStoragePath,
mozIStorageConnection* aConnection) {
AssertIsOnIOThread();
MOZ_ASSERT(!aStoragePath.IsEmpty());
MOZ_ASSERT(aConnection);

QM_TRY_INSPECT(const auto& archiveFile, GetArchiveFile(aStoragePath));

#ifdef DEBUG
{
QM_TRY_INSPECT(const bool& exists,
MOZ_TO_RESULT_INVOKE(archiveFile, Exists));

MOZ_ASSERT(exists);
}
#endif

QM_TRY_INSPECT(const auto& path,
MOZ_TO_RESULT_INVOKE_TYPED(nsString, archiveFile, GetPath));

QM_TRY_INSPECT(const auto& stmt,
MOZ_TO_RESULT_INVOKE_TYPED(
nsCOMPtr<mozIStorageStatement>, aConnection,
CreateStatement, "ATTACH DATABASE :path AS archive;"_ns));

QM_TRY(MOZ_TO_RESULT(stmt->BindStringByName("path"_ns, path)));
QM_TRY(MOZ_TO_RESULT(stmt->Execute()));

return NS_OK;
}

nsresult DetachArchiveDatabase(mozIStorageConnection* aConnection) {
AssertIsOnIOThread();
MOZ_ASSERT(aConnection);

QM_TRY(MOZ_TO_RESULT(
aConnection->ExecuteSimpleSQL("DETACH DATABASE archive"_ns)));

return NS_OK;
}

Result<nsCOMPtr<nsIFile>, nsresult> GetShadowFile(const nsAString& aBasePath) {
MOZ_ASSERT(IsOnIOThread() || IsOnGlobalConnectionThread());
MOZ_ASSERT(!aBasePath.IsEmpty());
Expand Down Expand Up @@ -6964,95 +6924,104 @@ nsresult PrepareDatastoreOp::DatabaseWork() {
if (hasDataForMigration) {
MOZ_ASSERT(mUsage == 0);

QM_TRY(MOZ_TO_RESULT(
AttachArchiveDatabase(quotaManager->GetStoragePath(), connection)));
{
QM_TRY_INSPECT(const auto& archiveFile,
GetArchiveFile(quotaManager->GetStoragePath()));

QM_TRY_INSPECT(const int64_t& newUsage,
GetUsage(*connection, mArchivedOriginScope.get()));
auto autoArchiveDatabaseAttacher =
AutoDatabaseAttacher(connection, archiveFile, "archive"_ns);

if (!quotaObject->MaybeUpdateSize(newUsage, /* aTruncate */ true)) {
return NS_ERROR_FILE_NO_DEVICE_SPACE;
}
QM_TRY(MOZ_TO_RESULT(autoArchiveDatabaseAttacher.Attach()));

auto autoUpdateSize = MakeScopeExit([&quotaObject] {
MOZ_ALWAYS_TRUE(quotaObject->MaybeUpdateSize(0, /* aTruncate */ true));
});
QM_TRY_INSPECT(const int64_t& newUsage,
GetUsage(*connection, mArchivedOriginScope.get()));

mozStorageTransaction transaction(
connection, false, mozIStorageConnection::TRANSACTION_IMMEDIATE);
if (!quotaObject->MaybeUpdateSize(newUsage, /* aTruncate */ true)) {
return NS_ERROR_FILE_NO_DEVICE_SPACE;
}

QM_TRY(MOZ_TO_RESULT(transaction.Start()));
auto autoUpdateSize = MakeScopeExit([&quotaObject] {
MOZ_ALWAYS_TRUE(
quotaObject->MaybeUpdateSize(0, /* aTruncate */ true));
});

{
nsCOMPtr<mozIStorageFunction> function = new CompressFunction();
mozStorageTransaction transaction(
connection, false, mozIStorageConnection::TRANSACTION_IMMEDIATE);

QM_TRY(MOZ_TO_RESULT(
connection->CreateFunction("compress"_ns, 1, function)));
QM_TRY(MOZ_TO_RESULT(transaction.Start()));

function = new CompressibleFunction();
{
nsCOMPtr<mozIStorageFunction> function = new CompressFunction();

QM_TRY(MOZ_TO_RESULT(
connection->CreateFunction("compressible"_ns, 1, function)));
QM_TRY(MOZ_TO_RESULT(
connection->CreateFunction("compress"_ns, 1, function)));

QM_TRY_INSPECT(
const auto& stmt,
MOZ_TO_RESULT_INVOKE_TYPED(
nsCOMPtr<mozIStorageStatement>, connection, CreateStatement,
"INSERT INTO data (key, value, utf16Length, compressed) "
"SELECT key, compress(value), utf16Length(value), "
"compressible(value) "
"FROM webappsstore2 "
"WHERE originKey = :originKey "
"AND originAttributes = :originAttributes;"_ns));

QM_TRY(MOZ_TO_RESULT(mArchivedOriginScope->BindToStatement(stmt)));
function = new CompressibleFunction();

QM_TRY(MOZ_TO_RESULT(stmt->Execute()));
QM_TRY(MOZ_TO_RESULT(
connection->CreateFunction("compressible"_ns, 1, function)));

QM_TRY(MOZ_TO_RESULT(connection->RemoveFunction("compress"_ns)));
QM_TRY_INSPECT(
const auto& stmt,
MOZ_TO_RESULT_INVOKE_TYPED(
nsCOMPtr<mozIStorageStatement>, connection, CreateStatement,
"INSERT INTO data (key, value, utf16Length, compressed) "
"SELECT key, compress(value), utf16Length(value), "
"compressible(value) "
"FROM webappsstore2 "
"WHERE originKey = :originKey "
"AND originAttributes = :originAttributes;"_ns));

QM_TRY(MOZ_TO_RESULT(connection->RemoveFunction("compressible"_ns)));
}
QM_TRY(MOZ_TO_RESULT(mArchivedOriginScope->BindToStatement(stmt)));

{
QM_TRY_INSPECT(
const auto& stmt,
MOZ_TO_RESULT_INVOKE_TYPED(
nsCOMPtr<mozIStorageStatement>, connection, CreateStatement,
"UPDATE database SET usage = :usage;"_ns));
QM_TRY(MOZ_TO_RESULT(stmt->Execute()));

QM_TRY(MOZ_TO_RESULT(stmt->BindInt64ByName("usage"_ns, newUsage)));
QM_TRY(MOZ_TO_RESULT(connection->RemoveFunction("compress"_ns)));

QM_TRY(MOZ_TO_RESULT(stmt->Execute()));
}
QM_TRY(MOZ_TO_RESULT(connection->RemoveFunction("compressible"_ns)));
}

{
QM_TRY_INSPECT(
const auto& stmt,
MOZ_TO_RESULT_INVOKE_TYPED(
nsCOMPtr<mozIStorageStatement>, connection, CreateStatement,
"DELETE FROM webappsstore2 "
"WHERE originKey = :originKey "
"AND originAttributes = :originAttributes;"_ns));

QM_TRY(MOZ_TO_RESULT(mArchivedOriginScope->BindToStatement(stmt)));
QM_TRY(MOZ_TO_RESULT(stmt->Execute()));
}
{
QM_TRY_INSPECT(
const auto& stmt,
MOZ_TO_RESULT_INVOKE_TYPED(
nsCOMPtr<mozIStorageStatement>, connection, CreateStatement,
"UPDATE database SET usage = :usage;"_ns));

QM_TRY(MOZ_TO_RESULT(
UpdateUsageFile(usageFile, usageJournalFile, newUsage)));
QM_TRY(MOZ_TO_RESULT(transaction.Commit()));
QM_TRY(MOZ_TO_RESULT(stmt->BindInt64ByName("usage"_ns, newUsage)));

QM_TRY(MOZ_TO_RESULT(stmt->Execute()));
}

{
QM_TRY_INSPECT(
const auto& stmt,
MOZ_TO_RESULT_INVOKE_TYPED(
nsCOMPtr<mozIStorageStatement>, connection, CreateStatement,
"DELETE FROM webappsstore2 "
"WHERE originKey = :originKey "
"AND originAttributes = :originAttributes;"_ns));

QM_TRY(MOZ_TO_RESULT(mArchivedOriginScope->BindToStatement(stmt)));
QM_TRY(MOZ_TO_RESULT(stmt->Execute()));
}

QM_TRY(MOZ_TO_RESULT(
UpdateUsageFile(usageFile, usageJournalFile, newUsage)));
QM_TRY(MOZ_TO_RESULT(transaction.Commit()));

autoUpdateSize.release();
autoUpdateSize.release();

QM_TRY(MOZ_TO_RESULT(usageJournalFile->Remove(false)));
QM_TRY(MOZ_TO_RESULT(DetachArchiveDatabase(connection)));
QM_TRY(MOZ_TO_RESULT(usageJournalFile->Remove(false)));

mUsage = newUsage;

QM_TRY(MOZ_TO_RESULT(autoArchiveDatabaseAttacher.Detach()));
}

MOZ_ASSERT(gArchivedOrigins);
MOZ_ASSERT(mArchivedOriginScope->HasMatches(gArchivedOrigins));
mArchivedOriginScope->RemoveMatches(gArchivedOrigins);

mUsage = newUsage;
}

nsCOMPtr<mozIStorageConnection> shadowConnection;
Expand Down Expand Up @@ -8365,59 +8334,70 @@ nsresult QuotaClient::AboutToClearOrigins(
return connection;
}()));

if (hasDataForRemoval) {
QM_TRY(MOZ_TO_RESULT(
AttachArchiveDatabase(quotaManager->GetStoragePath(), connection)));
}
{
Maybe<AutoDatabaseAttacher> maybeAutoArchiveDatabaseAttacher;

if (archivedOriginScope->IsPattern()) {
nsCOMPtr<mozIStorageFunction> function(
new MatchFunction(archivedOriginScope->GetPattern()));
if (hasDataForRemoval) {
QM_TRY_INSPECT(const auto& archiveFile,
GetArchiveFile(quotaManager->GetStoragePath()));

QM_TRY(
MOZ_TO_RESULT(connection->CreateFunction("match"_ns, 2, function)));
}
maybeAutoArchiveDatabaseAttacher.emplace(
AutoDatabaseAttacher(connection, archiveFile, "archive"_ns));

{
QM_TRY_INSPECT(
const auto& stmt,
MOZ_TO_RESULT_INVOKE_TYPED(nsCOMPtr<mozIStorageStatement>, connection,
CreateStatement, "BEGIN IMMEDIATE;"_ns));
QM_TRY(MOZ_TO_RESULT(maybeAutoArchiveDatabaseAttacher->Attach()));
}

QM_TRY(MOZ_TO_RESULT(stmt->Execute()));
}
if (archivedOriginScope->IsPattern()) {
nsCOMPtr<mozIStorageFunction> function(
new MatchFunction(archivedOriginScope->GetPattern()));

if (shadowWrites) {
QM_TRY(MOZ_TO_RESULT(
PerformDelete(connection, "main"_ns, archivedOriginScope.get())));
}
QM_TRY(
MOZ_TO_RESULT(connection->CreateFunction("match"_ns, 2, function)));
}

if (hasDataForRemoval) {
QM_TRY(MOZ_TO_RESULT(
PerformDelete(connection, "archive"_ns, archivedOriginScope.get())));
}
{
QM_TRY_INSPECT(const auto& stmt,
MOZ_TO_RESULT_INVOKE_TYPED(
nsCOMPtr<mozIStorageStatement>, connection,
CreateStatement, "BEGIN IMMEDIATE;"_ns));

{
QM_TRY_INSPECT(
const auto& stmt,
MOZ_TO_RESULT_INVOKE_TYPED(nsCOMPtr<mozIStorageStatement>, connection,
CreateStatement, "COMMIT;"_ns));
QM_TRY(MOZ_TO_RESULT(stmt->Execute()));
}

QM_TRY(MOZ_TO_RESULT(stmt->Execute()));
}
if (shadowWrites) {
QM_TRY(MOZ_TO_RESULT(
PerformDelete(connection, "main"_ns, archivedOriginScope.get())));
}

if (archivedOriginScope->IsPattern()) {
QM_TRY(MOZ_TO_RESULT(connection->RemoveFunction("match"_ns)));
}
if (hasDataForRemoval) {
QM_TRY(MOZ_TO_RESULT(PerformDelete(connection, "archive"_ns,
archivedOriginScope.get())));
}

if (hasDataForRemoval) {
QM_TRY(MOZ_TO_RESULT(DetachArchiveDatabase(connection)));
{
QM_TRY_INSPECT(const auto& stmt,
MOZ_TO_RESULT_INVOKE_TYPED(
nsCOMPtr<mozIStorageStatement>, connection,
CreateStatement, "COMMIT;"_ns));

MOZ_ASSERT(gArchivedOrigins);
MOZ_ASSERT(archivedOriginScope->HasMatches(gArchivedOrigins));
archivedOriginScope->RemoveMatches(gArchivedOrigins);
}
QM_TRY(MOZ_TO_RESULT(stmt->Execute()));
}

if (archivedOriginScope->IsPattern()) {
QM_TRY(MOZ_TO_RESULT(connection->RemoveFunction("match"_ns)));
}

if (hasDataForRemoval) {
MOZ_ASSERT(maybeAutoArchiveDatabaseAttacher.isSome());
QM_TRY(MOZ_TO_RESULT(maybeAutoArchiveDatabaseAttacher->Detach()));

maybeAutoArchiveDatabaseAttacher.reset();

MOZ_ASSERT(gArchivedOrigins);
MOZ_ASSERT(archivedOriginScope->HasMatches(gArchivedOrigins));
archivedOriginScope->RemoveMatches(gArchivedOrigins);
}
}
QM_TRY(MOZ_TO_RESULT(connection->Close()));
}

Expand Down
Loading

0 comments on commit 35ca020

Please sign in to comment.