blob: 57ef039971c35ca199c15c0ba8426253098d5e03 [file] [log] [blame]
//
// Copyright (C) 2022 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "host/commands/assemble_cvd/disk_builder.h"
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <android-base/file.h>
#include "common/libs/fs/shared_fd.h"
#include "common/libs/utils/files.h"
#include "host/libs/config/cuttlefish_config.h"
#include "host/libs/image_aggregator/image_aggregator.h"
#include "host/libs/vm_manager/crosvm_manager.h"
namespace cuttlefish {
static std::chrono::system_clock::time_point LastUpdatedInputDisk(
const std::vector<ImagePartition>& partitions) {
std::chrono::system_clock::time_point ret;
for (auto& partition : partitions) {
if (partition.label == "frp") {
continue;
}
auto partition_mod_time = FileModificationTime(partition.image_file_path);
if (partition_mod_time > ret) {
ret = partition_mod_time;
}
}
return ret;
}
DiskBuilder& DiskBuilder::Partitions(std::vector<ImagePartition> partitions) & {
partitions_ = std::move(partitions);
return *this;
}
DiskBuilder DiskBuilder::Partitions(std::vector<ImagePartition> partitions) && {
partitions_ = std::move(partitions);
return *this;
}
DiskBuilder& DiskBuilder::HeaderPath(std::string header_path) & {
header_path_ = std::move(header_path);
return *this;
}
DiskBuilder DiskBuilder::HeaderPath(std::string header_path) && {
header_path_ = std::move(header_path);
return *this;
}
DiskBuilder& DiskBuilder::FooterPath(std::string footer_path) & {
footer_path_ = std::move(footer_path);
return *this;
}
DiskBuilder DiskBuilder::FooterPath(std::string footer_path) && {
footer_path_ = std::move(footer_path);
return *this;
}
DiskBuilder& DiskBuilder::CrosvmPath(std::string crosvm_path) & {
crosvm_path_ = std::move(crosvm_path);
return *this;
}
DiskBuilder DiskBuilder::CrosvmPath(std::string crosvm_path) && {
crosvm_path_ = std::move(crosvm_path);
return *this;
}
DiskBuilder& DiskBuilder::VmManager(std::string vm_manager) & {
vm_manager_ = std::move(vm_manager);
return *this;
}
DiskBuilder DiskBuilder::VmManager(std::string vm_manager) && {
vm_manager_ = std::move(vm_manager);
return *this;
}
DiskBuilder& DiskBuilder::ConfigPath(std::string config_path) & {
config_path_ = std::move(config_path);
return *this;
}
DiskBuilder DiskBuilder::ConfigPath(std::string config_path) && {
config_path_ = std::move(config_path);
return *this;
}
DiskBuilder& DiskBuilder::CompositeDiskPath(std::string composite_disk_path) & {
composite_disk_path_ = std::move(composite_disk_path);
return *this;
}
DiskBuilder DiskBuilder::CompositeDiskPath(std::string composite_disk_path) && {
composite_disk_path_ = std::move(composite_disk_path);
return *this;
}
DiskBuilder& DiskBuilder::OverlayPath(std::string overlay_path) & {
overlay_path_ = std::move(overlay_path);
return *this;
}
DiskBuilder DiskBuilder::OverlayPath(std::string overlay_path) && {
overlay_path_ = std::move(overlay_path);
return *this;
}
DiskBuilder& DiskBuilder::ResumeIfPossible(bool resume_if_possible) & {
resume_if_possible_ = resume_if_possible;
return *this;
}
DiskBuilder DiskBuilder::ResumeIfPossible(bool resume_if_possible) && {
resume_if_possible_ = resume_if_possible;
return *this;
}
Result<std::string> DiskBuilder::TextConfig() {
std::ostringstream disk_conf;
CF_EXPECT(!vm_manager_.empty(), "Missing vm_manager");
disk_conf << vm_manager_ << "\n";
CF_EXPECT(!partitions_.empty(), "No partitions");
for (auto& partition : partitions_) {
disk_conf << partition.image_file_path << "\n";
}
return disk_conf.str();
}
Result<bool> DiskBuilder::WillRebuildCompositeDisk() {
if (!resume_if_possible_) {
return true;
}
CF_EXPECT(!config_path_.empty(), "No config path");
if (ReadFile(config_path_) != CF_EXPECT(TextConfig())) {
LOG(DEBUG) << "Composite disk text config mismatch";
return true;
}
CF_EXPECT(!partitions_.empty(), "No partitions");
auto last_component_mod_time = LastUpdatedInputDisk(partitions_);
CF_EXPECT(!composite_disk_path_.empty(), "No composite disk path");
auto composite_mod_time = FileModificationTime(composite_disk_path_);
if (composite_mod_time == decltype(composite_mod_time)()) {
LOG(DEBUG) << "No prior composite disk";
return true;
} else if (last_component_mod_time > composite_mod_time) {
LOG(DEBUG) << "Composite disk component file updated";
return true;
}
return false;
}
Result<bool> DiskBuilder::BuildCompositeDiskIfNecessary() {
if (!CF_EXPECT(WillRebuildCompositeDisk())) {
return false;
}
CF_EXPECT(!vm_manager_.empty());
if (vm_manager_ == vm_manager::CrosvmManager::name()) {
CF_EXPECT(!header_path_.empty(), "No header path");
CF_EXPECT(!footer_path_.empty(), "No footer path");
CreateCompositeDisk(partitions_, AbsolutePath(header_path_),
AbsolutePath(footer_path_),
AbsolutePath(composite_disk_path_));
} else {
// If this doesn't fit into the disk, it will fail while aggregating. The
// aggregator doesn't maintain any sparse attributes.
AggregateImage(partitions_, AbsolutePath(composite_disk_path_));
}
using android::base::WriteStringToFile;
CF_EXPECT(WriteStringToFile(CF_EXPECT(TextConfig()), config_path_), true);
return true;
}
Result<bool> DiskBuilder::BuildOverlayIfNecessary() {
bool can_reuse_overlay = resume_if_possible_;
CF_EXPECT(!overlay_path_.empty(), "Overlay path missing");
auto overlay_mod_time = FileModificationTime(overlay_path_);
CF_EXPECT(!composite_disk_path_.empty(), "Composite disk path missing");
auto composite_disk_mod_time = FileModificationTime(composite_disk_path_);
if (overlay_mod_time == decltype(overlay_mod_time)()) {
LOG(DEBUG) << "No prior overlay";
can_reuse_overlay = false;
} else if (overlay_mod_time < composite_disk_mod_time) {
LOG(DEBUG) << "Overlay is out of date";
can_reuse_overlay = false;
}
if (can_reuse_overlay) {
return false;
}
CF_EXPECT(!crosvm_path_.empty(), "crosvm binary missing");
CreateQcowOverlay(crosvm_path_, composite_disk_path_, overlay_path_);
return true;
}
} // namespace cuttlefish