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 <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
This commit is contained in:
Simon Glass
2025-12-22 20:36:42 -07:00
parent a6b1cd23a7
commit 8349f9f700
5 changed files with 86 additions and 6 deletions

View File

@@ -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
*

View File

@@ -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)

View File

@@ -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
*

View File

@@ -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 });

View File

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