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

Commit 91d23440 authored by Akilesh Kailash's avatar Akilesh Kailash Committed by Automerger Merge Worker
Browse files

Merge "Support of multiple device partitions by dm-user driver" am: 840d7b2a...

Merge "Support of multiple device partitions by dm-user driver" am: 840d7b2a am: cbc865a2 am: cd456170

Original change: https://android-review.googlesource.com/c/platform/system/core/+/1428108

Change-Id: I13cffd14c4efbfa418ab67a8f007ceb0267c3e3a
parents 99df8ab0 cd456170
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -429,7 +429,7 @@ bool DeviceMapper::GetDmDevicePathByName(const std::string& name, std::string* p

// Accepts a device mapper device name (like system_a, vendor_b etc) and
// returns its UUID.
bool DeviceMapper::GetDmDeviceUUIDByName(const std::string& name, std::string* uuid) {
bool DeviceMapper::GetDmDeviceUuidByName(const std::string& name, std::string* uuid) {
    struct dm_ioctl io;
    InitIo(&io, name);
    if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {
+1 −1
Original line number Diff line number Diff line
@@ -177,7 +177,7 @@ class DeviceMapper final {
    //
    // WaitForFile() should not be used in conjunction with this call, since it
    // could race with ueventd.
    bool GetDmDeviceUUIDByName(const std::string& name, std::string* path);
    bool GetDmDeviceUuidByName(const std::string& name, std::string* path);

    // Returns a device's unique path as generated by ueventd. This will return
    // true as long as the device has been created, even if ueventd has not
+165 −69
Original line number Diff line number Diff line
@@ -37,52 +37,86 @@ using android::base::unique_fd;
class SnapuserdTest : public ::testing::Test {
  protected:
    void SetUp() override {
        cow_ = std::make_unique<TemporaryFile>();
        ASSERT_GE(cow_->fd, 0) << strerror(errno);
        cow_system_ = std::make_unique<TemporaryFile>();
        ASSERT_GE(cow_system_->fd, 0) << strerror(errno);

        cow_product_ = std::make_unique<TemporaryFile>();
        ASSERT_GE(cow_product_->fd, 0) << strerror(errno);

        size_ = 100_MiB;
    }

    void TearDown() override { cow_ = nullptr; }
    void TearDown() override {
        cow_system_ = nullptr;
        cow_product_ = nullptr;
    }

    std::unique_ptr<TemporaryFile> cow_system_;
    std::unique_ptr<TemporaryFile> cow_product_;

    unique_fd sys_fd_;
    unique_fd product_fd_;
    size_t size_;

    std::unique_ptr<TemporaryFile> cow_;
    int system_blksize_;
    int product_blksize_;
    std::string system_device_name_;
    std::string product_device_name_;

    std::unique_ptr<uint8_t[]> random_buffer_1_;
    std::unique_ptr<uint8_t[]> random_buffer_2_;
    std::unique_ptr<uint8_t[]> zero_buffer_;
    std::unique_ptr<uint8_t[]> system_buffer_;
    std::unique_ptr<uint8_t[]> product_buffer_;

    void Init();
    void CreateCowDevice(std::unique_ptr<TemporaryFile>& cow);
    void CreateSystemDmUser();
    void CreateProductDmUser();
    void StartSnapuserdDaemon();
    void CreateSnapshotDevices();

    void TestIO(unique_fd& snapshot_fd, std::unique_ptr<uint8_t[]>&& buf);
};

TEST_F(SnapuserdTest, ReadWrite) {
    loff_t offset = 0;
    size_t size = 100_MiB;
void SnapuserdTest::Init() {
    unique_fd rnd_fd;
    unique_fd sys_fd;
    unique_fd snapshot_fd;
    unique_fd system_a_fd;
    std::string cmd;
    loff_t offset = 0;

    rnd_fd.reset(open("/dev/random", O_RDONLY));
    ASSERT_TRUE(rnd_fd > 0);

    std::unique_ptr<uint8_t[]> random_buffer_1;
    std::unique_ptr<uint8_t[]> random_buffer_2;
    std::unique_ptr<uint8_t[]> system_buffer;

    random_buffer_1 = std::make_unique<uint8_t[]>(size);

    random_buffer_2 = std::make_unique<uint8_t[]>(size);

    system_buffer = std::make_unique<uint8_t[]>(size);
    random_buffer_1_ = std::make_unique<uint8_t[]>(size_);
    random_buffer_2_ = std::make_unique<uint8_t[]>(size_);
    system_buffer_ = std::make_unique<uint8_t[]>(size_);
    product_buffer_ = std::make_unique<uint8_t[]>(size_);
    zero_buffer_ = std::make_unique<uint8_t[]>(size_);

    // Fill random data
    for (size_t j = 0; j < (size / 1_MiB); j++) {
        ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer_1.get() + offset, 1_MiB, 0), true);
    for (size_t j = 0; j < (size_ / 1_MiB); j++) {
        ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer_1_.get() + offset, 1_MiB, 0),
                  true);

        ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer_2.get() + offset, 1_MiB, 0), true);
        ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer_2_.get() + offset, 1_MiB, 0),
                  true);

        offset += 1_MiB;
    }

    sys_fd.reset(open("/dev/block/mapper/system_a", O_RDONLY));
    ASSERT_TRUE(sys_fd > 0);
    sys_fd_.reset(open("/dev/block/mapper/system_a", O_RDONLY));
    ASSERT_TRUE(sys_fd_ > 0);

    product_fd_.reset(open("/dev/block/mapper/product_a", O_RDONLY));
    ASSERT_TRUE(product_fd_ > 0);

    // Read from system partition from offset 0 of size 100MB
    ASSERT_EQ(ReadFullyAtOffset(sys_fd_, system_buffer_.get(), size_, 0), true);

    // Read from system partition from offset 0 of size 100MB
    ASSERT_EQ(ReadFullyAtOffset(sys_fd, system_buffer.get(), size, 0), true);
    ASSERT_EQ(ReadFullyAtOffset(product_fd_, product_buffer_.get(), size_, 0), true);
}

