1 #include "host/libs/config/data_image.h"
2 
3 #include <android-base/logging.h>
4 
5 #include "common/libs/fs/shared_buf.h"
6 
7 #include "common/libs/utils/files.h"
8 #include "common/libs/utils/subprocess.h"
9 
10 #include "host/libs/config/mbr.h"
11 
12 namespace cuttlefish {
13 
14 namespace {
15 const std::string kDataPolicyUseExisting = "use_existing";
16 const std::string kDataPolicyCreateIfMissing = "create_if_missing";
17 const std::string kDataPolicyAlwaysCreate = "always_create";
18 const std::string kDataPolicyResizeUpTo= "resize_up_to";
19 
20 const int FSCK_ERROR_CORRECTED = 1;
21 const int FSCK_ERROR_CORRECTED_REQUIRES_REBOOT = 2;
22 
ForceFsckImage(const char * data_image)23 bool ForceFsckImage(const char* data_image) {
24   auto fsck_path = cuttlefish::DefaultHostArtifactsPath("bin/fsck.f2fs");
25   int fsck_status = cuttlefish::execute({fsck_path, "-y", "-f", data_image});
26   if (fsck_status & ~(FSCK_ERROR_CORRECTED|FSCK_ERROR_CORRECTED_REQUIRES_REBOOT)) {
27     LOG(ERROR) << "`fsck.f2fs -y -f " << data_image << "` failed with code "
28                << fsck_status;
29     return false;
30   }
31   return true;
32 }
33 
ResizeImage(const char * data_image,int data_image_mb)34 bool ResizeImage(const char* data_image, int data_image_mb) {
35   auto file_mb = cuttlefish::FileSize(data_image) >> 20;
36   if (file_mb > data_image_mb) {
37     LOG(ERROR) << data_image << " is already " << file_mb << " MB, will not "
38                << "resize down.";
39     return false;
40   } else if (file_mb == data_image_mb) {
41     LOG(INFO) << data_image << " is already the right size";
42     return true;
43   } else {
44     off_t raw_target = static_cast<off_t>(data_image_mb) << 20;
45     auto fd = cuttlefish::SharedFD::Open(data_image, O_RDWR);
46     if (fd->Truncate(raw_target) != 0) {
47       LOG(ERROR) << "`truncate --size=" << data_image_mb << "M "
48                   << data_image << "` failed:" << fd->StrError();
49       return false;
50     }
51     bool fsck_success = ForceFsckImage(data_image);
52     if (!fsck_success) {
53       return false;
54     }
55     auto resize_path = cuttlefish::DefaultHostArtifactsPath("bin/resize.f2fs");
56     int resize_status = cuttlefish::execute({resize_path, data_image});
57     if (resize_status != 0) {
58       LOG(ERROR) << "`resize.f2fs " << data_image << "` failed with code "
59                  << resize_status;
60       return false;
61     }
62     fsck_success = ForceFsckImage(data_image);
63     if (!fsck_success) {
64       return false;
65     }
66   }
67   return true;
68 }
69 } // namespace
70 
CreateBlankImage(const std::string & image,int num_mb,const std::string & image_fmt)71 void CreateBlankImage(
72     const std::string& image, int num_mb, const std::string& image_fmt) {
73   LOG(DEBUG) << "Creating " << image;
74 
75   off_t image_size_bytes = static_cast<off_t>(num_mb) << 20;
76   // The newfs_msdos tool with the mandatory -C option will do the same
77   // as below to zero the image file, so we don't need to do it here
78   if (image_fmt != "sdcard") {
79     auto fd = cuttlefish::SharedFD::Open(image, O_CREAT | O_TRUNC | O_RDWR, 0666);
80     if (fd->Truncate(image_size_bytes) != 0) {
81       LOG(ERROR) << "`truncate --size=" << num_mb << "M " << image
82                  << "` failed:" << fd->StrError();
83       return;
84     }
85   }
86 
87   if (image_fmt == "ext4") {
88     cuttlefish::execute({"/sbin/mkfs.ext4", image});
89   } else if (image_fmt == "f2fs") {
90     auto make_f2fs_path = cuttlefish::DefaultHostArtifactsPath("bin/make_f2fs");
91     cuttlefish::execute({make_f2fs_path, "-t", image_fmt, image, "-g", "android"});
92   } else if (image_fmt == "sdcard") {
93     // Reserve 1MB in the image for the MBR and padding, to simulate what
94     // other OSes do by default when partitioning a drive
95     off_t offset_size_bytes = 1 << 20;
96     image_size_bytes -= offset_size_bytes;
97     off_t image_size_sectors = image_size_bytes / 512;
98     auto newfs_msdos_path = cuttlefish::DefaultHostArtifactsPath("bin/newfs_msdos");
99     cuttlefish::execute({newfs_msdos_path, "-F", "32", "-m", "0xf8", "-a", "4088",
100                                     "-o", "0",  "-c", "8",    "-h", "255",
101                                     "-u", "63", "-S", "512",
102                                     "-s", std::to_string(image_size_sectors),
103                                     "-C", std::to_string(num_mb) + "M",
104                                     "-@", std::to_string(offset_size_bytes),
105                                     image});
106     // Write the MBR after the filesystem is formatted, as the formatting tools
107     // don't consistently preserve the image contents
108     MasterBootRecord mbr = {
109       .partitions = {{
110         .partition_type = 0xC,
111         .first_lba = (std::uint32_t) offset_size_bytes / SECTOR_SIZE,
112         .num_sectors = (std::uint32_t) image_size_bytes / SECTOR_SIZE,
113       }},
114       .boot_signature = { 0x55, 0xAA },
115     };
116     auto fd = cuttlefish::SharedFD::Open(image, O_RDWR);
117     if (cuttlefish::WriteAllBinary(fd, &mbr) != sizeof(MasterBootRecord)) {
118       LOG(ERROR) << "Writing MBR to " << image << " failed:" << fd->StrError();
119       return;
120     }
121   } else if (image_fmt != "none") {
122     LOG(WARNING) << "Unknown image format '" << image_fmt
123                  << "' for " << image << ", treating as 'none'.";
124   }
125 }
126 
ApplyDataImagePolicy(const cuttlefish::CuttlefishConfig & config,const std::string & data_image)127 DataImageResult ApplyDataImagePolicy(const cuttlefish::CuttlefishConfig& config,
128                                      const std::string& data_image) {
129   bool data_exists = cuttlefish::FileHasContent(data_image.c_str());
130   bool remove{};
131   bool create{};
132   bool resize{};
133 
134   if (config.data_policy() == kDataPolicyUseExisting) {
135     if (!data_exists) {
136       LOG(ERROR) << "Specified data image file does not exists: " << data_image;
137       return DataImageResult::Error;
138     }
139     if (config.blank_data_image_mb() > 0) {
140       LOG(ERROR) << "You should NOT use -blank_data_image_mb with -data_policy="
141                  << kDataPolicyUseExisting;
142       return DataImageResult::Error;
143     }
144     create = false;
145     remove = false;
146     resize = false;
147   } else if (config.data_policy() == kDataPolicyAlwaysCreate) {
148     remove = data_exists;
149     create = true;
150     resize = false;
151   } else if (config.data_policy() == kDataPolicyCreateIfMissing) {
152     create = !data_exists;
153     remove = false;
154     resize = false;
155   } else if (config.data_policy() == kDataPolicyResizeUpTo) {
156     create = false;
157     remove = false;
158     resize = true;
159   } else {
160     LOG(ERROR) << "Invalid data_policy: " << config.data_policy();
161     return DataImageResult::Error;
162   }
163 
164   if (remove) {
165     cuttlefish::RemoveFile(data_image.c_str());
166   }
167 
168   if (create) {
169     if (config.blank_data_image_mb() <= 0) {
170       LOG(ERROR) << "-blank_data_image_mb is required to create data image";
171       return DataImageResult::Error;
172     }
173     CreateBlankImage(data_image.c_str(), config.blank_data_image_mb(),
174                      config.blank_data_image_fmt());
175     return DataImageResult::FileUpdated;
176   } else if (resize) {
177     if (!data_exists) {
178       LOG(ERROR) << data_image << " does not exist, but resizing was requested";
179       return DataImageResult::Error;
180     }
181     bool success = ResizeImage(data_image.c_str(), config.blank_data_image_mb());
182     return success ? DataImageResult::FileUpdated : DataImageResult::Error;
183   } else {
184     LOG(DEBUG) << data_image << " exists. Not creating it.";
185     return DataImageResult::NoChange;
186   }
187 }
188 
InitializeMiscImage(const std::string & misc_image)189 bool InitializeMiscImage(const std::string& misc_image) {
190   bool misc_exists = cuttlefish::FileHasContent(misc_image.c_str());
191 
192   if (misc_exists) {
193     LOG(DEBUG) << "misc partition image: use existing";
194     return true;
195   }
196 
197   LOG(DEBUG) << "misc partition image: creating empty";
198   CreateBlankImage(misc_image, 1 /* mb */, "none");
199   return true;
200 }
201 
202 } // namespace cuttlefish
203