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 
17 #include "host/commands/assemble_cvd/disk_flags.h"
18 
19 #include <sys/statvfs.h>
20 
21 #include <fstream>
22 
23 #include <android-base/logging.h>
24 #include <gflags/gflags.h>
25 
26 #include "common/libs/utils/files.h"
27 #include "common/libs/utils/subprocess.h"
28 #include "host/libs/config/cuttlefish_config.h"
29 #include "host/libs/config/data_image.h"
30 #include "host/libs/vm_manager/crosvm_manager.h"
31 #include "host/commands/assemble_cvd/assembler_defs.h"
32 #include "host/commands/assemble_cvd/boot_config.h"
33 #include "host/commands/assemble_cvd/boot_image_unpacker.h"
34 #include "host/commands/assemble_cvd/image_aggregator.h"
35 #include "host/commands/assemble_cvd/super_image_mixer.h"
36 
37 // Taken from external/avb/libavb/avb_slot_verify.c; this define is not in the headers
38 #define VBMETA_MAX_SIZE 65536ul
39 
40 using cuttlefish::AssemblerExitCodes;
41 using cuttlefish::CreateBlankImage;
42 using cuttlefish::DataImageResult;
43 using cuttlefish::InitializeMiscImage;
44 using cuttlefish::vm_manager::CrosvmManager;
45 
46 DEFINE_string(system_image_dir, cuttlefish::DefaultGuestImagePath(""),
47               "Location of the system partition images.");
48 
49 DEFINE_string(boot_env_image, "",
50               "Location of the boot environment image. If the image does not "
51               "exist, a default boot environment image is created.");
52 DEFINE_string(boot_image, "",
53               "Location of cuttlefish boot image. If empty it is assumed to be "
54               "boot.img in the directory specified by -system_image_dir.");
55 DEFINE_string(cache_image, "", "Location of the cache partition image.");
56 DEFINE_string(data_image, "", "Location of the data partition image.");
57 DEFINE_string(super_image, "", "Location of the super partition image.");
58 DEFINE_string(misc_image, "",
59               "Location of the misc partition image. If the image does not "
60               "exist, a blank new misc partition image is created.");
61 DEFINE_string(metadata_image, "", "Location of the metadata partition image "
62               "to be generated.");
63 DEFINE_string(vendor_boot_image, "",
64               "Location of cuttlefish vendor boot image. If empty it is assumed to "
65               "be vendor_boot.img in the directory specified by -system_image_dir.");
66 DEFINE_string(vbmeta_image, "",
67               "Location of cuttlefish vbmeta image. If empty it is assumed to "
68               "be vbmeta.img in the directory specified by -system_image_dir.");
69 DEFINE_string(vbmeta_system_image, "",
70               "Location of cuttlefish vbmeta_system image. If empty it is assumed to "
71               "be vbmeta_system.img in the directory specified by -system_image_dir.");
72 
73 DEFINE_int32(blank_metadata_image_mb, 16,
74              "The size of the blank metadata image to generate, MB.");
75 DEFINE_int32(blank_sdcard_image_mb, 2048,
76              "The size of the blank sdcard image to generate, MB.");
77 
78 DECLARE_string(initramfs_path);
79 DECLARE_string(kernel_path);
80 DECLARE_bool(resume);
81 
ResolveInstanceFiles()82 bool ResolveInstanceFiles() {
83   if (FLAGS_system_image_dir.empty()) {
84     LOG(ERROR) << "--system_image_dir must be specified.";
85     return false;
86   }
87 
88   // If user did not specify location of either of these files, expect them to
89   // be placed in --system_image_dir location.
90   std::string default_boot_image = FLAGS_system_image_dir + "/boot.img";
91   SetCommandLineOptionWithMode("boot_image", default_boot_image.c_str(),
92                                google::FlagSettingMode::SET_FLAGS_DEFAULT);
93   std::string default_cache_image = FLAGS_system_image_dir + "/cache.img";
94   SetCommandLineOptionWithMode("cache_image", default_cache_image.c_str(),
95                                google::FlagSettingMode::SET_FLAGS_DEFAULT);
96   std::string default_data_image = FLAGS_system_image_dir + "/userdata.img";
97   SetCommandLineOptionWithMode("data_image", default_data_image.c_str(),
98                                google::FlagSettingMode::SET_FLAGS_DEFAULT);
99   std::string default_metadata_image = FLAGS_system_image_dir + "/metadata.img";
100   SetCommandLineOptionWithMode("metadata_image", default_metadata_image.c_str(),
101                                google::FlagSettingMode::SET_FLAGS_DEFAULT);
102   std::string default_super_image = FLAGS_system_image_dir + "/super.img";
103   SetCommandLineOptionWithMode("super_image", default_super_image.c_str(),
104                                google::FlagSettingMode::SET_FLAGS_DEFAULT);
105   std::string default_misc_image = FLAGS_system_image_dir + "/misc.img";
106   SetCommandLineOptionWithMode("misc_image", default_misc_image.c_str(),
107                                google::FlagSettingMode::SET_FLAGS_DEFAULT);
108   std::string default_vendor_boot_image = FLAGS_system_image_dir
109                                         + "/vendor_boot.img";
110   SetCommandLineOptionWithMode("vendor_boot_image",
111                                default_vendor_boot_image.c_str(),
112                                google::FlagSettingMode::SET_FLAGS_DEFAULT);
113   std::string default_boot_env_image = FLAGS_system_image_dir + "/env.img";
114   SetCommandLineOptionWithMode("boot_env_image", default_boot_env_image.c_str(),
115                                google::FlagSettingMode::SET_FLAGS_DEFAULT);
116   std::string default_vbmeta_image = FLAGS_system_image_dir + "/vbmeta.img";
117   SetCommandLineOptionWithMode("vbmeta_image", default_vbmeta_image.c_str(),
118                                google::FlagSettingMode::SET_FLAGS_DEFAULT);
119   std::string default_vbmeta_system_image = FLAGS_system_image_dir
120                                           + "/vbmeta_system.img";
121   SetCommandLineOptionWithMode("vbmeta_system_image",
122                                default_vbmeta_system_image.c_str(),
123                                google::FlagSettingMode::SET_FLAGS_DEFAULT);
124 
125   return true;
126 }
127 
CreateBootImageUnpacker()128 std::unique_ptr<cuttlefish::BootImageUnpacker> CreateBootImageUnpacker() {
129   return cuttlefish::BootImageUnpacker::FromImages(
130       FLAGS_boot_image, FLAGS_vendor_boot_image);
131 }
132 
DecompressKernel(const std::string & src,const std::string & dst)133 static bool DecompressKernel(const std::string& src, const std::string& dst) {
134   cuttlefish::Command decomp_cmd(cuttlefish::DefaultHostArtifactsPath("bin/extract-vmlinux"));
135   decomp_cmd.AddParameter(src);
136   std::string current_path = getenv("PATH") == nullptr ? "" : getenv("PATH");
137   std::string bin_folder = cuttlefish::DefaultHostArtifactsPath("bin");
138   decomp_cmd.SetEnvironment({"PATH=" + current_path + ":" + bin_folder});
139   auto output_file = cuttlefish::SharedFD::Creat(dst.c_str(), 0666);
140   if (!output_file->IsOpen()) {
141     LOG(ERROR) << "Unable to create decompressed image file: "
142                << output_file->StrError();
143     return false;
144   }
145   decomp_cmd.RedirectStdIO(cuttlefish::Subprocess::StdIOChannel::kStdOut, output_file);
146   auto decomp_proc = decomp_cmd.Start();
147   return decomp_proc.Started() && decomp_proc.Wait() == 0;
148 }
149 
disk_config()150 static std::vector<ImagePartition> disk_config() {
151   std::vector<ImagePartition> partitions;
152 
153   // Note that if the positions of env or misc change, the environment for
154   // u-boot must be updated as well (see boot_config.cc and
155   // configs/cf-x86_defconfig in external/u-boot).
156   partitions.push_back(ImagePartition {
157     .label = "env",
158     .image_file_path = FLAGS_boot_env_image,
159   });
160   partitions.push_back(ImagePartition {
161     .label = "misc",
162     .image_file_path = FLAGS_misc_image,
163   });
164   partitions.push_back(ImagePartition {
165     .label = "boot_a",
166     .image_file_path = FLAGS_boot_image,
167   });
168   partitions.push_back(ImagePartition {
169     .label = "boot_b",
170     .image_file_path = FLAGS_boot_image,
171   });
172   partitions.push_back(ImagePartition {
173     .label = "vendor_boot_a",
174     .image_file_path = FLAGS_vendor_boot_image,
175   });
176   partitions.push_back(ImagePartition {
177     .label = "vendor_boot_b",
178     .image_file_path = FLAGS_vendor_boot_image,
179   });
180   partitions.push_back(ImagePartition {
181     .label = "vbmeta_a",
182     .image_file_path = FLAGS_vbmeta_image,
183   });
184   partitions.push_back(ImagePartition {
185     .label = "vbmeta_b",
186     .image_file_path = FLAGS_vbmeta_image,
187   });
188   partitions.push_back(ImagePartition {
189     .label = "vbmeta_system_a",
190     .image_file_path = FLAGS_vbmeta_system_image,
191   });
192   partitions.push_back(ImagePartition {
193     .label = "vbmeta_system_b",
194     .image_file_path = FLAGS_vbmeta_system_image,
195   });
196   partitions.push_back(ImagePartition {
197     .label = "super",
198     .image_file_path = FLAGS_super_image,
199   });
200   partitions.push_back(ImagePartition {
201     .label = "userdata",
202     .image_file_path = FLAGS_data_image,
203   });
204   partitions.push_back(ImagePartition {
205     .label = "cache",
206     .image_file_path = FLAGS_cache_image,
207   });
208   partitions.push_back(ImagePartition {
209     .label = "metadata",
210     .image_file_path = FLAGS_metadata_image,
211   });
212   return partitions;
213 }
214 
LastUpdatedInputDisk()215 static std::chrono::system_clock::time_point LastUpdatedInputDisk() {
216   std::chrono::system_clock::time_point ret;
217   for (auto& partition : disk_config()) {
218     auto partition_mod_time = cuttlefish::FileModificationTime(partition.image_file_path);
219     if (partition_mod_time > ret) {
220       ret = partition_mod_time;
221     }
222   }
223   return ret;
224 }
225 
ShouldCreateCompositeDisk(const cuttlefish::CuttlefishConfig & config)226 bool ShouldCreateCompositeDisk(const cuttlefish::CuttlefishConfig& config) {
227   if (!cuttlefish::FileExists(config.composite_disk_path())) {
228     return true;
229   }
230   auto composite_age = cuttlefish::FileModificationTime(config.composite_disk_path());
231   return composite_age < LastUpdatedInputDisk();
232 }
233 
ConcatRamdisks(const std::string & new_ramdisk_path,const std::string & ramdisk_a_path,const std::string & ramdisk_b_path)234 static bool ConcatRamdisks(
235     const std::string& new_ramdisk_path,
236     const std::string& ramdisk_a_path,
237     const std::string& ramdisk_b_path) {
238   // clear out file of any pre-existing content
239   std::ofstream new_ramdisk(new_ramdisk_path, std::ios_base::binary | std::ios_base::trunc);
240   std::ifstream ramdisk_a(ramdisk_a_path, std::ios_base::binary);
241   std::ifstream ramdisk_b(ramdisk_b_path, std::ios_base::binary);
242 
243   if(!new_ramdisk.is_open() || !ramdisk_a.is_open() || !ramdisk_b.is_open()) {
244     return false;
245   }
246 
247   new_ramdisk << ramdisk_a.rdbuf() << ramdisk_b.rdbuf();
248   return true;
249 }
250 
AvailableSpaceAtPath(const std::string & path)251 static off_t AvailableSpaceAtPath(const std::string& path) {
252   struct statvfs vfs;
253   if (statvfs(path.c_str(), &vfs) != 0) {
254     int error_num = errno;
255     LOG(ERROR) << "Could not find space available at " << path << ", error was "
256                << strerror(error_num);
257     return 0;
258   }
259   return vfs.f_bsize * vfs.f_bavail; // block size * free blocks for unprivileged users
260 }
261 
CreateCompositeDisk(const cuttlefish::CuttlefishConfig & config)262 bool CreateCompositeDisk(const cuttlefish::CuttlefishConfig& config) {
263   if (!cuttlefish::SharedFD::Open(config.composite_disk_path().c_str(), O_WRONLY | O_CREAT, 0644)->IsOpen()) {
264     LOG(ERROR) << "Could not ensure " << config.composite_disk_path() << " exists";
265     return false;
266   }
267   if (config.vm_manager() == CrosvmManager::name()) {
268     // Check if filling in the sparse image would run out of disk space.
269     auto existing_sizes = cuttlefish::SparseFileSizes(FLAGS_data_image);
270     if (existing_sizes.sparse_size == 0 && existing_sizes.disk_size == 0) {
271       LOG(ERROR) << "Unable to determine size of \"" << FLAGS_data_image
272                  << "\". Does this file exist?";
273     }
274     auto available_space = AvailableSpaceAtPath(FLAGS_data_image);
275     if (available_space < existing_sizes.sparse_size - existing_sizes.disk_size) {
276       // TODO(schuffelen): Duplicate this check in run_cvd when it can run on a separate machine
277       LOG(ERROR) << "Not enough space remaining in fs containing " << FLAGS_data_image;
278       LOG(ERROR) << "Wanted " << (existing_sizes.sparse_size - existing_sizes.disk_size);
279       LOG(ERROR) << "Got " << available_space;
280       return false;
281     } else {
282       LOG(DEBUG) << "Available space: " << available_space;
283       LOG(DEBUG) << "Sparse size of \"" << FLAGS_data_image << "\": "
284                  << existing_sizes.sparse_size;
285       LOG(DEBUG) << "Disk size of \"" << FLAGS_data_image << "\": "
286                  << existing_sizes.disk_size;
287     }
288     std::string header_path = config.AssemblyPath("gpt_header.img");
289     std::string footer_path = config.AssemblyPath("gpt_footer.img");
290     CreateCompositeDisk(disk_config(), header_path, footer_path,
291                         config.composite_disk_path());
292   } else {
293     // If this doesn't fit into the disk, it will fail while aggregating. The
294     // aggregator doesn't maintain any sparse attributes.
295     AggregateImage(disk_config(), config.composite_disk_path());
296   }
297   return true;
298 }
299 
300 const std::string kKernelDefaultPath = "kernel";
301 const std::string kInitramfsImg = "initramfs.img";
302 const std::string kRamdiskConcatExt = ".concat";
303 
CreateDynamicDiskFiles(const cuttlefish::FetcherConfig & fetcher_config,const cuttlefish::CuttlefishConfig * config,cuttlefish::BootImageUnpacker * boot_img_unpacker)304 void CreateDynamicDiskFiles(const cuttlefish::FetcherConfig& fetcher_config,
305                             const cuttlefish::CuttlefishConfig* config,
306                             cuttlefish::BootImageUnpacker* boot_img_unpacker) {
307 
308   if (!cuttlefish::FileHasContent(FLAGS_boot_image)) {
309     LOG(ERROR) << "File not found: " << FLAGS_boot_image;
310     exit(cuttlefish::kCuttlefishConfigurationInitError);
311   }
312 
313   if (!cuttlefish::FileHasContent(FLAGS_vendor_boot_image)) {
314     LOG(ERROR) << "File not found: " << FLAGS_vendor_boot_image;
315     exit(cuttlefish::kCuttlefishConfigurationInitError);
316   }
317 
318   if (!boot_img_unpacker->Unpack(config->ramdisk_image_path(),
319                                  config->vendor_ramdisk_image_path(),
320                                  config->use_unpacked_kernel()
321                                      ? config->kernel_image_path()
322                                      : "")) {
323     LOG(ERROR) << "Failed to unpack boot image";
324     exit(AssemblerExitCodes::kBootImageUnpackError);
325   }
326 
327   // TODO(134522463) as part of the bootloader refactor, repack the vendor boot
328   // image and use the bootloader to load both the boot and vendor ramdisk.
329   // Until then, this hack to get gki modules into cuttlefish will suffice.
330 
331   // If a vendor ramdisk comes in via this mechanism, let it supersede the one
332   // in the vendor boot image. This flag is what kernel presubmit testing uses
333   // to pass in the kernel ramdisk.
334 
335   // If no kernel is passed in or an initramfs is made available, the default
336   // vendor boot ramdisk or the initramfs provided should be appended to the
337   // boot ramdisk. If a kernel IS provided with no initramfs, it is safe to
338   // safe to assume that the kernel was built with no modules and expects no
339   // modules for cf to run properly.
340   std::string discovered_kernel = fetcher_config.FindCvdFileWithSuffix(kKernelDefaultPath);
341   std::string foreign_kernel = FLAGS_kernel_path.size() ? FLAGS_kernel_path : discovered_kernel;
342   std::string discovered_ramdisk = fetcher_config.FindCvdFileWithSuffix(kInitramfsImg);
343   std::string foreign_ramdisk = FLAGS_initramfs_path.size () ? FLAGS_initramfs_path : discovered_ramdisk;
344   if(!foreign_kernel.size() || foreign_ramdisk.size()) {
345     const std::string& vendor_ramdisk_path =
346       config->initramfs_path().size() ? config->initramfs_path()
347                                       : config->vendor_ramdisk_image_path();
348     if(!ConcatRamdisks(config->final_ramdisk_path(),
349                        config->ramdisk_image_path(), vendor_ramdisk_path)) {
350       LOG(ERROR) << "Failed to concatenate ramdisk and vendor ramdisk";
351       exit(AssemblerExitCodes::kInitRamFsConcatError);
352     }
353   }
354 
355   if (config->decompress_kernel()) {
356     if (!DecompressKernel(config->kernel_image_path(),
357         config->decompressed_kernel_image_path())) {
358       LOG(ERROR) << "Failed to decompress kernel";
359       exit(AssemblerExitCodes::kKernelDecompressError);
360     }
361   }
362 
363   // Create misc if necessary
364   if (!InitializeMiscImage(FLAGS_misc_image)) {
365     exit(cuttlefish::kCuttlefishConfigurationInitError);
366   }
367 
368   // Create data if necessary
369   DataImageResult dataImageResult = ApplyDataImagePolicy(*config, FLAGS_data_image);
370   if (dataImageResult == DataImageResult::Error) {
371     exit(cuttlefish::kCuttlefishConfigurationInitError);
372   }
373 
374   // Create boot_config if necessary
375   if (!InitBootloaderEnvPartition(*config, FLAGS_boot_env_image)) {
376     exit(cuttlefish::kCuttlefishConfigurationInitError);
377   }
378 
379   if (!cuttlefish::FileExists(FLAGS_metadata_image)) {
380     CreateBlankImage(FLAGS_metadata_image, FLAGS_blank_metadata_image_mb, "none");
381   }
382 
383   for (const auto& instance : config->Instances()) {
384     if (!cuttlefish::FileExists(instance.access_kregistry_path())) {
385       CreateBlankImage(instance.access_kregistry_path(), 2 /* mb */, "none");
386     }
387 
388     if (!cuttlefish::FileExists(instance.pstore_path())) {
389       CreateBlankImage(instance.pstore_path(), 2 /* mb */, "none");
390     }
391 
392     if (!cuttlefish::FileExists(instance.sdcard_path())) {
393       CreateBlankImage(instance.sdcard_path(),
394                        FLAGS_blank_sdcard_image_mb, "sdcard");
395     }
396   }
397 
398   // libavb expects to be able to read the maximum vbmeta size, so we must
399   // provide a partition which matches this or the read will fail
400   for (const auto& vbmeta_image : { FLAGS_vbmeta_image, FLAGS_vbmeta_system_image }) {
401     if (cuttlefish::FileSize(vbmeta_image) != VBMETA_MAX_SIZE) {
402       auto fd = cuttlefish::SharedFD::Open(vbmeta_image, O_RDWR);
403       if (fd->Truncate(VBMETA_MAX_SIZE) != 0) {
404         LOG(ERROR) << "`truncate --size=" << VBMETA_MAX_SIZE << " "
405                    << vbmeta_image << "` failed: " << fd->StrError();
406         exit(cuttlefish::kCuttlefishConfigurationInitError);
407       }
408     }
409   }
410 
411   if (SuperImageNeedsRebuilding(fetcher_config, *config)) {
412     if (!RebuildSuperImage(fetcher_config, *config, FLAGS_super_image)) {
413       LOG(ERROR) << "Super image rebuilding requested but could not be completed.";
414       exit(cuttlefish::kCuttlefishConfigurationInitError);
415     }
416   }
417 
418   bool oldCompositeDisk = ShouldCreateCompositeDisk(*config);
419   bool newDataImage = dataImageResult == DataImageResult::FileUpdated;
420   if (oldCompositeDisk || newDataImage) {
421     if (!CreateCompositeDisk(*config)) {
422       exit(cuttlefish::kDiskSpaceError);
423     }
424   }
425 
426   for (auto instance : config->Instances()) {
427     auto overlay_path = instance.PerInstancePath("overlay.img");
428     bool missingOverlay = !cuttlefish::FileExists(overlay_path);
429     bool newOverlay = cuttlefish::FileModificationTime(overlay_path)
430         < cuttlefish::FileModificationTime(config->composite_disk_path());
431     if (missingOverlay || oldCompositeDisk || !FLAGS_resume || newDataImage || newOverlay) {
432       if (FLAGS_resume) {
433         LOG(INFO) << "Requested to continue an existing session, (the default) "
434                   << "but the disk files have become out of date. Wiping the "
435                   << "old session files and starting a new session.";
436       }
437       CreateQcowOverlay(config->crosvm_binary(), config->composite_disk_path(), overlay_path);
438       CreateBlankImage(instance.access_kregistry_path(), 2 /* mb */, "none");
439       CreateBlankImage(instance.pstore_path(), 2 /* mb */, "none");
440     }
441   }
442 
443   for (auto instance : config->Instances()) {
444     // Check that the files exist
445     for (const auto& file : instance.virtual_disk_paths()) {
446       if (!file.empty() && !cuttlefish::FileHasContent(file.c_str())) {
447         LOG(ERROR) << "File not found: " << file;
448         exit(cuttlefish::kCuttlefishConfigurationInitError);
449       }
450     }
451   }
452 }
453