virtio: Plumb virtio-fs into the legacy filesystem code
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. Series-to: concept Cover-letter: virtio: Support virtio-fs This series introduces support for virtio-fs, a filesystem which provides access to host files from within a QEMU guest OS. A new filesystem driver is created with support for the three uclasses (FS, DIR, FILE). A compatibility layer is added as well, so that the existing cmdline work as expected. Only listing directories and reading files are supported so far. Since sandbox works by using a NULL blk_desc, a workaround is added for now. Once we switch commands (and bootstd!) over to the new filesystem approach, this will go away. It is possible to test this using something like: ./scripts/build-qemu -a x86 -rs -D . then within U-Boot: ls virtio 0 END Signed-off-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
163
drivers/virtio/fs_compat.c
Normal file
163
drivers/virtio/fs_compat.c
Normal file
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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 CONFIG_IS_ENABLED(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);
|
||||
|
||||
12
include/fs.h
12
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
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user