[PATCH 0/7] virtio: Support virtio-fs

From: Simon Glass <sjg@chromium.org> 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 Simon Glass (7): fs: Bring in fuse header file from Linux virtio: Acknowledge devices only when probed virtio: Rename a few legacy-PCI functions virtio: Add top-level functions for virtio-fs virtio: Add support for directories virtio: Add support for files virtio: Plumb virtio-fs into the legacy filesystem code MAINTAINERS | 2 + disk/part.c | 6 + drivers/virtio/Kconfig | 13 + drivers/virtio/Makefile | 1 + drivers/virtio/fs.c | 428 +++++++++ drivers/virtio/fs_compat.c | 163 ++++ drivers/virtio/fs_dir.c | 200 +++++ drivers/virtio/fs_file.c | 136 +++ drivers/virtio/fs_internal.h | 181 ++++ drivers/virtio/virtio-uclass.c | 13 +- drivers/virtio/virtio_pci_legacy.c | 8 +- fs/fs-uclass.c | 25 + fs/fs_legacy.c | 43 +- include/fs.h | 12 + include/fs_common.h | 6 + include/linux/fuse.h | 1289 ++++++++++++++++++++++++++++ include/virtio.h | 1 + include/virtio_fs.h | 82 ++ 18 files changed, 2594 insertions(+), 15 deletions(-) create mode 100644 drivers/virtio/fs.c create mode 100644 drivers/virtio/fs_compat.c create mode 100644 drivers/virtio/fs_dir.c create mode 100644 drivers/virtio/fs_file.c create mode 100644 drivers/virtio/fs_internal.h create mode 100644 include/linux/fuse.h create mode 100644 include/virtio_fs.h -- 2.43.0 base-commit: 0886499b1facb39c23a6ee5a014c192dc881e5e2 branch: qemf

