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