blob: c69cd6b16a34ea29ddddcb706dc9a35eb8ee4080 [file]
// Copyright 2026 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/update_client/op_space_check.h"
#include <stdint.h>
#include <utility>
#include "base/files/file_path.h"
#include "base/files/scoped_temp_dir.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "base/logging.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/thread_pool.h"
#include "base/types/expected.h"
#include "base/values.h"
#include "components/update_client/pipeline_util.h"
#include "components/update_client/protocol_definition.h"
#include "components/update_client/update_client_errors.h"
#include "components/update_client/utils.h"
namespace update_client {
namespace {
int64_t CalculateRequiredDiskSpace(const ProtocolParser::Pipeline& pipeline) {
int64_t download_size = 0;
int64_t expected_crx_size = 0;
for (const ProtocolParser::Operation& op : pipeline.operations) {
if (op.type == "download") {
download_size += op.size;
} else if (op.type == "crx3") {
expected_crx_size += op.size;
}
}
// Always require space for the downloaded content and the extracted CRX
// contents (estimated as the size of the CRX itself). For differential
// updates, also require space for the reconstructed CRX.
return download_size + expected_crx_size +
(download_size != expected_crx_size ? expected_crx_size : 0);
}
} // namespace
base::OnceClosure SpaceCheckOperation(
const ProtocolParser::Pipeline& pipeline,
base::RepeatingCallback<int64_t(const base::FilePath&)> get_available_space,
base::RepeatingCallback<void(base::DictValue)> event_adder,
const base::FilePath& path,
base::OnceCallback<void(base::expected<base::FilePath, CategorizedError>)>
callback) {
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE,
{base::MayBlock(), base::TaskPriority::USER_VISIBLE,
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
base::BindOnce(
[](base::RepeatingCallback<int64_t(const base::FilePath&)>
get_available_space) {
base::ScopedTempDir temp_dir;
return CreateScopedTempDirectory(temp_dir)
? get_available_space.Run(temp_dir.GetPath())
: int64_t{0};
},
get_available_space),
base::BindOnce(
[](int64_t required_bytes,
base::RepeatingCallback<void(base::DictValue)> event_adder,
base::OnceCallback<void(
base::expected<base::FilePath, CategorizedError>)> callback,
const base::FilePath& path, int64_t available_bytes) {
if (available_bytes < required_bytes) {
VLOG(1) << "available_bytes: " << available_bytes
<< ", required_bytes: " << required_bytes;
event_adder.Run(MakeSimpleOperationEvent(
CategorizedError({.category = ErrorCategory::kDownload,
.code = static_cast<int>(
CrxDownloaderError::DISK_FULL)}),
protocol_request::kEventDownload));
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback),
base::unexpected<CategorizedError>(
{.category = ErrorCategory::kDownload,
.code = static_cast<int>(
CrxDownloaderError::DISK_FULL)})));
return;
}
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), path));
},
CalculateRequiredDiskSpace(pipeline), event_adder,
std::move(callback), path));
// This operation does not support cancellation.
return base::DoNothing();
}
} // namespace update_client