Move ENOSPC tests to libfiemap.
These tests are still giving us trouble. Move them to libfiemap, which (1) is closer to the source of implementation, and (2) allows us to re-use the temporary filesystem code. This won't perturb the state of the actual device. The new test creates a 64MB ext4 or f2fs mount point as a sandbox, which should be much safer. Bug: 285197715 Bug: 298346574 Bug: 299142557 Test: fiemap_writer_test Merged-In: I33502d49613be4f269a80e5c632514fc56a0246a Ignore-AOSP-First: cherry-pick Change-Id: Iedb7c32a594c3b1fca2904f3441029aaed7edf2a
This commit is contained in:
parent
c742bfda6b
commit
3a60e82512
4 changed files with 87 additions and 110 deletions
|
@ -93,6 +93,9 @@ cc_test {
|
|||
test_options: {
|
||||
min_shipping_api_level: 29,
|
||||
},
|
||||
header_libs: [
|
||||
"libstorage_literals_headers",
|
||||
],
|
||||
require_root: true,
|
||||
}
|
||||
|
||||
|
|
|
@ -22,21 +22,25 @@
|
|||
#include <string.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/statvfs.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/vfs.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <fstab/fstab.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <libdm/loop_control.h>
|
||||
#include <libfiemap/fiemap_writer.h>
|
||||
#include <libfiemap/split_fiemap_writer.h>
|
||||
#include <libgsi/libgsi.h>
|
||||
#include <storage_literals/storage_literals.h>
|
||||
|
||||
#include "utility.h"
|
||||
|
||||
|
@ -46,6 +50,7 @@ namespace fiemap {
|
|||
using namespace std;
|
||||
using namespace std::string_literals;
|
||||
using namespace android::fiemap;
|
||||
using namespace android::storage_literals;
|
||||
using unique_fd = android::base::unique_fd;
|
||||
using LoopDevice = android::dm::LoopDevice;
|
||||
|
||||
|
@ -427,90 +432,123 @@ TEST_F(SplitFiemapTest, WritePastEnd) {
|
|||
ASSERT_FALSE(ptr->Write(buffer.get(), kSize));
|
||||
}
|
||||
|
||||
class VerifyBlockWritesExt4 : public ::testing::Test {
|
||||
// Get max file size and free space.
|
||||
std::pair<uint64_t, uint64_t> GetBigFileLimit(const std::string& mount_point) {
|
||||
struct statvfs fs;
|
||||
if (statvfs(mount_point.c_str(), &fs) < 0) {
|
||||
PLOG(ERROR) << "statfs failed";
|
||||
return {0, 0};
|
||||
}
|
||||
|
||||
auto fs_limit = static_cast<uint64_t>(fs.f_blocks) * (fs.f_bsize - 1);
|
||||
auto fs_free = static_cast<uint64_t>(fs.f_bfree) * fs.f_bsize;
|
||||
|
||||
LOG(INFO) << "Big file limit: " << fs_limit << ", free space: " << fs_free;
|
||||
|
||||
return {fs_limit, fs_free};
|
||||
}
|
||||
|
||||
class FsTest : public ::testing::Test {
|
||||
protected:
|
||||
// 2GB Filesystem and 4k block size by default
|
||||
static constexpr uint64_t block_size = 4096;
|
||||
static constexpr uint64_t fs_size = 2147483648;
|
||||
static constexpr uint64_t fs_size = 64 * 1024 * 1024;
|
||||
|
||||
protected:
|
||||
void SetUp() override {
|
||||
fs_path = std::string(getenv("TMPDIR")) + "/ext4_2G.img";
|
||||
void SetUp() {
|
||||
android::fs_mgr::Fstab fstab;
|
||||
ASSERT_TRUE(android::fs_mgr::ReadFstabFromFile("/proc/mounts", &fstab));
|
||||
|
||||
ASSERT_EQ(access(tmpdir_.path, F_OK), 0);
|
||||
fs_path_ = tmpdir_.path + "/fs_image"s;
|
||||
mntpoint_ = tmpdir_.path + "/mnt_point"s;
|
||||
|
||||
auto entry = android::fs_mgr::GetEntryForMountPoint(&fstab, "/data");
|
||||
ASSERT_NE(entry, nullptr);
|
||||
if (entry->fs_type == "ext4") {
|
||||
SetUpExt4();
|
||||
} else if (entry->fs_type == "f2fs") {
|
||||
SetUpF2fs();
|
||||
} else {
|
||||
FAIL() << "Unrecognized fs_type: " << entry->fs_type;
|
||||
}
|
||||
}
|
||||
|
||||
void SetUpExt4() {
|
||||
uint64_t count = fs_size / block_size;
|
||||
std::string dd_cmd =
|
||||
::android::base::StringPrintf("/system/bin/dd if=/dev/zero of=%s bs=%" PRIu64
|
||||
" count=%" PRIu64 " > /dev/null 2>&1",
|
||||
fs_path.c_str(), block_size, count);
|
||||
fs_path_.c_str(), block_size, count);
|
||||
std::string mkfs_cmd =
|
||||
::android::base::StringPrintf("/system/bin/mkfs.ext4 -q %s", fs_path.c_str());
|
||||
::android::base::StringPrintf("/system/bin/mkfs.ext4 -q %s", fs_path_.c_str());
|
||||
// create mount point
|
||||
mntpoint = std::string(getenv("TMPDIR")) + "/fiemap_mnt";
|
||||
ASSERT_EQ(mkdir(mntpoint.c_str(), S_IRWXU), 0);
|
||||
ASSERT_EQ(mkdir(mntpoint_.c_str(), S_IRWXU), 0);
|
||||
// create file for the file system
|
||||
int ret = system(dd_cmd.c_str());
|
||||
ASSERT_EQ(ret, 0);
|
||||
// Get and attach a loop device to the filesystem we created
|
||||
LoopDevice loop_dev(fs_path, 10s);
|
||||
LoopDevice loop_dev(fs_path_, 10s);
|
||||
ASSERT_TRUE(loop_dev.valid());
|
||||
// create file system
|
||||
ret = system(mkfs_cmd.c_str());
|
||||
ASSERT_EQ(ret, 0);
|
||||
|
||||
// mount the file system
|
||||
ASSERT_EQ(mount(loop_dev.device().c_str(), mntpoint.c_str(), "ext4", 0, nullptr), 0);
|
||||
ASSERT_EQ(mount(loop_dev.device().c_str(), mntpoint_.c_str(), "ext4", 0, nullptr), 0);
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
umount(mntpoint.c_str());
|
||||
rmdir(mntpoint.c_str());
|
||||
unlink(fs_path.c_str());
|
||||
}
|
||||
|
||||
std::string mntpoint;
|
||||
std::string fs_path;
|
||||
};
|
||||
|
||||
class VerifyBlockWritesF2fs : public ::testing::Test {
|
||||
// 2GB Filesystem and 4k block size by default
|
||||
static constexpr uint64_t block_size = 4096;
|
||||
static constexpr uint64_t fs_size = 2147483648;
|
||||
|
||||
protected:
|
||||
void SetUp() override {
|
||||
fs_path = std::string(getenv("TMPDIR")) + "/f2fs_2G.img";
|
||||
void SetUpF2fs() {
|
||||
uint64_t count = fs_size / block_size;
|
||||
std::string dd_cmd =
|
||||
::android::base::StringPrintf("/system/bin/dd if=/dev/zero of=%s bs=%" PRIu64
|
||||
" count=%" PRIu64 " > /dev/null 2>&1",
|
||||
fs_path.c_str(), block_size, count);
|
||||
fs_path_.c_str(), block_size, count);
|
||||
std::string mkfs_cmd =
|
||||
::android::base::StringPrintf("/system/bin/make_f2fs -q %s", fs_path.c_str());
|
||||
::android::base::StringPrintf("/system/bin/make_f2fs -q %s", fs_path_.c_str());
|
||||
// create mount point
|
||||
mntpoint = std::string(getenv("TMPDIR")) + "/fiemap_mnt";
|
||||
ASSERT_EQ(mkdir(mntpoint.c_str(), S_IRWXU), 0);
|
||||
ASSERT_EQ(mkdir(mntpoint_.c_str(), S_IRWXU), 0);
|
||||
// create file for the file system
|
||||
int ret = system(dd_cmd.c_str());
|
||||
ASSERT_EQ(ret, 0);
|
||||
// Get and attach a loop device to the filesystem we created
|
||||
LoopDevice loop_dev(fs_path, 10s);
|
||||
LoopDevice loop_dev(fs_path_, 10s);
|
||||
ASSERT_TRUE(loop_dev.valid());
|
||||
// create file system
|
||||
ret = system(mkfs_cmd.c_str());
|
||||
ASSERT_EQ(ret, 0);
|
||||
|
||||
// mount the file system
|
||||
ASSERT_EQ(mount(loop_dev.device().c_str(), mntpoint.c_str(), "f2fs", 0, nullptr), 0);
|
||||
ASSERT_EQ(mount(loop_dev.device().c_str(), mntpoint_.c_str(), "f2fs", 0, nullptr), 0);
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
umount(mntpoint.c_str());
|
||||
rmdir(mntpoint.c_str());
|
||||
unlink(fs_path.c_str());
|
||||
umount(mntpoint_.c_str());
|
||||
rmdir(mntpoint_.c_str());
|
||||
unlink(fs_path_.c_str());
|
||||
}
|
||||
|
||||
std::string mntpoint;
|
||||
std::string fs_path;
|
||||
TemporaryDir tmpdir_;
|
||||
std::string mntpoint_;
|
||||
std::string fs_path_;
|
||||
};
|
||||
|
||||
TEST_F(FsTest, LowSpaceError) {
|
||||
auto limits = GetBigFileLimit(mntpoint_);
|
||||
ASSERT_GE(limits.first, 0);
|
||||
|
||||
FiemapUniquePtr ptr;
|
||||
|
||||
auto test_file = mntpoint_ + "/big_file";
|
||||
auto status = FiemapWriter::Open(test_file, limits.first, &ptr);
|
||||
ASSERT_FALSE(status.is_ok());
|
||||
ASSERT_EQ(status.error_code(), FiemapStatus::ErrorCode::NO_SPACE);
|
||||
|
||||
// Also test for EFBIG.
|
||||
status = FiemapWriter::Open(test_file, 16_TiB, &ptr);
|
||||
ASSERT_FALSE(status.is_ok());
|
||||
ASSERT_NE(status.error_code(), FiemapStatus::ErrorCode::NO_SPACE);
|
||||
}
|
||||
|
||||
bool DetermineBlockSize() {
|
||||
struct statfs s;
|
||||
if (statfs(gTestDir.c_str(), &s)) {
|
||||
|
|
|
@ -2312,32 +2312,6 @@ TEST_F(SnapshotUpdateTest, Overflow) {
|
|||
<< "FinishedSnapshotWrites should detect overflow of CoW device.";
|
||||
}
|
||||
|
||||
TEST_F(SnapshotUpdateTest, LowSpace) {
|
||||
static constexpr auto kMaxFree = 10_MiB;
|
||||
auto userdata = std::make_unique<LowSpaceUserdata>();
|
||||
ASSERT_TRUE(userdata->Init(kMaxFree));
|
||||
|
||||
// Grow all partitions to 10_MiB, total 30_MiB. This requires 30 MiB of CoW space. After
|
||||
// using the empty space in super (< 1 MiB), it uses 30 MiB of /userdata space.
|
||||
constexpr uint64_t partition_size = 10_MiB;
|
||||
SetSize(sys_, partition_size);
|
||||
SetSize(vnd_, partition_size);
|
||||
SetSize(prd_, partition_size);
|
||||
sys_->set_estimate_cow_size(partition_size);
|
||||
vnd_->set_estimate_cow_size(partition_size);
|
||||
prd_->set_estimate_cow_size(partition_size);
|
||||
|
||||
AddOperationForPartitions();
|
||||
|
||||
// Execute the update.
|
||||
ASSERT_TRUE(sm->BeginUpdate());
|
||||
auto res = sm->CreateUpdateSnapshots(manifest_);
|
||||
ASSERT_FALSE(res);
|
||||
ASSERT_EQ(Return::ErrorCode::NO_SPACE, res.error_code());
|
||||
ASSERT_GE(res.required_size(), 14_MiB);
|
||||
ASSERT_LT(res.required_size(), 40_MiB);
|
||||
}
|
||||
|
||||
TEST_F(SnapshotUpdateTest, AddPartition) {
|
||||
group_->add_partition_names("dlkm");
|
||||
|
||||
|
@ -2699,50 +2673,6 @@ INSTANTIATE_TEST_SUITE_P(Snapshot, FlashAfterUpdateTest, Combine(Values(0, 1), B
|
|||
"Merge"s;
|
||||
});
|
||||
|
||||
class ImageManagerTest : public SnapshotTest {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
SKIP_IF_NON_VIRTUAL_AB();
|
||||
SnapshotTest::SetUp();
|
||||
}
|
||||
void TearDown() override {
|
||||
RETURN_IF_NON_VIRTUAL_AB();
|
||||
CleanUp();
|
||||
}
|
||||
void CleanUp() {
|
||||
if (!image_manager_) {
|
||||
return;
|
||||
}
|
||||
EXPECT_TRUE(!image_manager_->BackingImageExists(kImageName) ||
|
||||
image_manager_->DeleteBackingImage(kImageName));
|
||||
}
|
||||
|
||||
static constexpr const char* kImageName = "my_image";
|
||||
};
|
||||
|
||||
TEST_F(ImageManagerTest, CreateImageNoSpace) {
|
||||
bool at_least_one_failure = false;
|
||||
for (uint64_t size = 1_MiB; size <= 512_MiB; size *= 2) {
|
||||
auto userdata = std::make_unique<LowSpaceUserdata>();
|
||||
ASSERT_TRUE(userdata->Init(size));
|
||||
|
||||
uint64_t to_allocate = userdata->free_space() + userdata->bsize();
|
||||
|
||||
auto res = image_manager_->CreateBackingImage(kImageName, to_allocate,
|
||||
IImageManager::CREATE_IMAGE_DEFAULT);
|
||||
if (!res) {
|
||||
at_least_one_failure = true;
|
||||
} else {
|
||||
ASSERT_EQ(res.error_code(), FiemapStatus::ErrorCode::NO_SPACE) << res.string();
|
||||
}
|
||||
|
||||
CleanUp();
|
||||
}
|
||||
|
||||
ASSERT_TRUE(at_least_one_failure)
|
||||
<< "We should have failed to allocate at least one over-sized image";
|
||||
}
|
||||
|
||||
bool Mkdir(const std::string& path) {
|
||||
if (mkdir(path.c_str(), 0700) && errno != EEXIST) {
|
||||
std::cerr << "Could not mkdir " << path << ": " << strerror(errno) << std::endl;
|
||||
|
|
|
@ -37,6 +37,7 @@ using B = Size<0>;
|
|||
using KiB = Size<10>;
|
||||
using MiB = Size<20>;
|
||||
using GiB = Size<30>;
|
||||
using TiB = Size<40>;
|
||||
|
||||
constexpr B operator""_B(unsigned long long v) { // NOLINT
|
||||
return B{v};
|
||||
|
@ -54,6 +55,10 @@ constexpr GiB operator""_GiB(unsigned long long v) { // NOLINT
|
|||
return GiB{v};
|
||||
}
|
||||
|
||||
constexpr TiB operator""_TiB(unsigned long long v) { // NOLINT
|
||||
return TiB{v};
|
||||
}
|
||||
|
||||
template <typename Dest, typename Src>
|
||||
constexpr Dest size_cast(Src src) {
|
||||
if (Src::power < Dest::power) {
|
||||
|
@ -69,6 +74,7 @@ static_assert(1_B == 1);
|
|||
static_assert(1_KiB == 1 << 10);
|
||||
static_assert(1_MiB == 1 << 20);
|
||||
static_assert(1_GiB == 1 << 30);
|
||||
static_assert(1_TiB == 1ULL << 40);
|
||||
static_assert(size_cast<KiB>(1_B).count() == 0);
|
||||
static_assert(size_cast<KiB>(1024_B).count() == 1);
|
||||
static_assert(size_cast<KiB>(1_MiB).count() == 1024);
|
||||
|
|
Loading…
Reference in a new issue