From 333639e952b685745dd3cee9f4a663ceb898ac88 Mon Sep 17 00:00:00 2001 From: Akilesh Kailash Date: Tue, 24 Nov 2020 03:30:23 +0000 Subject: [PATCH] libsnaphot: Refactor cow_snapuserd test Refact cow_snapuserd unit test. This will test code changes in daemon in isolation. Currently it tests 1: Reading snapshot device and validate data 2: Merge and validate data. More tests related to merge will be added later; but this should provide a baseline unit test for daemon. BUG: 168311203 Test: cow_snapuserd_test Signed-off-by: Akilesh Kailash Change-Id: I4d77c435a9f045204a4ff3ece524c857f9fba50f --- fs_mgr/libsnapshot/Android.bp | 1 + fs_mgr/libsnapshot/cow_snapuserd_test.cpp | 635 ++++++++++------------ fs_mgr/libsnapshot/snapuserd_client.cpp | 3 +- 3 files changed, 297 insertions(+), 342 deletions(-) diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp index 34049d41c..e32fde062 100644 --- a/fs_mgr/libsnapshot/Android.bp +++ b/fs_mgr/libsnapshot/Android.bp @@ -563,6 +563,7 @@ cc_test { "libsnapshot_snapuserd", "libcutils_sockets", "libz", + "libfs_mgr", "libdm", ], header_libs: [ diff --git a/fs_mgr/libsnapshot/cow_snapuserd_test.cpp b/fs_mgr/libsnapshot/cow_snapuserd_test.cpp index 5483fd06f..16d931378 100644 --- a/fs_mgr/libsnapshot/cow_snapuserd_test.cpp +++ b/fs_mgr/libsnapshot/cow_snapuserd_test.cpp @@ -12,10 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include #include +#include #include #include +#include #include +#include #include #include @@ -24,7 +28,9 @@ #include #include +#include #include +#include #include #include #include @@ -37,111 +43,203 @@ using namespace android::storage_literals; using android::base::unique_fd; using LoopDevice = android::dm::LoopDevice; using namespace std::chrono_literals; +using namespace android::dm; +using namespace std; -class SnapuserdTest : public ::testing::Test { - protected: - void SetUp() override { - // TODO: Daemon started through first stage - // init does not have permission to read files - // from /data/nativetest. - system("setenforce 0"); - cow_system_ = std::make_unique(); - ASSERT_GE(cow_system_->fd, 0) << strerror(errno); +static constexpr char kSnapuserdSocketTest[] = "snapuserdTest"; - cow_product_ = std::make_unique(); - ASSERT_GE(cow_product_->fd, 0) << strerror(errno); +class TempDevice { + public: + TempDevice(const std::string& name, const DmTable& table) + : dm_(DeviceMapper::Instance()), name_(name), valid_(false) { + valid_ = dm_.CreateDevice(name, table, &path_, std::chrono::seconds(5)); + } + TempDevice(TempDevice&& other) noexcept + : dm_(other.dm_), name_(other.name_), path_(other.path_), valid_(other.valid_) { + other.valid_ = false; + } + ~TempDevice() { + if (valid_) { + dm_.DeleteDevice(name_); + } + } + bool Destroy() { + if (!valid_) { + return false; + } + valid_ = false; + return dm_.DeleteDevice(name_); + } + const std::string& path() const { return path_; } + const std::string& name() const { return name_; } + bool valid() const { return valid_; } - cow_system_1_ = std::make_unique(); - ASSERT_GE(cow_system_1_->fd, 0) << strerror(errno); + TempDevice(const TempDevice&) = delete; + TempDevice& operator=(const TempDevice&) = delete; - cow_product_1_ = std::make_unique(); - ASSERT_GE(cow_product_1_->fd, 0) << strerror(errno); - - std::string path = android::base::GetExecutableDirectory(); - - system_a_ = std::make_unique(path); - ASSERT_GE(system_a_->fd, 0) << strerror(errno); - - product_a_ = std::make_unique(path); - ASSERT_GE(product_a_->fd, 0) << strerror(errno); - - size_ = 100_MiB; + TempDevice& operator=(TempDevice&& other) noexcept { + name_ = other.name_; + valid_ = other.valid_; + other.valid_ = false; + return *this; } - void TearDown() override { - system("setenforce 1"); - cow_system_ = nullptr; - cow_product_ = nullptr; - - cow_system_1_ = nullptr; - cow_product_1_ = nullptr; - } - - std::unique_ptr system_a_; - std::unique_ptr product_a_; - - std::unique_ptr system_a_loop_; - std::unique_ptr product_a_loop_; - - std::unique_ptr cow_system_; - std::unique_ptr cow_product_; - - std::unique_ptr cow_system_1_; - std::unique_ptr cow_product_1_; - - unique_fd sys_fd_; - unique_fd product_fd_; - size_t size_; - - int system_blksize_; - int product_blksize_; - std::string system_device_name_; - std::string product_device_name_; - - std::string system_device_ctrl_name_; - std::string product_device_ctrl_name_; - - std::unique_ptr random_buffer_1_; - std::unique_ptr random_buffer_2_; - std::unique_ptr zero_buffer_; - std::unique_ptr system_buffer_; - std::unique_ptr product_buffer_; - - void Init(); - void InitCowDevices(); - void InitDaemon(); - void CreateCowDevice(std::unique_ptr& cow); - void CreateSystemDmUser(std::unique_ptr& cow); - void CreateProductDmUser(std::unique_ptr& cow); - void DeleteDmUser(std::unique_ptr& cow, std::string snapshot_device); - void StartSnapuserdDaemon(); - void CreateSnapshotDevices(); - void SwitchSnapshotDevices(); - - std::string GetSystemControlPath() { - return std::string("/dev/dm-user/") + system_device_ctrl_name_; - } - std::string GetProductControlPath() { - return std::string("/dev/dm-user/") + product_device_ctrl_name_; - } - - void TestIO(unique_fd& snapshot_fd, std::unique_ptr& buffer); - std::unique_ptr client_; + private: + DeviceMapper& dm_; + std::string name_; + std::string path_; + bool valid_; }; -void SnapuserdTest::Init() { +class CowSnapuserdTest final { + public: + bool Setup(); + bool Merge(); + void ValidateMerge(); + void ReadSnapshotDeviceAndValidate(); + void Shutdown(); + + std::string snapshot_dev() const { return snapshot_dev_->path(); } + + static const uint64_t kSectorSize = 512; + + private: + void SetupImpl(); + void MergeImpl(); + void CreateCowDevice(); + void CreateBaseDevice(); + void InitCowDevice(); + void SetDeviceControlName(); + void InitDaemon(); + void CreateDmUserDevice(); + void StartSnapuserdDaemon(); + void CreateSnapshotDevice(); + unique_fd CreateTempFile(const std::string& name, size_t size); + + unique_ptr base_loop_; + unique_ptr dmuser_dev_; + unique_ptr snapshot_dev_; + + std::string system_device_ctrl_name_; + std::string system_device_name_; + + unique_fd base_fd_; + std::unique_ptr cow_system_; + std::unique_ptr client_; + std::unique_ptr orig_buffer_; + std::unique_ptr merged_buffer_; + bool setup_ok_ = false; + bool merge_ok_ = false; + size_t size_ = 1_MiB; + int cow_num_sectors_; + int total_base_size_; +}; + +unique_fd CowSnapuserdTest::CreateTempFile(const std::string& name, size_t size) { + unique_fd fd(syscall(__NR_memfd_create, name.c_str(), MFD_ALLOW_SEALING)); + if (fd < 0) { + return {}; + } + if (size) { + if (ftruncate(fd, size) < 0) { + perror("ftruncate"); + return {}; + } + if (fcntl(fd, F_ADD_SEALS, F_SEAL_GROW | F_SEAL_SHRINK) < 0) { + perror("fcntl"); + return {}; + } + } + return fd; +} + +void CowSnapuserdTest::Shutdown() { + ASSERT_TRUE(client_->StopSnapuserd()); + ASSERT_TRUE(snapshot_dev_->Destroy()); + ASSERT_TRUE(dmuser_dev_->Destroy()); +} + +bool CowSnapuserdTest::Setup() { + SetupImpl(); + return setup_ok_; +} + +void CowSnapuserdTest::StartSnapuserdDaemon() { + pid_t pid = fork(); + ASSERT_GE(pid, 0); + if (pid == 0) { + std::string arg0 = "/system/bin/snapuserd"; + std::string arg1 = kSnapuserdSocketTest; + char* const argv[] = {arg0.data(), arg1.data(), nullptr}; + ASSERT_GE(execv(arg0.c_str(), argv), 0); + } else { + client_ = SnapuserdClient::Connect(kSnapuserdSocketTest, 10s); + ASSERT_NE(client_, nullptr); + } +} + +void CowSnapuserdTest::CreateBaseDevice() { unique_fd rnd_fd; - loff_t offset = 0; - std::unique_ptr random_buffer = std::make_unique(1_MiB); + + total_base_size_ = (size_ * 4); + base_fd_ = CreateTempFile("base_device", total_base_size_); + ASSERT_GE(base_fd_, 0); rnd_fd.reset(open("/dev/random", O_RDONLY)); ASSERT_TRUE(rnd_fd > 0); - random_buffer_1_ = std::make_unique(size_); - random_buffer_2_ = std::make_unique(size_); - system_buffer_ = std::make_unique(size_); - product_buffer_ = std::make_unique(size_); - zero_buffer_ = std::make_unique(size_); + std::unique_ptr random_buffer = std::make_unique(1_MiB); + + for (size_t j = 0; j < ((total_base_size_) / 1_MiB); j++) { + ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer.get(), 1_MiB, 0), true); + ASSERT_EQ(android::base::WriteFully(base_fd_, random_buffer.get(), 1_MiB), true); + } + + ASSERT_EQ(lseek(base_fd_, 0, SEEK_SET), 0); + + base_loop_ = std::make_unique(base_fd_, 10s); + ASSERT_TRUE(base_loop_->valid()); +} + +void CowSnapuserdTest::ReadSnapshotDeviceAndValidate() { + unique_fd snapshot_fd(open(snapshot_dev_->path().c_str(), O_RDONLY)); + ASSERT_TRUE(snapshot_fd > 0); + + std::unique_ptr snapuserd_buffer = std::make_unique(size_); + + // COPY + loff_t offset = 0; + ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size_, offset), true); + ASSERT_EQ(memcmp(snapuserd_buffer.get(), orig_buffer_.get(), size_), 0); + + // REPLACE + offset += size_; + ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size_, offset), true); + ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + size_, size_), 0); + + // ZERO + offset += size_; + ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size_, offset), true); + ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + (size_ * 2), size_), 0); + + // REPLACE + offset += size_; + ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size_, offset), true); + ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + (size_ * 3), size_), 0); +} + +void CowSnapuserdTest::CreateCowDevice() { + unique_fd rnd_fd; + loff_t offset = 0; + + std::string path = android::base::GetExecutableDirectory(); + cow_system_ = std::make_unique(path); + + rnd_fd.reset(open("/dev/random", O_RDONLY)); + ASSERT_TRUE(rnd_fd > 0); + + std::unique_ptr random_buffer_1_ = std::make_unique(size_); + std::unique_ptr random_buffer_2_ = std::make_unique(size_); // Fill random data for (size_t j = 0; j < (size_ / 1_MiB); j++) { @@ -154,77 +252,32 @@ void SnapuserdTest::Init() { offset += 1_MiB; } - for (size_t j = 0; j < (800_MiB / 1_MiB); j++) { - ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer.get(), 1_MiB, 0), true); - ASSERT_EQ(android::base::WriteFully(system_a_->fd, random_buffer.get(), 1_MiB), true); - } - - for (size_t j = 0; j < (800_MiB / 1_MiB); j++) { - ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer.get(), 1_MiB, 0), true); - ASSERT_EQ(android::base::WriteFully(product_a_->fd, random_buffer.get(), 1_MiB), true); - } - - // Create loopback devices - system_a_loop_ = std::make_unique(std::string(system_a_->path), 10s); - ASSERT_TRUE(system_a_loop_->valid()); - - product_a_loop_ = std::make_unique(std::string(product_a_->path), 10s); - ASSERT_TRUE(product_a_loop_->valid()); - - sys_fd_.reset(open(system_a_loop_->device().c_str(), O_RDONLY)); - ASSERT_TRUE(sys_fd_ > 0); - - product_fd_.reset(open(product_a_loop_->device().c_str(), 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 product partition from offset 0 of size 100MB - ASSERT_EQ(ReadFullyAtOffset(product_fd_, product_buffer_.get(), size_, 0), true); -} - -void SnapuserdTest::CreateCowDevice(std::unique_ptr& cow) { - //================Create a COW file with the following operations=========== - // - // Create COW file which is gz compressed - // - // 0-100 MB of replace operation with random data - // 100-200 MB of copy operation - // 200-300 MB of zero operation - // 300-400 MB of replace operation with random data - CowOptions options; options.compression = "gz"; CowWriter writer(options); - 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.Initialize(cow_system_->fd)); 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 blk_src_copy = num_blocks; + size_t blk_end_copy = blk_src_copy + num_blocks; size_t source_blk = 0; - // Copy blocks - source_blk starts from 0 as snapuserd - // 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 or product_a - for (size_t i = blk_start_copy; i < blk_end_copy; i++) { - ASSERT_TRUE(writer.AddCopy(i, source_blk)); + while (source_blk < num_blocks) { + ASSERT_TRUE(writer.AddCopy(source_blk, blk_src_copy)); source_blk += 1; + blk_src_copy += 1; } - size_t blk_zero_copy_start = blk_end_copy; + ASSERT_EQ(blk_src_copy, blk_end_copy); + + ASSERT_TRUE(writer.AddRawBlocks(source_blk, random_buffer_1_.get(), size_)); + + size_t blk_zero_copy_start = source_blk + num_blocks; size_t blk_zero_copy_end = blk_zero_copy_start + num_blocks; - // 100 MB filled with zeroes ASSERT_TRUE(writer.AddZeroBlocks(blk_zero_copy_start, num_blocks)); - // 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_)); @@ -232,233 +285,133 @@ void SnapuserdTest::CreateCowDevice(std::unique_ptr& cow) { // Flush operations ASSERT_TRUE(writer.Finalize()); - ASSERT_EQ(lseek(cow->fd, 0, SEEK_SET), 0); + // Construct the buffer required for validation + orig_buffer_ = std::make_unique(total_base_size_); + std::string zero_buffer(size_, 0); + + ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), size_, size_), true); + memcpy((char*)orig_buffer_.get() + size_, random_buffer_1_.get(), size_); + memcpy((char*)orig_buffer_.get() + (size_ * 2), (void*)zero_buffer.c_str(), size_); + memcpy((char*)orig_buffer_.get() + (size_ * 3), random_buffer_2_.get(), size_); } -void SnapuserdTest::CreateSystemDmUser(std::unique_ptr& cow) { - std::string cmd; +void CowSnapuserdTest::InitCowDevice() { + cow_num_sectors_ = client_->InitDmUserCow(system_device_ctrl_name_, cow_system_->path, + base_loop_->device()); + ASSERT_NE(cow_num_sectors_, 0); +} + +void CowSnapuserdTest::SetDeviceControlName() { system_device_name_.clear(); system_device_ctrl_name_.clear(); - std::string str(cow->path); + 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); - // Create a control device system_device_ctrl_name_ = system_device_name_ + "-ctrl"; - cmd = "dmctl create " + system_device_name_ + " user 0 " + std::to_string(system_blksize_); - cmd += " " + system_device_ctrl_name_; - - system(cmd.c_str()); } -void SnapuserdTest::DeleteDmUser(std::unique_ptr& cow, std::string snapshot_device) { - std::string cmd; +void CowSnapuserdTest::CreateDmUserDevice() { + DmTable dmuser_table; + ASSERT_TRUE(dmuser_table.AddTarget( + std::make_unique(0, cow_num_sectors_, system_device_ctrl_name_))); + ASSERT_TRUE(dmuser_table.valid()); - cmd = "dmctl delete " + snapshot_device; - system(cmd.c_str()); + dmuser_dev_ = std::make_unique(system_device_name_, dmuser_table); + ASSERT_TRUE(dmuser_dev_->valid()); + ASSERT_FALSE(dmuser_dev_->path().empty()); - cmd.clear(); - - std::string str(cow->path); - std::size_t found = str.find_last_of("/\\"); - ASSERT_NE(found, std::string::npos); - std::string device_name = str.substr(found + 1); - - cmd = "dmctl delete " + device_name; - - system(cmd.c_str()); + auto misc_device = "/dev/dm-user/" + system_device_ctrl_name_; + ASSERT_TRUE(android::fs_mgr::WaitForFile(misc_device, 10s)); } -void SnapuserdTest::CreateProductDmUser(std::unique_ptr& cow) { - std::string cmd; - product_device_name_.clear(); - product_device_ctrl_name_.clear(); - - std::string str(cow->path); - std::size_t found = str.find_last_of("/\\"); - ASSERT_NE(found, std::string::npos); - product_device_name_ = str.substr(found + 1); - product_device_ctrl_name_ = product_device_name_ + "-ctrl"; - cmd = "dmctl create " + product_device_name_ + " user 0 " + std::to_string(product_blksize_); - cmd += " " + product_device_ctrl_name_; - - system(cmd.c_str()); -} - -void SnapuserdTest::InitCowDevices() { - system_blksize_ = client_->InitDmUserCow(system_device_ctrl_name_, cow_system_->path, - system_a_loop_->device()); - ASSERT_NE(system_blksize_, 0); - - product_blksize_ = client_->InitDmUserCow(product_device_ctrl_name_, cow_product_->path, - product_a_loop_->device()); - ASSERT_NE(product_blksize_, 0); -} - -void SnapuserdTest::InitDaemon() { +void CowSnapuserdTest::InitDaemon() { bool ok = client_->AttachDmUser(system_device_ctrl_name_); ASSERT_TRUE(ok); - - ok = client_->AttachDmUser(product_device_ctrl_name_); - ASSERT_TRUE(ok); } -void SnapuserdTest::StartSnapuserdDaemon() { - ASSERT_TRUE(EnsureSnapuserdStarted()); +void CowSnapuserdTest::CreateSnapshotDevice() { + DmTable snap_table; + ASSERT_TRUE(snap_table.AddTarget(std::make_unique( + 0, total_base_size_ / kSectorSize, base_loop_->device(), dmuser_dev_->path(), + SnapshotStorageMode::Persistent, 8))); + ASSERT_TRUE(snap_table.valid()); - client_ = SnapuserdClient::Connect(kSnapuserdSocket, 5s); - ASSERT_NE(client_, nullptr); + snap_table.set_readonly(true); + + snapshot_dev_ = std::make_unique("cowsnapuserd-test-dm-snapshot", snap_table); + ASSERT_TRUE(snapshot_dev_->valid()); + ASSERT_FALSE(snapshot_dev_->path().empty()); } -void SnapuserdTest::CreateSnapshotDevices() { - std::string cmd; +void CowSnapuserdTest::SetupImpl() { + CreateBaseDevice(); + CreateCowDevice(); - cmd = "dmctl create system-snapshot -ro snapshot 0 " + std::to_string(system_blksize_); - cmd += " " + system_a_loop_->device(); - cmd += " /dev/block/mapper/" + system_device_name_; - cmd += " P 8"; - - system(cmd.c_str()); - - cmd.clear(); - - cmd = "dmctl create product-snapshot -ro snapshot 0 " + std::to_string(product_blksize_); - cmd += " " + product_a_loop_->device(); - cmd += " /dev/block/mapper/" + product_device_name_; - cmd += " P 8"; - - system(cmd.c_str()); -} - -void SnapuserdTest::SwitchSnapshotDevices() { - std::string cmd; - - cmd = "dmctl create system-snapshot-1 -ro snapshot 0 " + std::to_string(system_blksize_); - cmd += " " + system_a_loop_->device(); - cmd += " /dev/block/mapper/" + system_device_name_; - cmd += " P 8"; - - system(cmd.c_str()); - - cmd.clear(); - - cmd = "dmctl create product-snapshot-1 -ro snapshot 0 " + std::to_string(product_blksize_); - cmd += " " + product_a_loop_->device(); - cmd += " /dev/block/mapper/" + product_device_name_; - cmd += " P 8"; - - system(cmd.c_str()); -} - -void SnapuserdTest::TestIO(unique_fd& snapshot_fd, std::unique_ptr& buffer) { - loff_t offset = 0; - // std::unique_ptr buffer = std::move(buf); - - std::unique_ptr snapuserd_buffer = std::make_unique(size_); - - //================Start IO operation on dm-snapshot device================= - // This will test the following paths: - // - // 1: IO path for all three operations and interleaving of operations. - // 2: Merging of blocks in kernel during metadata read - // 3: Bulk IO issued by kernel duing merge operation - - // Read from snapshot device of size 100MB from offset 0. This tests the - // 1st replace operation. - // - // IO path: - // - // 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); - - // Update the offset - offset += size_; - - // 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_); - - // Read from snapshot device of size 100MB from offset 100MB. This tests the - // copy operation. - // - // IO path: - // - // dm-snap->dm-snap-persistent->dm-user->snapuserd->read_from_(system_a/product_a) partition - // (copy op) -> return - ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapuserd_buffer.get(), size_, offset), true); - - // Update the offset - offset += size_; - - // 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. - // - // IO path: - // - // 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); - - // Compare data with zero filled buffer - ASSERT_EQ(memcmp(snapuserd_buffer.get(), zero_buffer_.get(), size_), 0); - - // Update the offset - offset += size_; - - // Read from snapshot device of size 100MB from offset 300MB. This tests the - // final replace operation. - // - // IO path: - // - // 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); - - // 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(); - - CreateCowDevice(cow_system_); - CreateCowDevice(cow_product_); + SetDeviceControlName(); StartSnapuserdDaemon(); - InitCowDevices(); - - CreateSystemDmUser(cow_system_); - CreateProductDmUser(cow_product_); + InitCowDevice(); + CreateDmUserDevice(); InitDaemon(); - CreateSnapshotDevices(); + CreateSnapshotDevice(); + setup_ok_ = true; +} - snapshot_fd.reset(open("/dev/block/mapper/system-snapshot", O_RDONLY)); - ASSERT_TRUE(snapshot_fd > 0); - TestIO(snapshot_fd, system_buffer_); +bool CowSnapuserdTest::Merge() { + MergeImpl(); + return merge_ok_; +} - snapshot_fd.reset(open("/dev/block/mapper/product-snapshot", O_RDONLY)); - ASSERT_TRUE(snapshot_fd > 0); - TestIO(snapshot_fd, product_buffer_); +void CowSnapuserdTest::MergeImpl() { + DmTable merge_table; + ASSERT_TRUE(merge_table.AddTarget(std::make_unique( + 0, total_base_size_ / kSectorSize, base_loop_->device(), dmuser_dev_->path(), + SnapshotStorageMode::Merge, 8))); + ASSERT_TRUE(merge_table.valid()); + ASSERT_EQ(total_base_size_ / kSectorSize, merge_table.num_sectors()); - snapshot_fd.reset(-1); + DeviceMapper& dm = DeviceMapper::Instance(); + ASSERT_TRUE(dm.LoadTableAndActivate("cowsnapuserd-test-dm-snapshot", merge_table)); - DeleteDmUser(cow_system_, "system-snapshot"); - DeleteDmUser(cow_product_, "product-snapshot"); + while (true) { + vector status; + ASSERT_TRUE(dm.GetTableStatus("cowsnapuserd-test-dm-snapshot", &status)); + ASSERT_EQ(status.size(), 1); + ASSERT_EQ(strncmp(status[0].spec.target_type, "snapshot-merge", strlen("snapshot-merge")), + 0); - ASSERT_TRUE(client_->StopSnapuserd()); + DmTargetSnapshot::Status merge_status; + ASSERT_TRUE(DmTargetSnapshot::ParseStatusText(status[0].data, &merge_status)); + ASSERT_TRUE(merge_status.error.empty()); + if (merge_status.sectors_allocated == merge_status.metadata_sectors) { + break; + } + + std::this_thread::sleep_for(250ms); + } + + merge_ok_ = true; +} + +void CowSnapuserdTest::ValidateMerge() { + merged_buffer_ = std::make_unique(total_base_size_); + ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, merged_buffer_.get(), total_base_size_, 0), + true); + ASSERT_EQ(memcmp(merged_buffer_.get(), orig_buffer_.get(), total_base_size_), 0); +} + +TEST(Snapuserd_Test, Snapshot) { + CowSnapuserdTest harness; + ASSERT_TRUE(harness.Setup()); + harness.ReadSnapshotDeviceAndValidate(); + ASSERT_TRUE(harness.Merge()); + harness.ValidateMerge(); + harness.Shutdown(); } } // namespace snapshot diff --git a/fs_mgr/libsnapshot/snapuserd_client.cpp b/fs_mgr/libsnapshot/snapuserd_client.cpp index 7282bff77..16d02e424 100644 --- a/fs_mgr/libsnapshot/snapuserd_client.cpp +++ b/fs_mgr/libsnapshot/snapuserd_client.cpp @@ -57,7 +57,7 @@ bool EnsureSnapuserdStarted() { SnapuserdClient::SnapuserdClient(android::base::unique_fd&& sockfd) : sockfd_(std::move(sockfd)) {} static inline bool IsRetryErrno() { - return errno == ECONNREFUSED || errno == EINTR; + return errno == ECONNREFUSED || errno == EINTR || errno == ENOENT; } std::unique_ptr SnapuserdClient::Connect(const std::string& socket_name, @@ -112,6 +112,7 @@ bool SnapuserdClient::ValidateConnection() { } bool SnapuserdClient::Sendmsg(const std::string& msg) { + LOG(DEBUG) << "Sendmsg: msg " << msg << " sockfd: " << sockfd_; ssize_t numBytesSent = TEMP_FAILURE_RETRY(send(sockfd_, msg.data(), msg.size(), 0)); if (numBytesSent < 0) { PLOG(ERROR) << "Send failed";