void SnapuserdTest::CreateCowDevice(std::unique_ptr<TemporaryFile>& cow) {
    //================Create a COW file with the following operations===========
    //
    // Create COW file which is gz compressed
@@ -96,12 +130,12 @@ TEST_F(SnapuserdTest, ReadWrite) {
    options.compression = "gz";
    CowWriter writer(options);

    ASSERT_TRUE(writer.Initialize(cow_->fd));
    ASSERT_TRUE(writer.Initialize(cow->fd));

    // Write 100MB random data to COW file which is gz compressed from block 0
    ASSERT_TRUE(writer.AddRawBlocks(0, random_buffer_1.get(), size));
    ASSERT_TRUE(writer.AddRawBlocks(0, random_buffer_1_.get(), size_));

    size_t num_blocks = size / options.block_size;
    size_t num_blocks = size_ / options.block_size;
    size_t blk_start_copy = num_blocks;
    size_t blk_end_copy = blk_start_copy + num_blocks;
    size_t source_blk = 0;
@@ -110,7 +144,7 @@ TEST_F(SnapuserdTest, ReadWrite) {
    // has to read from block 0 in system_a partition
    //
    // This initializes copy operation from block 0 of size 100 MB from
    // /dev/block/mapper/system_a
    // /dev/block/mapper/system_a or product_a
    for (size_t i = blk_start_copy; i < blk_end_copy; i++) {
        ASSERT_TRUE(writer.AddCopy(i, source_blk));
        source_blk += 1;
@@ -125,14 +159,17 @@ TEST_F(SnapuserdTest, ReadWrite) {
    // Final 100MB filled with random data which is gz compressed
    size_t blk_random2_replace_start = blk_zero_copy_end;

    ASSERT_TRUE(writer.AddRawBlocks(blk_random2_replace_start, random_buffer_2.get(), size));
    ASSERT_TRUE(writer.AddRawBlocks(blk_random2_replace_start, random_buffer_2_.get(), size_));

    // Flush operations
    ASSERT_TRUE(writer.Finalize());

    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
    ASSERT_EQ(lseek(cow->fd, 0, SEEK_SET), 0);
}

    //================Setup dm-snapshot and start snapuserd daemon===========
void SnapuserdTest::CreateSystemDmUser() {
    unique_fd system_a_fd;
    std::string cmd;

    // Create a COW device. Number of sectors is chosen random which can
    // hold at least 400MB of data
@@ -140,39 +177,77 @@ TEST_F(SnapuserdTest, ReadWrite) {
    system_a_fd.reset(open("/dev/block/mapper/system_a", O_RDONLY));
    ASSERT_TRUE(system_a_fd > 0);

    int blksize;
    int err = ioctl(system_a_fd.get(), BLKGETSIZE, &blksize);
    if (err < 0) {
        ASSERT_TRUE(0);
    int err = ioctl(system_a_fd.get(), BLKGETSIZE, &system_blksize_);
    ASSERT_GE(err, 0);

    std::string str(cow_system_->path);
    std::size_t found = str.find_last_of("/\\");
    ASSERT_NE(found, std::string::npos);
    system_device_name_ = str.substr(found + 1);
    cmd = "dmctl create " + system_device_name_ + " user 0 " + std::to_string(system_blksize_);

    system(cmd.c_str());
}

    cmd = "dmctl create system_cow user 0 " + std::to_string(blksize);
void SnapuserdTest::CreateProductDmUser() {
    unique_fd product_a_fd;
    std::string cmd;

    // Create a COW device. Number of sectors is chosen random which can
    // hold at least 400MB of data

    product_a_fd.reset(open("/dev/block/mapper/product_a", O_RDONLY));
    ASSERT_TRUE(product_a_fd > 0);

    int err = ioctl(product_a_fd.get(), BLKGETSIZE, &product_blksize_);
    ASSERT_GE(err, 0);

    std::string str(cow_product_->path);
    std::size_t found = str.find_last_of("/\\");
    ASSERT_NE(found, std::string::npos);
    product_device_name_ = str.substr(found + 1);
    cmd = "dmctl create " + product_device_name_ + " user 0 " + std::to_string(product_blksize_);

    system(cmd.c_str());
}

void SnapuserdTest::StartSnapuserdDaemon() {
    // Start the snapuserd daemon
    if (fork() == 0) {
        const char* argv[] = {"/system/bin/snapuserd", cow_->path, "/dev/block/mapper/system_a",
                              nullptr};
        const char* argv[] = {"/system/bin/snapuserd",       cow_system_->path,
                              "/dev/block/mapper/system_a",  cow_product_->path,
                              "/dev/block/mapper/product_a", nullptr};
        if (execv(argv[0], const_cast<char**>(argv))) {
            ASSERT_TRUE(0);
        }
    }
}

    cmd.clear();
void SnapuserdTest::CreateSnapshotDevices() {
    std::string cmd;

    cmd = "dmctl create system-snapshot -ro snapshot 0 " + std::to_string(blksize);
    cmd += " /dev/block/mapper/system_a /dev/block/mapper/system_cow ";
    cmd = "dmctl create system-snapshot -ro snapshot 0 " + std::to_string(system_blksize_);
    cmd += " /dev/block/mapper/system_a";
    cmd += " /dev/block/mapper/" + system_device_name_;
    cmd += " P 8";

    system(cmd.c_str());

    // Wait so that snapshot device is created
    sleep(5);
    std::unique_ptr<uint8_t[]> snapuserd_buffer = std::make_unique<uint8_t[]>(size);
    cmd.clear();

    offset = 0;
    cmd = "dmctl create product-snapshot -ro snapshot 0 " + std::to_string(product_blksize_);
    cmd += " /dev/block/mapper/product_a";
    cmd += " /dev/block/mapper/" + product_device_name_;
    cmd += " P 8";

    snapshot_fd.reset(open("/dev/block/mapper/system-snapshot", O_RDONLY));
    ASSERT_TRUE(snapshot_fd > 0);
    system(cmd.c_str());
}

void SnapuserdTest::TestIO(unique_fd& snapshot_fd, std::unique_ptr<uint8_t[]>&& buf) {
    loff_t offset = 0;
    std::unique_ptr<uint8_t[]> buffer = std::move(buf);

    std::unique_ptr<uint8_t[]> snapuserd_buffer = std::make_unique<uint8_t[]>(size_);

    //================Start IO operation on dm-snapshot device=================
    // This will test the following paths:
@@ -189,16 +264,16 @@ TEST_F(SnapuserdTest, ReadWrite) {
    // dm-snap->dm-snap-persistent->dm-user->snapuserd->read_compressed_cow (replace
    // op)->decompress_cow->return

    ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size, offset), true);
    ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size_, offset), true);

    // Update the offset
    offset += size;
    offset += size_;

    // Compare data with random_buffer_1.
    ASSERT_EQ(memcmp(snapuserd_buffer.get(), random_buffer_1.get(), size), 0);
    // Compare data with random_buffer_1_.
    ASSERT_EQ(memcmp(snapuserd_buffer.get(), random_buffer_1_.get(), size_), 0);

    // Clear the buffer
    memset(snapuserd_buffer.get(), 0, size);
    memset(snapuserd_buffer.get(), 0, size_);

    // Read from snapshot device of size 100MB from offset 100MB. This tests the
    // copy operation.
@@ -207,13 +282,13 @@ TEST_F(SnapuserdTest, ReadWrite) {
    //
    // dm-snap->dm-snap-persistent->dm-user->snapuserd->read_from_system_a_partition
    // (copy op) -> return
    ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size, offset), true);
    ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size_, offset), true);

    // Update the offset
    offset += size;
    offset += size_;

    // Compare data with system_buffer.
    ASSERT_EQ(memcmp(snapuserd_buffer.get(), system_buffer.get(), size), 0);
    // Compare data with buffer.
    ASSERT_EQ(memcmp(snapuserd_buffer.get(), buffer.get(), size_), 0);

    // Read from snapshot device of size 100MB from offset 200MB. This tests the
    // zero operation.
@@ -222,16 +297,13 @@ TEST_F(SnapuserdTest, ReadWrite) {
    //
    // dm-snap->dm-snap-persistent->dm-user->snapuserd->fill_memory_with_zero
    // (zero op) -> return
    ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size, offset), true);

    // Fill the random_buffer_1 with zero as we no longer need it
    memset(random_buffer_1.get(), 0, size);
    ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size_, offset), true);

    // Compare data with zero filled buffer
    ASSERT_EQ(memcmp(snapuserd_buffer.get(), random_buffer_1.get(), size), 0);
    ASSERT_EQ(memcmp(snapuserd_buffer.get(), zero_buffer_.get(), size_), 0);

    // Update the offset
    offset += size;
    offset += size_;

    // Read from snapshot device of size 100MB from offset 300MB. This tests the
    // final replace operation.
