-----BEGIN PGP SIGNATURE----- iF0EABECAB0WIQRDQNE1cO+UXoOBCWTorT+BmrEOeAUCZ8epjwAKCRDorT+BmrEO eGPtAJ4xkVvM0OmK/dZBwdVVDMjKroC/zACaAsDXpFeOe6kT2WhEkvc6MqpfuNQ= =5OoV -----END PGP SIGNATURE----- gpgsig -----BEGIN SSH SIGNATURE----- U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgPpdpjxPACTIhnlvYz0GM4BR7FJ +rYv3jMbfxNKD3JvcAAAADZ2l0AAAAAAAAAAZzaGE1MTIAAABTAAAAC3NzaC1lZDI1NTE5 AAAAQCn8sR4oKubEOLtjfwngAI9k+KVB6e2XzmS6vwsN1oRV3O7k4oSXLnNH+sHPQXQ6lX 4cqrmxPKTONclXrV4Ggw8= -----END SSH SIGNATURE----- Merge tag 'android-15.0.0_r20' into staging/lineage-22.2_merge-android-15.0.0_r20 Android 15.0.0 Release 20 (BP1A.250305.019) # -----BEGIN PGP SIGNATURE----- # # iF0EABECAB0WIQRDQNE1cO+UXoOBCWTorT+BmrEOeAUCZ8epjwAKCRDorT+BmrEO # eGPtAJ4xkVvM0OmK/dZBwdVVDMjKroC/zACaAsDXpFeOe6kT2WhEkvc6MqpfuNQ= # =5OoV # -----END PGP SIGNATURE----- # gpg: Signature made Wed Mar 5 03:31:59 2025 EET # gpg: using DSA key 4340D13570EF945E83810964E8AD3F819AB10E78 # gpg: Good signature from "The Android Open Source Project <initial-contribution@android.com>" [ultimate] * tag 'android-15.0.0_r20': (183 commits) Revert "Define ueventd.rc.recovery" Define ueventd.rc.recovery Define init_second_stage.recovery Define reboot.recovery and watchdogd.recovery debuggerd: Use libprocessgroup to unfreeze Define toolbox.recovery Replace partition-specific toybox make module with soong modules Start aconfigd socket defined in configinfra mainline module Update trusty to use secretkeeper hal V1 ashmem: Ensure all memfds have non-executable permissions by default libsnapshot: Cleanup temp metadata during rollback libprocessgroup: Remove ramdisk_available from libcgrouprc libprocessgroup: Remove vendor_ramdisk_available from libcgrouprc libprocessgroup: Remove recovery_available from libcgrouprc gatekeeperd_service_fuzzer: Add signal() to handle SIGPIPE libutils OWNERS for shayba@ Deprecate cc_binary aconfigd and the controlling flag libprefetch: rename property name Update comments to point to the new location of event.logtags. Fix the dm-verity Merkle tree caches to not expire so quickly ... Conflicts: init/devices.cpp Change-Id: I16f4b8b40b74074b087b2fc719cf4a322ccd76cf
1005 lines
39 KiB
C++
1005 lines
39 KiB
C++
/*
|
|
* 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 <ctype.h>
|
|
#include <dirent.h>
|
|
#include <errno.h>
|
|
#include <fnmatch.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/mount.h>
|
|
#include <unistd.h>
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include <android-base/file.h>
|
|
#include <android-base/parseint.h>
|
|
#include <android-base/properties.h>
|
|
#include <android-base/stringprintf.h>
|
|
#include <android-base/strings.h>
|
|
#include <libgsi/libgsi.h>
|
|
|
|
#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},
|
|
{"lazytime", MS_LAZYTIME},
|
|
{"nosymfollow", MS_NOSYMFOLLOW},
|
|
{"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<off64_t>(0),
|
|
std::numeric_limits<off64_t>::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]);
|
|
entry->device_aliased.push_back(param[0] == "exp_alias" ? 1 : 0);
|
|
}
|
|
|
|
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);
|
|
CheckFlag("wrappedkey", wrapped_key);
|
|
|
|
#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, 200)) {
|
|
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<off64_t>(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<DIR, int (*)(DIR*)> fstabdir(opendir(fstabdir_name.c_str()), closedir);
|
|
if (!fstabdir) return {};
|
|
|
|
dirent* dp;
|
|
// Each element in fstab_dt_entries is <mount point, the line format in fstab file>.
|
|
std::vector<std::pair<std::string, std::string>> 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 <dev> <mnt_point> <type> <mnt_flags> <fsmgr_flags>\n
|
|
std::vector<std::string> 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 <device>s from the by-name symlinks specified in a fstab:
|
|
* /dev/block/<type>/<device>/by-name/<partition>
|
|
*
|
|
* <type> 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<std::string> ExtraBootDevices(const Fstab& fstab) {
|
|
std::set<std::string> 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/<type>/<device>/by-name/<partition>
|
|
// ^ 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/<partition>
|
|
|
|
// Erases /dev/block/, now we have <type>/<device>
|
|
blk_device.erase(0, std::string("/dev/block/").size());
|
|
|
|
// <type>/<device>
|
|
// ^ 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 <typename FstabPtr>
|
|
struct FstabPtrEntry {
|
|
using is_const_fstab = std::is_const<std::remove_pointer_t<FstabPtr>>;
|
|
using type = std::conditional_t<is_const_fstab::value, const FstabEntry, FstabEntry>;
|
|
};
|
|
|
|
template <typename FstabPtr, typename FstabPtrEntryType = typename FstabPtrEntry<FstabPtr>::type,
|
|
typename Pred>
|
|
std::vector<FstabPtrEntryType*> GetEntriesByPred(FstabPtr fstab, const Pred& pred) {
|
|
if (fstab == nullptr) {
|
|
return {};
|
|
}
|
|
std::vector<FstabPtrEntryType*> entries;
|
|
for (FstabPtrEntryType& entry : *fstab) {
|
|
if (pred(entry)) {
|
|
entries.push_back(&entry);
|
|
}
|
|
}
|
|
return entries;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
// Return the path to the recovery fstab file. There may be multiple fstab files;
|
|
// the one that is returned will be the first that exists of recovery.fstab.<fstab_suffix>,
|
|
// recovery.fstab.<hardware>, and recovery.fstab.<hardware.platform>.
|
|
std::string GetRecoveryFstabPath() {
|
|
for (const char* prop : {"fstab_suffix", "hardware", "hardware.platform"}) {
|
|
std::string suffix;
|
|
|
|
if (!fs_mgr_get_boot_config(prop, &suffix)) continue;
|
|
|
|
std::string fstab_path = "/etc/recovery.fstab." + suffix;
|
|
if (access(fstab_path.c_str(), F_OK) == 0) {
|
|
return fstab_path;
|
|
}
|
|
}
|
|
|
|
return "/etc/recovery.fstab";
|
|
}
|
|
|
|
// 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_suffix>,
|
|
// fstab.<hardware>, and fstab.<hardware.platform>. 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 GetRecoveryFstabPath();
|
|
}
|
|
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<std::string>& 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<std::string> 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<FstabEntry*> 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<FstabEntry*> 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<std::string> 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<FstabEntry*> 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<const FstabEntry*> 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<FstabEntry*> entries = GetEntriesForMountPoint(fstab, path);
|
|
return entries.empty() ? nullptr : entries.front();
|
|
}
|
|
|
|
const FstabEntry* GetEntryForMountPoint(const Fstab* fstab, const std::string& path) {
|
|
std::vector<const FstabEntry*> entries = GetEntriesForMountPoint(fstab, path);
|
|
return entries.empty() ? nullptr : entries.front();
|
|
}
|
|
|
|
std::set<std::string> GetBootDevices() {
|
|
std::set<std::string> 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 GetBootPartUuid() {
|
|
std::string boot_part_uuid;
|
|
|
|
if (GetBootconfig("androidboot.boot_part_uuid", &boot_part_uuid)) {
|
|
return boot_part_uuid;
|
|
}
|
|
|
|
ImportKernelCmdline([&](std::string key, std::string value) {
|
|
if (key == "androidboot.boot_part_uuid") {
|
|
boot_part_uuid = value;
|
|
}
|
|
});
|
|
|
|
return boot_part_uuid;
|
|
}
|
|
|
|
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;
|
|
}
|