From patchwork Fri Apr 3 14:04:45 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 2108 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=1775225191; bh=kpT+YNTJZp6jjCgdavEC6xDiXuhfbAUCi/ME5CLecaY=; 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=AOEA5OtQPH4hn6VbIrYhlJStz/NvI0WzP54QOZjMU87K7Gkpnt53TH/UZ7/sk1ak4 Za2mOm4zxZqzPGMbYpq2EejVpRpruWzIshYh6Ll+gO72GjzS90HGfc7sUbwvXxeARr L1cH84wF6w3xvUvxVdLYrez08GxMvHqzjs1C5lDXf/8g7g9x6jbIOygQZrkG2/Hcyj Au8TiqVsaAQDlIkZM1+KLYAEdQ8PIoroKFEsI6UQvETgeLsdpOwvxY+md7dS5BDp9b WIviKT6yS3kuSL8QeFv2n3cpRf06Da0KisFC9awZxDomiM9GVcJoFvBWnh1Zybrf44 AV3LL56pgmpoA== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 792CF6A368 for ; Fri, 3 Apr 2026 08:06:31 -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 DPTv8-Ve7COW for ; Fri, 3 Apr 2026 08:06:31 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225190; bh=kpT+YNTJZp6jjCgdavEC6xDiXuhfbAUCi/ME5CLecaY=; 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=FCcm23W1bRb5P7kjgYgqRdZZLgItBEuthbQ40VTRbRg5WQL/6t/167GtawkEenTfK ZEIxjNor/fGu/b2VdSM4qpmes15VhZ+dUAWWr56velGj3HBaqL0CMABr3ijJEsm8tx Kk+coCvGJxcqTG3CISxeVR+qXI9mo9y1UzEPe61+kZQsb0Ml2mIfz+igvCGB/LpVDT ODOP50flOPyagSUfcAyGszYXPps5kT6f9z09iiKG/8RrzdhKpr1ZBUwtYGY/g+LgKI 0xRxc4fuCipMMltIkYmpt7RPqbwDqf6wLae905okpWWKyktcdVV6wWIRKqgGsp6VmF Q+nFh6vRKQmVw== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 389E86A36D for ; Fri, 3 Apr 2026 08:06:30 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225186; bh=6LSvMLJr8cuMFGUJjNHhAyF7UltxX0dTCI0+JebmNL4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=pw1M0zDczqEKJJ9t6t5hoNiyx5Fw8LelRi5EgCtC7HxRKJ7IoToJCgkpz0Fdnquv4 wtfQlhEZjSxohThB3zYK0hoZgSK5yDcrTw2JqwfU+PW1QwUvq3bkVAvd3DW8YJ7CSJ TkVvi/tFJzUX0n1rkthUixqVOa4VvZV+qTjbAxJCt/qktCe7vzLb+NQ8js5ed9U2a6 5r7rrIbn1WPzWcMmTlNJ9opP2/HqXqd7CI4VJyX/4mAZYZkZ8SLHtbFDkzMFdXZnkD U5Vmg36kO6IcfUCfNOsAn+yZE+b+yOswbwuI6+EuDrtmR0rftJi46EJHgLYxvJJ1L1 9ZBuRa/53OoUg== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 5CA136A347; Fri, 3 Apr 2026 08:06:26 -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 TSvyWrA1mo-T; Fri, 3 Apr 2026 08:06:26 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1775225185; bh=4lR7lFti0pl0qvYS6LDI+XdmNOXS8vvGTGYP34C7ay8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ffYo5EJWS7Ej81JaZMvIxPEw0UC2PjBatpo+2R5FacS5CuqL4XcipuacOW1OdD6vi cPIAIbVK38iFcA5GBhumsxGfJk/8ezXYNNzxnD4URwIUpI4pt72BoC/8hgcJdX8FEv ZqFGihH4vRUCb4SW/5DuN9hv1ZoXPqNEbMvk3Fi/xazZarroV26bTBrJzbSPMwbVGw lqnyha7L0enVLAllv108OGf2caFtHmYvfwWvhtAWcsUPMohPNjGIoDMYr6E3SBTygs fd5i5yF4bnJyqkz2NygDz4P/RaXKbXCSsRAIP44ZcaVQYHjMKuKW9l+dK0dthlgV78 ktrsARQOBs+2w== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 7D46E6A340; Fri, 3 Apr 2026 08:06:25 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Fri, 3 Apr 2026 08:04:45 -0600 Message-ID: <20260403140523.1998228-24-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: WNZ5OUOGZZOW7M2ONEIP3SZPKKUU63XL X-Message-ID-Hash: WNZ5OUOGZZOW7M2ONEIP3SZPKKUU63XL 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 23/34] vfs: Add mkdir, rm, mv, df, symlink, ln and umount-all 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 operations for directory and file manipulation: - vfs_mkdir() for creating directories - vfs_unlink() for file deletion - vfs_rename() for rename/move (same-mount only) - vfs_statfs() and vfs_print_df() for filesystem usage - vfs_readlink() for reading symbolic links - vfs_ln() for creating symbolic links - vfs_umount_all() for unmounting all filesystems Add corresponding fs_ops (mkdir, unlink, rename, statfs, readlink, ln) and fs_do_*() wrappers in fs-uclass.c. Implement in ext4l where applicable. Signed-off-by: Simon Glass --- fs/fs-uclass.c | 61 ++++++++++++ fs/sandbox/sandboxfs.c | 33 +++++++ fs/vfs.c | 208 ++++++++++++++++++++++++++++++++++++++++- include/fs.h | 122 ++++++++++++++++++++++++ include/vfs.h | 17 ++++ 5 files changed, 437 insertions(+), 4 deletions(-) diff --git a/fs/fs-uclass.c b/fs/fs-uclass.c index 435fd32bc75..7338a266da9 100644 --- a/fs/fs-uclass.c +++ b/fs/fs-uclass.c @@ -115,6 +115,67 @@ int fs_mount(struct udevice *dev) return 0; } +int fs_do_ln(struct udevice *dev, const char *path, const char *target) +{ + struct fs_ops *ops = fs_get_ops(dev); + + if (!ops->ln) + return log_msg_ret("fln", -ENOSYS); + + return ops->ln(dev, path, target); +} + +int fs_do_rename(struct udevice *dev, const char *old_path, + const char *new_path) +{ + struct fs_ops *ops = fs_get_ops(dev); + + if (!ops->rename) + return log_msg_ret("frn", -ENOSYS); + + return ops->rename(dev, old_path, new_path); +} + +int fs_readlink(struct udevice *dev, const char *path, char *buf, int size) +{ + struct fs_ops *ops = fs_get_ops(dev); + + if (!ops->readlink) + return log_msg_ret("frl", -ENOSYS); + + return ops->readlink(dev, path, buf, size); +} + +int fs_do_statfs(struct udevice *dev, struct fs_statfs *stats) +{ + struct fs_ops *ops = fs_get_ops(dev); + + if (!ops->statfs) + return log_msg_ret("fss", -ENOSYS); + + return ops->statfs(dev, stats); +} + +int fs_do_unlink(struct udevice *dev, const char *path) +{ + struct fs_ops *ops = fs_get_ops(dev); + + if (!ops->unlink) + return log_msg_ret("fsu", -ENOSYS); + + return ops->unlink(dev, path); +} + +int fs_do_mkdir(struct udevice *dev, const char *path) +{ + struct fs_ops *ops = fs_get_ops(dev); + + if (!ops->mkdir) + return log_msg_ret("fsm", -ENOSYS); + + return ops->mkdir(dev, path); +} + int fs_unmount(struct udevice *dev) { struct fs_ops *ops = fs_get_ops(dev); diff --git a/fs/sandbox/sandboxfs.c b/fs/sandbox/sandboxfs.c index 70fb829ae67..87eec0b2acd 100644 --- a/fs/sandbox/sandboxfs.c +++ b/fs/sandbox/sandboxfs.c @@ -417,6 +417,34 @@ static int sandbox_fs_lookup_dir(struct udevice *dev, const char *path, return 0; } +static int sandbox_fs_ln(struct udevice *dev, const char *path, + const char *target) +{ + return os_symlink(target, path); +} + +static int sandbox_fs_rename(struct udevice *dev, const char *old_path, + const char *new_path) +{ + return os_rename(old_path, new_path); +} + +static int sandbox_fs_readlink(struct udevice *dev, const char *path, + char *buf, int size) +{ + return os_readlink(path, buf, size); +} + +static int sandbox_fs_unlink(struct udevice *dev, const char *path) +{ + return os_unlink(path); +} + +static int sandbox_fs_mkdir(struct udevice *dev, const char *path) +{ + return os_mkdir(path, 0755); +} + static int sandbox_fs_remove(struct udevice *dev) { return 0; @@ -426,6 +454,11 @@ static const struct fs_ops sandbox_fs_ops = { .mount = sandbox_fs_mount, .unmount = sandbox_fs_unmount, .lookup_dir = sandbox_fs_lookup_dir, + .ln = sandbox_fs_ln, + .rename = sandbox_fs_rename, + .readlink = sandbox_fs_readlink, + .unlink = sandbox_fs_unlink, + .mkdir = sandbox_fs_mkdir, }; static const struct udevice_id sandbox_fs_ids[] = { diff --git a/fs/vfs.c b/fs/vfs.c index 328b1076280..2e781e00d7e 100644 --- a/fs/vfs.c +++ b/fs/vfs.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -431,8 +432,8 @@ static int vfs_resolve_dir(const char *path, struct udevice **dirp, char *sub; int ret; - ret = vfs_resolve_mount(path, resolved, sizeof(resolved), - &mnt, &subpath); + ret = vfs_resolve_mount(path, resolved, + sizeof(resolved), &mnt, &subpath); if (ret) return log_msg_ret("vdm", ret); @@ -536,6 +537,36 @@ bool vfs_is_mount_point(struct udevice *dir) return !find_mount(dir, &mnt); } +int vfs_umount_all(void) +{ + struct udevice *dev, *next; + struct uclass *uc; + int ret, err = 0; + + ret = uclass_get(UCLASS_MOUNT, &uc); + if (ret) + return ret; + + uclass_foreach_dev_safe(dev, next, uc) { + struct vfsmount *mnt = dev_get_uclass_priv(dev); + + if (!device_active(dev)) + continue; + + ret = fs_unmount(mnt->target); + if (ret && ret != -ENOTCONN) { + err = ret; + continue; + } + + ret = fs_mount_uninit(dev); + if (ret) + err = ret; + } + + return err; +} + void vfs_print_mounts(void) { struct vfsmount *mnt; @@ -568,6 +599,175 @@ int vfs_open_file(const char *path, enum dir_open_flags_t oflags, return 0; } +void vfs_print_df(void) +{ + struct vfsmount *mnt; + struct udevice *dev; + + vfs_foreach_mount(mnt, dev) { + char path[FILE_MAX_PATH_LEN]; + struct fs_statfs stats; + int ret; + + if (vfs_mount_path(dev, path, sizeof(path))) + continue; + + ret = fs_do_statfs(mnt->target, &stats); + if (!ret) { + u64 used = stats.blocks - stats.bfree; + + printf("%-16s %6lu %10llu %10llu %10llu\n", path, + stats.bsize, stats.blocks * stats.bsize, + used * stats.bsize, + stats.bfree * stats.bsize); + } else { + printf("%-16s %6s %10s %10s %10s\n", path, + "-", "-", "-", "-"); + } + } +} + +int vfs_ln(const char *path, const char *target) +{ + char resolved[FILE_MAX_PATH_LEN]; + struct udevice *vfs, *mnt; + const char *subpath; + struct vfsmount *mnt_priv; + int ret; + + vfs = vfs_root(); + if (!vfs) + return log_msg_ret("vli", -ENXIO); + + path = vfs_path_resolve(vfs_getcwd(), path, resolved, sizeof(resolved)); + + if (!path) + return log_msg_ret("vrp", -ENAMETOOLONG); + ret = vfs_find_mount(vfs, path, &mnt, &subpath); + if (ret) + return log_msg_ret("vlm", ret); + + mnt_priv = dev_get_uclass_priv(mnt); + + return fs_do_ln(mnt_priv->target, subpath, target); +} + +int vfs_rename(const char *old_path, const char *new_path) +{ + char resolved_old[FILE_MAX_PATH_LEN]; + char resolved_new[FILE_MAX_PATH_LEN]; + struct udevice *mnt_old, *mnt_new; + const char *sub_old, *sub_new; + struct vfsmount *mnt_priv; + int ret; + + ret = vfs_resolve_mount(old_path, resolved_old, + sizeof(resolved_old), + &mnt_old, &sub_old); + if (ret) + return log_msg_ret("vro", ret); + + ret = vfs_resolve_mount(new_path, resolved_new, + sizeof(resolved_new), + &mnt_new, &sub_new); + if (ret) + return log_msg_ret("vrn", ret); + + /* Both paths must be on the same mount */ + if (mnt_old != mnt_new) + return log_msg_ret("vrx", -EXDEV); + + mnt_priv = dev_get_uclass_priv(mnt_old); + + return fs_do_rename(mnt_priv->target, sub_old, sub_new); +} + +int vfs_readlink(const char *path, char *buf, int size) +{ + char resolved[FILE_MAX_PATH_LEN]; + struct udevice *mnt; + struct vfsmount *mnt_priv; + const char *subpath; + int ret; + + ret = vfs_resolve_mount(path, resolved, + sizeof(resolved), &mnt, &subpath); + if (ret) + return log_msg_ret("rlm", ret); + + mnt_priv = dev_get_uclass_priv(mnt); + + return fs_readlink(mnt_priv->target, subpath, buf, size); +} + +int vfs_statfs(const char *path, struct fs_statfs *stats) +{ + char resolved[FILE_MAX_PATH_LEN]; + struct udevice *mnt; + struct vfsmount *mnt_priv; + const char *subpath; + int ret; + + ret = vfs_resolve_mount(path, resolved, + sizeof(resolved), &mnt, &subpath); + if (ret) + return log_msg_ret("dfm", ret); + + mnt_priv = dev_get_uclass_priv(mnt); + + return fs_do_statfs(mnt_priv->target, stats); +} + +int vfs_unlink(const char *path) +{ + char resolved[FILE_MAX_PATH_LEN]; + struct udevice *vfs, *mnt; + struct vfsmount *mnt_priv; + const char *subpath; + int ret; + + vfs = vfs_root(); + if (!vfs) + return log_msg_ret("vui", -ENXIO); + + path = vfs_path_resolve(vfs_getcwd(), path, resolved, sizeof(resolved)); + if (!path) + return log_msg_ret("vup", -ENAMETOOLONG); + + ret = vfs_find_mount(vfs, path, &mnt, &subpath); + if (ret) + return log_msg_ret("vum", ret); + + mnt_priv = dev_get_uclass_priv(mnt); + + return fs_do_unlink(mnt_priv->target, subpath); +} + +int vfs_mkdir(const char *path) +{ + char resolved[FILE_MAX_PATH_LEN]; + struct udevice *vfs, *mnt; + struct vfsmount *mnt_priv; + const char *subpath; + int ret; + + vfs = vfs_root(); + if (!vfs) + return log_msg_ret("vmi", -ENXIO); + + path = vfs_path_resolve(vfs_getcwd(), path, resolved, sizeof(resolved)); + if (!path) + return log_msg_ret("vmp", -ENAMETOOLONG); + + ret = vfs_find_mount(vfs, path, &mnt, &subpath); + if (ret) + return log_msg_ret("vmm", ret); + + mnt_priv = dev_get_uclass_priv(mnt); + + return fs_do_mkdir(mnt_priv->target, subpath); +} + int vfs_stat(const char *path, struct fs_dirent *dent) { struct fs_dir_stream *strm; @@ -612,8 +812,8 @@ int vfs_ls(const char *path) bool empty = true; int ret; - ret = vfs_resolve_mount(path, resolved, sizeof(resolved), - &mnt, &subpath); + ret = vfs_resolve_mount(path, resolved, + sizeof(resolved), &mnt, &subpath); if (ret) return ret; diff --git a/include/fs.h b/include/fs.h index 925f810902b..74cfd6af831 100644 --- a/include/fs.h +++ b/include/fs.h @@ -14,6 +14,7 @@ #include #include +struct fs_statfs; struct udevice; enum { @@ -79,6 +80,66 @@ struct fs_ops { */ int (*lookup_dir)(struct udevice *dev, const char *path, struct udevice **dirp); + + /** + * rename() - Rename or move a file or directory + * + * @dev: Filesystem device + * @old_path: Current path + * @new_path: New path + * Return 0 if OK, -ve on error + */ + int (*rename)(struct udevice *dev, const char *old_path, + const char *new_path); + + /** + * ln() - Create a symbolic link + * + * @dev: Filesystem device + * @path: Path of symlink to create + * @target: Target the symlink points to + * Return 0 if OK, -ve on error + */ + int (*ln)(struct udevice *dev, const char *path, const char *target); + + /** + * readlink() - Read the target of a symbolic link + * + * @dev: Filesystem device + * @path: Path to the symbolic link + * @buf: Buffer to receive the target path + * @size: Size of buffer + * Return: length of target string, or -ve on error + */ + int (*readlink)(struct udevice *dev, const char *path, char *buf, + int size); + + /** + * statfs() - Get filesystem statistics + * + * @dev: Filesystem device + * @stats: Returns filesystem statistics + * Return 0 if OK, -ve on error + */ + int (*statfs)(struct udevice *dev, struct fs_statfs *stats); + + /** + * unlink() - Delete a file + * + * @dev: Filesystem device + * @path: Path of the file to delete + * Return 0 if OK, -ve on error + */ + int (*unlink)(struct udevice *dev, const char *path); + + /** + * mkdir() - Create a directory + * + * @dev: Filesystem device + * @path: Path of the directory to create + * Return 0 if OK, -ve on error + */ + int (*mkdir)(struct udevice *dev, const char *path); }; /* Get access to a filesystem's operations */ @@ -112,6 +173,67 @@ int fs_unmount(struct udevice *dev); */ int fs_lookup_dir(struct udevice *dev, const char *path, struct udevice **dirp); +/** + * fs_do_ln() - Create a symbolic link on a filesystem + * + * @dev: Filesystem device + * @path: Path of symlink to create (within the filesystem) + * @target: Target the symlink points to + * Return: 0 if OK, -ENOSYS if not supported, other -ve on error + */ +int fs_do_ln(struct udevice *dev, const char *path, const char *target); + +/** + * fs_do_rename() - Rename or move a file on a filesystem + * + * Both paths must be within the same filesystem. + * + * @dev: Filesystem device + * @old_path: Current path (within the filesystem) + * @new_path: New path (within the filesystem) + * Return: 0 if OK, -ENOSYS if not supported, other -ve on error + */ +int fs_do_rename(struct udevice *dev, const char *old_path, + const char *new_path); + +/** + * fs_readlink() - Read a symbolic link target on a filesystem + * + * @dev: Filesystem device + * @path: Path to the symbolic link (within the filesystem) + * @buf: Buffer to receive the target path + * @size: Size of buffer + * Return: length of target, -ENOSYS if not supported, other -ve on error + */ +int fs_readlink(struct udevice *dev, const char *path, char *buf, int size); + +/** + * fs_do_statfs() - Get filesystem statistics + * + * @dev: Filesystem device + * @stats: Returns filesystem statistics + * Return: 0 if OK, -ENOSYS if not supported, other -ve on error + */ +int fs_do_statfs(struct udevice *dev, struct fs_statfs *stats); + +/** + * fs_do_unlink() - Delete a file on a filesystem + * + * @dev: Filesystem device + * @path: Path of the file to delete (within the filesystem) + * Return: 0 if OK, -ENOSYS if not supported, other -ve on error + */ +int fs_do_unlink(struct udevice *dev, const char *path); + +/** + * fs_do_mkdir() - Create a directory on a filesystem + * + * @dev: Filesystem device + * @path: Path of the directory to create (within the filesystem) + * Return: 0 if OK, -ENOSYS if not supported, other -ve on error + */ +int fs_do_mkdir(struct udevice *dev, const char *path); + /** * fs_split_path() - Get a list of subdirs in a filename * diff --git a/include/vfs.h b/include/vfs.h index 04cbd5d0ca9..9f82bc52bf9 100644 --- a/include/vfs.h +++ b/include/vfs.h @@ -131,6 +131,15 @@ int vfs_umount(struct udevice *mnt_dev); */ int vfs_umount_path(struct udevice *vfs, const char *path); +/** + * vfs_umount_all() - Unmount all filesystems + * + * Unmounts all mounted filesystems. Returns an error if any unmount fails. + * + * Return: 0 if OK, -ve on error + */ +int vfs_umount_all(void); + /** * vfs_find_mount() - Find the mount covering a path * @@ -292,6 +301,14 @@ int fs_mount_blkdev(const char *type, struct blk_desc *desc, int part_num, */ void vfs_print_mounts(void); +/** + * vfs_print_df() - Print filesystem usage for all mounts + * + * Iterates all mounts and prints statfs info for those that support it. + * Mounts that do not support statfs are listed with dashes. + */ +void vfs_print_df(void); + /** * do_cp() - Copy a file within the VFS *