/* * Copyright (C) 2017 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 "first_stage_mount.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "devices.h" #include "switch_root.h" #include "uevent.h" #include "uevent_listener.h" #include "util.h" using android::base::ReadFileToString; using android::base::Split; using android::base::Timer; using android::fs_mgr::AvbHandle; using android::fs_mgr::AvbHashtreeResult; using android::fs_mgr::AvbUniquePtr; using namespace std::literals; namespace android { namespace init { // Class Declarations // ------------------ class FirstStageMount { public: FirstStageMount(Fstab fstab); virtual ~FirstStageMount() = default; // The factory method to create either FirstStageMountVBootV1 or FirstStageMountVBootV2 // based on device tree configurations. static std::unique_ptr Create(); bool DoFirstStageMount(); // Mounts fstab entries read from device tree. bool InitDevices(); protected: ListenerAction HandleBlockDevice(const std::string& name, const Uevent&); bool InitRequiredDevices(); bool InitMappedDevice(const std::string& verity_device); bool CreateLogicalPartitions(); bool MountPartition(const Fstab::iterator& begin, bool erase_used_fstab_entry, Fstab::iterator* end = nullptr); bool MountPartitions(); bool TrySwitchSystemAsRoot(); bool TrySkipMountingPartitions(); bool IsDmLinearEnabled(); bool GetDmLinearMetadataDevice(); bool InitDmLinearBackingDevices(const android::fs_mgr::LpMetadata& metadata); void UseGsiIfPresent(); ListenerAction UeventCallback(const Uevent& uevent); // Pure virtual functions. virtual bool GetDmVerityDevices() = 0; virtual bool SetUpDmVerity(FstabEntry* fstab_entry) = 0; bool need_dm_verity_; Fstab fstab_; std::string lp_metadata_partition_; std::set required_devices_partition_names_; std::string super_partition_name_; std::unique_ptr device_handler_; UeventListener uevent_listener_; }; class FirstStageMountVBootV1 : public FirstStageMount { public: FirstStageMountVBootV1(Fstab fstab) : FirstStageMount(std::move(fstab)) {} ~FirstStageMountVBootV1() override = default; protected: bool GetDmVerityDevices() override; bool SetUpDmVerity(FstabEntry* fstab_entry) override; }; class FirstStageMountVBootV2 : public FirstStageMount { public: friend void SetInitAvbVersionInRecovery(); FirstStageMountVBootV2(Fstab fstab); ~FirstStageMountVBootV2() override = default; protected: bool GetDmVerityDevices() override; bool SetUpDmVerity(FstabEntry* fstab_entry) override; bool InitAvbHandle(); std::vector vbmeta_partitions_; AvbUniquePtr avb_handle_; }; // Static Functions // ---------------- static inline bool IsDtVbmetaCompatible(const Fstab& fstab) { if (std::any_of(fstab.begin(), fstab.end(), [](const auto& entry) { return entry.fs_mgr_flags.avb; })) { return true; } return is_android_dt_value_expected("vbmeta/compatible", "android,vbmeta"); } static bool IsRecoveryMode() { return access("/system/bin/recovery", F_OK) == 0; } static Fstab ReadFirstStageFstab() { Fstab fstab; if (!ReadFstabFromDt(&fstab)) { if (ReadDefaultFstab(&fstab)) { fstab.erase(std::remove_if(fstab.begin(), fstab.end(), [](const auto& entry) { return !entry.fs_mgr_flags.first_stage_mount; }), fstab.end()); } else { LOG(INFO) << "Failed to fstab for first stage mount"; } } return fstab; } // Class Definitions // ----------------- FirstStageMount::FirstStageMount(Fstab fstab) : need_dm_verity_(false), fstab_(std::move(fstab)), uevent_listener_(16 * 1024 * 1024) { auto boot_devices = fs_mgr_get_boot_devices(); device_handler_ = std::make_unique( std::vector{}, std::vector{}, std::vector{}, std::move(boot_devices), false); super_partition_name_ = fs_mgr_get_super_partition_name(); } std::unique_ptr FirstStageMount::Create() { auto fstab = ReadFirstStageFstab(); if (IsDtVbmetaCompatible(fstab)) { return std::make_unique(std::move(fstab)); } else { return std::make_unique(std::move(fstab)); } } bool FirstStageMount::DoFirstStageMount() { if (!IsDmLinearEnabled() && fstab_.empty()) { // Nothing to mount. LOG(INFO) << "First stage mount skipped (missing/incompatible/empty fstab in device tree)"; return true; } if (!InitDevices()) return false; if (!CreateLogicalPartitions()) return false; if (!MountPartitions()) return false; return true; } bool FirstStageMount::InitDevices() { return GetDmLinearMetadataDevice() && GetDmVerityDevices() && InitRequiredDevices(); } bool FirstStageMount::IsDmLinearEnabled() { for (const auto& entry : fstab_) { if (entry.fs_mgr_flags.logical) return true; } return false; } bool FirstStageMount::GetDmLinearMetadataDevice() { // Add any additional devices required for dm-linear mappings. if (!IsDmLinearEnabled()) { return true; } required_devices_partition_names_.emplace(super_partition_name_); // When booting from live GSI images, userdata is the super device. required_devices_partition_names_.emplace("userdata"); return true; } // Creates devices with uevent->partition_name matching one in the member variable // required_devices_partition_names_. Found partitions will then be removed from it // for the subsequent member function to check which devices are NOT created. bool FirstStageMount::InitRequiredDevices() { if (required_devices_partition_names_.empty()) { return true; } if (IsDmLinearEnabled() || need_dm_verity_) { const std::string dm_path = "/devices/virtual/misc/device-mapper"; bool found = false; auto dm_callback = [this, &dm_path, &found](const Uevent& uevent) { if (uevent.path == dm_path) { device_handler_->HandleUevent(uevent); found = true; return ListenerAction::kStop; } return ListenerAction::kContinue; }; uevent_listener_.RegenerateUeventsForPath("/sys" + dm_path, dm_callback); if (!found) { LOG(INFO) << "device-mapper device not found in /sys, waiting for its uevent"; Timer t; uevent_listener_.Poll(dm_callback, 10s); LOG(INFO) << "Wait for device-mapper returned after " << t; } if (!found) { LOG(ERROR) << "device-mapper device not found after polling timeout"; return false; } } auto uevent_callback = [this](const Uevent& uevent) { return UeventCallback(uevent); }; uevent_listener_.RegenerateUevents(uevent_callback); // UeventCallback() will remove found partitions from required_devices_partition_names_. // So if it isn't empty here, it means some partitions are not found. if (!required_devices_partition_names_.empty()) { LOG(INFO) << __PRETTY_FUNCTION__ << ": partition(s) not found in /sys, waiting for their uevent(s): " << android::base::Join(required_devices_partition_names_, ", "); Timer t; uevent_listener_.Poll(uevent_callback, 10s); LOG(INFO) << "Wait for partitions returned after " << t; } if (!required_devices_partition_names_.empty()) { LOG(ERROR) << __PRETTY_FUNCTION__ << ": partition(s) not found after polling timeout: " << android::base::Join(required_devices_partition_names_, ", "); return false; } return true; } bool FirstStageMount::InitDmLinearBackingDevices(const android::fs_mgr::LpMetadata& metadata) { auto partition_names = android::fs_mgr::GetBlockDevicePartitionNames(metadata); for (const auto& partition_name : partition_names) { const auto super_device = android::fs_mgr::GetMetadataSuperBlockDevice(metadata); if (partition_name == android::fs_mgr::GetBlockDevicePartitionName(*super_device)) { continue; } required_devices_partition_names_.emplace(partition_name); } if (required_devices_partition_names_.empty()) { return true; } auto uevent_callback = [this](const Uevent& uevent) { return UeventCallback(uevent); }; uevent_listener_.RegenerateUevents(uevent_callback); if (!required_devices_partition_names_.empty()) { LOG(ERROR) << __PRETTY_FUNCTION__ << ": partition(s) not found after polling timeout: " << android::base::Join(required_devices_partition_names_, ", "); return false; } return true; } bool FirstStageMount::CreateLogicalPartitions() { if (!IsDmLinearEnabled()) { return true; } if (lp_metadata_partition_.empty()) { LOG(ERROR) << "Could not locate logical partition tables in partition " << super_partition_name_; return false; } auto metadata = android::fs_mgr::ReadCurrentMetadata(lp_metadata_partition_); if (!metadata) { LOG(ERROR) << "Could not read logical partition metadata from " << lp_metadata_partition_; return false; } if (!InitDmLinearBackingDevices(*metadata.get())) { return false; } return android::fs_mgr::CreateLogicalPartitions(*metadata.get(), lp_metadata_partition_); } ListenerAction FirstStageMount::HandleBlockDevice(const std::string& name, const Uevent& uevent) { // Matches partition name to create device nodes. // Both required_devices_partition_names_ and uevent->partition_name have A/B // suffix when A/B is used. auto iter = required_devices_partition_names_.find(name); if (iter != required_devices_partition_names_.end()) { LOG(VERBOSE) << __PRETTY_FUNCTION__ << ": found partition: " << *iter; if (IsDmLinearEnabled() && name == super_partition_name_) { std::vector links = device_handler_->GetBlockDeviceSymlinks(uevent); lp_metadata_partition_ = links[0]; } required_devices_partition_names_.erase(iter); device_handler_->HandleUevent(uevent); if (required_devices_partition_names_.empty()) { return ListenerAction::kStop; } else { return ListenerAction::kContinue; } } return ListenerAction::kContinue; } ListenerAction FirstStageMount::UeventCallback(const Uevent& uevent) { // Ignores everything that is not a block device. if (uevent.subsystem != "block") { return ListenerAction::kContinue; } if (!uevent.partition_name.empty()) { return HandleBlockDevice(uevent.partition_name, uevent); } else { size_t base_idx = uevent.path.rfind('/'); if (base_idx != std::string::npos) { return HandleBlockDevice(uevent.path.substr(base_idx + 1), uevent); } } // Not found a partition or find an unneeded partition, continue to find others. return ListenerAction::kContinue; } // Creates "/dev/block/dm-XX" for dm-verity by running coldboot on /sys/block/dm-XX. bool FirstStageMount::InitMappedDevice(const std::string& dm_device) { const std::string device_name(basename(dm_device.c_str())); const std::string syspath = "/sys/block/" + device_name; bool found = false; auto verity_callback = [&device_name, &dm_device, this, &found](const Uevent& uevent) { if (uevent.device_name == device_name) { LOG(VERBOSE) << "Creating device-mapper device : " << dm_device; device_handler_->HandleUevent(uevent); found = true; return ListenerAction::kStop; } return ListenerAction::kContinue; }; uevent_listener_.RegenerateUeventsForPath(syspath, verity_callback); if (!found) { LOG(INFO) << "dm-verity device not found in /sys, waiting for its uevent"; Timer t; uevent_listener_.Poll(verity_callback, 10s); LOG(INFO) << "wait for dm-verity device returned after " << t; } if (!found) { LOG(ERROR) << "dm-verity device not found after polling timeout"; return false; } return true; } bool FirstStageMount::MountPartition(const Fstab::iterator& begin, bool erase_used_fstab_entry, Fstab::iterator* end) { if (begin->fs_mgr_flags.logical) { if (!fs_mgr_update_logical_partition(&(*begin))) { return false; } if (!InitMappedDevice(begin->blk_device)) { return false; } } if (!SetUpDmVerity(&(*begin))) { PLOG(ERROR) << "Failed to setup verity for '" << begin->mount_point << "'"; return false; } bool mounted = (fs_mgr_do_mount_one(*begin) == 0); // Try other mounts with the same mount point. Fstab::iterator current = begin + 1; for (; current != fstab_.end() && current->mount_point == begin->mount_point; current++) { if (!mounted) { // blk_device is already updated to /dev/dm- by SetUpDmVerity() above. // Copy it from the begin iterator. current->blk_device = begin->blk_device; mounted = (fs_mgr_do_mount_one(*current) == 0); } } if (erase_used_fstab_entry) { current = fstab_.erase(begin, current); } if (end) { *end = current; } return mounted; } // If system is in the fstab then we're not a system-as-root device, and in // this case, we mount system first then pivot to it. From that point on, // we are effectively identical to a system-as-root device. bool FirstStageMount::TrySwitchSystemAsRoot() { auto metadata_partition = std::find_if(fstab_.begin(), fstab_.end(), [](const auto& entry) { return entry.mount_point == "/metadata"; }); if (metadata_partition != fstab_.end()) { if (MountPartition(metadata_partition, true /* erase_used_fstab_entry */)) { UseGsiIfPresent(); } } auto system_partition = std::find_if(fstab_.begin(), fstab_.end(), [](const auto& entry) { return entry.mount_point == "/system"; }); if (system_partition == fstab_.end()) return true; if (MountPartition(system_partition, true /* erase_used_fstab_entry */)) { SwitchRoot("/system"); } else { PLOG(ERROR) << "Failed to mount /system"; return false; } return true; } // For GSI to skip mounting /product and /product_services, until there are // well-defined interfaces between them and /system. Otherwise, the GSI flashed // on /system might not be able to work with /product and /product_services. // When they're skipped here, /system/product and /system/product_services in // GSI will be used. bool FirstStageMount::TrySkipMountingPartitions() { constexpr const char kSkipMountConfig[] = "/system/etc/init/config/skip_mount.cfg"; std::string skip_config; if (!ReadFileToString(kSkipMountConfig, &skip_config)) { return true; } for (const auto& skip_mount_point : Split(skip_config, "\n")) { if (skip_mount_point.empty()) { continue; } auto it = std::remove_if(fstab_.begin(), fstab_.end(), [&skip_mount_point](const auto& entry) { return entry.mount_point == skip_mount_point; }); fstab_.erase(it, fstab_.end()); LOG(INFO) << "Skip mounting partition: " << skip_mount_point; } return true; } bool FirstStageMount::MountPartitions() { if (!TrySwitchSystemAsRoot()) return false; if (!TrySkipMountingPartitions()) return false; for (auto current = fstab_.begin(); current != fstab_.end();) { Fstab::iterator end; if (!MountPartition(current, false, &end)) { if (current->fs_mgr_flags.no_fail) { LOG(INFO) << "Failed to mount " << current->mount_point << ", ignoring mount for no_fail partition"; } else if (current->fs_mgr_flags.formattable) { LOG(INFO) << "Failed to mount " << current->mount_point << ", ignoring mount for formattable partition"; } else { PLOG(ERROR) << "Failed to mount " << current->mount_point; return false; } } current = end; } // heads up for instantiating required device(s) for overlayfs logic const auto devices = fs_mgr_overlayfs_required_devices(&fstab_); for (auto const& device : devices) { if (android::base::StartsWith(device, "/dev/block/by-name/")) { required_devices_partition_names_.emplace(basename(device.c_str())); auto uevent_callback = [this](const Uevent& uevent) { return UeventCallback(uevent); }; uevent_listener_.RegenerateUevents(uevent_callback); uevent_listener_.Poll(uevent_callback, 10s); } else { InitMappedDevice(device); } } fs_mgr_overlayfs_mount_all(&fstab_); return true; } void FirstStageMount::UseGsiIfPresent() { std::string metadata_file, error; if (!android::gsi::CanBootIntoGsi(&metadata_file, &error)) { LOG(INFO) << "GSI " << error << ", proceeding with normal boot"; return; } auto metadata = android::fs_mgr::ReadFromImageFile(metadata_file.c_str()); if (!metadata) { LOG(ERROR) << "GSI partition layout could not be read"; return; } if (!android::fs_mgr::CreateLogicalPartitions(*metadata.get(), "/dev/block/by-name/userdata")) { LOG(ERROR) << "GSI partition layout could not be instantiated"; return; } if (!android::gsi::MarkSystemAsGsi()) { PLOG(ERROR) << "GSI indicator file could not be written"; return; } // Replace the existing system fstab entry. auto system_partition = std::find_if(fstab_.begin(), fstab_.end(), [](const auto& entry) { return entry.mount_point == "/system"; }); if (system_partition != fstab_.end()) { fstab_.erase(system_partition); } fstab_.emplace_back(BuildGsiSystemFstabEntry()); } bool FirstStageMountVBootV1::GetDmVerityDevices() { std::string verity_loc_device; need_dm_verity_ = false; for (const auto& fstab_entry : fstab_) { // Don't allow verifyatboot in the first stage. if (fstab_entry.fs_mgr_flags.verify_at_boot) { LOG(ERROR) << "Partitions can't be verified at boot"; return false; } // Checks for verified partitions. if (fstab_entry.fs_mgr_flags.verify) { need_dm_verity_ = true; } // Checks if verity metadata is on a separate partition. Note that it is // not partition specific, so there must be only one additional partition // that carries verity state. if (!fstab_entry.verity_loc.empty()) { if (verity_loc_device.empty()) { verity_loc_device = fstab_entry.verity_loc; } else if (verity_loc_device != fstab_entry.verity_loc) { LOG(ERROR) << "More than one verity_loc found: " << verity_loc_device << ", " << fstab_entry.verity_loc; return false; } } } // Includes the partition names of fstab records and verity_loc_device (if any). // Notes that fstab_rec->blk_device has A/B suffix updated by fs_mgr when A/B is used. for (const auto& fstab_entry : fstab_) { if (!fstab_entry.fs_mgr_flags.logical) { required_devices_partition_names_.emplace(basename(fstab_entry.blk_device.c_str())); } } if (!verity_loc_device.empty()) { required_devices_partition_names_.emplace(basename(verity_loc_device.c_str())); } return true; } bool FirstStageMountVBootV1::SetUpDmVerity(FstabEntry* fstab_entry) { if (fstab_entry->fs_mgr_flags.verify) { int ret = fs_mgr_setup_verity(fstab_entry, false /* wait_for_verity_dev */); switch (ret) { case FS_MGR_SETUP_VERITY_SKIPPED: case FS_MGR_SETUP_VERITY_DISABLED: LOG(INFO) << "Verity disabled/skipped for '" << fstab_entry->mount_point << "'"; return true; case FS_MGR_SETUP_VERITY_SUCCESS: // The exact block device name (fstab_rec->blk_device) is changed to // "/dev/block/dm-XX". Needs to create it because ueventd isn't started in init // first stage. return InitMappedDevice(fstab_entry->blk_device); default: return false; } } return true; // Returns true to mount the partition. } // First retrieve any vbmeta partitions from device tree (legacy) then read through the fstab // for any further vbmeta partitions. FirstStageMountVBootV2::FirstStageMountVBootV2(Fstab fstab) : FirstStageMount(std::move(fstab)), avb_handle_(nullptr) { std::string device_tree_vbmeta_parts; read_android_dt_file("vbmeta/parts", &device_tree_vbmeta_parts); for (auto&& partition : Split(device_tree_vbmeta_parts, ",")) { if (!partition.empty()) { vbmeta_partitions_.emplace_back(std::move(partition)); } } for (const auto& entry : fstab_) { if (!entry.vbmeta_partition.empty()) { vbmeta_partitions_.emplace_back(entry.vbmeta_partition); } } if (vbmeta_partitions_.empty()) { LOG(ERROR) << "Failed to read vbmeta partitions."; } } bool FirstStageMountVBootV2::GetDmVerityDevices() { need_dm_verity_ = false; std::set logical_partitions; // fstab_rec->blk_device has A/B suffix. for (const auto& fstab_entry : fstab_) { if (fstab_entry.fs_mgr_flags.avb) { need_dm_verity_ = true; } if (fstab_entry.fs_mgr_flags.logical) { // Don't try to find logical partitions via uevent regeneration. logical_partitions.emplace(basename(fstab_entry.blk_device.c_str())); } else { required_devices_partition_names_.emplace(basename(fstab_entry.blk_device.c_str())); } } // Any partitions needed for verifying the partitions used in first stage mount, e.g. vbmeta // must be provided as vbmeta_partitions. if (need_dm_verity_) { if (vbmeta_partitions_.empty()) { LOG(ERROR) << "Missing vbmeta partitions"; return false; } std::string ab_suffix = fs_mgr_get_slot_suffix(); for (const auto& partition : vbmeta_partitions_) { std::string partition_name = partition + ab_suffix; if (logical_partitions.count(partition_name)) { continue; } // required_devices_partition_names_ is of type std::set so it's not an issue // to emplace a partition twice. e.g., /vendor might be in both places: // - device_tree_vbmeta_parts_ = "vbmeta,boot,system,vendor" // - mount_fstab_recs_: /vendor_a required_devices_partition_names_.emplace(partition_name); } } return true; } bool FirstStageMountVBootV2::SetUpDmVerity(FstabEntry* fstab_entry) { if (fstab_entry->fs_mgr_flags.avb) { if (!InitAvbHandle()) return false; AvbHashtreeResult hashtree_result = avb_handle_->SetUpAvbHashtree(fstab_entry, false /* wait_for_verity_dev */); switch (hashtree_result) { case AvbHashtreeResult::kDisabled: return true; // Returns true to mount the partition. case AvbHashtreeResult::kSuccess: // The exact block device name (fstab_rec->blk_device) is changed to // "/dev/block/dm-XX". Needs to create it because ueventd isn't started in init // first stage. return InitMappedDevice(fstab_entry->blk_device); default: return false; } } return true; // Returns true to mount the partition. } bool FirstStageMountVBootV2::InitAvbHandle() { if (avb_handle_) return true; // Returns true if the handle is already initialized. avb_handle_ = AvbHandle::Open(); if (!avb_handle_) { PLOG(ERROR) << "Failed to open AvbHandle"; return false; } // Sets INIT_AVB_VERSION here for init to set ro.boot.avb_version in the second stage. setenv("INIT_AVB_VERSION", avb_handle_->avb_version().c_str(), 1); return true; } // Public functions // ---------------- // Mounts partitions specified by fstab in device tree. bool DoFirstStageMount() { // Skips first stage mount if we're in recovery mode. if (IsRecoveryMode()) { LOG(INFO) << "First stage mount skipped (recovery mode)"; return true; } std::unique_ptr handle = FirstStageMount::Create(); if (!handle) { LOG(ERROR) << "Failed to create FirstStageMount"; return false; } return handle->DoFirstStageMount(); } void SetInitAvbVersionInRecovery() { if (!IsRecoveryMode()) { LOG(INFO) << "Skipped setting INIT_AVB_VERSION (not in recovery mode)"; return; } auto fstab = ReadFirstStageFstab(); if (!IsDtVbmetaCompatible(fstab)) { LOG(INFO) << "Skipped setting INIT_AVB_VERSION (not vbmeta compatible)"; return; } // Initializes required devices for the subsequent AvbHandle::Open() // to verify AVB metadata on all partitions in the verified chain. // We only set INIT_AVB_VERSION when the AVB verification succeeds, i.e., the // Open() function returns a valid handle. // We don't need to mount partitions here in recovery mode. FirstStageMountVBootV2 avb_first_mount(std::move(fstab)); if (!avb_first_mount.InitDevices()) { LOG(ERROR) << "Failed to init devices for INIT_AVB_VERSION"; return; } AvbUniquePtr avb_handle = AvbHandle::Open(); if (!avb_handle) { PLOG(ERROR) << "Failed to open AvbHandle for INIT_AVB_VERSION"; return; } setenv("INIT_AVB_VERSION", avb_handle->avb_version().c_str(), 1); } } // namespace init } // namespace android