From patchwork Fri Apr 3 14:04:40 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2103 Return-Path: X-Original-To: u-boot-concept@u-boot.org Delivered-To: u-boot-concept@u-boot.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225183; bh=qgW+OlXfhAKjaFtO7zOcsQ/mJxgcnjWBYBFtAkqJUjk=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=UkY+idWPqtpmq6hukrpaQyC5m+kWevlot2tB+YE/oFjfXEWwLBHoWenO9xdLy4Hi3 TfW8qSdT1D+gmqim4IUrFznbGwMh3zwmJ0+zUC0WivBAmqfAXzVh9irax4eZ4Nroap mFiR9whqmdKv9Iy7slu/tYXwQCxQs1uDF7J5C4QInyZ/MhRekQj9lsBgEISvmOoyBf 8z2Je7sOv84yE9SPvA8g5pJFf56+5fOWT/WM85GBguBm6ccAasn03NhRoQ+EHCdtaf Y7eATzdSYmqQW+RPqp/qd7cHyUpjdF/Lx0OyqPRQSuScM++sbA4d93nxFLwZSgIYaN D6IJ75CYmIgiA== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 012126A35C for ; Fri, 3 Apr 2026 08:06:23 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10024) with ESMTP id RgDfUpjCyjcE for ; Fri, 3 Apr 2026 08:06:22 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225181; bh=qgW+OlXfhAKjaFtO7zOcsQ/mJxgcnjWBYBFtAkqJUjk=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=LTs69oU/ICurPbldFpaMp7e8whsRBWg6+8zHkRuW7c/BpGJc9PX7/1+0lXy+3xK2k amAjMX5ClfiXdrADIfmMk8XYbawwU1/LK78qs+NK70fxhJPwJBGYnt7TfkgT8jnvXi aYAoyT/sWliSMzdDFvyFiOEVx0RNer7CzEuH8S074eJfXntBLBJH+CWIJbRhww7G7W qlxG9mWgjp2nrdYdfUStiNjDd/mpQJsssnhuAEjD14wF6MOGAMw9T9SeZ+3m7+gqYb Cx1gEvnSOJEOsT6jme9oK+qkLRJZeF6REHgb0Lcpqmn1zAO2LyPMns/gpkzq5pnXjF 5AA35g67z7y5g== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 325686A35F for ; Fri, 3 Apr 2026 08:06:21 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225177; bh=JghRX2GQqggrY967tQcJKWn6wjEfS7iwaAuofjixfHM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=wF8mWN0luw3mvsm4Wp6kY2PIQ8OvQWHN1laNsRfFqFr8e58DgHnzgGYicRlTG2CTn pI41NesJRr/RMDj6jeqTORFvTpg58t0K2/4S5FPOwv6mKRtH8H/wyT1Qi66TCbOiLn zztxtzCIQqXs9k0Sh66vC4euW0D6uUX1kibSAYNwI09pNPTclZT9dDm7Bnx3EBun+M wQG2dKlZr48g9Y+B3cnm8n6ERZ4Idolc3btvGd57LWtAMBGgX+AD7X3mr8Vq96oqdU lNsfcNphhqF5/vm7Oj8vAURm7aF5YiAY8n2Rbp0JedCSMQjmsxnjBqrg0sz2B4dTEf NHZcXUCuuxsuA== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id C15D16A34C; Fri, 3 Apr 2026 08:06:17 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10026) with ESMTP id lTDID7SWfUtV; Fri, 3 Apr 2026 08:06:17 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225173; bh=E7bXGd6Gv5bV/LW10ndZenXXSdxxgk+NHdKeq2e7Gcs=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ms2ARrqQDeqm1+KtauI5qnhCa3JBLdL258EqWKfwCrwGlEAFmNMnGXJRa0j0+bfq5 YLYCkvHY8dFf/iJ/Ceno7QO4zsy/BStyin0WibcP426t1iz0Qro0vmTQXhHoItnR0A 3MFArYgyMcnIbGgnIXOJf2WmaQ9a02osHGbUgpFi3u6LZxpWNjNubcMx4IPIKvYbCW ftEQGHYP9FMSwogf7zDvfwUM4VCJ0rUP5v4zETchvRlmXmwaCo6pzbZn0K7obwukzz fwfOtAtnOESV6nBn9l6aTsMRIu1m/3HyJ8WvlmjmMWOGSwtPsMNNmFM36eqcH7ty+k 3NaQptaFlGjYA== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 3BE876A376; Fri, 3 Apr 2026 08:06:13 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Fri, 3 Apr 2026 08:04:40 -0600 Message-ID: <20260403140523.1998228-19-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260403140523.1998228-1-sjg@u-boot.org> References: <20260403140523.1998228-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: 25D7PDYUUW7455RYVN66WSX3V355G7EL X-Message-ID-Hash: 25D7PDYUUW7455RYVN66WSX3V355G7EL X-MailFrom: sjg@u-boot.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Simon Glass X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 18/34] vfs: Add stat and block-device mount support List-Id: Discussion and patches related to U-Boot Concept Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Simon Glass 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 --- cmd/fs.c | 29 ++++++++++- fs/vfs.c | 140 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/vfs.h | 109 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 277 insertions(+), 1 deletion(-) 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", + "\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 #include #include +#include #include #include "vfs_internal.h" #include @@ -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 +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