blob: d96c3d03ec9bac62170f987d10332b05cc99f1fd [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ash/crosapi/copy_migrator.h"
#include <memory>
#include <string>
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/metrics/histogram_macros.h"
#include "base/task/thread_pool.h"
#include "base/timer/elapsed_timer.h"
#include "chrome/browser/ash/crosapi/browser_data_migrator.h"
#include "chrome/browser/ash/crosapi/browser_data_migrator_util.h"
#include "chrome/browser/ash/crosapi/migration_progress_tracker.h"
#include "chrome/common/chrome_constants.h"
namespace ash {
CopyMigrator::CopyMigrator(
const base::FilePath& original_profile_dir,
const std::string& user_id_hash,
std::unique_ptr<MigrationProgressTracker> progress_tracker,
scoped_refptr<browser_data_migrator_util::CancelFlag> cancel_flag,
MigrationFinishedCallback finished_callback)
: original_profile_dir_(original_profile_dir),
user_id_hash_(user_id_hash),
progress_tracker_(std::move(progress_tracker)),
cancel_flag_(cancel_flag),
finished_callback_(std::move(finished_callback)) {}
CopyMigrator::~CopyMigrator() = default;
void CopyMigrator::Migrate() {
// Post `MigrateInternal()` to a worker thread.
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE,
{base::MayBlock(), base::TaskPriority::USER_VISIBLE,
base::TaskShutdownBehavior::BLOCK_SHUTDOWN},
base::BindOnce(&CopyMigrator::MigrateInternal, *original_profile_dir_,
std::move(progress_tracker_), cancel_flag_),
std::move(finished_callback_));
}
// static
BrowserDataMigratorImpl::MigrationResult CopyMigrator::MigrateInternal(
const base::FilePath& original_profile_dir,
std::unique_ptr<MigrationProgressTracker> progress_tracker,
scoped_refptr<browser_data_migrator_util::CancelFlag> cancel_flag) {
BrowserDataMigratorImpl::DataWipeResult data_wipe_result =
BrowserDataMigratorImpl::DataWipeResult::kSkipped;
const base::FilePath tmp_dir =
original_profile_dir.Append(browser_data_migrator_util::kTmpDir);
const base::FilePath new_user_dir =
original_profile_dir.Append(browser_data_migrator_util::kLacrosDir);
if (base::DirectoryExists(new_user_dir)) {
if (!base::DeletePathRecursively(new_user_dir)) {
PLOG(ERROR) << "Deleting " << new_user_dir.value() << " failed: ";
UMA_HISTOGRAM_ENUMERATION(kFinalStatus, FinalStatus::kDataWipeFailed);
return {BrowserDataMigratorImpl::DataWipeResult::kFailed,
{BrowserDataMigrator::ResultKind::kFailed}};
}
data_wipe_result = BrowserDataMigratorImpl::DataWipeResult::kSucceeded;
}
// Check if tmp directory already exists and delete if it does.
if (base::PathExists(tmp_dir)) {
LOG(WARNING) << browser_data_migrator_util::kTmpDir
<< " already exists indicating migration was aborted on the"
"previous attempt.";
if (!base::DeletePathRecursively(tmp_dir)) {
PLOG(ERROR) << "Failed to delete tmp dir";
UMA_HISTOGRAM_ENUMERATION(kFinalStatus, FinalStatus::kDeleteTmpDirFailed);
return {data_wipe_result, {BrowserDataMigrator::ResultKind::kFailed}};
}
}
browser_data_migrator_util::TargetItems need_copy_items =
browser_data_migrator_util::GetTargetItems(
original_profile_dir,
browser_data_migrator_util::ItemType::kNeedCopyForCopy);
browser_data_migrator_util::TargetItems lacros_items =
browser_data_migrator_util::GetTargetItems(
original_profile_dir, browser_data_migrator_util::ItemType::kLacros);
const int64_t total_copy_size =
need_copy_items.total_size + lacros_items.total_size;
progress_tracker->SetTotalSizeToCopy(total_copy_size);
base::ElapsedTimer timer;
uint64_t required_size =
browser_data_migrator_util::ExtraBytesRequiredToBeFreed(
total_copy_size, original_profile_dir);
if (required_size > 0) {
UMA_HISTOGRAM_ENUMERATION(kFinalStatus, FinalStatus::kNotEnoughSpace);
return {data_wipe_result,
{BrowserDataMigrator::ResultKind::kFailed, required_size}};
}
// Copy files to `tmp_dir`.
if (!SetupTmpDir(lacros_items, need_copy_items, tmp_dir, cancel_flag.get(),
progress_tracker.get())) {
if (base::PathExists(tmp_dir)) {
base::DeletePathRecursively(tmp_dir);
}
if (cancel_flag->IsSet()) {
LOG(WARNING) << "Migration was cancelled.";
UMA_HISTOGRAM_ENUMERATION(kFinalStatus, FinalStatus::kCancelled);
return {data_wipe_result, {BrowserDataMigrator::ResultKind::kCancelled}};
}
UMA_HISTOGRAM_ENUMERATION(kFinalStatus, FinalStatus::kCopyFailed);
return {data_wipe_result, {BrowserDataMigrator::ResultKind::kFailed}};
}
// Move `tmp_dir` to `new_user_dir`.
if (!base::Move(tmp_dir, new_user_dir)) {
PLOG(ERROR) << "Move failed";
if (base::PathExists(tmp_dir)) {
base::DeletePathRecursively(tmp_dir);
}
UMA_HISTOGRAM_ENUMERATION(kFinalStatus, FinalStatus::kMoveFailed);
return {data_wipe_result, {BrowserDataMigrator::ResultKind::kFailed}};
}
LOG(WARNING) << "BrowserDataMigratorImpl::Migrate took "
<< timer.Elapsed().InMilliseconds() << " ms and migrated "
<< total_copy_size / (1024 * 1024) << " MB.";
UMA_HISTOGRAM_ENUMERATION(kFinalStatus, FinalStatus::kSuccess);
// Record elapsed time for a successful migration.
// Record byte size. Range 0 ~ 10GB in MBs.
UMA_HISTOGRAM_CUSTOM_COUNTS(kCopiedDataSize, total_copy_size / 1024 / 1024, 1,
10000, 100);
UMA_HISTOGRAM_CUSTOM_COUNTS(
kLacrosDataSize, lacros_items.total_size / 1024 / 1024, 1, 10000, 100);
UMA_HISTOGRAM_CUSTOM_COUNTS(
kCommonDataSize, need_copy_items.total_size / 1024 / 1024, 1, 10000, 100);
UMA_HISTOGRAM_MEDIUM_TIMES(kTotalTime, timer.Elapsed());
return {data_wipe_result, {BrowserDataMigratorImpl::ResultKind::kSucceeded}};
}
// static
bool CopyMigrator::SetupTmpDir(
const browser_data_migrator_util::TargetItems& lacros_items,
const browser_data_migrator_util::TargetItems& need_copy_items,
const base::FilePath& tmp_dir,
browser_data_migrator_util::CancelFlag* cancel_flag,
MigrationProgressTracker* progress_tracker) {
if (cancel_flag->IsSet())
return false;
base::File::Error error;
if (!base::CreateDirectoryAndGetError(
tmp_dir.Append(browser_data_migrator_util::kLacrosProfilePath),
&error)) {
PLOG(ERROR) << "CreateDirectoryFailed " << error;
// Maps to histogram enum `PlatformFileError`.
UMA_HISTOGRAM_ENUMERATION(kCreateDirectoryFail, -error,
-base::File::FILE_ERROR_MAX);
return false;
}
// Copy lacros items.
base::ElapsedTimer timer_for_lacros_items;
if (!browser_data_migrator_util::CopyTargetItems(
tmp_dir.Append(browser_data_migrator_util::kLacrosProfilePath),
lacros_items, cancel_flag, progress_tracker)) {
return false;
}
UMA_HISTOGRAM_MEDIUM_TIMES(kLacrosDataTime, timer_for_lacros_items.Elapsed());
// Copy common items.
base::ElapsedTimer timer_for_need_copy_items;
if (!browser_data_migrator_util::CopyTargetItems(
tmp_dir.Append(browser_data_migrator_util::kLacrosProfilePath),
need_copy_items, cancel_flag, progress_tracker)) {
return false;
}
UMA_HISTOGRAM_MEDIUM_TIMES(kCommonDataTime,
timer_for_need_copy_items.Elapsed());
// Create `First Run` sentinel file instead of copying. This avoids copying
// from outside of cryptohome.
if (!base::WriteFile(tmp_dir.Append(chrome::kFirstRunSentinel), ""))
return false;
return true;
}
} // namespace ash