[Concept,15/16] cmd: Add fsinfo command

Message ID 20251227204318.886983-16-sjg@u-boot.org
State New
Headers
Series fs: ext4l: Complete read-only filesystem support (Part I) |

Commit Message

Simon Glass Dec. 27, 2025, 8:43 p.m. UTC
  From: Simon Glass <simon.glass@canonical.com>

Add the fsinfo command to display filesystem statistics including block
size, total blocks, used blocks, and free blocks. Both raw byte counts
and human-readable sizes are shown.

Example output:
  => fsinfo mmc 0:1
  Block size: 4096 bytes
  Total blocks: 16384 (67108864 bytes, 64 MiB)
  Used blocks: 2065 (8458240 bytes, 8.1 MiB)
  Free blocks: 14319 (58650624 bytes, 55.9 MiB)

Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
---

 cmd/Kconfig                         | 10 ++++++
 cmd/fs.c                            | 14 ++++++++
 doc/usage/cmd/fsinfo.rst            | 53 +++++++++++++++++++++++++++++
 doc/usage/index.rst                 |  1 +
 fs/fs_legacy.c                      | 32 +++++++++++++++++
 include/fs_cmd.h                    | 11 ++++++
 test/fs/ext4l.c                     | 27 +++++++++++++++
 test/py/tests/test_fs/test_ext4l.py |  7 ++++
 8 files changed, 155 insertions(+)
 create mode 100644 doc/usage/cmd/fsinfo.rst
  

Patch

diff --git a/cmd/Kconfig b/cmd/Kconfig
index 5cb34509746..d602f430ab6 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -2839,6 +2839,16 @@  config CMD_FS_UUID
 	help
 	  Enables fsuuid command for filesystem UUID.
 
+config CMD_FSINFO
+	bool "fsinfo command"
+	depends on !CMD_JFFS2
+	default y if SANDBOX
+	help
+	  Enables the fsinfo command which prints filesystem statistics
+	  such as block size, total blocks, used blocks and free blocks.
+	  This command conflicts with the JFFS2 fsinfo command, so it
+	  cannot be enabled when JFFS2 support is enabled.
+
 config CMD_LUKS
 	bool "luks command"
 	depends on BLK_LUKS
diff --git a/cmd/fs.c b/cmd/fs.c
index 7058f17b24f..960fd024c75 100644
--- a/cmd/fs.c
+++ b/cmd/fs.c
@@ -152,3 +152,17 @@  U_BOOT_CMD(
 	"    - renames/moves a file/directory in 'dev' on 'interface' from\n"
 	"      'old_path' to 'new_path'"
 );
+
+#ifdef CONFIG_CMD_FSINFO
+static int do_fsinfo_wrapper(struct cmd_tbl *cmdtp, int flag, int argc,
+			     char *const argv[])
+{
+	return do_fs_statfs(cmdtp, flag, argc, argv);
+}
+
+U_BOOT_CMD(fsinfo, 3, 1, do_fsinfo_wrapper,
+	   "Print filesystem information",
+	   "<interface> <dev[:part]>\n"
+	   "    - Print filesystem statistics (block size, total/used/free blocks)"
+);
+#endif
diff --git a/doc/usage/cmd/fsinfo.rst b/doc/usage/cmd/fsinfo.rst
new file mode 100644
index 00000000000..5129a9a29b9
--- /dev/null
+++ b/doc/usage/cmd/fsinfo.rst
@@ -0,0 +1,53 @@ 
+.. SPDX-License-Identifier: GPL-2.0+
+
+.. index::
+   single: fsinfo (command)
+
+fsinfo command
+==============
+
+Synopsis
+--------
+
+::
+
+    fsinfo <interface> <dev[:part]>
+
+Description
+-----------
+
+The fsinfo command displays filesystem statistics for a partition including
+block size, total blocks, used blocks, and free blocks. Both raw byte counts
+and human-readable sizes are shown.
+
+interface
+    interface for accessing the block device (mmc, sata, scsi, usb, ....)
+
+dev
+    device number
+
+part
+    partition number, defaults to 1
+
+Example
+-------
+
+::
+
+    => fsinfo mmc 0:1
+    Block size: 4096 bytes
+    Total blocks: 16384 (67108864 bytes, 64 MiB)
+    Used blocks: 2065 (8458240 bytes, 8.1 MiB)
+    Free blocks: 14319 (58650624 bytes, 55.9 MiB)
+
+Configuration
+-------------
+
+The fsinfo command is only available if CONFIG_CMD_FS_GENERIC=y.
+
+Return value
+------------
+
+The return value $? is set to 0 (true) if the command succeeded and to 1
+(false) otherwise. If the filesystem does not support statfs, an error
+message is displayed.
diff --git a/doc/usage/index.rst b/doc/usage/index.rst
index bbea78a17b2..6c1414a0b84 100644
--- a/doc/usage/index.rst
+++ b/doc/usage/index.rst
@@ -81,6 +81,7 @@  Shell commands
    cmd/fdt
    cmd/font
    cmd/for
