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