1 /*
2 * Copyright (C) 2014 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 <ctype.h>
18 #include <dirent.h>
19 #include <errno.h>
20 #include <fnmatch.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <sys/mount.h>
25 #include <unistd.h>
26
27 #include <algorithm>
28 #include <array>
29 #include <utility>
30 #include <vector>
31
32 #include <android-base/file.h>
33 #include <android-base/parseint.h>
34 #include <android-base/properties.h>
35 #include <android-base/stringprintf.h>
36 #include <android-base/strings.h>
37 #include <libgsi/libgsi.h>
38
39 #include "fs_mgr_priv.h"
40
41 using android::base::EndsWith;
42 using android::base::ParseByteCount;
43 using android::base::ParseInt;
44 using android::base::ReadFileToString;
45 using android::base::Readlink;
46 using android::base::Split;
47 using android::base::StartsWith;
48
49 namespace android {
50 namespace fs_mgr {
51 namespace {
52
53 constexpr char kDefaultAndroidDtDir[] = "/proc/device-tree/firmware/android";
54
55 struct FlagList {
56 const char *name;
57 uint64_t flag;
58 };
59
60 FlagList kMountFlagsList[] = {
61 {"noatime", MS_NOATIME},
62 {"noexec", MS_NOEXEC},
63 {"nosuid", MS_NOSUID},
64 {"nodev", MS_NODEV},
65 {"nodiratime", MS_NODIRATIME},
66 {"ro", MS_RDONLY},
67 {"rw", 0},
68 {"sync", MS_SYNCHRONOUS},
69 {"remount", MS_REMOUNT},
70 {"bind", MS_BIND},
71 {"rec", MS_REC},
72 {"unbindable", MS_UNBINDABLE},
73 {"private", MS_PRIVATE},
74 {"slave", MS_SLAVE},
75 {"shared", MS_SHARED},
76 {"defaults", 0},
77 };
78
CalculateZramSize(int percentage)79 off64_t CalculateZramSize(int percentage) {
80 off64_t total;
81
82 total = sysconf(_SC_PHYS_PAGES);
83 total *= percentage;
84 total /= 100;
85
86 total *= sysconf(_SC_PAGESIZE);
87
88 return total;
89 }
90
91 // Fills 'dt_value' with the underlying device tree value string without the trailing '\0'.
92 // Returns true if 'dt_value' has a valid string, 'false' otherwise.
ReadDtFile(const std::string & file_name,std::string * dt_value)93 bool ReadDtFile(const std::string& file_name, std::string* dt_value) {
94 if (android::base::ReadFileToString(file_name, dt_value)) {
95 if (!dt_value->empty()) {
96 // Trim the trailing '\0' out, otherwise the comparison will produce false-negatives.
97 dt_value->resize(dt_value->size() - 1);
98 return true;
99 }
100 }
101
102 return false;
103 }
104
ParseFileEncryption(const std::string & arg,FstabEntry * entry)105 void ParseFileEncryption(const std::string& arg, FstabEntry* entry) {
106 entry->fs_mgr_flags.file_encryption = true;
107 entry->encryption_options = arg;
108 }
109
SetMountFlag(const std::string & flag,FstabEntry * entry)110 bool SetMountFlag(const std::string& flag, FstabEntry* entry) {
111 for (const auto& [name, value] : kMountFlagsList) {
112 if (flag == name) {
113 entry->flags |= value;
114 return true;
115 }
116 }
117 return false;
118 }
119
ParseMountFlags(const std::string & flags,FstabEntry * entry)120 void ParseMountFlags(const std::string& flags, FstabEntry* entry) {
121 std::string fs_options;
122 for (const auto& flag : Split(flags, ",")) {
123 if (!SetMountFlag(flag, entry)) {
124 // Unknown flag, so it must be a filesystem specific option.
125 if (!fs_options.empty()) {
126 fs_options.append(","); // appends a comma if not the first
127 }
128 fs_options.append(flag);
129
130 if (auto equal_sign = flag.find('='); equal_sign != std::string::npos) {
131 const auto arg = flag.substr(equal_sign + 1);
132 if (entry->fs_type == "f2fs" && StartsWith(flag, "reserve_root=")) {
133 if (!ParseInt(arg, &entry->reserved_size)) {
134 LWARNING << "Warning: reserve_root= flag malformed: " << arg;
135 } else {
136 entry->reserved_size <<= 12;
137 }
138 } else if (StartsWith(flag, "lowerdir=")) {
139 entry->lowerdir = std::move(arg);
140 }
141 }
142 }
143 }
144 entry->fs_options = std::move(fs_options);
145 }
146
ParseFsMgrFlags(const std::string & flags,FstabEntry * entry)147 void ParseFsMgrFlags(const std::string& flags, FstabEntry* entry) {
148 for (const auto& flag : Split(flags, ",")) {
149 if (flag.empty() || flag == "defaults") continue;
150 std::string arg;
151 if (auto equal_sign = flag.find('='); equal_sign != std::string::npos) {
152 arg = flag.substr(equal_sign + 1);
153 }
154
155 // First handle flags that simply set a boolean.
156 #define CheckFlag(flag_name, value) \
157 if (flag == flag_name) { \
158 entry->fs_mgr_flags.value = true; \
159 continue; \
160 }
161
162 CheckFlag("wait", wait);
163 CheckFlag("check", check);
164 CheckFlag("nonremovable", nonremovable);
165 CheckFlag("recoveryonly", recovery_only);
166 CheckFlag("noemulatedsd", no_emulated_sd);
167 CheckFlag("notrim", no_trim);
168 CheckFlag("verify", verify);
169 CheckFlag("formattable", formattable);
170 CheckFlag("slotselect", slot_select);
171 CheckFlag("latemount", late_mount);
172 CheckFlag("nofail", no_fail);
173 CheckFlag("verifyatboot", verify_at_boot);
174 CheckFlag("quota", quota);
175 CheckFlag("avb", avb);
176 CheckFlag("logical", logical);
177 CheckFlag("checkpoint=block", checkpoint_blk);
178 CheckFlag("checkpoint=fs", checkpoint_fs);
179 CheckFlag("first_stage_mount", first_stage_mount);
180 CheckFlag("slotselect_other", slot_select_other);
181 CheckFlag("fsverity", fs_verity);
182 CheckFlag("metadata_csum", ext_meta_csum);
183 CheckFlag("fscompress", fs_compress);
184
185 #undef CheckFlag
186
187 // Then handle flags that take an argument.
188 if (StartsWith(flag, "encryptable=")) {
189 // The encryptable flag is followed by an = and the location of the keys.
190 entry->fs_mgr_flags.crypt = true;
191 entry->key_loc = arg;
192 } else if (StartsWith(flag, "voldmanaged=")) {
193 // The voldmanaged flag is followed by an = and the label, a colon and the partition
194 // number or the word "auto", e.g. voldmanaged=sdcard:3
195 entry->fs_mgr_flags.vold_managed = true;
196 auto parts = Split(arg, ":");
197 if (parts.size() != 2) {
198 LWARNING << "Warning: voldmanaged= flag malformed: " << arg;
199 continue;
200 }
201
202 entry->label = std::move(parts[0]);
203 if (parts[1] == "auto") {
204 entry->partnum = -1;
205 } else {
206 if (!ParseInt(parts[1], &entry->partnum)) {
207 entry->partnum = -1;
208 LWARNING << "Warning: voldmanaged= flag malformed: " << arg;
209 continue;
210 }
211 }
212 } else if (StartsWith(flag, "length=")) {
213 // The length flag is followed by an = and the size of the partition.
214 if (!ParseInt(arg, &entry->length)) {
215 LWARNING << "Warning: length= flag malformed: " << arg;
216 }
217 } else if (StartsWith(flag, "swapprio=")) {
218 if (!ParseInt(arg, &entry->swap_prio)) {
219 LWARNING << "Warning: swapprio= flag malformed: " << arg;
220 }
221 } else if (StartsWith(flag, "zramsize=")) {
222 if (!arg.empty() && arg.back() == '%') {
223 arg.pop_back();
224 int val;
225 if (ParseInt(arg, &val, 0, 100)) {
226 entry->zram_size = CalculateZramSize(val);
227 } else {
228 LWARNING << "Warning: zramsize= flag malformed: " << arg;
229 }
230 } else {
231 if (!ParseInt(arg, &entry->zram_size)) {
232 LWARNING << "Warning: zramsize= flag malformed: " << arg;
233 }
234 }
235 } else if (StartsWith(flag, "forceencrypt=")) {
236 // The forceencrypt flag is followed by an = and the location of the keys.
237 entry->fs_mgr_flags.force_crypt = true;
238 entry->key_loc = arg;
239 } else if (StartsWith(flag, "fileencryption=")) {
240 ParseFileEncryption(arg, entry);
241 } else if (StartsWith(flag, "forcefdeorfbe=")) {
242 // The forcefdeorfbe flag is followed by an = and the location of the keys. Get it and
243 // return it.
244 entry->fs_mgr_flags.force_fde_or_fbe = true;
245 entry->key_loc = arg;
246 entry->encryption_options = "aes-256-xts:aes-256-cts";
247 } else if (StartsWith(flag, "max_comp_streams=")) {
248 if (!ParseInt(arg, &entry->max_comp_streams)) {
249 LWARNING << "Warning: max_comp_streams= flag malformed: " << arg;
250 }
251 } else if (StartsWith(flag, "reservedsize=")) {
252 // The reserved flag is followed by an = and the reserved size of the partition.
253 uint64_t size;
254 if (!ParseByteCount(arg, &size)) {
255 LWARNING << "Warning: reservedsize= flag malformed: " << arg;
256 } else {
257 entry->reserved_size = static_cast<off64_t>(size);
258 }
259 } else if (StartsWith(flag, "readahead_size_kb=")) {
260 int val;
261 if (ParseInt(arg, &val, 0, 16 * 1024)) {
262 entry->readahead_size_kb = val;
263 } else {
264 LWARNING << "Warning: readahead_size_kb= flag malformed (0 ~ 16MB): " << arg;
265 }
266 } else if (StartsWith(flag, "eraseblk=")) {
267 // The erase block size flag is followed by an = and the flash erase block size. Get it,
268 // check that it is a power of 2 and at least 4096, and return it.
269 off64_t val;
270 if (!ParseInt(arg, &val) || val < 4096 || (val & (val - 1)) != 0) {
271 LWARNING << "Warning: eraseblk= flag malformed: " << arg;
272 } else {
273 entry->erase_blk_size = val;
274 }
275 } else if (StartsWith(flag, "logicalblk=")) {
276 // The logical block size flag is followed by an = and the flash logical block size. Get
277 // it, check that it is a power of 2 and at least 4096, and return it.
278 off64_t val;
279 if (!ParseInt(arg, &val) || val < 4096 || (val & (val - 1)) != 0) {
280 LWARNING << "Warning: logicalblk= flag malformed: " << arg;
281 } else {
282 entry->logical_blk_size = val;
283 }
284 } else if (StartsWith(flag, "avb_keys=")) { // must before the following "avb"
285 entry->avb_keys = arg;
286 } else if (StartsWith(flag, "avb")) {
287 entry->fs_mgr_flags.avb = true;
288 entry->vbmeta_partition = arg;
289 } else if (StartsWith(flag, "keydirectory=")) {
290 // The metadata flag is followed by an = and the directory for the keys.
291 entry->metadata_key_dir = arg;
292 } else if (StartsWith(flag, "metadata_encryption=")) {
293 // Specify the cipher and flags to use for metadata encryption
294 entry->metadata_encryption = arg;
295 } else if (StartsWith(flag, "sysfs_path=")) {
296 // The path to trigger device gc by idle-maint of vold.
297 entry->sysfs_path = arg;
298 } else if (StartsWith(flag, "zram_backingdev_size=")) {
299 if (!ParseByteCount(arg, &entry->zram_backingdev_size)) {
300 LWARNING << "Warning: zram_backingdev_size= flag malformed: " << arg;
301 }
302 } else {
303 LWARNING << "Warning: unknown flag: " << flag;
304 }
305 }
306 }
307
InitAndroidDtDir()308 std::string InitAndroidDtDir() {
309 std::string android_dt_dir;
310 // The platform may specify a custom Android DT path in kernel cmdline
311 if (!fs_mgr_get_boot_config_from_bootconfig_source("android_dt_dir", &android_dt_dir) &&
312 !fs_mgr_get_boot_config_from_kernel_cmdline("android_dt_dir", &android_dt_dir)) {
313 // Fall back to the standard procfs-based path
314 android_dt_dir = kDefaultAndroidDtDir;
315 }
316 return android_dt_dir;
317 }
318
IsDtFstabCompatible()319 bool IsDtFstabCompatible() {
320 std::string dt_value;
321 std::string file_name = get_android_dt_dir() + "/fstab/compatible";
322
323 if (ReadDtFile(file_name, &dt_value) && dt_value == "android,fstab") {
324 // If there's no status property or its set to "ok" or "okay", then we use the DT fstab.
325 std::string status_value;
326 std::string status_file_name = get_android_dt_dir() + "/fstab/status";
327 return !ReadDtFile(status_file_name, &status_value) || status_value == "ok" ||
328 status_value == "okay";
329 }
330
331 return false;
332 }
333
ReadFstabFromDt()334 std::string ReadFstabFromDt() {
335 if (!is_dt_compatible() || !IsDtFstabCompatible()) {
336 return {};
337 }
338
339 std::string fstabdir_name = get_android_dt_dir() + "/fstab";
340 std::unique_ptr<DIR, int (*)(DIR*)> fstabdir(opendir(fstabdir_name.c_str()), closedir);
341 if (!fstabdir) return {};
342
343 dirent* dp;
344 // Each element in fstab_dt_entries is <mount point, the line format in fstab file>.
345 std::vector<std::pair<std::string, std::string>> fstab_dt_entries;
346 while ((dp = readdir(fstabdir.get())) != NULL) {
347 // skip over name, compatible and .
348 if (dp->d_type != DT_DIR || dp->d_name[0] == '.') continue;
349
350 // create <dev> <mnt_point> <type> <mnt_flags> <fsmgr_flags>\n
351 std::vector<std::string> fstab_entry;
352 std::string file_name;
353 std::string value;
354 // skip a partition entry if the status property is present and not set to ok
355 file_name = android::base::StringPrintf("%s/%s/status", fstabdir_name.c_str(), dp->d_name);
356 if (ReadDtFile(file_name, &value)) {
357 if (value != "okay" && value != "ok") {
358 LINFO << "dt_fstab: Skip disabled entry for partition " << dp->d_name;
359 continue;
360 }
361 }
362
363 file_name = android::base::StringPrintf("%s/%s/dev", fstabdir_name.c_str(), dp->d_name);
364 if (!ReadDtFile(file_name, &value)) {
365 LERROR << "dt_fstab: Failed to find device for partition " << dp->d_name;
366 return {};
367 }
368 fstab_entry.push_back(value);
369
370 std::string mount_point;
371 file_name =
372 android::base::StringPrintf("%s/%s/mnt_point", fstabdir_name.c_str(), dp->d_name);
373 if (ReadDtFile(file_name, &value)) {
374 LINFO << "dt_fstab: Using a specified mount point " << value << " for " << dp->d_name;
375 mount_point = value;
376 } else {
377 mount_point = android::base::StringPrintf("/%s", dp->d_name);
378 }
379 fstab_entry.push_back(mount_point);
380
381 file_name = android::base::StringPrintf("%s/%s/type", fstabdir_name.c_str(), dp->d_name);
382 if (!ReadDtFile(file_name, &value)) {
383 LERROR << "dt_fstab: Failed to find type for partition " << dp->d_name;
384 return {};
385 }
386 fstab_entry.push_back(value);
387
388 file_name = android::base::StringPrintf("%s/%s/mnt_flags", fstabdir_name.c_str(), dp->d_name);
389 if (!ReadDtFile(file_name, &value)) {
390 LERROR << "dt_fstab: Failed to find type for partition " << dp->d_name;
391 return {};
392 }
393 fstab_entry.push_back(value);
394
395 file_name = android::base::StringPrintf("%s/%s/fsmgr_flags", fstabdir_name.c_str(), dp->d_name);
396 if (!ReadDtFile(file_name, &value)) {
397 LERROR << "dt_fstab: Failed to find type for partition " << dp->d_name;
398 return {};
399 }
400 fstab_entry.push_back(value);
401 // Adds a fstab_entry to fstab_dt_entries, to be sorted by mount_point later.
402 fstab_dt_entries.emplace_back(mount_point, android::base::Join(fstab_entry, " "));
403 }
404
405 // Sort fstab_dt entries, to ensure /vendor is mounted before /vendor/abc is attempted.
406 std::sort(fstab_dt_entries.begin(), fstab_dt_entries.end(),
407 [](const auto& a, const auto& b) { return a.first < b.first; });
408
409 std::string fstab_result;
410 for (const auto& [_, dt_entry] : fstab_dt_entries) {
411 fstab_result += dt_entry + "\n";
412 }
413 return fstab_result;
414 }
415
416 // Return the path to the fstab file. There may be multiple fstab files; the
417 // one that is returned will be the first that exists of fstab.<fstab_suffix>,
418 // fstab.<hardware>, and fstab.<hardware.platform>. The fstab is searched for
419 // in /odm/etc/ and /vendor/etc/, as well as in the locations where it may be in
420 // the first stage ramdisk during early boot. Previously, the first stage
421 // ramdisk's copy of the fstab had to be located in the root directory, but now
422 // the system/etc directory is supported too and is the preferred location.
GetFstabPath()423 std::string GetFstabPath() {
424 for (const char* prop : {"fstab_suffix", "hardware", "hardware.platform"}) {
425 std::string suffix;
426
427 if (!fs_mgr_get_boot_config(prop, &suffix)) continue;
428
429 for (const char* prefix : {// late-boot/post-boot locations
430 "/odm/etc/fstab.", "/vendor/etc/fstab.",
431 // early boot locations
432 "/system/etc/fstab.", "/first_stage_ramdisk/system/etc/fstab.",
433 "/fstab.", "/first_stage_ramdisk/fstab."}) {
434 std::string fstab_path = prefix + suffix;
435 if (access(fstab_path.c_str(), F_OK) == 0) {
436 return fstab_path;
437 }
438 }
439 }
440
441 return "";
442 }
443
ReadFstabFile(FILE * fstab_file,bool proc_mounts,Fstab * fstab_out)444 bool ReadFstabFile(FILE* fstab_file, bool proc_mounts, Fstab* fstab_out) {
445 ssize_t len;
446 size_t alloc_len = 0;
447 char *line = NULL;
448 const char *delim = " \t";
449 char *save_ptr, *p;
450 Fstab fstab;
451
452 while ((len = getline(&line, &alloc_len, fstab_file)) != -1) {
453 /* if the last character is a newline, shorten the string by 1 byte */
454 if (line[len - 1] == '\n') {
455 line[len - 1] = '\0';
456 }
457
458 /* Skip any leading whitespace */
459 p = line;
460 while (isspace(*p)) {
461 p++;
462 }
463 /* ignore comments or empty lines */
464 if (*p == '#' || *p == '\0')
465 continue;
466
467 FstabEntry entry;
468
469 if (!(p = strtok_r(line, delim, &save_ptr))) {
470 LERROR << "Error parsing mount source";
471 goto err;
472 }
473 entry.blk_device = p;
474
475 if (!(p = strtok_r(NULL, delim, &save_ptr))) {
476 LERROR << "Error parsing mount_point";
477 goto err;
478 }
479 entry.mount_point = p;
480
481 if (!(p = strtok_r(NULL, delim, &save_ptr))) {
482 LERROR << "Error parsing fs_type";
483 goto err;
484 }
485 entry.fs_type = p;
486
487 if (!(p = strtok_r(NULL, delim, &save_ptr))) {
488 LERROR << "Error parsing mount_flags";
489 goto err;
490 }
491
492 ParseMountFlags(p, &entry);
493
494 // For /proc/mounts, ignore everything after mnt_freq and mnt_passno
495 if (proc_mounts) {
496 p += strlen(p);
497 } else if (!(p = strtok_r(NULL, delim, &save_ptr))) {
498 LERROR << "Error parsing fs_mgr_options";
499 goto err;
500 }
501
502 ParseFsMgrFlags(p, &entry);
503
504 if (entry.fs_mgr_flags.logical) {
505 entry.logical_partition_name = entry.blk_device;
506 }
507
508 fstab.emplace_back(std::move(entry));
509 }
510
511 if (fstab.empty()) {
512 LERROR << "No entries found in fstab";
513 goto err;
514 }
515
516 /* If an A/B partition, modify block device to be the real block device */
517 if (!fs_mgr_update_for_slotselect(&fstab)) {
518 LERROR << "Error updating for slotselect";
519 goto err;
520 }
521 free(line);
522 *fstab_out = std::move(fstab);
523 return true;
524
525 err:
526 free(line);
527 return false;
528 }
529
530 /* Extracts <device>s from the by-name symlinks specified in a fstab:
531 * /dev/block/<type>/<device>/by-name/<partition>
532 *
533 * <type> can be: platform, pci or vbd.
534 *
535 * For example, given the following entries in the input fstab:
536 * /dev/block/platform/soc/1da4000.ufshc/by-name/system
537 * /dev/block/pci/soc.0/f9824900.sdhci/by-name/vendor
538 * it returns a set { "soc/1da4000.ufshc", "soc.0/f9824900.sdhci" }.
539 */
ExtraBootDevices(const Fstab & fstab)540 std::set<std::string> ExtraBootDevices(const Fstab& fstab) {
541 std::set<std::string> boot_devices;
542
543 for (const auto& entry : fstab) {
544 std::string blk_device = entry.blk_device;
545 // Skips blk_device that doesn't conform to the format.
546 if (!android::base::StartsWith(blk_device, "/dev/block") ||
547 android::base::StartsWith(blk_device, "/dev/block/by-name") ||
548 android::base::StartsWith(blk_device, "/dev/block/bootdevice/by-name")) {
549 continue;
550 }
551 // Skips non-by_name blk_device.
552 // /dev/block/<type>/<device>/by-name/<partition>
553 // ^ slash_by_name
554 auto slash_by_name = blk_device.find("/by-name");
555 if (slash_by_name == std::string::npos) continue;
556 blk_device.erase(slash_by_name); // erases /by-name/<partition>
557
558 // Erases /dev/block/, now we have <type>/<device>
559 blk_device.erase(0, std::string("/dev/block/").size());
560
561 // <type>/<device>
562 // ^ first_slash
563 auto first_slash = blk_device.find('/');
564 if (first_slash == std::string::npos) continue;
565
566 auto boot_device = blk_device.substr(first_slash + 1);
567 if (!boot_device.empty()) boot_devices.insert(std::move(boot_device));
568 }
569
570 return boot_devices;
571 }
572
BuildDsuUserdataFstabEntry()573 FstabEntry BuildDsuUserdataFstabEntry() {
574 constexpr uint32_t kFlags = MS_NOATIME | MS_NOSUID | MS_NODEV;
575
576 FstabEntry userdata = {
577 .blk_device = "userdata_gsi",
578 .mount_point = "/data",
579 .fs_type = "ext4",
580 .flags = kFlags,
581 .reserved_size = 128 * 1024 * 1024,
582 };
583 userdata.fs_mgr_flags.wait = true;
584 userdata.fs_mgr_flags.check = true;
585 userdata.fs_mgr_flags.logical = true;
586 userdata.fs_mgr_flags.quota = true;
587 userdata.fs_mgr_flags.late_mount = true;
588 userdata.fs_mgr_flags.formattable = true;
589 return userdata;
590 }
591
EraseFstabEntry(Fstab * fstab,const std::string & mount_point)592 bool EraseFstabEntry(Fstab* fstab, const std::string& mount_point) {
593 auto iter = std::remove_if(fstab->begin(), fstab->end(),
594 [&](const auto& entry) { return entry.mount_point == mount_point; });
595 if (iter != fstab->end()) {
596 fstab->erase(iter, fstab->end());
597 return true;
598 }
599 return false;
600 }
601
602 } // namespace
603
TransformFstabForDsu(Fstab * fstab,const std::string & dsu_slot,const std::vector<std::string> & dsu_partitions)604 void TransformFstabForDsu(Fstab* fstab, const std::string& dsu_slot,
605 const std::vector<std::string>& dsu_partitions) {
606 static constexpr char kDsuKeysDir[] = "/avb";
607 // Convert userdata
608 // Inherit fstab properties for userdata.
609 FstabEntry userdata;
610 if (FstabEntry* entry = GetEntryForMountPoint(fstab, "/data")) {
611 userdata = *entry;
612 userdata.blk_device = android::gsi::kDsuUserdata;
613 userdata.fs_mgr_flags.logical = true;
614 userdata.fs_mgr_flags.formattable = true;
615 if (!userdata.metadata_key_dir.empty()) {
616 userdata.metadata_key_dir = android::gsi::GetDsuMetadataKeyDir(dsu_slot);
617 }
618 } else {
619 userdata = BuildDsuUserdataFstabEntry();
620 }
621
622 if (EraseFstabEntry(fstab, "/data")) {
623 fstab->emplace_back(userdata);
624 }
625
626 // Convert others
627 for (auto&& partition : dsu_partitions) {
628 if (!EndsWith(partition, gsi::kDsuPostfix)) {
629 continue;
630 }
631 // userdata has been handled
632 if (partition == android::gsi::kDsuUserdata) {
633 continue;
634 }
635 // scratch is handled by fs_mgr_overlayfs
636 if (partition == android::gsi::kDsuScratch) {
637 continue;
638 }
639 // dsu_partition_name = corresponding_partition_name + kDsuPostfix
640 // e.g.
641 // system_gsi for system
642 // product_gsi for product
643 // vendor_gsi for vendor
644 std::string lp_name = partition.substr(0, partition.length() - strlen(gsi::kDsuPostfix));
645 std::string mount_point = "/" + lp_name;
646 std::vector<FstabEntry*> entries = GetEntriesForMountPoint(fstab, mount_point);
647 if (entries.empty()) {
648 FstabEntry entry = {
649 .blk_device = partition,
650 // .logical_partition_name is required to look up AVB Hashtree descriptors.
651 .logical_partition_name = "system",
652 .mount_point = mount_point,
653 .fs_type = "ext4",
654 .flags = MS_RDONLY,
655 .fs_options = "barrier=1",
656 .avb_keys = kDsuKeysDir,
657 };
658 entry.fs_mgr_flags.wait = true;
659 entry.fs_mgr_flags.logical = true;
660 entry.fs_mgr_flags.first_stage_mount = true;
661 fstab->emplace_back(entry);
662 } else {
663 // If the corresponding partition exists, transform all its Fstab
664 // by pointing .blk_device to the DSU partition.
665 for (auto&& entry : entries) {
666 entry->blk_device = partition;
667 // AVB keys for DSU should always be under kDsuKeysDir.
668 entry->avb_keys = kDsuKeysDir;
669 }
670 // Make sure the ext4 is included to support GSI.
671 auto partition_ext4 =
672 std::find_if(fstab->begin(), fstab->end(), [&](const auto& entry) {
673 return entry.mount_point == mount_point && entry.fs_type == "ext4";
674 });
675 if (partition_ext4 == fstab->end()) {
676 auto new_entry = *GetEntryForMountPoint(fstab, mount_point);
677 new_entry.fs_type = "ext4";
678 fstab->emplace_back(new_entry);
679 }
680 }
681 }
682 }
683
EnableMandatoryFlags(Fstab * fstab)684 void EnableMandatoryFlags(Fstab* fstab) {
685 // Devices launched in R and after should enable fs_verity on userdata. The flag causes tune2fs
686 // to enable the feature. A better alternative would be to enable on mkfs at the beginning.
687 if (android::base::GetIntProperty("ro.product.first_api_level", 0) >= 30) {
688 std::vector<FstabEntry*> data_entries = GetEntriesForMountPoint(fstab, "/data");
689 for (auto&& entry : data_entries) {
690 // Besides ext4, f2fs is also supported. But the image is already created with verity
691 // turned on when it was first introduced.
692 if (entry->fs_type == "ext4") {
693 entry->fs_mgr_flags.fs_verity = true;
694 }
695 }
696 }
697 }
698
ReadFstabFromFile(const std::string & path,Fstab * fstab_out)699 bool ReadFstabFromFile(const std::string& path, Fstab* fstab_out) {
700 auto fstab_file = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
701 if (!fstab_file) {
702 PERROR << __FUNCTION__ << "(): cannot open file: '" << path << "'";
703 return false;
704 }
705
706 bool is_proc_mounts = path == "/proc/mounts";
707
708 Fstab fstab;
709 if (!ReadFstabFile(fstab_file.get(), is_proc_mounts, &fstab)) {
710 LERROR << __FUNCTION__ << "(): failed to load fstab from : '" << path << "'";
711 return false;
712 }
713 if (!is_proc_mounts) {
714 if (!access(android::gsi::kGsiBootedIndicatorFile, F_OK)) {
715 // This is expected to fail if host is android Q, since Q doesn't
716 // support DSU slotting. The DSU "active" indicator file would be
717 // non-existent or empty if DSU is enabled within the guest system.
718 // In that case, just use the default slot name "dsu".
719 std::string dsu_slot;
720 if (!android::gsi::GetActiveDsu(&dsu_slot) && errno != ENOENT) {
721 PERROR << __FUNCTION__ << "(): failed to get active DSU slot";
722 return false;
723 }
724 if (dsu_slot.empty()) {
725 dsu_slot = "dsu";
726 LWARNING << __FUNCTION__ << "(): assuming default DSU slot: " << dsu_slot;
727 }
728 // This file is non-existent on Q vendor.
729 std::string lp_names;
730 if (!ReadFileToString(gsi::kGsiLpNamesFile, &lp_names) && errno != ENOENT) {
731 PERROR << __FUNCTION__ << "(): failed to read DSU LP names";
732 return false;
733 }
734 TransformFstabForDsu(&fstab, dsu_slot, Split(lp_names, ","));
735 } else if (errno != ENOENT) {
736 PERROR << __FUNCTION__ << "(): failed to access() DSU booted indicator";
737 return false;
738 }
739 }
740
741 SkipMountingPartitions(&fstab, false /* verbose */);
742 EnableMandatoryFlags(&fstab);
743
744 *fstab_out = std::move(fstab);
745 return true;
746 }
747
748 // Returns fstab entries parsed from the device tree if they exist
ReadFstabFromDt(Fstab * fstab,bool verbose)749 bool ReadFstabFromDt(Fstab* fstab, bool verbose) {
750 std::string fstab_buf = ReadFstabFromDt();
751 if (fstab_buf.empty()) {
752 if (verbose) LINFO << __FUNCTION__ << "(): failed to read fstab from dt";
753 return false;
754 }
755
756 std::unique_ptr<FILE, decltype(&fclose)> fstab_file(
757 fmemopen(static_cast<void*>(const_cast<char*>(fstab_buf.c_str())),
758 fstab_buf.length(), "r"), fclose);
759 if (!fstab_file) {
760 if (verbose) PERROR << __FUNCTION__ << "(): failed to create a file stream for fstab dt";
761 return false;
762 }
763
764 if (!ReadFstabFile(fstab_file.get(), false, fstab)) {
765 if (verbose) {
766 LERROR << __FUNCTION__ << "(): failed to load fstab from kernel:" << std::endl
767 << fstab_buf;
768 }
769 return false;
770 }
771
772 SkipMountingPartitions(fstab, verbose);
773
774 return true;
775 }
776
777 #ifdef NO_SKIP_MOUNT
SkipMountingPartitions(Fstab *,bool)778 bool SkipMountingPartitions(Fstab*, bool) {
779 return true;
780 }
781 #else
782 // For GSI to skip mounting /product and /system_ext, until there are well-defined interfaces
783 // between them and /system. Otherwise, the GSI flashed on /system might not be able to work with
784 // device-specific /product and /system_ext. skip_mount.cfg belongs to system_ext partition because
785 // only common files for all targets can be put into system partition. It is under
786 // /system/system_ext because GSI is a single system.img that includes the contents of system_ext
787 // partition and product partition under /system/system_ext and /system/product, respectively.
SkipMountingPartitions(Fstab * fstab,bool verbose)788 bool SkipMountingPartitions(Fstab* fstab, bool verbose) {
789 static constexpr char kSkipMountConfig[] = "/system/system_ext/etc/init/config/skip_mount.cfg";
790
791 std::string skip_config;
792 auto save_errno = errno;
793 if (!ReadFileToString(kSkipMountConfig, &skip_config)) {
794 errno = save_errno; // missing file is expected
795 return true;
796 }
797
798 std::vector<std::string> skip_mount_patterns;
799 for (const auto& line : Split(skip_config, "\n")) {
800 if (line.empty() || StartsWith(line, "#")) {
801 continue;
802 }
803 skip_mount_patterns.push_back(line);
804 }
805
806 // Returns false if mount_point matches any of the skip mount patterns, so that the FstabEntry
807 // would be partitioned to the second group.
808 auto glob_pattern_mismatch = [&skip_mount_patterns](const FstabEntry& entry) -> bool {
809 for (const auto& pattern : skip_mount_patterns) {
810 if (!fnmatch(pattern.c_str(), entry.mount_point.c_str(), 0 /* flags */)) {
811 return false;
812 }
813 }
814 return true;
815 };
816 auto remove_from = std::stable_partition(fstab->begin(), fstab->end(), glob_pattern_mismatch);
817 if (verbose) {
818 for (auto it = remove_from; it != fstab->end(); ++it) {
819 LINFO << "Skip mounting mountpoint: " << it->mount_point;
820 }
821 }
822 fstab->erase(remove_from, fstab->end());
823 return true;
824 }
825 #endif
826
827 // Loads the fstab file and combines with fstab entries passed in from device tree.
ReadDefaultFstab(Fstab * fstab)828 bool ReadDefaultFstab(Fstab* fstab) {
829 fstab->clear();
830 ReadFstabFromDt(fstab, false /* verbose */);
831
832 std::string default_fstab_path;
833 // Use different fstab paths for normal boot and recovery boot, respectively
834 if (access("/system/bin/recovery", F_OK) == 0) {
835 default_fstab_path = "/etc/recovery.fstab";
836 } else { // normal boot
837 default_fstab_path = GetFstabPath();
838 }
839
840 Fstab default_fstab;
841 if (!default_fstab_path.empty() && ReadFstabFromFile(default_fstab_path, &default_fstab)) {
842 for (auto&& entry : default_fstab) {
843 fstab->emplace_back(std::move(entry));
844 }
845 } else {
846 LINFO << __FUNCTION__ << "(): failed to find device default fstab";
847 }
848
849 return !fstab->empty();
850 }
851
GetEntryForMountPoint(Fstab * fstab,const std::string & path)852 FstabEntry* GetEntryForMountPoint(Fstab* fstab, const std::string& path) {
853 if (fstab == nullptr) {
854 return nullptr;
855 }
856
857 for (auto& entry : *fstab) {
858 if (entry.mount_point == path) {
859 return &entry;
860 }
861 }
862
863 return nullptr;
864 }
865
GetEntriesForMountPoint(Fstab * fstab,const std::string & path)866 std::vector<FstabEntry*> GetEntriesForMountPoint(Fstab* fstab, const std::string& path) {
867 std::vector<FstabEntry*> entries;
868 if (fstab == nullptr) {
869 return entries;
870 }
871
872 for (auto& entry : *fstab) {
873 if (entry.mount_point == path) {
874 entries.emplace_back(&entry);
875 }
876 }
877
878 return entries;
879 }
880
GetBootDevices()881 std::set<std::string> GetBootDevices() {
882 // First check bootconfig, then kernel commandline, then the device tree
883 std::string dt_file_name = get_android_dt_dir() + "/boot_devices";
884 std::string value;
885 if (fs_mgr_get_boot_config_from_bootconfig_source("boot_devices", &value) ||
886 fs_mgr_get_boot_config_from_bootconfig_source("boot_device", &value)) {
887 std::set<std::string> boot_devices;
888 // remove quotes and split by spaces
889 auto boot_device_strings = base::Split(base::StringReplace(value, "\"", "", true), " ");
890 for (std::string_view device : boot_device_strings) {
891 // trim the trailing comma, keep the rest.
892 base::ConsumeSuffix(&device, ",");
893 boot_devices.emplace(device);
894 }
895 return boot_devices;
896 }
897
898 if (fs_mgr_get_boot_config_from_kernel_cmdline("boot_devices", &value) ||
899 ReadDtFile(dt_file_name, &value)) {
900 auto boot_devices = Split(value, ",");
901 return std::set<std::string>(boot_devices.begin(), boot_devices.end());
902 }
903
904 std::string cmdline;
905 if (android::base::ReadFileToString("/proc/cmdline", &cmdline)) {
906 std::set<std::string> boot_devices;
907 const std::string cmdline_key = "androidboot.boot_device";
908 for (const auto& [key, value] : fs_mgr_parse_cmdline(cmdline)) {
909 if (key == cmdline_key) {
910 boot_devices.emplace(value);
911 }
912 }
913 if (!boot_devices.empty()) {
914 return boot_devices;
915 }
916 }
917
918 // Fallback to extract boot devices from fstab.
919 Fstab fstab;
920 if (!ReadDefaultFstab(&fstab)) {
921 return {};
922 }
923
924 return ExtraBootDevices(fstab);
925 }
926
GetVerityDeviceName(const FstabEntry & entry)927 std::string GetVerityDeviceName(const FstabEntry& entry) {
928 std::string base_device;
929 if (entry.mount_point == "/") {
930 // When using system-as-root, the device name is fixed as "vroot".
931 if (entry.fs_mgr_flags.avb) {
932 return "vroot";
933 }
934 base_device = "system";
935 } else {
936 base_device = android::base::Basename(entry.mount_point);
937 }
938 return base_device + "-verity";
939 }
940
941 } // namespace fs_mgr
942 } // namespace android
943
944 // FIXME: The same logic is duplicated in system/core/init/
get_android_dt_dir()945 const std::string& get_android_dt_dir() {
946 // Set once and saves time for subsequent calls to this function
947 static const std::string kAndroidDtDir = android::fs_mgr::InitAndroidDtDir();
948 return kAndroidDtDir;
949 }
950
is_dt_compatible()951 bool is_dt_compatible() {
952 std::string file_name = get_android_dt_dir() + "/compatible";
953 std::string dt_value;
954 if (android::fs_mgr::ReadDtFile(file_name, &dt_value)) {
955 if (dt_value == "android,firmware") {
956 return true;
957 }
958 }
959
960 return false;
961 }
962