@@ -240,10 +312,34 @@ TEST_F(SnapuserdTest, ReadWrite) {
    //
    // dm-snap->dm-snap-persistent->dm-user->snapuserd->read_compressed_cow (replace
    // op)->decompress_cow->return
    ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size, offset), true);
    ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size_, offset), true);

    // Compare data with random_buffer_2_.
    ASSERT_EQ(memcmp(snapuserd_buffer.get(), random_buffer_2_.get(), size_), 0);
}

TEST_F(SnapuserdTest, ReadWrite) {
    unique_fd snapshot_fd;

    Init();

    // Compare data with random_buffer_2.
    ASSERT_EQ(memcmp(snapuserd_buffer.get(), random_buffer_2.get(), size), 0);
    CreateCowDevice(cow_system_);
    CreateCowDevice(cow_product_);

    CreateSystemDmUser();
    CreateProductDmUser();

    StartSnapuserdDaemon();

    CreateSnapshotDevices();

    snapshot_fd.reset(open("/dev/block/mapper/system-snapshot", O_RDONLY));
    ASSERT_TRUE(snapshot_fd > 0);
    TestIO(snapshot_fd, std::move(system_buffer_));

    snapshot_fd.reset(open("/dev/block/mapper/product-snapshot", O_RDONLY));
    ASSERT_TRUE(snapshot_fd > 0);
    TestIO(snapshot_fd, std::move(product_buffer_));
}

}  // namespace snapshot
+99 −5
Original line number Diff line number Diff line
@@ -17,10 +17,12 @@
#include <linux/types.h>
#include <stdlib.h>

