[PATCH 1/2] virtio-fs: add fuse protocol header

30 views
Skip to first unread message

Waldemar Kozaczuk

unread,
Feb 8, 2020, 6:22:07 PM2/8/20
to osv...@googlegroups.com, Waldemar Kozaczuk
This file is a copy of https://github.com/libfuse/libfuse/blob/38c9cb43787bf83ca4e9c9af707e82165de99008/include/fuse_kernel.h
and provides the wire-level interface of fuse protocol used to exchange
data between guest and host over virtio-fs device.

Signed-off-by: Waldemar Kozaczuk <jwkoz...@gmail.com>
---
fs/virtiofs/fuse_kernel.h | 848 ++++++++++++++++++++++++++++++++++++++
1 file changed, 848 insertions(+)
create mode 100644 fs/virtiofs/fuse_kernel.h

diff --git a/fs/virtiofs/fuse_kernel.h b/fs/virtiofs/fuse_kernel.h
new file mode 100644
index 00000000..018a00a2
--- /dev/null
+++ b/fs/virtiofs/fuse_kernel.h
@@ -0,0 +1,848 @@
+/* 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 <mik...@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.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
+ */
+
+#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 31
+
+/** 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 padding;
+};
+
+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)
+
+/**
+ * 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)
+ */
+#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)
+
+/**
+ * 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
+ */
+#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)
+
+/**
+ * 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_PRIV: kill suid and sgid bits
+ */
+#define FUSE_WRITE_CACHE (1 << 0)
+#define FUSE_WRITE_LOCKOWNER (1 << 1)
+#define FUSE_WRITE_KILL_PRIV (1 << 2)
+
+/**
+ * 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)
+
+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,
+
+ /* CUSE specific operations */
+ CUSE_INIT = 4096
+};
+
+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_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;
+};
+
+#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 unused;
+};
+
+struct fuse_create_in {
+ uint32_t flags;
+ uint32_t mode;
+ uint32_t umask;
+ uint32_t padding;
+};
+
+struct fuse_open_out {
+ uint64_t fh;
+ uint32_t open_flags;
+ uint32_t padding;
+};
+
+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;
+};
+
+struct fuse_setxattr_in {
+ uint32_t size;
+ uint32_t flags;
+};
+
+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;
+};
+
+#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 padding;
+ uint32_t unused[8];
+};
+
+#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;
+};
+
+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;
+ uint32_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[];
+};
+
+#define FUSE_NAME_OFFSET offsetof(struct fuse_dirent, name)
+#define FUSE_DIRENT_ALIGN(x) \
+ (((x) + sizeof(uint64_t) - 1) & ~(sizeof(uint64_t) - 1))
+#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 padding;
+};
+
+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;
+};
+
+/* Device ioctls: */
+#define FUSE_DEV_IOC_CLONE _IOR(229, 0, 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;
+};
+
+#endif /* _LINUX_FUSE_H */
--
2.20.1

Waldemar Kozaczuk

unread,
Feb 8, 2020, 6:22:11 PM2/8/20
to osv...@googlegroups.com, Waldemar Kozaczuk
This patch provides initial implementation of read-only subset
of new virtio-fs filesystem. One of the main two parts is new virtio
driver - virtio-fs - that handles interactions with virtio device
as specified in the new 1.2 virtio spec -
https://stefanha.github.io/virtio/virtio-fs.html#x1-41500011. The second
part implements VFS sublayer that provides virtio-fs filesystem in OSv and in essence
serves as a passthrough between VFS abstract layer and virtio-fs device and host filesystem.

The main motivation is to provide a way to run applications on OSv
without a need to create specific image for each app and not duplicate files
already available on host. In essence one should eventually be able to
execute an app on Linux host by reading the binaries exposed through
virtio-fs deamon. For that reason the decision to implement the read-inly
subset of the fuse interface was deliberate and we may never implement
full read-write functionality.

Please note that this initial implementation is missing readdir
functionality. Also the implementation of file read operation is
pretty naive and probably quite slow - each read requires an exit to
the host. Eventually we should optimize the latter by using some sort
of cache logic (like in ROFS) and ideally implement DAX (Direct Access) that would
provide direct guest/host mapping.

Here are the steps required to mount arbitrary directory from
host and run an app from it:

1. Get latest qemu source from master (> 4.2) and build it along with the virtiofsd
daemon the qemu source directory:
- mkdir build && cd build
- ../configure --prefix=$PWD --target-list=x86_64-softmmu
- make -j8 && make -j4 virtiofsd

2. Apply this patch to OSv tree.

3. Add following line to static/etc/fstab:
'/dev/virtiofs1 /virtiofs virtiofs defaults 0 0'

4. Add following line to usr.manifest.skel:
'/virtiofs: ../../static'

5. Build standard OSv image with native-example app and run it with this command line to make it set proper command line:
./scripts/run.py -e '/virtiofs/hello'

