
From: Simon Glass <sjg@chromium.org> Add an implementation of virtio-fs directories, including looking them up and reading them. This requires some information in struct fs_dir_stream to maintain the state of the directory read. Signed-off-by: Simon Glass <sjg@chromium.org> --- drivers/virtio/Makefile | 2 +- drivers/virtio/fs.c | 1 + drivers/virtio/fs_dir.c | 183 +++++++++++++++++++++++++++++++++++ drivers/virtio/fs_internal.h | 16 +++ include/fs_common.h | 2 + 5 files changed, 203 insertions(+), 1 deletion(-) create mode 100644 drivers/virtio/fs_dir.c diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile index aef4ae497f1..3e985f7a5e7 100644 --- a/drivers/virtio/Makefile +++ b/drivers/virtio/Makefile @@ -12,4 +12,4 @@ obj-$(CONFIG_VIRTIO_SANDBOX_EMUL) += sandbox_emul.o emul_blk.o obj-$(CONFIG_VIRTIO_NET) += virtio_net.o obj-$(CONFIG_VIRTIO_BLK) += virtio_blk.o obj-$(CONFIG_VIRTIO_RNG) += virtio_rng.o -obj-$(CONFIG_VIRTIO_FS) += fs.o +obj-$(CONFIG_VIRTIO_FS) += fs.o fs_dir.o diff --git a/drivers/virtio/fs.c b/drivers/virtio/fs.c index 73e08e022a5..ac60047da5a 100644 --- a/drivers/virtio/fs.c +++ b/drivers/virtio/fs.c @@ -409,6 +409,7 @@ static int virtio_fs_unmount(struct udevice *dev) static const struct fs_ops virtio_fs_ops = { .mount = virtio_fs_mount, .unmount = virtio_fs_unmount, + .lookup_dir = virtio_fs_setup_dir, }; static const struct udevice_id virtio_fs_ids[] = { diff --git a/drivers/virtio/fs_dir.c b/drivers/virtio/fs_dir.c new file mode 100644 index 00000000000..1bc536f56bf --- /dev/null +++ b/drivers/virtio/fs_dir.c @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * U-Boot Virtio-FS directories + * + * Copyright 2025 Simon Glass <sjg@chromium.org> + * + * Supports access to directories in virtio-fs + */ + +#define LOG_CATEGORY UCLASS_VIRTIO + +#include <dm.h> +#include <dir.h> +#include <fs.h> +#include <malloc.h> +#include <virtio_fs.h> +#include <dm/device-internal.h> +#include <dm/lists.h> +#include <linux/fuse.h> +#include "fs_internal.h" + +static int virtio_fs_dir_open(struct udevice *dev, struct fs_dir_stream *strm) +{ + struct virtio_fs_dir_priv *dir_priv = dev_get_priv(dev); + struct udevice *fs = dev_get_parent(dev); + int ret; + + strm = malloc(sizeof(struct fs_dir_stream)); + if (!strm) + return log_msg_ret("vso", -ENOMEM); + + log_debug("opening inode %lld\n", dir_priv->inode); + ret = virtio_fs_opendir(fs, dir_priv->inode, &strm->fh); + if (ret) { + log_err("Failed to open directory: %d\n", ret); + return ret; + } + strm->dev = dev; + + strm->offset = 0; + + return 0; +} + +int virtio_fs_dir_read(struct udevice *dev, struct fs_dir_stream *strm, + struct fs_dirent *dent) +{ + struct virtio_fs_dir_priv *dir_priv = dev_get_priv(dev); + struct udevice *fs = dev_get_parent(dev); + struct fuse_direntplus *ent; + size_t reclen; + struct fuse_attr *attr; + char buf[0x200]; + int ret, size; + + log_debug("start %lld strm %p\n", dir_priv->inode, strm); + log_debug("offset %lld\n", strm->offset); + ret = virtio_fs_readdir(fs, dir_priv->inode, strm->fh, strm->offset, + buf, sizeof(buf), &size); + if (ret) { + log_err("Failed to read directory: %d\n", ret); + return ret; + } + + if (!size) + return log_msg_ret("vde", -ENOENT); + + log_debug("virtio-fs: size %x\n", size); + + ent = (struct fuse_direntplus *)buf; + + /* this shouldn't happen, but just to be sure... */ + if (size < FUSE_NAME_OFFSET) + return -ENOSPC; + + reclen = FUSE_DIRENTPLUS_SIZE(ent); + attr = &ent->entry_out.attr; + strm->offset = ent->dirent.off; + + dent->type = ent->dirent.type; + dent->size = attr->size; + dent->attr = attr->flags; + strlcpy(dent->name, ent->dirent.name, + min((int)ent->dirent.namelen + 1, FS_DIRENT_NAME_LEN)); + + return 0; +} + +static int virtio_fs_dir_close(struct udevice *dev, struct fs_dir_stream *strm) +{ + struct virtio_fs_dir_priv *dir_priv = dev_get_priv(dev); + struct udevice *fs = dev_get_parent(dev); + int ret; + + log_debug("close\n"); + ret = virtio_fs_releasedir(fs, dir_priv->inode, strm->fh); + log_debug("ret %d\n", ret); + if (ret) { + log_err("Failed to release directory: %d\n", ret); + return ret; + } + + log_debug("close done\n"); + + return 0; +} + +static int virtio_fs_dir_remove(struct udevice *dev) +{ + struct virtio_fs_dir_priv *dir_priv = dev_get_priv(dev); + + if (*dir_priv->path) { + int ret; + + ret = virtio_fs_forget(dev, dir_priv->inode); + if (ret) + return log_msg_ret("vfr", ret); + } + + return 0; +} + +static struct dir_ops virtio_fs_dir_ops = { + .open = virtio_fs_dir_open, + .read = virtio_fs_dir_read, + .close = virtio_fs_dir_close, +}; + +static const struct udevice_id dir_ids[] = { + { .compatible = "virtio-fs,directory" }, + { } +}; + +U_BOOT_DRIVER(virtio_fs_dir) = { + .name = "virtio_fs_dir", + .id = UCLASS_DIR, + .of_match = dir_ids, + .remove = virtio_fs_dir_remove, + .ops = &virtio_fs_dir_ops, + .priv_auto = sizeof(struct virtio_fs_dir_priv), + .flags = DM_FLAG_ACTIVE_DMA, +}; + +int virtio_fs_setup_dir(struct udevice *fsdev, const char *path, + struct udevice **devp) +{ + struct virtio_fs_dir_priv *dir_priv; + struct udevice *dir; + bool has_path; + u64 inode; + int ret; + + log_debug("looking up path '%s'\n", path); + inode = FUSE_ROOT_ID; + has_path = path && strcmp("/", path); + if (has_path) { + ret = virtio_fs_lookup(fsdev, path, &inode); + if (ret) { + log_err("Failed to lookup directory '%s': %d\n", path, + ret); + return ret; + } + log_debug("got inode %lld\n", inode); + } + + ret = dir_add_probe(fsdev, DM_DRIVER_REF(virtio_fs_dir), path, &dir); + if (ret) + goto no_add; + + dir_priv = dev_get_priv(dir); + dir_priv->inode = inode; + log_debug("added new dir '%s' inode %llx\n", path, inode); + + *devp = dir; + + return 0; + +no_add: + if (has_path) + ret = virtio_fs_forget(fsdev, inode); + + return ret; +} diff --git a/drivers/virtio/fs_internal.h b/drivers/virtio/fs_internal.h index 17260ce9134..c71c7ca48a7 100644 --- a/drivers/virtio/fs_internal.h +++ b/drivers/virtio/fs_internal.h @@ -146,4 +146,20 @@ u64 virtio_fs_get_root(struct udevice *dev); long virtio_fs_read(struct udevice *dev, u64 nodeid, u64 fh, u64 offset, void *buf, uint size); +/** + * virtio_fs_setup_dir() - Look up a directory and create a device for it + * + * Looks up a path to find the corresponding inode in the virtio-fs filesystem, + * then creates and probes a new 'directory' device to represent it. + * + * If the path is the root (NULL or "/"), it uses the root inode directly. + * + * @fsdev: The virtio-fs filesystem device + * @path: The path of the directory to set up (e.g., "/boot" or "/") + * @devp: On success, returns a pointer to the newly created directory device + * Return: 0 on success, -ve on error + */ +int virtio_fs_setup_dir(struct udevice *fsdev, const char *path, + struct udevice **devp); + #endif diff --git a/include/fs_common.h b/include/fs_common.h index ac613d0862b..d5cbb708836 100644 --- a/include/fs_common.h +++ b/include/fs_common.h @@ -68,6 +68,8 @@ struct fs_dirent { struct fs_dir_stream { #ifdef CONFIG_FS struct udevice *dev; + u64 fh; + u64 offset; #endif struct blk_desc *desc; int part; -- 2.43.0