From: Simon Glass <sjg@chromium.org> This is needed for virtio-fs since it makes use of the same structures. Add a version from Linux v6.15 Signed-off-by: Simon Glass <sjg@chromium.org> --- MAINTAINERS | 1 + include/linux/fuse.h | 1289 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1290 insertions(+) create mode 100644 include/linux/fuse.h diff --git a/MAINTAINERS b/MAINTAINERS index e5389aeae45..566b577274e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1203,6 +1203,7 @@ F: include/file.h F: include/fpga.h F: include/fs.h F: include/iovec.h +F: include/linux/fuse.h F: test/dm/fpga.c FLATTENED DEVICE TREE diff --git a/include/linux/fuse.h b/include/linux/fuse.h new file mode 100644 index 00000000000..5ec43ecbceb --- /dev/null +++ b/include/linux/fuse.h @@ -0,0 +1,1289 @@ +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-2-Clause) */ +/* + This file defines the kernel interface of FUSE + Copyright (C) 2001-2008 Miklos Szeredi <miklos@szeredi.hu> + + This program can be distributed under the terms of the GNU GPL. + See the file COPYING. + + This -- and only this -- header file may also be distributed under + the terms of the BSD Licence as follows: + + Copyright (C) 2001-2007 Miklos Szeredi. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. +*/ + +/* + * This file defines the kernel interface of FUSE + * + * Protocol changelog: + * + * 7.1: + * - add the following messages: + * FUSE_SETATTR, FUSE_SYMLINK, FUSE_MKNOD, FUSE_MKDIR, FUSE_UNLINK, + * FUSE_RMDIR, FUSE_RENAME, FUSE_LINK, FUSE_OPEN, FUSE_READ, FUSE_WRITE, + * FUSE_RELEASE, FUSE_FSYNC, FUSE_FLUSH, FUSE_SETXATTR, FUSE_GETXATTR, + * FUSE_LISTXATTR, FUSE_REMOVEXATTR, FUSE_OPENDIR, FUSE_READDIR, + * FUSE_RELEASEDIR + * - add padding to messages to accommodate 32-bit servers on 64-bit kernels + * + * 7.2: + * - add FOPEN_DIRECT_IO and FOPEN_KEEP_CACHE flags + * - add FUSE_FSYNCDIR message + * + * 7.3: + * - add FUSE_ACCESS message + * - add FUSE_CREATE message + * - add filehandle to fuse_setattr_in + * + * 7.4: + * - add frsize to fuse_kstatfs + * - clean up request size limit checking + * + * 7.5: + * - add flags and max_write to fuse_init_out + * + * 7.6: + * - add max_readahead to fuse_init_in and fuse_init_out + * + * 7.7: + * - add FUSE_INTERRUPT message + * - add POSIX file lock support + * + * 7.8: + * - add lock_owner and flags fields to fuse_release_in + * - add FUSE_BMAP message + * - add FUSE_DESTROY message + * + * 7.9: + * - new fuse_getattr_in input argument of GETATTR + * - add lk_flags in fuse_lk_in + * - add lock_owner field to fuse_setattr_in, fuse_read_in and fuse_write_in + * - add blksize field to fuse_attr + * - add file flags field to fuse_read_in and fuse_write_in + * - Add ATIME_NOW and MTIME_NOW flags to fuse_setattr_in + * + * 7.10 + * - add nonseekable open flag + * + * 7.11 + * - add IOCTL message + * - add unsolicited notification support + * - add POLL message and NOTIFY_POLL notification + * + * 7.12 + * - add umask flag to input argument of create, mknod and mkdir + * - add notification messages for invalidation of inodes and + * directory entries + * + * 7.13 + * - make max number of background requests and congestion threshold + * tunables + * + * 7.14 + * - add splice support to fuse device + * + * 7.15 + * - add store notify + * - add retrieve notify + * + * 7.16 + * - add BATCH_FORGET request + * - FUSE_IOCTL_UNRESTRICTED shall now return with array of 'struct + * fuse_ioctl_iovec' instead of ambiguous 'struct iovec' + * - add FUSE_IOCTL_32BIT flag + * + * 7.17 + * - add FUSE_FLOCK_LOCKS and FUSE_RELEASE_FLOCK_UNLOCK + * + * 7.18 + * - add FUSE_IOCTL_DIR flag + * - add FUSE_NOTIFY_DELETE + * + * 7.19 + * - add FUSE_FALLOCATE + * + * 7.20 + * - add FUSE_AUTO_INVAL_DATA + * + * 7.21 + * - add FUSE_READDIRPLUS + * - send the requested events in POLL request + * + * 7.22 + * - add FUSE_ASYNC_DIO + * + * 7.23 + * - add FUSE_WRITEBACK_CACHE + * - add time_gran to fuse_init_out + * - add reserved space to fuse_init_out + * - add FATTR_CTIME + * - add ctime and ctimensec to fuse_setattr_in + * - add FUSE_RENAME2 request + * - add FUSE_NO_OPEN_SUPPORT flag + * + * 7.24 + * - add FUSE_LSEEK for SEEK_HOLE and SEEK_DATA support + * + * 7.25 + * - add FUSE_PARALLEL_DIROPS + * + * 7.26 + * - add FUSE_HANDLE_KILLPRIV + * - add FUSE_POSIX_ACL + * + * 7.27 + * - add FUSE_ABORT_ERROR + * + * 7.28 + * - add FUSE_COPY_FILE_RANGE + * - add FOPEN_CACHE_DIR + * - add FUSE_MAX_PAGES, add max_pages to init_out + * - add FUSE_CACHE_SYMLINKS + * + * 7.29 + * - add FUSE_NO_OPENDIR_SUPPORT flag + * + * 7.30 + * - add FUSE_EXPLICIT_INVAL_DATA + * - add FUSE_IOCTL_COMPAT_X32 + * + * 7.31 + * - add FUSE_WRITE_KILL_PRIV flag + * - add FUSE_SETUPMAPPING and FUSE_REMOVEMAPPING + * - add map_alignment to fuse_init_out, add FUSE_MAP_ALIGNMENT flag + * + * 7.32 + * - add flags to fuse_attr, add FUSE_ATTR_SUBMOUNT, add FUSE_SUBMOUNTS + * + * 7.33 + * - add FUSE_HANDLE_KILLPRIV_V2, FUSE_WRITE_KILL_SUIDGID, FATTR_KILL_SUIDGID + * - add FUSE_OPEN_KILL_SUIDGID + * - extend fuse_setxattr_in, add FUSE_SETXATTR_EXT + * - add FUSE_SETXATTR_ACL_KILL_SGID + * + * 7.34 + * - add FUSE_SYNCFS + * + * 7.35 + * - add FOPEN_NOFLUSH + * + * 7.36 + * - extend fuse_init_in with reserved fields, add FUSE_INIT_EXT init flag + * - add flags2 to fuse_init_in and fuse_init_out + * - add FUSE_SECURITY_CTX init flag + * - add security context to create, mkdir, symlink, and mknod requests + * - add FUSE_HAS_INODE_DAX, FUSE_ATTR_DAX + * + * 7.37 + * - add FUSE_TMPFILE + * + * 7.38 + * - add FUSE_EXPIRE_ONLY flag to fuse_notify_inval_entry + * - add FOPEN_PARALLEL_DIRECT_WRITES + * - add total_extlen to fuse_in_header + * - add FUSE_MAX_NR_SECCTX + * - add extension header + * - add FUSE_EXT_GROUPS + * - add FUSE_CREATE_SUPP_GROUP + * - add FUSE_HAS_EXPIRE_ONLY + * + * 7.39 + * - add FUSE_DIRECT_IO_ALLOW_MMAP + * - add FUSE_STATX and related structures + * + * 7.40 + * - add max_stack_depth to fuse_init_out, add FUSE_PASSTHROUGH init flag + * - add backing_id to fuse_open_out, add FOPEN_PASSTHROUGH open flag + * - add FUSE_NO_EXPORT_SUPPORT init flag + * - add FUSE_NOTIFY_RESEND, add FUSE_HAS_RESEND init flag + * + * 7.41 + * - add FUSE_ALLOW_IDMAP + * 7.42 + * - Add FUSE_OVER_IO_URING and all other io-uring related flags and data + * structures: + * - struct fuse_uring_ent_in_out + * - struct fuse_uring_req_header + * - struct fuse_uring_cmd_req + * - FUSE_URING_IN_OUT_HEADER_SZ + * - FUSE_URING_OP_IN_OUT_SZ + * - enum fuse_uring_cmd + * + * 7.43 + * - add FUSE_REQUEST_TIMEOUT + */ + +#ifndef _LINUX_FUSE_H +#define _LINUX_FUSE_H + +#ifdef __KERNEL__ +#include <linux/types.h> +#else +#include <stdint.h> +#endif + +/* + * Version negotiation: + * + * Both the kernel and userspace send the version they support in the + * INIT request and reply respectively. + * + * If the major versions match then both shall use the smallest + * of the two minor versions for communication. + * + * If the kernel supports a larger major version, then userspace shall + * reply with the major version it supports, ignore the rest of the + * INIT message and expect a new INIT message from the kernel with a + * matching major version. + * + * If the library supports a larger major version, then it shall fall + * back to the major protocol version sent by the kernel for + * communication and reply with that major version (and an arbitrary + * supported minor version). + */ + +/** Version number of this interface */ +#define FUSE_KERNEL_VERSION 7 + +/** Minor version number of this interface */ +#define FUSE_KERNEL_MINOR_VERSION 43 + +/** The node ID of the root inode */ +#define FUSE_ROOT_ID 1 + +/* Make sure all structures are padded to 64bit boundary, so 32bit + userspace works under 64bit kernels */ + +struct fuse_attr { + uint64_t ino; + uint64_t size; + uint64_t blocks; + uint64_t atime; + uint64_t mtime; + uint64_t ctime; + uint32_t atimensec; + uint32_t mtimensec; + uint32_t ctimensec; + uint32_t mode; + uint32_t nlink; + uint32_t uid; + uint32_t gid; + uint32_t rdev; + uint32_t blksize; + uint32_t flags; +}; + +/* + * The following structures are bit-for-bit compatible with the statx(2) ABI in + * Linux. + */ +struct fuse_sx_time { + int64_t tv_sec; + uint32_t tv_nsec; + int32_t __reserved; +}; + +struct fuse_statx { + uint32_t mask; + uint32_t blksize; + uint64_t attributes; + uint32_t nlink; + uint32_t uid; + uint32_t gid; + uint16_t mode; + uint16_t __spare0[1]; + uint64_t ino; + uint64_t size; + uint64_t blocks; + uint64_t attributes_mask; + struct fuse_sx_time atime; + struct fuse_sx_time btime; + struct fuse_sx_time ctime; + struct fuse_sx_time mtime; + uint32_t rdev_major; + uint32_t rdev_minor; + uint32_t dev_major; + uint32_t dev_minor; + uint64_t __spare2[14]; +}; + +struct fuse_kstatfs { + uint64_t blocks; + uint64_t bfree; + uint64_t bavail; + uint64_t files; + uint64_t ffree; + uint32_t bsize; + uint32_t namelen; + uint32_t frsize; + uint32_t padding; + uint32_t spare[6]; +}; + +struct fuse_file_lock { + uint64_t start; + uint64_t end; + uint32_t type; + uint32_t pid; /* tgid */ +}; + +/** + * Bitmasks for fuse_setattr_in.valid + */ +#define FATTR_MODE (1 << 0) +#define FATTR_UID (1 << 1) +#define FATTR_GID (1 << 2) +#define FATTR_SIZE (1 << 3) +#define FATTR_ATIME (1 << 4) +#define FATTR_MTIME (1 << 5) +#define FATTR_FH (1 << 6) +#define FATTR_ATIME_NOW (1 << 7) +#define FATTR_MTIME_NOW (1 << 8) +#define FATTR_LOCKOWNER (1 << 9) +#define FATTR_CTIME (1 << 10) +#define FATTR_KILL_SUIDGID (1 << 11) + +/** + * Flags returned by the OPEN request + * + * FOPEN_DIRECT_IO: bypass page cache for this open file + * FOPEN_KEEP_CACHE: don't invalidate the data cache on open + * FOPEN_NONSEEKABLE: the file is not seekable + * FOPEN_CACHE_DIR: allow caching this directory + * FOPEN_STREAM: the file is stream-like (no file position at all) + * FOPEN_NOFLUSH: don't flush data cache on close (unless FUSE_WRITEBACK_CACHE) + * FOPEN_PARALLEL_DIRECT_WRITES: Allow concurrent direct writes on the same inode + * FOPEN_PASSTHROUGH: passthrough read/write io for this open file + */ +#define FOPEN_DIRECT_IO (1 << 0) +#define FOPEN_KEEP_CACHE (1 << 1) +#define FOPEN_NONSEEKABLE (1 << 2) +#define FOPEN_CACHE_DIR (1 << 3) +#define FOPEN_STREAM (1 << 4) +#define FOPEN_NOFLUSH (1 << 5) +#define FOPEN_PARALLEL_DIRECT_WRITES (1 << 6) +#define FOPEN_PASSTHROUGH (1 << 7) + +/** + * INIT request/reply flags + * + * FUSE_ASYNC_READ: asynchronous read requests + * FUSE_POSIX_LOCKS: remote locking for POSIX file locks + * FUSE_FILE_OPS: kernel sends file handle for fstat, etc... (not yet supported) + * FUSE_ATOMIC_O_TRUNC: handles the O_TRUNC open flag in the filesystem + * FUSE_EXPORT_SUPPORT: filesystem handles lookups of "." and ".." + * FUSE_BIG_WRITES: filesystem can handle write size larger than 4kB + * FUSE_DONT_MASK: don't apply umask to file mode on create operations + * FUSE_SPLICE_WRITE: kernel supports splice write on the device + * FUSE_SPLICE_MOVE: kernel supports splice move on the device + * FUSE_SPLICE_READ: kernel supports splice read on the device + * FUSE_FLOCK_LOCKS: remote locking for BSD style file locks + * FUSE_HAS_IOCTL_DIR: kernel supports ioctl on directories + * FUSE_AUTO_INVAL_DATA: automatically invalidate cached pages + * FUSE_DO_READDIRPLUS: do READDIRPLUS (READDIR+LOOKUP in one) + * FUSE_READDIRPLUS_AUTO: adaptive readdirplus + * FUSE_ASYNC_DIO: asynchronous direct I/O submission + * FUSE_WRITEBACK_CACHE: use writeback cache for buffered writes + * FUSE_NO_OPEN_SUPPORT: kernel supports zero-message opens + * FUSE_PARALLEL_DIROPS: allow parallel lookups and readdir + * FUSE_HANDLE_KILLPRIV: fs handles killing suid/sgid/cap on write/chown/trunc + * FUSE_POSIX_ACL: filesystem supports posix acls + * FUSE_ABORT_ERROR: reading the device after abort returns ECONNABORTED + * FUSE_MAX_PAGES: init_out.max_pages contains the max number of req pages + * FUSE_CACHE_SYMLINKS: cache READLINK responses + * FUSE_NO_OPENDIR_SUPPORT: kernel supports zero-message opendir + * FUSE_EXPLICIT_INVAL_DATA: only invalidate cached pages on explicit request + * FUSE_MAP_ALIGNMENT: init_out.map_alignment contains log2(byte alignment) for + * foffset and moffset fields in struct + * fuse_setupmapping_out and fuse_removemapping_one. + * FUSE_SUBMOUNTS: kernel supports auto-mounting directory submounts + * FUSE_HANDLE_KILLPRIV_V2: fs kills suid/sgid/cap on write/chown/trunc. + * Upon write/truncate suid/sgid is only killed if caller + * does not have CAP_FSETID. Additionally upon + * write/truncate sgid is killed only if file has group + * execute permission. (Same as Linux VFS behavior). + * FUSE_SETXATTR_EXT: Server supports extended struct fuse_setxattr_in + * FUSE_INIT_EXT: extended fuse_init_in request + * FUSE_INIT_RESERVED: reserved, do not use + * FUSE_SECURITY_CTX: add security context to create, mkdir, symlink, and + * mknod + * FUSE_HAS_INODE_DAX: use per inode DAX + * FUSE_CREATE_SUPP_GROUP: add supplementary group info to create, mkdir, + * symlink and mknod (single group that matches parent) + * FUSE_HAS_EXPIRE_ONLY: kernel supports expiry-only entry invalidation + * FUSE_DIRECT_IO_ALLOW_MMAP: allow shared mmap in FOPEN_DIRECT_IO mode. + * FUSE_NO_EXPORT_SUPPORT: explicitly disable export support + * FUSE_HAS_RESEND: kernel supports resending pending requests, and the high bit + * of the request ID indicates resend requests + * FUSE_ALLOW_IDMAP: allow creation of idmapped mounts + * FUSE_OVER_IO_URING: Indicate that client supports io-uring + * FUSE_REQUEST_TIMEOUT: kernel supports timing out requests. + * init_out.request_timeout contains the timeout (in secs) + */ +#define FUSE_ASYNC_READ (1 << 0) +#define FUSE_POSIX_LOCKS (1 << 1) +#define FUSE_FILE_OPS (1 << 2) +#define FUSE_ATOMIC_O_TRUNC (1 << 3) +#define FUSE_EXPORT_SUPPORT (1 << 4) +#define FUSE_BIG_WRITES (1 << 5) +#define FUSE_DONT_MASK (1 << 6) +#define FUSE_SPLICE_WRITE (1 << 7) +#define FUSE_SPLICE_MOVE (1 << 8) +#define FUSE_SPLICE_READ (1 << 9) +#define FUSE_FLOCK_LOCKS (1 << 10) +#define FUSE_HAS_IOCTL_DIR (1 << 11) +#define FUSE_AUTO_INVAL_DATA (1 << 12) +#define FUSE_DO_READDIRPLUS (1 << 13) +#define FUSE_READDIRPLUS_AUTO (1 << 14) +#define FUSE_ASYNC_DIO (1 << 15) +#define FUSE_WRITEBACK_CACHE (1 << 16) +#define FUSE_NO_OPEN_SUPPORT (1 << 17) +#define FUSE_PARALLEL_DIROPS (1 << 18) +#define FUSE_HANDLE_KILLPRIV (1 << 19) +#define FUSE_POSIX_ACL (1 << 20) +#define FUSE_ABORT_ERROR (1 << 21) +#define FUSE_MAX_PAGES (1 << 22) +#define FUSE_CACHE_SYMLINKS (1 << 23) +#define FUSE_NO_OPENDIR_SUPPORT (1 << 24) +#define FUSE_EXPLICIT_INVAL_DATA (1 << 25) +#define FUSE_MAP_ALIGNMENT (1 << 26) +#define FUSE_SUBMOUNTS (1 << 27) +#define FUSE_HANDLE_KILLPRIV_V2 (1 << 28) +#define FUSE_SETXATTR_EXT (1 << 29) +#define FUSE_INIT_EXT (1 << 30) +#define FUSE_INIT_RESERVED (1 << 31) +/* bits 32..63 get shifted down 32 bits into the flags2 field */ +#define FUSE_SECURITY_CTX (1ULL << 32) +#define FUSE_HAS_INODE_DAX (1ULL << 33) +#define FUSE_CREATE_SUPP_GROUP (1ULL << 34) +#define FUSE_HAS_EXPIRE_ONLY (1ULL << 35) +#define FUSE_DIRECT_IO_ALLOW_MMAP (1ULL << 36) +#define FUSE_PASSTHROUGH (1ULL << 37) +#define FUSE_NO_EXPORT_SUPPORT (1ULL << 38) +#define FUSE_HAS_RESEND (1ULL << 39) +/* Obsolete alias for FUSE_DIRECT_IO_ALLOW_MMAP */ +#define FUSE_DIRECT_IO_RELAX FUSE_DIRECT_IO_ALLOW_MMAP +#define FUSE_ALLOW_IDMAP (1ULL << 40) +#define FUSE_OVER_IO_URING (1ULL << 41) +#define FUSE_REQUEST_TIMEOUT (1ULL << 42) + +/** + * CUSE INIT request/reply flags + * + * CUSE_UNRESTRICTED_IOCTL: use unrestricted ioctl + */ +#define CUSE_UNRESTRICTED_IOCTL (1 << 0) + +/** + * Release flags + */ +#define FUSE_RELEASE_FLUSH (1 << 0) +#define FUSE_RELEASE_FLOCK_UNLOCK (1 << 1) + +/** + * Getattr flags + */ +#define FUSE_GETATTR_FH (1 << 0) + +/** + * Lock flags + */ +#define FUSE_LK_FLOCK (1 << 0) + +/** + * WRITE flags + * + * FUSE_WRITE_CACHE: delayed write from page cache, file handle is guessed + * FUSE_WRITE_LOCKOWNER: lock_owner field is valid + * FUSE_WRITE_KILL_SUIDGID: kill suid and sgid bits + */ +#define FUSE_WRITE_CACHE (1 << 0) +#define FUSE_WRITE_LOCKOWNER (1 << 1) +#define FUSE_WRITE_KILL_SUIDGID (1 << 2) + +/* Obsolete alias; this flag implies killing suid/sgid only. */ +#define FUSE_WRITE_KILL_PRIV FUSE_WRITE_KILL_SUIDGID + +/** + * Read flags + */ +#define FUSE_READ_LOCKOWNER (1 << 1) + +/** + * Ioctl flags + * + * FUSE_IOCTL_COMPAT: 32bit compat ioctl on 64bit machine + * FUSE_IOCTL_UNRESTRICTED: not restricted to well-formed ioctls, retry allowed + * FUSE_IOCTL_RETRY: retry with new iovecs + * FUSE_IOCTL_32BIT: 32bit ioctl + * FUSE_IOCTL_DIR: is a directory + * FUSE_IOCTL_COMPAT_X32: x32 compat ioctl on 64bit machine (64bit time_t) + * + * FUSE_IOCTL_MAX_IOV: maximum of in_iovecs + out_iovecs + */ +#define FUSE_IOCTL_COMPAT (1 << 0) +#define FUSE_IOCTL_UNRESTRICTED (1 << 1) +#define FUSE_IOCTL_RETRY (1 << 2) +#define FUSE_IOCTL_32BIT (1 << 3) +#define FUSE_IOCTL_DIR (1 << 4) +#define FUSE_IOCTL_COMPAT_X32 (1 << 5) + +#define FUSE_IOCTL_MAX_IOV 256 + +/** + * Poll flags + * + * FUSE_POLL_SCHEDULE_NOTIFY: request poll notify + */ +#define FUSE_POLL_SCHEDULE_NOTIFY (1 << 0) + +/** + * Fsync flags + * + * FUSE_FSYNC_FDATASYNC: Sync data only, not metadata + */ +#define FUSE_FSYNC_FDATASYNC (1 << 0) + +/** + * fuse_attr flags + * + * FUSE_ATTR_SUBMOUNT: Object is a submount root + * FUSE_ATTR_DAX: Enable DAX for this file in per inode DAX mode + */ +#define FUSE_ATTR_SUBMOUNT (1 << 0) +#define FUSE_ATTR_DAX (1 << 1) + +/** + * Open flags + * FUSE_OPEN_KILL_SUIDGID: Kill suid and sgid if executable + */ +#define FUSE_OPEN_KILL_SUIDGID (1 << 0) + +/** + * setxattr flags + * FUSE_SETXATTR_ACL_KILL_SGID: Clear SGID when system.posix_acl_access is set + */ +#define FUSE_SETXATTR_ACL_KILL_SGID (1 << 0) + +/** + * notify_inval_entry flags + * FUSE_EXPIRE_ONLY + */ +#define FUSE_EXPIRE_ONLY (1 << 0) + +/** + * extension type + * FUSE_MAX_NR_SECCTX: maximum value of &fuse_secctx_header.nr_secctx + * FUSE_EXT_GROUPS: &fuse_supp_groups extension + */ +enum fuse_ext_type { + /* Types 0..31 are reserved for fuse_secctx_header */ + FUSE_MAX_NR_SECCTX = 31, + FUSE_EXT_GROUPS = 32, +}; + +enum fuse_opcode { + FUSE_LOOKUP = 1, + FUSE_FORGET = 2, /* no reply */ + FUSE_GETATTR = 3, + FUSE_SETATTR = 4, + FUSE_READLINK = 5, + FUSE_SYMLINK = 6, + FUSE_MKNOD = 8, + FUSE_MKDIR = 9, + FUSE_UNLINK = 10, + FUSE_RMDIR = 11, + FUSE_RENAME = 12, + FUSE_LINK = 13, + FUSE_OPEN = 14, + FUSE_READ = 15, + FUSE_WRITE = 16, + FUSE_STATFS = 17, + FUSE_RELEASE = 18, + FUSE_FSYNC = 20, + FUSE_SETXATTR = 21, + FUSE_GETXATTR = 22, + FUSE_LISTXATTR = 23, + FUSE_REMOVEXATTR = 24, + FUSE_FLUSH = 25, + FUSE_INIT = 26, + FUSE_OPENDIR = 27, + FUSE_READDIR = 28, + FUSE_RELEASEDIR = 29, + FUSE_FSYNCDIR = 30, + FUSE_GETLK = 31, + FUSE_SETLK = 32, + FUSE_SETLKW = 33, + FUSE_ACCESS = 34, + FUSE_CREATE = 35, + FUSE_INTERRUPT = 36, + FUSE_BMAP = 37, + FUSE_DESTROY = 38, + FUSE_IOCTL = 39, + FUSE_POLL = 40, + FUSE_NOTIFY_REPLY = 41, + FUSE_BATCH_FORGET = 42, + FUSE_FALLOCATE = 43, + FUSE_READDIRPLUS = 44, + FUSE_RENAME2 = 45, + FUSE_LSEEK = 46, + FUSE_COPY_FILE_RANGE = 47, + FUSE_SETUPMAPPING = 48, + FUSE_REMOVEMAPPING = 49, + FUSE_SYNCFS = 50, + FUSE_TMPFILE = 51, + FUSE_STATX = 52, + + /* CUSE specific operations */ + CUSE_INIT = 4096, + + /* Reserved opcodes: helpful to detect structure endian-ness */ + CUSE_INIT_BSWAP_RESERVED = 1048576, /* CUSE_INIT << 8 */ + FUSE_INIT_BSWAP_RESERVED = 436207616, /* FUSE_INIT << 24 */ +}; + +enum fuse_notify_code { + FUSE_NOTIFY_POLL = 1, + FUSE_NOTIFY_INVAL_INODE = 2, + FUSE_NOTIFY_INVAL_ENTRY = 3, + FUSE_NOTIFY_STORE = 4, + FUSE_NOTIFY_RETRIEVE = 5, + FUSE_NOTIFY_DELETE = 6, + FUSE_NOTIFY_RESEND = 7, + FUSE_NOTIFY_CODE_MAX, +}; + +/* The read buffer is required to be at least 8k, but may be much larger */ +#define FUSE_MIN_READ_BUFFER 8192 + +#define FUSE_COMPAT_ENTRY_OUT_SIZE 120 + +struct fuse_entry_out { + uint64_t nodeid; /* Inode ID */ + uint64_t generation; /* Inode generation: nodeid:gen must + be unique for the fs's lifetime */ + uint64_t entry_valid; /* Cache timeout for the name */ + uint64_t attr_valid; /* Cache timeout for the attributes */ + uint32_t entry_valid_nsec; + uint32_t attr_valid_nsec; + struct fuse_attr attr; +}; + +struct fuse_forget_in { + uint64_t nlookup; +}; + +struct fuse_forget_one { + uint64_t nodeid; + uint64_t nlookup; +}; + +struct fuse_batch_forget_in { + uint32_t count; + uint32_t dummy; +}; + +struct fuse_getattr_in { + uint32_t getattr_flags; + uint32_t dummy; + uint64_t fh; +}; + +#define FUSE_COMPAT_ATTR_OUT_SIZE 96 + +struct fuse_attr_out { + uint64_t attr_valid; /* Cache timeout for the attributes */ + uint32_t attr_valid_nsec; + uint32_t dummy; + struct fuse_attr attr; +}; + +struct fuse_statx_in { + uint32_t getattr_flags; + uint32_t reserved; + uint64_t fh; + uint32_t sx_flags; + uint32_t sx_mask; +}; + +struct fuse_statx_out { + uint64_t attr_valid; /* Cache timeout for the attributes */ + uint32_t attr_valid_nsec; + uint32_t flags; + uint64_t spare[2]; + struct fuse_statx stat; +}; + +#define FUSE_COMPAT_MKNOD_IN_SIZE 8 + +struct fuse_mknod_in { + uint32_t mode; + uint32_t rdev; + uint32_t umask; + uint32_t padding; +}; + +struct fuse_mkdir_in { + uint32_t mode; + uint32_t umask; +}; + +struct fuse_rename_in { + uint64_t newdir; +}; + +struct fuse_rename2_in { + uint64_t newdir; + uint32_t flags; + uint32_t padding; +}; + +struct fuse_link_in { + uint64_t oldnodeid; +}; + +struct fuse_setattr_in { + uint32_t valid; + uint32_t padding; + uint64_t fh; + uint64_t size; + uint64_t lock_owner; + uint64_t atime; + uint64_t mtime; + uint64_t ctime; + uint32_t atimensec; + uint32_t mtimensec; + uint32_t ctimensec; + uint32_t mode; + uint32_t unused4; + uint32_t uid; + uint32_t gid; + uint32_t unused5; +}; + +struct fuse_open_in { + uint32_t flags; + uint32_t open_flags; /* FUSE_OPEN_... */ +}; + +struct fuse_create_in { + uint32_t flags; + uint32_t mode; + uint32_t umask; + uint32_t open_flags; /* FUSE_OPEN_... */ +}; + +struct fuse_open_out { + uint64_t fh; + uint32_t open_flags; + int32_t backing_id; +}; + +struct fuse_release_in { + uint64_t fh; + uint32_t flags; + uint32_t release_flags; + uint64_t lock_owner; +}; + +struct fuse_flush_in { + uint64_t fh; + uint32_t unused; + uint32_t padding; + uint64_t lock_owner; +}; + +struct fuse_read_in { + uint64_t fh; + uint64_t offset; + uint32_t size; + uint32_t read_flags; + uint64_t lock_owner; + uint32_t flags; + uint32_t padding; +}; + +#define FUSE_COMPAT_WRITE_IN_SIZE 24 + +struct fuse_write_in { + uint64_t fh; + uint64_t offset; + uint32_t size; + uint32_t write_flags; + uint64_t lock_owner; + uint32_t flags; + uint32_t padding; +}; + +struct fuse_write_out { + uint32_t size; + uint32_t padding; +}; + +#define FUSE_COMPAT_STATFS_SIZE 48 + +struct fuse_statfs_out { + struct fuse_kstatfs st; +}; + +struct fuse_fsync_in { + uint64_t fh; + uint32_t fsync_flags; + uint32_t padding; +}; + +#define FUSE_COMPAT_SETXATTR_IN_SIZE 8 + +struct fuse_setxattr_in { + uint32_t size; + uint32_t flags; + uint32_t setxattr_flags; + uint32_t padding; +}; + +struct fuse_getxattr_in { + uint32_t size; + uint32_t padding; +}; + +struct fuse_getxattr_out { + uint32_t size; + uint32_t padding; +}; + +struct fuse_lk_in { + uint64_t fh; + uint64_t owner; + struct fuse_file_lock lk; + uint32_t lk_flags; + uint32_t padding; +}; + +struct fuse_lk_out { + struct fuse_file_lock lk; +}; + +struct fuse_access_in { + uint32_t mask; + uint32_t padding; +}; + +struct fuse_init_in { + uint32_t major; + uint32_t minor; + uint32_t max_readahead; + uint32_t flags; + uint32_t flags2; + uint32_t unused[11]; +}; + +#define FUSE_COMPAT_INIT_OUT_SIZE 8 +#define FUSE_COMPAT_22_INIT_OUT_SIZE 24 + +struct fuse_init_out { + uint32_t major; + uint32_t minor; + uint32_t max_readahead; + uint32_t flags; + uint16_t max_background; + uint16_t congestion_threshold; + uint32_t max_write; + uint32_t time_gran; + uint16_t max_pages; + uint16_t map_alignment; + uint32_t flags2; + uint32_t max_stack_depth; + uint16_t request_timeout; + uint16_t unused[11]; +}; + +#define CUSE_INIT_INFO_MAX 4096 + +struct cuse_init_in { + uint32_t major; + uint32_t minor; + uint32_t unused; + uint32_t flags; +}; + +struct cuse_init_out { + uint32_t major; + uint32_t minor; + uint32_t unused; + uint32_t flags; + uint32_t max_read; + uint32_t max_write; + uint32_t dev_major; /* chardev major */ + uint32_t dev_minor; /* chardev minor */ + uint32_t spare[10]; +}; + +struct fuse_interrupt_in { + uint64_t unique; +}; + +struct fuse_bmap_in { + uint64_t block; + uint32_t blocksize; + uint32_t padding; +}; + +struct fuse_bmap_out { + uint64_t block; +}; + +struct fuse_ioctl_in { + uint64_t fh; + uint32_t flags; + uint32_t cmd; + uint64_t arg; + uint32_t in_size; + uint32_t out_size; +}; + +struct fuse_ioctl_iovec { + uint64_t base; + uint64_t len; +}; + +struct fuse_ioctl_out { + int32_t result; + uint32_t flags; + uint32_t in_iovs; + uint32_t out_iovs; +}; + +struct fuse_poll_in { + uint64_t fh; + uint64_t kh; + uint32_t flags; + uint32_t events; +}; + +struct fuse_poll_out { + uint32_t revents; + uint32_t padding; +}; + +struct fuse_notify_poll_wakeup_out { + uint64_t kh; +}; + +struct fuse_fallocate_in { + uint64_t fh; + uint64_t offset; + uint64_t length; + uint32_t mode; + uint32_t padding; +}; + +/** + * FUSE request unique ID flag + * + * Indicates whether this is a resend request. The receiver should handle this + * request accordingly. + */ +#define FUSE_UNIQUE_RESEND (1ULL << 63) + +/** + * This value will be set by the kernel to + * (struct fuse_in_header).{uid,gid} fields in + * case when: + * - fuse daemon enabled FUSE_ALLOW_IDMAP + * - idmapping information is not available and uid/gid + * can not be mapped in accordance with an idmapping. + * + * Note: an idmapping information always available + * for inode creation operations like: + * FUSE_MKNOD, FUSE_SYMLINK, FUSE_MKDIR, FUSE_TMPFILE, + * FUSE_CREATE and FUSE_RENAME2 (with RENAME_WHITEOUT). + */ +#define FUSE_INVALID_UIDGID ((uint32_t)(-1)) + +struct fuse_in_header { + uint32_t len; + uint32_t opcode; + uint64_t unique; + uint64_t nodeid; + uint32_t uid; + uint32_t gid; + uint32_t pid; + uint16_t total_extlen; /* length of extensions in 8byte units */ + uint16_t padding; +}; + +struct fuse_out_header { + uint32_t len; + int32_t error; + uint64_t unique; +}; + +struct fuse_dirent { + uint64_t ino; + uint64_t off; + uint32_t namelen; + uint32_t type; + char name[]; +}; + +/* Align variable length records to 64bit boundary */ +#define FUSE_REC_ALIGN(x) \ + (((x) + sizeof(uint64_t) - 1) & ~(sizeof(uint64_t) - 1)) + +#define FUSE_NAME_OFFSET offsetof(struct fuse_dirent, name) +#define FUSE_DIRENT_ALIGN(x) FUSE_REC_ALIGN(x) +#define FUSE_DIRENT_SIZE(d) \ + FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen) + +struct fuse_direntplus { + struct fuse_entry_out entry_out; + struct fuse_dirent dirent; +}; + +#define FUSE_NAME_OFFSET_DIRENTPLUS \ + offsetof(struct fuse_direntplus, dirent.name) +#define FUSE_DIRENTPLUS_SIZE(d) \ + FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET_DIRENTPLUS + (d)->dirent.namelen) + +struct fuse_notify_inval_inode_out { + uint64_t ino; + int64_t off; + int64_t len; +}; + +struct fuse_notify_inval_entry_out { + uint64_t parent; + uint32_t namelen; + uint32_t flags; +}; + +struct fuse_notify_delete_out { + uint64_t parent; + uint64_t child; + uint32_t namelen; + uint32_t padding; +}; + +struct fuse_notify_store_out { + uint64_t nodeid; + uint64_t offset; + uint32_t size; + uint32_t padding; +}; + +struct fuse_notify_retrieve_out { + uint64_t notify_unique; + uint64_t nodeid; + uint64_t offset; + uint32_t size; + uint32_t padding; +}; + +/* Matches the size of fuse_write_in */ +struct fuse_notify_retrieve_in { + uint64_t dummy1; + uint64_t offset; + uint32_t size; + uint32_t dummy2; + uint64_t dummy3; + uint64_t dummy4; +}; + +struct fuse_backing_map { + int32_t fd; + uint32_t flags; + uint64_t padding; +}; + +/* Device ioctls: */ +#define FUSE_DEV_IOC_MAGIC 229 +#define FUSE_DEV_IOC_CLONE _IOR(FUSE_DEV_IOC_MAGIC, 0, uint32_t) +#define FUSE_DEV_IOC_BACKING_OPEN _IOW(FUSE_DEV_IOC_MAGIC, 1, \ + struct fuse_backing_map) +#define FUSE_DEV_IOC_BACKING_CLOSE _IOW(FUSE_DEV_IOC_MAGIC, 2, uint32_t) + +struct fuse_lseek_in { + uint64_t fh; + uint64_t offset; + uint32_t whence; + uint32_t padding; +}; + +struct fuse_lseek_out { + uint64_t offset; +}; + +struct fuse_copy_file_range_in { + uint64_t fh_in; + uint64_t off_in; + uint64_t nodeid_out; + uint64_t fh_out; + uint64_t off_out; + uint64_t len; + uint64_t flags; +}; + +#define FUSE_SETUPMAPPING_FLAG_WRITE (1ull << 0) +#define FUSE_SETUPMAPPING_FLAG_READ (1ull << 1) +struct fuse_setupmapping_in { + /* An already open handle */ + uint64_t fh; + /* Offset into the file to start the mapping */ + uint64_t foffset; + /* Length of mapping required */ + uint64_t len; + /* Flags, FUSE_SETUPMAPPING_FLAG_* */ + uint64_t flags; + /* Offset in Memory Window */ + uint64_t moffset; +}; + +struct fuse_removemapping_in { + /* number of fuse_removemapping_one follows */ + uint32_t count; +}; + +struct fuse_removemapping_one { + /* Offset into the dax window start the unmapping */ + uint64_t moffset; + /* Length of mapping required */ + uint64_t len; +}; + +#define FUSE_REMOVEMAPPING_MAX_ENTRY \ + (PAGE_SIZE / sizeof(struct fuse_removemapping_one)) + +struct fuse_syncfs_in { + uint64_t padding; +}; + +/* + * For each security context, send fuse_secctx with size of security context + * fuse_secctx will be followed by security context name and this in turn + * will be followed by actual context label. + * fuse_secctx, name, context + */ +struct fuse_secctx { + uint32_t size; + uint32_t padding; +}; + +/* + * Contains the information about how many fuse_secctx structures are being + * sent and what's the total size of all security contexts (including + * size of fuse_secctx_header). + * + */ +struct fuse_secctx_header { + uint32_t size; + uint32_t nr_secctx; +}; + +/** + * struct fuse_ext_header - extension header + * @size: total size of this extension including this header + * @type: type of extension + * + * This is made compatible with fuse_secctx_header by using type values > + * FUSE_MAX_NR_SECCTX + */ +struct fuse_ext_header { + uint32_t size; + uint32_t type; +}; + +/** + * struct fuse_supp_groups - Supplementary group extension + * @nr_groups: number of supplementary groups + * @groups: flexible array of group IDs + */ +struct fuse_supp_groups { + uint32_t nr_groups; + uint32_t groups[]; +}; + +/** + * Size of the ring buffer header + */ +#define FUSE_URING_IN_OUT_HEADER_SZ 128 +#define FUSE_URING_OP_IN_OUT_SZ 128 + +/* Used as part of the fuse_uring_req_header */ +struct fuse_uring_ent_in_out { + uint64_t flags; + + /* + * commit ID to be used in a reply to a ring request (see also + * struct fuse_uring_cmd_req) + */ + uint64_t commit_id; + + /* size of user payload buffer */ + uint32_t payload_sz; + uint32_t padding; + + uint64_t reserved; +}; + +/** + * Header for all fuse-io-uring requests + */ +struct fuse_uring_req_header { + /* struct fuse_in_header / struct fuse_out_header */ + char in_out[FUSE_URING_IN_OUT_HEADER_SZ]; + + /* per op code header */ + char op_in[FUSE_URING_OP_IN_OUT_SZ]; + + struct fuse_uring_ent_in_out ring_ent_in_out; +}; + +/** + * sqe commands to the kernel + */ +enum fuse_uring_cmd { + FUSE_IO_URING_CMD_INVALID = 0, + + /* register the request buffer and fetch a fuse request */ + FUSE_IO_URING_CMD_REGISTER = 1, + + /* commit fuse request result and fetch next request */ + FUSE_IO_URING_CMD_COMMIT_AND_FETCH = 2, +}; + +/** + * In the 80B command area of the SQE. + */ +struct fuse_uring_cmd_req { + uint64_t flags; + + /* entry identifier for commits */ + uint64_t commit_id; + + /* queue the command is for (queue index) */ + uint16_t qid; + uint8_t padding[6]; +}; + +#endif /* _LINUX_FUSE_H */ -- 2.43.0

