Loading fs_mgr/libdm/dm.cpp +1 −1 Original line number Diff line number Diff line Loading @@ -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) { Loading fs_mgr/libdm/include/libdm/dm.h +1 −1 Original line number Diff line number Diff line Loading @@ -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 Loading fs_mgr/libsnapshot/cow_snapuserd_test.cpp +165 −69 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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; Loading @@ -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; Loading @@ -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 Loading @@ -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: Loading @@ -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. Loading @@ -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. Loading @@ -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. Loading @@ -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 Loading fs_mgr/libsnapshot/snapuserd.cpp +99 −5 Original line number Diff line number Diff line Loading @@ -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> Loading @@ -35,6 +37,7 @@ namespace android { namespace snapshot { using namespace android; using namespace android::dm; using android::base::unique_fd; Loading @@ -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) { Loading Loading @@ -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; } Loading Loading @@ -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; } fs_mgr/tools/dmctl.cpp +6 −4 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; } Loading Loading @@ -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; Loading @@ -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; } Loading Loading @@ -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}, Loading Loading
fs_mgr/libdm/dm.cpp +1 −1 Original line number Diff line number Diff line Loading @@ -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) { Loading
fs_mgr/libdm/include/libdm/dm.h +1 −1 Original line number Diff line number Diff line Loading @@ -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 Loading
fs_mgr/libsnapshot/cow_snapuserd_test.cpp +165 −69 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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; Loading @@ -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; Loading @@ -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 Loading @@ -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: Loading @@ -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. Loading @@ -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. Loading @@ -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. Loading @@ -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 Loading
fs_mgr/libsnapshot/snapuserd.cpp +99 −5 Original line number Diff line number Diff line Loading @@ -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> Loading @@ -35,6 +37,7 @@ namespace android { namespace snapshot { using namespace android; using namespace android::dm; using android::base::unique_fd; Loading @@ -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) { Loading Loading @@ -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; } Loading Loading @@ -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; }
fs_mgr/tools/dmctl.cpp +6 −4 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; } Loading Loading @@ -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; Loading @@ -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; } Loading Loading @@ -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}, Loading