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

Commit 40b59482 authored by David Anderson's avatar David Anderson
Browse files

fs_mgr: replace DM_TABLE_STATUS use with libdm.

This change introduces a new GetTableStatus method on DeviceMapper,
which returns a vector of information about each target in a device's
table. Some target types (such as verity) can also return additional
information as a string.

Support for this call has also been added to the "dmctl" tool via a
"table" command. Examples:

    $ dmctl create blah zero 0 8000 linear 8000 1000 /dev/block/sdd1 0
    $ dmctl table blah
    Targets in the device-mapper table for blah:
    0-8000: zero
    8000-9000: linear

    For verity:
    sailfish:/ # dmctl table system
    Targets in the device-mapper table for system:
    0-4128792: android-verity, V

Bug: 110035986
Test: libdm_test gtest; AVB1 device still boots
Change-Id: Iaf13450d3b32e2264c7c399a8af8d6bade260592
parent 6effd4d6
Loading
Loading
Loading
Loading
+10 −13
Original line number Diff line number Diff line
@@ -50,6 +50,7 @@
#include <ext4_utils/ext4_sb.h>
#include <ext4_utils/ext4_utils.h>
#include <ext4_utils/wipe.h>
#include <libdm/dm.h>
#include <linux/fs.h>
#include <linux/loop.h>
#include <linux/magic.h>
@@ -76,6 +77,8 @@

#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))

using DeviceMapper = android::dm::DeviceMapper;

