From 59f04ee74c9cd6b0568029e6d895b504b438cefc Mon Sep 17 00:00:00 2001 From: Sandeep Patil Date: Wed, 30 May 2018 13:46:55 -0700 Subject: [PATCH 1/3] fs_mgr: device mapper: Add libdm and 'dmctl' tool to use it. libdm is small static library that is intended to be the one source to control device mapper. It is intended to have APIs to create, control and destroy device mapper targets. Eventually, all fs_mgr implementation that talks to device mapper will be moved to using libdm APIs. Added 'dmctl', a command line tool that lists the registered device mapper targets and their corresponding version. The tool will continue to get new features to exemplify new APIs implemented in libdm. The library is intentionally static and folds into libfs_mgr. All clients must link to libfs_mgr as a result. Test: dmctl list Bug: 110035986 Change-Id: I71e7146073a74e7523524bf3b20d0be6d06f9fad Signed-off-by: Sandeep Patil --- fs_mgr/Android.bp | 3 + fs_mgr/libdm/Android.bp | 37 ++++++++ fs_mgr/libdm/dm.cpp | 135 ++++++++++++++++++++++++++++++ fs_mgr/libdm/dm_table.cpp | 55 ++++++++++++ fs_mgr/libdm/dm_target.cpp | 29 +++++++ fs_mgr/libdm/include/dm.h | 108 ++++++++++++++++++++++++ fs_mgr/libdm/include/dm_table.h | 75 +++++++++++++++++ fs_mgr/libdm/include/dm_target.h | 77 +++++++++++++++++ fs_mgr/tools/Android.bp | 31 +++++++ fs_mgr/tools/dmctl.cpp | 139 +++++++++++++++++++++++++++++++ 10 files changed, 689 insertions(+) create mode 100644 fs_mgr/libdm/Android.bp create mode 100644 fs_mgr/libdm/dm.cpp create mode 100644 fs_mgr/libdm/dm_table.cpp create mode 100644 fs_mgr/libdm/dm_target.cpp create mode 100644 fs_mgr/libdm/include/dm.h create mode 100644 fs_mgr/libdm/include/dm_table.h create mode 100644 fs_mgr/libdm/include/dm_target.h create mode 100644 fs_mgr/tools/Android.bp create mode 100644 fs_mgr/tools/dmctl.cpp diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp index bc3b04b02..923442f90 100644 --- a/fs_mgr/Android.bp +++ b/fs_mgr/Android.bp @@ -56,12 +56,15 @@ cc_library_static { "libselinux", "libavb", "libfstab", + "libdm", ], export_static_lib_headers: [ "libfstab", + "libdm", ], whole_static_libs: [ "liblogwrap", + "libdm", "libfstab", ], cppflags: [ diff --git a/fs_mgr/libdm/Android.bp b/fs_mgr/libdm/Android.bp new file mode 100644 index 000000000..672d401cd --- /dev/null +++ b/fs_mgr/libdm/Android.bp @@ -0,0 +1,37 @@ +// +// Copyright (C) 2018 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +cc_library_static { + name: "libdm", + recovery_available: true, + + export_include_dirs: ["include"], + cflags: [ + // TODO(b/110035986): Allows us to create a skeleton of required classes + "-Wno-unused-private-field", + ], + + srcs: [ + "dm_table.cpp", + "dm_target.cpp", + "dm.cpp" + ], + + header_libs: [ + "libbase_headers", + "liblog_headers", + ], +} diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp new file mode 100644 index 000000000..9a2910e2c --- /dev/null +++ b/fs_mgr/libdm/dm.cpp @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "dm.h" + +namespace android { +namespace dm { + +DeviceMapper& DeviceMapper::Instance() { + static DeviceMapper instance; + return instance; +} +// Creates a new device mapper device +bool DeviceMapper::CreateDevice(const std::string& /* name */) { + // Creates a new device mapper device with the name passed in + return false; +} + +bool DeviceMapper::DeleteDevice(const std::string& /* name */) { + // Destroy device here first + return false; +} + +const std::unique_ptr DeviceMapper::table(const std::string& /* name */) const { + // TODO(b/110035986): Return the table, as read from the kernel instead + return nullptr; +} + +DmDeviceState DeviceMapper::state(const std::string& /* name */) const { + // TODO(b/110035986): Return the state, as read from the kernel instead + return DmDeviceState::INVALID; +} + +bool DeviceMapper::LoadTableAndActivate(const std::string& /* name */, const DmTable& /* table */) { + return false; +} + +// Reads all the available device mapper targets and their corresponding +// versions from the kernel and returns in a vector +bool DeviceMapper::GetAvailableTargets(std::vector* targets) { + targets->clear(); + + // calculate the space needed to read a maximum of kMaxPossibleDmTargets + uint32_t payload_size = sizeof(struct dm_target_versions); + payload_size += DM_MAX_TYPE_NAME; + // device mapper wants every target spec to be aligned at 8-byte boundary + payload_size = DM_ALIGN(payload_size); + payload_size *= kMaxPossibleDmTargets; + + uint32_t data_size = sizeof(struct dm_ioctl) + payload_size; + auto buffer = std::unique_ptr(calloc(1, data_size), free); + if (buffer == nullptr) { + LOG(ERROR) << "failed to allocate memory"; + return false; + } + + struct dm_ioctl* io = reinterpret_cast(buffer.get()); + io->data_start = sizeof(*io); + io->data_size = data_size; + io->version[0] = 4; + io->version[1] = 0; + io->version[2] = 0; + + if (ioctl(fd_, DM_LIST_VERSIONS, io)) { + PLOG(ERROR) << "Failed to get DM_LIST_VERSIONS from kernel"; + return false; + } + + // If the provided buffer wasn't enough to list all targets, note that + // any data beyond sizeof(*io) must not be read in this case + if (io->flags & DM_BUFFER_FULL_FLAG) { + LOG(INFO) << data_size << " is not enough memory to list all dm targets"; + return false; + } + + // if there are no targets registered, return success with empty vector + if (io->data_size == sizeof(*io)) { + return true; + } + + // Parse each target and list the name and version + // TODO(b/110035986): Templatize this + uint32_t next = sizeof(*io); + data_size = io->data_size - next; + struct dm_target_versions* vers = + reinterpret_cast(static_cast(buffer.get()) + next); + while (next && data_size) { + targets->emplace_back((vers)); + if (vers->next == 0) { + break; + } + next += vers->next; + data_size -= vers->next; + vers = reinterpret_cast(static_cast(buffer.get()) + next); + } + + return true; +} + +// Accepts a device mapper device name (like system_a, vendor_b etc) and +// returns the path to it's device node (or symlink to the device node) +std::string DeviceMapper::GetDmDevicePathByName(const std::string& /* name */) { + return ""; +} + +} // namespace dm +} // namespace android diff --git a/fs_mgr/libdm/dm_table.cpp b/fs_mgr/libdm/dm_table.cpp new file mode 100644 index 000000000..14b393297 --- /dev/null +++ b/fs_mgr/libdm/dm_table.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include + +#include "dm_table.h" + +namespace android { +namespace dm { + +bool DmTable::AddTarget(std::unique_ptr&& /* target */) { + return true; +} + +bool DmTable::RemoveTarget(std::unique_ptr&& /* target */) { + return true; +} + +bool DmTable::valid() const { + return true; +} + +uint64_t DmTable::size() const { + return valid() ? size_ : 0; +} + +// Returns a string represnetation 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 ""; +} + +} // namespace dm +} // namespace android diff --git a/fs_mgr/libdm/dm_target.cpp b/fs_mgr/libdm/dm_target.cpp new file mode 100644 index 000000000..8bcd5262a --- /dev/null +++ b/fs_mgr/libdm/dm_target.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include + +#include +#include + +#include "dm_target.h" + +namespace android { +namespace dm {} // namespace dm +} // namespace android diff --git a/fs_mgr/libdm/include/dm.h b/fs_mgr/libdm/include/dm.h new file mode 100644 index 000000000..0cd0149bc --- /dev/null +++ b/fs_mgr/libdm/include/dm.h @@ -0,0 +1,108 @@ +/* + * Copyright 2018 Google, Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _LIBDM_DM_H_ +#define _LIBDM_DM_H_ + +#include +#include +#include + +#include + +#include + +#include + +#define DM_ALIGN_MASK (7) +#define DM_ALIGN(x) ((x + DM_ALIGN_MASK) & ~DM_ALIGN_MASK) + +namespace android { +namespace dm { + +enum class DmDeviceState { INVALID, SUSPENDED, ACTIVE }; + +class DeviceMapper final { + public: + // Creates a device mapper device with given name. + // Return 'true' on success and 'false' on failure to + // create OR if a device mapper device with the same name already + // exists. + // TODO(b/110035986): Make this method private and to be only + // called through LoadTableAndActivate() below. + bool CreateDevice(const std::string& name); + + // Removes a device mapper device with the given name. + // Returns 'true' on success, false otherwise. + bool DeleteDevice(const std::string& name); + + // Reads the device mapper table from the device with given anme and + // returns it in a DmTable object. + const std::unique_ptr table(const std::string& name) const; + + // Returns the current state of the underlying device mapper device + // with given name. + // One of INVALID, SUSPENDED or ACTIVE. + DmDeviceState state(const std::string& name) const; + + // Loads the device mapper table from parameter into the underlying + // device mapper device with given name and activate / resumes the device in the process. + // If a device mapper device with the 'name', doesn't exist, it will be created. + // Returns 'true' on success, false otherwise. + bool LoadTableAndActivate(const std::string& name, const DmTable& table); + + // Returns true if a list of available device mapper targets registered in the kernel was + // successfully read and stored in 'targets'. Returns 'false' otherwise. + bool GetAvailableTargets(std::vector* targets); + + // Returns the path to the device mapper device node in '/dev' corresponding to + // 'name'. + std::string GetDmDevicePathByName(const std::string& name); + + // The only way to create a DeviceMapper object. + static DeviceMapper& Instance(); + + ~DeviceMapper() { + if (fd_ != -1) { + ::close(fd_); + } + } + + private: + // Maximum possible device mapper targets registered in the kernel. + // This is only used to read the list of targets from kernel so we allocate + // a finite amount of memory. This limit is in no way enforced by the kernel. + static constexpr uint32_t kMaxPossibleDmTargets = 256; + + DeviceMapper() : fd_(-1) { + fd_ = TEMP_FAILURE_RETRY(open("/dev/device-mapper", O_RDWR | O_CLOEXEC)); + if (fd_ < 0) { + PLOG(ERROR) << "Failed to open device-mapper"; + } + } + + int fd_; + // Non-copyable & Non-movable + DeviceMapper(const DeviceMapper&) = delete; + DeviceMapper& operator=(const DeviceMapper&) = delete; + DeviceMapper& operator=(DeviceMapper&&) = delete; + DeviceMapper(DeviceMapper&&) = delete; +}; + +} // namespace dm +} // namespace android + +#endif /* _LIBDM_DM_H_ */ diff --git a/fs_mgr/libdm/include/dm_table.h b/fs_mgr/libdm/include/dm_table.h new file mode 100644 index 000000000..0b1685db0 --- /dev/null +++ b/fs_mgr/libdm/include/dm_table.h @@ -0,0 +1,75 @@ +/* + * Copyright 2018 Google, Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _LIBDM_DMTABLE_H_ +#define _LIBDM_DMTABLE_H_ + +#include + +#include +#include +#include + +#include "dm_target.h" + +namespace android { +namespace dm { + +class DmTable { + public: + DmTable() : size_(0){}; + + // 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 + // any of the existing targets in the table. Gaps are allowed. The final check, including + // overlaps and gaps are done before loading the table. Returns 'false' on failure. + bool AddTarget(std::unique_ptr&& target); + + // Removes a target from the table for the range specified in the target object. Returns 'false' + // if the target name doesn't match with the one in the table. Returns 'true' if target is + // successfully removed. + bool RemoveTarget(std::unique_ptr&& target); + + // Checks the table to make sure it is valid. i.e. Checks for range overlaps, range gaps + // and returns 'true' if the table is ready to be loaded into kernel. Returns 'false' if the + // table is malformed. + bool valid() const; + + // Returns the total size represented by the table in terms of number of 512-byte sectors. + // NOTE: This function will overlook if there are any gaps in the targets added in the table. + uint64_t size() const; + + // Returns the string represntation of the table that is ready to be passed into the kernel + // as part of the DM_TABLE_LOAD ioctl. + std::string Serialize() const; + + ~DmTable() = default; + + private: + // list of targets defined in this table sorted by + // their start and end sectors. + // Note: Overlapping targets MUST never be added in this list. + std::vector> targets_; + + // Total size in terms of # of sectors, as calculated by looking at the last and the first + // target in 'target_'. + uint64_t size_; +}; + +} // namespace dm +} // namespace android + +#endif /* _LIBDM_DMTABLE_H_ */ diff --git a/fs_mgr/libdm/include/dm_target.h b/fs_mgr/libdm/include/dm_target.h new file mode 100644 index 000000000..31b0cb69c --- /dev/null +++ b/fs_mgr/libdm/include/dm_target.h @@ -0,0 +1,77 @@ +/* + * Copyright 2018 Google, Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _LIBDM_DMTARGET_H_ +#define _LIBDM_DMTARGET_H_ + +#include +#include + +#include + +#include + +namespace android { +namespace dm { + +class DmTarget { + public: + DmTarget(const std::string& name, uint64_t start = 0, uint64_t length = 0) + : name_(name), v0_(0), v1_(0), v2_(0), start_(start), length_(length){}; + + // Creates a DmTarget object from dm_target_version as read from kernel + // with DM_LIST_VERSION ioctl. + DmTarget(const struct dm_target_versions* vers) : start_(0), length_(0) { + CHECK(vers != nullptr) << "Can't create DmTarget with dm_target_versions set to nullptr"; + v0_ = vers->version[0]; + v1_ = vers->version[1]; + v2_ = vers->version[2]; + name_ = vers->name; + } + + virtual ~DmTarget() = default; + + // Returns name of the target. + const std::string& name() const { return name_; } + + // Returns size in number of sectors when this target is part of + // a DmTable, return 0 otherwise. + uint64_t size() const { return length_; } + + // Return string representation of the device mapper target version. + std::string version() const { + return std::to_string(v0_) + "." + std::to_string(v1_) + "." + std::to_string(v2_); + } + + // 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 { return ""; } + + private: + // Name of the target. + std::string name_; + // Target version. + uint32_t v0_, v1_, v2_; + // logical sector number start and total length (in terms of 512-byte sectors) represented + // by this target within a DmTable. + uint64_t start_, length_; +}; + +} // namespace dm +} // namespace android + +#endif /* _LIBDM_DMTARGET_H_ */ diff --git a/fs_mgr/tools/Android.bp b/fs_mgr/tools/Android.bp new file mode 100644 index 000000000..4d4aae41a --- /dev/null +++ b/fs_mgr/tools/Android.bp @@ -0,0 +1,31 @@ +// +// Copyright (C) 2018 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +cc_binary { + name: "dmctl", + srcs: ["dmctl.cpp"], + + static_libs: [ + "libfs_mgr", + ], + + shared_libs: [ + "libbase", + "liblog", + ], + + cflags: ["-Werror"], +} diff --git a/fs_mgr/tools/dmctl.cpp b/fs_mgr/tools/dmctl.cpp new file mode 100644 index 000000000..27e2a5825 --- /dev/null +++ b/fs_mgr/tools/dmctl.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +using DeviceMapper = ::android::dm::DeviceMapper; +using DmTarget = ::android::dm::DmTarget; + +static int Usage(void) { + std::cerr << "usage: dmctl [command options]"; + std::cerr << "commands:"; + std::cerr << " create [-lo ] "; + std::cerr, " delete "; + std::cerr, " list"; + std::cerr, " help"; + return -EINVAL; +} + +static int DmCreateCmdHandler(int argc, char** argv) { + if (argc <= 1) { + std::cerr << "DmCreateCmdHandler: Invalid arguments"; + if (argc > 0) std::cerr << " args: " << argv[0]; + return -EINVAL; + } + + // Parse Everything first to make sure we have everything we need. + std::string devname = argv[0]; + DeviceMapper& dm = DeviceMapper::Instance(); + std::vector targets; + if (!dm.GetAvailableTargets(&targets)) { + std::cerr << "Failed to read available device mapper targets"; + return -errno; + } + + if (targets.empty()) { + std::cerr << "zero device mapper targets available"; + return -EEXIST; + } + + for (const auto& target : targets) { + if (target.name() == argv[1]) { + // TODO(b/110035986) : Create the target here, return success for now. + return 0; + } + } + + std::cerr << "Invalid or non-existing target : " << argv[1]; + return -EINVAL; +} + +static int DmDeleteCmdHandler(int argc, char** argv) { + std::cout << "DmDeleteCmdHandler:" << std::endl; + std::cout << " args:" << std::endl; + for (int i = 0; i < argc; i++) { + std::cout << " " << argv[i] << std::endl; + } + + return 0; +} + +static int DmListCmdHandler(int /* argc */, char** /* argv */) { + std::cout << "Available Device Mapper Targets:" << std::endl; + + DeviceMapper& dm = DeviceMapper::Instance(); + std::vector targets; + if (!dm.GetAvailableTargets(&targets)) { + std::cerr << "Failed to read available device mapper targets"; + return -errno; + } + + if (targets.empty()) { + std::cout << " " << std::endl; + return 0; + } + + for (const auto& target : targets) { + std::cout << std::left << std::setw(20) << target.name() << " : " << target.version() + << std::endl; + } + + return 0; +} + +static int HelpCmdHandler(int /* argc */, char** /* argv */) { + Usage(); + return 0; +} + +static std::map> cmdmap = { + {"create", DmCreateCmdHandler}, + {"delete", DmDeleteCmdHandler}, + {"list", DmListCmdHandler}, + {"help", HelpCmdHandler}, +}; + +int main(int argc, char** argv) { + android::base::InitLogging(argv, &android::base::StderrLogger); + if (argc < 2) { + return Usage(); + } + + for (const auto& cmd : cmdmap) { + if (cmd.first == argv[1]) { + return cmd.second(argc - 2, argv + 2); + } + } + + return Usage(); +} From 45d94ab683d88a2d0532b382cffb9ce0c7b5444f Mon Sep 17 00:00:00 2001 From: Sandeep Patil Date: Tue, 12 Jun 2018 17:03:56 -0700 Subject: [PATCH 2/3] fs_mgr: libdm: add support to create and delete device mapper devices. Test: dmctl create system; dmctl delete system Test: verify that ueventd creates /dev/block/dm-X and verify the dm device name from /sys/block/dm-X/dm/name Bug: 110035986 Change-Id: I2a08e2ea7007c0c13fe64d444f0d6618784edae7 Signed-off-by: Sandeep Patil --- fs_mgr/libdm/dm.cpp | 90 +++++++++++++++++++++++++++++++++++---- fs_mgr/libdm/include/dm.h | 8 ++++ fs_mgr/tools/dmctl.cpp | 52 +++++++++++----------- 3 files changed, 113 insertions(+), 37 deletions(-) diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp index 9a2910e2c..c2f732ba1 100644 --- a/fs_mgr/libdm/dm.cpp +++ b/fs_mgr/libdm/dm.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -39,14 +40,69 @@ DeviceMapper& DeviceMapper::Instance() { return instance; } // Creates a new device mapper device -bool DeviceMapper::CreateDevice(const std::string& /* name */) { +bool DeviceMapper::CreateDevice(const std::string& name) { + if (name.empty()) { + LOG(ERROR) << "Unnamed device mapper device creation is not supported"; + return false; + } + + if (name.size() >= DM_NAME_LEN) { + LOG(ERROR) << "[" << name << "] is too long to be device mapper name"; + return false; + } + + std::unique_ptr io( + static_cast(malloc(sizeof(struct dm_ioctl))), free); + if (io == nullptr) { + LOG(ERROR) << "Failed to allocate dm_ioctl"; + return false; + } + InitIo(io.get(), name); + + if (ioctl(fd_, DM_DEV_CREATE, io.get())) { + PLOG(ERROR) << "DM_DEV_CREATE failed to create [" << name << "]"; + return false; + } + + // Check to make sure the newly created device doesn't already have targets + // added or opened by someone + CHECK(io->target_count == 0) << "Unexpected targets for newly created [" << name << "] device"; + CHECK(io->open_count == 0) << "Unexpected opens for newly created [" << name << "] device"; + // Creates a new device mapper device with the name passed in - return false; + return true; } -bool DeviceMapper::DeleteDevice(const std::string& /* name */) { - // Destroy device here first - return false; +bool DeviceMapper::DeleteDevice(const std::string& name) { + if (name.empty()) { + LOG(ERROR) << "Unnamed device mapper device creation is not supported"; + return false; + } + + if (name.size() >= DM_NAME_LEN) { + LOG(ERROR) << "[" << name << "] is too long to be device mapper name"; + return false; + } + + std::unique_ptr io( + static_cast(malloc(sizeof(struct dm_ioctl))), free); + if (io == nullptr) { + LOG(ERROR) << "Failed to allocate dm_ioctl"; + return false; + } + InitIo(io.get(), name); + + if (ioctl(fd_, DM_DEV_REMOVE, io.get())) { + PLOG(ERROR) << "DM_DEV_REMOVE failed to create [" << name << "]"; + return false; + } + + // Check to make sure appropriate uevent is generated so ueventd will + // do the right thing and remove the corresponding device node and symlinks. + CHECK(io->flags & DM_UEVENT_GENERATED_FLAG) + << "Didn't generate uevent for [" << name << "] removal"; + + return true; } const std::unique_ptr DeviceMapper::table(const std::string& /* name */) const { @@ -82,12 +138,13 @@ bool DeviceMapper::GetAvailableTargets(std::vector* targets) { return false; } + // Sets appropriate data size and data_start to make sure we tell kernel + // about the total size of the buffer we are passing and where to start + // writing the list of targets. struct dm_ioctl* io = reinterpret_cast(buffer.get()); - io->data_start = sizeof(*io); + InitIo(io); io->data_size = data_size; - io->version[0] = 4; - io->version[1] = 0; - io->version[2] = 0; + io->data_start = sizeof(*io); if (ioctl(fd_, DM_LIST_VERSIONS, io)) { PLOG(ERROR) << "Failed to get DM_LIST_VERSIONS from kernel"; @@ -131,5 +188,20 @@ std::string DeviceMapper::GetDmDevicePathByName(const std::string& /* name */) { return ""; } +// private methods of DeviceMapper +void DeviceMapper::InitIo(struct dm_ioctl* io, const std::string& name) const { + CHECK(io != nullptr) << "nullptr passed to dm_ioctl initialization"; + memset(io, 0, sizeof(*io)); + + io->version[0] = DM_VERSION0; + io->version[1] = DM_VERSION1; + io->version[2] = DM_VERSION2; + io->data_size = sizeof(*io); + io->data_start = 0; + if (!name.empty()) { + strlcpy(io->name, name.c_str(), sizeof(io->name)); + } +} + } // namespace dm } // namespace android diff --git a/fs_mgr/libdm/include/dm.h b/fs_mgr/libdm/include/dm.h index 0cd0149bc..d839393ac 100644 --- a/fs_mgr/libdm/include/dm.h +++ b/fs_mgr/libdm/include/dm.h @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -27,6 +28,11 @@ #include +// The minimum expected device mapper major.minor version +#define DM_VERSION0 (4) +#define DM_VERSION1 (0) +#define DM_VERSION2 (0) + #define DM_ALIGN_MASK (7) #define DM_ALIGN(x) ((x + DM_ALIGN_MASK) & ~DM_ALIGN_MASK) @@ -87,6 +93,8 @@ class DeviceMapper final { // a finite amount of memory. This limit is in no way enforced by the kernel. static constexpr uint32_t kMaxPossibleDmTargets = 256; + void InitIo(struct dm_ioctl* io, const std::string& name = std::string()) const; + DeviceMapper() : fd_(-1) { fd_ = TEMP_FAILURE_RETRY(open("/dev/device-mapper", O_RDWR | O_CLOEXEC)); if (fd_ < 0) { diff --git a/fs_mgr/tools/dmctl.cpp b/fs_mgr/tools/dmctl.cpp index 27e2a5825..c12383011 100644 --- a/fs_mgr/tools/dmctl.cpp +++ b/fs_mgr/tools/dmctl.cpp @@ -39,50 +39,46 @@ using DmTarget = ::android::dm::DmTarget; static int Usage(void) { std::cerr << "usage: dmctl [command options]"; std::cerr << "commands:"; - std::cerr << " create [-lo ] "; - std::cerr, " delete "; + std::cerr << " create [dm-target> [-lo ] ]"; + std::cerr, " delete "; std::cerr, " list"; std::cerr, " help"; return -EINVAL; } static int DmCreateCmdHandler(int argc, char** argv) { - if (argc <= 1) { - std::cerr << "DmCreateCmdHandler: Invalid arguments"; - if (argc > 0) std::cerr << " args: " << argv[0]; + if (argc < 1) { + std::cerr << "DmCreateCmdHandler: atleast 'name' MUST be provided for target device"; return -EINVAL; } - // Parse Everything first to make sure we have everything we need. - std::string devname = argv[0]; + std::string name = argv[0]; DeviceMapper& dm = DeviceMapper::Instance(); - std::vector targets; - if (!dm.GetAvailableTargets(&targets)) { - std::cerr << "Failed to read available device mapper targets"; - return -errno; + if (!dm.CreateDevice(name)) { + std::cerr << "DmCreateCmdHandler: Failed to create " << name << " device"; + return -EIO; } - if (targets.empty()) { - std::cerr << "zero device mapper targets available"; - return -EEXIST; + // 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 } - for (const auto& target : targets) { - if (target.name() == argv[1]) { - // TODO(b/110035986) : Create the target here, return success for now. - return 0; - } - } - - std::cerr << "Invalid or non-existing target : " << argv[1]; - return -EINVAL; + return 0; } static int DmDeleteCmdHandler(int argc, char** argv) { - std::cout << "DmDeleteCmdHandler:" << std::endl; - std::cout << " args:" << std::endl; - for (int i = 0; i < argc; i++) { - std::cout << " " << argv[i] << std::endl; + if (argc < 1) { + std::cerr << "DmCreateCmdHandler: atleast 'name' MUST be provided for target device"; + return -EINVAL; + } + + std::string name = argv[0]; + DeviceMapper& dm = DeviceMapper::Instance(); + if (!dm.DeleteDevice(name)) { + std::cerr << "DmCreateCmdHandler: Failed to create " << name << " device"; + return -EIO; } return 0; @@ -94,7 +90,7 @@ static int DmListCmdHandler(int /* argc */, char** /* argv */) { DeviceMapper& dm = DeviceMapper::Instance(); std::vector targets; if (!dm.GetAvailableTargets(&targets)) { - std::cerr << "Failed to read available device mapper targets"; + std::cerr << "Failed to read available device mapper targets" << std::endl; return -errno; } From f603cfdd70ddd961218834a95012c327b85ee4c8 Mon Sep 17 00:00:00 2001 From: Sandeep Patil Date: Wed, 13 Jun 2018 12:09:58 -0700 Subject: [PATCH 3/3] fs_mgr: libdm: Add support to list existing device mapper devices Test: dmctl create system; dmctl list devices; dmctl delete system; dmctl list devices Bug: 110035986 Change-Id: I4ae5d40041458421068976fa2a99c662c542a9a1 Signed-off-by: Sandeep Patil --- fs_mgr/libdm/dm.cpp | 63 +++++++++++++++++++++++++++++++++++++++ fs_mgr/libdm/include/dm.h | 36 ++++++++++++++++++++++ fs_mgr/tools/dmctl.cpp | 49 ++++++++++++++++++++++++++---- 3 files changed, 143 insertions(+), 5 deletions(-) diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp index c2f732ba1..57c127081 100644 --- a/fs_mgr/libdm/dm.cpp +++ b/fs_mgr/libdm/dm.cpp @@ -182,6 +182,69 @@ bool DeviceMapper::GetAvailableTargets(std::vector* targets) { return true; } +bool DeviceMapper::GetAvailableDevices(std::vector* devices) { + devices->clear(); + + // calculate the space needed to read a maximum of 256 targets, each with + // name with maximum length of 16 bytes + uint32_t payload_size = sizeof(struct dm_name_list); + // 128-bytes for the name + payload_size += DM_NAME_LEN; + // dm wants every device spec to be aligned at 8-byte boundary + payload_size = DM_ALIGN(payload_size); + payload_size *= kMaxPossibleDmDevices; + uint32_t data_size = sizeof(struct dm_ioctl) + payload_size; + auto buffer = std::unique_ptr(calloc(1, data_size), free); + if (buffer == nullptr) { + LOG(ERROR) << "failed to allocate memory"; + return false; + } + + // Sets appropriate data size and data_start to make sure we tell kernel + // about the total size of the buffer we are passing and where to start + // writing the list of targets. + struct dm_ioctl* io = reinterpret_cast(buffer.get()); + InitIo(io); + io->data_size = data_size; + io->data_start = sizeof(*io); + + if (ioctl(fd_, DM_LIST_DEVICES, io)) { + PLOG(ERROR) << "Failed to get DM_LIST_DEVICES from kernel"; + return false; + } + + // If the provided buffer wasn't enough to list all devices any data + // beyond sizeof(*io) must not be read. + if (io->flags & DM_BUFFER_FULL_FLAG) { + LOG(INFO) << data_size << " is not enough memory to list all dm devices"; + return false; + } + + // if there are no devices created yet, return success with empty vector + if (io->data_size == sizeof(*io)) { + return true; + } + + // Parse each device and add a new DmBlockDevice to the vector + // created from the kernel data. + uint32_t next = sizeof(*io); + data_size = io->data_size - next; + struct dm_name_list* dm_dev = + reinterpret_cast(static_cast(buffer.get()) + next); + + while (next && data_size) { + devices->emplace_back((dm_dev)); + if (dm_dev->next == 0) { + break; + } + next += dm_dev->next; + data_size -= dm_dev->next; + dm_dev = reinterpret_cast(static_cast(buffer.get()) + next); + } + + return true; +} + // Accepts a device mapper device name (like system_a, vendor_b etc) and // returns the path to it's device node (or symlink to the device node) std::string DeviceMapper::GetDmDevicePathByName(const std::string& /* name */) { diff --git a/fs_mgr/libdm/include/dm.h b/fs_mgr/libdm/include/dm.h index d839393ac..52a9a111a 100644 --- a/fs_mgr/libdm/include/dm.h +++ b/fs_mgr/libdm/include/dm.h @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include #include @@ -43,6 +45,28 @@ enum class DmDeviceState { INVALID, SUSPENDED, ACTIVE }; class DeviceMapper final { public: + class DmBlockDevice final { + public: + // only allow creating this with dm_name_list + DmBlockDevice() = delete; + + explicit DmBlockDevice(struct dm_name_list* d) : name_(d->name), dev_(d->dev){}; + + // Returs device mapper name associated with the block device + const std::string& name() const { return name_; } + + // Return major number for the block device + uint32_t Major() const { return major(dev_); } + + // Return minor number for the block device + uint32_t Minor() const { return minor(dev_); } + ~DmBlockDevice() = default; + + private: + std::string name_; + uint64_t dev_; + }; + // Creates a device mapper device with given name. // Return 'true' on success and 'false' on failure to // create OR if a device mapper device with the same name already @@ -74,6 +98,12 @@ class DeviceMapper final { // successfully read and stored in 'targets'. Returns 'false' otherwise. bool GetAvailableTargets(std::vector* targets); + // Return 'true' if it can successfully read the list of device mapper block devices + // currently created. 'devices' will be empty if the kernel interactions + // were successful and there are no block devices at the moment. Returns + // 'false' in case of any failure along the way. + bool GetAvailableDevices(std::vector* devices); + // Returns the path to the device mapper device node in '/dev' corresponding to // 'name'. std::string GetDmDevicePathByName(const std::string& name); @@ -93,6 +123,12 @@ class DeviceMapper final { // a finite amount of memory. This limit is in no way enforced by the kernel. static constexpr uint32_t kMaxPossibleDmTargets = 256; + // Maximum possible device mapper created block devices. Note that this is restricted by + // the minor numbers (that used to be 8 bits) that can be range from 0 to 2^20-1 in newer + // kernels. In Android systems however, we never expect these to grow beyond the artificial + // limit we are imposing here of 256. + static constexpr uint32_t kMaxPossibleDmDevices = 256; + void InitIo(struct dm_ioctl* io, const std::string& name = std::string()) const; DeviceMapper() : fd_(-1) { diff --git a/fs_mgr/tools/dmctl.cpp b/fs_mgr/tools/dmctl.cpp index c12383011..f5bdc3556 100644 --- a/fs_mgr/tools/dmctl.cpp +++ b/fs_mgr/tools/dmctl.cpp @@ -35,13 +35,14 @@ using DeviceMapper = ::android::dm::DeviceMapper; using DmTarget = ::android::dm::DmTarget; +using DmBlockDevice = ::android::dm::DeviceMapper::DmBlockDevice; static int Usage(void) { std::cerr << "usage: dmctl [command options]"; std::cerr << "commands:"; std::cerr << " create [dm-target> [-lo ] ]"; std::cerr, " delete "; - std::cerr, " list"; + std::cerr, " list "; std::cerr, " help"; return -EINVAL; } @@ -84,16 +85,14 @@ static int DmDeleteCmdHandler(int argc, char** argv) { return 0; } -static int DmListCmdHandler(int /* argc */, char** /* argv */) { - std::cout << "Available Device Mapper Targets:" << std::endl; - - DeviceMapper& dm = DeviceMapper::Instance(); +static int DmListTargets(DeviceMapper& dm) { std::vector targets; if (!dm.GetAvailableTargets(&targets)) { std::cerr << "Failed to read available device mapper targets" << std::endl; return -errno; } + std::cout << "Available Device Mapper Targets:" << std::endl; if (targets.empty()) { std::cout << " " << std::endl; return 0; @@ -107,6 +106,46 @@ static int DmListCmdHandler(int /* argc */, char** /* argv */) { return 0; } +static int DmListDevices(DeviceMapper& dm) { + std::vector devices; + if (!dm.GetAvailableDevices(&devices)) { + std::cerr << "Failed to read available device mapper devices" << std::endl; + return -errno; + } + std::cout << "Available Device Mapper Devices:" << std::endl; + if (devices.empty()) { + std::cout << " " << std::endl; + return 0; + } + + for (const auto& dev : devices) { + std::cout << std::left << std::setw(20) << dev.name() << " : " << dev.Major() << ":" + << dev.Minor() << std::endl; + } + + return 0; +} + +static const std::map> listmap = { + {"targets", DmListTargets}, + {"devices", DmListDevices}, +}; + +static int DmListCmdHandler(int argc, char** argv) { + if (argc < 1) { + std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl; + return -EINVAL; + } + + DeviceMapper& dm = DeviceMapper::Instance(); + for (const auto& l : listmap) { + if (l.first == argv[0]) return l.second(dm); + } + + std::cerr << "Invalid argument to \'dmctl list\': " << argv[0] << std::endl; + return -EINVAL; +} + static int HelpCmdHandler(int /* argc */, char** /* argv */) { Usage(); return 0;