6. Give permissions to virtiofsd daemon to be able to execute unshare (http://man7.org/linux/man-pages/man2/unshare.2.html):
sudo setcap cap_sys_admin+ep build/virtiofsd

7. Start virtiofs daemon in another terminal:
./build/virtiofsd --socket-path=/tmp/vhostqemu -o source=<OSV_ROOT>/apps/native-example -o cache=always -d

8. Finally run OSv by manually starting qemu with proper parameters (eventually we will change run.py to support it). Please see
parameters for new device:

<QEMU_BUILD_ROOT>/build/x86_64-softmmu/qemu-system-x86_64 \
-m 4G \
-smp 4 \
-vnc :1 \
-gdb tcp::1234,server,nowait \
-device virtio-blk-pci,id=blk0,drive=hd0,scsi=off,bootindex=0 \
-drive file=<OSV_ROOT>/build/last/usr.img,if=none,id=hd0,cache=none,aio=native \
-netdev user,id=un0,net=192.168.122.0/24,host=192.168.122.1 \
-device virtio-net-pci,netdev=un0 \
-device virtio-rng-pci \
-enable-kvm \
-cpu host,+x2apic \
-chardev stdio,mux=on,id=stdio,signal=off \
-mon chardev=stdio,mode=readline \
-device isa-serial,chardev=stdio \
-chardev socket,id=char0,path=/tmp/vhostqemu \
-device vhost-user-fs-pci,queue-size=1024,chardev=char0,tag=myfs \
-object memory-backend-file,id=mem,size=4G,mem-path=/dev/shm,share=on -numa node,memdev=mem // This last line may be not necessary

For more info read here - https://virtio-fs.gitlab.io/howto-qemu.html.

For more information information about VirtioFS please read this -
https://vmsplice.net/~stefan/virtio-fs_%20A%20Shared%20File%20System%20for%20Virtual%20Machines.pdf
and https://virtio-fs.gitlab.io/index.html#faq.

Partially implements #1062

Signed-off-by: Waldemar Kozaczuk <jwkoz...@gmail.com>
---
Makefile | 4 +
arch/x64/arch-setup.cc | 2 +
drivers/virtio-fs.cc | 230 ++++++++++++++++++++++++++
drivers/virtio-fs.hh | 64 ++++++++
drivers/virtio.hh | 1 +
fs/vfs/vfs_conf.cc | 3 +
fs/virtiofs/virtiofs.hh | 40 +++++
fs/virtiofs/virtiofs_i.hh | 41 +++++
fs/virtiofs/virtiofs_vfsops.cc | 162 +++++++++++++++++++
fs/virtiofs/virtiofs_vnops.cc | 286 +++++++++++++++++++++++++++++++++
include/osv/device.h | 1 +
11 files changed, 834 insertions(+)
create mode 100644 drivers/virtio-fs.cc
create mode 100644 drivers/virtio-fs.hh
create mode 100644 fs/virtiofs/virtiofs.hh
create mode 100644 fs/virtiofs/virtiofs_i.hh
create mode 100644 fs/virtiofs/virtiofs_vfsops.cc
create mode 100644 fs/virtiofs/virtiofs_vnops.cc

diff --git a/Makefile b/Makefile
index 69260dd6..4eef246c 100644
--- a/Makefile
+++ b/Makefile
@@ -829,6 +829,7 @@ drivers += drivers/vmxnet3-queues.o
drivers += drivers/virtio-blk.o
drivers += drivers/virtio-scsi.o
drivers += drivers/virtio-rng.o
+drivers += drivers/virtio-fs.o
drivers += drivers/kvmclock.o drivers/xenclock.o drivers/hypervclock.o
drivers += drivers/acpi.o
drivers += drivers/hpet.o
@@ -1794,6 +1795,9 @@ fs_objs += rofs/rofs_vfsops.o \
rofs/rofs_cache.o \
rofs/rofs_common.o

+fs_objs += virtiofs/virtiofs_vfsops.o \
+ virtiofs/virtiofs_vnops.o
+
fs_objs += pseudofs/pseudofs.o
fs_objs += procfs/procfs_vnops.o
fs_objs += sysfs/sysfs_vnops.o
diff --git a/arch/x64/arch-setup.cc b/arch/x64/arch-setup.cc
index 986a0928..b29f987c 100644
--- a/arch/x64/arch-setup.cc
+++ b/arch/x64/arch-setup.cc
@@ -256,6 +256,7 @@ void arch_init_premain()
#include "drivers/virtio-scsi.hh"
#include "drivers/virtio-net.hh"
#include "drivers/virtio-rng.hh"
+#include "drivers/virtio-fs.hh"
#include "drivers/xenplatform-pci.hh"
#include "drivers/ahci.hh"
#include "drivers/vmw-pvscsi.hh"
@@ -284,6 +285,7 @@ void arch_init_drivers()
drvman->register_driver(virtio::scsi::probe);
drvman->register_driver(virtio::net::probe);
drvman->register_driver(virtio::rng::probe);
+ drvman->register_driver(virtio::fs::probe);
drvman->register_driver(xenfront::xenplatform_pci::probe);
drvman->register_driver(ahci::hba::probe);
drvman->register_driver(vmw::pvscsi::probe);
diff --git a/drivers/virtio-fs.cc b/drivers/virtio-fs.cc
new file mode 100644
index 00000000..4869306d
--- /dev/null
+++ b/drivers/virtio-fs.cc
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2020 Waldemar Kozaczuk
+ *
+ * This work is open source software, licensed under the terms of the
+ * BSD license as described in the LICENSE file in the top-level directory.
+ */
+
+#include <sys/cdefs.h>
+
+#include "drivers/virtio.hh"
+#include "drivers/virtio-fs.hh"
+#include <osv/interrupt.hh>
+
+#include <osv/mempool.hh>
+#include <osv/mmu.hh>
+
+#include <string>
+#include <string.h>
+#include <map>
+#include <errno.h>
+#include <osv/debug.h>
+
+#include <osv/sched.hh>
+#include "osv/trace.hh"
+#include "osv/aligned_new.hh"
+
+#include <osv/device.h>
+
+using namespace memory;
+
+void fuse_req_wait(struct fuse_request* req)
+{
+ WITH_LOCK(req->req_mutex) {
+ req->req_wait.wait(req->req_mutex);
+ }
+}
+
+namespace virtio {
+
+static int fuse_make_request(void *driver, struct fuse_request* req)
+{
+ auto fs_driver = reinterpret_cast<fs*>(driver);
+ return fs_driver->make_request(req);
+}
+
+static void fuse_req_done(struct fuse_request* req)
+{
+ WITH_LOCK(req->req_mutex) {
+ req->req_wait.wake_one(req->req_mutex);
+ }
+}
+
+static void fuse_req_enqueue_input(vring* queue, struct fuse_request* req)
+{
+ // Header goes first
+ queue->add_out_sg(&req->in_header, sizeof(struct fuse_in_header));
+ //
+ // Add fuse in arguments as out sg
+ if (req->input_args_size) {
+ queue->add_out_sg(req->input_args_data, req->input_args_size);
+ }
+}
+
+static void fuse_req_enqueue_output(vring* queue, struct fuse_request* req)
+{
+ // Header goes first
+ queue->add_in_sg(&req->out_header, sizeof(struct fuse_out_header));
+ //
+ // Add fuse out arguments as in sg
+ if (req->output_args_size) {
+ queue->add_in_sg(req->output_args_data, req->output_args_size);
+ }
+}
+
+int fs::_instance = 0;
+
+static struct devops fs_devops {
+ no_open,
+ no_close,
+ no_read,
+ no_write,
+ no_ioctl,
+ no_devctl,
+ no_strategy,
+};
+
+struct driver fs_driver = {
+ "virtio_fs",
+ &fs_devops,
+ sizeof(struct fuse_strategy),
+};
+
+bool fs::ack_irq()
+{
+ auto isr = _dev.read_and_ack_isr();
+ auto queue = get_virt_queue(VQ_REQUEST);
+
+ if (isr) {
+ queue->disable_interrupts();
+ return true;
+ } else {
+ return false;
+ }
+}
+
+fs::fs(virtio_device& virtio_dev)
+ : virtio_driver(virtio_dev)
+{
+ _driver_name = "virtio-fs";
+ _id = _instance++;
+ virtio_i("VIRTIO FS INSTANCE %d\n", _id);
+
+ // Steps 4, 5 & 6 - negotiate and confirm features
+ setup_features();
+ read_config();
+
+ if (_config.num_queues < 1) {
+ virtio_i("Expected at least one request queue -> baling out!\n");
+ return;
+ }
+
+ // Step 7 - generic init of virtqueues
+ probe_virt_queues();
+
+ //register the single irq callback for the block
+ sched::thread* t = sched::thread::make([this] { this->req_done(); },
+ sched::thread::attr().name("virtio-fs"));
+ t->start();
+ auto queue = get_virt_queue(VQ_REQUEST);
+
+ interrupt_factory int_factory;
+ int_factory.register_msi_bindings = [queue, t](interrupt_manager &msi) {
+ msi.easy_register( {{ VQ_REQUEST, [=] { queue->disable_interrupts(); }, t }});
+ };
+
+ int_factory.create_pci_interrupt = [this,t](pci::device &pci_dev) {
+ return new pci_interrupt(
+ pci_dev,
+ [=] { return this->ack_irq(); },
+ [=] { t->wake(); });
+ };
+
+#ifndef AARCH64_PORT_STUB
+ int_factory.create_gsi_edge_interrupt = [this,t]() {
+ return new gsi_edge_interrupt(
+ _dev.get_irq(),
+ [=] { if (this->ack_irq()) t->wake(); });
+ };
+#endif
+
+ _dev.register_interrupt(int_factory);
+
+ // Enable indirect descriptor
+ queue->set_use_indirect(true);
+
+ // Step 8
+ add_dev_status(VIRTIO_CONFIG_S_DRIVER_OK);
+
+ std::string dev_name("virtiofs");
+ dev_name += std::to_string(_disk_idx++);
+
+ struct device *dev = device_create(&fs_driver, dev_name.c_str(), D_BLK); //TODO Should it be really D_BLK?
+ struct fuse_strategy *strategy = reinterpret_cast<struct fuse_strategy*>(dev->private_data);
+ strategy->drv = this;
+ strategy->make_request = fuse_make_request;
+
+ debugf("virtio-fs: Add device instance %d as [%s]\n", _id, dev_name.c_str());
+}
+
+fs::~fs()
+{
+ //TODO: In theory maintain the list of free instances and gc it
+ // including the thread objects and their stack
+}
+
+void fs::read_config()
+{
+ virtio_conf_read(0, &(_config.tag[0]), sizeof(_config.tag));
+ virtio_conf_read(offsetof(fs_config,num_queues), &(_config.num_queues), sizeof(_config.num_queues));
+ debugf("virtio-fs: Detected device with tag: [%s] and num_queues: %d\n", _config.tag, _config.num_queues);
+}
+
+void fs::req_done()
+{
+ auto* queue = get_virt_queue(VQ_REQUEST);
+ fs_req* req;
+
+ while (1) {
+ virtio_driver::wait_for_queue(queue, &vring::used_ring_not_empty);
+
+ u32 len;
+ while((req = static_cast<fs_req*>(queue->get_buf_elem(&len))) != nullptr) {
+ fuse_req_done(req->fuse_req);
+ delete req;
+ queue->get_buf_finalize();
+ }
+
+ // wake up the requesting thread in case the ring was full before
+ queue->wakeup_waiter();
+ }
+}
+
+int fs::make_request(struct fuse_request* req)
+{
+ // The lock is here for parallel requests protection
+ WITH_LOCK(_lock) {
+
+ if (!req) return EIO;
+
+ auto* queue = get_virt_queue(VQ_REQUEST);
+
+ queue->init_sg();
+
+ fuse_req_enqueue_input(queue, req);
+ fuse_req_enqueue_output(queue, req);
+
+ auto* fs_request = new fs_req(req);
+ queue->add_buf_wait(fs_request);
+ queue->kick();
+
+ return 0;
+ }
+}
+
+hw_driver* fs::probe(hw_device* dev)
+{
+ return virtio::probe<fs, VIRTIO_ID_FS>(dev);
+}
+
+}
diff --git a/drivers/virtio-fs.hh b/drivers/virtio-fs.hh
new file mode 100644
index 00000000..efdb956d
--- /dev/null
+++ b/drivers/virtio-fs.hh
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2020 Waldemar Kozaczuk
+ *
+ * This work is open source software, licensed under the terms of the
+ * BSD license as described in the LICENSE file in the top-level directory.
+ */
+
+#ifndef VIRTIO_FS_DRIVER_H
+#define VIRTIO_FS_DRIVER_H
+
+#include <osv/mutex.h>
+#include <osv/waitqueue.hh>
+#include "drivers/virtio.hh"
+#include "drivers/virtio-device.hh"
+#include "fs/virtiofs/virtiofs_i.hh"
+
+namespace virtio {
+
+enum {
+ VQ_HIPRIO,
+ VQ_REQUEST
+};
+
+class fs : public virtio_driver {
+public:
+ struct fs_config {
+ char tag[36];
+ u32 num_queues;
+ } __attribute__((packed));
+
+ explicit fs(virtio_device& dev);
+ virtual ~fs();
+
+ virtual std::string get_name() const { return _driver_name; }
+ void read_config();
+
+ int make_request(struct fuse_request*);
+
+ void req_done();
+ int64_t size();
+
+ bool ack_irq();
+
+ static hw_driver* probe(hw_device* dev);
+private:
+ struct fs_req {
+ fs_req(struct fuse_request* f) :fuse_req(f) {};
+ ~fs_req() {};
+
+ struct fuse_request* fuse_req;
+ };
+
+ std::string _driver_name;
+ fs_config _config;
+
+ //maintains the virtio instance number for multiple drives
+ static int _instance;
+ int _id;
+ // This mutex protects parallel make_request invocations
+ mutex _lock;
+};
+
+}
+#endif
diff --git a/drivers/virtio.hh b/drivers/virtio.hh
index 0cdc6ec5..aa832c51 100644
--- a/drivers/virtio.hh
+++ b/drivers/virtio.hh
@@ -59,6 +59,7 @@ enum {
VIRTIO_ID_SCSI = 8,
VIRTIO_ID_9P = 9,
VIRTIO_ID_RPROC_SERIAL = 11,
+ VIRTIO_ID_FS = 26,
};

const unsigned max_virtqueues_nr = 64;
diff --git a/fs/vfs/vfs_conf.cc b/fs/vfs/vfs_conf.cc
index ac9c111c..a23ea48f 100644
--- a/fs/vfs/vfs_conf.cc
+++ b/fs/vfs/vfs_conf.cc
@@ -52,9 +52,11 @@ extern struct vfsops nfs_vfsops;
extern struct vfsops procfs_vfsops;
extern struct vfsops sysfs_vfsops;
extern struct vfsops zfs_vfsops;
+extern struct vfsops virtiofs_vfsops;

extern int ramfs_init(void);
extern int rofs_init(void);
+extern int virtiofs_init(void);
extern int devfs_init(void);
extern int nfs_init(void);
extern int procfs_init(void);
@@ -72,5 +74,6 @@ const struct vfssw vfssw[] = {
{"sysfs", sysfs_init, &sysfs_vfsops},
{"zfs", zfs_init, &zfs_vfsops},
{"rofs", rofs_init, &rofs_vfsops},
+ {"virtiofs", virtiofs_init, &virtiofs_vfsops},
{nullptr, fs_noop, nullptr},
};
diff --git a/fs/virtiofs/virtiofs.hh b/fs/virtiofs/virtiofs.hh
new file mode 100644
index 00000000..892c9ca7
--- /dev/null
+++ b/fs/virtiofs/virtiofs.hh
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2020 Waldemar Kozaczuk
+ *
+ * This work is open source software, licensed under the terms of the
+ * BSD license as described in the LICENSE file in the top-level directory.
+ */
+
+#ifndef __INCLUDE_VIRTIOFS_H__
+#define __INCLUDE_VIRTIOFS_H__
+
+#include <osv/vnode.h>
+#include <osv/mount.h>
+#include <osv/dentry.h>
+#include <osv/prex.h>
+#include <osv/buf.h>
+#include "fuse_kernel.h"
+
+#define VIRTIOFS_DEBUG_ENABLED 1
+
+#if defined(VIRTIOFS_DEBUG_ENABLED)
+#define virtiofs_debug(...) kprintf("[virtiofs] " __VA_ARGS__)
+#else
+#define virtiofs_debug(...)
+#endif
+
+struct virtiofs_inode {
+ uint64_t nodeid;
+ struct fuse_attr attr;
+};
+
+struct virtiofs_file_data {
+ uint64_t file_handle;
+};
+
+void virtiofs_set_vnode(struct vnode *vnode, struct virtiofs_inode *inode);
+
+extern struct vfsops virtiofs_vfsops;
+extern struct vnops virtiofs_vnops;
+
+#endif
diff --git a/fs/virtiofs/virtiofs_i.hh b/fs/virtiofs/virtiofs_i.hh
new file mode 100644
index 00000000..c5dc10d2
--- /dev/null
+++ b/fs/virtiofs/virtiofs_i.hh
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2020 Waldemar Kozaczuk
+ *
+ * This work is open source software, licensed under the terms of the
+ * BSD license as described in the LICENSE file in the top-level directory.
+ */
+
+#ifndef VIRTIOFS_IO_H
+#define VIRTIOFS_IO_H
+
+#include "fuse_kernel.h"
+#include <osv/mutex.h>
+#include <osv/waitqueue.hh>
+
+struct fuse_request
+{
+ struct fuse_in_header in_header;
+ struct fuse_out_header out_header;
+
+ void *input_args_data;
+ size_t input_args_size;
+
+ void *output_args_data;
+ size_t output_args_size;
+
+ mutex_t req_mutex;
+ waitqueue req_wait;
+};
+
+struct fuse_strategy {
+ void *drv;
+ int (*make_request)(void*, struct fuse_request*);
+};
+
+int fuse_req_send_and_receive_reply(fuse_strategy* strategy, uint32_t opcode, uint64_t nodeid,
+ void *input_args_data, size_t input_args_size,
+ void *output_args_data, size_t output_args_size);
+
+void fuse_req_wait(struct fuse_request* req);
+
+#endif
diff --git a/fs/virtiofs/virtiofs_vfsops.cc b/fs/virtiofs/virtiofs_vfsops.cc
new file mode 100644
index 00000000..4e8bf26e
--- /dev/null
+++ b/fs/virtiofs/virtiofs_vfsops.cc
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2020 Waldemar Kozaczuk
+ *
+ * This work is open source software, licensed under the terms of the
+ * BSD license as described in the LICENSE file in the top-level directory.
+ */
+
+#include <sys/types.h>
+#include <osv/device.h>
+#include <osv/debug.h>
+#include <iomanip>
+#include <iostream>
+#include "virtiofs.hh"
+#include "virtiofs_i.hh"
+
+static int virtiofs_mount(struct mount *mp, const char *dev, int flags, const void *data);
+static int virtiofs_sync(struct mount *mp);
+static int virtiofs_statfs(struct mount *mp, struct statfs *statp);
+static int virtiofs_unmount(struct mount *mp, int flags);
+
+#define virtiofs_vget ((vfsop_vget_t)vfs_nullop)
+
+struct vfsops virtiofs_vfsops = {
+ virtiofs_mount, /* mount */
+ virtiofs_unmount, /* unmount */
+ virtiofs_sync, /* sync */
+ virtiofs_vget, /* vget */
+ virtiofs_statfs, /* statfs */
+ &virtiofs_vnops /* vnops */
+};
+
+std::atomic<uint64_t> fuse_unique_id(1);
+
+int fuse_req_send_and_receive_reply(fuse_strategy* strategy, uint32_t opcode, uint64_t nodeid,
+ void *input_args_data, size_t input_args_size, void *output_args_data, size_t output_args_size)
+{
+ auto *req = new (std::nothrow) fuse_request();
+
+ req->in_header.len = 0; //TODO
+ req->in_header.opcode = opcode;
+ req->in_header.unique = fuse_unique_id.fetch_add(1, std::memory_order_relaxed);
+ req->in_header.nodeid = nodeid;
+ req->in_header.uid = 0;
+ req->in_header.gid = 0;
+ req->in_header.pid = 0;
+
+ req->input_args_data = input_args_data;
+ req->input_args_size = input_args_size;
+
+ req->output_args_data = output_args_data;
+ req->output_args_size = output_args_size;
+
+ assert(strategy->drv);
+ strategy->make_request(strategy->drv, req);
+ fuse_req_wait(req);
+
+ int error = -req->out_header.error;
+ delete req;
+
+ return error;
+}
+
+void virtiofs_set_vnode(struct vnode *vnode, struct virtiofs_inode *inode)
+{
+ if (vnode == nullptr || inode == nullptr) {
+ return;
+ }
+
+ vnode->v_data = inode;
+ vnode->v_ino = inode->nodeid;
+
+ // Set type
+ if (S_ISDIR(inode->attr.mode)) {
+ vnode->v_type = VDIR;
+ } else if (S_ISREG(inode->attr.mode)) {
+ vnode->v_type = VREG;
+ } else if (S_ISLNK(inode->attr.mode)) {
+ vnode->v_type = VLNK;
+ }
+
+ vnode->v_mode = 0555;
+ vnode->v_size = inode->attr.size;
+}
+
+static int
+virtiofs_mount(struct mount *mp, const char *dev, int flags, const void *data) {
+ struct device *device;
+ int error = -1;
+
+ error = device_open(dev + 5, DO_RDWR, &device);
+ if (error) {
+ kprintf("[virtiofs] Error opening device!\n");
+ return error;
+ }
+
+ mp->m_dev = device;
+
+ auto *in_args = new(std::nothrow) fuse_init_in();
+ in_args->major = FUSE_KERNEL_VERSION;
+ in_args->minor = FUSE_KERNEL_MINOR_VERSION;
+ in_args->max_readahead = PAGE_SIZE;
+ in_args->flags = 0; //TODO Investigate which flags to set
+
+ auto *out_args = new(std::nothrow) fuse_init_out();
+
+ auto *strategy = reinterpret_cast<fuse_strategy *>(device->private_data);
+ error = fuse_req_send_and_receive_reply(strategy, FUSE_INIT, FUSE_ROOT_ID,
+ in_args, sizeof(*in_args), out_args, sizeof(*out_args));
+
+ if (!error) {
+ virtiofs_debug("Initialized fuse filesystem with version major: %d, minor: %d\n",
+ out_args->major, out_args->minor);
+
+ auto *root_node = new virtiofs_inode();
+ root_node->nodeid = FUSE_ROOT_ID;
+ root_node->attr.mode = S_IFDIR;
+
+ virtiofs_set_vnode(mp->m_root->d_vnode, root_node);
+
+ mp->m_data = strategy;
+ mp->m_dev = device;
+ } else {
+ kprintf("[virtiofs] Failed to initialized fuse filesystem!\n");
+ }
+
+ delete out_args;
+ delete in_args;
+
+ return error;
+}
+
+static int virtiofs_sync(struct mount *mp) {
+ return 0;
+}
+
+static int virtiofs_statfs(struct mount *mp, struct statfs *statp)
+{
+ //TODO
+ //struct virtiofs_info *virtiofs = (struct virtiofs_info *) mp->m_data;
+
+ //statp->f_bsize = sb->block_size;
+
+ // Total blocks
+ //statp->f_blocks = sb->structure_info_blocks_count + sb->structure_info_first_block;
+ // Read only. 0 blocks free
+ statp->f_bfree = 0;
+ statp->f_bavail = 0;
+
+ statp->f_ffree = 0;
+ //statp->f_files = sb->inodes_count; //Needs to be inode count
+
+ statp->f_namelen = 0; //FIXME
+
+ return 0;
+}
+
+static int
+virtiofs_unmount(struct mount *mp, int flags)
+{
+ struct device *dev = mp->m_dev;
+ return device_close(dev);
+}
diff --git a/fs/virtiofs/virtiofs_vnops.cc b/fs/virtiofs/virtiofs_vnops.cc
new file mode 100644
index 00000000..3c212274
--- /dev/null
+++ b/fs/virtiofs/virtiofs_vnops.cc
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2020 Waldemar Kozaczuk
+ *
+ * This work is open source software, licensed under the terms of the
+ * BSD license as described in the LICENSE file in the top-level directory.
+ */
+
+#include <sys/stat.h>
+#include <dirent.h>
+#include <sys/param.h>
+
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fcntl.h>
+
+#include <osv/prex.h>
+#include <osv/vnode.h>
+#include <osv/file.h>
+#include <osv/mount.h>
+#include <osv/debug.h>
+
+#include <sys/types.h>
+#include <osv/device.h>
+#include <osv/sched.hh>
+
+#include "virtiofs.hh"
+#include "virtiofs_i.hh"
+
+#define VERIFY_READ_INPUT_ARGUMENTS() \
+ /* Cant read directories */\
+ if (vnode->v_type == VDIR) \
+ return EISDIR; \
+ /* Cant read anything but reg */\
+ if (vnode->v_type != VREG) \
+ return EINVAL; \
+ /* Cant start reading before the first byte */\
+ if (uio->uio_offset < 0) \
+ return EINVAL; \
+ /* Need to read more than 1 byte */\
+ if (uio->uio_resid == 0) \
+ return 0; \
+ /* Cant read after the end of the file */\
+ if (uio->uio_offset >= (off_t)vnode->v_size) \
+ return 0;
+
+int virtiofs_init(void) {
+ return 0;
+}
+
+static int virtiofs_lookup(struct vnode *vnode, char *name, struct vnode **vpp)
+{
+ struct virtiofs_inode *inode = (struct virtiofs_inode *) vnode->v_data;
+ struct vnode *vp = nullptr;
+
+ if (*name == '\0') {
+ return ENOENT;
+ }
+
+ if (!S_ISDIR(inode->attr.mode)) {
+ kprintf("[virtiofs] inode:%d, ABORTED lookup of %s because not a directory\n", inode->nodeid, name);
+ return ENOTDIR;
+ }
+
+ auto *out_args = new (std::nothrow) fuse_entry_out();
+ auto input = new char[strlen(name) + 1];
+ strcpy(input, name);
+
+ auto *strategy = reinterpret_cast<fuse_strategy*>(vnode->v_mount->m_data);
+ int error = fuse_req_send_and_receive_reply(strategy, FUSE_LOOKUP, inode->nodeid,
+ input, strlen(name) + 1, out_args, sizeof(*out_args));
+
+ if (!error) {
+ if (vget(vnode->v_mount, out_args->nodeid, &vp)) { //TODO: Will it ever work? Revisit
+ virtiofs_debug("lookup found vp in cache!\n");
+ *vpp = vp;
+ return 0;
+ }
+
+ auto *new_inode = new virtiofs_inode();
+ new_inode->nodeid = out_args->nodeid;
+ virtiofs_debug("inode %d, lookup found inode %d for %s!\n", inode->nodeid, new_inode->nodeid, name);
+ memcpy(&new_inode->attr, &out_args->attr, sizeof(out_args->attr));
+
+ virtiofs_set_vnode(vp, new_inode);
+ *vpp = vp;
+ } else {
+ kprintf("[virtiofs] inode:%d, lookup failed to find %s\n", inode->nodeid, name);
+ //TODO Implement proper error handling by sending FUSE_FORGET
+ }
+
+ delete input;
+ delete out_args;
+
+ return error;
+}
+
+static int virtiofs_open(struct file *fp)
+{
+ if ((file_flags(fp) & FWRITE)) {
+ // Do no allow opening files to write
+ return (EROFS);
+ }
+
+ struct vnode *vnode = file_dentry(fp)->d_vnode;
+ struct virtiofs_inode *inode = (struct virtiofs_inode *) vnode->v_data;
+
+ auto *out_args = new (std::nothrow) fuse_open_out();
+ auto *input_args = new (std::nothrow) fuse_open_in();
+ input_args->flags = O_RDONLY;
+
+ auto *strategy = reinterpret_cast<fuse_strategy*>(vnode->v_mount->m_data);
+ int error = fuse_req_send_and_receive_reply(strategy, FUSE_OPEN, inode->nodeid,
+ input_args, sizeof(*input_args), out_args, sizeof(*out_args));
+
+ if (!error) {
+ virtiofs_debug("inode %d, opened\n", inode->nodeid);
+
+ auto *file_data = new virtiofs_file_data();
+ file_data->file_handle = out_args->fh;
+ fp->f_data = file_data;
+ }
+
+ delete input_args;
+ delete out_args;
+
+ return error;
+}
+
+static int virtiofs_close(struct vnode *vnode, struct file *fp)
+{
+ struct virtiofs_inode *inode = (struct virtiofs_inode *) vnode->v_data;
+
+ auto *input_args = new (std::nothrow) fuse_release_in();
+ auto *file_data = reinterpret_cast<virtiofs_file_data*>(fp->f_data);
+ input_args->fh = file_data->file_handle;
+
+ auto *strategy = reinterpret_cast<fuse_strategy*>(vnode->v_mount->m_data);
+ auto error = fuse_req_send_and_receive_reply(strategy, FUSE_RELEASE, inode->nodeid,
+ input_args, sizeof(*input_args), nullptr, 0);
+
+ if (!error) {
+ fp->f_data = nullptr;
+ delete file_data;
+ virtiofs_debug("inode %d, closed\n", inode->nodeid);
+ }
+
+ //TODO: Investigate if we should send FUSE_FORGET once all handles to the file closed on our side
+
+ delete input_args;
+
+ return error;
+}
+
+static int virtiofs_readlink(struct vnode *vnode, struct uio *uio)
+{
+ struct virtiofs_inode *inode = (struct virtiofs_inode *) vnode->v_data;
+
+ auto *link_path = new (std::nothrow) char[PATH_MAX];
+
+ auto *strategy = reinterpret_cast<fuse_strategy*>(vnode->v_mount->m_data);
+ int error = fuse_req_send_and_receive_reply(strategy, FUSE_READLINK, inode->nodeid,
+ nullptr, 0, link_path, PATH_MAX);
+
+ int ret = 0;
+ if (!error) {
+ virtiofs_debug("inode %d, read symlink [%s]\n", inode->nodeid, link_path);
+ ret = uiomove(link_path, strlen(link_path), uio);
+ } else {
+ kprintf("[virtiofs] Error reading data\n");
+ ret = error;
+ }
+
+ delete link_path;
+
+ return ret;
+}
+
+//TODO: Optimize it to reduce number of exits to host (each fuse_req_send_and_receive_reply())
+// by reading eagerly "ahead/around" just like ROFS does and caching it
+static int virtiofs_read(struct vnode *vnode, struct file *fp, struct uio *uio, int ioflag)
+{
+ struct virtiofs_inode *inode = (struct virtiofs_inode *) vnode->v_data;
+
+ VERIFY_READ_INPUT_ARGUMENTS()
+
+ // Total read amount is what they requested, or what is left
+ uint64_t read_amt = std::min<uint64_t>(inode->attr.size - uio->uio_offset, uio->uio_resid);
+ void *buf = malloc(read_amt);
+
+ auto *input_args = new (std::nothrow) fuse_read_in();
+ auto *file_data = reinterpret_cast<virtiofs_file_data*>(fp->f_data);
+ input_args->fh = file_data->file_handle;
+ input_args->offset = uio->uio_offset;
+ input_args->size = read_amt;
+ input_args->flags = ioflag;
+ input_args->lock_owner = 0;
+
+ virtiofs_debug("inode %d, reading %d bytes at offset %d\n", inode->nodeid, read_amt, uio->uio_offset);
+
+ auto *strategy = reinterpret_cast<fuse_strategy*>(vnode->v_mount->m_data);
+ auto error = fuse_req_send_and_receive_reply(strategy, FUSE_READ, inode->nodeid,
+ input_args, sizeof(*input_args), buf, read_amt);
+
+ int ret = 0;
+ if (!error) {
+ ret = uiomove(buf, read_amt, uio);
+ } else {
+ kprintf("[virtiofs] Error reading data\n");
+ ret = error;
+ }
+
+ free(buf);
+ free(input_args);
+
+ return ret;
+}
+//
+static int virtiofs_readdir(struct vnode *vnode, struct file *fp, struct dirent *dir)
+{
+ //TODO Implement
+ return EPERM;
+}
+
+static int virtiofs_getattr(struct vnode *vnode, struct vattr *attr)
+{
+ struct virtiofs_inode *inode = (struct virtiofs_inode *) vnode->v_data;
+
+ attr->va_mode = 0555; //Is it really correct?
+
+ if (S_ISDIR(inode->attr.mode)) {
+ attr->va_type = VDIR;
+ } else if (S_ISREG(inode->attr.mode)) {
+ attr->va_type = VREG;
+ } else if (S_ISLNK(inode->attr.mode)) {
+ attr->va_type = VLNK;
+ }
+
+ attr->va_nodeid = vnode->v_ino;
+ attr->va_size = inode->attr.size;
+
+ return 0;
+}
+
+#define virtiofs_write ((vnop_write_t)vop_erofs)
+#define virtiofs_seek ((vnop_seek_t)vop_nullop)
+#define virtiofs_ioctl ((vnop_ioctl_t)vop_nullop)
+#define virtiofs_create ((vnop_create_t)vop_erofs)
+#define virtiofs_remove ((vnop_remove_t)vop_erofs)
+#define virtiofs_rename ((vnop_rename_t)vop_erofs)
+#define virtiofs_mkdir ((vnop_mkdir_t)vop_erofs)
+#define virtiofs_rmdir ((vnop_rmdir_t)vop_erofs)
+#define virtiofs_setattr ((vnop_setattr_t)vop_erofs)
+#define virtiofs_inactive ((vnop_inactive_t)vop_nullop)
+#define virtiofs_truncate ((vnop_truncate_t)vop_erofs)
+#define virtiofs_link ((vnop_link_t)vop_erofs)
+#define virtiofs_arc ((vnop_cache_t) nullptr)
+#define virtiofs_fallocate ((vnop_fallocate_t)vop_erofs)
+#define virtiofs_fsync ((vnop_fsync_t)vop_nullop)
+#define virtiofs_symlink ((vnop_symlink_t)vop_erofs)
+
+struct vnops virtiofs_vnops = {
+ virtiofs_open, /* open */
+ virtiofs_close, /* close */
+ virtiofs_read, /* read */
+ virtiofs_write, /* write - returns error when called */
+ virtiofs_seek, /* seek */
+ virtiofs_ioctl, /* ioctl */
+ virtiofs_fsync, /* fsync */
+ virtiofs_readdir, /* readdir */
+ virtiofs_lookup, /* lookup */
+ virtiofs_create, /* create - returns error when called */
+ virtiofs_remove, /* remove - returns error when called */
+ virtiofs_rename, /* rename - returns error when called */
+ virtiofs_mkdir, /* mkdir - returns error when called */
+ virtiofs_rmdir, /* rmdir - returns error when called */
+ virtiofs_getattr, /* getattr */
+ virtiofs_setattr, /* setattr - returns error when called */
+ virtiofs_inactive, /* inactive */
+ virtiofs_truncate, /* truncate - returns error when called*/
+ virtiofs_link, /* link - returns error when called*/
+ virtiofs_arc, /* arc */ //TODO: Implement to allow memory re-use when mapping files, investigate using virtio-fs DAX
+ virtiofs_fallocate, /* fallocate - returns error when called*/
+ virtiofs_readlink, /* read link */
+ virtiofs_symlink /* symbolic link - returns error when called*/
+};
diff --git a/include/osv/device.h b/include/osv/device.h
index 16d2e470..17490bde 100755
--- a/include/osv/device.h
+++ b/include/osv/device.h
@@ -89,6 +89,7 @@ struct devops {
#define no_write ((devop_write_t)enodev)
#define no_ioctl ((devop_ioctl_t)enodev)
#define no_devctl ((devop_devctl_t)nullop)
+#define no_strategy ((devop_strategy_t)nullop)

/*
* Driver object
--
2.20.1

Nadav Har'El

unread,
Feb 9, 2020, 10:21:17 AM2/9/20
to Waldemar Kozaczuk, Osv Dev
On Sun, Feb 9, 2020 at 1:22 AM Waldemar Kozaczuk <jwkoz...@gmail.com> wrote:
This file is a copy of https://github.com/libfuse/libfuse/blob/38c9cb43787bf83ca4e9c9af707e82165de99008/include/fuse_kernel.h
and provides the wire-level interface of fuse protocol used to exchange
data between guest and host over virtio-fs device.

I'm not familiar with fuse, so I don't know - does every "fuse"-base tool needs to include its own copy of this header file?
I see in Fedora there is a "fuse-devel" package, which has a bunch of header files with different names. Would any one
of those do, or they are different things? (please excuse me if I'm asking a stupid question, as I said I am not very familiar
with fuse).
--
You received this message because you are subscribed to the Google Groups "OSv Development" group.
To unsubscribe from this group and stop receiving emails from it, send an email to osv-dev+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/osv-dev/20200208232200.15009-1-jwkozaczuk%40gmail.com.

Commit Bot

unread,
Feb 9, 2020, 10:38:05 AM2/9/20
to osv...@googlegroups.com, Waldemar Kozaczuk
From: Waldemar Kozaczuk <jwkoz...@gmail.com>
Committer: Nadav Har'El <n...@scylladb.com>
Branch: master

virtio-fs: add fuse protocol header

This file is a copy of https://github.com/libfuse/libfuse/blob/38c9cb43787bf83ca4e9c9af707e82165de99008/include/fuse_kernel.h
and provides the wire-level interface of fuse protocol used to exchange
data between guest and host over virtio-fs device.

Signed-off-by: Waldemar Kozaczuk <jwkoz...@gmail.com>
Message-Id: <20200208232200.1...@gmail.com>

---
diff --git a/fs/virtiofs/fuse_kernel.h b/fs/virtiofs/fuse_kernel.h
--- a/fs/virtiofs/fuse_kernel.h

Commit Bot

unread,
Feb 9, 2020, 10:38:06 AM2/9/20
to osv...@googlegroups.com, Waldemar Kozaczuk
From: Waldemar Kozaczuk <jwkoz...@gmail.com>
Committer: Nadav Har'El <n...@scylladb.com>
Branch: master

virtio-fs: initial implementation of read-only subset
Message-Id: <20200208232200.1...@gmail.com>

---
diff --git a/Makefile b/Makefile
--- a/Makefile
+++ b/Makefile
@@ -829,6 +829,7 @@ drivers += drivers/vmxnet3-queues.o
drivers += drivers/virtio-blk.o
drivers += drivers/virtio-scsi.o
drivers += drivers/virtio-rng.o
+drivers += drivers/virtio-fs.o
drivers += drivers/kvmclock.o drivers/xenclock.o drivers/hypervclock.o
drivers += drivers/acpi.o
drivers += drivers/hpet.o
@@ -1794,6 +1795,9 @@ fs_objs += rofs/rofs_vfsops.o \
rofs/rofs_cache.o \
rofs/rofs_common.o

+fs_objs += virtiofs/virtiofs_vfsops.o \
+ virtiofs/virtiofs_vnops.o
+
fs_objs += pseudofs/pseudofs.o
fs_objs += procfs/procfs_vnops.o
fs_objs += sysfs/sysfs_vnops.o
diff --git a/arch/x64/arch-setup.cc b/arch/x64/arch-setup.cc
--- a/arch/x64/arch-setup.cc
+++ b/arch/x64/arch-setup.cc
@@ -256,6 +256,7 @@ void arch_init_premain()
#include "drivers/virtio-scsi.hh"
#include "drivers/virtio-net.hh"
#include "drivers/virtio-rng.hh"
+#include "drivers/virtio-fs.hh"
#include "drivers/xenplatform-pci.hh"
#include "drivers/ahci.hh"
#include "drivers/vmw-pvscsi.hh"
@@ -284,6 +285,7 @@ void arch_init_drivers()
drvman->register_driver(virtio::scsi::probe);
drvman->register_driver(virtio::net::probe);
drvman->register_driver(virtio::rng::probe);
+ drvman->register_driver(virtio::fs::probe);
drvman->register_driver(xenfront::xenplatform_pci::probe);
drvman->register_driver(ahci::hba::probe);
drvman->register_driver(vmw::pvscsi::probe);
diff --git a/drivers/virtio-fs.cc b/drivers/virtio-fs.cc
--- a/drivers/virtio-fs.cc
--- a/drivers/virtio-fs.hh
--- a/drivers/virtio.hh
+++ b/drivers/virtio.hh
@@ -59,6 +59,7 @@ enum {
VIRTIO_ID_SCSI = 8,
VIRTIO_ID_9P = 9,
VIRTIO_ID_RPROC_SERIAL = 11,
+ VIRTIO_ID_FS = 26,
};

const unsigned max_virtqueues_nr = 64;
diff --git a/fs/vfs/vfs_conf.cc b/fs/vfs/vfs_conf.cc
--- a/fs/vfs/vfs_conf.cc
+++ b/fs/vfs/vfs_conf.cc
@@ -52,9 +52,11 @@ extern struct vfsops nfs_vfsops;
extern struct vfsops procfs_vfsops;
extern struct vfsops sysfs_vfsops;
extern struct vfsops zfs_vfsops;
+extern struct vfsops virtiofs_vfsops;

extern int ramfs_init(void);
extern int rofs_init(void);
+extern int virtiofs_init(void);
extern int devfs_init(void);
extern int nfs_init(void);
extern int procfs_init(void);
@@ -72,5 +74,6 @@ const struct vfssw vfssw[] = {
{"sysfs", sysfs_init, &sysfs_vfsops},
{"zfs", zfs_init, &zfs_vfsops},
{"rofs", rofs_init, &rofs_vfsops},
+ {"virtiofs", virtiofs_init, &virtiofs_vfsops},
{nullptr, fs_noop, nullptr},
};
diff --git a/fs/virtiofs/virtiofs.hh b/fs/virtiofs/virtiofs.hh
--- a/fs/virtiofs/virtiofs.hh
--- a/fs/virtiofs/virtiofs_i.hh
--- a/fs/virtiofs/virtiofs_vfsops.cc
--- a/fs/virtiofs/virtiofs_vnops.cc

Waldek Kozaczuk

unread,
Feb 9, 2020, 11:01:11 AM2/9/20
to Nadav Har'El, Osv Dev
I think that having a copy of this file is a good idea as that way we specify exactly which version of the protocol we implement. I think that Linux and libfuse has its one copy. We should probably add license for that to our licenses directory. The file itself has liberal BSD license so I think we are fine. 
Reply all
Reply to author
Forward
0 new messages