| /* |
| * 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 <inttypes.h> |
| #include <linux/dm-ioctl.h> |
| #include <string.h> |
| #include <sys/ioctl.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| |
| #include <sstream> |
| |
| #include <android-base/file.h> |
| #include <android-base/logging.h> |
| #include <android-base/stringprintf.h> |
| #include <android-base/strings.h> |
| #include <android-base/unique_fd.h> |
| #include <fs_mgr/file_wait.h> |
| #include <liblp/reader.h> |
| |
| #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 CreateLogicalPartitionParams& params, |
| const LpMetadataBlockDevice& block_device, |
| const std::string& super_device, std::string* result) { |
| // 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.) |
| std::string name = GetBlockDevicePartitionName(block_device); |
| if (android::base::StartsWith(name, "dm-")) { |
| // Device-mapper nodes are not normally allowed in LpMetadata, since |
| // they are not consistent across reboots. However for the purposes of |
| // testing it's useful to handle them. For example when running DSUs, |
| // userdata is a device-mapper device, and some stacking will result |
| // when using libfiemap. |
| *result = "/dev/block/" + name; |
| return true; |
| } |
| |
| auto opener = params.partition_opener; |
| std::string dev_string = opener->GetDeviceString(name); |
| if (GetMetadataSuperBlockDevice(*params.metadata) == &block_device) { |
| dev_string = opener->GetDeviceString(super_device); |
| } |
| |
| // Note: device-mapper will not accept symlinks, so we must use realpath |
| // here. If the device string is a major:minor sequence, we don't need to |
| // to call Realpath (it would not work anyway). |
| if (android::base::StartsWith(dev_string, "/")) { |
| if (!android::base::Realpath(dev_string, result)) { |
| PERROR << "realpath: " << dev_string; |
| return false; |
| } |
| } else { |
| *result = dev_string; |
| } |
| return true; |
| } |
| |
| bool CreateDmTableInternal(const CreateLogicalPartitionParams& params, DmTable* table) { |
| const auto& super_device = params.block_device; |
| |
| uint64_t sector = 0; |
| for (size_t i = 0; i < params.partition->num_extents; i++) { |
| const auto& extent = params.metadata->extents[params.partition->first_extent_index + i]; |
| std::unique_ptr<DmTarget> target; |
| switch (extent.target_type) { |
| case LP_TARGET_TYPE_ZERO: |
| target = std::make_unique<DmTargetZero>(sector, extent.num_sectors); |
| break; |
| case LP_TARGET_TYPE_LINEAR: { |
| const auto& block_device = params.metadata->block_devices[extent.target_source]; |
| std::string dev_string; |
| if (!GetPhysicalPartitionDevicePath(params, block_device, super_device, |
| &dev_string)) { |
| LOG(ERROR) << "Unable to complete device-mapper table, unknown block device"; |
| return false; |
| } |
| target = std::make_unique<DmTargetLinear>(sector, extent.num_sectors, dev_string, |
| 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 (params.partition->attributes & LP_PARTITION_ATTR_READONLY) { |
| table->set_readonly(true); |
| } |
| if (params.force_writable) { |
| table->set_readonly(false); |
| } |
| return true; |
| } |
| |
| bool CreateDmTable(CreateLogicalPartitionParams params, DmTable* table) { |
| CreateLogicalPartitionParams::OwnedData owned_data; |
| if (!params.InitDefaults(&owned_data)) return false; |
| return CreateDmTableInternal(params, table); |
| } |
| |
| 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<LpMetadata> 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) { |
| CreateLogicalPartitionParams params = { |
| .block_device = super_device, |
| .metadata = &metadata, |
| }; |
| for (const auto& partition : metadata.partitions) { |
| if (!partition.num_extents) { |
| LINFO << "Skipping zero-length logical partition: " << GetPartitionName(partition); |
| continue; |
| } |
| if (partition.attributes & LP_PARTITION_ATTR_DISABLED) { |
| LINFO << "Skipping disabled partition: " << GetPartitionName(partition); |
| continue; |
| } |
| |
| params.partition = &partition; |
| |
| std::string ignore_path; |
| if (!CreateLogicalPartition(params, &ignore_path)) { |
| LERROR << "Could not create logical partition: " << GetPartitionName(partition); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| bool CreateLogicalPartitionParams::InitDefaults(CreateLogicalPartitionParams::OwnedData* owned) { |
| if (block_device.empty()) { |
| LOG(ERROR) << "block_device is required for CreateLogicalPartition"; |
| return false; |
| } |
| |
| if (!partition_opener) { |
| owned->partition_opener = std::make_unique<PartitionOpener>(); |
| partition_opener = owned->partition_opener.get(); |
| } |
| |
| // Read metadata if needed. |
| if (!metadata) { |
| if (!metadata_slot) { |
| LOG(ERROR) << "Either metadata or a metadata slot must be specified."; |
| return false; |
| } |
| auto slot = *metadata_slot; |
| if (owned->metadata = ReadMetadata(*partition_opener, block_device, slot); |
| !owned->metadata) { |
| LOG(ERROR) << "Could not read partition table for: " << block_device; |
| return false; |
| } |
| metadata = owned->metadata.get(); |
| } |
| |
| // Find the partition by name if needed. |
| if (!partition) { |
| for (const auto& metadata_partition : metadata->partitions) { |
| if (android::fs_mgr::GetPartitionName(metadata_partition) == partition_name) { |
| partition = &metadata_partition; |
| break; |
| } |
| } |
| } |
| if (!partition) { |
| LERROR << "Could not find any partition with name: " << partition_name; |
| return false; |
| } |
| if (partition_name.empty()) { |
| partition_name = android::fs_mgr::GetPartitionName(*partition); |
| } else if (partition_name != android::fs_mgr::GetPartitionName(*partition)) { |
| LERROR << "Inconsistent partition_name " << partition_name << " with partition " |
| << android::fs_mgr::GetPartitionName(*partition); |
| return false; |
| } |
| |
| if (device_name.empty()) { |
| device_name = partition_name; |
| } |
| |
| return true; |
| } |
| |
| bool CreateLogicalPartition(CreateLogicalPartitionParams params, std::string* path) { |
| CreateLogicalPartitionParams::OwnedData owned_data; |
| if (!params.InitDefaults(&owned_data)) return false; |
| |
| DmTable table; |
| if (!CreateDmTableInternal(params, &table)) { |
| return false; |
| } |
| |
| DeviceMapper& dm = DeviceMapper::Instance(); |
| if (!dm.CreateDevice(params.device_name, table, path, params.timeout_ms)) { |
| return false; |
| } |
| LINFO << "Created logical partition " << params.device_name << " on device " << *path; |
| return true; |
| } |
| |
| std::string CreateLogicalPartitionParams::GetDeviceName() const { |
| if (!device_name.empty()) return device_name; |
| return GetPartitionName(); |
| } |
| |
| std::string CreateLogicalPartitionParams::GetPartitionName() const { |
| if (!partition_name.empty()) return partition_name; |
| if (partition) return android::fs_mgr::GetPartitionName(*partition); |
| return "<unknown partition>"; |
| } |
| |
| bool UnmapDevice(const std::string& name) { |
| DeviceMapper& dm = DeviceMapper::Instance(); |
| if (!dm.DeleteDevice(name)) { |
| return false; |
| } |
| return true; |
| } |
| |
| bool DestroyLogicalPartition(const std::string& name) { |
| if (!UnmapDevice(name)) { |
| return false; |
| } |
| LINFO << "Unmapped logical partition " << name; |
| return true; |
| } |
| |
| } // namespace fs_mgr |
| } // namespace android |