// record fs stat
enum FsStatFlags {
    FS_STAT_IS_EXT4 = 0x0001,
@@ -1369,12 +1372,6 @@ bool fs_mgr_update_verity_state(fs_mgr_verity_state_callback callback) {
        return false;
    }

    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open("/dev/device-mapper", O_RDWR | O_CLOEXEC)));
    if (fd == -1) {
        PERROR << "Error opening device mapper";
        return false;
    }

    std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
                                                               fs_mgr_free_fstab);
    if (!fstab) {
@@ -1382,8 +1379,8 @@ bool fs_mgr_update_verity_state(fs_mgr_verity_state_callback callback) {
        return false;
    }

    alignas(dm_ioctl) char buffer[DM_BUF_SIZE];
    struct dm_ioctl* io = (struct dm_ioctl*)buffer;
    DeviceMapper& dm = DeviceMapper::Instance();

    bool system_root = android::base::GetProperty("ro.build.system_root_image", "") == "true";

    for (int i = 0; i < fstab->num_entries; i++) {
@@ -1399,20 +1396,20 @@ bool fs_mgr_update_verity_state(fs_mgr_verity_state_callback callback) {
            mount_point = basename(fstab->recs[i].mount_point);
        }

        fs_mgr_dm_ioctl_init(io, DM_BUF_SIZE, mount_point);
        const char* status = nullptr;

        const char* status;
        if (ioctl(fd, DM_TABLE_STATUS, io)) {
        std::vector<DeviceMapper::TargetInfo> table;
        if (!dm.GetTableStatus(mount_point, &table) || table.empty() || table[0].data.empty()) {
            if (fstab->recs[i].fs_mgr_flags & MF_VERIFYATBOOT) {
                status = "V";
            } else {
                PERROR << "Failed to query DM_TABLE_STATUS for " << mount_point.c_str();
                continue;
            }
        } else {
            status = table[0].data.c_str();
        }

        status = &buffer[io->data_start + sizeof(struct dm_target_spec)];

        // To be consistent in vboot 1.0 and vboot 2.0 (AVB), change the mount_point
        // back to 'system' for the callback. So it has property [partition.system.verified]
        // instead of [partition.vroot.verified].
+40 −0
Original line number Diff line number Diff line
@@ -272,6 +272,46 @@ bool DeviceMapper::GetDmDevicePathByName(const std::string& name, std::string* p
    return true;
}

bool DeviceMapper::GetTableStatus(const std::string& name, std::vector<TargetInfo>* table) {
    char buffer[4096];
    struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(buffer);

    InitIo(io, name);
    io->data_size = sizeof(buffer);
    io->data_start = sizeof(*io);
    if (ioctl(fd_, DM_TABLE_STATUS, io) < 0) {
        PLOG(ERROR) << "DM_TABLE_STATUS failed for " << name;
        return false;
    }
    if (io->flags & DM_BUFFER_FULL_FLAG) {
        PLOG(ERROR) << "DM_TABLE_STATUS result for " << name << " was too large";
        return false;
    }

    uint32_t cursor = io->data_start;
    uint32_t data_end = std::min(io->data_size, uint32_t(sizeof(buffer)));
    for (uint32_t i = 0; i < io->target_count; i++) {
        if (cursor + sizeof(struct dm_target_spec) > data_end) {
            break;
        }
        // After each dm_target_spec is a status string. spec->next is an
        // offset from |io->data_start|, and we clamp it to the size of our
        // buffer.
        struct dm_target_spec* spec = reinterpret_cast<struct dm_target_spec*>(buffer + cursor);
        uint32_t data_offset = cursor + sizeof(dm_target_spec);
        uint32_t next_cursor = std::min(io->data_start + spec->next, data_end);

        std::string data;
        if (next_cursor > data_offset) {
            // Note: we use c_str() to eliminate any extra trailing 0s.
            data = std::string(buffer + data_offset, next_cursor - data_offset).c_str();
        }
        table->emplace_back(*spec, data);
        cursor = next_cursor;
    }
    return true;
}

// 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";
+14 −0
Original line number Diff line number Diff line
@@ -160,6 +160,20 @@ TEST(libdm, DmLinear) {
        ASSERT_EQ(strncmp(sector, message2, sizeof(message2)), 0);
    }

    // Test GetTableStatus.
    DeviceMapper& dm = DeviceMapper::Instance();
    vector<DeviceMapper::TargetInfo> targets;
    ASSERT_TRUE(dm.GetTableStatus(dev.name(), &targets));
    ASSERT_EQ(targets.size(), 2);
    EXPECT_EQ(strcmp(targets[0].spec.target_type, "linear"), 0);
    EXPECT_TRUE(targets[0].data.empty());
    EXPECT_EQ(targets[0].spec.sector_start, 0);
    EXPECT_EQ(targets[0].spec.length, 1);
    EXPECT_EQ(strcmp(targets[1].spec.target_type, "linear"), 0);
    EXPECT_TRUE(targets[1].data.empty());
    EXPECT_EQ(targets[1].spec.sector_start, 1);
    EXPECT_EQ(targets[1].spec.length, 1);

    // Normally the TestDevice destructor would delete this, but at least one
    // test should ensure that device deletion works.
    ASSERT_TRUE(dev.Destroy());
+13 −0
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@

#include <memory>
#include <string>
#include <utility>
#include <vector>

#include <android-base/logging.h>
@@ -117,6 +118,18 @@ class DeviceMapper final {
        }
    }

    // Query the status of a table, given a device name. The output vector will
    // contain one TargetInfo for each target in the table. If the device does
    // not exist, or there were too many targets, the call will fail and return
    // false.
    struct TargetInfo {
        struct dm_target_spec spec;
        std::string data;
        TargetInfo(const struct dm_target_spec& spec, const std::string& data)
            : spec(spec), data(data) {}
    };
    bool GetTableStatus(const std::string& name, std::vector<TargetInfo>* table);

  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
+27 −0
Original line number Diff line number Diff line
@@ -50,6 +50,7 @@ static int Usage(void) {
    std::cerr << "  delete <dm-name>" << std::endl;
    std::cerr << "  list <devices | targets>" << std::endl;
    std::cerr << "  getpath <dm-name>" << std::endl;
    std::cerr << "  table <dm-name>" << std::endl;
    std::cerr << "  help" << std::endl;
    std::cerr << std::endl;
    std::cerr << "Target syntax:" << std::endl;
@@ -258,6 +259,31 @@ static int GetPathCmdHandler(int argc, char** argv) {
    return 0;
}

static int TableCmdHandler(int argc, char** argv) {
    if (argc != 1) {
        std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
        return -EINVAL;
    }

    DeviceMapper& dm = DeviceMapper::Instance();
    std::vector<DeviceMapper::TargetInfo> table;
    if (!dm.GetTableStatus(argv[0], &table)) {
        std::cerr << "Could not query table status of device \"" << argv[0] << "\"." << std::endl;
        return -EINVAL;
    }
    std::cout << "Targets in the device-mapper table for " << argv[0] << ":" << std::endl;
    for (const auto& target : table) {
        std::cout << target.spec.sector_start << "-"
                  << (target.spec.sector_start + target.spec.length) << ": "
                  << target.spec.target_type;
        if (!target.data.empty()) {
            std::cout << ", " << target.data;
        }
        std::cout << std::endl;
    }
    return 0;
}

static std::map<std::string, std::function<int(int, char**)>> cmdmap = {
        // clang-format off
        {"create", DmCreateCmdHandler},
@@ -265,6 +291,7 @@ static std::map<std::string, std::function<int(int, char**)>> cmdmap = {
        {"list", DmListCmdHandler},
        {"help", HelpCmdHandler},
        {"getpath", GetPathCmdHandler},
        {"table", TableCmdHandler},
        // clang-format on
};