1 //
2 // Copyright (C) 2022 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15
16 #include "host/commands/assemble_cvd/disk_builder.h"
17
18 #include <fstream>
19 #include <sstream>
20 #include <string>
21 #include <vector>
22
23 #include <android-base/file.h>
24
25 #include "common/libs/fs/shared_fd.h"
26 #include "common/libs/utils/files.h"
27 #include "host/libs/config/cuttlefish_config.h"
28 #include "host/libs/image_aggregator/image_aggregator.h"
29 #include "host/libs/vm_manager/crosvm_manager.h"
30
31 namespace cuttlefish {
32
LastUpdatedInputDisk(const std::vector<ImagePartition> & partitions)33 static std::chrono::system_clock::time_point LastUpdatedInputDisk(
34 const std::vector<ImagePartition>& partitions) {
35 std::chrono::system_clock::time_point ret;
36 for (auto& partition : partitions) {
37 if (partition.label == "frp") {
38 continue;
39 }
40
41 auto partition_mod_time = FileModificationTime(partition.image_file_path);
42 if (partition_mod_time > ret) {
43 ret = partition_mod_time;
44 }
45 }
46 return ret;
47 }
48
Partitions(std::vector<ImagePartition> partitions)49 DiskBuilder& DiskBuilder::Partitions(std::vector<ImagePartition> partitions) & {
50 partitions_ = std::move(partitions);
51 return *this;
52 }
Partitions(std::vector<ImagePartition> partitions)53 DiskBuilder DiskBuilder::Partitions(std::vector<ImagePartition> partitions) && {
54 partitions_ = std::move(partitions);
55 return *this;
56 }
57
HeaderPath(std::string header_path)58 DiskBuilder& DiskBuilder::HeaderPath(std::string header_path) & {
59 header_path_ = std::move(header_path);
60 return *this;
61 }
HeaderPath(std::string header_path)62 DiskBuilder DiskBuilder::HeaderPath(std::string header_path) && {
63 header_path_ = std::move(header_path);
64 return *this;
65 }
66
FooterPath(std::string footer_path)67 DiskBuilder& DiskBuilder::FooterPath(std::string footer_path) & {
68 footer_path_ = std::move(footer_path);
69 return *this;
70 }
FooterPath(std::string footer_path)71 DiskBuilder DiskBuilder::FooterPath(std::string footer_path) && {
72 footer_path_ = std::move(footer_path);
73 return *this;
74 }
75
CrosvmPath(std::string crosvm_path)76 DiskBuilder& DiskBuilder::CrosvmPath(std::string crosvm_path) & {
77 crosvm_path_ = std::move(crosvm_path);
78 return *this;
79 }
CrosvmPath(std::string crosvm_path)80 DiskBuilder DiskBuilder::CrosvmPath(std::string crosvm_path) && {
81 crosvm_path_ = std::move(crosvm_path);
82 return *this;
83 }
84
VmManager(std::string vm_manager)85 DiskBuilder& DiskBuilder::VmManager(std::string vm_manager) & {
86 vm_manager_ = std::move(vm_manager);
87 return *this;
88 }
VmManager(std::string vm_manager)89 DiskBuilder DiskBuilder::VmManager(std::string vm_manager) && {
90 vm_manager_ = std::move(vm_manager);
91 return *this;
92 }
93
ConfigPath(std::string config_path)94 DiskBuilder& DiskBuilder::ConfigPath(std::string config_path) & {
95 config_path_ = std::move(config_path);
96 return *this;
97 }
ConfigPath(std::string config_path)98 DiskBuilder DiskBuilder::ConfigPath(std::string config_path) && {
99 config_path_ = std::move(config_path);
100 return *this;
101 }
102
CompositeDiskPath(std::string composite_disk_path)103 DiskBuilder& DiskBuilder::CompositeDiskPath(std::string composite_disk_path) & {
104 composite_disk_path_ = std::move(composite_disk_path);
105 return *this;
106 }
CompositeDiskPath(std::string composite_disk_path)107 DiskBuilder DiskBuilder::CompositeDiskPath(std::string composite_disk_path) && {
108 composite_disk_path_ = std::move(composite_disk_path);
109 return *this;
110 }
111
OverlayPath(std::string overlay_path)112 DiskBuilder& DiskBuilder::OverlayPath(std::string overlay_path) & {
113 overlay_path_ = std::move(overlay_path);
114 return *this;
115 }
OverlayPath(std::string overlay_path)116 DiskBuilder DiskBuilder::OverlayPath(std::string overlay_path) && {
117 overlay_path_ = std::move(overlay_path);
118 return *this;
119 }
120
ResumeIfPossible(bool resume_if_possible)121 DiskBuilder& DiskBuilder::ResumeIfPossible(bool resume_if_possible) & {
122 resume_if_possible_ = resume_if_possible;
123 return *this;
124 }
ResumeIfPossible(bool resume_if_possible)125 DiskBuilder DiskBuilder::ResumeIfPossible(bool resume_if_possible) && {
126 resume_if_possible_ = resume_if_possible;
127 return *this;
128 }
129
TextConfig()130 Result<std::string> DiskBuilder::TextConfig() {
131 std::ostringstream disk_conf;
132
133 CF_EXPECT(!vm_manager_.empty(), "Missing vm_manager");
134 disk_conf << vm_manager_ << "\n";
135
136 CF_EXPECT(!partitions_.empty(), "No partitions");
137 for (auto& partition : partitions_) {
138 disk_conf << partition.image_file_path << "\n";
139 }
140 return disk_conf.str();
141 }
142
WillRebuildCompositeDisk()143 Result<bool> DiskBuilder::WillRebuildCompositeDisk() {
144 if (!resume_if_possible_) {
145 return true;
146 }
147
148 CF_EXPECT(!config_path_.empty(), "No config path");
149 if (ReadFile(config_path_) != CF_EXPECT(TextConfig())) {
150 LOG(DEBUG) << "Composite disk text config mismatch";
151 return true;
152 }
153
154 CF_EXPECT(!partitions_.empty(), "No partitions");
155 auto last_component_mod_time = LastUpdatedInputDisk(partitions_);
156
157 CF_EXPECT(!composite_disk_path_.empty(), "No composite disk path");
158 auto composite_mod_time = FileModificationTime(composite_disk_path_);
159
160 if (composite_mod_time == decltype(composite_mod_time)()) {
161 LOG(DEBUG) << "No prior composite disk";
162 return true;
163 } else if (last_component_mod_time > composite_mod_time) {
164 LOG(DEBUG) << "Composite disk component file updated";
165 return true;
166 }
167
168 return false;
169 }
170
BuildCompositeDiskIfNecessary()171 Result<bool> DiskBuilder::BuildCompositeDiskIfNecessary() {
172 if (!CF_EXPECT(WillRebuildCompositeDisk())) {
173 return false;
174 }
175
176 CF_EXPECT(!vm_manager_.empty());
177 if (vm_manager_ == vm_manager::CrosvmManager::name()) {
178 CF_EXPECT(!header_path_.empty(), "No header path");
179 CF_EXPECT(!footer_path_.empty(), "No footer path");
180 CreateCompositeDisk(partitions_, AbsolutePath(header_path_),
181 AbsolutePath(footer_path_),
182 AbsolutePath(composite_disk_path_));
183 } else {
184 // If this doesn't fit into the disk, it will fail while aggregating. The
185 // aggregator doesn't maintain any sparse attributes.
186 AggregateImage(partitions_, AbsolutePath(composite_disk_path_));
187 }
188
189 using android::base::WriteStringToFile;
190 CF_EXPECT(WriteStringToFile(CF_EXPECT(TextConfig()), config_path_), true);
191
192 return true;
193 }
194
BuildOverlayIfNecessary()195 Result<bool> DiskBuilder::BuildOverlayIfNecessary() {
196 bool can_reuse_overlay = resume_if_possible_;
197
198 CF_EXPECT(!overlay_path_.empty(), "Overlay path missing");
199 auto overlay_mod_time = FileModificationTime(overlay_path_);
200
201 CF_EXPECT(!composite_disk_path_.empty(), "Composite disk path missing");
202 auto composite_disk_mod_time = FileModificationTime(composite_disk_path_);
203 if (overlay_mod_time == decltype(overlay_mod_time)()) {
204 LOG(DEBUG) << "No prior overlay";
205 can_reuse_overlay = false;
206 } else if (overlay_mod_time < composite_disk_mod_time) {
207 LOG(DEBUG) << "Overlay is out of date";
208 can_reuse_overlay = false;
209 }
210
211 if (can_reuse_overlay) {
212 return false;
213 }
214
215 CF_EXPECT(!crosvm_path_.empty(), "crosvm binary missing");
216 CreateQcowOverlay(crosvm_path_, composite_disk_path_, overlay_path_);
217
218 return true;
219 }
220
221 } // namespace cuttlefish
222