storage: Add task to clean up MediaLicense.db from buckets
The MediaLicense database is no longer being used, and exists and takes up client space in a number of buckets. This CL introduces a cleanup that would go through every bucket, and if the path containing MediaLicense database exists, we delete everything in that path. Bug: 373898308 Change-Id: I3482eab291b081bea9c8c2001d6d23d6bd77ac76 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6088694 Reviewed-by: Evan Stade <estade@chromium.org> Commit-Queue: Vikram Pasupathy <vpasupathy@chromium.org> Cr-Commit-Position: refs/heads/main@{#1410666}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
b6390ae27e
commit
423fe125f3
@ -67,6 +67,10 @@ const char kBucketTable[] = "buckets";
|
||||
// registered into the buckets table. Introduced 2022-05 (crrev.com/c/3594211).
|
||||
const char kBucketsTableBootstrapped[] = "IsBucketsBootstrapped";
|
||||
|
||||
// Flag to not repeat MediaLicenseDatabase cleanup in all the bucket
|
||||
// directories. Introduced 2025-01 (crrev.com/c/6088694).
|
||||
const char kMediaLicenseDatabaseRemoved[] = "IsMediaLicenseDatabaseRemoved";
|
||||
|
||||
const int kCommitIntervalMs = 30000;
|
||||
|
||||
const base::Clock* g_clock_for_testing = nullptr;
|
||||
@ -925,6 +929,28 @@ QuotaError QuotaDatabase::SetIsBootstrapped(bool bootstrap_flag) {
|
||||
: QuotaError::kDatabaseError;
|
||||
}
|
||||
|
||||
bool QuotaDatabase::IsMediaLicenseDatabaseRemoved() {
|
||||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
if (EnsureOpened() != QuotaError::kNone) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int flag = 0;
|
||||
return meta_table_->GetValue(kMediaLicenseDatabaseRemoved, &flag) && flag;
|
||||
}
|
||||
|
||||
QuotaError QuotaDatabase::SetIsMediaLicenseDatabaseRemoved(bool removed_flag) {
|
||||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
QuotaError open_error = EnsureOpened();
|
||||
if (open_error != QuotaError::kNone) {
|
||||
return open_error;
|
||||
}
|
||||
|
||||
return meta_table_->SetValue(kMediaLicenseDatabaseRemoved, removed_flag)
|
||||
? QuotaError::kNone
|
||||
: QuotaError::kDatabaseError;
|
||||
}
|
||||
|
||||
bool QuotaDatabase::RecoverOrRaze(int error_code) {
|
||||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
|
||||
|
@ -216,6 +216,12 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaDatabase {
|
||||
bool IsBootstrapped();
|
||||
QuotaError SetIsBootstrapped(bool bootstrap_flag);
|
||||
|
||||
// Returns false if SetIsMediaLicensedDatabaseRemoved() has never been called
|
||||
// before, which means that Media License Databases still potentially exist
|
||||
// within the bucket directories.
|
||||
bool IsMediaLicenseDatabaseRemoved();
|
||||
QuotaError SetIsMediaLicenseDatabaseRemoved(bool removed_flag);
|
||||
|
||||
// If the database has failed to open, this will attempt to reopen it.
|
||||
// Otherwise, it will attempt to recover the database. If recovery is
|
||||
// attempted but fails, the database will be razed. In all cases, this will
|
||||
|
@ -66,6 +66,7 @@
|
||||
#include "storage/browser/quota/quota_manager_proxy.h"
|
||||
#include "storage/browser/quota/quota_override_handle.h"
|
||||
#include "storage/browser/quota/quota_temporary_storage_evictor.h"
|
||||
#include "storage/browser/quota/storage_directory_util.h"
|
||||
#include "storage/browser/quota/usage_tracker.h"
|
||||
#include "third_party/blink/public/common/storage_key/storage_key.h"
|
||||
#include "third_party/blink/public/mojom/quota/quota_types.mojom-shared.h"
|
||||
@ -1906,6 +1907,39 @@ void QuotaManagerImpl::EnsureDatabaseOpened() {
|
||||
}
|
||||
|
||||
MaybeBootstrapDatabase();
|
||||
MaybeRemoveMediaLicenseDatabases();
|
||||
}
|
||||
|
||||
void QuotaManagerImpl::MaybeRemoveMediaLicenseDatabases() {
|
||||
db_runner_->PostTaskAndReplyWithResult(
|
||||
FROM_HERE,
|
||||
base::BindOnce(&QuotaDatabase::IsMediaLicenseDatabaseRemoved,
|
||||
base::Unretained(database_.get())),
|
||||
base::BindOnce(&QuotaManagerImpl::DidGetMediaLicenseDatabaseRemovalFlag,
|
||||
weak_factory_.GetWeakPtr()));
|
||||
}
|
||||
|
||||
void QuotaManagerImpl::DidGetMediaLicenseDatabaseRemovalFlag(
|
||||
bool is_media_license_database_removed) {
|
||||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
if (!is_media_license_database_removed) {
|
||||
RemoveMediaLicenseDatabases();
|
||||
}
|
||||
}
|
||||
|
||||
void QuotaManagerImpl::RemoveMediaLicenseDatabases() {
|
||||
base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
|
||||
FROM_HERE,
|
||||
base::BindOnce(&QuotaManagerImpl::DeleteMediaLicenseDatabase,
|
||||
weak_factory_.GetWeakPtr()),
|
||||
kMinutesAfterStartupToBeginMediaLicenseDatabaseDeletion);
|
||||
|
||||
PostTaskAndReplyWithResultForDBThread(
|
||||
base::BindOnce([](QuotaDatabase* database) {
|
||||
DCHECK(database);
|
||||
return database->SetIsMediaLicenseDatabaseRemoved(true);
|
||||
}),
|
||||
base::DoNothing(), FROM_HERE);
|
||||
}
|
||||
|
||||
void QuotaManagerImpl::MaybeBootstrapDatabase() {
|
||||
@ -2004,6 +2038,13 @@ void QuotaManagerImpl::DidSetDatabaseBootstrapped(QuotaError error) {
|
||||
base::BindOnce(&QuotaManagerImpl::StartEviction,
|
||||
weak_factory_.GetWeakPtr()),
|
||||
kMinutesAfterStartupToBeginEviction);
|
||||
|
||||
// Schedule the MediaLicenseDatabase deletion task.
|
||||
base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
|
||||
FROM_HERE,
|
||||
base::BindOnce(&QuotaManagerImpl::DeleteMediaLicenseDatabase,
|
||||
weak_factory_.GetWeakPtr()),
|
||||
kMinutesAfterStartupToBeginMediaLicenseDatabaseDeletion);
|
||||
}
|
||||
|
||||
void QuotaManagerImpl::RunDatabaseCallbacks() {
|
||||
@ -2289,6 +2330,39 @@ void QuotaManagerImpl::StartEviction() {
|
||||
temporary_storage_evictor_->Start();
|
||||
}
|
||||
|
||||
void QuotaManagerImpl::DeleteMediaLicenseDatabase() {
|
||||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
|
||||
GetBucketsModifiedBetween(
|
||||
StorageType::kTemporary, base::Time::Min(), base::Time::Max(),
|
||||
base::BindOnce(&QuotaManagerImpl::DidGetBucketsForMediaLicenseDeletion,
|
||||
weak_factory_.GetWeakPtr()));
|
||||
}
|
||||
|
||||
void QuotaManagerImpl::DidGetBucketsForMediaLicenseDeletion(
|
||||
const std::set<BucketLocator>& buckets) {
|
||||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
|
||||
std::vector<base::FilePath> media_license_dir_paths;
|
||||
|
||||
for (const BucketLocator& bucket : buckets) {
|
||||
if (bucket.storage_key.IsFirstPartyContext()) {
|
||||
base::FilePath media_license_bucket_path = CreateClientBucketPath(
|
||||
profile_path_, bucket, QuotaClientType::kMediaLicense);
|
||||
media_license_dir_paths.push_back(media_license_bucket_path);
|
||||
}
|
||||
}
|
||||
|
||||
db_runner_->PostTask(FROM_HERE,
|
||||
base::BindOnce(
|
||||
[](std::vector<base::FilePath> file_paths) {
|
||||
for (base::FilePath& path : file_paths) {
|
||||
base::DeletePathRecursively(path);
|
||||
}
|
||||
},
|
||||
std::move(media_license_dir_paths)));
|
||||
}
|
||||
|
||||
void QuotaManagerImpl::DeleteBucketFromDatabase(
|
||||
const BucketLocator& bucket,
|
||||
bool commit_immediately,
|
||||
|
@ -477,6 +477,12 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaManagerImpl
|
||||
static constexpr base::TimeDelta kMinutesAfterStartupToBeginEviction =
|
||||
base::Minutes(5);
|
||||
|
||||
// After 15 minutes from startup, go through the buckets to delete the
|
||||
// MediaLicense database from all of the bucket directories.
|
||||
static constexpr base::TimeDelta
|
||||
kMinutesAfterStartupToBeginMediaLicenseDatabaseDeletion =
|
||||
base::Minutes(15);
|
||||
|
||||
static constexpr int kThresholdOfErrorsToBeDenylisted = 3;
|
||||
|
||||
static constexpr char kDatabaseName[] = "QuotaManager";
|
||||
@ -524,6 +530,8 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaManagerImpl
|
||||
|
||||
bool is_db_disabled_for_testing() { return db_disabled_; }
|
||||
|
||||
void DeleteMediaLicenseDatabaseForTesting() { DeleteMediaLicenseDatabase(); }
|
||||
|
||||
void AddObserver(
|
||||
mojo::PendingRemote<storage::mojom::QuotaManagerObserver> observer);
|
||||
|
||||
@ -584,6 +592,16 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaManagerImpl
|
||||
// manager by RegisterClient().
|
||||
void EnsureDatabaseOpened();
|
||||
|
||||
// Removes Media License Databases only if it hasn't already happened.
|
||||
void MaybeRemoveMediaLicenseDatabases();
|
||||
|
||||
// Methods to help with the removal of the Media License Databases, including
|
||||
// retrieving the flag from the metadata and disabling the database if there
|
||||
// is an error with the retrieval.
|
||||
void RemoveMediaLicenseDatabases();
|
||||
void DidGetMediaLicenseDatabaseRemovalFlag(
|
||||
bool is_media_license_database_removed);
|
||||
|
||||
// Bootstraps only if it hasn't already happened.
|
||||
void MaybeBootstrapDatabase();
|
||||
// Bootstraps database with storage keys that may not have been registered.
|
||||
@ -676,6 +694,11 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) QuotaManagerImpl
|
||||
// user-facing dialog in Chrome.
|
||||
void NotifyWriteFailed(const blink::StorageKey& storage_key);
|
||||
|
||||
// Methods for MediaLicenseDB logic.
|
||||
void DeleteMediaLicenseDatabase();
|
||||
void DidGetBucketsForMediaLicenseDeletion(
|
||||
const std::set<BucketLocator>& buckets);
|
||||
|
||||
// Methods for eviction logic.
|
||||
void StartEviction();
|
||||
void DeleteBucketFromDatabase(
|
||||
|
@ -48,6 +48,7 @@
|
||||
#include "storage/browser/quota/quota_manager_impl.h"
|
||||
#include "storage/browser/quota/quota_manager_proxy.h"
|
||||
#include "storage/browser/quota/quota_override_handle.h"
|
||||
#include "storage/browser/quota/storage_directory_util.h"
|
||||
#include "storage/browser/test/mock_quota_client.h"
|
||||
#include "storage/browser/test/mock_special_storage_policy.h"
|
||||
#include "testing/gmock/include/gmock/gmock.h"
|
||||
@ -3801,4 +3802,78 @@ TEST_F(QuotaManagerImplTest, StaticReportedQuota_Bucket) {
|
||||
EXPECT_EQ(result.quota, storage_capacity.available_space);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(QuotaManagerImplTest, DeleteMediaLicenseDb) {
|
||||
auto clock = std::make_unique<base::SimpleTestClock>();
|
||||
QuotaDatabase::SetClockForTesting(clock.get());
|
||||
clock->SetNow(base::Time::Now());
|
||||
|
||||
// Create some buckets with MediaLicense.db files.
|
||||
static const ClientBucketData kData[] = {
|
||||
{"http://foo.com/", kDefaultBucketName, kTemp, 1},
|
||||
{"http://bar.com/", kDefaultBucketName, kTemp, 1},
|
||||
};
|
||||
MockQuotaClient* client =
|
||||
CreateAndRegisterClient(QuotaClientType::kFileSystem, {kTemp});
|
||||
RegisterClientBucketData(client, kData);
|
||||
|
||||
// Create MediaLicense.db files in the bucket paths.
|
||||
for (const auto& data : kData) {
|
||||
ASSERT_OK_AND_ASSIGN(auto bucket, GetBucket(ToStorageKey(data.origin),
|
||||
data.name, data.type));
|
||||
auto media_license_path =
|
||||
CreateClientBucketPath(data_dir_.GetPath(), bucket.ToBucketLocator(),
|
||||
QuotaClientType::kMediaLicense);
|
||||
base::CreateDirectory(media_license_path);
|
||||
auto db_path =
|
||||
media_license_path.Append(FILE_PATH_LITERAL("MediaLicense.db"));
|
||||
std::string dummy_content = "dummy db content";
|
||||
ASSERT_TRUE(base::WriteFile(db_path, dummy_content));
|
||||
ASSERT_TRUE(base::PathExists(db_path));
|
||||
}
|
||||
|
||||
// Run deletion on startup.
|
||||
quota_manager_impl_->DeleteMediaLicenseDatabaseForTesting();
|
||||
task_environment_.RunUntilIdle();
|
||||
|
||||
// Verify MediaLicense.db files are deleted.
|
||||
for (const auto& data : kData) {
|
||||
ASSERT_OK_AND_ASSIGN(auto bucket, GetBucket(ToStorageKey(data.origin),
|
||||
data.name, data.type));
|
||||
auto media_license_path =
|
||||
CreateClientBucketPath(data_dir_.GetPath(), bucket.ToBucketLocator(),
|
||||
QuotaClientType::kMediaLicense);
|
||||
auto db_path =
|
||||
media_license_path.Append(FILE_PATH_LITERAL("MediaLicense.db"));
|
||||
EXPECT_FALSE(base::PathExists(db_path));
|
||||
}
|
||||
|
||||
QuotaDatabase::SetClockForTesting(nullptr);
|
||||
}
|
||||
|
||||
TEST_F(QuotaManagerImplTest, DeleteMediaLicenseDb_NonexistentFiles) {
|
||||
// Test deletion works when no MediaLicense.db files exist.
|
||||
static const ClientBucketData kData[] = {
|
||||
{"http://foo.com/", kDefaultBucketName, kTemp, 1},
|
||||
};
|
||||
MockQuotaClient* client =
|
||||
CreateAndRegisterClient(QuotaClientType::kFileSystem, {kTemp});
|
||||
RegisterClientBucketData(client, kData);
|
||||
|
||||
ASSERT_OK_AND_ASSIGN(auto bucket, GetBucket(ToStorageKey(kData[0].origin),
|
||||
kData[0].name, kData[0].type));
|
||||
auto media_license_path =
|
||||
CreateClientBucketPath(data_dir_.GetPath(), bucket.ToBucketLocator(),
|
||||
QuotaClientType::kMediaLicense);
|
||||
auto db_path =
|
||||
media_license_path.Append(FILE_PATH_LITERAL("MediaLicense.db"));
|
||||
ASSERT_FALSE(base::PathExists(db_path));
|
||||
|
||||
quota_manager_impl_->DeleteMediaLicenseDatabaseForTesting();
|
||||
task_environment_.RunUntilIdle();
|
||||
|
||||
// Should complete without errors.
|
||||
EXPECT_FALSE(base::PathExists(db_path));
|
||||
}
|
||||
|
||||
} // namespace storage
|
||||
|
Reference in New Issue
Block a user