Merge changes from topic "libdm"
am: b066bbebd1
Change-Id: I2e7c1c71c9c7e25797a79702ceeafe756f457d26
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index bc3b04b..923442f 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -56,12 +56,15 @@
"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 0000000..672d401
--- /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 0000000..57c1270
--- /dev/null
+++ b/fs_mgr/libdm/dm.cpp
@@ -0,0 +1,270 @@
+/*
+ * 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 <errno.h>
+#include <fcntl.h>
+#include <linux/dm-ioctl.h>
+#include <stdint.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+#include <android-base/unique_fd.h>
+
+#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) {
+ 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<struct dm_ioctl, decltype(&free)> io(
+ static_cast<struct dm_ioctl*>(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 true;
+}
+
+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<struct dm_ioctl, decltype(&free)> io(
+ static_cast<struct dm_ioctl*>(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<DmTable> 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<DmTarget>* 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<void, void (*)(void*)>(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<struct dm_ioctl*>(buffer.get());
+ InitIo(io);
+ io->data_size = data_size;
+ io->data_start = sizeof(*io);
+
+ 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<struct dm_target_versions*>(static_cast<char*>(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<struct dm_target_versions*>(static_cast<char*>(buffer.get()) + next);
+ }
+
+ return true;
+}
+
+bool DeviceMapper::GetAvailableDevices(std::vector<DmBlockDevice>* 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<void, void (*)(void*)>(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<struct dm_ioctl*>(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<struct dm_name_list*>(static_cast<char*>(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<struct dm_name_list*>(static_cast<char*>(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 "";
+}
+
+// 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/dm_table.cpp b/fs_mgr/libdm/dm_table.cpp
new file mode 100644
index 0000000..14b3932
--- /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 <android-base/logging.h>
+#include <android-base/macros.h>
+
+#include <string>
+#include <vector>
+
+#include "dm_table.h"
+
+namespace android {
+namespace dm {
+
+bool DmTable::AddTarget(std::unique_ptr<DmTarget>&& /* target */) {
+ return true;
+}
+
+bool DmTable::RemoveTarget(std::unique_ptr<DmTarget>&& /* 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 0000000..8bcd526
--- /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 <android-base/logging.h>
+#include <android-base/macros.h>
+
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+#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 0000000..52a9a11
--- /dev/null
+++ b/fs_mgr/libdm/include/dm.h
@@ -0,0 +1,152 @@
+/*
+ * 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 <errno.h>
+#include <fcntl.h>
+#include <linux/dm-ioctl.h>
+#include <linux/kdev_t.h>
+#include <sys/sysmacros.h>
+#include <unistd.h>
+
+#include <memory>
+
+#include <android-base/logging.h>
+
+#include <dm_table.h>
+
+// 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)
+
+namespace android {
+namespace dm {
+
+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
+ // 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<DmTable> 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<DmTarget>* 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<DmBlockDevice>* devices);
+
+ // 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;
+
+ // 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) {
+ 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 0000000..0b1685d
--- /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 <stdint.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#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<DmTarget>&& 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<DmTarget>&& 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<std::unique_ptr<DmTarget>> 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 0000000..31b0cb6
--- /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 <linux/dm-ioctl.h>
+#include <stdint.h>
+
+#include <android-base/logging.h>
+
+#include <string>
+
+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 0000000..4d4aae4
--- /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 0000000..f5bdc35
--- /dev/null
+++ b/fs_mgr/tools/dmctl.cpp
@@ -0,0 +1,174 @@
+/*
+ * 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 <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <linux/dm-ioctl.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/unique_fd.h>
+#include <dm.h>
+
+#include <functional>
+#include <iomanip>
+#include <ios>
+#include <iostream>
+#include <map>
+#include <string>
+#include <vector>
+
+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> [command options]";
+ std::cerr << "commands:";
+ std::cerr << " create <dm-name> [dm-target> [-lo <filename>] <dm-target-args>]";
+ std::cerr, " delete <dm-name>";
+ std::cerr, " list <devices | targets>";
+ std::cerr, " help";
+ return -EINVAL;
+}
+
+static int DmCreateCmdHandler(int argc, char** argv) {
+ 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.CreateDevice(name)) {
+ std::cerr << "DmCreateCmdHandler: Failed to create " << name << " device";
+ 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;
+}
+
+static int DmDeleteCmdHandler(int argc, char** argv) {
+ 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;
+}
+
+static int DmListTargets(DeviceMapper& dm) {
+ std::vector<DmTarget> 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 << " <empty>" << 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 DmListDevices(DeviceMapper& dm) {
+ std::vector<DmBlockDevice> 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 << " <empty>" << 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<std::string, std::function<int(DeviceMapper&)>> 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;
+}
+
+static std::map<std::string, std::function<int(int, char**)>> 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();
+}