From: Alberto Ruiz <[email protected]> Add a KUnit test that exercises the EROFS memback mount path with a compressed (LZ4) image. The test embeds a minimal 8 KiB EROFS image containing a single file with known content ("The quick brown fox. " repeated 250 times), mounts it via erofs_memback_set_pending(), reads back the decompressed file data, and verifies it byte-for-byte.
Since erofs_memback_set_pending() is __init, the test uses kunit_test_init_section_suites() so it runs during kernel init. Assisted-by: Claude:claude-opus-4-6 Signed-off-by: Alberto Ruiz <[email protected]> --- fs/erofs/Kconfig | 11 ++++ fs/erofs/Makefile | 1 + fs/erofs/memback_test.c | 151 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 163 insertions(+) diff --git a/fs/erofs/Kconfig b/fs/erofs/Kconfig index 97c48ebe8458..adf26e67924c 100644 --- a/fs/erofs/Kconfig +++ b/fs/erofs/Kconfig @@ -213,3 +213,14 @@ config EROFS_FS_PAGE_CACHE_SHARE content fingerprints on the same machine. If unsure, say N. + +config EROFS_FS_MEMBACK_KUNIT_TEST + bool "Test EROFS memory-backed mount" if !KUNIT_ALL_TESTS + depends on EROFS_FS=y && EROFS_FS_ZIP && KUNIT=y + default KUNIT_ALL_TESTS + help + KUnit test for the EROFS memory-backed mount path. Embeds a small + LZ4-compressed EROFS image and verifies that it can be mounted and + read via the memback interface during kernel init. + + Say N unless you are running KUnit tests. diff --git a/fs/erofs/Makefile b/fs/erofs/Makefile index 8f3b73835328..b98c95e96e85 100644 --- a/fs/erofs/Makefile +++ b/fs/erofs/Makefile @@ -12,3 +12,4 @@ erofs-$(CONFIG_EROFS_FS_BACKED_BY_FILE) += fileio.o erofs-$(CONFIG_EROFS_FS_ONDEMAND) += fscache.o erofs-y += memback.o erofs-$(CONFIG_EROFS_FS_PAGE_CACHE_SHARE) += ishare.o +obj-$(CONFIG_EROFS_FS_MEMBACK_KUNIT_TEST) += memback_test.o diff --git a/fs/erofs/memback_test.c b/fs/erofs/memback_test.c new file mode 100644 index 000000000000..9661b76cfa0f --- /dev/null +++ b/fs/erofs/memback_test.c @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * KUnit tests for EROFS memory-backed mount (memback). + * + * Embeds a minimal LZ4-compressed EROFS image and verifies that the memback + * path can mount it and serve decompressed file data correctly. + */ +#include <kunit/test.h> +#include <linux/fs.h> +#include <linux/init.h> +#include <linux/init_syscalls.h> +#include <linux/namei.h> +#include <linux/slab.h> +#include <uapi/linux/mount.h> +#include "internal.h" + +#define EROFS_MEMBACK_TEST_DIR "/erofs_memback_test" +#define EROFS_MEMBACK_TEST_FILE EROFS_MEMBACK_TEST_DIR "/testfile" + +/* + * "The quick brown fox. " repeated 250 times = 5250 bytes, LZ4-compressed + * into a 2-block (4096 x 2) EROFS image by: + * mkfs.erofs -zlz4 -b4096 -x-1 image.img testdir/ + * + * The full image is 8192 bytes but only 384 bytes are non-zero. We store + * just the two non-zero regions (superblock+metadata at offset 1024 and + * compressed data at offset 8140) and reconstruct the image at runtime. + * + * The image is placed at a non-page-aligned offset (3 × 512 = 1536 bytes) + * within the allocation to exercise the non-page-aligned memback path. + */ +#define EROFS_TEST_IMAGE_SIZE 8192 +#define EROFS_TEST_IMAGE_OFFSET 1536 + +static const unsigned char erofs_test_metadata[] __initconst = { + 0xe2, 0xe1, 0xf5, 0xe0, 0xee, 0x93, 0xd1, 0x4a, 0x03, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x24, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5d, 0x77, 0x31, 0x6a, 0x00, 0x00, 0x00, 0x00, 0xac, 0xb1, 0x04, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2c, 0x51, 0x16, 0x54, 0xcc, 0x6a, 0x43, 0xdd, 0x96, 0xc4, 0x3e, 0x45, + 0x16, 0x53, 0x0e, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0xed, 0x41, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, + 0xe8, 0x03, 0x00, 0x00, 0x50, 0x77, 0x31, 0x6a, 0x00, 0x00, 0x00, 0x00, + 0x7c, 0x8d, 0x20, 0x14, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x02, 0x00, + 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x00, 0x02, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, 0x00, 0x01, 0x00, + 0x2e, 0x2e, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x66, 0x69, 0x6c, 0x65, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0xa4, 0x81, 0x00, 0x00, + 0x82, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, + 0x50, 0x77, 0x31, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x3a, 0x37, 0x14, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x10, 0x82, 0x04, +}; + +static const unsigned char erofs_test_cdata[] __initconst = { + 0xff, 0x06, 0x54, 0x68, 0x65, 0x20, 0x71, 0x75, 0x69, 0x63, 0x6b, + 0x20, 0x62, 0x72, 0x6f, 0x77, 0x6e, 0x20, 0x66, 0x6f, 0x78, 0x2e, + 0x20, 0x15, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x69, 0x50, 0x66, 0x6f, 0x78, 0x2e, 0x20, +}; + +#define EXPECTED_PATTERN "The quick brown fox. " +#define EXPECTED_PATTERN_LEN 21 +#define EXPECTED_REPEATS 250 +#define EXPECTED_FILE_SIZE (EXPECTED_PATTERN_LEN * EXPECTED_REPEATS) + +static void __init erofs_memback_test_mount_compressed(struct kunit *test) +{ + void *buf; + struct file *file; + char *readbuf; + ssize_t nread; + int ret, i; + + buf = kzalloc(EROFS_TEST_IMAGE_OFFSET + EROFS_TEST_IMAGE_SIZE, + GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buf); + memcpy(buf + EROFS_TEST_IMAGE_OFFSET + 1024, erofs_test_metadata, + sizeof(erofs_test_metadata)); + memcpy(buf + EROFS_TEST_IMAGE_OFFSET + 8140, erofs_test_cdata, + sizeof(erofs_test_cdata)); + + erofs_memback_set_pending(buf + EROFS_TEST_IMAGE_OFFSET, + EROFS_TEST_IMAGE_SIZE); + + ret = init_mkdir(EROFS_MEMBACK_TEST_DIR, 0700); + KUNIT_ASSERT_EQ(test, ret, 0); + + ret = init_mount("none", EROFS_MEMBACK_TEST_DIR, "erofs", MS_RDONLY, + NULL); + if (ret) { + init_rmdir(EROFS_MEMBACK_TEST_DIR); + kfree(buf); + KUNIT_FAIL(test, "init_mount failed: %d", ret); + return; + } + + file = filp_open(EROFS_MEMBACK_TEST_FILE, O_RDONLY, 0); + if (IS_ERR(file)) { + init_umount(EROFS_MEMBACK_TEST_DIR, 0); + init_rmdir(EROFS_MEMBACK_TEST_DIR); + kfree(buf); + KUNIT_FAIL(test, "filp_open failed: %ld", PTR_ERR(file)); + return; + } + + KUNIT_EXPECT_EQ(test, (int)i_size_read(file_inode(file)), + EXPECTED_FILE_SIZE); + + readbuf = kzalloc(EXPECTED_FILE_SIZE, GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, readbuf); + + nread = kernel_read(file, readbuf, EXPECTED_FILE_SIZE, &file->f_pos); + KUNIT_EXPECT_EQ(test, (int)nread, EXPECTED_FILE_SIZE); + + for (i = 0; i < EXPECTED_REPEATS && nread == EXPECTED_FILE_SIZE; i++) + KUNIT_EXPECT_MEMEQ(test, readbuf + i * EXPECTED_PATTERN_LEN, + EXPECTED_PATTERN, EXPECTED_PATTERN_LEN); + + kfree(readbuf); + fput(file); + init_umount(EROFS_MEMBACK_TEST_DIR, 0); + init_rmdir(EROFS_MEMBACK_TEST_DIR); + kfree(buf); +} + +static struct kunit_case erofs_memback_test_cases[] __initdata = { + KUNIT_CASE(erofs_memback_test_mount_compressed), + {}, +}; + +static struct kunit_suite __refdata erofs_memback_test_suite = { + .name = "erofs_memback", + .test_cases = erofs_memback_test_cases, +}; +kunit_test_init_section_suites(&erofs_memback_test_suite); + +MODULE_DESCRIPTION("EROFS memback KUnit test suite"); +MODULE_LICENSE("GPL"); -- 2.53.0
