From: Simon Glass <simon.glass@canonical.com>
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.
Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
---
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(-)
@@ -12,6 +12,7 @@
#include <blk.h>
#include <env.h>
#include <fs.h>
+#include <fs_legacy.h>
#include <membuf.h>
#include <part.h>
#include <malloc.h>
@@ -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
*
@@ -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)
@@ -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
*
@@ -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 });
@@ -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'):