@@ -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[])
@@ -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;
@@ -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