blob: bed71cf9f6e093dc9372be53933652a9a4c515d6 [file] [log] [blame]
// Copyright (C) 2018 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <android-base/strings.h>
#include <gflags/gflags.h>
#include <fcntl.h>
#include <linux/fs.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
#include <chrono>
#include <iostream>
#include <memory>
#include <string_view>
#include <android-base/file.h>
#include <android-base/properties.h>
#include <android-base/unique_fd.h>
#include <fs_mgr/file_wait.h>
#include <gtest/gtest.h>
#include <libdm/dm.h>
#include <libdm/loop_control.h>
#include <libsnapshot/cow_writer.h>
#include <snapuserd/dm_user_block_server.h>
#include <storage_literals/storage_literals.h>
#include "handler_manager.h"
#include "merge_worker.h"
#include "read_worker.h"
#include "snapuserd_core.h"
#include "testing/dm_user_harness.h"
#include "testing/host_harness.h"
#include "testing/temp_device.h"
#include "utility.h"
namespace android {
namespace snapshot {
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;
using testing::AssertionFailure;
using testing::AssertionResult;
using testing::AssertionSuccess;
class SnapuserdTestBase : public ::testing::TestWithParam<bool> {
protected:
void SetUp() override;
void TearDown() override;
void CreateBaseDevice();
void CreateCowDevice();
void SetDeviceControlName();
std::unique_ptr<ICowWriter> CreateCowDeviceInternal();
std::unique_ptr<ITestHarness> harness_;
size_t size_ = 10_MiB;
int total_base_size_ = 0;
std::string system_device_ctrl_name_;
std::string system_device_name_;
unique_ptr<IBackingDevice> base_dev_;
unique_fd base_fd_;
std::unique_ptr<TemporaryFile> cow_system_;
std::unique_ptr<uint8_t[]> orig_buffer_;
};
void SnapuserdTestBase::SetUp() {
#if __ANDROID__
harness_ = std::make_unique<DmUserTestHarness>();
#else
harness_ = std::make_unique<HostTestHarness>();
#endif
}
void SnapuserdTestBase::TearDown() {}
void SnapuserdTestBase::CreateBaseDevice() {
total_base_size_ = (size_ * 5);
base_dev_ = harness_->CreateBackingDevice(total_base_size_);
ASSERT_NE(base_dev_, nullptr);
base_fd_.reset(open(base_dev_->GetPath().c_str(), O_RDWR | O_CLOEXEC));
ASSERT_GE(base_fd_, 0);
unique_fd rnd_fd(open("/dev/random", O_RDONLY));
ASSERT_GE(rnd_fd, 0);
std::unique_ptr<uint8_t[]> random_buffer = std::make_unique<uint8_t[]>(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);
}
std::unique_ptr<ICowWriter> SnapuserdTestBase::CreateCowDeviceInternal() {
cow_system_ = std::make_unique<TemporaryFile>();
CowOptions options;
options.compression = "gz";
unique_fd fd(cow_system_->fd);
cow_system_->fd = -1;
return CreateCowWriter(kDefaultCowVersion, options, std::move(fd));
}
void SnapuserdTestBase::CreateCowDevice() {
unique_fd rnd_fd;
loff_t offset = 0;
auto writer = CreateCowDeviceInternal();
ASSERT_NE(writer, nullptr);
rnd_fd.reset(open("/dev/random", O_RDONLY));
ASSERT_TRUE(rnd_fd > 0);
std::unique_ptr<uint8_t[]> random_buffer_1_ = 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);
offset += 1_MiB;
}
size_t num_blocks = size_ / writer->GetBlockSize();
size_t blk_end_copy = num_blocks * 2;
size_t source_blk = num_blocks - 1;
size_t blk_src_copy = blk_end_copy - 1;
uint32_t sequence[num_blocks * 2];
// Sequence for Copy ops
for (int i = 0; i < num_blocks; i++) {
sequence[i] = num_blocks - 1 - i;
}
// Sequence for Xor ops
for (int i = 0; i < num_blocks; i++) {
sequence[num_blocks + i] = 5 * num_blocks - 1 - i;
}
ASSERT_TRUE(writer->AddSequenceData(2 * num_blocks, sequence));
size_t x = num_blocks;
while (1) {
ASSERT_TRUE(writer->AddCopy(source_blk, blk_src_copy));
x -= 1;
if (x == 0) {
break;
}
source_blk -= 1;
blk_src_copy -= 1;
}
source_blk = num_blocks;
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;
ASSERT_TRUE(writer->AddZeroBlocks(blk_zero_copy_start, num_blocks));
size_t blk_random2_replace_start = blk_zero_copy_end;
ASSERT_TRUE(writer->AddRawBlocks(blk_random2_replace_start, random_buffer_1_.get(), size_));
size_t blk_xor_start = blk_random2_replace_start + num_blocks;
size_t xor_offset = BLOCK_SZ / 2;
ASSERT_TRUE(writer->AddXorBlocks(blk_xor_start, random_buffer_1_.get(), size_, num_blocks,
xor_offset));
// Flush operations
ASSERT_TRUE(writer->Finalize());
// Construct the buffer required for validation
orig_buffer_ = std::make_unique<uint8_t[]>(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_1_.get(), size_);
ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, &orig_buffer_.get()[size_ * 4], size_,
size_ + xor_offset),
true);
for (int i = 0; i < size_; i++) {
orig_buffer_.get()[(size_ * 4) + i] =
(uint8_t)(orig_buffer_.get()[(size_ * 4) + i] ^ random_buffer_1_.get()[i]);
}
}
void SnapuserdTestBase::SetDeviceControlName() {
system_device_name_.clear();
system_device_ctrl_name_.clear();
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);
system_device_ctrl_name_ = system_device_name_ + "-ctrl";
}
class SnapuserdTest : public SnapuserdTestBase {
public:
void SetupDefault();
void SetupOrderedOps();
void SetupOrderedOpsInverted();
void SetupCopyOverlap_1();
void SetupCopyOverlap_2();
bool Merge();
void ValidateMerge();
void ReadSnapshotDeviceAndValidate();
void ReadSnapshotAndValidateOverlappingBlocks();
void Shutdown();
void MergeInterrupt();
void MergeInterruptFixed(int duration);
void MergeInterruptAndValidate(int duration);
void MergeInterruptRandomly(int max_duration);
bool StartMerge();
void CheckMergeCompletion();
static const uint64_t kSectorSize = 512;
protected:
void SetUp() override;
void TearDown() override;
void SetupImpl();
void SimulateDaemonRestart();
void CreateCowDeviceOrderedOps();
void CreateCowDeviceOrderedOpsInverted();
void CreateCowDeviceWithCopyOverlap_1();
void CreateCowDeviceWithCopyOverlap_2();
void SetupDaemon();
void InitCowDevice();
void InitDaemon();
void CreateUserDevice();
unique_ptr<IUserDevice> dmuser_dev_;
std::unique_ptr<uint8_t[]> merged_buffer_;
std::unique_ptr<SnapshotHandlerManager> handlers_;
int cow_num_sectors_;
};
void SnapuserdTest::SetUp() {
ASSERT_NO_FATAL_FAILURE(SnapuserdTestBase::SetUp());
handlers_ = std::make_unique<SnapshotHandlerManager>();
}
void SnapuserdTest::TearDown() {
SnapuserdTestBase::TearDown();
Shutdown();
}
void SnapuserdTest::Shutdown() {
if (dmuser_dev_) {
ASSERT_TRUE(dmuser_dev_->Destroy());
}
auto misc_device = "/dev/dm-user/" + system_device_ctrl_name_;
ASSERT_TRUE(handlers_->DeleteHandler(system_device_ctrl_name_));
ASSERT_TRUE(android::fs_mgr::WaitForFileDeleted(misc_device, 10s));
handlers_->TerminateMergeThreads();
handlers_->JoinAllThreads();
handlers_ = std::make_unique<SnapshotHandlerManager>();
}
void SnapuserdTest::SetupDefault() {
ASSERT_NO_FATAL_FAILURE(SetupImpl());
}
void SnapuserdTest::SetupOrderedOps() {
ASSERT_NO_FATAL_FAILURE(CreateBaseDevice());
ASSERT_NO_FATAL_FAILURE(CreateCowDeviceOrderedOps());
ASSERT_NO_FATAL_FAILURE(SetupDaemon());
}
void SnapuserdTest::SetupOrderedOpsInverted() {
ASSERT_NO_FATAL_FAILURE(CreateBaseDevice());
ASSERT_NO_FATAL_FAILURE(CreateCowDeviceOrderedOpsInverted());
ASSERT_NO_FATAL_FAILURE(SetupDaemon());
}
void SnapuserdTest::SetupCopyOverlap_1() {
ASSERT_NO_FATAL_FAILURE(CreateBaseDevice());
ASSERT_NO_FATAL_FAILURE(CreateCowDeviceWithCopyOverlap_1());
ASSERT_NO_FATAL_FAILURE(SetupDaemon());
}
void SnapuserdTest::SetupCopyOverlap_2() {
ASSERT_NO_FATAL_FAILURE(CreateBaseDevice());
ASSERT_NO_FATAL_FAILURE(CreateCowDeviceWithCopyOverlap_2());
ASSERT_NO_FATAL_FAILURE(SetupDaemon());
}
void SnapuserdTest::SetupDaemon() {
SetDeviceControlName();
ASSERT_NO_FATAL_FAILURE(CreateUserDevice());
ASSERT_NO_FATAL_FAILURE(InitCowDevice());
ASSERT_NO_FATAL_FAILURE(InitDaemon());
}
void SnapuserdTest::ReadSnapshotDeviceAndValidate() {
unique_fd fd(open(dmuser_dev_->GetPath().c_str(), O_RDONLY));
ASSERT_GE(fd, 0);
std::unique_ptr<uint8_t[]> snapuserd_buffer = std::make_unique<uint8_t[]>(size_);
// COPY
loff_t offset = 0;
ASSERT_EQ(ReadFullyAtOffset(fd, snapuserd_buffer.get(), size_, offset), true);
ASSERT_EQ(memcmp(snapuserd_buffer.get(), orig_buffer_.get(), size_), 0);
// REPLACE
offset += size_;
ASSERT_EQ(ReadFullyAtOffset(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(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(fd, snapuserd_buffer.get(), size_, offset), true);
ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + (size_ * 3), size_), 0);
// XOR
offset += size_;
ASSERT_EQ(ReadFullyAtOffset(fd, snapuserd_buffer.get(), size_, offset), true);
ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + (size_ * 4), size_), 0);
}
void SnapuserdTest::ReadSnapshotAndValidateOverlappingBlocks() {
// Open COW device
unique_fd fd(open(cow_system_->path, O_RDONLY));
ASSERT_GE(fd, 0);
CowReader reader;
ASSERT_TRUE(reader.Parse(fd));
const auto& header = reader.GetHeader();
size_t total_mapped_addr_length = header.prefix.header_size + BUFFER_REGION_DEFAULT_SIZE;
ASSERT_GE(header.prefix.major_version, 2);
void* mapped_addr = mmap(NULL, total_mapped_addr_length, PROT_READ, MAP_SHARED, fd.get(), 0);
ASSERT_NE(mapped_addr, MAP_FAILED);
bool populate_data_from_scratch = false;
struct BufferState* ra_state =
reinterpret_cast<struct BufferState*>((char*)mapped_addr + header.prefix.header_size);
if (ra_state->read_ahead_state == kCowReadAheadDone) {
populate_data_from_scratch = true;
}
size_t num_merge_ops = header.num_merge_ops;
// We have some partial merge operations completed.
// To test the merge-resume path, forcefully corrupt the data of the base
// device for the offsets where the merge is still pending.
if (num_merge_ops && populate_data_from_scratch) {
std::string corrupt_buffer(4096, 0);
// Corrupt two blocks from the point where the merge has to be resumed by
// writing down zeroe's.
//
// Now, since this is a merge-resume path, the "correct" data should be
// in the scratch space of the COW device. When there is an I/O request
// from the snapshot device, the data has to be retrieved from the
// scratch space. If not and I/O is routed to the base device, we
// may end up with corruption.
off_t corrupt_offset = (num_merge_ops + 2) * 4096;
if (corrupt_offset < size_) {
ASSERT_EQ(android::base::WriteFullyAtOffset(base_fd_, (void*)corrupt_buffer.c_str(),
4096, corrupt_offset),
true);
corrupt_offset -= 4096;
ASSERT_EQ(android::base::WriteFullyAtOffset(base_fd_, (void*)corrupt_buffer.c_str(),
4096, corrupt_offset),
true);
fsync(base_fd_.get());
}
}
// Time to read the snapshot device.
unique_fd snapshot_fd(open(dmuser_dev_->GetPath().c_str(), O_RDONLY | O_DIRECT | O_SYNC));
ASSERT_GE(snapshot_fd, 0);
void* buff_addr;
ASSERT_EQ(posix_memalign(&buff_addr, 4096, size_), 0);
std::unique_ptr<void, decltype(&::free)> snapshot_buffer(buff_addr, ::free);
// Scan the entire snapshot device and read the data and verify data
// integrity. Since the base device was forcefully corrupted, the data from
// this scan should be retrieved from scratch space of the COW partition.
//
// Furthermore, after the merge is complete, base device data is again
// verified as the aforementioned corrupted blocks aren't persisted.
ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapshot_buffer.get(), size_, 0), true);
ASSERT_EQ(memcmp(snapshot_buffer.get(), orig_buffer_.get(), size_), 0);
}
void SnapuserdTest::CreateCowDeviceWithCopyOverlap_2() {
auto writer = CreateCowDeviceInternal();
ASSERT_NE(writer, nullptr);
size_t num_blocks = size_ / writer->GetBlockSize();
size_t x = num_blocks;
size_t blk_src_copy = 0;
// Create overlapping copy operations
while (1) {
ASSERT_TRUE(writer->AddCopy(blk_src_copy, blk_src_copy + 1));
x -= 1;
if (x == 1) {
break;
}
blk_src_copy += 1;
}
// Flush operations
ASSERT_TRUE(writer->Finalize());
// Construct the buffer required for validation
orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
// Read the entire base device
ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), total_base_size_, 0),
true);
// Merged operations required for validation
int block_size = 4096;
x = num_blocks;
loff_t src_offset = block_size;
loff_t dest_offset = 0;
while (1) {
memmove((char*)orig_buffer_.get() + dest_offset, (char*)orig_buffer_.get() + src_offset,
block_size);
x -= 1;
if (x == 1) {
break;
}
src_offset += block_size;
dest_offset += block_size;
}
}
void SnapuserdTest::CreateCowDeviceWithCopyOverlap_1() {
auto writer = CreateCowDeviceInternal();
ASSERT_NE(writer, nullptr);
size_t num_blocks = size_ / writer->GetBlockSize();
size_t x = num_blocks;
size_t blk_src_copy = num_blocks - 1;
// Create overlapping copy operations
while (1) {
ASSERT_TRUE(writer->AddCopy(blk_src_copy + 1, blk_src_copy));
x -= 1;
if (x == 0) {
ASSERT_EQ(blk_src_copy, 0);
break;
}
blk_src_copy -= 1;
}
// Flush operations
ASSERT_TRUE(writer->Finalize());
// Construct the buffer required for validation
orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
// Read the entire base device
ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), total_base_size_, 0),
true);
// Merged operations
ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), writer->GetBlockSize(),
0),
true);
ASSERT_EQ(android::base::ReadFullyAtOffset(
base_fd_, (char*)orig_buffer_.get() + writer->GetBlockSize(), size_, 0),
true);
}
void SnapuserdTest::CreateCowDeviceOrderedOpsInverted() {
unique_fd rnd_fd;
loff_t offset = 0;
auto writer = CreateCowDeviceInternal();
ASSERT_NE(writer, nullptr);
rnd_fd.reset(open("/dev/random", O_RDONLY));
ASSERT_TRUE(rnd_fd > 0);
std::unique_ptr<uint8_t[]> random_buffer_1_ = 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);
offset += 1_MiB;
}
size_t num_blocks = size_ / writer->GetBlockSize();
size_t blk_end_copy = num_blocks * 3;
size_t source_blk = num_blocks - 1;
size_t blk_src_copy = blk_end_copy - 1;
uint16_t xor_offset = 5;
size_t x = num_blocks;
while (1) {
ASSERT_TRUE(writer->AddCopy(source_blk, blk_src_copy));
x -= 1;
if (x == 0) {
break;
}
source_blk -= 1;
blk_src_copy -= 1;
}
for (size_t i = num_blocks; i > 0; i--) {
ASSERT_TRUE(writer->AddXorBlocks(
num_blocks + i - 1, &random_buffer_1_.get()[writer->GetBlockSize() * (i - 1)],
writer->GetBlockSize(), 2 * num_blocks + i - 1, xor_offset));
}
// Flush operations
ASSERT_TRUE(writer->Finalize());
// Construct the buffer required for validation
orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
// Read the entire base device
ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), total_base_size_, 0),
true);
// Merged Buffer
memmove(orig_buffer_.get(), (char*)orig_buffer_.get() + 2 * size_, size_);
memmove(orig_buffer_.get() + size_, (char*)orig_buffer_.get() + 2 * size_ + xor_offset, size_);
for (int i = 0; i < size_; i++) {
orig_buffer_.get()[size_ + i] ^= random_buffer_1_.get()[i];
}
}
void SnapuserdTest::CreateCowDeviceOrderedOps() {
unique_fd rnd_fd;
loff_t offset = 0;
auto writer = CreateCowDeviceInternal();
ASSERT_NE(writer, nullptr);
rnd_fd.reset(open("/dev/random", O_RDONLY));
ASSERT_TRUE(rnd_fd > 0);
std::unique_ptr<uint8_t[]> random_buffer_1_ = 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);
offset += 1_MiB;
}
memset(random_buffer_1_.get(), 0, size_);
size_t num_blocks = size_ / writer->GetBlockSize();
size_t x = num_blocks;
size_t source_blk = 0;
size_t blk_src_copy = 2 * num_blocks;
uint16_t xor_offset = 5;
while (1) {
ASSERT_TRUE(writer->AddCopy(source_blk, blk_src_copy));
x -= 1;
if (x == 0) {
break;
}
source_blk += 1;
blk_src_copy += 1;
}
ASSERT_TRUE(writer->AddXorBlocks(num_blocks, random_buffer_1_.get(), size_, 2 * num_blocks,
xor_offset));
// Flush operations
ASSERT_TRUE(writer->Finalize());
// Construct the buffer required for validation
orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
// Read the entire base device
ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), total_base_size_, 0),
true);
// Merged Buffer
memmove(orig_buffer_.get(), (char*)orig_buffer_.get() + 2 * size_, size_);
memmove(orig_buffer_.get() + size_, (char*)orig_buffer_.get() + 2 * size_ + xor_offset, size_);
for (int i = 0; i < size_; i++) {
orig_buffer_.get()[size_ + i] ^= random_buffer_1_.get()[i];
}
}
void SnapuserdTest::InitCowDevice() {
auto factory = harness_->GetBlockServerFactory();
auto opener = factory->CreateOpener(system_device_ctrl_name_);
auto handler =
handlers_->AddHandler(system_device_ctrl_name_, cow_system_->path, base_dev_->GetPath(),
base_dev_->GetPath(), opener, 1, GetParam(), false);
ASSERT_NE(handler, nullptr);
ASSERT_NE(handler->snapuserd(), nullptr);
#ifdef __ANDROID__
ASSERT_NE(handler->snapuserd()->GetNumSectors(), 0);
#endif
}
void SnapuserdTest::CreateUserDevice() {
auto dev_sz = base_dev_->GetSize();
ASSERT_NE(dev_sz, 0);
cow_num_sectors_ = dev_sz >> 9;
dmuser_dev_ = harness_->CreateUserDevice(system_device_name_, system_device_ctrl_name_,
cow_num_sectors_);
ASSERT_NE(dmuser_dev_, nullptr);
}
void SnapuserdTest::InitDaemon() {
ASSERT_TRUE(handlers_->StartHandler(system_device_ctrl_name_));
}
void SnapuserdTest::CheckMergeCompletion() {
while (true) {
double percentage = handlers_->GetMergePercentage();
if ((int)percentage == 100) {
break;
}
std::this_thread::sleep_for(1s);
}
}
void SnapuserdTest::SetupImpl() {
ASSERT_NO_FATAL_FAILURE(CreateBaseDevice());
ASSERT_NO_FATAL_FAILURE(CreateCowDevice());
SetDeviceControlName();
ASSERT_NO_FATAL_FAILURE(CreateUserDevice());
ASSERT_NO_FATAL_FAILURE(InitCowDevice());
ASSERT_NO_FATAL_FAILURE(InitDaemon());
}
bool SnapuserdTest::Merge() {
if (!StartMerge()) {
return false;
}
CheckMergeCompletion();
return true;
}
bool SnapuserdTest::StartMerge() {
return handlers_->InitiateMerge(system_device_ctrl_name_);
}
void SnapuserdTest::ValidateMerge() {
merged_buffer_ = std::make_unique<uint8_t[]>(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);
}
void SnapuserdTest::SimulateDaemonRestart() {
ASSERT_NO_FATAL_FAILURE(Shutdown());
std::this_thread::sleep_for(500ms);
SetDeviceControlName();
ASSERT_NO_FATAL_FAILURE(CreateUserDevice());
ASSERT_NO_FATAL_FAILURE(InitCowDevice());
ASSERT_NO_FATAL_FAILURE(InitDaemon());
}
void SnapuserdTest::MergeInterruptRandomly(int max_duration) {
std::srand(std::time(nullptr));
ASSERT_TRUE(StartMerge());
for (int i = 0; i < 20; i++) {
int duration = std::rand() % max_duration;
std::this_thread::sleep_for(std::chrono::milliseconds(duration));
ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());
ASSERT_TRUE(StartMerge());
}
ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());
ASSERT_TRUE(Merge());
}
void SnapuserdTest::MergeInterruptFixed(int duration) {
ASSERT_TRUE(StartMerge());
for (int i = 0; i < 25; i++) {
std::this_thread::sleep_for(std::chrono::milliseconds(duration));
ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());
ASSERT_TRUE(StartMerge());
}
ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());
ASSERT_TRUE(Merge());
}
void SnapuserdTest::MergeInterruptAndValidate(int duration) {
ASSERT_TRUE(StartMerge());
for (int i = 0; i < 15; i++) {
std::this_thread::sleep_for(std::chrono::milliseconds(duration));
ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());
ReadSnapshotAndValidateOverlappingBlocks();
ASSERT_TRUE(StartMerge());
}
ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());
ASSERT_TRUE(Merge());
}
void SnapuserdTest::MergeInterrupt() {
// Interrupt merge at various intervals
ASSERT_TRUE(StartMerge());
std::this_thread::sleep_for(250ms);
ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());
ASSERT_TRUE(StartMerge());
std::this_thread::sleep_for(250ms);
ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());
ASSERT_TRUE(StartMerge());
std::this_thread::sleep_for(150ms);
ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());
ASSERT_TRUE(StartMerge());
std::this_thread::sleep_for(100ms);
ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());
ASSERT_TRUE(StartMerge());
std::this_thread::sleep_for(800ms);
ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());
ASSERT_TRUE(StartMerge());
std::this_thread::sleep_for(600ms);
ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());
ASSERT_TRUE(Merge());
}
TEST_P(SnapuserdTest, Snapshot_IO_TEST) {
if (!harness_->HasUserDevice()) {
GTEST_SKIP() << "Skipping snapshot read; not supported";
}
ASSERT_NO_FATAL_FAILURE(SetupDefault());
// I/O before merge
ASSERT_NO_FATAL_FAILURE(ReadSnapshotDeviceAndValidate());
ASSERT_TRUE(Merge());
ValidateMerge();
// I/O after merge - daemon should read directly
// from base device
ASSERT_NO_FATAL_FAILURE(ReadSnapshotDeviceAndValidate());
}
TEST_P(SnapuserdTest, Snapshot_MERGE_IO_TEST) {
if (!harness_->HasUserDevice()) {
GTEST_SKIP() << "Skipping snapshot read; not supported";
}
ASSERT_NO_FATAL_FAILURE(SetupDefault());
// Issue I/O before merge begins
auto read_future =
std::async(std::launch::async, &SnapuserdTest::ReadSnapshotDeviceAndValidate, this);
// Start the merge
ASSERT_TRUE(Merge());
ValidateMerge();
read_future.wait();
}
TEST_P(SnapuserdTest, Snapshot_MERGE_IO_TEST_1) {
if (!harness_->HasUserDevice()) {
GTEST_SKIP() << "Skipping snapshot read; not supported";
}
ASSERT_NO_FATAL_FAILURE(SetupDefault());
// Start the merge
ASSERT_TRUE(StartMerge());
// Issue I/O in parallel when merge is in-progress
auto read_future =
std::async(std::launch::async, &SnapuserdTest::ReadSnapshotDeviceAndValidate, this);
CheckMergeCompletion();
ValidateMerge();
read_future.wait();
}
TEST_P(SnapuserdTest, Snapshot_Merge_Resume) {
ASSERT_NO_FATAL_FAILURE(SetupDefault());
ASSERT_NO_FATAL_FAILURE(MergeInterrupt());
ValidateMerge();
}
TEST_P(SnapuserdTest, Snapshot_COPY_Overlap_TEST_1) {
ASSERT_NO_FATAL_FAILURE(SetupCopyOverlap_1());
ASSERT_TRUE(Merge());
ValidateMerge();
}
TEST_P(SnapuserdTest, Snapshot_COPY_Overlap_TEST_2) {
ASSERT_NO_FATAL_FAILURE(SetupCopyOverlap_2());
ASSERT_TRUE(Merge());
ValidateMerge();
}
TEST_P(SnapuserdTest, Snapshot_COPY_Overlap_Merge_Resume_TEST) {
ASSERT_NO_FATAL_FAILURE(SetupCopyOverlap_1());
ASSERT_NO_FATAL_FAILURE(MergeInterrupt());
ValidateMerge();
}
TEST_P(SnapuserdTest, Snapshot_COPY_Overlap_Merge_Resume_IO_Validate_TEST) {
if (!harness_->HasUserDevice()) {
GTEST_SKIP() << "Skipping snapshot read; not supported";
}
ASSERT_NO_FATAL_FAILURE(SetupCopyOverlap_2());
ASSERT_NO_FATAL_FAILURE(MergeInterruptAndValidate(2));
ValidateMerge();
}
TEST_P(SnapuserdTest, Snapshot_Merge_Crash_Fixed_Ordered) {
ASSERT_NO_FATAL_FAILURE(SetupOrderedOps());
ASSERT_NO_FATAL_FAILURE(MergeInterruptFixed(300));
ValidateMerge();
}
TEST_P(SnapuserdTest, Snapshot_Merge_Crash_Random_Ordered) {
ASSERT_NO_FATAL_FAILURE(SetupOrderedOps());
ASSERT_NO_FATAL_FAILURE(MergeInterruptRandomly(500));
ValidateMerge();
}
TEST_P(SnapuserdTest, Snapshot_Merge_Crash_Fixed_Inverted) {
ASSERT_NO_FATAL_FAILURE(SetupOrderedOpsInverted());
ASSERT_NO_FATAL_FAILURE(MergeInterruptFixed(50));
ValidateMerge();
}
TEST_P(SnapuserdTest, Snapshot_Merge_Crash_Random_Inverted) {
ASSERT_NO_FATAL_FAILURE(SetupOrderedOpsInverted());
ASSERT_NO_FATAL_FAILURE(MergeInterruptRandomly(50));
ValidateMerge();
}
class HandlerTest : public SnapuserdTestBase {
protected:
void SetUp() override;
void TearDown() override;
AssertionResult ReadSectors(sector_t sector, uint64_t size, void* buffer);
TestBlockServerFactory factory_;
std::shared_ptr<TestBlockServerOpener> opener_;
std::shared_ptr<SnapshotHandler> handler_;
std::unique_ptr<ReadWorker> read_worker_;
TestBlockServer* block_server_;
std::future<bool> handler_thread_;
};
void HandlerTest::SetUp() {
ASSERT_NO_FATAL_FAILURE(SnapuserdTestBase::SetUp());
ASSERT_NO_FATAL_FAILURE(CreateBaseDevice());
ASSERT_NO_FATAL_FAILURE(CreateCowDevice());
ASSERT_NO_FATAL_FAILURE(SetDeviceControlName());
opener_ = factory_.CreateTestOpener(system_device_ctrl_name_);
ASSERT_NE(opener_, nullptr);
handler_ = std::make_shared<SnapshotHandler>(system_device_ctrl_name_, cow_system_->path,
base_dev_->GetPath(), base_dev_->GetPath(),
opener_, 1, false, false);
ASSERT_TRUE(handler_->InitCowDevice());
ASSERT_TRUE(handler_->InitializeWorkers());
read_worker_ = std::make_unique<ReadWorker>(cow_system_->path, base_dev_->GetPath(),
system_device_ctrl_name_, base_dev_->GetPath(),
handler_->GetSharedPtr(), opener_);
ASSERT_TRUE(read_worker_->Init());
block_server_ = static_cast<TestBlockServer*>(read_worker_->block_server());
handler_thread_ = std::async(std::launch::async, &SnapshotHandler::Start, handler_.get());
}
void HandlerTest::TearDown() {
ASSERT_TRUE(factory_.DeleteQueue(system_device_ctrl_name_));
ASSERT_TRUE(handler_thread_.get());
SnapuserdTestBase::TearDown();
}
AssertionResult HandlerTest::ReadSectors(sector_t sector, uint64_t size, void* buffer) {
if (!read_worker_->RequestSectors(sector, size)) {
return AssertionFailure() << "request sectors failed";
}
std::string result = std::move(block_server_->sent_io());
if (result.size() != size) {
return AssertionFailure() << "size mismatch in result, got " << result.size()
<< ", expected " << size;
}
memcpy(buffer, result.data(), size);
return AssertionSuccess();
}
// This test mirrors ReadSnapshotDeviceAndValidate.
TEST_P(HandlerTest, Read) {
std::unique_ptr<uint8_t[]> snapuserd_buffer = std::make_unique<uint8_t[]>(size_);
// COPY
loff_t offset = 0;
ASSERT_TRUE(ReadSectors(offset / SECTOR_SIZE, size_, snapuserd_buffer.get()));
ASSERT_EQ(memcmp(snapuserd_buffer.get(), orig_buffer_.get(), size_), 0);
// REPLACE
offset += size_;
ASSERT_TRUE(ReadSectors(offset / SECTOR_SIZE, size_, snapuserd_buffer.get()));
ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + size_, size_), 0);
// ZERO
offset += size_;
ASSERT_TRUE(ReadSectors(offset / SECTOR_SIZE, size_, snapuserd_buffer.get()));
ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + (size_ * 2), size_), 0);
// REPLACE
offset += size_;
ASSERT_TRUE(ReadSectors(offset / SECTOR_SIZE, size_, snapuserd_buffer.get()));
ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + (size_ * 3), size_), 0);
// XOR
offset += size_;
ASSERT_TRUE(ReadSectors(offset / SECTOR_SIZE, size_, snapuserd_buffer.get()));
ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + (size_ * 4), size_), 0);
}
TEST_P(HandlerTest, ReadUnalignedSector) {
std::unique_ptr<uint8_t[]> snapuserd_buffer = std::make_unique<uint8_t[]>(BLOCK_SZ);
ASSERT_TRUE(ReadSectors(1, BLOCK_SZ, snapuserd_buffer.get()));
ASSERT_EQ(memcmp(snapuserd_buffer.get(), orig_buffer_.get() + SECTOR_SIZE, BLOCK_SZ), 0);
}
TEST_P(HandlerTest, ReadUnalignedSize) {
std::unique_ptr<uint8_t[]> snapuserd_buffer = std::make_unique<uint8_t[]>(SECTOR_SIZE);
ASSERT_TRUE(ReadSectors(0, SECTOR_SIZE, snapuserd_buffer.get()));
ASSERT_EQ(memcmp(snapuserd_buffer.get(), orig_buffer_.get(), SECTOR_SIZE), 0);
}
std::vector<bool> GetIoUringConfigs() {
#if __ANDROID__
if (!android::base::GetBoolProperty("ro.virtual_ab.io_uring.enabled", false)) {
return {false};
}
#endif
if (!KernelSupportsIoUring()) {
return {false};
}
return {false, true};
}
std::string IoUringConfigName(const testing::TestParamInfo<SnapuserdTest::ParamType>& info) {
return info.param ? "io_uring" : "sync";
}
INSTANTIATE_TEST_SUITE_P(Io, SnapuserdTest, ::testing::ValuesIn(GetIoUringConfigs()),
IoUringConfigName);
INSTANTIATE_TEST_SUITE_P(Io, HandlerTest, ::testing::ValuesIn(GetIoUringConfigs()),
IoUringConfigName);
} // namespace snapshot
} // namespace android
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
gflags::ParseCommandLineFlags(&argc, &argv, false);
return RUN_ALL_TESTS();
}