[Concept,22/34] cmd: Move VFS cat implementation to cmd/cat.c

Message ID 20260403140523.1998228-23-sjg@u-boot.org
State New
Headers
Series Add a virtual filesystem (VFS) layer to U-Boot |

Commit Message

Simon Glass April 3, 2026, 2:04 p.m. UTC
  From: Simon Glass <sjg@chromium.org>

Move the VFS cat command from cmd/fs.c into cmd/cat.c alongside the
legacy implementation. The file now uses #if IS_ENABLED(CONFIG_VFS)
to select between VFS and legacy versions, avoiding duplicate command
registrations. Remove the ifndef CONFIG_CMD_VFS guard in cmd/Makefile
since cmd/cat.c now handles both cases.

Signed-off-by: Simon Glass <sjg@chromium.org>
---

 cmd/cat.c                 | 78 ++++++++++++++++++++++++++++++++++++---
 test/dm/fs.c              | 27 ++++++++++++++
 test/py/tests/test_cat.py |  3 +-
 3 files changed, 101 insertions(+), 7 deletions(-)
  

Patch

diff --git a/cmd/cat.c b/cmd/cat.c
index 80627f12cfc..8e0a065473d 100644
--- a/cmd/cat.c
+++ b/cmd/cat.c
@@ -6,11 +6,72 @@ 
 
 #include <abuf.h>
 #include <command.h>
+#include <dm.h>
+#include <file.h>
 #include <fs_legacy.h>
 #include <malloc.h>
 #include <mapmem.h>
+#include <vfs.h>
 #include <linux/errno.h>
 
+#if IS_ENABLED(CONFIG_VFS)
+
+#define CAT_BUF_SIZE	0x1000
+
+static int do_cat(struct cmd_tbl *cmdtp, int flag, int argc,
+		  char *const argv[])
+{
+	struct file_uc_priv *uc_priv;
+	struct udevice *fil;
+	char buf[CAT_BUF_SIZE];
+	loff_t remaining;
+	int ret;
+
+	if (argc < 2)
+		return CMD_RET_USAGE;
+
+	ret = vfs_open_file(argv[1], DIR_O_RDONLY, &fil);
+	if (ret) {
+		printf("Error: %dE\n", ret);
+		return CMD_RET_FAILURE;
+	}
+
+	uc_priv = dev_get_uclass_priv(fil);
+	remaining = uc_priv->size;
+
+	while (remaining > 0) {
+		long chunk = min((loff_t)CAT_BUF_SIZE - 1, remaining);
+		long nread;
+
+		nread = file_read(fil, buf, chunk);
+		if (nread < 0) {
+			printf("Read failed: %ldE\n", nread);
+			return CMD_RET_FAILURE;
+		}
+		if (!nread)
+			break;
+
+		buf[nread] = '\0';
+		puts(buf);
+		remaining -= nread;
+	}
+
+	return CMD_RET_SUCCESS;
+}
+
+U_BOOT_LONGHELP(cat,
+	"<path>\n"
+	"    - Print the contents of a file in the VFS");
+
+U_BOOT_CMD_COMPLETE(
+	cat,	2,	1,	do_cat,
+	"print file to standard output",
+	cat_help_text,
+	vfs_cmd_complete
+);
+
+#else /* !CONFIG_VFS */
+
 static int do_cat(struct cmd_tbl *cmdtp, int flag, int argc,
 		  char *const argv[])
 {
@@ -29,28 +90,32 @@  static int do_cat(struct cmd_tbl *cmdtp, int flag, int argc,
 
 	ret = fs_load_alloc(ifname, dev, file, 0, 0, &buf);
 
-	// check file exists
+	/* check file exists */
 	switch (ret) {
 	case 0:
 		break;
 	case -ENOMEDIUM:
 		return CMD_RET_FAILURE;
 	case -ENOENT:
-		log_err("File does not exist: ifname=%s dev=%s file=%s\n", ifname, dev, file);
+		log_err("File does not exist: ifname=%s dev=%s file=%s\n",
+			ifname, dev, file);
 		return CMD_RET_FAILURE;
 	case -E2BIG:
-		log_err("File is too large: ifname=%s dev=%s file=%s\n", ifname, dev, file);
+		log_err("File is too large: ifname=%s dev=%s file=%s\n",
+			ifname, dev, file);
 		return CMD_RET_FAILURE;
 	case -ENOMEM:
-		log_err("Not enough memory: ifname=%s dev=%s file=%s\n", ifname, dev, file);
+		log_err("Not enough memory: ifname=%s dev=%s file=%s\n",
+			ifname, dev, file);
 		return CMD_RET_FAILURE;
 	default:
 	case -EIO:
-		log_err("File-read failed: ifname=%s dev=%s file=%s\n", ifname, dev, file);
+		log_err("File-read failed: ifname=%s dev=%s file=%s\n",
+			ifname, dev, file);
 		return CMD_RET_FAILURE;
 	}
 
-	// print file content
+	/* print file content */
 	((char *)buf.data)[buf.size] = '\0';
 	puts(buf.data);
 
@@ -67,3 +132,4 @@  U_BOOT_CMD(cat, 4, 1, do_cat,
 	   "Print file to standard output",
 	   cat_help_text
 );
+#endif /* CONFIG_VFS */
diff --git a/test/dm/fs.c b/test/dm/fs.c
index 8bba5263546..a8bd2a8e100 100644
--- a/test/dm/fs.c
+++ b/test/dm/fs.c
@@ -614,6 +614,33 @@  static int dm_test_vfs_stat(struct unit_test_state *uts)
 }
 DM_TEST(dm_test_vfs_stat, UTF_SCAN_FDT);
 
+/* Test the cat command via VFS */
+static int dm_test_vfs_cat(struct unit_test_state *uts)
+{
+	ut_assertok(vfs_init());
+
+	ut_assertok(run_command("mount hostfs /host", 0));
+	ut_assert_console_end();
+
+	/* Write a small file and cat it */
+	memcpy(map_sysmem(0x1000, 11), "hello world", 11);
+	ut_assertok(run_command("save 1000 /host/.cat_test 0xb", 0));
+	ut_assert_nextlinen("11 bytes");
+	ut_assert_console_end();
+
+	ut_assertok(run_command("cat /host/.cat_test", 0));
+	ut_assert_nextline("hello world");
+	ut_assert_console_end();
+
+	os_unlink(".cat_test");
+
+	ut_assertok(run_command("umount /host", 0));
+	ut_assert_console_end();
+
+	return 0;
+}
+DM_TEST(dm_test_vfs_cat, UTF_SCAN_FDT);
+
 /* Test save and load round-trip via VFS */
 static int dm_test_vfs_save(struct unit_test_state *uts)
 {
diff --git a/test/py/tests/test_cat.py b/test/py/tests/test_cat.py
index f793b9fe0a1..ad5ef963e8d 100644
--- a/test/py/tests/test_cat.py
+++ b/test/py/tests/test_cat.py
@@ -6,7 +6,8 @@ 
 import pytest
 from tests.fs_helper import FsHelper
 
-@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('sandbox')
+@pytest.mark.boardspec('!sandbox')
 @pytest.mark.buildconfigspec('cmd_cat')
 def test_cat(ubman):
     """ Unit test for cat