/*
|
* 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 <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 (!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<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() && !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
|