• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007 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 "recovery_utils/roots.h"
18 
19 #include <fcntl.h>
20 #include <stdint.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <sys/stat.h>
24 #include <sys/types.h>
25 #include <sys/wait.h>
26 #include <unistd.h>
27 
28 #include <iostream>
29 #include <string>
30 #include <vector>
31 
32 #include <android-base/file.h>
33 #include <android-base/logging.h>
34 #include <android-base/properties.h>
35 #include <android-base/stringprintf.h>
36 #include <android-base/unique_fd.h>
37 #include <ext4_utils/ext4_utils.h>
38 #include <ext4_utils/wipe.h>
39 #include <fs_mgr.h>
40 #include <fs_mgr/roots.h>
41 #include <fstab/fstab.h>
42 
43 #include "otautil/sysutil.h"
44 
45 using android::fs_mgr::Fstab;
46 using android::fs_mgr::FstabEntry;
47 using android::fs_mgr::ReadDefaultFstab;
48 
49 static Fstab fstab;
50 
51 constexpr const char* CACHE_ROOT = "/cache";
52 
load_volume_table()53 void load_volume_table() {
54   if (!ReadDefaultFstab(&fstab)) {
55     LOG(ERROR) << "Failed to read default fstab";
56     return;
57   }
58 
59   fstab.emplace_back(FstabEntry{
60       .blk_device = "ramdisk",
61       .mount_point = "/tmp",
62       .fs_type = "ramdisk",
63       .length = 0,
64   });
65 
66   std::cout << "recovery filesystem table" << std::endl << "=========================" << std::endl;
67   for (size_t i = 0; i < fstab.size(); ++i) {
68     const auto& entry = fstab[i];
69     std::cout << "  " << i << " " << entry.mount_point << " "
70               << " " << entry.fs_type << " " << entry.blk_device << " " << entry.length
71               << std::endl;
72   }
73   std::cout << std::endl;
74 }
75 
volume_for_mount_point(const std::string & mount_point)76 Volume* volume_for_mount_point(const std::string& mount_point) {
77   return android::fs_mgr::GetEntryForMountPoint(&fstab, mount_point);
78 }
79 
80 // Mount the volume specified by path at the given mount_point.
ensure_path_mounted_at(const std::string & path,const std::string & mount_point)81 int ensure_path_mounted_at(const std::string& path, const std::string& mount_point) {
82   return android::fs_mgr::EnsurePathMounted(&fstab, path, mount_point) ? 0 : -1;
83 }
84 
ensure_path_mounted(const std::string & path)85 int ensure_path_mounted(const std::string& path) {
86   // Mount at the default mount point.
87   return android::fs_mgr::EnsurePathMounted(&fstab, path) ? 0 : -1;
88 }
89 
ensure_path_unmounted(const std::string & path)90 int ensure_path_unmounted(const std::string& path) {
91   return android::fs_mgr::EnsurePathUnmounted(&fstab, path) ? 0 : -1;
92 }
93 
BlockDevHasFstab(const std::string & path)94 bool BlockDevHasFstab(const std::string& path) {
95   std::string bdev_path;
96   if (!android::base::Realpath(path, &bdev_path)) {
97     PLOG(ERROR) << "Failed to get realpath for " << path;
98     return false;
99   }
100   for (const auto& entry : fstab) {
101     std::string fstab_bdev_path;
102     if (!android::base::Realpath(entry.blk_device, &fstab_bdev_path)) {
103       PLOG(ERROR) << "Failed to get realpath for " << entry.blk_device;
104       return false;
105     }
106     if (fstab_bdev_path == bdev_path) {
107       return true;
108     }
109   }
110   return false;
111 }
112 
exec_cmd(const std::vector<std::string> & args)113 static int exec_cmd(const std::vector<std::string>& args) {
114   CHECK(!args.empty());
115   auto argv = StringVectorToNullTerminatedArray(args);
116 
117   pid_t child;
118   if ((child = fork()) == 0) {
119     execv(argv[0], argv.data());
120     _exit(EXIT_FAILURE);
121   }
122 
123   int status;
124   waitpid(child, &status, 0);
125   if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
126     LOG(ERROR) << args[0] << " failed with status " << WEXITSTATUS(status);
127   }
128   return WEXITSTATUS(status);
129 }
130 
get_file_size(int fd,uint64_t reserve_len)131 static int64_t get_file_size(int fd, uint64_t reserve_len) {
132   struct stat buf;
133   int ret = fstat(fd, &buf);
134   if (ret) return 0;
135 
136   int64_t computed_size;
137   if (S_ISREG(buf.st_mode)) {
138     computed_size = buf.st_size - reserve_len;
139   } else if (S_ISBLK(buf.st_mode)) {
140     uint64_t block_device_size = get_block_device_size(fd);
141     if (block_device_size < reserve_len ||
142         block_device_size > std::numeric_limits<int64_t>::max()) {
143       computed_size = 0;
144     } else {
145       computed_size = block_device_size - reserve_len;
146     }
147   } else {
148     computed_size = 0;
149   }
150 
151   return computed_size;
152 }
153 
LocateFormattableEntry(const std::vector<FstabEntry * > & entries)154 static FstabEntry* LocateFormattableEntry(const std::vector<FstabEntry*>& entries) {
155   if (entries.empty()) {
156     return nullptr;
157   }
158   FstabEntry* f2fs_entry = nullptr;
159   for (auto&& entry : entries) {
160     if (getpagesize() != 4096 && entry->fs_type == "f2fs") {
161       f2fs_entry = entry;
162       continue;
163     }
164     if (f2fs_entry) {
165       LOG(INFO) << "Skipping F2FS format for block device " << entry->blk_device << " @ "
166                 << entry->mount_point
167                 << " in non-4K mode for dev option enabled devices, "
168                    "as these devices need to toggle between 4K/16K mode, and F2FS does "
169                    "not support page_size != block_size configuration.";
170     }
171     return entry;
172   }
173   if (f2fs_entry) {
174     LOG(INFO) << "Using F2FS for " << f2fs_entry->blk_device << " @ " << f2fs_entry->mount_point
175               << " even though we are in non-4K mode. Device might require a data wipe after "
176                  "going back to 4K mode, as F2FS does not support page_size != block_size";
177   }
178   return f2fs_entry;
179 }
180 
WipeBlockDevice(const char * path)181 bool WipeBlockDevice(const char* path) {
182   android::base::unique_fd fd(open(path, O_RDWR));
183   if (fd == -1) {
184     PLOG(ERROR) << "WipeBlockDevice: failed to open " << path;
185     return false;
186   }
187   int64_t device_size = get_file_size(fd.get(), 0);
188   if (device_size < 0) {
189     PLOG(ERROR) << "WipeBlockDevice: failed to determine size of " << device_size;
190     return false;
191   }
192   if (device_size == 0) {
193     PLOG(ERROR) << "WipeBlockDevice: block device " << device_size << " has 0 length, skip wiping";
194     return false;
195   }
196   if (!wipe_block_device(fd.get(), device_size)) {
197     return true;
198   }
199   PLOG(ERROR) << "Failed to wipe " << path;
200   return false;
201 }
202 
format_volume(const std::string & volume,const std::string & directory,std::string_view new_fstype)203 int format_volume(const std::string& volume, const std::string& directory,
204                   std::string_view new_fstype) {
205   const auto entries = android::fs_mgr::GetEntriesForPath(&fstab, volume);
206   if (entries.empty()) {
207     LOG(ERROR) << "unknown volume \"" << volume << "\"";
208     return -1;
209   }
210 
211   const FstabEntry* v = LocateFormattableEntry(entries);
212   if (v == nullptr) {
213     LOG(ERROR) << "Unable to find formattable entry for \"" << volume << "\"";
214     return -1;
215   }
216   if (v->fs_type == "ramdisk") {
217     LOG(ERROR) << "can't format_volume \"" << volume << "\"";
218     return -1;
219   }
220   if (v->mount_point != volume) {
221     LOG(ERROR) << "can't give path \"" << volume << "\" to format_volume";
222     return -1;
223   }
224   if (ensure_path_unmounted(volume) != 0) {
225     LOG(ERROR) << "format_volume: Failed to unmount \"" << v->mount_point << "\"";
226     return -1;
227   }
228   if (v->fs_type != "ext4" && v->fs_type != "f2fs") {
229     LOG(ERROR) << "format_volume: fs_type \"" << v->fs_type << "\" unsupported";
230     return -1;
231   }
232 
233   bool needs_casefold = false;
234 
235   if (volume == "/data") {
236     needs_casefold = android::base::GetBoolProperty("external_storage.casefold.enabled", false);
237   }
238 
239   int64_t length = 0;
240   if (v->length > 0) {
241     length = v->length;
242   } else if (v->length < 0) {
243     android::base::unique_fd fd(open(v->blk_device.c_str(), O_RDONLY));
244     if (fd == -1) {
245       PLOG(ERROR) << "format_volume: failed to open " << v->blk_device;
246       return -1;
247     }
248     length = get_file_size(fd.get(), -v->length);
249     if (length <= 0) {
250       LOG(ERROR) << "get_file_size: invalid size " << length << " for " << v->blk_device;
251       return -1;
252     }
253   }
254 
255   // If the raw disk will be used as a metadata encrypted device mapper target,
256   // next boot will do encrypt_in_place the raw disk. While fs_mgr mounts /data
257   // as RO to avoid write file operations before encrypt_inplace, this code path
258   // is not well tested so we would like to avoid it if possible. For safety,
259   // let vold do the formatting on boot for metadata encrypted devices, except
260   // when user specified a new fstype. Because init formats /data according
261   // to fstab, it's difficult to override the fstab in init.
262   if (!v->metadata_key_dir.empty() && length == 0 && new_fstype.empty()) {
263     android::base::unique_fd fd(open(v->blk_device.c_str(), O_RDWR));
264     if (fd == -1) {
265       PLOG(ERROR) << "format_volume: failed to open " << v->blk_device;
266       return -1;
267     }
268     int64_t device_size = get_file_size(fd.get(), 0);
269     if (device_size > 0 && !wipe_block_device(fd.get(), device_size)) {
270       LOG(INFO) << "format_volume: wipe metadata encrypted " << v->blk_device << " with size "
271                 << device_size;
272       return 0;
273     }
274   }
275 
276   if ((v->fs_type == "ext4" && new_fstype.empty()) || new_fstype == "ext4") {
277     LOG(INFO) << "Formatting " << v->blk_device << " as ext4";
278     static constexpr int kBlockSize = 4096;
279     std::vector<std::string> mke2fs_args = {
280       "/system/bin/mke2fs", "-F", "-t", "ext4", "-b", std::to_string(kBlockSize),
281     };
282 
283     // Following is added for Project ID's quota as they require wider inodes.
284     // The Quotas themselves are enabled by tune2fs on boot.
285     mke2fs_args.push_back("-I");
286     mke2fs_args.push_back("512");
287 
288     if (v->fs_mgr_flags.ext_meta_csum) {
289       mke2fs_args.push_back("-O");
290       mke2fs_args.push_back("metadata_csum");
291       mke2fs_args.push_back("-O");
292       mke2fs_args.push_back("64bit");
293       mke2fs_args.push_back("-O");
294       mke2fs_args.push_back("extent");
295     }
296 
297     int raid_stride = v->logical_blk_size / kBlockSize;
298     int raid_stripe_width = v->erase_blk_size / kBlockSize;
299     // stride should be the max of 8KB and logical block size
300     if (v->logical_blk_size != 0 && v->logical_blk_size < 8192) {
301       raid_stride = 8192 / kBlockSize;
302     }
303     if (v->erase_blk_size != 0 && v->logical_blk_size != 0) {
304       mke2fs_args.push_back("-E");
305       mke2fs_args.push_back(
306           android::base::StringPrintf("stride=%d,stripe-width=%d", raid_stride, raid_stripe_width));
307     }
308     mke2fs_args.push_back(v->blk_device);
309     if (length != 0) {
310       mke2fs_args.push_back(std::to_string(length / kBlockSize));
311     }
312 
313     int result = exec_cmd(mke2fs_args);
314     if (result == 0 && !directory.empty()) {
315       std::vector<std::string> e2fsdroid_args = {
316         "/system/bin/e2fsdroid", "-e", "-f", directory, "-a", volume, v->blk_device,
317       };
318       result = exec_cmd(e2fsdroid_args);
319     }
320 
321     if (result != 0) {
322       PLOG(ERROR) << "format_volume: Failed to make ext4 on " << v->blk_device;
323       return -1;
324     }
325     return 0;
326   }
327 
328   // Has to be f2fs because we checked earlier.
329   LOG(INFO) << "Formatting " << v->blk_device << " as f2fs";
330   static constexpr int kSectorSize = 4096;
331   std::vector<std::string> make_f2fs_cmd = {
332     "/system/bin/make_f2fs",
333     "-g",
334     "android",
335   };
336 
337   make_f2fs_cmd.push_back("-O");
338   make_f2fs_cmd.push_back("project_quota,extra_attr");
339 
340   if (needs_casefold) {
341     make_f2fs_cmd.push_back("-O");
342     make_f2fs_cmd.push_back("casefold");
343     make_f2fs_cmd.push_back("-C");
344     make_f2fs_cmd.push_back("utf8");
345   }
346   if (v->fs_mgr_flags.fs_compress) {
347     make_f2fs_cmd.push_back("-O");
348     make_f2fs_cmd.push_back("compression");
349     make_f2fs_cmd.push_back("-O");
350     make_f2fs_cmd.push_back("extra_attr");
351   }
352   make_f2fs_cmd.push_back("-b");
353   make_f2fs_cmd.push_back(std::to_string(getpagesize()));
354   make_f2fs_cmd.push_back(v->blk_device);
355   if (length >= kSectorSize) {
356     make_f2fs_cmd.push_back(std::to_string(length / kSectorSize));
357   }
358 
359   if (exec_cmd(make_f2fs_cmd) != 0) {
360     PLOG(ERROR) << "format_volume: Failed to make_f2fs on " << v->blk_device
361                 << " wiping the block device to avoid leaving partially formatted data.";
362     WipeBlockDevice(v->blk_device.c_str());
363     return -1;
364   }
365   if (!directory.empty()) {
366     std::vector<std::string> sload_f2fs_cmd = {
367       "/system/bin/sload_f2fs", "-f", directory, "-t", volume, v->blk_device,
368     };
369     if (exec_cmd(sload_f2fs_cmd) != 0) {
370       PLOG(ERROR) << "format_volume: Failed to sload_f2fs on " << v->blk_device;
371       return -1;
372     }
373   }
374   return 0;
375 }
376 
format_volume(const std::string & volume)377 int format_volume(const std::string& volume) {
378   return format_volume(volume, "", "");
379 }
380 
setup_install_mounts()381 int setup_install_mounts() {
382   if (fstab.empty()) {
383     LOG(ERROR) << "can't set up install mounts: no fstab loaded";
384     return -1;
385   }
386   for (const FstabEntry& entry : fstab) {
387     // We don't want to do anything with "/".
388     if (entry.mount_point == "/") {
389       continue;
390     }
391 
392     if (entry.mount_point == "/tmp" || entry.mount_point == "/cache") {
393       if (ensure_path_mounted(entry.mount_point) != 0) {
394         LOG(ERROR) << "Failed to mount " << entry.mount_point;
395         return -1;
396       }
397     } else {
398       if (ensure_path_unmounted(entry.mount_point) != 0) {
399         LOG(ERROR) << "Failed to unmount " << entry.mount_point;
400         return -1;
401       }
402     }
403   }
404   return 0;
405 }
406 
HasCache()407 bool HasCache() {
408   CHECK(!fstab.empty());
409   static bool has_cache = volume_for_mount_point(CACHE_ROOT) != nullptr;
410   return has_cache;
411 }
412