#include <csignal>
#include <cstring>
#include <iostream>
#include <limits>
#include <string>
#include <thread>
#include <vector>

#include <android-base/file.h>
@@ -35,6 +37,7 @@
namespace android {
namespace snapshot {

using namespace android;
using namespace android::dm;
using android::base::unique_fd;

@@ -45,6 +48,60 @@ static constexpr size_t PAYLOAD_SIZE = (1UL << 16);

static_assert(PAYLOAD_SIZE >= BLOCK_SIZE);

class Target {
  public:
    // Represents an already-created Target, which is referenced by UUID.
    Target(std::string uuid) : uuid_(uuid) {}

    const auto& uuid() { return uuid_; }
    std::string control_path() { return std::string("/dev/dm-user-") + uuid(); }

  private:
    const std::string uuid_;
};

class Daemon {
    // The Daemon class is a singleton to avoid
    // instantiating more than once
  public:
    static Daemon& Instance() {
        static Daemon instance;
        return instance;
    }

    bool IsRunning();

  private:
    bool is_running_;

    Daemon();
    Daemon(Daemon const&) = delete;
    void operator=(Daemon const&) = delete;

    static void SignalHandler(int signal);
};

Daemon::Daemon() {
    is_running_ = true;
    signal(SIGINT, Daemon::SignalHandler);
    signal(SIGTERM, Daemon::SignalHandler);
}

bool Daemon::IsRunning() {
    return is_running_;
}

void Daemon::SignalHandler(int signal) {
    LOG(DEBUG) << "Snapuserd received signal: " << signal;
    switch (signal) {
        case SIGINT:
        case SIGTERM: {
            Daemon::Instance().is_running_ = false;
            break;
        }
    }
}

class BufferSink : public IByteSink {
  public:
    void Initialize(size_t size) {
@@ -558,10 +615,26 @@ int Snapuserd::Run() {
        return 1;
    }

    // TODO: use UUID to support multiple partitions
    ctrl_fd_.reset(open("/dev/dm-user", O_RDWR));
    std::string str(in_cow_device_);
    std::size_t found = str.find_last_of("/\\");
    CHECK(found != std::string::npos);
    std::string device_name = str.substr(found + 1);

    LOG(DEBUG) << "Fetching UUID for: " << device_name;

    auto& dm = dm::DeviceMapper::Instance();
    std::string uuid;
    if (!dm.GetDmDeviceUuidByName(device_name, &uuid)) {
        LOG(ERROR) << "Unable to find UUID for " << in_cow_device_;
        return 1;
    }

    LOG(DEBUG) << "UUID: " << uuid;
    Target t(uuid);

    ctrl_fd_.reset(open(t.control_path().c_str(), O_RDWR));
    if (ctrl_fd_ < 0) {
        LOG(ERROR) << "Unable to open /dev/dm-user";
        LOG(ERROR) << "Unable to open " << t.control_path();
        return 1;
    }

@@ -682,9 +755,30 @@ int Snapuserd::Run() {
}  // namespace snapshot
}  // namespace android

void run_thread(std::string cow_device, std::string backing_device) {
    android::snapshot::Snapuserd snapd(cow_device, backing_device);
    snapd.Run();
}

int main([[maybe_unused]] int argc, char** argv) {
    android::base::InitLogging(argv, &android::base::KernelLogger);
    android::snapshot::Snapuserd snapd(argv[1], argv[2]);

    return snapd.Run();
    android::snapshot::Daemon& daemon = android::snapshot::Daemon::Instance();

    while (daemon.IsRunning()) {
        // TODO: This is hardcoded wherein:
        // argv[1] = system_cow, argv[2] = /dev/block/mapper/system_a
        // argv[3] = product_cow, argv[4] = /dev/block/mapper/product_a
        //
        // This should be fixed based on some kind of IPC or setup a
        // command socket and spin up the thread based when a new
        // partition is visible.
        std::thread system_a(run_thread, argv[1], argv[2]);
        std::thread product_a(run_thread, argv[3], argv[4]);

        system_a.join();
        product_a.join();
    }

    return 0;
}
+6 −4
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@
#include <vector>

using namespace std::literals::string_literals;
using namespace std::chrono_literals;
using namespace android::dm;
using DmBlockDevice = ::android::dm::DeviceMapper::DmBlockDevice;

@@ -242,8 +243,9 @@ static int DmCreateCmdHandler(int argc, char** argv) {
        return ret;
    }

