• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 = HostBinaryPath("fsck.f2fs");
25   int fsck_status = 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 = 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 = 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 = HostBinaryPath("resize.f2fs");
56     int resize_status = 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 = 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     execute({"/sbin/mkfs.ext4", image});
89   } else if (image_fmt == "f2fs") {
90     auto make_f2fs_path = cuttlefish::HostBinaryPath("make_f2fs");
91     execute({make_f2fs_path, "-t", image_fmt, image, "-C", "utf8", "-O",
92              "compression,extra_attr,prjquota", "-g", "android"});
93   } else if (image_fmt == "sdcard") {
94     // Reserve 1MB in the image for the MBR and padding, to simulate what
95     // other OSes do by default when partitioning a drive
96     off_t offset_size_bytes = 1 << 20;
97     image_size_bytes -= offset_size_bytes;
98     off_t image_size_sectors = image_size_bytes / 512;
99     auto newfs_msdos_path = HostBinaryPath("newfs_msdos");
100     execute({newfs_msdos_path, "-F", "32", "-m", "0xf8", "-a", "4088",
101                                "-o", "0",  "-c", "8",    "-h", "255",
102                                "-u", "63", "-S", "512",
103                                "-s", std::to_string(image_size_sectors),
104                                "-C", std::to_string(num_mb) + "M",
105                                "-@", std::to_string(offset_size_bytes),
106                                image});
107     // Write the MBR after the filesystem is formatted, as the formatting tools
108     // don't consistently preserve the image contents
109     MasterBootRecord mbr = {
110       .partitions = {{
111         .partition_type = 0xC,
112         .first_lba = (std::uint32_t) offset_size_bytes / SECTOR_SIZE,
113         .num_sectors = (std::uint32_t) image_size_bytes / SECTOR_SIZE,
114       }},
115       .boot_signature = { 0x55, 0xAA },
116     };
117     auto fd = SharedFD::Open(image, O_RDWR);
118     if (WriteAllBinary(fd, &mbr) != sizeof(MasterBootRecord)) {
119       LOG(ERROR) << "Writing MBR to " << image << " failed:" << fd->StrError();
120       return;
121     }
122   } else if (image_fmt != "none") {
123     LOG(WARNING) << "Unknown image format '" << image_fmt
124                  << "' for " << image << ", treating as 'none'.";
125   }
126 }
127 
ApplyDataImagePolicy(const CuttlefishConfig & config,const std::string & data_image)128 DataImageResult ApplyDataImagePolicy(const CuttlefishConfig& config,
129                                      const std::string& data_image) {
130   bool data_exists = FileHasContent(data_image.c_str());
131   bool remove{};
132   bool create{};
133   bool resize{};
134 
135   if (config.data_policy() == kDataPolicyUseExisting) {
136     if (!data_exists) {
137       LOG(ERROR) << "Specified data image file does not exists: " << data_image;
138       return DataImageResult::Error;
139     }
140     if (config.blank_data_image_mb() > 0) {
141       LOG(ERROR) << "You should NOT use -blank_data_image_mb with -data_policy="
142                  << kDataPolicyUseExisting;
143       return DataImageResult::Error;
144     }
145     create = false;
146     remove = false;
147     resize = false;
148   } else if (config.data_policy() == kDataPolicyAlwaysCreate) {
149     remove = data_exists;
150     create = true;
151     resize = false;
152   } else if (config.data_policy() == kDataPolicyCreateIfMissing) {
153     create = !data_exists;
154     remove = false;
155     resize = false;
156   } else if (config.data_policy() == kDataPolicyResizeUpTo) {
157     create = false;
158     remove = false;
159     resize = true;
160   } else {
161     LOG(ERROR) << "Invalid data_policy: " << config.data_policy();
162     return DataImageResult::Error;
163   }
164 
165   if (remove) {
166     RemoveFile(data_image.c_str());
167   }
168 
169   if (create) {
170     if (config.blank_data_image_mb() <= 0) {
171       LOG(ERROR) << "-blank_data_image_mb is required to create data image";
172       return DataImageResult::Error;
173     }
174     CreateBlankImage(data_image.c_str(), config.blank_data_image_mb(),
175                      config.blank_data_image_fmt());
176     return DataImageResult::FileUpdated;
177   } else if (resize) {
178     if (!data_exists) {
179       LOG(ERROR) << data_image << " does not exist, but resizing was requested";
180       return DataImageResult::Error;
181     }
182     bool success = ResizeImage(data_image.c_str(), config.blank_data_image_mb());
183     return success ? DataImageResult::FileUpdated : DataImageResult::Error;
184   } else {
185     LOG(DEBUG) << data_image << " exists. Not creating it.";
186     return DataImageResult::NoChange;
187   }
188 }
189 
InitializeMiscImage(const std::string & misc_image)190 bool InitializeMiscImage(const std::string& misc_image) {
191   bool misc_exists = FileHasContent(misc_image.c_str());
192 
193   if (misc_exists) {
194     LOG(DEBUG) << "misc partition image: use existing";
195     return true;
196   }
197 
198   LOG(DEBUG) << "misc partition image: creating empty";
199   CreateBlankImage(misc_image, 1 /* mb */, "none");
200   return true;
201 }
202 
203 } // namespace cuttlefish
204