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