Loading fs_mgr/libdm/dm.cpp +71 −0 Original line number Diff line number Diff line Loading @@ -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> Loading Loading @@ -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 fs_mgr/libdm/dm_test.cpp +61 −0 Original line number Diff line number Diff line Loading @@ -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> Loading Loading @@ -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); } fs_mgr/libdm/include/libdm/dm.h +13 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading
fs_mgr/libdm/dm.cpp +71 −0 Original line number Diff line number Diff line Loading @@ -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> Loading Loading @@ -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
fs_mgr/libdm/dm_test.cpp +61 −0 Original line number Diff line number Diff line Loading @@ -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> Loading Loading @@ -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); }
fs_mgr/libdm/include/libdm/dm.h +13 −0 Original line number Diff line number Diff line Loading @@ -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 Loading