
From: Simon Glass <sjg@chromium.org> Provide access to the virtual queue and some functions for sending various FUSE messages. Add myself as a maintainer of virtio. Signed-off-by: Simon Glass <sjg@chromium.org> --- MAINTAINERS | 1 + drivers/virtio/Kconfig | 13 + drivers/virtio/Makefile | 1 + drivers/virtio/fs.c | 427 +++++++++++++++++++++++++++++++++ drivers/virtio/fs_internal.h | 149 ++++++++++++ drivers/virtio/virtio-uclass.c | 1 + include/virtio.h | 1 + include/virtio_fs.h | 82 +++++++ 8 files changed, 675 insertions(+) create mode 100644 drivers/virtio/fs.c create mode 100644 drivers/virtio/fs_internal.h create mode 100644 include/virtio_fs.h diff --git a/MAINTAINERS b/MAINTAINERS index 566b577274e..70b06c71d2e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1854,6 +1854,7 @@ F: include/video*.h VirtIO M: Bin Meng <bmeng.cn@gmail.com> +M: Simon Glass <sjg@chromium.org> S: Maintained F: drivers/virtio/ F: cmd/virtio.c diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig index 858556fe802..088c27cfb4a 100644 --- a/drivers/virtio/Kconfig +++ b/drivers/virtio/Kconfig @@ -85,4 +85,17 @@ config VIRTIO_RNG help This is the virtual random number generator driver. It can be used with QEMU based targets. + +config VIRTIO_FS + bool "virtio filesystem driver" + depends on VIRTIO + default y + help + Provides support for virtio-fs which provides access to host files + within the guest OS. This needs a user-space helper (virtiofsd) when + running QEMU - see https://virtio-fs.gitlab.io/ for details. + + A specification for the protocol is available at + https://docs.oasis-open.org/virtio/virtio/v1.3/virtio-v1.3.html + endmenu diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile index 4709e16f789..aef4ae497f1 100644 --- a/drivers/virtio/Makefile +++ b/drivers/virtio/Makefile @@ -12,3 +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 diff --git a/drivers/virtio/fs.c b/drivers/virtio/fs.c new file mode 100644 index 00000000000..73e08e022a5 --- /dev/null +++ b/drivers/virtio/fs.c @@ -0,0 +1,427 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * U-Boot Virtio-FS Driver + * + * Copyright 2025 Simon Glass <sjg@chromium.org> + * + * This driver provides U-Boot with the ability to access a virtio-fs + * device, allowing the bootloader to read files from a shared directory + * on the host. This is particularly useful for loading kernels, device + * tree blobs, and other boot-time resources. + * + * The driver is implemented using the U-Boot driver model and the virtio + * uclass. It communicates with the host using the FUSE protocol over + * virtqueues. + */ + +#define LOG_CATEGORY UCLASS_VIRTIO + +#include <dir.h> +#include <dm.h> +#include <fs.h> +#include <log.h> +#include <virtio.h> +#include <virtio_fs.h> +#include <virtio_ring.h> +#include <linux/fuse.h> +#include "fs_internal.h" + +#define MAX_FNAME_LEN 256 /* including \0 terminator */ +#define VIRTIO_FS_TAG_SIZE 36 + +/** + * struct virtio_fs_config - Configuration info for virtio-fs + * + * Modelled on Linux v6.15 include/uapi/linux/type.h + * + * @tag: filesystem name padded with NUL + */ +struct __packed virtio_fs_config { + u8 tag[VIRTIO_FS_TAG_SIZE]; + __le32 unused1; + __le32 unused2; +}; + +/* + * Driver-private data + * + * @tag: Tag for the filesystem + * @root: inode of the root node, or 0 if not known + * @vdev: virtio device to which this FS is attached + * @vq: Virtual queue to use + * @config: Configuration read from QEMU + * @root_inode: Inode of the root directory exported into QEMU + * @next_id: ID to use for the next FUSE request + */ +struct virtio_fs_priv { + char tag[VIRTIO_FS_TAG_SIZE + 1]; + struct virtio_device *vdev; + struct virtqueue *vq; + struct virtio_fs_config config; + u64 root_inode; + int next_id; +}; + +u64 virtio_fs_get_root(struct udevice *dev) +{ + struct virtio_fs_priv *priv = dev_get_priv(dev); + + return priv->root_inode; +} + +/** + * virtio_fs_xfer() - Perform a transfer of a FUSE request + * + * @dev: virtio-fs device to use (UCLASS_FS) + * @inhdr: FUSE header to send + * @in: Extra data to send (must be present) + * @insize: Size of data to send + * @outhdr: Returns the output header received via FUSE + * @out: Place to put extra output data (NULL if none) + * @outsize: Size of output buffer at @out (must be 0 if @out is NULL) + * Return: 0 if OK, -ve on error + */ +static int virtio_fs_xfer(struct udevice *dev, struct fuse_in_header *inhdr, + const void *in, int insize, + struct fuse_out_header *outhdr, void *out, + int outsize) +{ + struct virtio_fs_priv *priv = dev_get_priv(dev); + struct virtqueue *vq = priv->vq; + struct virtio_sg sg[4]; + struct virtio_sg *sgs[4]; + uint dummy_len; + int i, ret; + + inhdr->unique = priv->next_id++; + + sg[0].addr = inhdr; + sg[0].length = sizeof(*inhdr); + sg[1].addr = (void *)in; + sg[1].length = insize; + sg[2].addr = outhdr; + sg[2].length = sizeof(*outhdr); + sg[3].addr = out; + sg[3].length = outsize; + for (i = 0; i < 4; i++) + sgs[i] = &sg[i]; + inhdr->len = sizeof(*inhdr) + insize; + outhdr->len = sizeof(*outhdr) + outsize; + + log_debug("sg[1].addr %p sg[3].length %zx\n", sg[1].addr, sg[3].length); + ret = virtqueue_add(vq, sgs, 2, out ? 2 : 1); + if (ret) { + log_err("Failed to add buffers to virtqueue\n"); + return ret; + } + + virtqueue_kick(vq); + + log_debug("wait..."); + while (!virtqueue_get_buf(vq, &dummy_len)) + ; + log_debug("done\n"); + if (outhdr->error) + return log_msg_ret("vix", outhdr->error); + + return 0; +} + +int virtio_fs_lookup_(struct udevice *dev, u64 nodeid, const char *name, + struct fuse_entry_out *out) +{ + struct fuse_in_header inhdr = {}; + struct fuse_out_header outhdr; + int name_len = strlen(name) + 1; + int ret; + + inhdr.opcode = FUSE_LOOKUP; + inhdr.nodeid = nodeid; + + ret = virtio_fs_xfer(dev, &inhdr, name, name_len, &outhdr, out, + sizeof(struct fuse_entry_out)); + log_debug("len %x error %x unique %llx\n", outhdr.len, outhdr.error, + outhdr.unique); + if (ret) + return log_msg_ret("vfl", ret); + + return 0; +} + +int virtio_fs_lookup(struct udevice *dev, const char *name, u64 *entryp) +{ + struct virtio_fs_priv *priv = dev_get_priv(dev); + struct fuse_entry_out out; + int ret; + + log_debug("dev '%s': lookup in inode %llx name '%s'\n", dev->name, + priv->root_inode, name); + ret = virtio_fs_lookup_(dev, priv->root_inode, name, &out); + if (ret) + return log_msg_ret("vfl", ret); + *entryp = out.nodeid; + + return 0; +} + +int virtio_fs_forget(struct udevice *dev, u64 nodeid) +{ + struct fuse_in_header inhdr = {}; + struct fuse_forget_in in; + struct fuse_out_header outhdr; + int ret; + + inhdr.opcode = FUSE_FORGET; + inhdr.nodeid = nodeid; + in.nlookup = 1; + + ret = virtio_fs_xfer(dev, &inhdr, &in, sizeof(in), &outhdr, NULL, 0); + log_debug("len %x error %x unique %llx\n", outhdr.len, outhdr.error, + outhdr.unique); + if (ret) + return log_msg_ret("vfl", ret); + + return 0; +} + +int virtio_fs_opendir(struct udevice *dev, u64 nodeid, u64 *fhp) +{ + struct fuse_in_header inhdr = {}; + struct fuse_open_in in = {}; + struct fuse_out_header outhdr; + struct fuse_open_out out; + int ret; + + inhdr.opcode = FUSE_OPENDIR; + inhdr.nodeid = nodeid; + + ret = virtio_fs_xfer(dev, &inhdr, &in, sizeof(in), &outhdr, &out, + sizeof(struct fuse_open_out)); + log_debug("fh %llx open_flags %x backing_id %x\n", out.fh, + out.open_flags, out.backing_id); + log_debug("len %x error %x unique %llx\n", outhdr.len, outhdr.error, + outhdr.unique); + if (ret) + return log_msg_ret("vfo", ret); + *fhp = out.fh; + + return 0; +} + +int virtio_fs_readdir(struct udevice *dev, u64 nodeid, u64 fh, u64 offset, + void *buf, int size, int *out_sizep) +{ + struct fuse_in_header inhdr = {}; + struct fuse_read_in in = {}; + struct fuse_out_header outhdr; + uint len; + int ret; + + inhdr.opcode = FUSE_READDIRPLUS; + inhdr.nodeid = nodeid; + + in.fh = fh; + in.size = size; + in.offset = offset; + ret = virtio_fs_xfer(dev, &inhdr, &in, sizeof(in), &outhdr, buf, size); + log_debug("len %x error %x unique %llx\n", outhdr.len, outhdr.error, + outhdr.unique); + if (ret) + return log_msg_ret("vfr", ret); + + len = outhdr.len - sizeof(outhdr); + /* sanity check */ + if (len > size) { + log_debug("virtio: internal size error outhdr.len %x size %x\n", + outhdr.len, size); + return log_msg_ret("vle", -EFAULT); + } + *out_sizep = len; + + return 0; +} + +int virtio_fs_releasedir(struct udevice *dev, u64 nodeid, u64 fh) +{ + struct fuse_in_header inhdr = {}; + struct fuse_release_in in = {}; + struct fuse_out_header outhdr; + int ret; + + inhdr.opcode = FUSE_RELEASEDIR; + inhdr.nodeid = nodeid; + in.fh = fh; + + ret = virtio_fs_xfer(dev, &inhdr, &in, sizeof(in), &outhdr, NULL, 0); + if (ret) + return log_msg_ret("vfe", ret); + log_debug("len %x error %x unique %llx\n", outhdr.len, outhdr.error, + outhdr.unique); + + return 0; +} + +int virtio_fs_open_file(struct udevice *dev, u64 nodeid, + enum dir_open_flags_t flags, u64 *fhp, uint *flagsp) +{ + struct fuse_in_header inhdr = {}; + char buf[MAX_FNAME_LEN + sizeof(struct fuse_open_in)]; + struct fuse_open_in *in = (struct fuse_open_in *)buf; + struct fuse_out_header outhdr; + struct fuse_open_out out; + int ret; + + inhdr.opcode = FUSE_OPEN; + inhdr.nodeid = nodeid; + + in->flags = 0; + in->open_flags = flags; + + ret = virtio_fs_xfer(dev, &inhdr, buf, sizeof(*in), &outhdr, + &out, sizeof(struct fuse_open_out)); + if (ret) + return log_msg_ret("vfr", ret); + + *fhp = out.fh; + *flagsp = out.open_flags; + + return 0; +} + +long virtio_fs_read(struct udevice *dev, u64 nodeid, u64 fh, u64 offset, + void *buf, uint size) +{ + struct fuse_in_header inhdr = {}; + struct fuse_read_in in = {}; + struct fuse_out_header outhdr; + int ret; + + log_debug("dev '%s' nodeid %lld fh %lld offset %llx size %x buf %p\n", + dev->name, nodeid, fh, offset, size, buf); + inhdr.opcode = FUSE_READ; + inhdr.nodeid = nodeid; + + in.fh = fh; + in.offset = offset; + in.size = size; + + ret = virtio_fs_xfer(dev, &inhdr, &in, sizeof(in), &outhdr, buf, size); + if (ret) + return log_msg_ret("vfr", ret); + log_debug("read len %x\n", outhdr.len); + + return outhdr.len; +} + +static int virtio_fs_init(struct udevice *dev) +{ + struct fuse_in_header inhdr = {}; + struct fuse_init_in in = {}; + struct fuse_out_header outhdr; + struct fuse_init_out out; + int ret; + + inhdr.opcode = FUSE_INIT; + + in.major = FUSE_KERNEL_VERSION; + in.minor = FUSE_KERNEL_MINOR_VERSION; + + ret = virtio_fs_xfer(dev, &inhdr, &in, sizeof(in), &outhdr, &out, + sizeof(out)); + if (ret) + return log_msg_ret("vfx", ret); + log_debug("major %x minor %x max_readahead %x flags %x ", out.major, + out.minor, out.max_readahead, out.flags); + log_debug("max_background %x congestion_threshold %x max_write %x\n", + out.max_background, out.congestion_threshold, out.max_write); + log_debug("time_gran %x max_pages %x, map_alignment %x flags2 %x ", + out.time_gran, out.max_pages, out.map_alignment, out.flags2); + log_debug("max_stack_depth %x request_timeout %x\n", + out.max_stack_depth, out.request_timeout); + + return 0; +} + +static int _virtio_fs_probe(struct udevice *dev) +{ + struct virtio_dev_priv *virtio_priv = dev_get_uclass_priv(dev->parent); + struct virtio_fs_priv *priv = dev_get_priv(dev); + int ret; + + /* Indicate what driver features we support */ + virtio_driver_features_init(virtio_priv, NULL, 0, NULL, 0); + + virtio_cread_bytes(dev, 0, &priv->tag, VIRTIO_FS_TAG_SIZE); + priv->tag[VIRTIO_FS_TAG_SIZE] = '\0'; + + log_debug("tag %s\n", priv->tag); + + ret = virtio_find_vqs(dev, 1, &priv->vq); + if (ret) + return log_msg_ret("vff", ret); + + return 0; +} + +static int virtio_fs_mount(struct udevice *dev) +{ + struct fs_priv *uc_priv = dev_get_uclass_priv(dev); + struct virtio_fs_priv *priv = dev_get_priv(dev); + struct fuse_entry_out out; + int ret; + + if (uc_priv->mounted) + return log_msg_ret("vfi", -EISCONN); + + ret = virtio_fs_init(dev); + if (ret) + return log_msg_ret("vfi", ret); + + ret = virtio_fs_lookup_(dev, FUSE_ROOT_ID, ".", &out); + if (ret) { + log_err("Failed to lookup root directory: %d\n", ret); + return ret; + } + priv->root_inode = out.nodeid; + log_debug("directory found, ino=%lld\n", priv->root_inode); + + uc_priv->mounted = true; + + return 0; +} + +static int virtio_fs_unmount(struct udevice *dev) +{ + struct fs_priv *uc_priv = dev_get_uclass_priv(dev); + struct virtio_fs_priv *priv = dev_get_priv(dev); + int ret; + + if (!uc_priv->mounted) + return log_msg_ret("vfu", -ENOTCONN); + + ret = virtio_fs_forget(dev, priv->root_inode); + if (ret) + return log_msg_ret("vff", ret); + + return 0; +} + +static const struct fs_ops virtio_fs_ops = { + .mount = virtio_fs_mount, + .unmount = virtio_fs_unmount, +}; + +static const struct udevice_id virtio_fs_ids[] = { + { .compatible = "virtio,fs" }, + { } +}; + +U_BOOT_DRIVER(virtio_fs) = { + .name = VIRTIO_FS_DRV_NAME, + .id = UCLASS_FS, + .of_match = virtio_fs_ids, + .ops = &virtio_fs_ops, + .probe = _virtio_fs_probe, + .priv_auto = sizeof(struct virtio_fs_priv), + .flags = DM_FLAG_ACTIVE_DMA, +}; diff --git a/drivers/virtio/fs_internal.h b/drivers/virtio/fs_internal.h new file mode 100644 index 00000000000..17260ce9134 --- /dev/null +++ b/drivers/virtio/fs_internal.h @@ -0,0 +1,149 @@ +/* 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 + */ + +#ifndef _FS_INTERNAL_H +#define _FS_INTERNAL_H + +#include <dir.h> + +struct fuse_entry_out; +struct udevice; + +/** + * struct virtio_fs_dir_priv - Information about a directory + * + * @inode: Associated inode for the directory + * @path: Path of this directory, e.g. '/fred/mary', or NULL for the root + * directory + */ +struct virtio_fs_dir_priv { + u64 inode; + char *path; +}; + +/** + * virtio_fs_lookup() - Look up an entry in a directory + * + * @dev: Filesystem device which holds the directory (UCLASS_FS) + * @nodeid: Node ID of the directory containing the entry + * @name: Name of the entry + * @out: Returns lookup info + * Return: 0 on success, -ENOENT if not found, other -ve on other error + */ +int virtio_fs_lookup_(struct udevice *dev, u64 nodeid, const char *name, + struct fuse_entry_out *out); + +/** + * virtio_fs_lookup() - Look up an entry in a directory + * + * This is a simplified wrapper around virtio_fs_lookup_() that performs a + * lookup within the filesystem's root directory. + * + * @dev: Filesystem device which holds the directory (UCLASS_FS) + * @name: Name of the entry + * @entryp: Returns the node ID of the entry, on success + * Return: 0 on success, -ENOENT if not found, other -ve on other error + */ +int virtio_fs_lookup(struct udevice *dev, const char *name, u64 *entryp); + +/** + * virtio_fs_forget() - Forget a nodeid + * + * Tells FUSE that this nodeid is no-longer needed + * + * @dev: Filesystem device which holds the directory (UCLASS_FS) + * @nodeid: Node ID to forget + * Return: 0 on success, -ve on error + */ +int virtio_fs_forget(struct udevice *dev, u64 nodeid); + +/** + * virtio_fs_opendir() - Open a directory for reading + * + * @dev: Filesystem device which holds the directory (UCLASS_FS) + * @nodeid: Node ID of the directory + * @fhp: Returns unique filehandle for the directory + * Return: 0 if OK, -ve on error + */ +int virtio_fs_opendir(struct udevice *dev, u64 nodeid, u64 *fhp); + +/** + * virtio_fs_readdir() - Read a chunk of entries from a directory + * + * Fills in @buf with directory records, using an internal FUSE format. The + * format is one struct fuse_direntplus (plus a name string) for each record. + * See include/linux/fuse.h for the struct details (unfortunately not + * commented). Use FUSE_DIRENTPLUS_SIZE() to calculate the size of each entry. + * + * It would be possible to convert the format into struct fs_dirent but the + * API which uses it only allows returning a single record at a time, so it + * seems best to tackle that later. + * + * @dev: Filesystem device which holds the directory (UCLASS_FS) + * @nodeid: Node ID of the directory + * @fh: Unique filehandle for the directory + * @offset: Start offset for reading; use the value returned from the previous + * call, or 0 to start at the beginning + * @buf: Buffer to receive records, must be large enough to hold at least one + * struct fuse_direntplus + * @size: Size of buffer + * @out_sizep: Returns amount of data used in the buffer + */ +int virtio_fs_readdir(struct udevice *dev, u64 nodeid, u64 fh, u64 offset, + void *buf, int size, int *out_sizep); + +/** + * virtio_fs_releasedir() - Close a directory + * + * Use this on a directory opened with virtio_fs_opendir() when you have + * finished reading entries with virtio_fs_readdir() + * + * @dev: Filesystem device which holds the directory (UCLASS_FS) + * @nodeid: Node ID of the directory + * @fh: Unique filehandle for the directory + */ +int virtio_fs_releasedir(struct udevice *dev, u64 nodeid, u64 fh); + +/** + * virtio_fs_open_file() - Open a file + * + * @dev: Filesystem device which holds the file (UCLASS_FS) + * @nodeid: Node ID of the directory containing the file + * @flags: Open-mode flags to use + * @fhp: Return unique filehandle for the directory + * @flagsp: Updated open-mode flags + */ +int virtio_fs_open_file(struct udevice *dev, u64 nodeid, + enum dir_open_flags_t flags, u64 *fhp, uint *flagsp); + +/** + * virtio_fs_get_root() - Get the nodeid of the root directory of the virtio-fs + * + * Return: node ID of root node + */ +u64 virtio_fs_get_root(struct udevice *dev); + +/** + * virtio_fs_read() - Read data from an open file + * + * Read a block of data from a file previously opened with + * virtio_fs_open_file() + * + * @dev: Filesystem device (UCLASS_FS) + * @nodeid: Node ID of the file being read (for context) + * @fh: File handle of the open file + * @offset: Offset within the file to start reading from + * @buf: Buffer to store the read data + * @size: Maximum number of bytes to read + * Return: Number of bytes read on success, -ve on error + */ +long virtio_fs_read(struct udevice *dev, u64 nodeid, u64 fh, u64 offset, + void *buf, uint size); + +#endif diff --git a/drivers/virtio/virtio-uclass.c b/drivers/virtio/virtio-uclass.c index 19728fdbe89..947d224bbd2 100644 --- a/drivers/virtio/virtio-uclass.c +++ b/drivers/virtio/virtio-uclass.c @@ -30,6 +30,7 @@ static const char *const virtio_drv_name[VIRTIO_ID_MAX_NUM] = { [VIRTIO_ID_NET] = VIRTIO_NET_DRV_NAME, [VIRTIO_ID_BLOCK] = VIRTIO_BLK_DRV_NAME, [VIRTIO_ID_RNG] = VIRTIO_RNG_DRV_NAME, + [VIRTIO_ID_FS] = VIRTIO_FS_DRV_NAME, }; int virtio_get_config(struct udevice *vdev, unsigned int offset, diff --git a/include/virtio.h b/include/virtio.h index ef4eeb16a29..b0811cbdc24 100644 --- a/include/virtio.h +++ b/include/virtio.h @@ -30,6 +30,7 @@ #define VIRTIO_NET_DRV_NAME "virtio-net" #define VIRTIO_BLK_DRV_NAME "virtio-blk" #define VIRTIO_RNG_DRV_NAME "virtio-rng" +#define VIRTIO_FS_DRV_NAME "virtio-fs" /* Status byte for guest to report progress, and synchronize features */ diff --git a/include/virtio_fs.h b/include/virtio_fs.h new file mode 100644 index 00000000000..c01e1c3c591 --- /dev/null +++ b/include/virtio_fs.h @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef VIRTIO_FS_H +#define VIRTIO_FS_H + +struct blk_desc; +struct disk_partition; +struct fs_dirent; +struct fs_dir_stream; + +/** + * virtio_fs_compat_opendir() - Open a directory + * + * This is a compatibility interface for struct fstype_info + * + * @fname: Path to directory to open ("/" for root) + * @strmp: Returns an allocated pointer to the new stream + * Return 0 if OK, -ve on error + */ +int virtio_fs_compat_opendir(const char *fname, struct fs_dir_stream **strmp); + +/** + * virtio_fs_compat_readdir() - Read a single directory entry + * + * This is a compatibility interface for struct fstype_info + * + * @strm: Directory stream as created by virtio_fs_compat_opendir() + * @dentp: Return an allocated entry on success + * Return: 0 if OK, -ENOENT if no more entries, other -ve value on other error + */ +int virtio_fs_compat_readdir(struct fs_dir_stream *strm, + struct fs_dirent **dentp); + +/** + * virtio_fs_compat_closedir() - Stop reading the directory + * + * This is a compatibility interface for struct fstype_info + * + * Frees @strm and releases the directory + * + * @strm: Directory stream as created by virtio_fs_compat_opendir() + */ +void virtio_fs_compat_closedir(struct fs_dir_stream *strm); + +/** + * virtio_fs_compat_probe() - Probe for a virtio-fs filesystem + * + * This is a compatibility interface for struct fstype_info + * + * For now, this just locates the first available UCLASS_FS device and sets a + * global variable to it, for use by the above virtio_fs_compat_...() functions. + * + * @fs_dev_desc: Block device (not used, can be NULL) + * @fs_partition: Partition (not used, can be NULL) + * Return: 0 if OK, -ve on error + */ +int virtio_fs_compat_probe(struct blk_desc *fs_dev_desc, + struct disk_partition *fs_partition); + +/** + * virtio_fs_compat_size() - Get the size of a file + * + * @fname: Filename to check + * @sizep: Returns size of the file, on success + * Return: 0 if OK, -ve on error + */ +int virtio_fs_compat_size(const char *fname, loff_t *sizep); + +/** + * virtio_fs_compat_read() - Read from a file + * + * @fname: Filename to read from + * @buf: Buffer to read into + * @offset: Offset within the file to start reading + * @len: Number of bytes to read, or 0 to read all + * @actread: Returns the number of bytes actually read, on success + * Return: 0 if OK, -ve on error + */ +int virtio_fs_compat_read(const char *fname, void *buf, loff_t offset, + loff_t len, loff_t *actread); + +#endif -- 2.43.0