/* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fstab_priv.h" #include "logging_macros.h" using android::base::EndsWith; using android::base::ParseByteCount; using android::base::ParseInt; using android::base::ReadFileToString; using android::base::Readlink; using android::base::Split; using android::base::StartsWith; namespace android { namespace fs_mgr { namespace { constexpr char kProcMountsPath[] = "/proc/mounts"; struct FlagList { const char* name; uint64_t flag; }; FlagList kMountFlagsList[] = { {"noatime", MS_NOATIME}, {"noexec", MS_NOEXEC}, {"nosuid", MS_NOSUID}, {"nodev", MS_NODEV}, {"nodiratime", MS_NODIRATIME}, {"ro", MS_RDONLY}, {"rw", 0}, {"sync", MS_SYNCHRONOUS}, {"remount", MS_REMOUNT}, {"bind", MS_BIND}, {"rec", MS_REC}, {"unbindable", MS_UNBINDABLE}, {"private", MS_PRIVATE}, {"slave", MS_SLAVE}, {"shared", MS_SHARED}, {"defaults", 0}, }; off64_t CalculateZramSize(int percentage) { off64_t total; total = sysconf(_SC_PHYS_PAGES); total *= percentage; total /= 100; total *= sysconf(_SC_PAGESIZE); return total; } // Fills 'dt_value' with the underlying device tree value string without the trailing '\0'. // Returns true if 'dt_value' has a valid string, 'false' otherwise. bool ReadDtFile(const std::string& file_name, std::string* dt_value) { if (android::base::ReadFileToString(file_name, dt_value)) { if (!dt_value->empty()) { // Trim the trailing '\0' out, otherwise the comparison will produce false-negatives. dt_value->resize(dt_value->size() - 1); return true; } } return false; } void ParseFileEncryption(const std::string& arg, FstabEntry* entry) { entry->fs_mgr_flags.file_encryption = true; entry->encryption_options = arg; } bool SetMountFlag(const std::string& flag, FstabEntry* entry) { for (const auto& [name, value] : kMountFlagsList) { if (flag == name) { entry->flags |= value; return true; } } return false; } void ParseMountFlags(const std::string& flags, FstabEntry* entry) { std::string fs_options; for (const auto& flag : Split(flags, ",")) { if (!SetMountFlag(flag, entry)) { // Unknown flag, so it must be a filesystem specific option. if (!fs_options.empty()) { fs_options.append(","); // appends a comma if not the first } fs_options.append(flag); if (auto equal_sign = flag.find('='); equal_sign != std::string::npos) { const auto arg = flag.substr(equal_sign + 1); if (entry->fs_type == "f2fs" && StartsWith(flag, "reserve_root=")) { off64_t size_in_4k_blocks; if (!ParseInt(arg, &size_in_4k_blocks, static_cast(0), std::numeric_limits::max() >> 12)) { LWARNING << "Warning: reserve_root= flag malformed: " << arg; } else { entry->reserved_size = size_in_4k_blocks << 12; } } else if (StartsWith(flag, "lowerdir=")) { entry->lowerdir = arg; } } } } entry->fs_options = std::move(fs_options); } void ParseUserDevices(const std::string& arg, FstabEntry* entry) { auto param = Split(arg, ":"); if (param.size() != 2) { LWARNING << "Warning: device= malformed: " << arg; return; } if (access(param[1].c_str(), F_OK) != 0) { LWARNING << "Warning: device does not exist : " << param[1]; return; } if (param[0] == "zoned") { // atgc in f2fs does not support a zoned device auto options = Split(entry->fs_options, ","); options.erase(std::remove(options.begin(), options.end(), "atgc"), options.end()); entry->fs_options = android::base::Join(options, ","); LINFO << "Removed ATGC in fs_options as " << entry->fs_options << " for zoned device"; entry->fs_mgr_flags.is_zoned = true; } entry->user_devices.push_back(param[1]); } bool ParseFsMgrFlags(const std::string& flags, FstabEntry* entry) { for (const auto& flag : Split(flags, ",")) { if (flag.empty() || flag == "defaults") continue; std::string arg; if (auto equal_sign = flag.find('='); equal_sign != std::string::npos) { arg = flag.substr(equal_sign + 1); } // First handle flags that simply set a boolean. #define CheckFlag(flag_name, value) \ if (flag == flag_name) { \ entry->fs_mgr_flags.value = true; \ continue; \ } CheckFlag("wait", wait); CheckFlag("check", check); CheckFlag("nonremovable", nonremovable); CheckFlag("recoveryonly", recovery_only); CheckFlag("noemulatedsd", no_emulated_sd); CheckFlag("notrim", no_trim); CheckFlag("formattable", formattable); CheckFlag("slotselect", slot_select); CheckFlag("latemount", late_mount); CheckFlag("nofail", no_fail); CheckFlag("quota", quota); CheckFlag("avb", avb); CheckFlag("logical", logical); CheckFlag("checkpoint=block", checkpoint_blk); CheckFlag("checkpoint=fs", checkpoint_fs); CheckFlag("first_stage_mount", first_stage_mount); CheckFlag("slotselect_other", slot_select_other); CheckFlag("fsverity", fs_verity); CheckFlag("metadata_csum", ext_meta_csum); CheckFlag("fscompress", fs_compress); CheckFlag("overlayfs_remove_missing_lowerdir", overlayfs_remove_missing_lowerdir); #undef CheckFlag // Then handle flags that take an argument. if (StartsWith(flag, "encryptable=")) { // The "encryptable" flag identifies adoptable storage volumes. The // argument to this flag is ignored, but it should be "userdata". // // Historical note: this flag was originally meant just for /data, // to indicate that FDE (full disk encryption) can be enabled. // Unfortunately, it was also overloaded to identify adoptable // storage volumes. Today, FDE is no longer supported, leaving only // the adoptable storage volume meaning for this flag. entry->fs_mgr_flags.crypt = true; } else if (StartsWith(flag, "forceencrypt=") || StartsWith(flag, "forcefdeorfbe=")) { LERROR << "flag no longer supported: " << flag; return false; } else if (StartsWith(flag, "voldmanaged=")) { // The voldmanaged flag is followed by an = and the label, a colon and the partition // number or the word "auto", e.g. voldmanaged=sdcard:3 entry->fs_mgr_flags.vold_managed = true; auto parts = Split(arg, ":"); if (parts.size() != 2) { LWARNING << "Warning: voldmanaged= flag malformed: " << arg; continue; } entry->label = std::move(parts[0]); if (parts[1] == "auto") { entry->partnum = -1; } else { if (!ParseInt(parts[1], &entry->partnum)) { entry->partnum = -1; LWARNING << "Warning: voldmanaged= flag malformed: " << arg; continue; } } } else if (StartsWith(flag, "length=")) { // The length flag is followed by an = and the size of the partition. if (!ParseInt(arg, &entry->length)) { LWARNING << "Warning: length= flag malformed: " << arg; } } else if (StartsWith(flag, "swapprio=")) { if (!ParseInt(arg, &entry->swap_prio)) { LWARNING << "Warning: swapprio= flag malformed: " << arg; } } else if (StartsWith(flag, "zramsize=")) { if (!arg.empty() && arg.back() == '%') { arg.pop_back(); int val; if (ParseInt(arg, &val, 0, 100)) { entry->zram_size = CalculateZramSize(val); } else { LWARNING << "Warning: zramsize= flag malformed: " << arg; } } else { if (!ParseInt(arg, &entry->zram_size)) { LWARNING << "Warning: zramsize= flag malformed: " << arg; } } } else if (StartsWith(flag, "fileencryption=") || flag == "fileencryption") { // "fileencryption" enables file-based encryption. It's normally followed by an = and // then the encryption options. But that can be omitted to use the default options. ParseFileEncryption(arg, entry); } else if (StartsWith(flag, "max_comp_streams=")) { if (!ParseInt(arg, &entry->max_comp_streams)) { LWARNING << "Warning: max_comp_streams= flag malformed: " << arg; } } else if (StartsWith(flag, "reservedsize=")) { // The reserved flag is followed by an = and the reserved size of the partition. uint64_t size; if (!ParseByteCount(arg, &size)) { LWARNING << "Warning: reservedsize= flag malformed: " << arg; } else { entry->reserved_size = static_cast(size); } } else if (StartsWith(flag, "readahead_size_kb=")) { int val; if (ParseInt(arg, &val, 0, 16 * 1024)) { entry->readahead_size_kb = val; } else { LWARNING << "Warning: readahead_size_kb= flag malformed (0 ~ 16MB): " << arg; } } else if (StartsWith(flag, "eraseblk=")) { // The erase block size flag is followed by an = and the flash erase block size. Get it, // check that it is a power of 2 and at least 4096, and return it. off64_t val; if (!ParseInt(arg, &val) || val < 4096 || (val & (val - 1)) != 0) { LWARNING << "Warning: eraseblk= flag malformed: " << arg; } else { entry->erase_blk_size = val; } } else if (StartsWith(flag, "logicalblk=")) { // The logical block size flag is followed by an = and the flash logical block size. Get // it, check that it is a power of 2 and at least 4096, and return it. off64_t val; if (!ParseInt(arg, &val) || val < 4096 || (val & (val - 1)) != 0) { LWARNING << "Warning: logicalblk= flag malformed: " << arg; } else { entry->logical_blk_size = val; } } else if (StartsWith(flag, "avb_keys=")) { // must before the following "avb" entry->avb_keys = arg; } else if (StartsWith(flag, "avb_hashtree_digest=")) { // "avb_hashtree_digest" must before the following "avb" // The path where hex-encoded hashtree descriptor root digest is located. entry->avb_hashtree_digest = arg; } else if (StartsWith(flag, "avb")) { entry->fs_mgr_flags.avb = true; entry->vbmeta_partition = arg; } else if (StartsWith(flag, "keydirectory=")) { // The keydirectory flag enables metadata encryption. It is // followed by an = and the directory containing the metadata // encryption key. entry->metadata_key_dir = arg; } else if (StartsWith(flag, "metadata_encryption=")) { // The metadata_encryption flag specifies the cipher and flags to // use for metadata encryption, if the defaults aren't sufficient. // It doesn't actually enable metadata encryption; that is done by // "keydirectory". entry->metadata_encryption_options = arg; } else if (StartsWith(flag, "sysfs_path=")) { // The path to trigger device gc by idle-maint of vold. entry->sysfs_path = arg; } else if (StartsWith(flag, "zram_backingdev_size=")) { if (!ParseByteCount(arg, &entry->zram_backingdev_size)) { LWARNING << "Warning: zram_backingdev_size= flag malformed: " << arg; } } else if (StartsWith(flag, "device=")) { ParseUserDevices(arg, entry); } else { LWARNING << "Warning: unknown flag: " << flag; } } // FDE is no longer supported, so reject "encryptable" when used without // "vold_managed". For now skip this check when in recovery mode, since // some recovery fstabs still contain the FDE options since they didn't do // anything in recovery mode anyway (except possibly to cause the // reservation of a crypto footer) and thus never got removed. if (entry->fs_mgr_flags.crypt && !entry->fs_mgr_flags.vold_managed && !InRecovery()) { LERROR << "FDE is no longer supported; 'encryptable' can only be used for adoptable " "storage"; return false; } return true; } bool IsDtFstabCompatible() { std::string dt_value; std::string file_name = GetAndroidDtDir() + "fstab/compatible"; if (ReadDtFile(file_name, &dt_value) && dt_value == "android,fstab") { // If there's no status property or its set to "ok" or "okay", then we use the DT fstab. std::string status_value; std::string status_file_name = GetAndroidDtDir() + "fstab/status"; return !ReadDtFile(status_file_name, &status_value) || status_value == "ok" || status_value == "okay"; } return false; } std::string ReadFstabFromDt() { if (!is_dt_compatible() || !IsDtFstabCompatible()) { return {}; } std::string fstabdir_name = GetAndroidDtDir() + "fstab"; std::unique_ptr fstabdir(opendir(fstabdir_name.c_str()), closedir); if (!fstabdir) return {}; dirent* dp; // Each element in fstab_dt_entries is . std::vector> fstab_dt_entries; while ((dp = readdir(fstabdir.get())) != NULL) { // skip over name, compatible and . if (dp->d_type != DT_DIR || dp->d_name[0] == '.') continue; // create \n std::vector fstab_entry; std::string file_name; std::string value; // skip a partition entry if the status property is present and not set to ok file_name = android::base::StringPrintf("%s/%s/status", fstabdir_name.c_str(), dp->d_name); if (ReadDtFile(file_name, &value)) { if (value != "okay" && value != "ok") { LINFO << "dt_fstab: Skip disabled entry for partition " << dp->d_name; continue; } } file_name = android::base::StringPrintf("%s/%s/dev", fstabdir_name.c_str(), dp->d_name); if (!ReadDtFile(file_name, &value)) { LERROR << "dt_fstab: Failed to find device for partition " << dp->d_name; return {}; } fstab_entry.push_back(value); std::string mount_point; file_name = android::base::StringPrintf("%s/%s/mnt_point", fstabdir_name.c_str(), dp->d_name); if (ReadDtFile(file_name, &value)) { LINFO << "dt_fstab: Using a specified mount point " << value << " for " << dp->d_name; mount_point = value; } else { mount_point = android::base::StringPrintf("/%s", dp->d_name); } fstab_entry.push_back(mount_point); file_name = android::base::StringPrintf("%s/%s/type", fstabdir_name.c_str(), dp->d_name); if (!ReadDtFile(file_name, &value)) { LERROR << "dt_fstab: Failed to find type for partition " << dp->d_name; return {}; } fstab_entry.push_back(value); file_name = android::base::StringPrintf("%s/%s/mnt_flags", fstabdir_name.c_str(), dp->d_name); if (!ReadDtFile(file_name, &value)) { LERROR << "dt_fstab: Failed to find type for partition " << dp->d_name; return {}; } fstab_entry.push_back(value); file_name = android::base::StringPrintf("%s/%s/fsmgr_flags", fstabdir_name.c_str(), dp->d_name); if (!ReadDtFile(file_name, &value)) { LERROR << "dt_fstab: Failed to find type for partition " << dp->d_name; return {}; } fstab_entry.push_back(value); // Adds a fstab_entry to fstab_dt_entries, to be sorted by mount_point later. fstab_dt_entries.emplace_back(mount_point, android::base::Join(fstab_entry, " ")); } // Sort fstab_dt entries, to ensure /vendor is mounted before /vendor/abc is attempted. std::sort(fstab_dt_entries.begin(), fstab_dt_entries.end(), [](const auto& a, const auto& b) { return a.first < b.first; }); std::string fstab_result; for (const auto& [_, dt_entry] : fstab_dt_entries) { fstab_result += dt_entry + "\n"; } return fstab_result; } /* Extracts s from the by-name symlinks specified in a fstab: * /dev/block///by-name/ * * can be: platform, pci or vbd. * * For example, given the following entries in the input fstab: * /dev/block/platform/soc/1da4000.ufshc/by-name/system * /dev/block/pci/soc.0/f9824900.sdhci/by-name/vendor * it returns a set { "soc/1da4000.ufshc", "soc.0/f9824900.sdhci" }. */ std::set ExtraBootDevices(const Fstab& fstab) { std::set boot_devices; for (const auto& entry : fstab) { std::string blk_device = entry.blk_device; // Skips blk_device that doesn't conform to the format. if (!android::base::StartsWith(blk_device, "/dev/block") || android::base::StartsWith(blk_device, "/dev/block/by-name") || android::base::StartsWith(blk_device, "/dev/block/bootdevice/by-name")) { continue; } // Skips non-by_name blk_device. // /dev/block///by-name/ // ^ slash_by_name auto slash_by_name = blk_device.find("/by-name"); if (slash_by_name == std::string::npos) continue; blk_device.erase(slash_by_name); // erases /by-name/ // Erases /dev/block/, now we have / blk_device.erase(0, std::string("/dev/block/").size()); // / // ^ first_slash auto first_slash = blk_device.find('/'); if (first_slash == std::string::npos) continue; auto boot_device = blk_device.substr(first_slash + 1); if (!boot_device.empty()) boot_devices.insert(std::move(boot_device)); } return boot_devices; } // Helper class that maps Fstab* -> FstabEntry; const Fstab* -> const FstabEntry. template struct FstabPtrEntry { using is_const_fstab = std::is_const>; using type = std::conditional_t; }; template ::type, typename Pred> std::vector GetEntriesByPred(FstabPtr fstab, const Pred& pred) { if (fstab == nullptr) { return {}; } std::vector entries; for (FstabPtrEntryType& entry : *fstab) { if (pred(entry)) { entries.push_back(&entry); } } return entries; } } // namespace // Return the path to the fstab file. There may be multiple fstab files; the // one that is returned will be the first that exists of fstab., // fstab., and fstab.. The fstab is searched for // in /odm/etc/ and /vendor/etc/, as well as in the locations where it may be in // the first stage ramdisk during early boot. Previously, the first stage // ramdisk's copy of the fstab had to be located in the root directory, but now // the system/etc directory is supported too and is the preferred location. std::string GetFstabPath() { if (InRecovery()) { return "/etc/recovery.fstab"; } for (const char* prop : {"fstab_suffix", "hardware", "hardware.platform"}) { std::string suffix; if (!fs_mgr_get_boot_config(prop, &suffix)) continue; for (const char* prefix : {// late-boot/post-boot locations "/odm/etc/fstab.", "/vendor/etc/fstab.", // early boot locations "/system/etc/fstab.", "/first_stage_ramdisk/system/etc/fstab.", "/fstab.", "/first_stage_ramdisk/fstab."}) { std::string fstab_path = prefix + suffix; if (access(fstab_path.c_str(), F_OK) == 0) { return fstab_path; } } } return ""; } bool ParseFstabFromString(const std::string& fstab_str, bool proc_mounts, Fstab* fstab_out) { const int expected_fields = proc_mounts ? 4 : 5; Fstab fstab; for (const auto& line : android::base::Split(fstab_str, "\n")) { auto fields = android::base::Tokenize(line, " \t"); // Ignore empty lines and comments. if (fields.empty() || android::base::StartsWith(fields.front(), '#')) { continue; } if (fields.size() < expected_fields) { LERROR << "Error parsing fstab: expected " << expected_fields << " fields, got " << fields.size(); return false; } FstabEntry entry; auto it = fields.begin(); entry.blk_device = std::move(*it++); entry.mount_point = std::move(*it++); entry.fs_type = std::move(*it++); ParseMountFlags(std::move(*it++), &entry); // For /proc/mounts, ignore everything after mnt_freq and mnt_passno if (!proc_mounts && !ParseFsMgrFlags(std::move(*it++), &entry)) { LERROR << "Error parsing fs_mgr_flags"; return false; } if (entry.fs_mgr_flags.logical) { entry.logical_partition_name = entry.blk_device; } fstab.emplace_back(std::move(entry)); } if (fstab.empty()) { LERROR << "No entries found in fstab"; return false; } /* If an A/B partition, modify block device to be the real block device */ if (!fs_mgr_update_for_slotselect(&fstab)) { LERROR << "Error updating for slotselect"; return false; } *fstab_out = std::move(fstab); return true; } void TransformFstabForDsu(Fstab* fstab, const std::string& dsu_slot, const std::vector& dsu_partitions) { static constexpr char kDsuKeysDir[] = "/avb"; for (auto&& partition : dsu_partitions) { if (!EndsWith(partition, gsi::kDsuPostfix)) { continue; } // scratch is handled by fs_mgr_overlayfs if (partition == android::gsi::kDsuScratch) { continue; } // Convert userdata partition. if (partition == android::gsi::kDsuUserdata) { for (auto&& entry : GetEntriesForMountPoint(fstab, "/data")) { entry->blk_device = android::gsi::kDsuUserdata; entry->fs_mgr_flags.logical = true; entry->fs_mgr_flags.formattable = true; if (!entry->metadata_key_dir.empty()) { entry->metadata_key_dir = android::gsi::GetDsuMetadataKeyDir(dsu_slot); } } continue; } // Convert RO partitions. // // dsu_partition_name = corresponding_partition_name + kDsuPostfix // e.g. // system_gsi for system // product_gsi for product // vendor_gsi for vendor std::string lp_name = partition.substr(0, partition.length() - strlen(gsi::kDsuPostfix)); std::string mount_point = "/" + lp_name; // List of fs_type entries we're lacking, need to synthesis these later. std::vector lack_fs_list = {"ext4", "erofs"}; // Only support early mount (first_stage_mount) partitions. auto pred = [&mount_point](const FstabEntry& entry) { return entry.fs_mgr_flags.first_stage_mount && entry.mount_point == mount_point; }; // Transform all matching entries and assume they are all adjacent for simplicity. for (auto&& entry : GetEntriesByPred(fstab, pred)) { // .blk_device is replaced with the DSU partition. entry->blk_device = partition; // .avb_keys hints first_stage_mount to load the chained-vbmeta image from partition // footer. See aosp/932779 for more details. entry->avb_keys = kDsuKeysDir; // .logical_partition_name is required to look up AVB Hashtree descriptors. entry->logical_partition_name = lp_name; entry->fs_mgr_flags.logical = true; entry->fs_mgr_flags.slot_select = false; entry->fs_mgr_flags.slot_select_other = false; if (auto it = std::find(lack_fs_list.begin(), lack_fs_list.end(), entry->fs_type); it != lack_fs_list.end()) { lack_fs_list.erase(it); } } if (!lack_fs_list.empty()) { // Insert at the end of the existing mountpoint group, or at the end of fstab. // We assume there is at most one matching mountpoint group, which is the common case. auto it = std::find_if_not(std::find_if(fstab->begin(), fstab->end(), pred), fstab->end(), pred); for (const auto& fs_type : lack_fs_list) { it = std::next(fstab->insert(it, {.blk_device = partition, .logical_partition_name = lp_name, .mount_point = mount_point, .fs_type = fs_type, .flags = MS_RDONLY, .avb_keys = kDsuKeysDir, .fs_mgr_flags{ .wait = true, .logical = true, .first_stage_mount = true, }})); } } } } void EnableMandatoryFlags(Fstab* fstab) { // Devices launched in R and after must support fs_verity. Set flag to cause tune2fs // to enable the feature on userdata and metadata partitions. if (android::base::GetIntProperty("ro.product.first_api_level", 0) >= 30) { // Devices launched in R and after should enable fs_verity on userdata. // A better alternative would be to enable on mkfs at the beginning. std::vector data_entries = GetEntriesForMountPoint(fstab, "/data"); for (auto&& entry : data_entries) { // Besides ext4, f2fs is also supported. But the image is already created with verity // turned on when it was first introduced. if (entry->fs_type == "ext4") { entry->fs_mgr_flags.fs_verity = true; } } // Devices shipping with S and earlier likely do not already have fs_verity enabled via // mkfs, so enable it here. std::vector metadata_entries = GetEntriesForMountPoint(fstab, "/metadata"); for (auto&& entry : metadata_entries) { entry->fs_mgr_flags.fs_verity = true; } } } static bool ReadFstabFromFileCommon(const std::string& path, Fstab* fstab_out) { std::string fstab_str; if (!android::base::ReadFileToString(path, &fstab_str, /* follow_symlinks = */ true)) { PERROR << __FUNCTION__ << "(): failed to read file: '" << path << "'"; return false; } Fstab fstab; if (!ParseFstabFromString(fstab_str, path == kProcMountsPath, &fstab)) { LERROR << __FUNCTION__ << "(): failed to load fstab from : '" << path << "'"; return false; } EnableMandatoryFlags(&fstab); *fstab_out = std::move(fstab); return true; } bool ReadFstabFromFile(const std::string& path, Fstab* fstab) { if (!ReadFstabFromFileCommon(path, fstab)) { return false; } if (path != kProcMountsPath && !InRecovery()) { if (!access(android::gsi::kGsiBootedIndicatorFile, F_OK)) { std::string dsu_slot; if (!android::gsi::GetActiveDsu(&dsu_slot)) { PERROR << __FUNCTION__ << "(): failed to get active DSU slot"; return false; } std::string lp_names; if (!ReadFileToString(gsi::kGsiLpNamesFile, &lp_names)) { PERROR << __FUNCTION__ << "(): failed to read DSU LP names"; return false; } TransformFstabForDsu(fstab, dsu_slot, Split(lp_names, ",")); } else if (errno != ENOENT) { PERROR << __FUNCTION__ << "(): failed to access() DSU booted indicator"; return false; } SkipMountingPartitions(fstab, false /* verbose */); } return true; } bool ReadFstabFromProcMounts(Fstab* fstab) { // Don't call `ReadFstabFromFile` because the code for `path != kProcMountsPath` has an extra // code size cost, even if it's never executed. return ReadFstabFromFileCommon(kProcMountsPath, fstab); } // Returns fstab entries parsed from the device tree if they exist bool ReadFstabFromDt(Fstab* fstab, bool verbose) { std::string fstab_buf = ReadFstabFromDt(); if (fstab_buf.empty()) { if (verbose) LINFO << __FUNCTION__ << "(): failed to read fstab from dt"; return false; } if (!ParseFstabFromString(fstab_buf, /* proc_mounts = */ false, fstab)) { if (verbose) { LERROR << __FUNCTION__ << "(): failed to load fstab from kernel:" << std::endl << fstab_buf; } return false; } SkipMountingPartitions(fstab, verbose); return true; } #ifdef NO_SKIP_MOUNT static constexpr bool kNoSkipMount = true; #else static constexpr bool kNoSkipMount = false; #endif // For GSI to skip mounting /product and /system_ext, until there are well-defined interfaces // between them and /system. Otherwise, the GSI flashed on /system might not be able to work with // device-specific /product and /system_ext. skip_mount.cfg belongs to system_ext partition because // only common files for all targets can be put into system partition. It is under // /system/system_ext because GSI is a single system.img that includes the contents of system_ext // partition and product partition under /system/system_ext and /system/product, respectively. bool SkipMountingPartitions(Fstab* fstab, bool verbose) { if (kNoSkipMount) { return true; } static constexpr char kSkipMountConfig[] = "/system/system_ext/etc/init/config/skip_mount.cfg"; std::string skip_mount_config; auto save_errno = errno; if (!ReadFileToString(kSkipMountConfig, &skip_mount_config)) { errno = save_errno; // missing file is expected return true; } return SkipMountWithConfig(skip_mount_config, fstab, verbose); } bool SkipMountWithConfig(const std::string& skip_mount_config, Fstab* fstab, bool verbose) { std::vector skip_mount_patterns; for (const auto& line : Split(skip_mount_config, "\n")) { if (line.empty() || StartsWith(line, "#")) { continue; } skip_mount_patterns.push_back(line); } // Returns false if mount_point matches any of the skip mount patterns, so that the FstabEntry // would be partitioned to the second group. auto glob_pattern_mismatch = [&skip_mount_patterns](const FstabEntry& entry) -> bool { for (const auto& pattern : skip_mount_patterns) { if (!fnmatch(pattern.c_str(), entry.mount_point.c_str(), 0 /* flags */)) { return false; } } return true; }; auto remove_from = std::stable_partition(fstab->begin(), fstab->end(), glob_pattern_mismatch); if (verbose) { for (auto it = remove_from; it != fstab->end(); ++it) { LINFO << "Skip mounting mountpoint: " << it->mount_point; } } fstab->erase(remove_from, fstab->end()); return true; } // Loads the fstab file and combines with fstab entries passed in from device tree. bool ReadDefaultFstab(Fstab* fstab) { fstab->clear(); ReadFstabFromDt(fstab, false /* verbose */); Fstab default_fstab; const std::string default_fstab_path = GetFstabPath(); if (!default_fstab_path.empty() && ReadFstabFromFile(default_fstab_path, &default_fstab)) { fstab->insert(fstab->end(), std::make_move_iterator(default_fstab.begin()), std::make_move_iterator(default_fstab.end())); } else { LINFO << __FUNCTION__ << "(): failed to find device default fstab"; } return !fstab->empty(); } std::vector GetEntriesForMountPoint(Fstab* fstab, const std::string& path) { return GetEntriesByPred(fstab, [&path](const FstabEntry& entry) { return entry.mount_point == path; }); } FstabEntry* GetEntryForMountPoint(Fstab* fstab, const std::string_view path, const std::string_view fstype) { auto&& vec = GetEntriesByPred(fstab, [&path, fstype](const FstabEntry& entry) { return entry.mount_point == path && entry.fs_type == fstype; }); return vec.empty() ? nullptr : vec.front(); } std::vector GetEntriesForMountPoint(const Fstab* fstab, const std::string& path) { return GetEntriesByPred(fstab, [&path](const FstabEntry& entry) { return entry.mount_point == path; }); } FstabEntry* GetEntryForMountPoint(Fstab* fstab, const std::string& path) { std::vector entries = GetEntriesForMountPoint(fstab, path); return entries.empty() ? nullptr : entries.front(); } const FstabEntry* GetEntryForMountPoint(const Fstab* fstab, const std::string& path) { std::vector entries = GetEntriesForMountPoint(fstab, path); return entries.empty() ? nullptr : entries.front(); } std::set GetBootDevices() { std::set boot_devices; // First check bootconfig, then kernel commandline, then the device tree std::string value; if (GetBootconfig("androidboot.boot_devices", &value) || GetBootconfig("androidboot.boot_device", &value)) { // split by spaces and trim the trailing comma. for (std::string_view device : android::base::Split(value, " ")) { base::ConsumeSuffix(&device, ","); boot_devices.emplace(device); } return boot_devices; } const std::string dt_file_name = GetAndroidDtDir() + "boot_devices"; if (GetKernelCmdline("androidboot.boot_devices", &value) || ReadDtFile(dt_file_name, &value)) { auto boot_devices_list = Split(value, ","); return {std::make_move_iterator(boot_devices_list.begin()), std::make_move_iterator(boot_devices_list.end())}; } ImportKernelCmdline([&](std::string key, std::string value) { if (key == "androidboot.boot_device") { boot_devices.emplace(std::move(value)); } }); if (!boot_devices.empty()) { return boot_devices; } // Fallback to extract boot devices from fstab. Fstab fstab; if (!ReadDefaultFstab(&fstab)) { return {}; } return ExtraBootDevices(fstab); } std::string GetVerityDeviceName(const FstabEntry& entry) { std::string base_device; if (entry.mount_point == "/") { // When using system-as-root, the device name is fixed as "vroot". if (entry.fs_mgr_flags.avb) { return "vroot"; } base_device = "system"; } else { base_device = android::base::Basename(entry.mount_point); } return base_device + "-verity"; } bool InRecovery() { // Check the existence of recovery binary instead of using the compile time // __ANDROID_RECOVERY__ macro. // If BOARD_USES_RECOVERY_AS_BOOT is true, both normal and recovery boot // mode would use the same init binary, which would mean during normal boot // the '/init' binary is actually a symlink pointing to // init_second_stage.recovery, which would be compiled with // __ANDROID_RECOVERY__ defined. return access("/system/bin/recovery", F_OK) == 0 || access("/sbin/recovery", F_OK) == 0; } } // namespace fs_mgr } // namespace android bool is_dt_compatible() { std::string file_name = android::fs_mgr::GetAndroidDtDir() + "compatible"; std::string dt_value; if (android::fs_mgr::ReadDtFile(file_name, &dt_value)) { if (dt_value == "android,firmware") { return true; } } return false; }