blob: 1f21a71762388b238fd4c6d943d6f452e5cd1578 [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 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<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 = 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<DmTargetLinear>(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 (!WaitForFile(*path, timeout_ms)) {
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<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) {
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() && !WaitForFileDeleted(path, timeout_ms)) {
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