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 <sspatil@google.com>
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index 9a2910e..c2f732b 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -22,6 +22,7 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <memory>
 #include <string>
 #include <vector>
 
@@ -39,14 +40,69 @@
     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<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 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<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 {
@@ -82,12 +138,13 @@
         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());
-    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 @@
     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 0cd0149..d839393 100644
--- a/fs_mgr/libdm/include/dm.h
+++ b/fs_mgr/libdm/include/dm.h
@@ -19,6 +19,7 @@
 
 #include <errno.h>
 #include <fcntl.h>
+#include <linux/dm-ioctl.h>
 #include <unistd.h>
 
 #include <memory>
@@ -27,6 +28,11 @@
 
 #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)
 
@@ -87,6 +93,8 @@
     // 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 27e2a58..c123830 100644
--- a/fs_mgr/tools/dmctl.cpp
+++ b/fs_mgr/tools/dmctl.cpp
@@ -39,50 +39,46 @@
 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-device>";
+    std::cerr << "  create <dm-name> [dm-target> [-lo <filename>] <dm-target-args>]";
+    std::cerr, "  delete <dm-name>";
     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<DmTarget> 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 @@
     DeviceMapper& dm = DeviceMapper::Instance();
     std::vector<DmTarget> 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;
     }