
From: Simon Glass <sjg@chromium.org> Add compatibility function to support a single virtio-fs via the normal 'ls' and 'load' commands. Other commands are not supported for now. Add a workaround for the fact that virtiofs does not use a block device. The existing filesystem layer does not support non-block filesystems - sandbox's hostfs mostly bypasses this code. Make sure that sandbox does not try to mount a virtio-fs as this does not work at present. It will require either a fake driver for virtio-fs which connects to host files, or possibly something involving FUSE. Signed-off-by: Simon Glass <sjg@chromium.org> --- disk/part.c | 6 ++ drivers/virtio/Makefile | 2 +- drivers/virtio/fs_compat.c | 163 +++++++++++++++++++++++++++++++++++++ fs/fs-uclass.c | 25 ++++++ fs/fs_legacy.c | 43 +++++++++- include/fs.h | 12 +++ include/fs_common.h | 4 + 7 files changed, 252 insertions(+), 3 deletions(-) create mode 100644 drivers/virtio/fs_compat.c diff --git a/disk/part.c b/disk/part.c index bab27ba07d8..3aab99b069b 100644 --- a/disk/part.c +++ b/disk/part.c @@ -482,7 +482,13 @@ int blk_get_device_part_str(const char *ifname, const char *dev_part_str, return 0; } #endif + if (IS_ENABLED(CONFIG_VIRTIO_FS) && !strcmp(ifname, "virtiofs")) { + strcpy((char *)info->type, BOOT_PART_TYPE); + strcpy((char *)info->name, "Virtio filesystem"); + info->fs_type = FS_TYPE_VIRTIO; + return 0; + } #if IS_ENABLED(CONFIG_CMD_UBIFS) && !IS_ENABLED(CONFIG_XPL_BUILD) /* * Special-case ubi, ubi goes through a mtd, rather than through diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile index 37b805a9c24..60ec9b65b17 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 fs_dir.o fs_file.o +obj-$(CONFIG_VIRTIO_FS) += fs.o fs_dir.o fs_file.o fs_compat.o diff --git a/drivers/virtio/fs_compat.c b/drivers/virtio/fs_compat.c new file mode 100644 index 00000000000..a3819f01ba5 --- /dev/null +++ b/drivers/virtio/fs_compat.c @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * U-Boot Virtio-FS compatibility layer, to allow use with the legacy + * filesystem-layer + * + * Copyright 2025 Simon Glass <sjg@chromium.org> + * + */ + +#define LOG_CATEGORY UCLASS_VIRTIO + +#include <dir.h> +#include <dm.h> +#include <file.h> +#include <fs.h> +#include <malloc.h> +#include <virtio.h> +#include <virtio_fs.h> +#include <linux/fuse.h> +#include "fs_internal.h" + +static struct udevice *fs_dev; + +int virtio_fs_compat_opendir(const char *fname, struct fs_dir_stream **strmp) +{ + struct udevice *dev; + int ret; + + log_debug("starting fs_dev %p\n", fs_dev); + log_debug("lookup dev '%s' fname '%s'\n", fs_dev->name, fname); + ret = fs_lookup_dir(fs_dev, fname, &dev); + if (ret) + return log_msg_ret("vld", ret); + + log_debug("open\n"); + ret = dir_open(dev, strmp); + if (ret) + return log_msg_ret("vdo", ret); + + return 0; +} + +int virtio_fs_compat_readdir(struct fs_dir_stream *strm, + struct fs_dirent **dentp) +{ + struct fs_dirent *dent; + int ret; + + log_debug("read dev '%s'\n", fs_dev->name); + dent = malloc(sizeof(struct fs_dirent)); + if (!dent) + return log_msg_ret("vrD", -ENOMEM); + + ret = dir_read(strm->dev, strm, dent); + if (ret) { + free(dent); + return log_msg_ret("vrd", ret); + } + *dentp = dent; + log_debug("read done\n"); + + return 0; +} + +void virtio_fs_compat_closedir(struct fs_dir_stream *strm) +{ + int ret; + + log_debug("close dev '%s'\n", fs_dev->name); + ret = dir_close(strm->dev, strm); + if (ret) + log_err("dir_close() failed: %dE\n", ret); +} + +int virtio_fs_compat_probe(struct blk_desc *fs_dev_desc, + struct disk_partition *fs_partition) +{ + struct udevice *dev; + int ret; + + /* + * at present, sandbox cannot support virtiofs fully, so add a + * work-around here + */ + if (IS_ENABLED(CONFIG_SANDBOX)) + return log_msg_ret("vfc", -ENOENT); + + ret = uclass_first_device_err(UCLASS_FS, &dev); + if (ret) { + printf("No filesystem (err %dE)\n", ret); + return ret; + } + ret = fs_mount(dev); + if (ret && ret != -EISCONN) { + printf("Cannot mount filesystem (err %dE)\n", ret); + return ret; + } + + fs_dev = dev; + log_debug("fs_dev %p\n", fs_dev); + + return 0; +} + +int virtio_fs_compat_size(const char *fname, loff_t *sizep) +{ + struct fuse_entry_out out; + const char *leaf; + char *subdir; + int ret; + + log_debug("filename '%s'\n", fname); + + ret = fs_split_path(fname, &subdir, &leaf); + if (ret) + return log_msg_ret("vcp", ret); + log_debug("subdir '%s' leaf '%s'\n", subdir, leaf); + + ret = virtio_fs_lookup_(fs_dev, virtio_fs_get_root(fs_dev), fname, + &out); + if (ret) + return log_msg_ret("vcl", ret); + + log_debug("inode %llx size %llx\n", out.nodeid, out.attr.size); + *sizep = out.attr.size; + + return 0; +} + +int virtio_fs_compat_read(const char *fname, void *buf, loff_t offset, + loff_t len, loff_t *actread) +{ + struct udevice *dir, *fil; + const char *leaf; + char *subdir; + long nread; + int ret; + + log_debug("load '%s'\n", fname); + + ret = fs_split_path(fname, &subdir, &leaf); + if (ret) + return log_msg_ret("fcr", ret); + log_debug("subdir '%s' leaf '%s'\n", subdir, leaf); + + ret = fs_lookup_dir(fs_dev, subdir, &dir); + if (ret) + return log_msg_ret("fcl", ret); + log_debug("dir '%s'\n", dir->name); + + ret = virtio_fs_setup_file(dir, leaf, DIR_O_RDONLY, &fil); + log_debug("virtio_fs_file_open returned %d\n", ret); + if (ret) + return log_msg_ret("fco", ret); + + log_debug("reading file '%s'\n", fil->name); + nread = file_read_at(fil, buf, offset, len); + if (nread < 0) + return log_msg_ret("fco", nread); + *actread = nread; + + return 0; +} diff --git a/fs/fs-uclass.c b/fs/fs-uclass.c index 7311bc72e04..96fb04a106c 100644 --- a/fs/fs-uclass.c +++ b/fs/fs-uclass.c @@ -13,6 +13,31 @@ #include <fs.h> #include <dm/device-internal.h> +int fs_split_path(const char *fname, char **subdirp, const char **leafp) +{ + char *subdir, *p; + + if (!*fname) + return log_msg_ret("fsp", -EINVAL); + + /* allocate space for the whole filename, for simplicity */ + subdir = strdup(fname); + if (!subdir) + return log_msg_ret("fsp", -ENOMEM); + + p = strrchr(subdir, '/'); + if (p) { + *leafp = p + 1; + *p = '\0'; + } else { + *leafp = fname; + strcpy(subdir, "/"); + } + *subdirp = subdir; + + return 0; +} + int fs_lookup_dir(struct udevice *dev, const char *path, struct udevice **dirp) { struct fs_ops *ops = fs_get_ops(dev); diff --git a/fs/fs_legacy.c b/fs/fs_legacy.c index 82936142c07..8e3a6702730 100644 --- a/fs/fs_legacy.c +++ b/fs/fs_legacy.c @@ -35,6 +35,7 @@ #include <squashfs.h> #include <erofs.h> #include <exfat.h> +#include <virtio_fs.h> DECLARE_GLOBAL_DATA_PTR; @@ -405,6 +406,28 @@ static struct fstype_info fstypes[] = { .mkdir = exfat_fs_mkdir, .rename = exfat_fs_rename, }, +#endif +#if IS_ENABLED(CONFIG_VIRTIO_FS) + { + .fstype = FS_TYPE_VIRTIO, + .name = "virtio", + .null_dev_desc_ok = true, + .probe = virtio_fs_compat_probe, + .opendir = virtio_fs_compat_opendir, + .readdir = virtio_fs_compat_readdir, + .ls = fs_ls_generic, + .read = virtio_fs_compat_read, + .size = virtio_fs_compat_size, + .close = fs_close_unsupported, + .closedir = virtio_fs_compat_closedir, + .exists = fs_exists_unsupported, + .uuid = fs_uuid_unsupported, + .write = fs_write_unsupported, + .ln = fs_ln_unsupported, + .unlink = fs_unlink_unsupported, + .mkdir = fs_mkdir_unsupported, + .rename = fs_rename_unsupported, + }, #endif { .fstype = FS_TYPE_ANY, @@ -693,7 +716,15 @@ struct fs_dirent *fs_readdir(struct fs_dir_stream *dirs) struct fs_dirent *dirent; int ret; - fs_set_blk_dev_with_part(dirs->desc, dirs->part); + /* + * If this is not a block device we can assume it is virtiofs, since we + * cannot reach this code otherwise. Sandbox uses its own 'ls' method. + */ + if (dirs->desc) + fs_set_blk_dev_with_part(dirs->desc, dirs->part); + else + fs_type = FS_TYPE_VIRTIO; + info = fs_get_info(fs_type); ret = info->readdir(dirs, &dirent); @@ -713,7 +744,15 @@ void fs_closedir(struct fs_dir_stream *dirs) if (!dirs) return; - fs_set_blk_dev_with_part(dirs->desc, dirs->part); + /* + * If this is not a block device we can assume it is virtiofs, since we + * cannot reach this code otherwise. Sandbox uses its own 'ls' method. + */ + if (dirs->desc) + fs_set_blk_dev_with_part(dirs->desc, dirs->part); + else + fs_type = FS_TYPE_VIRTIO; + info = fs_get_info(fs_type); info->closedir(dirs); diff --git a/include/fs.h b/include/fs.h index e682ff7fa6a..5321d339b07 100644 --- a/include/fs.h +++ b/include/fs.h @@ -96,4 +96,16 @@ int fs_unmount(struct udevice *dev); */ int fs_lookup_dir(struct udevice *dev, const char *path, struct udevice **dirp); +/** + * fs_split_path() - Get a list of subdirs in a filename + * + * For example, '/path/to/fred' returns an alist containing allocated strings + * 'path' and 'to', with \*leafp pointing to the 'f' + * + * @fname: Filename to parse + * @subdirp: Returns an allocating string containing the subdirs, or "/" if none + * @leafp: Returns a pointer to the leaf filename, within @fname + */ +int fs_split_path(const char *fname, char **subdirp, const char **leafp); + #endif diff --git a/include/fs_common.h b/include/fs_common.h index d5cbb708836..8cb3999fb10 100644 --- a/include/fs_common.h +++ b/include/fs_common.h @@ -7,6 +7,9 @@ #include <rtc.h> +/** + * @FS_TYPE_VIRTIO: virtio-fs for access to the host filesystem from QEMU + */ enum fs_type_t { FS_TYPE_ANY = 0, FS_TYPE_FAT, @@ -18,6 +21,7 @@ enum fs_type_t { FS_TYPE_EROFS, FS_TYPE_SEMIHOSTING, FS_TYPE_EXFAT, + FS_TYPE_VIRTIO, }; /* -- 2.43.0