blob: 70d15c2bd0bb585019b33554ec57fb067175a125 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/offline_pages/core/model/persistent_page_consistency_check_task.h"
#include <string>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/files/file_util.h"
#include "base/metrics/histogram_macros.h"
#include "base/numerics/safe_conversions.h"
#include "components/offline_pages/core/archive_manager.h"
#include "components/offline_pages/core/client_policy_controller.h"
#include "components/offline_pages/core/model/delete_page_task.h"
#include "components/offline_pages/core/model/get_pages_task.h"
#include "components/offline_pages/core/offline_page_client_policy.h"
#include "components/offline_pages/core/offline_page_metadata_store.h"
#include "components/offline_pages/core/offline_store_utils.h"
#include "components/offline_pages/core/page_criteria.h"
#include "sql/database.h"
#include "sql/statement.h"
#include "sql/transaction.h"
namespace base {
class Time;
} // namespace base
namespace offline_pages {
namespace {
const base::TimeDelta kExpireThreshold = base::TimeDelta::FromDays(365);
std::vector<OfflinePageItem> GetPersistentPages(
const ClientPolicyController* policy_controller,
sql::Database* db) {
PageCriteria criteria;
criteria.user_requested_download = true;
return std::move(
GetPagesTask::ReadPagesWithCriteriaSync(policy_controller, criteria, db)
.pages);
}
bool SetItemsFileMissingTimeSync(const std::vector<int64_t>& item_ids,
base::Time missing_time,
sql::Database* db) {
static const char kSql[] = "UPDATE OR IGNORE offlinepages_v1"
" SET file_missing_time=?"
" WHERE offline_id=?";
for (auto offline_id : item_ids) {
sql::Statement statement(db->GetCachedStatement(SQL_FROM_HERE, kSql));
statement.BindInt64(0, store_utils::ToDatabaseTime(missing_time));
statement.BindInt64(1, offline_id);
if (!statement.Run())
return false;
}
return true;
}
bool MarkPagesAsMissing(const std::vector<int64_t>& ids_of_missing_pages,
base::Time missing_time,
sql::Database* db) {
return SetItemsFileMissingTimeSync(ids_of_missing_pages, missing_time, db);
}
bool MarkPagesAsReappeared(const std::vector<int64_t>& ids_of_reappeared_pages,
sql::Database* db) {
return SetItemsFileMissingTimeSync(ids_of_reappeared_pages, base::Time(), db);
}
PersistentPageConsistencyCheckTask::CheckResult
PersistentPageConsistencyCheckSync(
OfflinePageMetadataStore* store,
const base::FilePath& private_dir,
const base::FilePath& public_dir,
const ClientPolicyController* policy_controller,
base::Time check_time,
sql::Database* db) {
std::vector<int64_t> download_ids_of_deleted_pages;
sql::Transaction transaction(db);
if (!transaction.Begin())
return {SyncOperationResult::TRANSACTION_BEGIN_ERROR,
download_ids_of_deleted_pages};
std::vector<OfflinePageItem> persistent_page_infos =
GetPersistentPages(policy_controller, db);
std::vector<int64_t> pages_found_missing;
std::vector<int64_t> pages_reappeared;
std::vector<int64_t> page_ids_to_delete;
for (const OfflinePageItem& item : persistent_page_infos) {
if (base::PathExists(item.file_path)) {
if (item.file_missing_time != base::Time())
pages_reappeared.push_back(item.offline_id);
} else {
if (item.file_missing_time == base::Time()) {
pages_found_missing.push_back(item.offline_id);
} else {
if (check_time - item.file_missing_time > kExpireThreshold) {
page_ids_to_delete.push_back(item.offline_id);
download_ids_of_deleted_pages.push_back(item.system_download_id);
}
}
}
}
if (!DeletePageTask::DeletePagesFromDbSync(page_ids_to_delete, db) ||
!MarkPagesAsMissing(pages_found_missing, check_time, db) ||
!MarkPagesAsReappeared(pages_reappeared, db)) {
return {SyncOperationResult::DB_OPERATION_ERROR,
download_ids_of_deleted_pages};
}
if (page_ids_to_delete.size() > 0) {
UMA_HISTOGRAM_COUNTS_1M(
"OfflinePages.ConsistencyCheck.Persistent.ExpiredEntryCount",
base::saturated_cast<int32_t>(page_ids_to_delete.size()));
}
if (pages_found_missing.size() > 0) {
UMA_HISTOGRAM_COUNTS_1M(
"OfflinePages.ConsistencyCheck.Persistent.MissingFileCount",
base::saturated_cast<int32_t>(pages_found_missing.size()));
}
if (pages_reappeared.size() > 0) {
UMA_HISTOGRAM_COUNTS_1M(
"OfflinePages.ConsistencyCheck.Persistent.ReappearedFileCount",
base::saturated_cast<int32_t>(pages_reappeared.size()));
}
if (!transaction.Commit())
return {SyncOperationResult::TRANSACTION_COMMIT_ERROR,
download_ids_of_deleted_pages};
return {SyncOperationResult::SUCCESS, download_ids_of_deleted_pages};
}
} // namespace
PersistentPageConsistencyCheckTask::CheckResult::CheckResult() = default;
PersistentPageConsistencyCheckTask::CheckResult::CheckResult(
SyncOperationResult result,
const std::vector<int64_t>& system_download_ids)
: result(result), download_ids_of_deleted_pages(system_download_ids) {}
PersistentPageConsistencyCheckTask::CheckResult::CheckResult(
const CheckResult& other) = default;
PersistentPageConsistencyCheckTask::CheckResult&
PersistentPageConsistencyCheckTask::CheckResult::operator=(
const CheckResult& other) = default;
PersistentPageConsistencyCheckTask::CheckResult::~CheckResult() {}
PersistentPageConsistencyCheckTask::PersistentPageConsistencyCheckTask(
OfflinePageMetadataStore* store,
ArchiveManager* archive_manager,
ClientPolicyController* policy_controller,
base::Time check_time,
PersistentPageConsistencyCheckCallback callback)
: store_(store),
archive_manager_(archive_manager),
policy_controller_(policy_controller),
check_time_(check_time),
callback_(std::move(callback)),
weak_ptr_factory_(this) {
DCHECK(store_);
DCHECK(archive_manager_);
DCHECK(policy_controller_);
}
PersistentPageConsistencyCheckTask::~PersistentPageConsistencyCheckTask() =
default;
void PersistentPageConsistencyCheckTask::Run() {
store_->Execute(base::BindOnce(&PersistentPageConsistencyCheckSync, store_,
archive_manager_->GetPrivateArchivesDir(),
archive_manager_->GetPublicArchivesDir(),
policy_controller_, check_time_),
base::BindOnce(&PersistentPageConsistencyCheckTask::
OnPersistentPageConsistencyCheckDone,
weak_ptr_factory_.GetWeakPtr()),
CheckResult{SyncOperationResult::INVALID_DB_CONNECTION, {}});
}
void PersistentPageConsistencyCheckTask::OnPersistentPageConsistencyCheckDone(
CheckResult check_result) {
UMA_HISTOGRAM_ENUMERATION("OfflinePages.ConsistencyCheck.Persistent.Result",
check_result.result);
// If sync operation failed, invoke the callback with an empty list of
// download ids.
if (check_result.result != SyncOperationResult::SUCCESS) {
std::move(callback_).Run(false, {});
} else {
std::move(callback_).Run(true, check_result.download_ids_of_deleted_pages);
}
TaskComplete();
}
} // namespace offline_pages