From 2d3e79f5d5f04274e5415a30417b1f05ac65ab1d Mon Sep 17 00:00:00 2001 From: David Anderson Date: Thu, 21 Jun 2018 14:58:30 -0700 Subject: [PATCH] libdm: Add unit tests for dm-linear. Bug: 110035986 Test: libdm_test gtest Change-Id: I83fb978931aa36b83880c4d44745cc45ee516fc4 --- fs_mgr/libdm/Android.bp | 1 + fs_mgr/libdm/dm_test.cpp | 131 ++++++++++++++++++++++++++++- fs_mgr/libdm/loop_control_test.cpp | 5 +- fs_mgr/libdm/test_util.cpp | 52 ++++++++++++ fs_mgr/libdm/test_util.h | 35 ++++++++ 5 files changed, 220 insertions(+), 4 deletions(-) create mode 100644 fs_mgr/libdm/test_util.cpp create mode 100644 fs_mgr/libdm/test_util.h diff --git a/fs_mgr/libdm/Android.bp b/fs_mgr/libdm/Android.bp index 21a4cb5bd..22af1238e 100644 --- a/fs_mgr/libdm/Android.bp +++ b/fs_mgr/libdm/Android.bp @@ -49,5 +49,6 @@ cc_test { srcs: [ "dm_test.cpp", "loop_control_test.cpp", + "test_util.cpp", ] } diff --git a/fs_mgr/libdm/dm_test.cpp b/fs_mgr/libdm/dm_test.cpp index adbe82088..67dc958d4 100644 --- a/fs_mgr/libdm/dm_test.cpp +++ b/fs_mgr/libdm/dm_test.cpp @@ -14,13 +14,29 @@ * limitations under the License. */ -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include #include #include +#include +#include "test_util.h" using namespace std; using namespace android::dm; +using unique_fd = android::base::unique_fd; TEST(libdm, HasMinimumTargets) { DeviceMapper& dm = DeviceMapper::Instance(); @@ -35,3 +51,116 @@ TEST(libdm, HasMinimumTargets) { auto iter = by_name.find("linear"); EXPECT_NE(iter, by_name.end()); } + +// Helper to ensure that device mapper devices are released. +class TempDevice { + public: + TempDevice(const std::string& name, const DmTable& table) + : dm_(DeviceMapper::Instance()), name_(name), valid_(false) { + valid_ = dm_.CreateDevice(name, table); + } + TempDevice(TempDevice&& other) : dm_(other.dm_), name_(other.name_), valid_(other.valid_) { + other.valid_ = false; + } + ~TempDevice() { + if (valid_) { + dm_.DeleteDevice(name_); + } + } + bool Destroy() { + if (!valid_) { + return false; + } + valid_ = false; + return dm_.DeleteDevice(name_); + } + bool WaitForUdev() const { + auto start_time = std::chrono::steady_clock::now(); + while (true) { + if (!access(path().c_str(), F_OK)) { + return true; + } + if (errno != ENOENT) { + return false; + } + std::this_thread::sleep_for(50ms); + std::chrono::duration elapsed = std::chrono::steady_clock::now() - start_time; + if (elapsed >= 5s) { + return false; + } + } + } + std::string path() const { + std::string device_path; + if (!dm_.GetDmDevicePathByName(name_, &device_path)) { + return ""; + } + return device_path; + } + const std::string& name() const { return name_; } + bool valid() const { return valid_; } + + TempDevice(const TempDevice&) = delete; + TempDevice& operator=(const TempDevice&) = delete; + + TempDevice& operator=(TempDevice&& other) { + name_ = other.name_; + valid_ = other.valid_; + other.valid_ = false; + return *this; + } + + private: + DeviceMapper& dm_; + std::string name_; + bool valid_; +}; + +TEST(libdm, DmLinear) { + unique_fd tmp1(CreateTempFile("file_1", 4096)); + ASSERT_GE(tmp1, 0); + unique_fd tmp2(CreateTempFile("file_2", 4096)); + ASSERT_GE(tmp2, 0); + + // Create two different files. These will back two separate loop devices. + const char message1[] = "Hello! This is sector 1."; + const char message2[] = "Goodbye. This is sector 2."; + ASSERT_TRUE(android::base::WriteFully(tmp1, message1, sizeof(message1))); + ASSERT_TRUE(android::base::WriteFully(tmp2, message2, sizeof(message2))); + + LoopDevice loop_a(tmp1); + ASSERT_TRUE(loop_a.valid()); + LoopDevice loop_b(tmp2); + ASSERT_TRUE(loop_b.valid()); + + // Define a 2-sector device, with each sector mapping to the first sector + // of one of our loop devices. + DmTable table; + ASSERT_TRUE(table.AddTarget(make_unique(0, 1, loop_a.device(), 0))); + ASSERT_TRUE(table.AddTarget(make_unique(1, 1, loop_b.device(), 0))); + ASSERT_TRUE(table.valid()); + + TempDevice dev("libdm-test-dm-linear", table); + ASSERT_TRUE(dev.valid()); + ASSERT_FALSE(dev.path().empty()); + ASSERT_TRUE(dev.WaitForUdev()); + + // Note: a scope is needed to ensure that there are no open descriptors + // when we go to close the device. + { + unique_fd dev_fd(open(dev.path().c_str(), O_RDWR)); + ASSERT_GE(dev_fd, 0); + + // Test that each sector of our device is correctly mapped to each loop + // device. + char sector[512]; + ASSERT_TRUE(android::base::ReadFully(dev_fd, sector, sizeof(sector))); + ASSERT_EQ(strncmp(sector, message1, sizeof(message1)), 0); + ASSERT_TRUE(android::base::ReadFully(dev_fd, sector, sizeof(sector))); + ASSERT_EQ(strncmp(sector, message2, sizeof(message2)), 0); + } + + // Normally the TestDevice destructor would delete this, but at least one + // test should ensure that device deletion works. + ASSERT_TRUE(dev.Destroy()); +} diff --git a/fs_mgr/libdm/loop_control_test.cpp b/fs_mgr/libdm/loop_control_test.cpp index cd1d015ff..08bdc00a3 100644 --- a/fs_mgr/libdm/loop_control_test.cpp +++ b/fs_mgr/libdm/loop_control_test.cpp @@ -17,17 +17,16 @@ #include "libdm/loop_control.h" #include -#include #include #include #include -#include #include #include #include #include #include +#include "test_util.h" using namespace std; using namespace android::dm; @@ -36,7 +35,7 @@ using unique_fd = android::base::unique_fd; static unique_fd TempFile() { // A loop device needs to be at least one sector to actually work, so fill // up the file with a message. - unique_fd fd(syscall(__NR_memfd_create, "fake_disk", 0)); + unique_fd fd(CreateTempFile("temp", 0)); if (fd < 0) { return {}; } diff --git a/fs_mgr/libdm/test_util.cpp b/fs_mgr/libdm/test_util.cpp new file mode 100644 index 000000000..307251cda --- /dev/null +++ b/fs_mgr/libdm/test_util.cpp @@ -0,0 +1,52 @@ +/* + * 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 +#include +#include +#include +#include +#include + +#include "test_util.h" + +namespace android { +namespace dm { + +using unique_fd = android::base::unique_fd; + +// Create a temporary in-memory file. If size is non-zero, the file will be +// created with a fixed size. +unique_fd 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; +} + +} // namespace dm +} // namespace android diff --git a/fs_mgr/libdm/test_util.h b/fs_mgr/libdm/test_util.h new file mode 100644 index 000000000..96b051ca2 --- /dev/null +++ b/fs_mgr/libdm/test_util.h @@ -0,0 +1,35 @@ +/* + * 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. + */ + +#ifndef _LIBDM_TEST_UTILS_H_ +#define _LIBDM_TEST_UTILS_H_ + +#include +#include + +#include + +namespace android { +namespace dm { + +// Create a temporary in-memory file. If size is non-zero, the file will be +// created with a fixed size. +android::base::unique_fd CreateTempFile(const std::string& name, size_t size); + +} // namespace dm +} // namespace android + +#endif // _LIBDM_TEST_UTILS_H_