Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit db70cbf7 authored by David Anderson's avatar David Anderson
Browse files

snapuserd: Remove test dependence on LoopDevice.

LoopDevice requires root, which is an obstacle to running this test in
automation. The test also requires memfd which is not available in our
included glibc. Create an IBackingDevice layer so we can use temporary
files instead on host tests, while keeping the block-device code for
on-device tests, which more closely matches how snapuserd runs.

Bug: 288273605
Test: snapuserd_test
Change-Id: I89b154921b6bbcf8fe213ef7f5c4da4d48322909
parent 1cf0e904
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -221,6 +221,7 @@ cc_test {
    ],
    srcs: [
        "testing/dm_user_harness.cpp",
        "testing/harness.cpp",
        "user-space-merge/snapuserd_test.cpp",
    ],
    shared_libs: [
+116 −0
Original line number Diff line number Diff line
// Copyright (C) 2023 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 "harness.h"

#ifdef __ANDROID__
#include <linux/memfd.h>
#endif
#include <sys/mman.h>
#include <sys/resource.h>
#include <unistd.h>

#include <android-base/file.h>
#include <ext4_utils/ext4_utils.h>
#include <libdm/loop_control.h>
#include "snapuserd_logging.h"

namespace android {
namespace snapshot {

using namespace std::chrono_literals;
using android::base::unique_fd;
using android::dm::LoopDevice;

#ifdef __ANDROID__
// Prefer this on device since it is a real block device, which is more similar
// to how we use snapuserd.
class MemoryBackedDevice final : public IBackingDevice {
  public:
    bool Init(uint64_t size) {
        memfd_.reset(memfd_create("snapuserd_test", MFD_ALLOW_SEALING));
        if (memfd_ < 0) {
            PLOG(ERROR) << "memfd_create failed";
            return false;
        }
        if (ftruncate(memfd_.get(), size) < 0) {
            PLOG(ERROR) << "ftruncate failed";
            return false;
        }
        if (fcntl(memfd_.get(), F_ADD_SEALS, F_SEAL_GROW | F_SEAL_SHRINK) < 0) {
            PLOG(ERROR) << "fcntl seal failed";
            return false;
        }
        dev_ = std::make_unique<LoopDevice>(memfd_, 10s);
        return dev_->valid();
    }
    const std::string& GetPath() override { return dev_->device(); }
    uint64_t GetSize() override {
        unique_fd fd(open(GetPath().c_str(), O_RDONLY | O_CLOEXEC));
        if (fd < 0) {
            PLOG(ERROR) << "open failed: " << GetPath();
            return 0;
        }
        return get_block_device_size(fd.get());
    }

  private:
    unique_fd memfd_;
    std::unique_ptr<LoopDevice> dev_;
};
#endif

class FileBackedDevice final : public IBackingDevice {
  public:
    bool Init(uint64_t size) {
        if (temp_.fd < 0) {
            return false;
        }
        if (ftruncate(temp_.fd, size) < 0) {
            PLOG(ERROR) << "ftruncate failed: " << temp_.path;
            return false;
        }
        path_ = temp_.path;
        return true;
    }

    const std::string& GetPath() override { return path_; }
    uint64_t GetSize() override {
        off_t off = lseek(temp_.fd, 0, SEEK_END);
        if (off < 0) {
            PLOG(ERROR) << "lseek failed: " << temp_.path;
            return 0;
        }
        return off;
    }

  private:
    TemporaryFile temp_;
    std::string path_;
};

std::unique_ptr<IBackingDevice> ITestHarness::CreateBackingDevice(uint64_t size) {
#ifdef __ANDROID__
    auto dev = std::make_unique<MemoryBackedDevice>();
#else
    auto dev = std::make_unique<FileBackedDevice>();
#endif
    if (!dev->Init(size)) {
        return nullptr;
    }
    return dev;
}

}  // namespace snapshot
}  // namespace android
+9 −0
Original line number Diff line number Diff line
@@ -33,6 +33,14 @@ class IUserDevice {
    virtual bool Destroy() = 0;
};

// Interface for an fd/temp file that is a block device when possible.
class IBackingDevice {
  public:
    virtual ~IBackingDevice() {}
    virtual const std::string& GetPath() = 0;
    virtual uint64_t GetSize() = 0;
};

class ITestHarness {
  public:
    virtual ~ITestHarness() {}
@@ -41,6 +49,7 @@ class ITestHarness {
                                                          uint64_t num_sectors) = 0;
    virtual IBlockServerFactory* GetBlockServerFactory() = 0;
    virtual bool HasUserDevice() = 0;
    virtual std::unique_ptr<IBackingDevice> CreateBackingDevice(uint64_t size);
};

}  // namespace snapshot
+12 −35
Original line number Diff line number Diff line
@@ -17,7 +17,6 @@

#include <fcntl.h>
#include <linux/fs.h>
#include <linux/memfd.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/syscall.h>
@@ -97,7 +96,7 @@ class SnapuserdTest : public ::testing::Test {
    void InitDaemon();
    void CreateUserDevice();

    unique_ptr<LoopDevice> base_loop_;
    unique_ptr<IBackingDevice> base_dev_;
    unique_ptr<IUserDevice> dmuser_dev_;

    std::string system_device_ctrl_name_;
@@ -116,24 +115,6 @@ class SnapuserdTest : public ::testing::Test {
    std::unique_ptr<ITestHarness> harness_;
};

static 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;
}

void SnapuserdTest::SetUp() {
    harness_ = std::make_unique<DmUserTestHarness>();
}
@@ -189,14 +170,16 @@ bool SnapuserdTest::SetupDaemon() {
}

void SnapuserdTest::CreateBaseDevice() {
    unique_fd rnd_fd;

    total_base_size_ = (size_ * 5);
    base_fd_ = CreateTempFile("base_device", total_base_size_);

    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);

    rnd_fd.reset(open("/dev/random", O_RDONLY));
    ASSERT_TRUE(rnd_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);

@@ -206,9 +189,6 @@ void SnapuserdTest::CreateBaseDevice() {
    }

    ASSERT_EQ(lseek(base_fd_, 0, SEEK_SET), 0);

    base_loop_ = std::make_unique<LoopDevice>(base_fd_, 10s);
    ASSERT_TRUE(base_loop_->valid());
}

void SnapuserdTest::ReadSnapshotDeviceAndValidate() {
@@ -544,8 +524,8 @@ 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_loop_->device(),
                                 base_loop_->device(), opener, 1, use_iouring, false);
            handlers_.AddHandler(system_device_ctrl_name_, cow_system_->path, base_dev_->GetPath(),
                                 base_dev_->GetPath(), opener, 1, use_iouring, false);
    ASSERT_NE(handler, nullptr);
    ASSERT_NE(handler->snapuserd(), nullptr);
#ifdef __ANDROID__
@@ -566,11 +546,8 @@ void SnapuserdTest::SetDeviceControlName() {
}

void SnapuserdTest::CreateUserDevice() {
    unique_fd fd(TEMP_FAILURE_RETRY(open(base_loop_->device().c_str(), O_RDONLY | O_CLOEXEC)));
    ASSERT_TRUE(fd > 0);

    uint64_t dev_sz = get_block_device_size(fd.get());
    ASSERT_TRUE(dev_sz > 0);
    auto dev_sz = base_dev_->GetSize();
    ASSERT_NE(dev_sz, 0);

    cow_num_sectors_ = dev_sz >> 9;