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

Commit 1fd9c556 authored by Nikita Ioffe's avatar Nikita Ioffe Committed by Gerrit Code Review
Browse files

Merge "libdm: add an api to unwind dm stack"

parents 9e6ac648 9fb63038
Loading
Loading
Loading
Loading
+71 −0
Original line number Diff line number Diff line
@@ -16,12 +16,14 @@

#include "libdm/dm.h"

#include <linux/dm-ioctl.h>
#include <sys/ioctl.h>
#include <sys/sysmacros.h>
#include <sys/types.h>

#include <chrono>
#include <functional>
#include <string_view>
#include <thread>

#include <android-base/file.h>
@@ -504,5 +506,74 @@ std::string DeviceMapper::GetTargetType(const struct dm_target_spec& spec) {
    return std::string{spec.target_type, sizeof(spec.target_type)};
}

static bool ExtractBlockDeviceName(const std::string& path, std::string* name) {
    static constexpr std::string_view kDevBlockPrefix("/dev/block/");
    if (android::base::StartsWith(path, kDevBlockPrefix)) {
        *name = path.substr(kDevBlockPrefix.length());
        return true;
    }
    return false;
}

bool DeviceMapper::IsDmBlockDevice(const std::string& path) {
    std::string name;
    if (!ExtractBlockDeviceName(path, &name)) {
        return false;
    }
    return android::base::StartsWith(name, "dm-");
}

std::optional<std::string> DeviceMapper::GetDmDeviceNameByPath(const std::string& path) {
    std::string name;
    if (!ExtractBlockDeviceName(path, &name)) {
        LOG(WARNING) << path << " is not a block device";
        return std::nullopt;
    }
    if (!android::base::StartsWith(name, "dm-")) {
        LOG(WARNING) << path << " is not a dm device";
        return std::nullopt;
    }
    std::string dm_name_file = "/sys/block/" + name + "/dm/name";
    std::string dm_name;
    if (!android::base::ReadFileToString(dm_name_file, &dm_name)) {
        PLOG(ERROR) << "Failed to read file " << dm_name_file;
        return std::nullopt;
    }
    dm_name = android::base::Trim(dm_name);
    return dm_name;
}

std::optional<std::string> DeviceMapper::GetParentBlockDeviceByPath(const std::string& path) {
    std::string name;
    if (!ExtractBlockDeviceName(path, &name)) {
        LOG(WARNING) << path << " is not a block device";
        return std::nullopt;
    }
    if (!android::base::StartsWith(name, "dm-")) {
        // Reached bottom of the device mapper stack.
        return std::nullopt;
    }
    auto slaves_dir = "/sys/block/" + name + "/slaves";
    auto dir = std::unique_ptr<DIR, decltype(&closedir)>(opendir(slaves_dir.c_str()), closedir);
    if (dir == nullptr) {
        PLOG(ERROR) << "Failed to open: " << slaves_dir;
        return std::nullopt;
    }
    std::string sub_device_name = "";
    for (auto entry = readdir(dir.get()); entry; entry = readdir(dir.get())) {
        if (entry->d_type != DT_LNK) continue;
        if (!sub_device_name.empty()) {
            LOG(ERROR) << "Too many slaves in " << slaves_dir;
            return std::nullopt;
        }
        sub_device_name = entry->d_name;
    }
    if (sub_device_name.empty()) {
        LOG(ERROR) << "No slaves in " << slaves_dir;
        return std::nullopt;
    }
    return "/dev/block/" + sub_device_name;
}

}  // namespace dm
}  // namespace android
+61 −0
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@
#include <thread>

#include <android-base/file.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <gtest/gtest.h>
#include <libdm/dm.h>
@@ -544,3 +545,63 @@ TEST(libdm, DeleteDeviceWithTimeout) {
    ASSERT_NE(0, access(path.c_str(), F_OK));
    ASSERT_EQ(ENOENT, errno);
}

TEST(libdm, IsDmBlockDevice) {
    unique_fd tmp(CreateTempFile("file_1", 4096));
    ASSERT_GE(tmp, 0);
    LoopDevice loop(tmp, 10s);
    ASSERT_TRUE(loop.valid());
    ASSERT_TRUE(android::base::StartsWith(loop.device(), "/dev/block"));

    DmTable table;
    ASSERT_TRUE(table.Emplace<DmTargetLinear>(0, 1, loop.device(), 0));
    ASSERT_TRUE(table.valid());

    TempDevice dev("libdm-test-dm-linear", table);
    ASSERT_TRUE(dev.valid());

    DeviceMapper& dm = DeviceMapper::Instance();
    ASSERT_TRUE(dm.IsDmBlockDevice(dev.path()));
    ASSERT_FALSE(dm.IsDmBlockDevice(loop.device()));
}

TEST(libdm, GetDmDeviceNameByPath) {
    unique_fd tmp(CreateTempFile("file_1", 4096));
    ASSERT_GE(tmp, 0);
    LoopDevice loop(tmp, 10s);
    ASSERT_TRUE(loop.valid());
    ASSERT_TRUE(android::base::StartsWith(loop.device(), "/dev/block"));

    DmTable table;
    ASSERT_TRUE(table.Emplace<DmTargetLinear>(0, 1, loop.device(), 0));
    ASSERT_TRUE(table.valid());

    TempDevice dev("libdm-test-dm-linear", table);
    ASSERT_TRUE(dev.valid());

    DeviceMapper& dm = DeviceMapper::Instance();
    // Not a dm device, GetDmDeviceNameByPath will return std::nullopt.
    ASSERT_FALSE(dm.GetDmDeviceNameByPath(loop.device()));
    auto name = dm.GetDmDeviceNameByPath(dev.path());
    ASSERT_EQ("libdm-test-dm-linear", *name);
}

TEST(libdm, GetParentBlockDeviceByPath) {
    unique_fd tmp(CreateTempFile("file_1", 4096));
    ASSERT_GE(tmp, 0);
    LoopDevice loop(tmp, 10s);
    ASSERT_TRUE(loop.valid());
    ASSERT_TRUE(android::base::StartsWith(loop.device(), "/dev/block"));

    DmTable table;
    ASSERT_TRUE(table.Emplace<DmTargetLinear>(0, 1, loop.device(), 0));
    ASSERT_TRUE(table.valid());

    TempDevice dev("libdm-test-dm-linear", table);
    ASSERT_TRUE(dev.valid());

    DeviceMapper& dm = DeviceMapper::Instance();
    ASSERT_FALSE(dm.GetParentBlockDeviceByPath(loop.device()));
    auto sub_block_device = dm.GetParentBlockDeviceByPath(dev.path());
    ASSERT_EQ(loop.device(), *sub_block_device);
}
+13 −0
Original line number Diff line number Diff line
@@ -214,6 +214,19 @@ class DeviceMapper final {

    static std::string GetTargetType(const struct dm_target_spec& spec);

    // Returns true if given path is a path to a dm block device.
    bool IsDmBlockDevice(const std::string& path);

    // Returns name of a dm-device with the given path, or std::nulloptr if given path is not a
    // dm-device.
    std::optional<std::string> GetDmDeviceNameByPath(const std::string& path);

    // Returns a parent block device of a dm device with the given path, or std::nullopt if:
    //  * Given path doesn't correspond to a dm device.
    //  * A dm device is based on top of more than one block devices.
    //  * A failure occurred.
    std::optional<std::string> GetParentBlockDeviceByPath(const std::string& path);

  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