+   cmd/fsinfo
    cmd/fwu_mdata
    cmd/gpio
    cmd/gpt
diff --git a/fs/fs_legacy.c b/fs/fs_legacy.c
index e8a5f8d9672..efb5ab669ff 100644
--- a/fs/fs_legacy.c
+++ b/fs/fs_legacy.c
@@ -1105,6 +1105,38 @@  int do_fs_type(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
 	return CMD_RET_SUCCESS;
 }
 
+int do_fs_statfs(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+	struct fs_statfs st;
+	u64 used;
+	int ret;
+
+	if (argc != 3)
+		return CMD_RET_USAGE;
+
+	if (fs_set_blk_dev(argv[1], argv[2], FS_TYPE_ANY))
+		return 1;
+
+	ret = fs_statfs(&st);
+	if (ret) {
+		printf("** Filesystem info not supported **\n");
+		return CMD_RET_FAILURE;
+	}
+
+	used = st.blocks - st.bfree;
+	printf("Block size: %lu bytes\n", st.bsize);
+	printf("Total blocks: %llu (%llu bytes, ", st.blocks,
+	       st.blocks * st.bsize);
+	print_size(st.blocks * st.bsize, ")\n");
+	printf("Used blocks: %llu (%llu bytes, ", used, used * st.bsize);
+	print_size(used * st.bsize, ")\n");
+	printf("Free blocks: %llu (%llu bytes, ", st.bfree,
+	       st.bfree * st.bsize);
+	print_size(st.bfree * st.bsize, ")\n");
+
+	return 0;
+}
+
 int do_rm(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[],
 	  int fstype)
 {
diff --git a/include/fs_cmd.h b/include/fs_cmd.h
index 3b58ad516e7..b28799d5ae4 100644
--- a/include/fs_cmd.h
+++ b/include/fs_cmd.h
@@ -80,4 +80,15 @@  int do_fs_type(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]);
  */
 int do_fs_types(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[]);
 
+/**
+ * do_fs_statfs - Get filesystem statistics.
+ *
+ * @cmdtp: Command information for fsinfo
+ * @flag: Command flags (CMD_FLAG\_...)
+ * @argc: Number of arguments
+ * @argv: List of arguments
+ * Return: result (see enum command_ret_t)
+ */
+int do_fs_statfs(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]);
+
 #endif
diff --git a/test/fs/ext4l.c b/test/fs/ext4l.c
index 02ad13ec71d..2641ac3678c 100644
--- a/test/fs/ext4l.c
+++ b/test/fs/ext4l.c
@@ -327,3 +327,30 @@  static int fs_test_ext4l_uuid_norun(struct unit_test_state *uts)
 }
 FS_TEST_ARGS(fs_test_ext4l_uuid_norun, UTF_SCAN_FDT | UTF_CONSOLE | UTF_MANUAL,
 	     { "fs_image", UT_ARG_STR });
+
+/**
+ * fs_test_ext4l_fsinfo_norun() - Test fsinfo command
+ *
+ * This test verifies that the fsinfo command displays filesystem statistics.
+ *
+ * Arguments:
+ *   fs_image: Path to the ext4 filesystem image
+ */
+static int fs_test_ext4l_fsinfo_norun(struct unit_test_state *uts)
+{
+	const char *fs_image = ut_str(EXT4L_ARG_IMAGE);
+
+	ut_assertnonnull(fs_image);
+	ut_assertok(run_commandf("host bind 0 %s", fs_image));
+	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:");
+	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 });
diff --git a/test/py/tests/test_fs/test_ext4l.py b/test/py/tests/test_fs/test_ext4l.py
index 2bbbe766e6a..0a9da40f358 100644
--- a/test/py/tests/test_fs/test_ext4l.py
+++ b/test/py/tests/test_fs/test_ext4l.py
@@ -124,3 +124,10 @@  class TestExt4l:
             output = ubman.run_command(
                 f'ut -f fs fs_test_ext4l_uuid_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'):
+            output = ubman.run_command(
+                f'ut -f fs fs_test_ext4l_fsinfo_norun fs_image={ext4_image}')
+            assert 'failures: 0' in output