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 // Identify path to fstab file. Lookup is based on pattern
417 // fstab.<fstab_suffix>, fstab.<hardware>, fstab.<hardware.platform> in
418 // folders /odm/etc, vendor/etc, or /.
GetFstabPath()419 std::string GetFstabPath() {
420 for (const char* prop : {"fstab_suffix", "hardware", "hardware.platform"}) {
421 std::string suffix;
422
423 if (!fs_mgr_get_boot_config(prop, &suffix)) continue;
424
425 for (const char* prefix :
426 {"/odm/etc/fstab.", "/vendor/etc/fstab.", "/fstab.", "/first_stage_ramdisk/fstab."}) {
427 std::string fstab_path = prefix + suffix;
428 if (access(fstab_path.c_str(), F_OK) == 0) {
429 return fstab_path;
430 }
431 }
432 }
433
434 return "";
435 }
436
ReadFstabFile(FILE * fstab_file,bool proc_mounts,Fstab * fstab_out)437 bool ReadFstabFile(FILE* fstab_file, bool proc_mounts, Fstab* fstab_out) {
438 ssize_t len;
439 size_t alloc_len = 0;
440 char *line = NULL;
441 const char *delim = " \t";
442 char *save_ptr, *p;
443 Fstab fstab;
444
445 while ((len = getline(&line, &alloc_len, fstab_file)) != -1) {
446 /* if the last character is a newline, shorten the string by 1 byte */
447 if (line[len - 1] == '\n') {
448 line[len - 1] = '\0';
449 }
450
451 /* Skip any leading whitespace */
452 p = line;
453 while (isspace(*p)) {
454 p++;
455 }
456 /* ignore comments or empty lines */
457 if (*p == '#' || *p == '\0')
458 continue;
459
460 FstabEntry entry;
461
462 if (!(p = strtok_r(line, delim, &save_ptr))) {
463 LERROR << "Error parsing mount source";
464 goto err;
465 }
466 entry.blk_device = p;
467
468 if (!(p = strtok_r(NULL, delim, &save_ptr))) {
469 LERROR << "Error parsing mount_point";
470 goto err;
471 }
472 entry.mount_point = p;
473
474 if (!(p = strtok_r(NULL, delim, &save_ptr))) {
475 LERROR << "Error parsing fs_type";
476 goto err;
477 }
478 entry.fs_type = p;
479
480 if (!(p = strtok_r(NULL, delim, &save_ptr))) {
481 LERROR << "Error parsing mount_flags";
482 goto err;
483 }
484
485 ParseMountFlags(p, &entry);
486
487 // For /proc/mounts, ignore everything after mnt_freq and mnt_passno
488 if (proc_mounts) {
489 p += strlen(p);
490 } else if (!(p = strtok_r(NULL, delim, &save_ptr))) {
491 LERROR << "Error parsing fs_mgr_options";
492 goto err;
493 }
494
495 ParseFsMgrFlags(p, &entry);
496
497 if (entry.fs_mgr_flags.logical) {
498 entry.logical_partition_name = entry.blk_device;
499 }
500
501 fstab.emplace_back(std::move(entry));
502 }
503
504 if (fstab.empty()) {
505 LERROR << "No entries found in fstab";
506 goto err;
507 }
508
509 /* If an A/B partition, modify block device to be the real block device */
510 if (!fs_mgr_update_for_slotselect(&fstab)) {
511 LERROR << "Error updating for slotselect";
512 goto err;
513 }
514 free(line);
515 *fstab_out = std::move(fstab);
516 return true;
517
518 err:
519 free(line);
520 return false;
521 }
522
523 /* Extracts <device>s from the by-name symlinks specified in a fstab:
524 * /dev/block/<type>/<device>/by-name/<partition>
525 *
526 * <type> can be: platform, pci or vbd.
527 *
528 * For example, given the following entries in the input fstab:
529 * /dev/block/platform/soc/1da4000.ufshc/by-name/system
530 * /dev/block/pci/soc.0/f9824900.sdhci/by-name/vendor
531 * it returns a set { "soc/1da4000.ufshc", "soc.0/f9824900.sdhci" }.
532 */
ExtraBootDevices(const Fstab & fstab)533 std::set<std::string> ExtraBootDevices(const Fstab& fstab) {
534 std::set<std::string> boot_devices;
535
536 for (const auto& entry : fstab) {
537 std::string blk_device = entry.blk_device;
538 // Skips blk_device that doesn't conform to the format.
539 if (!android::base::StartsWith(blk_device, "/dev/block") ||
540 android::base::StartsWith(blk_device, "/dev/block/by-name") ||
541 android::base::StartsWith(blk_device, "/dev/block/bootdevice/by-name")) {
542 continue;
543 }
544 // Skips non-by_name blk_device.
545 // /dev/block/<type>/<device>/by-name/<partition>
546 // ^ slash_by_name
547 auto slash_by_name = blk_device.find("/by-name");
548 if (slash_by_name == std::string::npos) continue;
549 blk_device.erase(slash_by_name); // erases /by-name/<partition>
550
551 // Erases /dev/block/, now we have <type>/<device>
552 blk_device.erase(0, std::string("/dev/block/").size());
553
554 // <type>/<device>
555 // ^ first_slash
556 auto first_slash = blk_device.find('/');
557 if (first_slash == std::string::npos) continue;
558
559 auto boot_device = blk_device.substr(first_slash + 1);
560 if (!boot_device.empty()) boot_devices.insert(std::move(boot_device));
561 }
562
563 return boot_devices;
564 }
565
BuildDsuUserdataFstabEntry()566 FstabEntry BuildDsuUserdataFstabEntry() {
567 constexpr uint32_t kFlags = MS_NOATIME | MS_NOSUID | MS_NODEV;
568
569 FstabEntry userdata = {
570 .blk_device = "userdata_gsi",
571 .mount_point = "/data",
572 .fs_type = "ext4",
573 .flags = kFlags,
574 .reserved_size = 128 * 1024 * 1024,
575 };
576 userdata.fs_mgr_flags.wait = true;
577 userdata.fs_mgr_flags.check = true;
578 userdata.fs_mgr_flags.logical = true;
579 userdata.fs_mgr_flags.quota = true;
580 userdata.fs_mgr_flags.late_mount = true;
581 userdata.fs_mgr_flags.formattable = true;
582 return userdata;
583 }
584
EraseFstabEntry(Fstab * fstab,const std::string & mount_point)585 bool EraseFstabEntry(Fstab* fstab, const std::string& mount_point) {
586 auto iter = std::remove_if(fstab->begin(), fstab->end(),
587 [&](const auto& entry) { return entry.mount_point == mount_point; });
588 if (iter != fstab->end()) {
589 fstab->erase(iter, fstab->end());
590 return true;
591 }
592 return false;
593 }
594
595 } // namespace
596
TransformFstabForDsu(Fstab * fstab,const std::string & dsu_slot,const std::vector<std::string> & dsu_partitions)597 void TransformFstabForDsu(Fstab* fstab, const std::string& dsu_slot,
598 const std::vector<std::string>& dsu_partitions) {
599 static constexpr char kDsuKeysDir[] = "/avb";
600 // Convert userdata
601 // Inherit fstab properties for userdata.
602 FstabEntry userdata;
603 if (FstabEntry* entry = GetEntryForMountPoint(fstab, "/data")) {
604 userdata = *entry;
605 userdata.blk_device = android::gsi::kDsuUserdata;
606 userdata.fs_mgr_flags.logical = true;
607 userdata.fs_mgr_flags.formattable = true;
608 if (!userdata.metadata_key_dir.empty()) {
609 userdata.metadata_key_dir = android::gsi::GetDsuMetadataKeyDir(dsu_slot);
610 }
611 } else {
612 userdata = BuildDsuUserdataFstabEntry();
613 }
614
615 if (EraseFstabEntry(fstab, "/data")) {
616 fstab->emplace_back(userdata);
617 }
618
619 // Convert others
620 for (auto&& partition : dsu_partitions) {
621 if (!EndsWith(partition, gsi::kDsuPostfix)) {
622 continue;
623 }
624 // userdata has been handled
625 if (partition == android::gsi::kDsuUserdata) {
626 continue;
627 }
628 // scratch is handled by fs_mgr_overlayfs
629 if (partition == android::gsi::kDsuScratch) {
630 continue;
631 }
632 // dsu_partition_name = corresponding_partition_name + kDsuPostfix
633 // e.g.
634 // system_gsi for system
635 // product_gsi for product
636 // vendor_gsi for vendor
637 std::string lp_name = partition.substr(0, partition.length() - strlen(gsi::kDsuPostfix));
638 std::string mount_point = "/" + lp_name;
639 std::vector<FstabEntry*> entries = GetEntriesForMountPoint(fstab, mount_point);
640 if (entries.empty()) {
641 FstabEntry entry = {
642 .blk_device = partition,
643 // .logical_partition_name is required to look up AVB Hashtree descriptors.
644 .logical_partition_name = "system",
645 .mount_point = mount_point,
646 .fs_type = "ext4",
647 .flags = MS_RDONLY,
648 .fs_options = "barrier=1",
649 .avb_keys = kDsuKeysDir,
650 };
651 entry.fs_mgr_flags.wait = true;
652 entry.fs_mgr_flags.logical = true;
653 entry.fs_mgr_flags.first_stage_mount = true;
654 fstab->emplace_back(entry);
655 } else {
656 // If the corresponding partition exists, transform all its Fstab
657 // by pointing .blk_device to the DSU partition.
658 for (auto&& entry : entries) {
659 entry->blk_device = partition;
660 // AVB keys for DSU should always be under kDsuKeysDir.
661 entry->avb_keys = kDsuKeysDir;
662 }
663 // Make sure the ext4 is included to support GSI.
664 auto partition_ext4 =
665 std::find_if(fstab->begin(), fstab->end(), [&](const auto& entry) {
666 return entry.mount_point == mount_point && entry.fs_type == "ext4";
667 });
668 if (partition_ext4 == fstab->end()) {
669 auto new_entry = *GetEntryForMountPoint(fstab, mount_point);
670 new_entry.fs_type = "ext4";
671 fstab->emplace_back(new_entry);
672 }
673 }
674 }
675 }
676
EnableMandatoryFlags(Fstab * fstab)677 void EnableMandatoryFlags(Fstab* fstab) {
678 // Devices launched in R and after should enable fs_verity on userdata. The flag causes tune2fs
679 // to enable the feature. A better alternative would be to enable on mkfs at the beginning.
680 if (android::base::GetIntProperty("ro.product.first_api_level", 0) >= 30) {
681 std::vector<FstabEntry*> data_entries = GetEntriesForMountPoint(fstab, "/data");
682 for (auto&& entry : data_entries) {
683 // Besides ext4, f2fs is also supported. But the image is already created with verity
684 // turned on when it was first introduced.
685 if (entry->fs_type == "ext4") {
686 entry->fs_mgr_flags.fs_verity = true;
687 }
688 }
689 }
690 }
691
ReadFstabFromFile(const std::string & path,Fstab * fstab_out)692 bool ReadFstabFromFile(const std::string& path, Fstab* fstab_out) {
693 auto fstab_file = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
694 if (!fstab_file) {
695 PERROR << __FUNCTION__ << "(): cannot open file: '" << path << "'";
696 return false;
697 }
698
699 bool is_proc_mounts = path == "/proc/mounts";
700
701 Fstab fstab;
702 if (!ReadFstabFile(fstab_file.get(), is_proc_mounts, &fstab)) {
703 LERROR << __FUNCTION__ << "(): failed to load fstab from : '" << path << "'";
704 return false;
705 }
706 if (!is_proc_mounts) {
707 if (!access(android::gsi::kGsiBootedIndicatorFile, F_OK)) {
708 // This is expected to fail if host is android Q, since Q doesn't
709 // support DSU slotting. The DSU "active" indicator file would be
710 // non-existent or empty if DSU is enabled within the guest system.
711 // In that case, just use the default slot name "dsu".
712 std::string dsu_slot;
713 if (!android::gsi::GetActiveDsu(&dsu_slot) && errno != ENOENT) {
714 PERROR << __FUNCTION__ << "(): failed to get active DSU slot";
715 return false;
716 }
717 if (dsu_slot.empty()) {
718 dsu_slot = "dsu";
719 LWARNING << __FUNCTION__ << "(): assuming default DSU slot: " << dsu_slot;
720 }
721 // This file is non-existent on Q vendor.
722 std::string lp_names;
723 if (!ReadFileToString(gsi::kGsiLpNamesFile, &lp_names) && errno != ENOENT) {
724 PERROR << __FUNCTION__ << "(): failed to read DSU LP names";
725 return false;
726 }
727 TransformFstabForDsu(&fstab, dsu_slot, Split(lp_names, ","));
728 } else if (errno != ENOENT) {
729 PERROR << __FUNCTION__ << "(): failed to access() DSU booted indicator";
730 return false;
731 }
732 }
733
734 SkipMountingPartitions(&fstab, false /* verbose */);
735 EnableMandatoryFlags(&fstab);
736
737 *fstab_out = std::move(fstab);
738 return true;
739 }
740
741 // Returns fstab entries parsed from the device tree if they exist
ReadFstabFromDt(Fstab * fstab,bool verbose)742 bool ReadFstabFromDt(Fstab* fstab, bool verbose) {
743 std::string fstab_buf = ReadFstabFromDt();
744 if (fstab_buf.empty()) {
745 if (verbose) LINFO << __FUNCTION__ << "(): failed to read fstab from dt";
746 return false;
747 }
748
749 std::unique_ptr<FILE, decltype(&fclose)> fstab_file(
750 fmemopen(static_cast<void*>(const_cast<char*>(fstab_buf.c_str())),
751 fstab_buf.length(), "r"), fclose);
752 if (!fstab_file) {
753 if (verbose) PERROR << __FUNCTION__ << "(): failed to create a file stream for fstab dt";
754 return false;
755 }
756
757 if (!ReadFstabFile(fstab_file.get(), false, fstab)) {
758 if (verbose) {
759 LERROR << __FUNCTION__ << "(): failed to load fstab from kernel:" << std::endl
760 << fstab_buf;
761 }
762 return false;
763 }
764
765 SkipMountingPartitions(fstab, verbose);
766
767 return true;
768 }
769
770 #ifdef NO_SKIP_MOUNT
SkipMountingPartitions(Fstab *,bool)771 bool SkipMountingPartitions(Fstab*, bool) {
772 return true;
773 }
774 #else
775 // For GSI to skip mounting /product and /system_ext, until there are well-defined interfaces
776 // between them and /system. Otherwise, the GSI flashed on /system might not be able to work with
777 // device-specific /product and /system_ext. skip_mount.cfg belongs to system_ext partition because
778 // only common files for all targets can be put into system partition. It is under
779 // /system/system_ext because GSI is a single system.img that includes the contents of system_ext
780 // partition and product partition under /system/system_ext and /system/product, respectively.
SkipMountingPartitions(Fstab * fstab,bool verbose)781 bool SkipMountingPartitions(Fstab* fstab, bool verbose) {
782 static constexpr char kSkipMountConfig[] = "/system/system_ext/etc/init/config/skip_mount.cfg";
783
784 std::string skip_config;
785 auto save_errno = errno;
786 if (!ReadFileToString(kSkipMountConfig, &skip_config)) {
787 errno = save_errno; // missing file is expected
788 return true;
789 }
790
791 std::vector<std::string> skip_mount_patterns;
792 for (const auto& line : Split(skip_config, "\n")) {
793 if (line.empty() || StartsWith(line, "#")) {
794 continue;
795 }
796 skip_mount_patterns.push_back(line);
797 }
798
799 // Returns false if mount_point matches any of the skip mount patterns, so that the FstabEntry
800 // would be partitioned to the second group.
801 auto glob_pattern_mismatch = [&skip_mount_patterns](const FstabEntry& entry) -> bool {
802 for (const auto& pattern : skip_mount_patterns) {
803 if (!fnmatch(pattern.c_str(), entry.mount_point.c_str(), 0 /* flags */)) {
804 return false;
805 }
806 }
807 return true;
808 };
809 auto remove_from = std::stable_partition(fstab->begin(), fstab->end(), glob_pattern_mismatch);
810 if (verbose) {
811 for (auto it = remove_from; it != fstab->end(); ++it) {
812 LINFO << "Skip mounting mountpoint: " << it->mount_point;
813 }
814 }
815 fstab->erase(remove_from, fstab->end());
816 return true;
817 }
818 #endif
819
820 // Loads the fstab file and combines with fstab entries passed in from device tree.
ReadDefaultFstab(Fstab * fstab)821 bool ReadDefaultFstab(Fstab* fstab) {
822 fstab->clear();
823 ReadFstabFromDt(fstab, false /* verbose */);
824
825 std::string default_fstab_path;
826 // Use different fstab paths for normal boot and recovery boot, respectively
827 if (access("/system/bin/recovery", F_OK) == 0) {
828 default_fstab_path = "/etc/recovery.fstab";
829 } else { // normal boot
830 default_fstab_path = GetFstabPath();
831 }
832
833 Fstab default_fstab;
834 if (!default_fstab_path.empty() && ReadFstabFromFile(default_fstab_path, &default_fstab)) {
835 for (auto&& entry : default_fstab) {
836 fstab->emplace_back(std::move(entry));
837 }
838 } else {
839 LINFO << __FUNCTION__ << "(): failed to find device default fstab";
840 }
841
842 return !fstab->empty();
843 }
844
GetEntryForMountPoint(Fstab * fstab,const std::string & path)845 FstabEntry* GetEntryForMountPoint(Fstab* fstab, const std::string& path) {
846 if (fstab == nullptr) {
847 return nullptr;
848 }
849
850 for (auto& entry : *fstab) {
851 if (entry.mount_point == path) {
852 return &entry;
853 }
854 }
855
856 return nullptr;
857 }
858
GetEntriesForMountPoint(Fstab * fstab,const std::string & path)859 std::vector<FstabEntry*> GetEntriesForMountPoint(Fstab* fstab, const std::string& path) {
860 std::vector<FstabEntry*> entries;
861 if (fstab == nullptr) {
862 return entries;
863 }
864
865 for (auto& entry : *fstab) {
866 if (entry.mount_point == path) {
867 entries.emplace_back(&entry);
868 }
869 }
870
871 return entries;
872 }
873
GetBootDevices()874 std::set<std::string> GetBootDevices() {
875 // First check bootconfig, then kernel commandline, then the device tree
876 std::string dt_file_name = get_android_dt_dir() + "/boot_devices";
877 std::string value;
878 if (fs_mgr_get_boot_config_from_bootconfig_source("boot_devices", &value) ||
879 fs_mgr_get_boot_config_from_bootconfig_source("boot_device", &value)) {
880 std::set<std::string> boot_devices;
881 // remove quotes and split by spaces
882 auto boot_device_strings = base::Split(base::StringReplace(value, "\"", "", true), " ");
883 for (std::string_view device : boot_device_strings) {
884 // trim the trailing comma, keep the rest.
885 base::ConsumeSuffix(&device, ",");
886 boot_devices.emplace(device);
887 }
888 return boot_devices;
889 }
890
891 if (fs_mgr_get_boot_config_from_kernel_cmdline("boot_devices", &value) ||
892 ReadDtFile(dt_file_name, &value)) {
893 auto boot_devices = Split(value, ",");
894 return std::set<std::string>(boot_devices.begin(), boot_devices.end());
895 }
896
897 std::string cmdline;
898 if (android::base::ReadFileToString("/proc/cmdline", &cmdline)) {
899 std::set<std::string> boot_devices;
900 const std::string cmdline_key = "androidboot.boot_device";
901 for (const auto& [key, value] : fs_mgr_parse_cmdline(cmdline)) {
902 if (key == cmdline_key) {
903 boot_devices.emplace(value);
904 }
905 }
906 if (!boot_devices.empty()) {
907 return boot_devices;
908 }
909 }
910
911 // Fallback to extract boot devices from fstab.
912 Fstab fstab;
913 if (!ReadDefaultFstab(&fstab)) {
914 return {};
915 }
916
917 return ExtraBootDevices(fstab);
918 }
919
GetVerityDeviceName(const FstabEntry & entry)920 std::string GetVerityDeviceName(const FstabEntry& entry) {
921 std::string base_device;
922 if (entry.mount_point == "/") {
923 // When using system-as-root, the device name is fixed as "vroot".
924 if (entry.fs_mgr_flags.avb) {
925 return "vroot";
926 }
927 base_device = "system";
928 } else {
929 base_device = android::base::Basename(entry.mount_point);
930 }
931 return base_device + "-verity";
932 }
933
934 } // namespace fs_mgr
935 } // namespace android
936
937 // FIXME: The same logic is duplicated in system/core/init/
get_android_dt_dir()938 const std::string& get_android_dt_dir() {
939 // Set once and saves time for subsequent calls to this function
940 static const std::string kAndroidDtDir = android::fs_mgr::InitAndroidDtDir();
941 return kAndroidDtDir;
942 }
943
is_dt_compatible()944 bool is_dt_compatible() {
945 std::string file_name = get_android_dt_dir() + "/compatible";
946 std::string dt_value;
947 if (android::fs_mgr::ReadDtFile(file_name, &dt_value)) {
948 if (dt_value == "android,firmware") {
949 return true;
950 }
951 }
952
953 return false;
954 }
955