[Concept,18/34] vfs: Add stat and block-device mount support

Message ID 20260403140523.1998228-19-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>

Add vfs_stat() to look up file or directory information by path.

Add fs_mount_blkdev() and fs_mount_blkdev_auto() to create and mount
a filesystem device from a block device, with VFS_FS_TYPE()
registration for auto-detection of filesystem types.

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

 cmd/fs.c      |  29 ++++++++++-
 fs/vfs.c      | 140 ++++++++++++++++++++++++++++++++++++++++++++++++++
 include/vfs.h | 109 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 277 insertions(+), 1 deletion(-)
  

Patch

diff --git a/cmd/fs.c b/cmd/fs.c
index 21fe6ba3655..694222e5d10 100644
--- a/cmd/fs.c
+++ b/cmd/fs.c
@@ -1,6 +1,6 @@ 
 // SPDX-License-Identifier: GPL-2.0
 /*
- * VFS-based filesystem commands - mount, umount, ls, load
+ * VFS-based filesystem commands - mount, umount, ls, load, size
  *
  * These replace the legacy commands in cmd/fs_legacy.c with versions that
  * use absolute paths through the virtual filesystem layer.
@@ -171,6 +171,33 @@  U_BOOT_CMD(
 	"    - List files at 'path' in the VFS (default cwd)"
 );
 
+static int do_size(struct cmd_tbl *cmdtp, int flag, int argc,
+		   char *const argv[])
+{
+	struct fs_dirent dent;
+	int ret;
+
+	if (argc < 2)
+		return CMD_RET_USAGE;
+
+	ret = vfs_stat(argv[1], &dent);
+	if (ret) {
+		printf("Error: %dE\n", ret);
+		return CMD_RET_FAILURE;
+	}
+
+	env_set_hex("filesize", dent.size);
+
+	return CMD_RET_SUCCESS;
+}
+
+U_BOOT_CMD(
+	size,	2,	0,	do_size,
+	"determine a file's size",
+	"<path>\n"
+	"    - Find file at 'path' in the VFS, determine its size,\n"
+	"      and store in the 'filesize' variable."
+);
 
 static int do_vfs_load(struct cmd_tbl *cmdtp, int flag, int argc,
 		       char *const argv[])
diff --git a/fs/vfs.c b/fs/vfs.c
index 0cce5872735..3812706af6c 100644
--- a/fs/vfs.c
+++ b/fs/vfs.c
@@ -19,6 +19,7 @@ 
 #include <fs.h>
 #include <fs_common.h>
 #include <malloc.h>
+#include <part.h>
 #include <vfs.h>
 #include "vfs_internal.h"
 #include <dm/device-internal.h>
@@ -566,6 +567,40 @@  int vfs_open_file(const char *path, enum dir_open_flags_t oflags,
 	return 0;
 }
 
+int vfs_stat(const char *path, struct fs_dirent *dent)
+{
+	struct fs_dir_stream *strm;
+	struct fs_dirent entry;
+	struct udevice *dir;
+	char *leaf;
+	int ret;
+
+	ret = vfs_resolve_dir(path, &dir, &leaf);
+	if (ret)
+		return log_msg_ret("vsd", ret);
+
+	/* Scan the directory for the matching entry */
+	ret = dir_open(dir, &strm);
+	if (ret) {
+		free(leaf);
+		return log_msg_ret("vso", ret);
+	}
+
+	while (!(ret = dir_read(dir, strm, &entry))) {
+		if (!strcmp(entry.name, leaf)) {
+			*dent = entry;
+			dir_close(dir, strm);
+			free(leaf);
+			return 0;
+		}
+	}
+
+	dir_close(dir, strm);
+	free(leaf);
+
+	return log_msg_ret("vsn", -ENOENT);
+}
+
 int vfs_ls(const char *path)
 {
 	char resolved[FILE_MAX_PATH_LEN];
@@ -611,6 +646,111 @@  int vfs_ls(const char *path)
 	return 0;
 }
 