From: Simon Glass <sjg@chromium.org> At present virtio devices are acknowleged when bound. This results in a call to virtio_set_status() which uses device-private data in several cases, e.g. in virtio_pci_get_status() This data is only allocated when the device is probed, so the access is either ignored (on QEMU) or results in a crash (on sandbox). Acknowledging the device so early does not seem to be necessary. Move it to the pre-probe function instead. If we later find it necessary to acknowledge the device while binding, we can update the uclass and drivers to use plat data instead of priv. Signed-off-by: Simon Glass <sjg@chromium.org> --- drivers/virtio/virtio-uclass.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/drivers/virtio/virtio-uclass.c b/drivers/virtio/virtio-uclass.c index 17ef47df030..19728fdbe89 100644 --- a/drivers/virtio/virtio-uclass.c +++ b/drivers/virtio/virtio-uclass.c @@ -257,14 +257,6 @@ static int virtio_uclass_post_probe(struct udevice *udev) return 0; } -static int virtio_uclass_child_post_bind(struct udevice *vdev) -{ - /* Acknowledge that we've seen the device */ - virtio_add_status(vdev, VIRTIO_CONFIG_S_ACKNOWLEDGE); - - return 0; -} - static int virtio_uclass_child_pre_probe(struct udevice *vdev) { struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(vdev->parent); @@ -278,6 +270,9 @@ static int virtio_uclass_child_pre_probe(struct udevice *vdev) if (device_get_uclass_id(vdev) == UCLASS_BOOTDEV) return 0; + /* Acknowledge that we've seen the device */ + virtio_add_status(vdev, VIRTIO_CONFIG_S_ACKNOWLEDGE); + /* * Save the real virtio device (eg: virtio-net, virtio-blk) to * the transport (parent) device's uclass priv for future use. @@ -391,7 +386,6 @@ UCLASS_DRIVER(virtio) = { .flags = DM_UC_FLAG_SEQ_ALIAS, .pre_probe = virtio_uclass_pre_probe, .post_probe = virtio_uclass_post_probe, - .child_post_bind = virtio_uclass_child_post_bind, .child_pre_probe = virtio_uclass_child_pre_probe, .child_post_probe = virtio_uclass_child_post_probe, .per_device_auto = sizeof(struct virtio_dev_priv), -- 2.43.0

From: Simon Glass <sjg@chromium.org> With LSP it is confusing to have two functions named the same in different files. Rename a few of the legacy ones. Signed-off-by: Simon Glass <sjg@chromium.org> --- drivers/virtio/virtio_pci_legacy.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/virtio/virtio_pci_legacy.c b/drivers/virtio/virtio_pci_legacy.c index 15f8c6e7d25..fe3e3b475ea 100644 --- a/drivers/virtio/virtio_pci_legacy.c +++ b/drivers/virtio/virtio_pci_legacy.c @@ -277,7 +277,7 @@ static int virtio_pci_notify(struct udevice *udev, struct virtqueue *vq) return 0; } -static int virtio_pci_bind(struct udevice *udev) +static int virtio_pci_bind_legacy(struct udevice *udev) { static unsigned int num_devs; char name[20]; @@ -289,7 +289,7 @@ static int virtio_pci_bind(struct udevice *udev) return 0; } -static int virtio_pci_probe(struct udevice *udev) +static int virtio_pci_probe_legacy(struct udevice *udev) { struct pci_child_plat *pplat = dev_get_parent_plat(udev); struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev); @@ -348,8 +348,8 @@ U_BOOT_DRIVER(virtio_pci_legacy) = { .name = VIRTIO_PCI_DRV_NAME, .id = UCLASS_VIRTIO, .ops = &virtio_pci_ops, - .bind = virtio_pci_bind, - .probe = virtio_pci_probe, + .bind = virtio_pci_bind_legacy, + .probe = virtio_pci_probe_legacy, .priv_auto = sizeof(struct virtio_pci_priv), }; -- 2.43.0

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

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

From: Simon Glass <sjg@chromium.org> Add an implementation of virtio-fs files, including looking them up and reading them. Signed-off-by: Simon Glass <sjg@chromium.org> --- drivers/virtio/Makefile | 2 +- drivers/virtio/fs_dir.c | 17 +++++ drivers/virtio/fs_file.c | 136 +++++++++++++++++++++++++++++++++++ drivers/virtio/fs_internal.h | 16 +++++ 4 files changed, 170 insertions(+), 1 deletion(-) create mode 100644 drivers/virtio/fs_file.c diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile index 3e985f7a5e7..37b805a9c24 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 +obj-$(CONFIG_VIRTIO_FS) += fs.o fs_dir.o fs_file.o diff --git a/drivers/virtio/fs_dir.c b/drivers/virtio/fs_dir.c index 1bc536f56bf..b346ded8a09 100644 --- a/drivers/virtio/fs_dir.c +++ b/drivers/virtio/fs_dir.c @@ -105,6 +105,22 @@ static int virtio_fs_dir_close(struct udevice *dev, struct fs_dir_stream *strm) return 0; } +static int open_file(struct udevice *dev, const char *leaf, + enum dir_open_flags_t oflags, struct udevice **filp) +{ + struct udevice *fil; + int ret; + + log_debug("start '%s'\n", leaf); + ret = virtio_fs_setup_file(dev, leaf, oflags, &fil); + log_debug("ret %d\n", ret); + if (ret) + return log_msg_ret("dof", ret); + *filp = fil; + + return 0; +} + static int virtio_fs_dir_remove(struct udevice *dev) { struct virtio_fs_dir_priv *dir_priv = dev_get_priv(dev); @@ -124,6 +140,7 @@ static struct dir_ops virtio_fs_dir_ops = { .open = virtio_fs_dir_open, .read = virtio_fs_dir_read, .close = virtio_fs_dir_close, + .open_file = open_file, }; static const struct udevice_id dir_ids[] = { diff --git a/drivers/virtio/fs_file.c b/drivers/virtio/fs_file.c new file mode 100644 index 00000000000..adb428fcd3e --- /dev/null +++ b/drivers/virtio/fs_file.c @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * U-Boot Virtio-FS files + * + * Copyright 2025 Simon Glass <sjg@chromium.org> + * + * Supports access to files in virtio-fs + */ + +#define LOG_CATEGORY UCLASS_VIRTIO + +#include <dm.h> +#include <file.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" + +/** + * struct file_priv - Information about a virtio file + * + * @flags: Open-mode flags + * @backing_id: Backing ID for the file + * @fh: Unique filehandle for the file + * @size: Size of the file + */ +struct file_priv { + u64 nodeid; + enum dir_open_flags_t flags; + u64 fh; +}; + +static ssize_t virtio_fs_read_iter(struct udevice *dev, struct iov_iter *iter, + loff_t pos) +{ + struct file_priv *priv = dev_get_priv(dev); + struct udevice *dir = dev_get_parent(dev); + struct udevice *fsdev = dev_get_parent(dir); + ssize_t ret; + + log_debug("start dev '%s' len %zx\n", dev->name, iter->count); + ret = virtio_fs_read(fsdev, priv->nodeid, priv->fh, pos, + iter_iov_ptr(iter), iter_iov_avail(iter)); + if (ret < 0) + return log_msg_ret("vfr", ret); + iter_advance(iter, ret); + log_debug("read %zx bytes\n", ret); + + return ret; +} + +static int virtio_fs_file_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 file_ops virtio_fs_file_ops = { + .read_iter = virtio_fs_read_iter, +}; + +static const struct udevice_id file_ids[] = { + { .compatible = "virtio-fs,file" }, + { } +}; + +U_BOOT_DRIVER(virtio_fs_file) = { + .name = "virtio_fs_file", + .id = UCLASS_FILE, + .of_match = file_ids, + .remove = virtio_fs_file_remove, + .ops = &virtio_fs_file_ops, + .priv_auto = sizeof(struct file_priv), + .flags = DM_FLAG_ACTIVE_DMA, +}; + +int virtio_fs_setup_file(struct udevice *dir, const char *leaf, + enum dir_open_flags_t oflags, struct udevice **devp) +{ + struct udevice *fil, *fsdev = dev_get_parent(dir); + struct virtio_fs_dir_priv *dir_priv = dev_get_priv(dir); + struct file_uc_priv *file_uc_priv; + struct file_priv *file_priv; + struct fuse_entry_out out; + uint flags; + u64 fh; + int ret; + + log_debug("dir '%s' inode %llx leaf '%s' oflags %d\n", dir->name, + dir_priv->inode, leaf, oflags); + + ret = virtio_fs_lookup_(fsdev, dir_priv->inode, leaf, &out); + if (ret) { + log_debug("lookup fail ret=%d\n", ret); + return log_msg_ret("vfl", ret); + } + + log_debug("open nodeid %lld\n", out.nodeid); + ret = virtio_fs_open_file(fsdev, out.nodeid, oflags, &fh, &flags); + if (ret) { + log_debug("fail ret=%d\n", ret); + return log_msg_ret("vfo", ret); + } + log_debug("result fh %llx flags %x\n", fh, flags); + + ret = file_add_probe(dir, DM_DRIVER_REF(virtio_fs_file), leaf, + out.attr.size, flags, &fil); + if (ret) { + /* TODO: close file? */ + return log_msg_ret("vfp", ret); + } + + file_priv = dev_get_priv(fil); + file_priv->nodeid = out.nodeid; + file_priv->fh = fh; + file_priv->flags = flags; + file_uc_priv = dev_get_uclass_priv(fil); + + log_debug("opened file dev '%s' inode %lld size %zx\n", fil->name, + file_priv->nodeid, file_uc_priv->size); + *devp = fil; + + return 0; +} diff --git a/drivers/virtio/fs_internal.h b/drivers/virtio/fs_internal.h index c71c7ca48a7..460a79d9854 100644 --- a/drivers/virtio/fs_internal.h +++ b/drivers/virtio/fs_internal.h @@ -162,4 +162,20 @@ long virtio_fs_read(struct udevice *dev, u64 nodeid, u64 fh, u64 offset, int virtio_fs_setup_dir(struct udevice *fsdev, const char *path, struct udevice **devp); +/** + * virtio_fs_setup_file() - Look up and open a file, creating a new device + * + * Sets up a new open file: performs a lookup for the file within a given + * directory, opens it via FUSE, then probes and adds a new 'file' device to + * represent the opened file + * + * @dir: The directory device in which to look for the file + * @leaf: The name of the file to open (the leaf name) + * @oflags: Open flags to use when opening the file + * @devp: On success, returns a pointer to the newly created file device + * Return: 0 on success, -ve on error + */ +int virtio_fs_setup_file(struct udevice *dir, const char *leaf, + enum dir_open_flags_t flags, struct udevice **devp); + #endif -- 2.43.0

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
participants (1)
-
sjg@u-boot.org