1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
|
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/download/internal/background_service/file_monitor_impl.h"
#include "base/debug/alias.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/stl_util.h"
#include "base/task/sequenced_task_runner.h"
#include "base/threading/scoped_blocking_call.h"
#include "build/build_config.h"
#if BUILDFLAG(IS_CHROMEOS)
#include <grp.h>
#endif // BUILDFLAG(IS_CHROMEOS)
namespace download {
namespace {
// Creates the download directory if it doesn't exist.
bool InitializeAndCreateDownloadDirectory(const base::FilePath& dir_path) {
// Create the download directory.
bool success = base::PathExists(dir_path);
if (!success) {
base::File::Error error = base::File::Error::FILE_OK;
success = base::CreateDirectoryAndGetError(dir_path, &error);
if (!success)
stats::LogsFileDirectoryCreationError(error);
}
#if BUILDFLAG(IS_CHROMEOS)
if (success) {
// System daemons on ChromeOS may run as a user different than the Chrome
// process but need to access files under the directory created here.
// Because of that, grant the execute permission on the created directory
// to group and other users. Also chronos-access group should have read
// access to the directory.
if (HANDLE_EINTR(chmod(dir_path.value().c_str(),
S_IRWXU | S_IRGRP | S_IXGRP | S_IXOTH)) != 0) {
return false;
}
struct group grp, *result = nullptr;
std::vector<char> buffer(16384);
getgrnam_r("chronos-access", &grp, buffer.data(), buffer.size(), &result);
// Ignoring as the group might not exist in tests.
if (result) {
success =
HANDLE_EINTR(chown(dir_path.value().c_str(), -1, grp.gr_gid)) == 0;
}
}
#endif // BUILDFLAG(IS_CHROMEOS)
return success;
}
void GetFilesInDirectory(const base::FilePath& directory,
std::set<base::FilePath>& paths_out) {
base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
base::BlockingType::MAY_BLOCK);
base::FileEnumerator file_enumerator(directory, false /* recursive */,
base::FileEnumerator::FILES);
for (base::FilePath path = file_enumerator.Next(); !path.value().empty();
path = file_enumerator.Next()) {
paths_out.insert(path);
}
}
void DeleteFilesOnFileThread(const std::set<base::FilePath>& paths,
stats::FileCleanupReason reason) {
base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
base::BlockingType::MAY_BLOCK);
int num_delete_attempted = 0;
int num_delete_failed = 0;
int num_delete_by_external = 0;
int num_files = paths.size();
// Lock variables on the stack for investigating https://crbug.com/1428815
base::debug::Alias(&num_files);
base::debug::Alias(&num_delete_attempted);
for (const base::FilePath& path : paths) {
if (!base::PathExists(path)) {
num_delete_by_external++;
continue;
}
num_delete_attempted++;
DCHECK(!base::DirectoryExists(path));
if (!base::DeleteFile(path)) {
num_delete_failed++;
}
}
stats::LogFileCleanupStatus(reason, num_delete_attempted, num_delete_failed,
num_delete_by_external);
}
void DeleteUnknownFilesOnFileThread(
const base::FilePath& directory,
const std::set<base::FilePath>& download_file_paths) {
base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
base::BlockingType::MAY_BLOCK);
std::set<base::FilePath> files_in_dir;
GetFilesInDirectory(directory, files_in_dir);
std::set<base::FilePath> files_to_remove =
base::STLSetDifference<std::set<base::FilePath>>(files_in_dir,
download_file_paths);
DeleteFilesOnFileThread(files_to_remove, stats::FileCleanupReason::UNKNOWN);
}
bool HardRecoverOnFileThread(const base::FilePath& directory) {
base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
base::BlockingType::MAY_BLOCK);
std::set<base::FilePath> files_in_dir;
GetFilesInDirectory(directory, files_in_dir);
DeleteFilesOnFileThread(files_in_dir,
stats::FileCleanupReason::HARD_RECOVERY);
return InitializeAndCreateDownloadDirectory(directory);
}
} // namespace
FileMonitorImpl::FileMonitorImpl(
const base::FilePath& download_file_dir,
const scoped_refptr<base::SequencedTaskRunner>& file_thread_task_runner)
: download_file_dir_(download_file_dir),
file_thread_task_runner_(file_thread_task_runner) {}
FileMonitorImpl::~FileMonitorImpl() = default;
void FileMonitorImpl::Initialize(InitCallback callback) {
file_thread_task_runner_->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(&InitializeAndCreateDownloadDirectory, download_file_dir_),
std::move(callback));
}
void FileMonitorImpl::DeleteUnknownFiles(
const Model::EntryList& known_entries,
const std::vector<DriverEntry>& known_driver_entries,
base::OnceClosure completion_callback) {
std::set<base::FilePath> download_file_paths;
for (Entry* entry : known_entries) {
download_file_paths.insert(entry->target_file_path);
}
for (const DriverEntry& driver_entry : known_driver_entries) {
download_file_paths.insert(driver_entry.current_file_path);
}
file_thread_task_runner_->PostTaskAndReply(
FROM_HERE,
base::BindOnce(&DeleteUnknownFilesOnFileThread, download_file_dir_,
download_file_paths),
std::move(completion_callback));
}
void FileMonitorImpl::CleanupFilesForCompletedEntries(
const Model::EntryList& entries,
base::OnceClosure completion_callback) {
std::set<base::FilePath> files_to_remove;
for (auto* entry : entries) {
files_to_remove.insert(entry->target_file_path);
// TODO(xingliu): Consider logs life time after the file being deleted on
// the file thread.
stats::LogFileLifeTime(base::Time::Now() - entry->completion_time);
}
file_thread_task_runner_->PostTaskAndReply(
FROM_HERE,
base::BindOnce(&DeleteFilesOnFileThread, files_to_remove,
stats::FileCleanupReason::TIMEOUT),
std::move(completion_callback));
}
void FileMonitorImpl::DeleteFiles(
const std::set<base::FilePath>& files_to_remove,
stats::FileCleanupReason reason) {
file_thread_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&DeleteFilesOnFileThread, files_to_remove, reason));
}
void FileMonitorImpl::HardRecover(InitCallback callback) {
file_thread_task_runner_->PostTaskAndReplyWithResult(
FROM_HERE, base::BindOnce(&HardRecoverOnFileThread, download_file_dir_),
std::move(callback));
}
} // namespace download
|