From 8349f9f70078fc9542b69abb0d2b4fa2a1823fec Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 22 Dec 2025 20:36:42 -0700 Subject: [PATCH] fs: ext4l: Add statfs support Implement ext4l_statfs() to return filesystem statistics using the ext4 superblock. The function returns block size, total block count, and free block count. Add unit test to verify the statfs implementation returns valid data. Cover-letter: fs: ext4l: Complete read-only filesystem support (Part I) This series completes read-only support for the ext4l filesystem driver, which is a port of the Linux ext4 driver to U-Boot. The ext4l driver provides more complete ext4 support than the existing ext4 driver, including proper handling of extents, directory hashing, and other ext4 features. Changes include: Sandbox infrastructure: - Fix IRQ macros and buffer_head includes for sandbox builds Core fixes: - Fix path lookup by implementing proper dentry operations - Fix fscrypt_match_name to do actual name comparison Filesystem operations: - Add directory listing (opendir/readdir/closedir) - Add file existence check (exists) - Add file size query (size) - Add file read support (read) - Add UUID query (uuid) - Add filesystem statistics (statfs) New command: - Add fsinfo command to display filesystem statistics Testing: - Add comprehensive unit tests for all operations - Enable fsuuid command for sandbox testing END Co-developed-by: Claude Opus 4.5 Signed-off-by: Simon Glass --- fs/ext4l/interface.c | 22 ++++++++++++ fs/fs_legacy.c | 2 +- include/ext4l.h | 9 +++++ test/fs/ext4l.c | 52 ++++++++++++++++++++++++++--- test/py/tests/test_fs/test_ext4l.py | 7 ++++ 5 files changed, 86 insertions(+), 6 deletions(-) diff --git a/fs/ext4l/interface.c b/fs/ext4l/interface.c index f25664369e6..7c37c99488a 100644 --- a/fs/ext4l/interface.c +++ b/fs/ext4l/interface.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -100,6 +101,27 @@ int ext4l_uuid(char *uuid_str) return 0; } +/** + * ext4l_statfs() - Get filesystem statistics + * + * @stats: Pointer to fs_statfs structure to fill + * Return: 0 on success, -ENODEV if not mounted + */ +int ext4l_statfs(struct fs_statfs *stats) +{ + struct ext4_super_block *es; + + if (!ext4l_sb) + return -ENODEV; + + es = EXT4_SB(ext4l_sb)->s_es; + stats->bsize = ext4l_sb->s_blocksize; + stats->blocks = ext4_blocks_count(es); + stats->bfree = ext4_free_blocks_count(es); + + return 0; +} + /** * ext4l_set_blk_dev() - Set the block device for ext4l operations * diff --git a/fs/fs_legacy.c b/fs/fs_legacy.c index efb5ab669ff..155092519dd 100644 --- a/fs/fs_legacy.c +++ b/fs/fs_legacy.c @@ -292,7 +292,7 @@ static struct fstype_info fstypes[] = { .mkdir = fs_mkdir_unsupported, .ln = fs_ln_unsupported, .rename = fs_rename_unsupported, - .statfs = fs_statfs_unsupported, + .statfs = ext4l_statfs, }, #endif #if IS_ENABLED(CONFIG_SANDBOX) && !IS_ENABLED(CONFIG_XPL_BUILD) diff --git a/include/ext4l.h b/include/ext4l.h index 9d9e79b7695..9cfe4867ffa 100644 --- a/include/ext4l.h +++ b/include/ext4l.h @@ -13,6 +13,7 @@ struct blk_desc; struct disk_partition; struct fs_dir_stream; struct fs_dirent; +struct fs_statfs; /** * ext4l_probe() - Probe a block device for an ext4 filesystem @@ -84,6 +85,14 @@ int ext4l_get_uuid(u8 *uuid); */ int ext4l_uuid(char *uuid_str); +/** + * ext4l_statfs() - Get filesystem statistics + * + * @stats: Pointer to fs_statfs structure to fill + * Return: 0 on success, -ENODEV if not mounted + */ +int ext4l_statfs(struct fs_statfs *stats); + /** * ext4l_opendir() - Open a directory for iteration * diff --git a/test/fs/ext4l.c b/test/fs/ext4l.c index 2641ac3678c..43801f252f7 100644 --- a/test/fs/ext4l.c +++ b/test/fs/ext4l.c @@ -331,7 +331,7 @@ FS_TEST_ARGS(fs_test_ext4l_uuid_norun, UTF_SCAN_FDT | UTF_CONSOLE | UTF_MANUAL, /** * fs_test_ext4l_fsinfo_norun() - Test fsinfo command * - * This test verifies that the fsinfo command displays filesystem statistics. + * Verifies that the fsinfo command displays filesystem statistics. * * Arguments: * fs_image: Path to the ext4 filesystem image @@ -339,18 +339,60 @@ FS_TEST_ARGS(fs_test_ext4l_uuid_norun, UTF_SCAN_FDT | UTF_CONSOLE | UTF_MANUAL, static int fs_test_ext4l_fsinfo_norun(struct unit_test_state *uts) { const char *fs_image = ut_str(EXT4L_ARG_IMAGE); + struct fs_statfs stats; + u64 used; ut_assertnonnull(fs_image); ut_assertok(run_commandf("host bind 0 %s", fs_image)); + ut_assertok(fs_set_blk_dev("host", "0", FS_TYPE_ANY)); + ut_assertok(ext4l_statfs(&stats)); + used = stats.blocks - stats.bfree; + console_record_reset_enable(); ut_assertok(run_commandf("fsinfo host 0")); - ut_assert_nextlinen("Block size:"); - ut_assert_nextlinen("Total blocks:"); - ut_assert_nextlinen("Used blocks:"); - ut_assert_nextlinen("Free blocks:"); + + /* Skip any EXT4-fs mount messages, check output format */ + ut_assert_skip_to_line("Block size: %lu bytes", stats.bsize); + ut_assert_nextlinen("Total blocks: %llu (%llu bytes,", + stats.blocks, stats.blocks * stats.bsize); + ut_assert_nextlinen("Used blocks: %llu (%llu bytes,", + used, used * stats.bsize); + ut_assert_nextlinen("Free blocks: %llu (%llu bytes,", + stats.bfree, stats.bfree * stats.bsize); ut_assert_console_end(); return 0; } FS_TEST_ARGS(fs_test_ext4l_fsinfo_norun, UTF_SCAN_FDT | UTF_CONSOLE | UTF_MANUAL, { "fs_image", UT_ARG_STR }); + +/** + * fs_test_ext4l_statfs_norun() - Test ext4l_statfs function + * + * Verifies that ext4l can return filesystem statistics. + * + * Arguments: + * fs_image: Path to the ext4 filesystem image + */ +static int fs_test_ext4l_statfs_norun(struct unit_test_state *uts) +{ + const char *fs_image = ut_str(EXT4L_ARG_IMAGE); + struct fs_statfs stats; + + ut_assertnonnull(fs_image); + ut_assertok(run_commandf("host bind 0 %s", fs_image)); + ut_assertok(fs_set_blk_dev("host", "0", FS_TYPE_ANY)); + + /* Get filesystem statistics */ + ut_assertok(ext4l_statfs(&stats)); + + /* Verify reasonable values for a 64MB filesystem */ + ut_asserteq(SZ_4K, stats.bsize); + ut_assert(stats.blocks > 0); + ut_assert(stats.bfree > 0); + ut_assert(stats.bfree <= stats.blocks); + + return 0; +} +FS_TEST_ARGS(fs_test_ext4l_statfs_norun, UTF_SCAN_FDT | UTF_CONSOLE | UTF_MANUAL, + { "fs_image", UT_ARG_STR }); diff --git a/test/py/tests/test_fs/test_ext4l.py b/test/py/tests/test_fs/test_ext4l.py index 0a9da40f358..754c2cc69c4 100644 --- a/test/py/tests/test_fs/test_ext4l.py +++ b/test/py/tests/test_fs/test_ext4l.py @@ -125,6 +125,13 @@ class TestExt4l: f'ut -f fs fs_test_ext4l_uuid_norun fs_image={ext4_image}') assert 'failures: 0' in output + def test_statfs(self, ubman, ext4_image): + """Test that ext4l can return filesystem statistics.""" + with ubman.log.section('Test ext4l statfs'): + output = ubman.run_command( + f'ut -f fs fs_test_ext4l_statfs_norun fs_image={ext4_image}') + assert 'failures: 0' in output + def test_fsinfo(self, ubman, ext4_image): """Test that fsinfo command displays filesystem statistics.""" with ubman.log.section('Test ext4l fsinfo'):