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'):