aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>2011-10-25 12:10:40 +0530
committerAneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>2011-10-31 12:34:18 +0530
commit9db221ae73a18e0bd2b1ee6c7dc1904ed06fb464 (patch)
treee07b64eb5813824d4840e5e3f5abd2d6017dc3c0
parentcc720ddb5435ceaa4cda397ca3e74b099752b763 (diff)
hw/9pfs: Add synthetic file system support using 9p
This patch create a synthetic file system with mount tag v_synth when -virtfs_synth command line option is specified in qemu. The synthetic file system can be mounted in guest using 9p using the below command line mount -t 9p -oversion=9p2000.L,trans=virtio v_synth <mountpint> Synthetic file system enabled different qemu subsystem to register callbacks for read and write events from guest. The subsystem can create directories and files in the synthetic file system as show in ex below qemu_v9fs_synth_mkdir(NULL, 0777, "test2", &node); qemu_v9fs_synth_add_file(node, 0777, "testfile", my_test_read, NULL, NULL); Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
-rw-r--r--Makefile.objs1
-rw-r--r--fsdev/qemu-fsdev.c1
-rw-r--r--fsdev/qemu-fsdev.h1
-rw-r--r--hw/9pfs/virtio-9p-synth.c574
-rw-r--r--hw/9pfs/virtio-9p-synth.h50
-rw-r--r--hw/9pfs/virtio-9p.h6
-rw-r--r--qemu-options.hx9
-rw-r--r--vl.c18
8 files changed, 660 insertions, 0 deletions
diff --git a/Makefile.objs b/Makefile.objs
index 01587c8f8f..3dead4e4a1 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -310,6 +310,7 @@ hw-obj-$(CONFIG_SOUND) += $(sound-obj-y)
9pfs-nested-$(CONFIG_VIRTFS) += virtio-9p-xattr-user.o virtio-9p-posix-acl.o
9pfs-nested-$(CONFIG_VIRTFS) += virtio-9p-coth.o cofs.o codir.o cofile.o
9pfs-nested-$(CONFIG_VIRTFS) += coxattr.o virtio-9p-handle.o
+9pfs-nested-$(CONFIG_VIRTFS) += virtio-9p-synth.o
hw-obj-$(CONFIG_REALLY_VIRTFS) += $(addprefix 9pfs/, $(9pfs-nested-y))
$(addprefix 9pfs/, $(9pfs-nested-y)): QEMU_CFLAGS+=$(GLIB_CFLAGS)
diff --git a/fsdev/qemu-fsdev.c b/fsdev/qemu-fsdev.c
index 27d10cb3b3..7fd2aa7793 100644
--- a/fsdev/qemu-fsdev.c
+++ b/fsdev/qemu-fsdev.c
@@ -24,6 +24,7 @@ static QTAILQ_HEAD(FsDriverEntry_head, FsDriverListEntry) fsdriver_entries =
static FsDriverTable FsDrivers[] = {
{ .name = "local", .ops = &local_ops},
{ .name = "handle", .ops = &handle_ops},
+ { .name = "synth", .ops = &synth_ops},
};
int qemu_fsdev_add(QemuOpts *opts)
diff --git a/fsdev/qemu-fsdev.h b/fsdev/qemu-fsdev.h
index 5099085720..8ef847374a 100644
--- a/fsdev/qemu-fsdev.h
+++ b/fsdev/qemu-fsdev.h
@@ -53,4 +53,5 @@ int qemu_fsdev_add(QemuOpts *opts);
FsDriverEntry *get_fsdev_fsentry(char *id);
extern FileOperations local_ops;
extern FileOperations handle_ops;
+extern FileOperations synth_ops;
#endif
diff --git a/hw/9pfs/virtio-9p-synth.c b/hw/9pfs/virtio-9p-synth.c
new file mode 100644
index 0000000000..72a916c577
--- /dev/null
+++ b/hw/9pfs/virtio-9p-synth.c
@@ -0,0 +1,574 @@
+/*
+ * Virtio 9p synthetic file system support
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Malahal Naineni <malahal@us.ibm.com>
+ * Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include "hw/virtio.h"
+#include "virtio-9p.h"
+#include "virtio-9p-xattr.h"
+#include "fsdev/qemu-fsdev.h"
+#include "virtio-9p-synth.h"
+
+#include <sys/stat.h>
+
+/* Root node for synth file system */
+V9fsSynthNode v9fs_synth_root = {
+ .name = "/",
+ .actual_attr = {
+ .mode = 0555 | S_IFDIR,
+ .nlink = 1,
+ },
+ .attr = &v9fs_synth_root.actual_attr,
+};
+
+/*FIXME!! should be converted to qemu_rwlock_t */
+static pthread_rwlock_t v9fs_synth_mutex;
+static int v9fs_synth_node_count;
+/* set to 1 when the synth fs is ready */
+static int v9fs_synth_fs;
+
+static V9fsSynthNode *v9fs_add_dir_node(V9fsSynthNode *parent, int mode,
+ const char *name,
+ V9fsSynthNodeAttr *attr, int inode)
+{
+ V9fsSynthNode *node;
+
+ /* Add directory type and remove write bits */
+ mode = ((mode & 0777) | S_IFDIR) & ~(S_IWUSR | S_IWGRP | S_IWOTH);
+ node = g_malloc0(sizeof(V9fsSynthNode));
+ if (attr) {
+ /* We are adding .. or . entries */
+ node->attr = attr;
+ node->attr->nlink++;
+ } else {
+ node->attr = &node->actual_attr;
+ node->attr->inode = inode;
+ node->attr->nlink = 1;
+ /* We don't allow write to directories */
+ node->attr->mode = mode;
+ node->attr->write = NULL;
+ node->attr->read = NULL;
+ }
+ node->private = node;
+ strncpy(node->name, name, sizeof(node->name));
+ QLIST_INSERT_HEAD(&parent->child, node, sibling);
+ return node;
+}
+
+int qemu_v9fs_synth_mkdir(V9fsSynthNode *parent, int mode,
+ const char *name, V9fsSynthNode **result)
+{
+ int ret;
+ V9fsSynthNode *node, *tmp;
+
+ if (!v9fs_synth_fs) {
+ return EAGAIN;
+ }
+ if (!name || (strlen(name) >= NAME_MAX)) {
+ return EINVAL;
+ }
+ if (!parent) {
+ parent = &v9fs_synth_root;
+ }
+ pthread_rwlock_wrlock(&v9fs_synth_mutex);
+ QLIST_FOREACH(tmp, &parent->child, sibling) {
+ if (!strcmp(tmp->name, name)) {
+ ret = EEXIST;
+ goto err_out;
+ }
+ }
+ /* Add the name */
+ node = v9fs_add_dir_node(parent, mode, name, NULL, v9fs_synth_node_count++);
+ v9fs_add_dir_node(node, parent->attr->mode, "..",
+ parent->attr, parent->attr->inode);
+ v9fs_add_dir_node(node, node->attr->mode, ".",
+ node->attr, node->attr->inode);
+ *result = node;
+ ret = 0;
+err_out:
+ pthread_rwlock_unlock(&v9fs_synth_mutex);
+ return ret;
+}
+
+int qemu_v9fs_synth_add_file(V9fsSynthNode *parent, int mode,
+ const char *name, v9fs_synth_read read,
+ v9fs_synth_write write, void *arg)
+{
+ int ret;
+ V9fsSynthNode *node, *tmp;
+
+ if (!v9fs_synth_fs) {
+ return EAGAIN;
+ }
+ if (!name || (strlen(name) >= NAME_MAX)) {
+ return EINVAL;
+ }
+ if (!parent) {
+ parent = &v9fs_synth_root;
+ }
+
+ pthread_rwlock_wrlock(&v9fs_synth_mutex);
+ QLIST_FOREACH(tmp, &parent->child, sibling) {
+ if (!strcmp(tmp->name, name)) {
+ ret = EEXIST;
+ goto err_out;
+ }
+ }
+ /* Add file type and remove write bits */
+ mode = ((mode & 0777) | S_IFREG);
+ node = g_malloc0(sizeof(V9fsSynthNode));
+ node->attr = &node->actual_attr;
+ node->attr->inode = v9fs_synth_node_count++;
+ node->attr->nlink = 1;
+ node->attr->read = read;
+ node->attr->write = write;
+ node->attr->mode = mode;
+ node->private = arg;
+ strncpy(node->name, name, sizeof(node->name));
+ QLIST_INSERT_HEAD(&parent->child, node, sibling);
+ ret = 0;
+err_out:
+ pthread_rwlock_unlock(&v9fs_synth_mutex);
+ return ret;
+}
+
+static void v9fs_synth_fill_statbuf(V9fsSynthNode *node, struct stat *stbuf)
+{
+ stbuf->st_dev = 0;
+ stbuf->st_ino = node->attr->inode;
+ stbuf->st_mode = node->attr->mode;
+ stbuf->st_nlink = node->attr->nlink;
+ stbuf->st_uid = 0;
+ stbuf->st_gid = 0;
+ stbuf->st_rdev = 0;
+ stbuf->st_size = 0;
+ stbuf->st_blksize = 0;
+ stbuf->st_blocks = 0;
+ stbuf->st_atime = 0;
+ stbuf->st_mtime = 0;
+ stbuf->st_ctime = 0;
+}
+
+static int v9fs_synth_lstat(FsContext *fs_ctx,
+ V9fsPath *fs_path, struct stat *stbuf)
+{
+ V9fsSynthNode *node = *(V9fsSynthNode **)fs_path->data;
+
+ v9fs_synth_fill_statbuf(node, stbuf);
+ return 0;
+}
+
+static int v9fs_synth_fstat(FsContext *fs_ctx,
+ V9fsFidOpenState *fs, struct stat *stbuf)
+{
+ V9fsSynthOpenState *synth_open = fs->private;
+ v9fs_synth_fill_statbuf(synth_open->node, stbuf);
+ return 0;
+}
+
+static int v9fs_synth_opendir(FsContext *ctx,
+ V9fsPath *fs_path, V9fsFidOpenState *fs)
+{
+ V9fsSynthOpenState *synth_open;
+ V9fsSynthNode *node = *(V9fsSynthNode **)fs_path->data;
+
+ synth_open = g_malloc(sizeof(*synth_open));
+ synth_open->node = node;
+ node->open_count++;
+ fs->private = synth_open;
+ return 0;
+}
+
+static int v9fs_synth_closedir(FsContext *ctx, V9fsFidOpenState *fs)
+{
+ V9fsSynthOpenState *synth_open = fs->private;
+ V9fsSynthNode *node = synth_open->node;
+
+ node->open_count--;
+ g_free(synth_open);
+ fs->private = NULL;
+ return 0;
+}
+
+static off_t v9fs_synth_telldir(FsContext *ctx, V9fsFidOpenState *fs)
+{
+ V9fsSynthOpenState *synth_open = fs->private;
+ return synth_open->offset;
+}
+
+static void v9fs_synth_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off)
+{
+ V9fsSynthOpenState *synth_open = fs->private;
+ synth_open->offset = off;
+}
+
+static void v9fs_synth_rewinddir(FsContext *ctx, V9fsFidOpenState *fs)
+{
+ v9fs_synth_seekdir(ctx, fs, 0);
+}
+
+static void v9fs_synth_direntry(V9fsSynthNode *node,
+ struct dirent *entry, off_t off)
+{
+ strcpy(entry->d_name, node->name);
+ entry->d_ino = node->attr->inode;
+ entry->d_off = off + 1;
+}
+
+static int v9fs_synth_get_dentry(V9fsSynthNode *dir, struct dirent *entry,
+ struct dirent **result, off_t off)
+{
+ int i = 0;
+ V9fsSynthNode *node;
+
+ pthread_rwlock_rdlock(&v9fs_synth_mutex);
+ QLIST_FOREACH(node, &dir->child, sibling) {
+ /* This is the off child of the directory */
+ if (i == off) {
+ break;
+ }
+ i++;
+ }
+ pthread_rwlock_unlock(&v9fs_synth_mutex);
+ if (!node) {
+ /* end of directory */
+ *result = NULL;
+ return 0;
+ }
+ v9fs_synth_direntry(node, entry, off);
+ *result = entry;
+ return 0;
+}
+
+static int v9fs_synth_readdir_r(FsContext *ctx, V9fsFidOpenState *fs,
+ struct dirent *entry, struct dirent **result)
+{
+ int ret;
+ V9fsSynthOpenState *synth_open = fs->private;
+ V9fsSynthNode *node = synth_open->node;
+ ret = v9fs_synth_get_dentry(node, entry, result, synth_open->offset);
+ if (!ret && *result != NULL) {
+ synth_open->offset++;
+ }
+ return ret;
+}
+
+static int v9fs_synth_open(FsContext *ctx, V9fsPath *fs_path,
+ int flags, V9fsFidOpenState *fs)
+{
+ V9fsSynthOpenState *synth_open;
+ V9fsSynthNode *node = *(V9fsSynthNode **)fs_path->data;
+
+ synth_open = g_malloc(sizeof(*synth_open));
+ synth_open->node = node;
+ node->open_count++;
+ fs->private = synth_open;
+ return 0;
+}
+
+static int v9fs_synth_open2(FsContext *fs_ctx, V9fsPath *dir_path,
+ const char *name, int flags,
+ FsCred *credp, V9fsFidOpenState *fs)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+static int v9fs_synth_close(FsContext *ctx, V9fsFidOpenState *fs)
+{
+ V9fsSynthOpenState *synth_open = fs->private;
+ V9fsSynthNode *node = synth_open->node;
+
+ node->open_count--;
+ g_free(synth_open);
+ fs->private = NULL;
+ return 0;
+}
+
+static ssize_t v9fs_synth_pwritev(FsContext *ctx, V9fsFidOpenState *fs,
+ const struct iovec *iov,
+ int iovcnt, off_t offset)
+{
+ int i, count = 0, wcount;
+ V9fsSynthOpenState *synth_open = fs->private;
+ V9fsSynthNode *node = synth_open->node;
+ if (!node->attr->write) {
+ errno = EPERM;
+ return -1;
+ }
+ for (i = 0; i < iovcnt; i++) {
+ wcount = node->attr->write(iov[i].iov_base, iov[i].iov_len,
+ offset, node->private);
+ offset += wcount;
+ count += wcount;
+ /* If we wrote less than requested. we are done */
+ if (wcount < iov[i].iov_len) {
+ break;
+ }
+ }
+ return count;
+}
+
+static ssize_t v9fs_synth_preadv(FsContext *ctx, V9fsFidOpenState *fs,
+ const struct iovec *iov,
+ int iovcnt, off_t offset)
+{
+ int i, count = 0, rcount;
+ V9fsSynthOpenState *synth_open = fs->private;
+ V9fsSynthNode *node = synth_open->node;
+ if (!node->attr->read) {
+ errno = EPERM;
+ return -1;
+ }
+ for (i = 0; i < iovcnt; i++) {
+ rcount = node->attr->read(iov[i].iov_base, iov[i].iov_len,
+ offset, node->private);
+ offset += rcount;
+ count += rcount;
+ /* If we read less than requested. we are done */
+ if (rcount < iov[i].iov_len) {
+ break;
+ }
+ }
+ return count;
+}
+
+static int v9fs_synth_truncate(FsContext *ctx, V9fsPath *path, off_t offset)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+static int v9fs_synth_chmod(FsContext *fs_ctx, V9fsPath *path, FsCred *credp)
+{
+ errno = EPERM;
+ return -1;
+}
+
+static int v9fs_synth_mknod(FsContext *fs_ctx, V9fsPath *path,
+ const char *buf, FsCred *credp)
+{
+ errno = EPERM;
+ return -1;
+}
+
+static int v9fs_synth_mkdir(FsContext *fs_ctx, V9fsPath *path,
+ const char *buf, FsCred *credp)
+{
+ errno = EPERM;
+ return -1;
+}
+
+static ssize_t v9fs_synth_readlink(FsContext *fs_ctx, V9fsPath *path,
+ char *buf, size_t bufsz)
+{
+ errno = ENOSYS;
+ return -1;
+}
+
+static int v9fs_synth_symlink(FsContext *fs_ctx, const char *oldpath,
+ V9fsPath *newpath, const char *buf, FsCred *credp)
+{
+ errno = EPERM;
+ return -1;
+}
+
+static int v9fs_synth_link(FsContext *fs_ctx, V9fsPath *oldpath,
+ V9fsPath *newpath, const char *buf)
+{
+ errno = EPERM;
+ return -1;
+}
+
+static int v9fs_synth_rename(FsContext *ctx, const char *oldpath,
+ const char *newpath)
+{
+ errno = EPERM;
+ return -1;
+}
+
+static int v9fs_synth_chown(FsContext *fs_ctx, V9fsPath *path, FsCred *credp)
+{
+ errno = EPERM;
+ return -1;
+}
+
+static int v9fs_synth_utimensat(FsContext *fs_ctx, V9fsPath *path,
+ const struct timespec *buf)
+{
+ errno = EPERM;
+ return 0;
+}
+
+static int v9fs_synth_remove(FsContext *ctx, const char *path)
+{
+ errno = EPERM;
+ return -1;
+}
+
+static int v9fs_synth_fsync(FsContext *ctx, V9fsFidOpenState *fs, int datasync)
+{
+ errno = ENOSYS;
+ return 0;
+}
+
+static int v9fs_synth_statfs(FsContext *s, V9fsPath *fs_path,
+ struct statfs *stbuf)
+{
+ stbuf->f_type = 0xABCD;
+ stbuf->f_bsize = 512;
+ stbuf->f_blocks = 0;
+ stbuf->f_files = v9fs_synth_node_count;
+ stbuf->f_namelen = NAME_MAX;
+ return 0;
+}
+
+static ssize_t v9fs_synth_lgetxattr(FsContext *ctx, V9fsPath *path,
+ const char *name, void *value, size_t size)
+{
+ errno = ENOTSUP;
+ return -1;
+}
+
+static ssize_t v9fs_synth_llistxattr(FsContext *ctx, V9fsPath *path,
+ void *value, size_t size)
+{
+ errno = ENOTSUP;
+ return -1;
+}
+
+static int v9fs_synth_lsetxattr(FsContext *ctx, V9fsPath *path,
+ const char *name, void *value,
+ size_t size, int flags)
+{
+ errno = ENOTSUP;
+ return -1;
+}
+
+static int v9fs_synth_lremovexattr(FsContext *ctx,
+ V9fsPath *path, const char *name)
+{
+ errno = ENOTSUP;
+ return -1;
+}
+
+static int v9fs_synth_name_to_path(FsContext *ctx, V9fsPath *dir_path,
+ const char *name, V9fsPath *target)
+{
+ V9fsSynthNode *node;
+ V9fsSynthNode *dir_node;
+
+ /* "." and ".." are not allowed */
+ if (!strcmp(name, ".") || !strcmp(name, "..")) {
+ errno = EINVAL;
+ return -1;
+
+ }
+ if (!dir_path) {
+ dir_node = &v9fs_synth_root;
+ } else {
+ dir_node = *(V9fsSynthNode **)dir_path->data;
+ }
+ if (!strcmp(name, "/")) {
+ node = dir_node;
+ goto out;
+ }
+ /* search for the name in the childern */
+ pthread_rwlock_rdlock(&v9fs_synth_mutex);
+ QLIST_FOREACH(node, &dir_node->child, sibling) {
+ if (!strcmp(node->name, name)) {
+ break;
+ }
+ }
+ pthread_rwlock_unlock(&v9fs_synth_mutex);
+ if (!node) {
+ errno = ENOENT;
+ return -1;
+ }
+out:
+ /* Copy the node pointer to fid */
+ target->data = g_malloc(sizeof(void *));
+ memcpy(target->data, &node, sizeof(void *));
+ target->size = sizeof(void *);
+ return 0;
+}
+
+static int v9fs_synth_renameat(FsContext *ctx, V9fsPath *olddir,
+ const char *old_name, V9fsPath *newdir,
+ const char *new_name)
+{
+ errno = EPERM;
+ return -1;
+}
+
+static int v9fs_synth_unlinkat(FsContext *ctx, V9fsPath *dir,
+ const char *name, int flags)
+{
+ errno = EPERM;
+ return -1;
+}
+
+static int v9fs_synth_init(FsContext *ctx)
+{
+ pthread_rwlockattr_t rwlockattr;
+
+ QLIST_INIT(&v9fs_synth_root.child);
+ pthread_rwlockattr_init(&rwlockattr);
+ pthread_rwlock_init(&v9fs_synth_mutex, &rwlockattr);
+
+ /* Add "." and ".." entries for root */
+ v9fs_add_dir_node(&v9fs_synth_root, v9fs_synth_root.attr->mode,
+ "..", v9fs_synth_root.attr, v9fs_synth_root.attr->inode);
+ v9fs_add_dir_node(&v9fs_synth_root, v9fs_synth_root.attr->mode,
+ ".", v9fs_synth_root.attr, v9fs_synth_root.attr->inode);
+
+ /* Mark the subsystem is ready for use */
+ v9fs_synth_fs = 1;
+ return 0;
+}
+
+FileOperations synth_ops = {
+ .init = v9fs_synth_init,
+ .lstat = v9fs_synth_lstat,
+ .readlink = v9fs_synth_readlink,
+ .close = v9fs_synth_close,
+ .closedir = v9fs_synth_closedir,
+ .open = v9fs_synth_open,
+ .opendir = v9fs_synth_opendir,
+ .rewinddir = v9fs_synth_rewinddir,
+ .telldir = v9fs_synth_telldir,
+ .readdir_r = v9fs_synth_readdir_r,
+ .seekdir = v9fs_synth_seekdir,
+ .preadv = v9fs_synth_preadv,
+ .pwritev = v9fs_synth_pwritev,
+ .chmod = v9fs_synth_chmod,
+ .mknod = v9fs_synth_mknod,
+ .mkdir = v9fs_synth_mkdir,
+ .fstat = v9fs_synth_fstat,
+ .open2 = v9fs_synth_open2,
+ .symlink = v9fs_synth_symlink,
+ .link = v9fs_synth_link,
+ .truncate = v9fs_synth_truncate,
+ .rename = v9fs_synth_rename,
+ .chown = v9fs_synth_chown,
+ .utimensat = v9fs_synth_utimensat,
+ .remove = v9fs_synth_remove,
+ .fsync = v9fs_synth_fsync,
+ .statfs = v9fs_synth_statfs,
+ .lgetxattr = v9fs_synth_lgetxattr,
+ .llistxattr = v9fs_synth_llistxattr,
+ .lsetxattr = v9fs_synth_lsetxattr,
+ .lremovexattr = v9fs_synth_lremovexattr,
+ .name_to_path = v9fs_synth_name_to_path,
+ .renameat = v9fs_synth_renameat,
+ .unlinkat = v9fs_synth_unlinkat,
+};
diff --git a/hw/9pfs/virtio-9p-synth.h b/hw/9pfs/virtio-9p-synth.h
new file mode 100644
index 0000000000..e03f434633
--- /dev/null
+++ b/hw/9pfs/virtio-9p-synth.h
@@ -0,0 +1,50 @@
+/*
+ * Virtio 9p
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <limits.h>
+
+typedef struct V9fsSynthNode V9fsSynthNode;
+typedef ssize_t (*v9fs_synth_read)(void *buf, int len, off_t offset,
+ void *arg);
+typedef ssize_t (*v9fs_synth_write)(void *buf, int len, off_t offset,
+ void *arg);
+typedef struct V9fsSynthNodeAttr {
+ int mode;
+ int inode;
+ int nlink;
+ v9fs_synth_read read;
+ v9fs_synth_write write;
+} V9fsSynthNodeAttr;
+
+struct V9fsSynthNode {
+ QLIST_HEAD(, V9fsSynthNode) child;
+ QLIST_ENTRY(V9fsSynthNode) sibling;
+ char name[NAME_MAX];
+ V9fsSynthNodeAttr *attr;
+ V9fsSynthNodeAttr actual_attr;
+ void *private;
+ int open_count;
+};
+
+typedef struct V9fsSynthOpenState {
+ off_t offset;
+ V9fsSynthNode *node;
+} V9fsSynthOpenState;
+
+extern int qemu_v9fs_synth_mkdir(V9fsSynthNode *parent, int mode,
+ const char *name, V9fsSynthNode **result);
+extern int qemu_v9fs_synth_add_file(V9fsSynthNode *parent, int mode,
+ const char *name, v9fs_synth_read read,
+ v9fs_synth_write write, void *arg);
diff --git a/hw/9pfs/virtio-9p.h b/hw/9pfs/virtio-9p.h
index 6273b8589a..7f883563d6 100644
--- a/hw/9pfs/virtio-9p.h
+++ b/hw/9pfs/virtio-9p.h
@@ -212,6 +212,12 @@ union V9fsFidOpenState {
int fd;
DIR *dir;
V9fsXattr xattr;
+ /*
+ * private pointer for fs drivers, that
+ * have its own internal representation of
+ * open files.
+ */
+ void *private;
};
struct V9fsFidState
diff --git a/qemu-options.hx b/qemu-options.hx
index 1baa381ab9..8df6165791 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -628,6 +628,15 @@ read-write access is given.
@end table
ETEXI
+DEF("virtfs_synth", 0, QEMU_OPTION_virtfs_synth,
+ "-virtfs_synth Create synthetic file system image\n",
+ QEMU_ARCH_ALL)
+STEXI
+@item -virtfs_synth
+@findex -virtfs_synth
+Create synthetic file system image
+ETEXI
+
DEFHEADING()
DEF("name", HAS_ARG, QEMU_OPTION_name,
diff --git a/vl.c b/vl.c
index c51a7403cb..37a79e588c 100644
--- a/vl.c
+++ b/vl.c
@@ -2717,6 +2717,24 @@ int main(int argc, char **argv, char **envp)
qemu_opt_get(opts, "mount_tag"));
break;
}
+ case QEMU_OPTION_virtfs_synth: {
+ QemuOpts *fsdev;
+ QemuOpts *device;
+
+ fsdev = qemu_opts_create(qemu_find_opts("fsdev"), "v_synth", 1);
+ if (!fsdev) {
+ fprintf(stderr, "duplicate option: %s\n", "virtfs_synth");
+ exit(1);
+ }
+ qemu_opt_set(fsdev, "fsdriver", "synth");
+ qemu_opt_set(fsdev, "path", "/"); /* ignored */
+
+ device = qemu_opts_create(qemu_find_opts("device"), NULL, 0);
+ qemu_opt_set(device, "driver", "virtio-9p-pci");
+ qemu_opt_set(device, "fsdev", "v_synth");
+ qemu_opt_set(device, "mount_tag", "v_synth");
+ break;
+ }
case QEMU_OPTION_serial:
add_device_config(DEV_SERIAL, optarg);
default_serial = 0;