[minfs] Enable out-of-space Minfs test on FVM-backed partition
Additionally, force "resize" test to exhaust all space
on disk after resizing.
ZX-3320 #comment In Progress
Test: This patch is a test
Change-Id: I721ddfe723741060519f94d4817f0a8ed1d8ffd7
diff --git a/system/utest/fs/test-minfs.cpp b/system/utest/fs/test-minfs.cpp
index 981ee6e..ac5b691 100644
--- a/system/utest/fs/test-minfs.cpp
+++ b/system/utest/fs/test-minfs.cpp
@@ -30,6 +30,14 @@
namespace {
+// Using twice as many blocks and slices of half-size, we have just as much space, but we require
+// resizing to fill our filesystem.
+const test_disk_t kGrowableTestDisk = {
+ .block_count = TEST_BLOCK_COUNT_DEFAULT * 2,
+ .block_size = TEST_BLOCK_SIZE_DEFAULT,
+ .slice_size = TEST_FVM_SLICE_SIZE_DEFAULT / 2,
+};
+
bool QueryInfo(fuchsia_io_FilesystemInfo* info) {
BEGIN_HELPER;
fbl::unique_fd fd(open(kMountPath, O_RDONLY | O_DIRECTORY));
@@ -186,11 +194,13 @@
END_TEST;
}
-bool GetUsedBlocks(uint32_t* used_blocks) {
+bool GetFreeBlocks(uint32_t* out_free_blocks) {
BEGIN_HELPER;
fuchsia_io_FilesystemInfo info;
ASSERT_TRUE(QueryInfo(&info));
- *used_blocks = static_cast<uint32_t>((info.total_bytes - info.used_bytes) / info.block_size);
+ uint64_t total_bytes = info.total_bytes + info.free_shared_pool_bytes;
+ uint64_t used_bytes = info.used_bytes;
+ *out_free_blocks = static_cast<uint32_t>((total_bytes - used_bytes) / info.block_size);
END_HELPER;
}
@@ -203,7 +213,7 @@
uint32_t free_blocks;
while (true) {
- ASSERT_TRUE(GetUsedBlocks(&free_blocks));
+ ASSERT_TRUE(GetFreeBlocks(&free_blocks));
if (free_blocks <= max_remaining_blocks) {
break;
}
@@ -290,7 +300,7 @@
}
// Make sure we now have only 1 block remaining.
- ASSERT_TRUE(GetUsedBlocks(&free_blocks));
+ ASSERT_TRUE(GetFreeBlocks(&free_blocks));
ASSERT_EQ(free_blocks, 1);
// We should now have exactly 1 free block remaining. Attempt to write into the indirect
@@ -324,7 +334,7 @@
ASSERT_TRUE(sml_fd);
// Make sure we now have at least kMinfsDirect + 1 blocks remaining.
- ASSERT_TRUE(GetUsedBlocks(&free_blocks));
+ ASSERT_TRUE(GetFreeBlocks(&free_blocks));
ASSERT_GE(free_blocks, minfs::kMinfsDirect + 1);
// We have some room now, so create a new directory.
@@ -361,7 +371,7 @@
}
// Ensure that there is now exactly one block remaining.
- ASSERT_TRUE(GetUsedBlocks(&actual_blocks));
+ ASSERT_TRUE(GetFreeBlocks(&actual_blocks));
ASSERT_EQ(free_blocks, actual_blocks);
// Now, attempt to add one more file to the directory we created. Since it will need to
@@ -409,7 +419,7 @@
}
// Ensure that there is now exactly one block remaining.
- ASSERT_TRUE(GetUsedBlocks(&actual_blocks));
+ ASSERT_TRUE(GetFreeBlocks(&actual_blocks));
ASSERT_EQ(free_blocks, actual_blocks);
// Now, attempt to rename one of our original files under the new directory.
@@ -440,7 +450,7 @@
}
uint32_t original_blocks;
- ASSERT_TRUE(GetUsedBlocks(&original_blocks));
+ ASSERT_TRUE(GetFreeBlocks(&original_blocks));
uint32_t fd_count = 100;
fbl::unique_fd fds[fd_count];
@@ -473,7 +483,7 @@
// Check that the number of Minfs free blocks has decreased.
uint32_t current_blocks;
- ASSERT_TRUE(GetUsedBlocks(¤t_blocks));
+ ASSERT_TRUE(GetFreeBlocks(¤t_blocks));
ASSERT_LT(current_blocks, original_blocks);
// Put the ramdisk to sleep and close all the fds. This will cause file purge to fail,
@@ -491,7 +501,7 @@
// Writeback should have failed.
// However, the in-memory state has been updated correctly.
- ASSERT_TRUE(GetUsedBlocks(¤t_blocks));
+ ASSERT_TRUE(GetFreeBlocks(¤t_blocks));
ASSERT_EQ(current_blocks, original_blocks);
// Remount Minfs, which should cause leftover unlinked files to be removed.
@@ -499,7 +509,7 @@
ASSERT_TRUE(check_remount());
// Check that the block count has been reverted to the value before any files were added.
- ASSERT_TRUE(GetUsedBlocks(¤t_blocks));
+ ASSERT_TRUE(GetFreeBlocks(¤t_blocks));
ASSERT_EQ(current_blocks, original_blocks);
END_TEST;
@@ -522,3 +532,8 @@
RUN_TEST_MEDIUM(TestMetrics)
RUN_TEST_MEDIUM(TestUnlinkFail)
)
+
+// Running with an isolated FVM to avoid interactions with the other integration tests.
+FS_TEST_CASE(FsMinfsFullFvmTests, kGrowableTestDisk,
+ RUN_TEST_LARGE(TestFullOperations),
+FS_TEST_FVM, minfs, 1)
diff --git a/system/utest/fs/test-resize.cpp b/system/utest/fs/test-resize.cpp
index 72f641b..58c8c56 100644
--- a/system/utest/fs/test-resize.cpp
+++ b/system/utest/fs/test-resize.cpp
@@ -14,7 +14,9 @@
#include <fbl/algorithm.h>
#include <fbl/unique_ptr.h>
+#include <fuchsia/io/c/fidl.h>
#include <fvm/fvm.h>
+#include <lib/fzl/fdio.h>
#include <minfs/format.h>
#include <unittest/unittest.h>
#include <zircon/compiler.h>
@@ -25,35 +27,95 @@
namespace {
-bool TestUseAllInodes(void) {
+bool QueryInfo(uint64_t* out_free_pool_size) {
+ BEGIN_HELPER;
+ fuchsia_io_FilesystemInfo info;
+ fbl::unique_fd fd(open(kMountPath, O_RDONLY | O_DIRECTORY));
+ ASSERT_TRUE(fd);
+ zx_status_t status;
+ fzl::FdioCaller caller(std::move(fd));
+ ASSERT_EQ(fuchsia_io_DirectoryAdminQueryFilesystem(caller.borrow_channel(), &status, &info),
+ ZX_OK);
+ // This should always be true, for all filesystems.
+ ASSERT_GT(info.total_bytes, info.used_bytes);
+ *out_free_pool_size = info.free_shared_pool_bytes;
+ END_HELPER;
+}
+
+bool EnsureCanGrow() {
+ BEGIN_HELPER;
+ uint64_t free_pool_size;
+ ASSERT_TRUE(QueryInfo(&free_pool_size));
+ // This tests expects to run with free FVM space.
+ ASSERT_GT(free_pool_size, 0);
+ END_HELPER;
+}
+
+bool EnsureCannotGrow() {
+ BEGIN_HELPER;
+ uint64_t free_pool_size;
+ ASSERT_TRUE(QueryInfo(&free_pool_size));
+ ASSERT_EQ(free_pool_size, 0);
+ END_HELPER;
+}
+
+const test_disk_t max_inode_disk = {
+ .block_count = 1LLU << 15,
+ .block_size = 1LLU << 9,
+ .slice_size = 1LLU << 20,
+};
+
+template <bool Remount>
+bool TestUseAllInodes() {
BEGIN_TEST;
+ if (use_real_disk) {
+ fprintf(stderr, "Ramdisk required; skipping test\n");
+ return true;
+ }
ASSERT_TRUE(test_info->supports_resize);
+ ASSERT_TRUE(EnsureCanGrow());
// Create 100,000 inodes.
// We expect that this will force enough inodes to cause the
// filesystem structures to resize partway through.
constexpr size_t kFilesPerDirectory = 100;
- constexpr size_t kDirectoryCount = 1000;
- for (size_t d = 0; d < kDirectoryCount; d++) {
+ size_t d = 0;
+ while (true) {
if (d % 100 == 0) {
printf("Creating directory (containing 100 files): %lu\n", d);
}
char dname[128];
snprintf(dname, sizeof(dname), "::%lu", d);
- ASSERT_EQ(mkdir(dname, 0666), 0);
+ if (mkdir(dname, 0666) < 0) {
+ ASSERT_EQ(errno, ENOSPC);
+ break;
+ }
+ bool stop = false;
for (size_t f = 0; f < kFilesPerDirectory; f++) {
char fname[128];
snprintf(fname, sizeof(fname), "::%lu/%lu", d, f);
- int fd = open(fname, O_CREAT | O_RDWR | O_EXCL);
- ASSERT_GT(fd, 0);
- ASSERT_EQ(close(fd), 0);
+ fbl::unique_fd fd(open(fname, O_CREAT | O_RDWR | O_EXCL));
+ if (!fd) {
+ ASSERT_EQ(errno, ENOSPC);
+ stop = true;
+ break;
+ }
}
+ if (stop) {
+ break;
+ }
+ d++;
}
- printf("Unmounting, Re-mounting, verifying...\n");
- ASSERT_TRUE(check_remount(), "Could not remount filesystem");
+ ASSERT_TRUE(EnsureCannotGrow());
- for (size_t d = 0; d < kDirectoryCount; d++) {
+ if (Remount) {
+ printf("Unmounting, Re-mounting, verifying...\n");
+ ASSERT_TRUE(check_remount(), "Could not remount filesystem");
+ }
+
+ size_t directory_count = d;
+ for (size_t d = 0; d < directory_count; d++) {
if (d % 100 == 0) {
printf("Deleting directory (containing 100 files): %lu\n", d);
}
@@ -70,49 +132,70 @@
END_TEST;
}
-const test_disk_t disk = {
+const test_disk_t max_data_disk = {
.block_count = 1LLU << 17,
.block_size = 1LLU << 9,
.slice_size = 1LLU << 20,
};
-bool TestUseAllData(void) {
+template <bool Remount>
+bool TestUseAllData() {
BEGIN_TEST;
+ if (use_real_disk) {
+ fprintf(stderr, "Ramdisk required; skipping test\n");
+ return true;
+ }
constexpr size_t kBufSize = (1 << 20);
- constexpr size_t kFileBufCount = 19;
+ constexpr size_t kFileBufCount = 20;
ASSERT_TRUE(test_info->supports_resize);
+ ASSERT_TRUE(EnsureCanGrow());
uint64_t disk_size = test_disk_info.block_count * test_disk_info.block_size;
- size_t metadata_size = fvm::MetadataSize(disk_size, disk.slice_size);
+ size_t metadata_size = fvm::MetadataSize(disk_size, max_data_disk.slice_size);
ASSERT_GT(disk_size, metadata_size * 2);
disk_size -= 2 * metadata_size;
- ASSERT_GT(disk_size, minfs::kMinfsMinimumSlices * disk.slice_size);
- disk_size -= minfs::kMinfsMinimumSlices * disk.slice_size;
+ ASSERT_GT(disk_size, minfs::kMinfsMinimumSlices * max_data_disk.slice_size);
+ disk_size -= minfs::kMinfsMinimumSlices * max_data_disk.slice_size;
- size_t file_count = disk_size / kBufSize / kFileBufCount;
- ASSERT_GT(file_count, 0);
-
- fbl::AllocChecker ac;
- fbl::unique_ptr<uint8_t[]> buf(new (&ac) uint8_t[kBufSize]);
- ASSERT_TRUE(ac.check());
+ fbl::unique_ptr<uint8_t[]> buf(new uint8_t[kBufSize]);
memset(buf.get(), 0, kBufSize);
- for (size_t f = 0; f < file_count; f++) {
- printf("Creating 19 MB file #%lu\n", f);
+ size_t f = 0;
+ while (true) {
+ printf("Creating 20 MB file #%lu\n", f);
char fname[128];
snprintf(fname, sizeof(fname), "::%lu", f);
- int fd = open(fname, O_CREAT | O_RDWR | O_EXCL);
- ASSERT_GT(fd, 0);
- for (size_t i = 0; i < kFileBufCount; i++) {
- ASSERT_EQ(write(fd, buf.get(), kBufSize), kBufSize);
+ fbl::unique_fd fd(open(fname, O_CREAT | O_RDWR | O_EXCL));
+ if (!fd) {
+ ASSERT_EQ(errno, ENOSPC);
+ break;
}
- ASSERT_EQ(close(fd), 0);
+ f++;
+ bool stop = false;
+ for (size_t i = 0; i < kFileBufCount; i++) {
+ ASSERT_EQ(ftruncate(fd.get(), kBufSize * kFileBufCount), 0);
+ ssize_t r = write(fd.get(), buf.get(), kBufSize);
+ if (r != kBufSize) {
+ ASSERT_EQ(errno, ENOSPC);
+ stop = true;
+ break;
+ }
+ }
+ if (stop) {
+ break;
+ }
}
- ASSERT_TRUE(check_remount(), "Could not remount filesystem");
+ ASSERT_TRUE(EnsureCannotGrow());
+ if (Remount) {
+ printf("Unmounting, Re-mounting, verifying...\n");
+ ASSERT_TRUE(check_remount(), "Could not remount filesystem");
+ }
+
+ size_t file_count = f;
for (size_t f = 0; f < file_count; f++) {
char fname[128];
snprintf(fname, sizeof(fname), "::%lu", f);
@@ -125,10 +208,18 @@
} // namespace
// Reformat the disk between tests to restore original size.
-RUN_FOR_ALL_FILESYSTEMS_TYPE(fs_resize_tests_inodes, disk, FS_TEST_FVM,
- RUN_TEST_LARGE(TestUseAllInodes)
+RUN_FOR_ALL_FILESYSTEMS_TYPE(fs_resize_tests_inodes_remount, max_inode_disk, FS_TEST_FVM,
+ RUN_TEST_LARGE((TestUseAllInodes<true>))
)
-RUN_FOR_ALL_FILESYSTEMS_TYPE(fs_resize_tests_data, disk, FS_TEST_FVM,
- RUN_TEST_LARGE(TestUseAllData)
+RUN_FOR_ALL_FILESYSTEMS_TYPE(fs_resize_tests_inodes, max_inode_disk, FS_TEST_FVM,
+ RUN_TEST_LARGE((TestUseAllInodes<false>))
+)
+
+RUN_FOR_ALL_FILESYSTEMS_TYPE(fs_resize_tests_data_remount, max_data_disk, FS_TEST_FVM,
+ RUN_TEST_LARGE((TestUseAllData<true>))
+)
+
+RUN_FOR_ALL_FILESYSTEMS_TYPE(fs_resize_tests_data, max_data_disk, FS_TEST_FVM,
+ RUN_TEST_LARGE((TestUseAllData<false>))
)