1 //
2 // Copyright (C) 2019 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 "super_image_mixer.h"
17
18 #include <errno.h>
19 #include <sys/stat.h>
20
21 #include <algorithm>
22 #include <cstdio>
23 #include <functional>
24 #include <memory>
25
26 #include <android-base/strings.h>
27 #include <android-base/logging.h>
28
29 #include "common/libs/fs/shared_buf.h"
30 #include "common/libs/utils/archive.h"
31 #include "common/libs/utils/files.h"
32 #include "common/libs/utils/subprocess.h"
33 #include "host/commands/assemble_cvd/misc_info.h"
34 #include "host/libs/config/cuttlefish_config.h"
35 #include "host/libs/config/fetcher_config.h"
36
37 namespace cuttlefish {
38
SuperImageNeedsRebuilding(const FetcherConfig & fetcher_config)39 bool SuperImageNeedsRebuilding(const FetcherConfig& fetcher_config) {
40 bool has_default_build = false;
41 bool has_system_build = false;
42 for (const auto& file_iter : fetcher_config.get_cvd_files()) {
43 if (file_iter.second.source == FileSource::DEFAULT_BUILD) {
44 has_default_build = true;
45 } else if (file_iter.second.source == FileSource::SYSTEM_BUILD) {
46 has_system_build = true;
47 }
48 }
49 return has_default_build && has_system_build;
50 }
51
52 namespace {
53
TargetFilesZip(const FetcherConfig & fetcher_config,FileSource source)54 std::string TargetFilesZip(const FetcherConfig& fetcher_config,
55 FileSource source) {
56 for (const auto& file_iter : fetcher_config.get_cvd_files()) {
57 const auto& file_path = file_iter.first;
58 const auto& file_info = file_iter.second;
59 if (file_info.source != source) {
60 continue;
61 }
62 std::string expected_filename = "target_files-" + file_iter.second.build_id;
63 if (file_path.find(expected_filename) != std::string::npos) {
64 return file_path;
65 }
66 }
67 return "";
68 }
69
70 const std::string kMiscInfoPath = "META/misc_info.txt";
71 const std::set<std::string> kDefaultTargetImages = {
72 "IMAGES/boot.img", "IMAGES/init_boot.img",
73 "IMAGES/odm.img", "IMAGES/odm_dlkm.img",
74 "IMAGES/recovery.img", "IMAGES/userdata.img",
75 "IMAGES/vbmeta.img", "IMAGES/vendor.img",
76 "IMAGES/vendor_dlkm.img", "IMAGES/vbmeta_vendor_dlkm.img",
77 "IMAGES/system_dlkm.img",
78 };
79 const std::set<std::string> kDefaultTargetBuildProp = {
80 "ODM/build.prop",
81 "ODM/etc/build.prop",
82 "VENDOR/build.prop",
83 "VENDOR/etc/build.prop",
84 };
85
FindImports(Archive * archive,const std::string & build_prop_file)86 void FindImports(Archive* archive, const std::string& build_prop_file) {
87 auto contents = archive->ExtractToMemory(build_prop_file);
88 auto lines = android::base::Split(contents, "\n");
89 for (const auto& line : lines) {
90 auto parts = android::base::Split(line, " ");
91 if (parts.size() >= 2 && parts[0] == "import") {
92 LOG(INFO) << build_prop_file << ": " << line;
93 }
94 }
95 }
96
CombineTargetZipFiles(const std::string & default_target_zip,const std::string & system_target_zip,const std::string & output_path)97 Result<void> CombineTargetZipFiles(const std::string& default_target_zip,
98 const std::string& system_target_zip,
99 const std::string& output_path) {
100 Archive default_target_archive(default_target_zip);
101 auto default_target_contents = default_target_archive.Contents();
102 CF_EXPECT(default_target_contents.size() != 0,
103 "Could not open " << default_target_zip);
104
105 Archive system_target_archive(system_target_zip);
106 auto system_target_contents = system_target_archive.Contents();
107 CF_EXPECT(system_target_contents.size() != 0,
108 "Could not open " << system_target_zip);
109
110 CF_EXPECT(
111 mkdir(output_path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) >= 0,
112 "Could not create directory " << output_path);
113
114 std::string output_meta = output_path + "/META";
115 CF_EXPECT(
116 mkdir(output_meta.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) >= 0,
117 "Could not create directory " << output_meta);
118
119 CF_EXPECT(
120 std::find(default_target_contents.begin(), default_target_contents.end(),
121 kMiscInfoPath) != default_target_contents.end(),
122 "Default target files zip does not have " << kMiscInfoPath);
123
124 CF_EXPECT(
125 std::find(system_target_contents.begin(), system_target_contents.end(),
126 kMiscInfoPath) != system_target_contents.end(),
127 "System target files zip does not have " << kMiscInfoPath);
128
129 const auto default_misc =
130 ParseMiscInfo(default_target_archive.ExtractToMemory(kMiscInfoPath));
131 CF_EXPECT(default_misc.size() != 0,
132 "Could not read the default misc_info.txt file.");
133
134 const auto system_misc =
135 ParseMiscInfo(system_target_archive.ExtractToMemory(kMiscInfoPath));
136 CF_EXPECT(system_misc.size() != 0,
137 "Could not read the system misc_info.txt file.");
138
139 auto output_misc = default_misc;
140 auto system_super_partitions = SuperPartitionComponents(system_misc);
141 // Ensure specific skipped partitions end up in the misc_info.txt
142 for (auto partition :
143 {"odm", "odm_dlkm", "vendor", "vendor_dlkm", "system_dlkm"}) {
144 if (std::find(system_super_partitions.begin(), system_super_partitions.end(),
145 partition) == system_super_partitions.end()) {
146 system_super_partitions.push_back(partition);
147 }
148 }
149 CF_EXPECT(SetSuperPartitionComponents(system_super_partitions, &output_misc),
150 "Failed to update super partitions components for misc_info");
151
152 auto misc_output_path = output_path + "/" + kMiscInfoPath;
153 SharedFD misc_output_file =
154 SharedFD::Creat(misc_output_path.c_str(), 0644);
155 CF_EXPECT(misc_output_file->IsOpen(), "Failed to open output misc file: "
156 << misc_output_file->StrError());
157
158 CF_EXPECT(WriteAll(misc_output_file, WriteMiscInfo(output_misc)) >= 0,
159 "Failed to write output misc file contents: "
160 << misc_output_file->StrError());
161
162 for (const auto& name : default_target_contents) {
163 if (!android::base::StartsWith(name, "IMAGES/")) {
164 continue;
165 } else if (!android::base::EndsWith(name, ".img")) {
166 continue;
167 } else if (kDefaultTargetImages.count(name) == 0) {
168 continue;
169 }
170 LOG(INFO) << "Writing " << name;
171 CF_EXPECT(default_target_archive.ExtractFiles({name}, output_path),
172 "Failed to extract " << name << " from the default target zip");
173 }
174 for (const auto& name : default_target_contents) {
175 if (!android::base::EndsWith(name, "build.prop")) {
176 continue;
177 } else if (kDefaultTargetBuildProp.count(name) == 0) {
178 continue;
179 }
180 FindImports(&default_target_archive, name);
181 LOG(INFO) << "Writing " << name;
182 CF_EXPECT(default_target_archive.ExtractFiles({name}, output_path),
183 "Failed to extract " << name << " from the default target zip");
184 }
185
186 for (const auto& name : system_target_contents) {
187 if (!android::base::StartsWith(name, "IMAGES/")) {
188 continue;
189 } else if (!android::base::EndsWith(name, ".img")) {
190 continue;
191 } else if (kDefaultTargetImages.count(name) > 0) {
192 continue;
193 }
194 LOG(INFO) << "Writing " << name;
195 CF_EXPECT(system_target_archive.ExtractFiles({name}, output_path),
196 "Failed to extract " << name << " from the system target zip");
197 }
198 for (const auto& name : system_target_contents) {
199 if (!android::base::EndsWith(name, "build.prop")) {
200 continue;
201 } else if (kDefaultTargetBuildProp.count(name) > 0) {
202 continue;
203 }
204 FindImports(&system_target_archive, name);
205 LOG(INFO) << "Writing " << name;
206 CF_EXPECT(system_target_archive.ExtractFiles({name}, output_path),
207 "Failed to extract " << name << " from the default target zip");
208 }
209
210 return {};
211 }
212
BuildSuperImage(const std::string & combined_target_zip,const std::string & output_path)213 bool BuildSuperImage(const std::string& combined_target_zip,
214 const std::string& output_path) {
215 std::string build_super_image_binary;
216 std::string otatools_path;
217 if (FileExists(DefaultHostArtifactsPath("otatools/bin/build_super_image"))) {
218 build_super_image_binary =
219 DefaultHostArtifactsPath("otatools/bin/build_super_image");
220 otatools_path = DefaultHostArtifactsPath("otatools");
221 } else if (FileExists(HostBinaryPath("build_super_image"))) {
222 build_super_image_binary =
223 HostBinaryPath("build_super_image");
224 otatools_path = DefaultHostArtifactsPath("");
225 } else {
226 LOG(ERROR) << "Could not find otatools";
227 return false;
228 }
229 return execute({
230 build_super_image_binary,
231 "--path=" + otatools_path,
232 combined_target_zip,
233 output_path,
234 }) == 0;
235 }
236
RebuildSuperImage(const FetcherConfig & fetcher_config,const CuttlefishConfig & config,const std::string & output_path)237 Result<void> RebuildSuperImage(const FetcherConfig& fetcher_config,
238 const CuttlefishConfig& config,
239 const std::string& output_path) {
240 std::string default_target_zip =
241 TargetFilesZip(fetcher_config, FileSource::DEFAULT_BUILD);
242 CF_EXPECT(default_target_zip != "",
243 "Unable to find default target zip file.");
244
245 std::string system_target_zip =
246 TargetFilesZip(fetcher_config, FileSource::SYSTEM_BUILD);
247 CF_EXPECT(system_target_zip != "", "Unable to find system target zip file.");
248
249 auto instance = config.ForDefaultInstance();
250 // TODO(schuffelen): Use cuttlefish_assembly
251 std::string combined_target_path = instance.PerInstanceInternalPath("target_combined");
252 // TODO(schuffelen): Use otatools/bin/merge_target_files
253 CF_EXPECT(CombineTargetZipFiles(default_target_zip, system_target_zip,
254 combined_target_path),
255 "Could not combine target zip files.");
256
257 CF_EXPECT(BuildSuperImage(combined_target_path, output_path),
258 "Could not write the final output super image.");
259 return {};
260 }
261
262 class SuperImageRebuilderImpl : public SuperImageRebuilder {
263 public:
INJECT(SuperImageRebuilderImpl (const FetcherConfig & fetcher_config,const CuttlefishConfig & config,const CuttlefishConfig::InstanceSpecific & instance))264 INJECT(SuperImageRebuilderImpl(
265 const FetcherConfig& fetcher_config, const CuttlefishConfig& config,
266 const CuttlefishConfig::InstanceSpecific& instance))
267 : fetcher_config_(fetcher_config), config_(config), instance_(instance) {}
268
Name() const269 std::string Name() const override { return "SuperImageRebuilderImpl"; }
Enabled() const270 bool Enabled() const override { return true; }
271
272 private:
Dependencies() const273 std::unordered_set<SetupFeature*> Dependencies() const override { return {}; }
ResultSetup()274 Result<void> ResultSetup() override {
275 if (SuperImageNeedsRebuilding(fetcher_config_)) {
276 CF_EXPECT(RebuildSuperImage(fetcher_config_, config_,
277 instance_.new_super_image()));
278 }
279 return {};
280 }
281
282 const FetcherConfig& fetcher_config_;
283 const CuttlefishConfig& config_;
284 const CuttlefishConfig::InstanceSpecific& instance_;
285 };
286
287 } // namespace
288
289 fruit::Component<fruit::Required<const FetcherConfig, const CuttlefishConfig,
290 const CuttlefishConfig::InstanceSpecific>,
291 SuperImageRebuilder>
SuperImageRebuilderComponent()292 SuperImageRebuilderComponent() {
293 return fruit::createComponent()
294 .bind<SuperImageRebuilder, SuperImageRebuilderImpl>()
295 .addMultibinding<SetupFeature, SuperImageRebuilder>();
296 }
297
298 } // namespace cuttlefish
299