blob: 9046132f817a57309b62e004210441aee48d688b [file] [log] [blame]
/*
* 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