libdm: Implement zero and linear targets.
This change implements DmTargetZero and DmTargetLinear, and integrates them into dmctl. It also implements DmTarget and DmTable serialization. Example dmctl invocation: dmctl create my-device -ro \ linear 0 800 /dev/block/by-name/system 0 \ zero 800 1200 \ linear 1200 1500 /dev/block/by-name/system 1200 Bug: 110035986 Test: libdm_test gtest Change-Id: I7f945c1d9e23cfb78239c23a1aad88e8aef4972b
This commit is contained in:
parent
c7def6849a
commit
bac58aeecf
|
@ -114,6 +114,9 @@ bool DeviceMapper::LoadTableAndActivate(const std::string& name, const DmTable&
|
|||
io->data_size = ioctl_buffer.size();
|
||||
io->data_start = sizeof(struct dm_ioctl);
|
||||
io->target_count = static_cast<uint32_t>(table.num_targets());
|
||||
if (table.readonly()) {
|
||||
io->flags |= DM_READONLY_FLAG;
|
||||
}
|
||||
if (ioctl(fd_, DM_TABLE_LOAD, io)) {
|
||||
PLOG(ERROR) << "DM_TABLE_LOAD failed";
|
||||
return false;
|
||||
|
|
|
@ -22,7 +22,8 @@
|
|||
namespace android {
|
||||
namespace dm {
|
||||
|
||||
bool DmTable::AddTarget(std::unique_ptr<DmTarget>&& /* target */) {
|
||||
bool DmTable::AddTarget(std::unique_ptr<DmTarget>&& target) {
|
||||
targets_.push_back(std::move(target));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -31,6 +32,14 @@ bool DmTable::RemoveTarget(std::unique_ptr<DmTarget>&& /* target */) {
|
|||
}
|
||||
|
||||
bool DmTable::valid() const {
|
||||
if (targets_.empty()) {
|
||||
LOG(ERROR) << "Device-mapper table must have at least one target.";
|
||||
return "";
|
||||
}
|
||||
if (targets_[0]->start() != 0) {
|
||||
LOG(ERROR) << "Device-mapper table must start at logical sector 0.";
|
||||
return "";
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -38,14 +47,22 @@ uint64_t DmTable::num_sectors() const {
|
|||
return valid() ? num_sectors_ : 0;
|
||||
}
|
||||
|
||||
// Returns a string represnetation of the table that is ready to be passed
|
||||
// down to the kernel for loading
|
||||
// Returns a string representation of the table that is ready to be passed
|
||||
// down to the kernel for loading.
|
||||
//
|
||||
// Implementation must verify there are no gaps in the table, table starts
|
||||
// with sector == 0, and iterate over each target to get its table
|
||||
// serialized.
|
||||
std::string DmTable::Serialize() const {
|
||||
return "";
|
||||
if (!valid()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string table;
|
||||
for (const auto& target : targets_) {
|
||||
table += target->Serialize();
|
||||
}
|
||||
return table;
|
||||
}
|
||||
|
||||
} // namespace dm
|
||||
|
|
|
@ -19,6 +19,41 @@
|
|||
#include <android-base/logging.h>
|
||||
#include <android-base/macros.h>
|
||||
|
||||
#include <libdm/dm.h>
|
||||
|
||||
namespace android {
|
||||
namespace dm {} // namespace dm
|
||||
namespace dm {
|
||||
|
||||
std::string DmTarget::Serialize() const {
|
||||
// Create a string containing a dm_target_spec, parameter data, and an
|
||||
// explicit null terminator.
|
||||
std::string data(sizeof(dm_target_spec), '\0');
|
||||
data += GetParameterString();
|
||||
data.push_back('\0');
|
||||
|
||||
// The kernel expects each target to be 8-byte aligned.
|
||||
size_t padding = DM_ALIGN(data.size()) - data.size();
|
||||
for (size_t i = 0; i < padding; i++) {
|
||||
data.push_back('\0');
|
||||
}
|
||||
|
||||
// Finally fill in the dm_target_spec.
|
||||
struct dm_target_spec* spec = reinterpret_cast<struct dm_target_spec*>(&data[0]);
|
||||
spec->sector_start = start();
|
||||
spec->length = size();
|
||||
strlcpy(spec->target_type, name().c_str(), sizeof(spec->target_type));
|
||||
spec->next = (uint32_t)data.size();
|
||||
return data;
|
||||
}
|
||||
|
||||
std::string DmTargetZero::GetParameterString() const {
|
||||
// The zero target type has no additional parameters.
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string DmTargetLinear::GetParameterString() const {
|
||||
return block_device_ + " " + std::to_string(physical_sector_);
|
||||
}
|
||||
|
||||
} // namespace dm
|
||||
} // namespace android
|
||||
|
|
|
@ -30,7 +30,7 @@ namespace dm {
|
|||
|
||||
class DmTable {
|
||||
public:
|
||||
DmTable() : num_sectors_(0){};
|
||||
DmTable() : num_sectors_(0), readonly_(false) {}
|
||||
|
||||
// Adds a target to the device mapper table for a range specified in the target object.
|
||||
// The function will return 'true' if the target was successfully added and doesn't overlap with
|
||||
|
@ -59,6 +59,9 @@ class DmTable {
|
|||
// as part of the DM_TABLE_LOAD ioctl.
|
||||
std::string Serialize() const;
|
||||
|
||||
void set_readonly(bool readonly) { readonly_ = readonly; }
|
||||
bool readonly() const { return readonly_; }
|
||||
|
||||
~DmTable() = default;
|
||||
|
||||
private:
|
||||
|
@ -70,6 +73,9 @@ class DmTable {
|
|||
// Total size in terms of # of sectors, as calculated by looking at the last and the first
|
||||
// target in 'target_'.
|
||||
uint64_t num_sectors_;
|
||||
|
||||
// True if the device should be read-only; false otherwise.
|
||||
bool readonly_;
|
||||
};
|
||||
|
||||
} // namespace dm
|
||||
|
|
|
@ -55,7 +55,7 @@ class DmTarget {
|
|||
virtual ~DmTarget() = default;
|
||||
|
||||
// Returns name of the target.
|
||||
virtual const std::string& name() const = 0;
|
||||
virtual std::string name() const = 0;
|
||||
|
||||
// Return the first logical sector represented by this target.
|
||||
uint64_t start() const { return start_; }
|
||||
|
@ -67,7 +67,12 @@ class DmTarget {
|
|||
// Function that converts this object to a string of arguments that can
|
||||
// be passed to the kernel for adding this target in a table. Each target (e.g. verity, linear)
|
||||
// must implement this, for it to be used on a device.
|
||||
virtual std::string Serialize() const = 0;
|
||||
std::string Serialize() const;
|
||||
|
||||
protected:
|
||||
// Get the parameter string that is passed to the end of the dm_target_spec
|
||||
// for this target type.
|
||||
virtual std::string GetParameterString() const = 0;
|
||||
|
||||
private:
|
||||
// logical sector number start and total length (in terms of 512-byte sectors) represented
|
||||
|
@ -75,6 +80,28 @@ class DmTarget {
|
|||
uint64_t start_, length_;
|
||||
};
|
||||
|
||||
class DmTargetZero final : public DmTarget {
|
||||
public:
|
||||
DmTargetZero(uint64_t start, uint64_t length) : DmTarget(start, length) {}
|
||||
|
||||
std::string name() const override { return "zero"; }
|
||||
std::string GetParameterString() const override;
|
||||
};
|
||||
|
||||
class DmTargetLinear final : public DmTarget {
|
||||
public:
|
||||
DmTargetLinear(uint64_t start, uint64_t length, const std::string& block_device,
|
||||
uint64_t physical_sector)
|
||||
: DmTarget(start, length), block_device_(block_device), physical_sector_(physical_sector) {}
|
||||
|
||||
std::string name() const override { return "linear"; }
|
||||
std::string GetParameterString() const override;
|
||||
|
||||
private:
|
||||
std::string block_device_;
|
||||
uint64_t physical_sector_;
|
||||
};
|
||||
|
||||
} // namespace dm
|
||||
} // namespace android
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <android-base/parseint.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <libdm/dm.h>
|
||||
|
||||
|
@ -30,46 +31,131 @@
|
|||
#include <ios>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using DeviceMapper = ::android::dm::DeviceMapper;
|
||||
using DmTable = ::android::dm::DmTable;
|
||||
using DmTarget = ::android::dm::DmTarget;
|
||||
using DmTargetLinear = ::android::dm::DmTargetLinear;
|
||||
using DmTargetZero = ::android::dm::DmTargetZero;
|
||||
using DmTargetTypeInfo = ::android::dm::DmTargetTypeInfo;
|
||||
using DmBlockDevice = ::android::dm::DeviceMapper::DmBlockDevice;
|
||||
|
||||
static int Usage(void) {
|
||||
std::cerr << "usage: dmctl <command> [command options]" << std::endl;
|
||||
std::cerr << "commands:" << std::endl;
|
||||
std::cerr << " create <dm-name> [<dm-target> [-lo <filename>] <dm-target-args>]" << std::endl;
|
||||
std::cerr << " create <dm-name> [-ro] <targets...>" << std::endl;
|
||||
std::cerr << " delete <dm-name>" << std::endl;
|
||||
std::cerr << " list <devices | targets>" << std::endl;
|
||||
std::cerr << " help" << std::endl;
|
||||
std::cerr << std::endl;
|
||||
std::cerr << "Target syntax:" << std::endl;
|
||||
std::cerr << " <target_type> <start_sector> <num_sectors> [target_data]" << std::endl;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
class TargetParser final {
|
||||
public:
|
||||
TargetParser(int argc, char** argv) : arg_index_(0), argc_(argc), argv_(argv) {}
|
||||
|
||||
bool More() const { return arg_index_ < argc_; }
|
||||
std::unique_ptr<DmTarget> Next() {
|
||||
if (!HasArgs(3)) {
|
||||
std::cerr << "Expected <target_type> <start_sector> <num_sectors>" << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string target_type = NextArg();
|
||||
uint64_t start_sector, num_sectors;
|
||||
if (!android::base::ParseUint(NextArg(), &start_sector)) {
|
||||
std::cerr << "Expected start sector, got: " << PreviousArg() << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
if (!android::base::ParseUint(NextArg(), &num_sectors) || !num_sectors) {
|
||||
std::cerr << "Expected non-zero sector count, got: " << PreviousArg() << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (target_type == "zero") {
|
||||
return std::make_unique<DmTargetZero>(start_sector, num_sectors);
|
||||
} else if (target_type == "linear") {
|
||||
if (!HasArgs(2)) {
|
||||
std::cerr << "Expected \"linear\" <block_device> <sector>" << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string block_device = NextArg();
|
||||
uint64_t physical_sector;
|
||||
if (!android::base::ParseUint(NextArg(), &physical_sector)) {
|
||||
std::cerr << "Expected sector, got: \"" << PreviousArg() << "\"" << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
return std::make_unique<DmTargetLinear>(start_sector, num_sectors, block_device,
|
||||
physical_sector);
|
||||
} else {
|
||||
std::cerr << "Unrecognized target type: " << target_type << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
bool HasArgs(int count) { return arg_index_ + count <= argc_; }
|
||||
const char* NextArg() {
|
||||
CHECK(arg_index_ < argc_);
|
||||
return argv_[arg_index_++];
|
||||
}
|
||||
const char* PreviousArg() {
|
||||
CHECK(arg_index_ >= 0);
|
||||
return argv_[arg_index_ - 1];
|
||||
}
|
||||
|
||||
private:
|
||||
int arg_index_;
|
||||
int argc_;
|
||||
char** argv_;
|
||||
};
|
||||
|
||||
static int DmCreateCmdHandler(int argc, char** argv) {
|
||||
if (argc < 1) {
|
||||
std::cerr << "Usage: dmctl create <name> [table-args]" << std::endl;
|
||||
std::cerr << "Usage: dmctl create <dm-name> [-ro] <targets...>" << std::endl;
|
||||
return -EINVAL;
|
||||
}
|
||||
std::string name = argv[0];
|
||||
|
||||
// Parse extended options first.
|
||||
DmTable table;
|
||||
int arg_index = 1;
|
||||
while (arg_index < argc && argv[arg_index][0] == '-') {
|
||||
if (strcmp(argv[arg_index], "-ro") == 0) {
|
||||
table.set_readonly(true);
|
||||
} else {
|
||||
std::cerr << "Unrecognized option: " << argv[arg_index] << std::endl;
|
||||
return -EINVAL;
|
||||
}
|
||||
arg_index++;
|
||||
}
|
||||
|
||||
// Parse everything else as target information.
|
||||
TargetParser parser(argc - arg_index, argv + arg_index);
|
||||
while (parser.More()) {
|
||||
std::unique_ptr<DmTarget> target = parser.Next();
|
||||
if (!target || !table.AddTarget(std::move(target))) {
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (table.num_targets() == 0) {
|
||||
std::cerr << "Must define at least one target." << std::endl;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
DmTable table;
|
||||
|
||||
std::string name = argv[0];
|
||||
DeviceMapper& dm = DeviceMapper::Instance();
|
||||
if (!dm.CreateDevice(name, table)) {
|
||||
std::cerr << "Failed to create device-mapper device with name: " << name << std::endl;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
// if we also have target specified
|
||||
if (argc > 1) {
|
||||
// fall through for now. This will eventually create a DmTarget() based on the target name
|
||||
// passing it the table that is specified at the command line
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue