• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright (C) 2023 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 <android-base/file.h>
17 #include <android-base/logging.h>
18 #include <android-base/strings.h>
19 
20 #include <fcntl.h>
21 
22 #include <fcntl.h>
23 #include <map>
24 #include <queue>
25 #include <set>
26 #include <sstream>
27 #include <string>
28 #include <vector>
29 
30 #include "common/libs/utils/files.h"
31 #include "common/libs/utils/subprocess.h"
32 #include "host/commands/assemble_cvd/boot_image_utils.h"
33 #include "host/commands/assemble_cvd/ramdisk_modules.h"
34 #include "host/libs/config/cuttlefish_config.h"
35 
36 namespace cuttlefish {
37 
38 namespace {
39 
RoundDown(size_t a,size_t divisor)40 constexpr size_t RoundDown(size_t a, size_t divisor) {
41   return a / divisor * divisor;
42 }
43 
RoundUp(size_t a,size_t divisor)44 constexpr size_t RoundUp(size_t a, size_t divisor) {
45   return RoundDown(a + divisor, divisor);
46 }
47 
48 template <typename Container>
WriteLinesToFile(const Container & lines,const char * path)49 bool WriteLinesToFile(const Container& lines, const char* path) {
50   android::base::unique_fd fd(
51       open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0640));
52   if (!fd.ok()) {
53     PLOG(ERROR) << "Failed to open " << path;
54     return false;
55   }
56   for (const auto& line : lines) {
57     if (!android::base::WriteFully(fd, line.data(), line.size())) {
58       PLOG(ERROR) << "Failed to write to " << path;
59       return false;
60     }
61     const char c = '\n';
62     if (write(fd.get(), &c, 1) != 1) {
63       PLOG(ERROR) << "Failed to write to " << path;
64       return false;
65     }
66   }
67   return true;
68 }
69 
70 
71 // Generate a filesystem_config.txt for all files in |fs_root|
WriteFsConfig(const char * output_path,const std::string & fs_root,const std::string & mount_point)72 bool WriteFsConfig(const char* output_path, const std::string& fs_root,
73                    const std::string& mount_point) {
74   android::base::unique_fd fd(
75       open(output_path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644));
76   if (!fd.ok()) {
77     PLOG(ERROR) << "Failed to open " << output_path;
78     return false;
79   }
80   if (!android::base::WriteStringToFd(
81           " 0 0 755 selabel=u:object_r:rootfs:s0 capabilities=0x0\n", fd)) {
82     PLOG(ERROR) << "Failed to write to " << output_path;
83     return false;
84   }
85   WalkDirectory(fs_root, [&fd, &output_path, &mount_point,
86                           &fs_root](const std::string& file_path) {
87     const auto filename = file_path.substr(
88         fs_root.back() == '/' ? fs_root.size() : fs_root.size() + 1);
89     std::string fs_context = " 0 0 644 capabilities=0x0\n";
90     if (DirectoryExists(file_path)) {
91       fs_context = " 0 0 755 capabilities=0x0\n";
92     }
93     if (!android::base::WriteStringToFd(
94             mount_point + "/" + filename + fs_context, fd)) {
95       PLOG(ERROR) << "Failed to write to " << output_path;
96       return false;
97     }
98     return true;
99   });
100   return true;
101 }
102 
GetRamdiskModules(const std::vector<std::string> & all_modules)103 std::vector<std::string> GetRamdiskModules(
104     const std::vector<std::string>& all_modules) {
105   static const auto ramdisk_modules_allow_list =
106       std::set<std::string>(RAMDISK_MODULES.begin(), RAMDISK_MODULES.end());
107   std::vector<std::string> ramdisk_modules;
108   for (const auto& mod_path : all_modules) {
109     if (mod_path.empty()) {
110       continue;
111     }
112     const auto mod_name = cpp_basename(mod_path);
113     if (ramdisk_modules_allow_list.count(mod_name) != 0) {
114       ramdisk_modules.emplace_back(mod_path);
115     }
116   }
117   return ramdisk_modules;
118 }
119 
120 // Filter the dependency map |deps| to only contain nodes in |allow_list|
FilterDependencies(const std::map<std::string,std::vector<std::string>> & deps,const std::set<std::string> & allow_list)121 std::map<std::string, std::vector<std::string>> FilterDependencies(
122     const std::map<std::string, std::vector<std::string>>& deps,
123     const std::set<std::string>& allow_list) {
124   std::map<std::string, std::vector<std::string>> new_deps;
125   for (const auto& mod_name : allow_list) {
126     new_deps[mod_name].clear();
127   }
128   for (const auto& [mod_name, children] : deps) {
129     if (!allow_list.count(mod_name)) {
130       continue;
131     }
132     for (const auto& child : children) {
133       if (!allow_list.count(child)) {
134         continue;
135       }
136       new_deps[mod_name].emplace_back(child);
137     }
138   }
139   return new_deps;
140 }
141 
142 // Write dependency map to modules.dep file
WriteDepsToFile(const std::map<std::string,std::vector<std::string>> & deps,const std::string & output_path)143 bool WriteDepsToFile(
144     const std::map<std::string, std::vector<std::string>>& deps,
145     const std::string& output_path) {
146   std::stringstream ss;
147   for (const auto& [key, val] : deps) {
148     ss << key << ":";
149     for (const auto& dep : val) {
150       ss << " " << dep;
151     }
152     ss << "\n";
153   }
154   if (!android::base::WriteStringToFile(ss.str(), output_path)) {
155     PLOG(ERROR) << "Failed to write modules.dep to " << output_path;
156     return false;
157   }
158   return true;
159 }
160 
161 // Parse modules.dep into an in-memory data structure, key is path to a kernel
162 // module, value is all dependency modules
LoadModuleDeps(const std::string & filename)163 std::map<std::string, std::vector<std::string>> LoadModuleDeps(
164     const std::string& filename) {
165   std::map<std::string, std::vector<std::string>> dependency_map;
166   const auto dep_str = android::base::Trim(ReadFile(filename));
167   const auto dep_lines = android::base::Split(dep_str, "\n");
168   for (const auto& line : dep_lines) {
169     const auto mod_name = line.substr(0, line.find(":"));
170     const auto deps =
171         android::base::Tokenize(line.substr(mod_name.size() + 1), " ");
172     if (!deps.empty()) {
173       dependency_map[mod_name] = deps;
174     }
175   }
176 
177   return dependency_map;
178 }
179 
180 // Recursively compute all modules which |start_nodes| depend on
ComputeTransitiveClosure(const std::vector<std::string> & start_nodes,const std::map<std::string,std::vector<std::string>> & dependencies)181 std::set<std::string> ComputeTransitiveClosure(
182     const std::vector<std::string>& start_nodes,
183     const std::map<std::string, std::vector<std::string>>& dependencies) {
184   std::deque<std::string> queue(start_nodes.begin(), start_nodes.end());
185   std::set<std::string> visited;
186   while (!queue.empty()) {
187     const auto cur = queue.front();
188     queue.pop_front();
189     if (visited.find(cur) != visited.end()) {
190       continue;
191     }
192     visited.insert(cur);
193     const auto it = dependencies.find(cur);
194     if (it == dependencies.end()) {
195       continue;
196     }
197     for (const auto& dep : it->second) {
198       queue.emplace_back(dep);
199     }
200   }
201   return visited;
202 }
203 
GenerateFileContexts(const char * output_path,const std::string & mount_point)204 bool GenerateFileContexts(const char* output_path,
205                           const std::string& mount_point) {
206   const auto file_contexts_txt = std::string(output_path) + ".txt";
207   android::base::unique_fd fd(open(file_contexts_txt.c_str(),
208                                    O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC,
209                                    0644));
210   if (!fd.ok()) {
211     PLOG(ERROR) << "Failed to open " << output_path;
212     return false;
213   }
214   if (!android::base::WriteStringToFd(mount_point +
215                                           "(/.*)?       "
216                                           "  u:object_r:vendor_file:s0\n",
217                                       fd)) {
218     return false;
219   }
220   if (!android::base::WriteStringToFd(
221           mount_point + "/etc(/.*)?       "
222                         "  u:object_r:vendor_configs_file:s0\n",
223           fd)) {
224     return false;
225   }
226   Command cmd(HostBinaryPath("sefcontext_compile"));
227   cmd.AddParameter("-o");
228   cmd.AddParameter(output_path);
229   cmd.AddParameter(file_contexts_txt);
230   const auto exit_code = cmd.Start().Wait();
231   return exit_code == 0;
232 }
233 
AddVbmetaFooter(const std::string & output_image,const std::string & partition_name)234 bool AddVbmetaFooter(const std::string& output_image,
235                      const std::string& partition_name) {
236   auto avbtool_path = HostBinaryPath("avbtool");
237   Command avb_cmd(avbtool_path);
238   // Add host binary path to PATH, so that avbtool can locate host util
239   // binaries such as 'fec'
240   auto PATH =
241       StringFromEnv("PATH", "") + ":" + cpp_dirname(avb_cmd.Executable());
242   // Must unset an existing environment variable in order to modify it
243   avb_cmd.UnsetFromEnvironment("PATH");
244   avb_cmd.AddEnvironmentVariable("PATH", PATH);
245 
246   avb_cmd.AddParameter("add_hashtree_footer");
247   // Arbitrary salt to keep output consistent
248   avb_cmd.AddParameter("--salt");
249   avb_cmd.AddParameter("62BBAAA0", "E4BD99E783AC");
250   avb_cmd.AddParameter("--image");
251   avb_cmd.AddParameter(output_image);
252   avb_cmd.AddParameter("--partition_name");
253   avb_cmd.AddParameter(partition_name);
254 
255   auto exit_code = avb_cmd.Start().Wait();
256   if (exit_code != 0) {
257     LOG(ERROR) << "Failed to add avb footer to image " << output_image;
258     return false;
259   }
260 
261   return true;
262 }
263 
264 }  // namespace
265 
266 // Steps for building a vendor_dlkm.img:
267 // 1. Generate filesystem_config.txt , which contains standard linux file
268 // permissions, we use 0755 for directories, and 0644 for all files
269 // 2. Write file_contexts, which contains all selinux labels
270 // 3. Call  sefcontext_compile to compile file_contexts
271 // 4. call mkuserimg_mke2fs to build an image, using filesystem_config and
272 // file_contexts previously generated
273 // 5. call avbtool to add hashtree footer, so that init/bootloader can verify
274 // AVB chain
BuildVendorDLKM(const std::string & src_dir,const bool is_erofs,const std::string & output_image)275 bool BuildVendorDLKM(const std::string& src_dir, const bool is_erofs,
276                      const std::string& output_image) {
277   if (is_erofs) {
278     LOG(ERROR)
279         << "Building vendor_dlkm in EROFS format is currently not supported!";
280     return false;
281   }
282   const auto fs_config = output_image + ".fs_config";
283   if (!WriteFsConfig(fs_config.c_str(), src_dir, "/vendor_dlkm")) {
284     return false;
285   }
286   const auto file_contexts_bin = output_image + ".file_contexts";
287   if (!GenerateFileContexts(file_contexts_bin.c_str(), "/vendor_dlkm")) {
288     return false;
289   }
290 
291   // We are using directory size as an estimate of final image size. To avoid
292   // any rounding errors, add 16M of head room.
293   const auto fs_size = RoundUp(GetDiskUsage(src_dir) + 16 * 1024 * 1024, 4096);
294   LOG(INFO) << "vendor_dlkm src dir " << src_dir << " has size "
295             << fs_size / 1024 << " KB";
296   const auto mkfs = HostBinaryPath("mkuserimg_mke2fs");
297   Command mkfs_cmd(mkfs);
298   // Arbitrary UUID/seed, just to keep output consistent between runs
299   mkfs_cmd.AddParameter("--mke2fs_uuid");
300   mkfs_cmd.AddParameter("cb09b942-ed4e-46a1-81dd-7d535bf6c4b1");
301   mkfs_cmd.AddParameter("--mke2fs_hash_seed");
302   mkfs_cmd.AddParameter("765d8aba-d93f-465a-9fcf-14bb794eb7f4");
303   // Arbitrary date, just to keep output consistent
304   mkfs_cmd.AddParameter("-T");
305   mkfs_cmd.AddParameter("900979200000");
306 
307   // selinux permission to keep selinux happy
308   mkfs_cmd.AddParameter("--fs_config");
309   mkfs_cmd.AddParameter(fs_config);
310 
311   mkfs_cmd.AddParameter(src_dir);
312   mkfs_cmd.AddParameter(output_image);
313   mkfs_cmd.AddParameter("ext4");
314   mkfs_cmd.AddParameter("/vendor_dlkm");
315   mkfs_cmd.AddParameter(std::to_string(fs_size));
316   mkfs_cmd.AddParameter(file_contexts_bin);
317 
318   int exit_code = mkfs_cmd.Start().Wait();
319   if (exit_code != 0) {
320     LOG(ERROR) << "Failed to build vendor_dlkm ext4 image";
321     return false;
322   }
323   return AddVbmetaFooter(output_image, "vendor_dlkm");
324 }
325 
RepackSuperWithVendorDLKM(const std::string & superimg_path,const std::string & vendor_dlkm_path)326 bool RepackSuperWithVendorDLKM(const std::string& superimg_path,
327                                const std::string& vendor_dlkm_path) {
328   Command lpadd(HostBinaryPath("lpadd"));
329   lpadd.AddParameter("--replace");
330   lpadd.AddParameter(superimg_path);
331   lpadd.AddParameter("vendor_dlkm_a");
332   lpadd.AddParameter("google_vendor_dynamic_partitions_a");
333   lpadd.AddParameter(vendor_dlkm_path);
334   const auto exit_code = lpadd.Start().Wait();
335   return exit_code == 0;
336 }
337 
RebuildVbmetaVendor(const std::string & vendor_dlkm_img,const std::string & vbmeta_path)338 bool RebuildVbmetaVendor(const std::string& vendor_dlkm_img,
339                          const std::string& vbmeta_path) {
340   auto avbtool_path = HostBinaryPath("avbtool");
341   Command vbmeta_cmd(avbtool_path);
342   vbmeta_cmd.AddParameter("make_vbmeta_image");
343   vbmeta_cmd.AddParameter("--output");
344   vbmeta_cmd.AddParameter(vbmeta_path);
345   vbmeta_cmd.AddParameter("--algorithm");
346   vbmeta_cmd.AddParameter("SHA256_RSA4096");
347   vbmeta_cmd.AddParameter("--key");
348   vbmeta_cmd.AddParameter(DefaultHostArtifactsPath("etc/cvd_avb_testkey.pem"));
349 
350   vbmeta_cmd.AddParameter("--include_descriptors_from_image");
351   vbmeta_cmd.AddParameter(vendor_dlkm_img);
352   vbmeta_cmd.AddParameter("--padding_size");
353   vbmeta_cmd.AddParameter("4096");
354 
355   bool success = vbmeta_cmd.Start().Wait();
356   if (success != 0) {
357     LOG(ERROR) << "Unable to create vbmeta. Exited with status " << success;
358     return false;
359   }
360 
361   const auto vbmeta_size = FileSize(vbmeta_path);
362   if (vbmeta_size > VBMETA_MAX_SIZE) {
363     LOG(ERROR) << "Generated vbmeta - " << vbmeta_path
364                << " is larger than the expected " << VBMETA_MAX_SIZE
365                << ". Stopping.";
366     return false;
367   }
368   if (vbmeta_size != VBMETA_MAX_SIZE) {
369     auto fd = SharedFD::Open(vbmeta_path, O_RDWR | O_CLOEXEC);
370     if (!fd->IsOpen() || fd->Truncate(VBMETA_MAX_SIZE) != 0) {
371       LOG(ERROR) << "`truncate --size=" << VBMETA_MAX_SIZE << " " << vbmeta_path
372                  << "` failed: " << fd->StrError();
373       return false;
374     }
375   }
376   return true;
377 }
378 
SplitRamdiskModules(const std::string & ramdisk_path,const std::string & ramdisk_stage_dir,const std::string & vendor_dlkm_build_dir)379 bool SplitRamdiskModules(const std::string& ramdisk_path,
380                          const std::string& ramdisk_stage_dir,
381                          const std::string& vendor_dlkm_build_dir) {
382   const auto target_modules_dir = vendor_dlkm_build_dir + "/lib/modules";
383   const auto ret = EnsureDirectoryExists(target_modules_dir);
384   CHECK(ret.ok()) << ret.error().Message();
385   UnpackRamdisk(ramdisk_path, ramdisk_stage_dir);
386   const auto module_load_file =
387       android::base::Trim(FindFile(ramdisk_stage_dir.c_str(), "modules.load"));
388   if (module_load_file.empty()) {
389     LOG(ERROR) << "Failed to find modules.dep file in input ramdisk "
390                << ramdisk_path;
391     return false;
392   }
393   LOG(INFO) << "modules.load location " << module_load_file;
394   const auto module_list =
395       android::base::Tokenize(ReadFile(module_load_file), "\n");
396   const auto module_base_dir = cpp_dirname(module_load_file);
397   const auto deps = LoadModuleDeps(module_base_dir + "/modules.dep");
398   const auto ramdisk_modules =
399       ComputeTransitiveClosure(GetRamdiskModules(module_list), deps);
400   std::set<std::string> vendor_dlkm_modules;
401 
402   // Move non-ramdisk modules to vendor_dlkm
403   for (const auto& module_path : module_list) {
404     if (!ramdisk_modules.count(module_path)) {
405       const auto vendor_dlkm_module_location =
406           target_modules_dir + "/" + module_path;
407       EnsureDirectoryExists(cpp_dirname(vendor_dlkm_module_location));
408       RenameFile(module_base_dir + "/" + module_path,
409                  vendor_dlkm_module_location);
410       vendor_dlkm_modules.emplace(module_path);
411     }
412   }
413   LOG(INFO) << "There are " << ramdisk_modules.size() << " ramdisk modules and "
414             << vendor_dlkm_modules.size() << " vendor_dlkm modules";
415 
416   // Write updated modules.dep and modules.load files
417   CHECK(WriteDepsToFile(FilterDependencies(deps, ramdisk_modules),
418                         module_base_dir + "/modules.dep"));
419   CHECK(WriteDepsToFile(FilterDependencies(deps, vendor_dlkm_modules),
420                         target_modules_dir + "/modules.dep"));
421   CHECK(WriteLinesToFile(ramdisk_modules, module_load_file.c_str()));
422   CHECK(WriteLinesToFile(vendor_dlkm_modules,
423                          (target_modules_dir + "/modules.load").c_str()));
424   PackRamdisk(ramdisk_stage_dir, ramdisk_path);
425   return true;
426 }
427 
428 }  // namespace cuttlefish
429