diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp index 9a53fe9af..8bd3b9de0 100644 --- a/fs_mgr/libdm/dm.cpp +++ b/fs_mgr/libdm/dm.cpp @@ -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(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; diff --git a/fs_mgr/libdm/dm_table.cpp b/fs_mgr/libdm/dm_table.cpp index 868286e22..cb6f210ae 100644 --- a/fs_mgr/libdm/dm_table.cpp +++ b/fs_mgr/libdm/dm_table.cpp @@ -22,7 +22,8 @@ namespace android { namespace dm { -bool DmTable::AddTarget(std::unique_ptr&& /* target */) { +bool DmTable::AddTarget(std::unique_ptr&& target) { + targets_.push_back(std::move(target)); return true; } @@ -31,6 +32,14 @@ bool DmTable::RemoveTarget(std::unique_ptr&& /* 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 diff --git a/fs_mgr/libdm/dm_target.cpp b/fs_mgr/libdm/dm_target.cpp index dbe4fede1..593441603 100644 --- a/fs_mgr/libdm/dm_target.cpp +++ b/fs_mgr/libdm/dm_target.cpp @@ -19,6 +19,41 @@ #include #include +#include + 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(&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 diff --git a/fs_mgr/libdm/include/libdm/dm_table.h b/fs_mgr/libdm/include/libdm/dm_table.h index 8a5c62400..5c639bee0 100644 --- a/fs_mgr/libdm/include/libdm/dm_table.h +++ b/fs_mgr/libdm/include/libdm/dm_table.h @@ -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 diff --git a/fs_mgr/libdm/include/libdm/dm_target.h b/fs_mgr/libdm/include/libdm/dm_target.h index 50902f249..31b6a70a7 100644 --- a/fs_mgr/libdm/include/libdm/dm_target.h +++ b/fs_mgr/libdm/include/libdm/dm_target.h @@ -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 diff --git a/fs_mgr/tools/dmctl.cpp b/fs_mgr/tools/dmctl.cpp index 0478867ca..c15173f45 100644 --- a/fs_mgr/tools/dmctl.cpp +++ b/fs_mgr/tools/dmctl.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -30,46 +31,131 @@ #include #include #include +#include #include #include 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 options]" << std::endl; std::cerr << "commands:" << std::endl; - std::cerr << " create [ [-lo ] ]" << std::endl; + std::cerr << " create [-ro] " << std::endl; std::cerr << " delete " << std::endl; std::cerr << " list " << std::endl; std::cerr << " help" << std::endl; + std::cerr << std::endl; + std::cerr << "Target syntax:" << std::endl; + std::cerr << " [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 Next() { + if (!HasArgs(3)) { + std::cerr << "Expected " << 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(start_sector, num_sectors); + } else if (target_type == "linear") { + if (!HasArgs(2)) { + std::cerr << "Expected \"linear\" " << 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(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 [table-args]" << std::endl; + std::cerr << "Usage: dmctl create [-ro] " << 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 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; }