+int fs_mount_blkdev_auto(struct blk_desc *desc, int part_num,
+			 struct disk_partition *part, const char *mountpoint)
+{
+	struct driver *drv = ll_entry_start(struct driver, driver);
+	const int n_ents = ll_entry_count(struct driver, driver);
+	int i;
+
+	for (i = 0; i < n_ents; i++, drv++) {
+		const char *name = drv->name;
+		int len = strlen(name);
+		char type[30];
+		int ret;
+
+		if (drv->id != UCLASS_FS)
+			continue;
+
+		/* Strip "_fs" suffix to get the type name */
+		if (len < 4 || strcmp(name + len - 3, "_fs"))
+			continue;
+
+		strlcpy(type, name, min((int)sizeof(type), len - 2));
+
+		ret = fs_mount_blkdev(type, desc, part_num, part, mountpoint);
+		if (!ret)
+			return 0;
+		if (ret == -EBUSY)
+			return log_msg_ret("fmb", ret);
+	}
+
+	return -ENODEV;
+}
+
+int fs_mount_blkdev(const char *type, struct blk_desc *desc, int part_num,
+		    struct disk_partition *part, const char *mountpoint)
+{
+	char resolved[FILE_MAX_PATH_LEN];
+	static int blkfs_count;
+	char drv_name[30], dev_name[30];
+	struct udevice *vfs, *mnt, *dir, *dev;
+	const char *subpath;
+	struct fs_plat *plat;
+	char *str;
+	int ret;
+
+	vfs = vfs_root();
+	if (!vfs)
+		return log_msg_ret("fmi", -ENXIO);
+
+	mountpoint = vfs_path_resolve(vfs_getcwd(), mountpoint,
+				      resolved, sizeof(resolved));
+	if (!mountpoint)
+		return log_msg_ret("fmp", -ENAMETOOLONG);
+
+	snprintf(drv_name, sizeof(drv_name), "%s_fs", type);
+	snprintf(dev_name, sizeof(dev_name), "%s.%d", drv_name, blkfs_count);
+
+	str = strdup(dev_name);
+	if (!str)
+		return -ENOMEM;
+
+	ret = device_bind_driver(dm_root(), drv_name, str, &dev);
+	if (ret) {
+		free(str);
+		return log_msg_ret("fmb", ret);
+	}
+	device_set_name_alloced(dev);
+
+	/* Set block device info in uclass platdata before probing */
+	plat = dev_get_uclass_plat(dev);
+	plat->desc = desc;
+	plat->part = *part;
+
+	ret = device_probe(dev);
+	if (ret) {
+		device_unbind(dev);
+		return log_msg_ret("fmp", ret);
+	}
+
+	/* Check if the mountpoint is already in use */
+	ret = vfs_find_mount(vfs, mountpoint, &mnt, &subpath);
+	if (!ret && mnt && !*subpath) {
+		device_remove(dev, DM_REMOVE_NORMAL);
+		device_unbind(dev);
+		return log_msg_ret("fme", -EBUSY);
+	}
+
+	ret = vfs_resolve(vfs, mountpoint, &dir);
+	if (ret) {
+		device_remove(dev, DM_REMOVE_NORMAL);
+		device_unbind(dev);
+		return log_msg_ret("fmr", ret);
+	}
+
+	ret = vfs_mount(vfs, dir, dev);
+	if (ret) {
+		device_remove(dev, DM_REMOVE_NORMAL);
+		device_unbind(dev);
+		return log_msg_ret("fmm", ret);
+	}
+
+	blkfs_count++;
+
+	return 0;
+}
+
 struct udevice *vfs_root(void)
 {
 	struct udevice *dev;
diff --git a/include/vfs.h b/include/vfs.h
index 521494e4199..6e0c67ca031 100644
--- a/include/vfs.h
+++ b/include/vfs.h
@@ -13,6 +13,10 @@ 
 
 #include <dir.h>
 
+struct blk_desc;
+struct disk_partition;
+struct fs_dirent;
+struct fs_statfs;
 struct udevice;
 
 /**
@@ -182,4 +186,109 @@  int vfs_ls(const char *path);
 int vfs_open_file(const char *path, enum dir_open_flags_t oflags,
 		  struct udevice **filp);
 
+/**
+ * vfs_stat() - Get information about a file or directory
+ *
+ * Looks up the named entry in its parent directory and fills in a
+ * struct fs_dirent with type, size and timestamps. Handles both files
+ * and directories. Calls vfs_init() if needed.
+ *
+ * @path: Absolute or relative VFS path
+ * @dent: Returns the directory entry information
+ * Return: 0 if OK, -ve on error
+ */
+int vfs_stat(const char *path, struct fs_dirent *dent);
+
+/**
+ * vfs_mkdir() - Create a directory by absolute or relative VFS path
+ *
+ * @path: Path of the directory to create
+ * Return: 0 if OK, -ve on error
+ */
+int vfs_mkdir(const char *path);
+
+/**
+ * vfs_unlink() - Delete a file by absolute or relative VFS path
+ *
+ * @path: Path of the file to delete
+ * Return: 0 if OK, -ve on error
+ */
+int vfs_unlink(const char *path);
+
+/**
+ * vfs_rename() - Rename or move a file or directory
+ *
+ * Both paths must be on the same mounted filesystem.
+ *
+ * @old_path: Current absolute or relative VFS path
+ * @new_path: New absolute or relative VFS path
+ * Return: 0 if OK, -EXDEV if paths are on different mounts, other -ve on error
+ */
+/**
+ * vfs_ln() - Create a symbolic link
+ *
+ * @path: Absolute or relative VFS path for the new symlink
+ * @target: Target path the symlink points to
+ * Return: 0 if OK, -ve on error
+ */
+int vfs_ln(const char *path, const char *target);
+
+int vfs_rename(const char *old_path, const char *new_path);
+
+/**
+ * vfs_readlink() - Read a symbolic link target
+ *
+ * @path: Absolute or relative VFS path to the symlink
+ * @buf: Buffer to receive the target path
+ * @size: Size of buffer
+ * Return: length of target, or -ve on error
+ */
+int vfs_readlink(const char *path, char *buf, int size);
+
+/**
+ * vfs_statfs() - Get filesystem statistics for a mount point
+ *
+ * @path: Absolute or relative VFS path (any path within the mount)
+ * @stats: Returns filesystem statistics
+ * Return: 0 if OK, -ve on error
+ */
+int vfs_statfs(const char *path, struct fs_statfs *stats);
+
+/**
+ * fs_mount_blkdev_auto() - Auto-detect and mount a filesystem from a block
+ *	device
+ *
+ * Tries each UCLASS_FS driver with a "_fs" suffix in turn until one
+ * succeeds.
+ *
+ * @desc: Block device descriptor
+ * @part: Partition information
+ * @mountpoint: Absolute path to mount at
+ * Return: 0 if OK, -ENODEV if no FS type matched, other -ve on error
+ */
+int fs_mount_blkdev_auto(struct blk_desc *desc, int part_num,
+			 struct disk_partition *part, const char *mountpoint);
+
+/**
+ * fs_mount_blkdev() - Create and mount a FS device from a block device
+ *
+ * Uses a naming convention to find the right driver: filesystem type "ext4"
+ * maps to driver "ext4_fs". The block device and partition info are stored
+ * in the generic struct fs_plat so that any block-backed FS driver can
+ * read them.
+ *
+ * @type: Filesystem type name (e.g. "ext4")
+ * @desc: Block device descriptor
+ * @part: Partition information
+ * @mountpoint: Absolute path to mount at
+ * Return: 0 if OK, -ve on error
+ */
+int fs_mount_blkdev(const char *type, struct blk_desc *desc, int part_num,
+		    struct disk_partition *part, const char *mountpoint);
+
+/**
+ * vfs_print_mounts() - Print all current mounts
+ */
+void vfs_print_mounts(void);
+
 #endif