• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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