From 268c34e398d8f4e7e6f65513f8e7cc9d6b551e85 Mon Sep 17 00:00:00 2001 From: Mike Fleetwood Date: Fri, 30 Aug 2019 17:25:40 +0100 Subject: [PATCH] Create loop devices for LVM2 PV file system interface tests (!49) Creating an LVM2 PV as a non-root user on an image file fails like this: $ truncate -s 256M test.img $ lvm pvcreate `pwd`/test.img WARNING: Running as a non-root user. Functionality may be unavailable. /run/lvm/lvmetad.socket: access failed: Permission denied WARNING: Failed to connect to lvmetad. Falling back to device scanning. /run/lock/lvm/P_orphans:aux: open failed: Permission denied Can't get lock for orphan PVs. $ echo $? 5 Trying the same as root also fails: # truncate -s 256M test.img # lvm pvcreate `pwd`/test.img Device /root/test.img not found. # echo $? 5 LVM seems strongly predicated on only using block devices [1]. LVM can use loop devices though, but loop devices can only be created by root. # truncate -s 256M test.img # losetup -f --show `pwd`/test.img /dev/loop0 # lvm pvcreate /dev/loop0 Physical volume "/dev/loop0" successfully created. # echo $? 0 Make the LVM2 PV tests require user root and use loop device over the test image. Tests for the other file system types still directly uses the image file. This makes the LVM2 PV tests pass when run as root, or successfully skipped when run as non-root. [1] lvmconfig --typeconfig default --withcomments --withspace | less From the "devices" section of the commented default configuration, LVM uses block devices found below /dev, devices provided by udev and/or found in sysfs. Closes !49 - Add file system interface tests --- tests/test_SupportedFileSystems.cc | 109 +++++++++++++++++++++++++++-- 1 file changed, 105 insertions(+), 4 deletions(-) diff --git a/tests/test_SupportedFileSystems.cc b/tests/test_SupportedFileSystems.cc index c7a61e8e..57ef1c54 100644 --- a/tests/test_SupportedFileSystems.cc +++ b/tests/test_SupportedFileSystems.cc @@ -206,6 +206,19 @@ const std::string param_fsname(const ::testing::TestParamInfo& info) } +#define SKIP_IF_NOT_ROOT_FOR_REQUIRED_LOOPDEV_FOR_FS(fstype) \ + if (m_fstype == fstype) \ + { \ + if (getuid() != 0) \ + { \ + std::cout << __FILE__ << ":" << __LINE__ << ": Skip test. Not root " \ + << "to be able to create required loop device" << std::endl; \ + return; \ + } \ + m_require_loopdev = true; \ + } + + const Byte_Value IMAGESIZE_Default = 256*MEBIBYTE; const Byte_Value IMAGESIZE_Larger = 512*MEBIBYTE; @@ -229,6 +242,10 @@ protected: static void setup_supported_filesystems(); static void teardown_supported_filesystems(); + virtual const std::string create_loopdev(const std::string& image_name) const; + virtual void detach_loopdev(const std::string& loopdev_name) const; + virtual void reload_loopdev_size(const std::string& loopdev_name) const; + virtual void reload_partition(); virtual void resize_image(Byte_Value new_size); virtual void shrink_partition(Byte_Value size); @@ -238,6 +255,8 @@ protected: FSType m_fstype; FileSystem* m_fs_object; // Alias pointer + bool m_require_loopdev; + std::string m_dev_name; Partition m_partition; OperationDetail m_operation_detail; }; @@ -248,8 +267,9 @@ const char* SupportedFileSystemsTest::s_image_name = "test_ SupportedFileSystemsTest::SupportedFileSystemsTest() - // Initialise top-level operation detail object with description ... - : m_fstype(GetParam()), m_fs_object(NULL), m_operation_detail("Operation details:", STATUS_NONE) + : m_fstype(GetParam()), m_fs_object(NULL), m_require_loopdev(false), + // Initialise top-level operation detail object with description ... + m_operation_detail("Operation details:", STATUS_NONE) { } @@ -276,12 +296,19 @@ void SupportedFileSystemsTest::extra_setup(Byte_Value size) << size << ". errno=" << errno << "," << strerror(errno); close(fd); + m_dev_name = s_image_name; + if (m_require_loopdev) + m_dev_name = create_loopdev(s_image_name); + reload_partition(); } void SupportedFileSystemsTest::TearDown() { + if (m_require_loopdev) + detach_loopdev(m_dev_name); + unlink(s_image_name); m_fs_object = NULL; @@ -339,6 +366,66 @@ void SupportedFileSystemsTest::teardown_supported_filesystems() } +// Create loop device over named image file and return the device name. +const std::string SupportedFileSystemsTest::create_loopdev(const std::string& image_name) const +{ + Glib::ustring output; + Glib::ustring error; + Glib::ustring cmd = "losetup --find --show " + Glib::shell_quote(image_name); + int exit_status = Utils::execute_command(cmd, output, error, true); + if (exit_status != 0) + { + ADD_FAILURE() << __func__ << "(): Execute: " << cmd << "\n" + << error + << __func__ << "(): Losetup failed with exit status " << exit_status << "\n" + << __func__ << "(): Failed to create required loop device"; + return ""; + } + + // Strip trailing New Line. + size_t len = output.length(); + if (len > 0 && output[len-1] == '\n') + output.resize(len-1); + + return output; +} + + +// Detach named loop device. +void SupportedFileSystemsTest::detach_loopdev(const std::string& loopdev_name) const +{ + Glib::ustring output; + Glib::ustring error; + Glib::ustring cmd = "losetup --detach " + Glib::shell_quote(loopdev_name); + int exit_status = Utils::execute_command(cmd, output, error, true); + if (exit_status != 0) + { + // Report losetup detach error but don't fail the test because of it. + std::cout << __func__ << "(): Execute: " << cmd << "\n" + << error + << __func__ << "(): Losetup failed with exit status " << exit_status << "\n" + << __func__ << "(): Failed to detach loop device. Test NOT affected" << std::endl; + } +} + + +// Instruct loop device to reload the size of the underlying image file. +void SupportedFileSystemsTest::reload_loopdev_size(const std::string& loopdev_name) const +{ + Glib::ustring output; + Glib::ustring error; + Glib::ustring cmd = "losetup --set-capacity " + Glib::shell_quote(loopdev_name); + int exit_status = Utils::execute_command(cmd, output, error, true); + if (exit_status != 0) + { + ADD_FAILURE() << __func__ << "(): Execute: " << cmd << "\n" + << error + << __func__ << "(): Losetup failed with exit status " << exit_status << "\n" + << __func__ << "(): Failed to reload loop device size"; + } +} + + // (Re)initialise m_partition as a Partition object spanning the whole of the image file // with file system type only. No file system usage, label or UUID. void SupportedFileSystemsTest::reload_partition() @@ -346,11 +433,11 @@ void SupportedFileSystemsTest::reload_partition() m_partition.Reset(); // Use libparted to get the sector size etc. of the image file. - PedDevice* lp_device = ped_device_get(s_image_name); + PedDevice* lp_device = ped_device_get(m_dev_name.c_str()); ASSERT_TRUE(lp_device != NULL); // Prepare partition object spanning whole of the image file. - m_partition.set_unpartitioned(s_image_name, + m_partition.set_unpartitioned(m_dev_name, lp_device->path, m_fstype, lp_device->length, @@ -370,6 +457,9 @@ void SupportedFileSystemsTest::resize_image(Byte_Value new_size) ASSERT_EQ(ftruncate(fd, (off_t)new_size), 0) << "Failed to resize image file '" << s_image_name << "' to size " << new_size << ". errno=" << errno << "," << strerror(errno); close(fd); + + if (m_require_loopdev) + reload_loopdev_size(m_dev_name); } @@ -384,6 +474,8 @@ void SupportedFileSystemsTest::shrink_partition(Byte_Value new_size) TEST_P(SupportedFileSystemsTest, Create) { SKIP_IF_FS_DOESNT_SUPPORT(create); + SKIP_IF_NOT_ROOT_FOR_REQUIRED_LOOPDEV_FOR_FS(FS_LVM2_PV); + extra_setup(); // Call create, check for success and print operation details on failure. ASSERT_TRUE(m_fs_object->create(m_partition, m_operation_detail)) << m_operation_detail; @@ -394,6 +486,7 @@ TEST_P(SupportedFileSystemsTest, CreateAndReadUsage) { SKIP_IF_FS_DOESNT_SUPPORT(create); SKIP_IF_FS_DOESNT_SUPPORT(read); + SKIP_IF_NOT_ROOT_FOR_REQUIRED_LOOPDEV_FOR_FS(FS_LVM2_PV); extra_setup(); ASSERT_TRUE(m_fs_object->create(m_partition, m_operation_detail)) << m_operation_detail; @@ -421,6 +514,7 @@ TEST_P(SupportedFileSystemsTest, CreateAndReadLabel) { SKIP_IF_FS_DOESNT_SUPPORT(create); SKIP_IF_FS_DOESNT_SUPPORT(read_label); + SKIP_IF_NOT_ROOT_FOR_REQUIRED_LOOPDEV_FOR_FS(FS_LVM2_PV); const char* fs_label = "TEST_LABEL"; extra_setup(); @@ -441,6 +535,7 @@ TEST_P(SupportedFileSystemsTest, CreateAndReadUUID) { SKIP_IF_FS_DOESNT_SUPPORT(create); SKIP_IF_FS_DOESNT_SUPPORT(read_uuid); + SKIP_IF_NOT_ROOT_FOR_REQUIRED_LOOPDEV_FOR_FS(FS_LVM2_PV); extra_setup(); ASSERT_TRUE(m_fs_object->create(m_partition, m_operation_detail)) << m_operation_detail; @@ -459,6 +554,7 @@ TEST_P(SupportedFileSystemsTest, CreateAndWriteLabel) { SKIP_IF_FS_DOESNT_SUPPORT(create); SKIP_IF_FS_DOESNT_SUPPORT(write_label); + SKIP_IF_NOT_ROOT_FOR_REQUIRED_LOOPDEV_FOR_FS(FS_LVM2_PV); extra_setup(); m_partition.set_filesystem_label("FIRST"); @@ -474,6 +570,7 @@ TEST_P(SupportedFileSystemsTest, CreateAndWriteUUID) { SKIP_IF_FS_DOESNT_SUPPORT(create); SKIP_IF_FS_DOESNT_SUPPORT(write_uuid); + SKIP_IF_NOT_ROOT_FOR_REQUIRED_LOOPDEV_FOR_FS(FS_LVM2_PV); extra_setup(); ASSERT_TRUE(m_fs_object->create(m_partition, m_operation_detail)) << m_operation_detail; @@ -487,6 +584,7 @@ TEST_P(SupportedFileSystemsTest, CreateAndCheck) { SKIP_IF_FS_DOESNT_SUPPORT(create); SKIP_IF_FS_DOESNT_SUPPORT(check); + SKIP_IF_NOT_ROOT_FOR_REQUIRED_LOOPDEV_FOR_FS(FS_LVM2_PV); extra_setup(); ASSERT_TRUE(m_fs_object->create(m_partition, m_operation_detail)) << m_operation_detail; @@ -500,6 +598,7 @@ TEST_P(SupportedFileSystemsTest, CreateAndRemove) { SKIP_IF_FS_DOESNT_SUPPORT(create); SKIP_IF_FS_DOESNT_SUPPORT(remove); + SKIP_IF_NOT_ROOT_FOR_REQUIRED_LOOPDEV_FOR_FS(FS_LVM2_PV); extra_setup(); ASSERT_TRUE(m_fs_object->create(m_partition, m_operation_detail)) << m_operation_detail; @@ -514,6 +613,7 @@ TEST_P(SupportedFileSystemsTest, CreateAndGrow) { SKIP_IF_FS_DOESNT_SUPPORT(create); SKIP_IF_FS_DOESNT_SUPPORT(grow); + SKIP_IF_NOT_ROOT_FOR_REQUIRED_LOOPDEV_FOR_FS(FS_LVM2_PV); extra_setup(IMAGESIZE_Default); ASSERT_TRUE(m_fs_object->create(m_partition, m_operation_detail)) << m_operation_detail; @@ -529,6 +629,7 @@ TEST_P(SupportedFileSystemsTest, CreateAndShrink) { SKIP_IF_FS_DOESNT_SUPPORT(create); SKIP_IF_FS_DOESNT_SUPPORT(shrink); + SKIP_IF_NOT_ROOT_FOR_REQUIRED_LOOPDEV_FOR_FS(FS_LVM2_PV); extra_setup(IMAGESIZE_Larger); ASSERT_TRUE(m_fs_object->create(m_partition, m_operation_detail)) << m_operation_detail;