    std::string ignore_path;
    DeviceMapper& dm = DeviceMapper::Instance();
    if (!dm.CreateDevice(name, table)) {
    if (!dm.CreateDevice(name, table, &ignore_path, 5s)) {
        std::cerr << "Failed to create device-mapper device with name: " << name << std::endl;
        return -EIO;
    }
@@ -392,7 +394,7 @@ static int GetPathCmdHandler(int argc, char** argv) {
    return 0;
}

static int GetUUIDCmdHandler(int argc, char** argv) {
static int GetUuidCmdHandler(int argc, char** argv) {
    if (argc != 1) {
        std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
        return -EINVAL;
@@ -400,7 +402,7 @@ static int GetUUIDCmdHandler(int argc, char** argv) {

    DeviceMapper& dm = DeviceMapper::Instance();
    std::string uuid;
    if (!dm.GetDmDeviceUUIDByName(argv[0], &uuid)) {
    if (!dm.GetDmDeviceUuidByName(argv[0], &uuid)) {
        std::cerr << "Could not query uuid of device \"" << argv[0] << "\"." << std::endl;
        return -EINVAL;
    }
@@ -521,7 +523,7 @@ static std::map<std::string, std::function<int(int, char**)>> cmdmap = {
        {"list", DmListCmdHandler},
        {"help", HelpCmdHandler},
        {"getpath", GetPathCmdHandler},
        {"getuuid", GetUUIDCmdHandler},
        {"getuuid", GetUuidCmdHandler},
        {"info", InfoCmdHandler},
        {"table", TableCmdHandler},
        {"status", StatusCmdHandler},