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