Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f603cfdd authored by Sandeep Patil's avatar Sandeep Patil
Browse files

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: default avatarSandeep Patil <sspatil@google.com>
parent 45d94ab6
Loading
Loading
Loading
Loading
+63 −0
Original line number Diff line number Diff line
@@ -182,6 +182,69 @@ bool DeviceMapper::GetAvailableTargets(std::vector<DmTarget>* targets) {
    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 */) {
+36 −0
Original line number Diff line number Diff line
@@ -20,6 +20,8 @@
#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>
@@ -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<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);
@@ -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) {
+44 −5
Original line number Diff line number Diff line
@@ -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> [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";
    std::cerr, "  list <devices | targets>";
    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<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;
@@ -107,6 +106,46 @@ static int DmListCmdHandler(int /* argc */, char** /* argv */) {
    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;