/* * Copyright (C) 2018 The Android Open Source Project * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "fs_mgr_dm_linear.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fs_mgr_priv.h" namespace android { namespace fs_mgr { using DeviceMapper = android::dm::DeviceMapper; using DmTable = android::dm::DmTable; using DmTarget = android::dm::DmTarget; using DmTargetZero = android::dm::DmTargetZero; using DmTargetLinear = android::dm::DmTargetLinear; static bool GetPhysicalPartitionDevicePath(const LpMetadata& metadata, const LpMetadataBlockDevice& block_device, const std::string& super_device, std::string* result) { // Note: device-mapper will not accept symlinks, so we must use realpath // here. std::string name = GetBlockDevicePartitionName(block_device); std::string path = "/dev/block/by-name/" + name; // If the super device is the source of this block device's metadata, // make sure we use the correct super device (and not just "super", // which might not exist.) if (GetMetadataSuperBlockDevice(metadata) == &block_device) { path = super_device; } if (!android::base::Realpath(path, result)) { PERROR << "realpath: " << path; return false; } return true; } static bool CreateDmTable(const LpMetadata& metadata, const LpMetadataPartition& partition, const std::string& super_device, DmTable* table) { uint64_t sector = 0; for (size_t i = 0; i < partition.num_extents; i++) { const auto& extent = metadata.extents[partition.first_extent_index + i]; std::unique_ptr target; switch (extent.target_type) { case LP_TARGET_TYPE_ZERO: target = std::make_unique(sector, extent.num_sectors); break; case LP_TARGET_TYPE_LINEAR: { const auto& block_device = metadata.block_devices[extent.target_source]; std::string path; if (!GetPhysicalPartitionDevicePath(metadata, block_device, super_device, &path)) { LOG(ERROR) << "Unable to complete device-mapper table, unknown block device"; return false; } target = std::make_unique(sector, extent.num_sectors, path, extent.target_data); break; } default: LOG(ERROR) << "Unknown target type in metadata: " << extent.target_type; return false; } if (!table->AddTarget(std::move(target))) { return false; } sector += extent.num_sectors; } if (partition.attributes & LP_PARTITION_ATTR_READONLY) { table->set_readonly(true); } return true; } static bool CreateLogicalPartition(const LpMetadata& metadata, const LpMetadataPartition& partition, bool force_writable, const std::chrono::milliseconds& timeout_ms, const std::string& super_device, std::string* path) { DeviceMapper& dm = DeviceMapper::Instance(); DmTable table; if (!CreateDmTable(metadata, partition, super_device, &table)) { return false; } if (force_writable) { table.set_readonly(false); } std::string name = GetPartitionName(partition); if (!dm.CreateDevice(name, table)) { return false; } if (!dm.GetDmDevicePathByName(name, path)) { return false; } if (timeout_ms > std::chrono::milliseconds::zero()) { if (!fs_mgr_wait_for_file(*path, timeout_ms, FileWaitMode::Exists)) { DestroyLogicalPartition(name, {}); LERROR << "Timed out waiting for device path: " << *path; return false; } } LINFO << "Created logical partition " << name << " on device " << *path; return true; } bool CreateLogicalPartitions(const std::string& block_device) { uint32_t slot = SlotNumberForSlotSuffix(fs_mgr_get_slot_suffix()); auto metadata = ReadMetadata(block_device.c_str(), slot); if (!metadata) { LOG(ERROR) << "Could not read partition table."; return true; } return CreateLogicalPartitions(*metadata.get(), block_device); } std::unique_ptr ReadCurrentMetadata(const std::string& block_device) { uint32_t slot = SlotNumberForSlotSuffix(fs_mgr_get_slot_suffix()); return ReadMetadata(block_device.c_str(), slot); } bool CreateLogicalPartitions(const LpMetadata& metadata, const std::string& super_device) { for (const auto& partition : metadata.partitions) { if (!partition.num_extents) { LINFO << "Skipping zero-length logical partition: " << GetPartitionName(partition); continue; } std::string path; if (!CreateLogicalPartition(metadata, partition, false, {}, super_device, &path)) { LERROR << "Could not create logical partition: " << GetPartitionName(partition); return false; } } return true; } bool CreateLogicalPartition(const std::string& block_device, const LpMetadata& metadata, const std::string& partition_name, bool force_writable, const std::chrono::milliseconds& timeout_ms, std::string* path) { for (const auto& partition : metadata.partitions) { if (GetPartitionName(partition) == partition_name) { return CreateLogicalPartition(metadata, partition, force_writable, timeout_ms, block_device, path); } } LERROR << "Could not find any partition with name: " << partition_name; return false; } bool CreateLogicalPartition(const std::string& block_device, uint32_t metadata_slot, const std::string& partition_name, bool force_writable, const std::chrono::milliseconds& timeout_ms, std::string* path) { auto metadata = ReadMetadata(block_device.c_str(), metadata_slot); if (!metadata) { LOG(ERROR) << "Could not read partition table."; return true; } return CreateLogicalPartition(block_device, *metadata.get(), partition_name, force_writable, timeout_ms, path); } bool UnmapDevice(const std::string& name, const std::chrono::milliseconds& timeout_ms) { DeviceMapper& dm = DeviceMapper::Instance(); std::string path; if (timeout_ms > std::chrono::milliseconds::zero()) { dm.GetDmDevicePathByName(name, &path); } if (!dm.DeleteDevice(name)) { return false; } if (!path.empty() && !fs_mgr_wait_for_file(path, timeout_ms, FileWaitMode::DoesNotExist)) { LERROR << "Timed out waiting for device path to unlink: " << path; return false; } return true; } bool DestroyLogicalPartition(const std::string& name, const std::chrono::milliseconds& timeout_ms) { if (!UnmapDevice(name, timeout_ms)) { return false; } LINFO << "Unmapped logical partition " << name; return true; } } // namespace fs_mgr } // namespace android