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;