summary refs log tree commit diff stats
path: root/hw/9pfs
diff options
context:
space:
mode:
Diffstat (limited to 'hw/9pfs')
-rw-r--r--hw/9pfs/codir.c16
-rw-r--r--hw/9pfs/cofile.c61
-rw-r--r--hw/9pfs/cofs.c2
-rw-r--r--hw/9pfs/virtio-9p-coth.h9
-rw-r--r--hw/9pfs/virtio-9p-debug.c646
-rw-r--r--hw/9pfs/virtio-9p-debug.h6
-rw-r--r--hw/9pfs/virtio-9p-device.c70
-rw-r--r--hw/9pfs/virtio-9p-handle.c198
-rw-r--r--hw/9pfs/virtio-9p-local.c215
-rw-r--r--hw/9pfs/virtio-9p-synth.c571
-rw-r--r--hw/9pfs/virtio-9p-synth.h50
-rw-r--r--hw/9pfs/virtio-9p.c232
-rw-r--r--hw/9pfs/virtio-9p.h62
13 files changed, 1254 insertions, 884 deletions
diff --git a/hw/9pfs/codir.c b/hw/9pfs/codir.c
index 72732e7c53..9b6d47d91d 100644
--- a/hw/9pfs/codir.c
+++ b/hw/9pfs/codir.c
@@ -29,7 +29,7 @@ int v9fs_co_readdir_r(V9fsPDU *pdu, V9fsFidState *fidp, struct dirent *dent,
     v9fs_co_run_in_worker(
         {
             errno = 0;
-            err = s->ops->readdir_r(&s->ctx, fidp->fs.dir, dent, result);
+            err = s->ops->readdir_r(&s->ctx, &fidp->fs, dent, result);
             if (!*result && errno) {
                 err = -errno;
             } else {
@@ -49,7 +49,7 @@ off_t v9fs_co_telldir(V9fsPDU *pdu, V9fsFidState *fidp)
     }
     v9fs_co_run_in_worker(
         {
-            err = s->ops->telldir(&s->ctx, fidp->fs.dir);
+            err = s->ops->telldir(&s->ctx, &fidp->fs);
             if (err < 0) {
                 err = -errno;
             }
@@ -65,7 +65,7 @@ void v9fs_co_seekdir(V9fsPDU *pdu, V9fsFidState *fidp, off_t offset)
     }
     v9fs_co_run_in_worker(
         {
-            s->ops->seekdir(&s->ctx, fidp->fs.dir, offset);
+            s->ops->seekdir(&s->ctx, &fidp->fs, offset);
         });
 }
 
@@ -77,7 +77,7 @@ void v9fs_co_rewinddir(V9fsPDU *pdu, V9fsFidState *fidp)
     }
     v9fs_co_run_in_worker(
         {
-            s->ops->rewinddir(&s->ctx, fidp->fs.dir);
+            s->ops->rewinddir(&s->ctx, &fidp->fs);
         });
 }
 
@@ -129,8 +129,8 @@ int v9fs_co_opendir(V9fsPDU *pdu, V9fsFidState *fidp)
     v9fs_path_read_lock(s);
     v9fs_co_run_in_worker(
         {
-            fidp->fs.dir = s->ops->opendir(&s->ctx, &fidp->path);
-            if (!fidp->fs.dir) {
+            err = s->ops->opendir(&s->ctx, &fidp->path, &fidp->fs);
+            if (err < 0) {
                 err = -errno;
             } else {
                 err = 0;
@@ -146,7 +146,7 @@ int v9fs_co_opendir(V9fsPDU *pdu, V9fsFidState *fidp)
     return err;
 }
 
-int v9fs_co_closedir(V9fsPDU *pdu, DIR *dir)
+int v9fs_co_closedir(V9fsPDU *pdu, V9fsFidOpenState *fs)
 {
     int err;
     V9fsState *s = pdu->s;
@@ -156,7 +156,7 @@ int v9fs_co_closedir(V9fsPDU *pdu, DIR *dir)
     }
     v9fs_co_run_in_worker(
         {
-            err = s->ops->closedir(&s->ctx, dir);
+            err = s->ops->closedir(&s->ctx, fs);
             if (err < 0) {
                 err = -errno;
             }
diff --git a/hw/9pfs/cofile.c b/hw/9pfs/cofile.c
index 7ad4bec005..586b0382f6 100644
--- a/hw/9pfs/cofile.c
+++ b/hw/9pfs/cofile.c
@@ -17,6 +17,30 @@
 #include "qemu-coroutine.h"
 #include "virtio-9p-coth.h"
 
+int v9fs_co_st_gen(V9fsPDU *pdu, V9fsPath *path, mode_t st_mode,
+                   V9fsStatDotl *v9stat)
+{
+    int err = 0;
+    V9fsState *s = pdu->s;
+
+    if (v9fs_request_cancelled(pdu)) {
+        return -EINTR;
+    }
+    if (s->ctx.exops.get_st_gen) {
+        v9fs_path_read_lock(s);
+        v9fs_co_run_in_worker(
+            {
+                err = s->ctx.exops.get_st_gen(&s->ctx, path, st_mode,
+                                              &v9stat->st_gen);
+                if (err < 0) {
+                    err = -errno;
+                }
+            });
+        v9fs_path_unlock(s);
+    }
+    return err;
+}
+
 int v9fs_co_lstat(V9fsPDU *pdu, V9fsPath *path, struct stat *stbuf)
 {
     int err;
@@ -37,7 +61,7 @@ int v9fs_co_lstat(V9fsPDU *pdu, V9fsPath *path, struct stat *stbuf)
     return err;
 }
 
-int v9fs_co_fstat(V9fsPDU *pdu, int fd, struct stat *stbuf)
+int v9fs_co_fstat(V9fsPDU *pdu, V9fsFidState *fidp, struct stat *stbuf)
 {
     int err;
     V9fsState *s = pdu->s;
@@ -47,7 +71,7 @@ int v9fs_co_fstat(V9fsPDU *pdu, int fd, struct stat *stbuf)
     }
     v9fs_co_run_in_worker(
         {
-            err = s->ops->fstat(&s->ctx, fd, stbuf);
+            err = s->ops->fstat(&s->ctx, &fidp->fs, stbuf);
             if (err < 0) {
                 err = -errno;
             }
@@ -66,8 +90,8 @@ int v9fs_co_open(V9fsPDU *pdu, V9fsFidState *fidp, int flags)
     v9fs_path_read_lock(s);
     v9fs_co_run_in_worker(
         {
-            fidp->fs.fd = s->ops->open(&s->ctx, &fidp->path, flags);
-            if (fidp->fs.fd == -1) {
+            err = s->ops->open(&s->ctx, &fidp->path, flags, &fidp->fs);
+            if (err == -1) {
                 err = -errno;
             } else {
                 err = 0;
@@ -106,9 +130,9 @@ int v9fs_co_open2(V9fsPDU *pdu, V9fsFidState *fidp, V9fsString *name, gid_t gid,
     v9fs_path_read_lock(s);
     v9fs_co_run_in_worker(
         {
-            fidp->fs.fd = s->ops->open2(&s->ctx, &fidp->path,
-                                        name->data, flags, &cred);
-            if (fidp->fs.fd == -1) {
+            err = s->ops->open2(&s->ctx, &fidp->path,
+                                name->data, flags, &cred, &fidp->fs);
+            if (err < 0) {
                 err = -errno;
             } else {
                 v9fs_path_init(&path);
@@ -117,12 +141,12 @@ int v9fs_co_open2(V9fsPDU *pdu, V9fsFidState *fidp, V9fsString *name, gid_t gid,
                     err = s->ops->lstat(&s->ctx, &path, stbuf);
                     if (err < 0) {
                         err = -errno;
-                        s->ops->close(&s->ctx, fidp->fs.fd);
+                        s->ops->close(&s->ctx, &fidp->fs);
                     } else {
                         v9fs_path_copy(&fidp->path, &path);
                     }
                 } else {
-                    s->ops->close(&s->ctx, fidp->fs.fd);
+                    s->ops->close(&s->ctx, &fidp->fs);
                 }
                 v9fs_path_free(&path);
             }
@@ -137,7 +161,7 @@ int v9fs_co_open2(V9fsPDU *pdu, V9fsFidState *fidp, V9fsString *name, gid_t gid,
     return err;
 }
 
-int v9fs_co_close(V9fsPDU *pdu, int fd)
+int v9fs_co_close(V9fsPDU *pdu, V9fsFidOpenState *fs)
 {
     int err;
     V9fsState *s = pdu->s;
@@ -147,7 +171,7 @@ int v9fs_co_close(V9fsPDU *pdu, int fd)
     }
     v9fs_co_run_in_worker(
         {
-            err = s->ops->close(&s->ctx, fd);
+            err = s->ops->close(&s->ctx, fs);
             if (err < 0) {
                 err = -errno;
             }
@@ -160,16 +184,15 @@ int v9fs_co_close(V9fsPDU *pdu, int fd)
 
 int v9fs_co_fsync(V9fsPDU *pdu, V9fsFidState *fidp, int datasync)
 {
-    int fd, err;
+    int err;
     V9fsState *s = pdu->s;
 
     if (v9fs_request_cancelled(pdu)) {
         return -EINTR;
     }
-    fd = fidp->fs.fd;
     v9fs_co_run_in_worker(
         {
-            err = s->ops->fsync(&s->ctx, fd, datasync);
+            err = s->ops->fsync(&s->ctx, &fidp->fs, datasync);
             if (err < 0) {
                 err = -errno;
             }
@@ -202,16 +225,15 @@ int v9fs_co_link(V9fsPDU *pdu, V9fsFidState *oldfid,
 int v9fs_co_pwritev(V9fsPDU *pdu, V9fsFidState *fidp,
                     struct iovec *iov, int iovcnt, int64_t offset)
 {
-    int fd, err;
+    int err;
     V9fsState *s = pdu->s;
 
     if (v9fs_request_cancelled(pdu)) {
         return -EINTR;
     }
-    fd = fidp->fs.fd;
     v9fs_co_run_in_worker(
         {
-            err = s->ops->pwritev(&s->ctx, fd, iov, iovcnt, offset);
+            err = s->ops->pwritev(&s->ctx, &fidp->fs, iov, iovcnt, offset);
             if (err < 0) {
                 err = -errno;
             }
@@ -222,16 +244,15 @@ int v9fs_co_pwritev(V9fsPDU *pdu, V9fsFidState *fidp,
 int v9fs_co_preadv(V9fsPDU *pdu, V9fsFidState *fidp,
                    struct iovec *iov, int iovcnt, int64_t offset)
 {
-    int fd, err;
+    int err;
     V9fsState *s = pdu->s;
 
     if (v9fs_request_cancelled(pdu)) {
         return -EINTR;
     }
-    fd = fidp->fs.fd;
     v9fs_co_run_in_worker(
         {
-            err = s->ops->preadv(&s->ctx, fd, iov, iovcnt, offset);
+            err = s->ops->preadv(&s->ctx, &fidp->fs, iov, iovcnt, offset);
             if (err < 0) {
                 err = -errno;
             }
diff --git a/hw/9pfs/cofs.c b/hw/9pfs/cofs.c
index 68745add1e..83f125bd47 100644
--- a/hw/9pfs/cofs.c
+++ b/hw/9pfs/cofs.c
@@ -323,7 +323,7 @@ int v9fs_co_name_to_path(V9fsPDU *pdu, V9fsPath *dirpath,
     int err;
     V9fsState *s = pdu->s;
 
-    if (s->ctx.flags & PATHNAME_FSCONTEXT) {
+    if (s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT) {
         err = s->ops->name_to_path(&s->ctx, dirpath, name, path);
         if (err < 0) {
             err = -errno;
diff --git a/hw/9pfs/virtio-9p-coth.h b/hw/9pfs/virtio-9p-coth.h
index 4630080e53..c4b74b0221 100644
--- a/hw/9pfs/virtio-9p-coth.h
+++ b/hw/9pfs/virtio-9p-coth.h
@@ -80,7 +80,7 @@ extern int v9fs_co_rename(V9fsPDU *, V9fsPath *, V9fsPath *);
 extern int v9fs_co_unlinkat(V9fsPDU *, V9fsPath *, V9fsString *, int flags);
 extern int v9fs_co_renameat(V9fsPDU *, V9fsPath *, V9fsString *,
                             V9fsPath *, V9fsString *);
-extern int v9fs_co_fstat(V9fsPDU *, int, struct stat *);
+extern int v9fs_co_fstat(V9fsPDU *, V9fsFidState *, struct stat *);
 extern int v9fs_co_opendir(V9fsPDU *, V9fsFidState *);
 extern int v9fs_co_open(V9fsPDU *, V9fsFidState *, int);
 extern int v9fs_co_open2(V9fsPDU *, V9fsFidState *, V9fsString *,
@@ -88,8 +88,8 @@ extern int v9fs_co_open2(V9fsPDU *, V9fsFidState *, V9fsString *,
 extern int v9fs_co_lsetxattr(V9fsPDU *, V9fsPath *, V9fsString *,
                              void *, size_t, int);
 extern int v9fs_co_lremovexattr(V9fsPDU *, V9fsPath *, V9fsString *);
-extern int v9fs_co_closedir(V9fsPDU *, DIR *);
-extern int v9fs_co_close(V9fsPDU *, int);
+extern int v9fs_co_closedir(V9fsPDU *, V9fsFidOpenState *);
+extern int v9fs_co_close(V9fsPDU *, V9fsFidOpenState *);
 extern int v9fs_co_fsync(V9fsPDU *, V9fsFidState *, int);
 extern int v9fs_co_symlink(V9fsPDU *, V9fsFidState *, V9fsString *,
                            const char *, gid_t, struct stat *);
@@ -101,4 +101,7 @@ extern int v9fs_co_preadv(V9fsPDU *, V9fsFidState *,
                           struct iovec *, int, int64_t);
 extern int v9fs_co_name_to_path(V9fsPDU *, V9fsPath *,
                                 const char *, V9fsPath *);
+extern int v9fs_co_st_gen(V9fsPDU *pdu, V9fsPath *path, mode_t,
+                          V9fsStatDotl *v9stat);
+
 #endif
diff --git a/hw/9pfs/virtio-9p-debug.c b/hw/9pfs/virtio-9p-debug.c
deleted file mode 100644
index 96925f04a4..0000000000
--- a/hw/9pfs/virtio-9p-debug.c
+++ /dev/null
@@ -1,646 +0,0 @@
-/*
- * Virtio 9p PDU debug
- *
- * Copyright IBM, Corp. 2010
- *
- * Authors:
- *  Anthony Liguori   <aliguori@us.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 "hw/pc.h"
-#include "virtio-9p.h"
-#include "virtio-9p-debug.h"
-
-#define BUG_ON(cond) assert(!(cond))
-
-static FILE *llogfile;
-
-static struct iovec *get_sg(V9fsPDU *pdu, int rx)
-{
-    if (rx) {
-        return pdu->elem.in_sg;
-    }
-    return pdu->elem.out_sg;
-}
-
-static int get_sg_count(V9fsPDU *pdu, int rx)
-{
-    if (rx) {
-        return pdu->elem.in_num;
-    }
-    return pdu->elem.out_num;
-
-}
-
-static void pprint_int8(V9fsPDU *pdu, int rx, size_t *offsetp,
-                        const char *name)
-{
-    size_t copied;
-    int count = get_sg_count(pdu, rx);
-    size_t offset = *offsetp;
-    struct iovec *sg = get_sg(pdu, rx);
-    int8_t value;
-
-    copied = do_pdu_unpack(&value, sg, count, offset, sizeof(value));
-
-    BUG_ON(copied != sizeof(value));
-    offset += sizeof(value);
-    fprintf(llogfile, "%s=0x%x", name, value);
-    *offsetp = offset;
-}
-
-static void pprint_int16(V9fsPDU *pdu, int rx, size_t *offsetp,
-                        const char *name)
-{
-    size_t copied;
-    int count = get_sg_count(pdu, rx);
-    struct iovec *sg = get_sg(pdu, rx);
-    size_t offset = *offsetp;
-    int16_t value;
-
-
-    copied = do_pdu_unpack(&value, sg, count, offset, sizeof(value));
-
-    BUG_ON(copied != sizeof(value));
-    offset += sizeof(value);
-    fprintf(llogfile, "%s=0x%x", name, value);
-    *offsetp = offset;
-}
-
-static void pprint_int32(V9fsPDU *pdu, int rx, size_t *offsetp,
-                        const char *name)
-{
-    size_t copied;
-    int count = get_sg_count(pdu, rx);
-    struct iovec *sg = get_sg(pdu, rx);
-    size_t offset = *offsetp;
-    int32_t value;
-
-
-    copied = do_pdu_unpack(&value, sg, count, offset, sizeof(value));
-
-    BUG_ON(copied != sizeof(value));
-    offset += sizeof(value);
-    fprintf(llogfile, "%s=0x%x", name, value);
-    *offsetp = offset;
-}
-
-static void pprint_int64(V9fsPDU *pdu, int rx, size_t *offsetp,
-                        const char *name)
-{
-    size_t copied;
-    int count = get_sg_count(pdu, rx);
-    struct iovec *sg = get_sg(pdu, rx);
-    size_t offset = *offsetp;
-    int64_t value;
-
-
-    copied = do_pdu_unpack(&value, sg, count, offset, sizeof(value));
-
-    BUG_ON(copied != sizeof(value));
-    offset += sizeof(value);
-    fprintf(llogfile, "%s=0x%" PRIx64, name, value);
-    *offsetp = offset;
-}
-
-static void pprint_str(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name)
-{
-    int sg_count = get_sg_count(pdu, rx);
-    struct iovec *sg = get_sg(pdu, rx);
-    size_t offset = *offsetp;
-    uint16_t tmp_size, size;
-    size_t result;
-    size_t copied = 0;
-    int i = 0;
-
-    /* get the size */
-    copied = do_pdu_unpack(&tmp_size, sg, sg_count, offset, sizeof(tmp_size));
-    BUG_ON(copied != sizeof(tmp_size));
-    size = le16_to_cpupu(&tmp_size);
-    offset += copied;
-
-    fprintf(llogfile, "%s=", name);
-    for (i = 0; size && i < sg_count; i++) {
-        size_t len;
-        if (offset >= sg[i].iov_len) {
-            /* skip this sg */
-            offset -= sg[i].iov_len;
-            continue;
-        } else {
-            len = MIN(sg[i].iov_len - offset, size);
-            result = fwrite(sg[i].iov_base + offset, 1, len, llogfile);
-            BUG_ON(result != len);
-            size -= len;
-            copied += len;
-            if (size) {
-                offset = 0;
-                continue;
-            }
-        }
-    }
-    *offsetp += copied;
-}
-
-static void pprint_qid(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name)
-{
-    fprintf(llogfile, "%s={", name);
-    pprint_int8(pdu, rx, offsetp, "type");
-    pprint_int32(pdu, rx, offsetp, ", version");
-    pprint_int64(pdu, rx, offsetp, ", path");
-    fprintf(llogfile, "}");
-}
-
-static void pprint_stat(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name)
-{
-    fprintf(llogfile, "%s={", name);
-    pprint_int16(pdu, rx, offsetp, "size");
-    pprint_int16(pdu, rx, offsetp, ", type");
-    pprint_int32(pdu, rx, offsetp, ", dev");
-    pprint_qid(pdu, rx, offsetp, ", qid");
-    pprint_int32(pdu, rx, offsetp, ", mode");
-    pprint_int32(pdu, rx, offsetp, ", atime");
-    pprint_int32(pdu, rx, offsetp, ", mtime");
-    pprint_int64(pdu, rx, offsetp, ", length");
-    pprint_str(pdu, rx, offsetp, ", name");
-    pprint_str(pdu, rx, offsetp, ", uid");
-    pprint_str(pdu, rx, offsetp, ", gid");
-    pprint_str(pdu, rx, offsetp, ", muid");
-    pprint_str(pdu, rx, offsetp, ", extension");
-    pprint_int32(pdu, rx, offsetp, ", uid");
-    pprint_int32(pdu, rx, offsetp, ", gid");
-    pprint_int32(pdu, rx, offsetp, ", muid");
-    fprintf(llogfile, "}");
-}
-
-static void pprint_stat_dotl(V9fsPDU *pdu, int rx, size_t *offsetp,
-                                                  const char *name)
-{
-    fprintf(llogfile, "%s={", name);
-    pprint_qid(pdu, rx, offsetp, "qid");
-    pprint_int32(pdu, rx, offsetp, ", st_mode");
-    pprint_int64(pdu, rx, offsetp, ", st_nlink");
-    pprint_int32(pdu, rx, offsetp, ", st_uid");
-    pprint_int32(pdu, rx, offsetp, ", st_gid");
-    pprint_int64(pdu, rx, offsetp, ", st_rdev");
-    pprint_int64(pdu, rx, offsetp, ", st_size");
-    pprint_int64(pdu, rx, offsetp, ", st_blksize");
-    pprint_int64(pdu, rx, offsetp, ", st_blocks");
-    pprint_int64(pdu, rx, offsetp, ", atime");
-    pprint_int64(pdu, rx, offsetp, ", atime_nsec");
-    pprint_int64(pdu, rx, offsetp, ", mtime");
-    pprint_int64(pdu, rx, offsetp, ", mtime_nsec");
-    pprint_int64(pdu, rx, offsetp, ", ctime");
-    pprint_int64(pdu, rx, offsetp, ", ctime_nsec");
-    fprintf(llogfile, "}");
-}
-
-
-
-static void pprint_strs(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name)
-{
-    int sg_count = get_sg_count(pdu, rx);
-    struct iovec *sg = get_sg(pdu, rx);
-    size_t offset = *offsetp;
-    uint16_t tmp_count, count, i;
-    size_t copied = 0;
-
-    fprintf(llogfile, "%s={", name);
-
-    /* Get the count */
-    copied = do_pdu_unpack(&tmp_count, sg, sg_count, offset, sizeof(tmp_count));
-    BUG_ON(copied != sizeof(tmp_count));
-    count = le16_to_cpupu(&tmp_count);
-    offset += copied;
-
-    for (i = 0; i < count; i++) {
-        char str[512];
-        if (i) {
-            fprintf(llogfile, ", ");
-        }
-        snprintf(str, sizeof(str), "[%d]", i);
-        pprint_str(pdu, rx, &offset, str);
-    }
-
-    fprintf(llogfile, "}");
-
-    *offsetp = offset;
-}
-
-static void pprint_qids(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name)
-{
-    int sg_count = get_sg_count(pdu, rx);
-    struct iovec *sg = get_sg(pdu, rx);
-    size_t offset = *offsetp;
-    uint16_t tmp_count, count, i;
-    size_t copied = 0;
-
-    fprintf(llogfile, "%s={", name);
-
-    copied = do_pdu_unpack(&tmp_count, sg, sg_count, offset, sizeof(tmp_count));
-    BUG_ON(copied != sizeof(tmp_count));
-    count = le16_to_cpupu(&tmp_count);
-    offset += copied;
-
-    for (i = 0; i < count; i++) {
-        char str[512];
-        if (i) {
-            fprintf(llogfile, ", ");
-        }
-        snprintf(str, sizeof(str), "[%d]", i);
-        pprint_qid(pdu, rx, &offset, str);
-    }
-
-    fprintf(llogfile, "}");
-
-    *offsetp = offset;
-}
-
-static void pprint_sg(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name)
-{
-    struct iovec *sg = get_sg(pdu, rx);
-    unsigned int count;
-    int i;
-
-    if (rx) {
-        count = pdu->elem.in_num;
-    } else {
-        count = pdu->elem.out_num;
-    }
-
-    fprintf(llogfile, "%s={", name);
-    for (i = 0; i < count; i++) {
-        if (i) {
-            fprintf(llogfile, ", ");
-        }
-        fprintf(llogfile, "(%p, 0x%zx)", sg[i].iov_base, sg[i].iov_len);
-    }
-    fprintf(llogfile, "}");
-}
-
-/* FIXME: read from a directory fid returns serialized stat_t's */
-#ifdef DEBUG_DATA
-static void pprint_data(V9fsPDU *pdu, int rx, size_t *offsetp, const char *name)
-{
-    struct iovec *sg = get_sg(pdu, rx);
-    size_t offset = *offsetp;
-    unsigned int count;
-    int32_t size;
-    int total, i, j;
-    ssize_t len;
-
-    if (rx) {
-        count = pdu->elem.in_num;
-    } else {
-        count = pdu->elem.out_num;
-    }
-
-    BUG_ON((offset + sizeof(size)) > sg[0].iov_len);
-
-    memcpy(&size, sg[0].iov_base + offset, sizeof(size));
-    offset += sizeof(size);
-
-    fprintf(llogfile, "size: %x\n", size);
-
-    sg[0].iov_base += 11; /* skip header */
-    sg[0].iov_len -= 11;
-
-    total = 0;
-    for (i = 0; i < count; i++) {
-        total += sg[i].iov_len;
-        if (total >= size) {
-            /* trim sg list so writev does the right thing */
-            sg[i].iov_len -= (total - size);
-            i++;
-            break;
-        }
-    }
-
-    fprintf(llogfile, "%s={\"", name);
-    fflush(llogfile);
-    for (j = 0; j < i; j++) {
-        if (j) {
-            fprintf(llogfile, "\", \"");
-            fflush(llogfile);
-        }
-
-        do {
-            len = writev(fileno(llogfile), &sg[j], 1);
-        } while (len == -1 && errno == EINTR);
-        fprintf(llogfile, "len == %ld: %m\n", len);
-        BUG_ON(len != sg[j].iov_len);
-    }
-    fprintf(llogfile, "\"}");
-
-    sg[0].iov_base -= 11;
-    sg[0].iov_len += 11;
-
-}
-#endif
-
-void pprint_pdu(V9fsPDU *pdu)
-{
-    size_t offset = 7;
-
-    if (llogfile == NULL) {
-        llogfile = fopen("/tmp/pdu.log", "w");
-    }
-
-    BUG_ON(!llogfile);
-
-    switch (pdu->id) {
-    case P9_TREADDIR:
-        fprintf(llogfile, "TREADDIR: (");
-        pprint_int32(pdu, 0, &offset, "fid");
-        pprint_int64(pdu, 0, &offset, ", initial offset");
-        pprint_int32(pdu, 0, &offset, ", max count");
-        break;
-    case P9_RREADDIR:
-        fprintf(llogfile, "RREADDIR: (");
-        pprint_int32(pdu, 1, &offset, "count");
-#ifdef DEBUG_DATA
-        pprint_data(pdu, 1, &offset, ", data");
-#endif
-        break;
-    case P9_TMKDIR:
-        fprintf(llogfile, "TMKDIR: (");
-        pprint_int32(pdu, 0, &offset, "fid");
-        pprint_str(pdu, 0, &offset, "name");
-        pprint_int32(pdu, 0, &offset, "mode");
-        pprint_int32(pdu, 0, &offset, "gid");
-        break;
-    case P9_RMKDIR:
-        fprintf(llogfile, "RMKDIR: (");
-        pprint_qid(pdu, 0, &offset, "qid");
-        break;
-    case P9_TVERSION:
-        fprintf(llogfile, "TVERSION: (");
-        pprint_int32(pdu, 0, &offset, "msize");
-        pprint_str(pdu, 0, &offset, ", version");
-        break;
-    case P9_RVERSION:
-        fprintf(llogfile, "RVERSION: (");
-        pprint_int32(pdu, 1, &offset, "msize");
-        pprint_str(pdu, 1, &offset, ", version");
-        break;
-    case P9_TGETATTR:
-        fprintf(llogfile, "TGETATTR: (");
-        pprint_int32(pdu, 0, &offset, "fid");
-        break;
-    case P9_RGETATTR:
-        fprintf(llogfile, "RGETATTR: (");
-        pprint_stat_dotl(pdu, 1, &offset, "getattr");
-        break;
-    case P9_TAUTH:
-        fprintf(llogfile, "TAUTH: (");
-        pprint_int32(pdu, 0, &offset, "afid");
-        pprint_str(pdu, 0, &offset, ", uname");
-        pprint_str(pdu, 0, &offset, ", aname");
-        pprint_int32(pdu, 0, &offset, ", n_uname");
-        break;
-    case P9_RAUTH:
-        fprintf(llogfile, "RAUTH: (");
-        pprint_qid(pdu, 1, &offset, "qid");
-        break;
-    case P9_TATTACH:
-        fprintf(llogfile, "TATTACH: (");
-        pprint_int32(pdu, 0, &offset, "fid");
-        pprint_int32(pdu, 0, &offset, ", afid");
-        pprint_str(pdu, 0, &offset, ", uname");
-        pprint_str(pdu, 0, &offset, ", aname");
-        pprint_int32(pdu, 0, &offset, ", n_uname");
-        break;
-    case P9_RATTACH:
-        fprintf(llogfile, "RATTACH: (");
-        pprint_qid(pdu, 1, &offset, "qid");
-        break;
-    case P9_TERROR:
-        fprintf(llogfile, "TERROR: (");
-        break;
-    case P9_RERROR:
-        fprintf(llogfile, "RERROR: (");
-        pprint_str(pdu, 1, &offset, "ename");
-        pprint_int32(pdu, 1, &offset, ", ecode");
-        break;
-    case P9_TFLUSH:
-        fprintf(llogfile, "TFLUSH: (");
-        pprint_int16(pdu, 0, &offset, "oldtag");
-        break;
-    case P9_RFLUSH:
-        fprintf(llogfile, "RFLUSH: (");
-        break;
-    case P9_TWALK:
-        fprintf(llogfile, "TWALK: (");
-        pprint_int32(pdu, 0, &offset, "fid");
-        pprint_int32(pdu, 0, &offset, ", newfid");
-        pprint_strs(pdu, 0, &offset, ", wnames");
-        break;
-    case P9_RWALK:
-        fprintf(llogfile, "RWALK: (");
-        pprint_qids(pdu, 1, &offset, "wqids");
-        break;
-    case P9_TOPEN:
-        fprintf(llogfile, "TOPEN: (");
-        pprint_int32(pdu, 0, &offset, "fid");
-        pprint_int8(pdu, 0, &offset, ", mode");
-        break;
-    case P9_ROPEN:
-        fprintf(llogfile, "ROPEN: (");
-        pprint_qid(pdu, 1, &offset, "qid");
-        pprint_int32(pdu, 1, &offset, ", iounit");
-        break;
-    case P9_TCREATE:
-        fprintf(llogfile, "TCREATE: (");
-        pprint_int32(pdu, 0, &offset, "fid");
-        pprint_str(pdu, 0, &offset, ", name");
-        pprint_int32(pdu, 0, &offset, ", perm");
-        pprint_int8(pdu, 0, &offset, ", mode");
-        pprint_str(pdu, 0, &offset, ", extension");
-        break;
-    case P9_RCREATE:
-        fprintf(llogfile, "RCREATE: (");
-        pprint_qid(pdu, 1, &offset, "qid");
-        pprint_int32(pdu, 1, &offset, ", iounit");
-        break;
-    case P9_TSYMLINK:
-        fprintf(llogfile, "TSYMLINK: (");
-        pprint_int32(pdu, 0, &offset, "fid");
-        pprint_str(pdu, 0, &offset, ", name");
-        pprint_str(pdu, 0, &offset, ", symname");
-        pprint_int32(pdu, 0, &offset, ", gid");
-        break;
-    case P9_RSYMLINK:
-        fprintf(llogfile, "RSYMLINK: (");
-        pprint_qid(pdu, 1, &offset, "qid");
-        break;
-    case P9_TLCREATE:
-        fprintf(llogfile, "TLCREATE: (");
-        pprint_int32(pdu, 0, &offset, "dfid");
-        pprint_str(pdu, 0, &offset, ", name");
-        pprint_int32(pdu, 0, &offset, ", flags");
-        pprint_int32(pdu, 0, &offset, ", mode");
-        pprint_int32(pdu, 0, &offset, ", gid");
-        break;
-    case P9_RLCREATE:
-        fprintf(llogfile, "RLCREATE: (");
-        pprint_qid(pdu, 1, &offset, "qid");
-        pprint_int32(pdu, 1, &offset, ", iounit");
-        break;
-    case P9_TMKNOD:
-	fprintf(llogfile, "TMKNOD: (");
-        pprint_int32(pdu, 0, &offset, "fid");
-        pprint_str(pdu, 0, &offset, "name");
-        pprint_int32(pdu, 0, &offset, "mode");
-        pprint_int32(pdu, 0, &offset, "major");
-        pprint_int32(pdu, 0, &offset, "minor");
-        pprint_int32(pdu, 0, &offset, "gid");
-        break;
-    case P9_RMKNOD:
-        fprintf(llogfile, "RMKNOD: )");
-        pprint_qid(pdu, 0, &offset, "qid");
-        break;
-    case P9_TREADLINK:
-	fprintf(llogfile, "TREADLINK: (");
-        pprint_int32(pdu, 0, &offset, "fid");
-        break;
-    case P9_RREADLINK:
-	fprintf(llogfile, "RREADLINK: (");
-        pprint_str(pdu, 0, &offset, "target");
-        break;
-    case P9_TREAD:
-        fprintf(llogfile, "TREAD: (");
-        pprint_int32(pdu, 0, &offset, "fid");
-        pprint_int64(pdu, 0, &offset, ", offset");
-        pprint_int32(pdu, 0, &offset, ", count");
-        pprint_sg(pdu, 0, &offset, ", sg");
-        break;
-    case P9_RREAD:
-        fprintf(llogfile, "RREAD: (");
-        pprint_int32(pdu, 1, &offset, "count");
-        pprint_sg(pdu, 1, &offset, ", sg");
-        offset = 7;
-#ifdef DEBUG_DATA
-        pprint_data(pdu, 1, &offset, ", data");
-#endif
-        break;
-    case P9_TWRITE:
-        fprintf(llogfile, "TWRITE: (");
-        pprint_int32(pdu, 0, &offset, "fid");
-        pprint_int64(pdu, 0, &offset, ", offset");
-        pprint_int32(pdu, 0, &offset, ", count");
-        break;
-    case P9_RWRITE:
-        fprintf(llogfile, "RWRITE: (");
-        pprint_int32(pdu, 1, &offset, "count");
-        break;
-    case P9_TCLUNK:
-        fprintf(llogfile, "TCLUNK: (");
-        pprint_int32(pdu, 0, &offset, "fid");
-        break;
-    case P9_RCLUNK:
-        fprintf(llogfile, "RCLUNK: (");
-        break;
-    case P9_TFSYNC:
-        fprintf(llogfile, "TFSYNC: (");
-        pprint_int32(pdu, 0, &offset, "fid");
-        break;
-    case P9_RFSYNC:
-        fprintf(llogfile, "RFSYNC: (");
-        break;
-    case P9_TLINK:
-        fprintf(llogfile, "TLINK: (");
-        pprint_int32(pdu, 0, &offset, "dfid");
-        pprint_int32(pdu, 0, &offset, ", fid");
-        pprint_str(pdu, 0, &offset, ", newpath");
-        break;
-    case P9_RLINK:
-        fprintf(llogfile, "RLINK: (");
-        break;
-    case P9_TREMOVE:
-        fprintf(llogfile, "TREMOVE: (");
-        pprint_int32(pdu, 0, &offset, "fid");
-        break;
-    case P9_RREMOVE:
-        fprintf(llogfile, "RREMOVE: (");
-        break;
-    case P9_TSTAT:
-        fprintf(llogfile, "TSTAT: (");
-        pprint_int32(pdu, 0, &offset, "fid");
-        break;
-    case P9_RSTAT:
-        fprintf(llogfile, "RSTAT: (");
-        offset += 2; /* ignored */
-        pprint_stat(pdu, 1, &offset, "stat");
-        break;
-    case P9_TWSTAT:
-        fprintf(llogfile, "TWSTAT: (");
-        pprint_int32(pdu, 0, &offset, "fid");
-        offset += 2; /* ignored */
-        pprint_stat(pdu, 0, &offset, ", stat");
-        break;
-    case P9_RWSTAT:
-        fprintf(llogfile, "RWSTAT: (");
-        break;
-    case P9_TXATTRWALK:
-        fprintf(llogfile, "TXATTRWALK: (");
-        pprint_int32(pdu, 0, &offset, "fid");
-        pprint_int32(pdu, 0, &offset, ", newfid");
-        pprint_str(pdu, 0, &offset, ", xattr name");
-        break;
-    case P9_RXATTRWALK:
-        fprintf(llogfile, "RXATTRWALK: (");
-        pprint_int64(pdu, 1, &offset, "xattrsize");
-    case P9_TXATTRCREATE:
-        fprintf(llogfile, "TXATTRCREATE: (");
-        pprint_int32(pdu, 0, &offset, "fid");
-        pprint_str(pdu, 0, &offset, ", name");
-        pprint_int64(pdu, 0, &offset, ", xattrsize");
-        pprint_int32(pdu, 0, &offset, ", flags");
-        break;
-    case P9_RXATTRCREATE:
-        fprintf(llogfile, "RXATTRCREATE: (");
-        break;
-    case P9_TLOCK:
-        fprintf(llogfile, "TLOCK: (");
-        pprint_int32(pdu, 0, &offset, "fid");
-        pprint_int8(pdu, 0, &offset, ", type");
-        pprint_int32(pdu, 0, &offset, ", flags");
-        pprint_int64(pdu, 0, &offset, ", start");
-        pprint_int64(pdu, 0, &offset, ", length");
-        pprint_int32(pdu, 0, &offset, ", proc_id");
-        pprint_str(pdu, 0, &offset, ", client_id");
-        break;
-    case P9_RLOCK:
-        fprintf(llogfile, "RLOCK: (");
-        pprint_int8(pdu, 0, &offset, "status");
-        break;
-    case P9_TGETLOCK:
-        fprintf(llogfile, "TGETLOCK: (");
-        pprint_int32(pdu, 0, &offset, "fid");
-        pprint_int8(pdu, 0, &offset, ", type");
-        pprint_int64(pdu, 0, &offset, ", start");
-        pprint_int64(pdu, 0, &offset, ", length");
-        pprint_int32(pdu, 0, &offset, ", proc_id");
-        pprint_str(pdu, 0, &offset, ", client_id");
-        break;
-    case P9_RGETLOCK:
-        fprintf(llogfile, "RGETLOCK: (");
-        pprint_int8(pdu, 0, &offset, "type");
-        pprint_int64(pdu, 0, &offset, ", start");
-        pprint_int64(pdu, 0, &offset, ", length");
-        pprint_int32(pdu, 0, &offset, ", proc_id");
-        pprint_str(pdu, 0, &offset, ", client_id");
-        break;
-    default:
-        fprintf(llogfile, "unknown(%d): (", pdu->id);
-        break;
-    }
-
-    fprintf(llogfile, ")\n");
-    /* Flush the log message out */
-    fflush(llogfile);
-}
diff --git a/hw/9pfs/virtio-9p-debug.h b/hw/9pfs/virtio-9p-debug.h
deleted file mode 100644
index d9a249118d..0000000000
--- a/hw/9pfs/virtio-9p-debug.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef _QEMU_VIRTIO_9P_DEBUG_H
-#define _QEMU_VIRTIO_9P_DEBUG_H
-
-void pprint_pdu(V9fsPDU *pdu);
-
-#endif
diff --git a/hw/9pfs/virtio-9p-device.c b/hw/9pfs/virtio-9p-device.c
index 513e181c82..bba4c54762 100644
--- a/hw/9pfs/virtio-9p-device.c
+++ b/hw/9pfs/virtio-9p-device.c
@@ -49,7 +49,8 @@ VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf)
     V9fsState *s;
     int i, len;
     struct stat stat;
-    FsTypeEntry *fse;
+    FsDriverEntry *fse;
+    V9fsPath path;
 
     s = (V9fsState *)virtio_common_init("virtio-9p",
                                     VIRTIO_ID_9P,
@@ -82,55 +83,33 @@ VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf)
         exit(1);
     }
 
-    if (!strcmp(fse->security_model, "passthrough")) {
-        /* Files on the Fileserver set to client user credentials */
-        s->ctx.fs_sm = SM_PASSTHROUGH;
+    s->ctx.export_flags = fse->export_flags;
+    s->ctx.fs_root = g_strdup(fse->path);
+    s->ctx.exops.get_st_gen = NULL;
+
+    if (fse->export_flags & V9FS_SM_PASSTHROUGH) {
         s->ctx.xops = passthrough_xattr_ops;
-    } else if (!strcmp(fse->security_model, "mapped")) {
-        /* Files on the fileserver are set to QEMU credentials.
-         * Client user credentials are saved in extended attributes.
-         */
-        s->ctx.fs_sm = SM_MAPPED;
+    } else if (fse->export_flags & V9FS_SM_MAPPED) {
         s->ctx.xops = mapped_xattr_ops;
-    } else if (!strcmp(fse->security_model, "none")) {
-        /*
-         * Files on the fileserver are set to QEMU credentials.
-         */
-        s->ctx.fs_sm = SM_NONE;
-        s->ctx.xops = none_xattr_ops;
-    } else {
-        fprintf(stderr, "Default to security_model=none. You may want"
-                " enable advanced security model using "
-                "security option:\n\t security_model=passthrough\n\t "
-                "security_model=mapped\n");
-        s->ctx.fs_sm = SM_NONE;
+    } else if (fse->export_flags & V9FS_SM_NONE) {
         s->ctx.xops = none_xattr_ops;
     }
 
-    if (lstat(fse->path, &stat)) {
-        fprintf(stderr, "share path %s does not exist\n", fse->path);
-        exit(1);
-    } else if (!S_ISDIR(stat.st_mode)) {
-        fprintf(stderr, "share path %s is not a directory\n", fse->path);
-        exit(1);
-    }
-
-    s->ctx.fs_root = g_strdup(fse->path);
     len = strlen(conf->tag);
     if (len > MAX_TAG_LEN) {
-        len = MAX_TAG_LEN;
+        fprintf(stderr, "mount tag '%s' (%d bytes) is longer than "
+                "maximum (%d bytes)", conf->tag, len, MAX_TAG_LEN);
+        exit(1);
     }
     /* s->tag is non-NULL terminated string */
     s->tag = g_malloc(len);
     memcpy(s->tag, conf->tag, len);
     s->tag_len = len;
     s->ctx.uid = -1;
-    s->ctx.flags = 0;
 
     s->ops = fse->ops;
     s->vdev.get_features = virtio_9p_get_features;
-    s->config_size = sizeof(struct virtio_9p_config) +
-                        s->tag_len;
+    s->config_size = sizeof(struct virtio_9p_config) + s->tag_len;
     s->vdev.get_config = virtio_9p_get_config;
     s->fid_list = NULL;
     qemu_co_rwlock_init(&s->rename_lock);
@@ -144,6 +123,27 @@ VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf)
         fprintf(stderr, "worker thread initialization failed\n");
         exit(1);
     }
+
+    /*
+     * Check details of export path, We need to use fs driver
+     * call back to do that. Since we are in the init path, we don't
+     * use co-routines here.
+     */
+    v9fs_path_init(&path);
+    if (s->ops->name_to_path(&s->ctx, NULL, "/", &path) < 0) {
+        fprintf(stderr,
+                "error in converting name to path %s", strerror(errno));
+        exit(1);
+    }
+    if (s->ops->lstat(&s->ctx, &path, &stat)) {
+        fprintf(stderr, "share path %s does not exist\n", fse->path);
+        exit(1);
+    } else if (!S_ISDIR(stat.st_mode)) {
+        fprintf(stderr, "share path %s is not a directory\n", fse->path);
+        exit(1);
+    }
+    v9fs_path_free(&path);
+
     return &s->vdev;
 }
 
@@ -169,6 +169,8 @@ static PCIDeviceInfo virtio_9p_info = {
     .revision  = VIRTIO_PCI_ABI_VERSION,
     .class_id  = 0x2,
     .qdev.props = (Property[]) {
+        DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags,
+                        VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true),
         DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2),
         DEFINE_VIRTIO_COMMON_FEATURES(VirtIOPCIProxy, host_features),
         DEFINE_PROP_STRING("mount_tag", VirtIOPCIProxy, fsconf.tag),
diff --git a/hw/9pfs/virtio-9p-handle.c b/hw/9pfs/virtio-9p-handle.c
index 5c8b5ed471..c38e0e7863 100644
--- a/hw/9pfs/virtio-9p-handle.c
+++ b/hw/9pfs/virtio-9p-handle.c
@@ -21,49 +21,56 @@
 #include <sys/un.h>
 #include <attr/xattr.h>
 #include <unistd.h>
-
-struct handle_data {
-    int mountfd;
-    int handle_bytes;
-};
-
-#if __GLIBC__ <= 2 && __GLIBC_MINOR__ < 14
-struct file_handle {
-        unsigned int handle_bytes;
-        int handle_type;
-        unsigned char handle[0];
-};
+#include <linux/fs.h>
+#ifdef CONFIG_LINUX_MAGIC_H
+#include <linux/magic.h>
 #endif
+#include <sys/ioctl.h>
 
-#ifndef AT_EMPTY_PATH
-#define AT_EMPTY_PATH   0x1000  /* Allow empty relative pathname */
+#ifndef XFS_SUPER_MAGIC
+#define XFS_SUPER_MAGIC  0x58465342
 #endif
-#ifndef O_PATH
-#define O_PATH    010000000
+#ifndef EXT2_SUPER_MAGIC
+#define EXT2_SUPER_MAGIC 0xEF53
 #endif
-
-#ifndef __NR_name_to_handle_at
-#if defined(__i386__)
-#define __NR_name_to_handle_at  341
-#define __NR_open_by_handle_at  342
-#elif defined(__x86_64__)
-#define __NR_name_to_handle_at  303
-#define __NR_open_by_handle_at  304
+#ifndef REISERFS_SUPER_MAGIC
+#define REISERFS_SUPER_MAGIC 0x52654973
 #endif
+#ifndef BTRFS_SUPER_MAGIC
+#define BTRFS_SUPER_MAGIC 0x9123683E
 #endif
 
-#ifdef __NR_name_to_handle_at
+struct handle_data {
+    int mountfd;
+    int handle_bytes;
+};
+
+#ifdef CONFIG_OPEN_BY_HANDLE
 static inline int name_to_handle(int dirfd, const char *name,
                                  struct file_handle *fh, int *mnt_id, int flags)
 {
-    return syscall(__NR_name_to_handle_at, dirfd, name, fh, mnt_id, flags);
+    return name_to_handle_at(dirfd, name, fh, mnt_id, flags);
 }
 
 static inline int open_by_handle(int mountfd, const char *fh, int flags)
 {
-    return syscall(__NR_open_by_handle_at, mountfd, fh, flags);
+    return open_by_handle_at(mountfd, (struct file_handle *)fh, flags);
 }
 #else
+
+struct file_handle {
+    unsigned int handle_bytes;
+    int handle_type;
+    unsigned char handle[0];
+};
+
+#ifndef AT_EMPTY_PATH
+#define AT_EMPTY_PATH   0x1000  /* Allow empty relative pathname */
+#endif
+#ifndef O_PATH
+#define O_PATH    010000000
+#endif
+
 static inline int name_to_handle(int dirfd, const char *name,
                                  struct file_handle *fh, int *mnt_id, int flags)
 {
@@ -126,82 +133,105 @@ static ssize_t handle_readlink(FsContext *fs_ctx, V9fsPath *fs_path,
     return ret;
 }
 
-static int handle_close(FsContext *ctx, int fd)
+static int handle_close(FsContext *ctx, V9fsFidOpenState *fs)
 {
-    return close(fd);
+    return close(fs->fd);
 }
 
-static int handle_closedir(FsContext *ctx, DIR *dir)
+static int handle_closedir(FsContext *ctx, V9fsFidOpenState *fs)
 {
-    return closedir(dir);
+    return closedir(fs->dir);
 }
 
-static int handle_open(FsContext *ctx, V9fsPath *fs_path, int flags)
+static int handle_open(FsContext *ctx, V9fsPath *fs_path,
+                       int flags, V9fsFidOpenState *fs)
 {
     struct handle_data *data = (struct handle_data *)ctx->private;
 
-    return open_by_handle(data->mountfd, fs_path->data, flags);
+    fs->fd = open_by_handle(data->mountfd, fs_path->data, flags);
+    return fs->fd;
 }
 
-static DIR *handle_opendir(FsContext *ctx, V9fsPath *fs_path)
+static int handle_opendir(FsContext *ctx,
+                          V9fsPath *fs_path, V9fsFidOpenState *fs)
 {
-    int fd;
-    fd = handle_open(ctx, fs_path, O_DIRECTORY);
-    if (fd < 0) {
-        return NULL;
+    int ret;
+    ret = handle_open(ctx, fs_path, O_DIRECTORY, fs);
+    if (ret < 0) {
+        return -1;
+    }
+    fs->dir = fdopendir(ret);
+    if (!fs->dir) {
+        return -1;
     }
-    return fdopendir(fd);
+    return 0;
 }
 
-static void handle_rewinddir(FsContext *ctx, DIR *dir)
+static void handle_rewinddir(FsContext *ctx, V9fsFidOpenState *fs)
 {
-    return rewinddir(dir);
+    return rewinddir(fs->dir);
 }
 
-static off_t handle_telldir(FsContext *ctx, DIR *dir)
+static off_t handle_telldir(FsContext *ctx, V9fsFidOpenState *fs)
 {
-    return telldir(dir);
+    return telldir(fs->dir);
 }
 
-static int handle_readdir_r(FsContext *ctx, DIR *dir, struct dirent *entry,
+static int handle_readdir_r(FsContext *ctx, V9fsFidOpenState *fs,
+                            struct dirent *entry,
                             struct dirent **result)
 {
-    return readdir_r(dir, entry, result);
+    return readdir_r(fs->dir, entry, result);
 }
 
-static void handle_seekdir(FsContext *ctx, DIR *dir, off_t off)
+static void handle_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off)
 {
-    return seekdir(dir, off);
+    return seekdir(fs->dir, off);
 }
 
-static ssize_t handle_preadv(FsContext *ctx, int fd, const struct iovec *iov,
+static ssize_t handle_preadv(FsContext *ctx, V9fsFidOpenState *fs,
+                             const struct iovec *iov,
                              int iovcnt, off_t offset)
 {
 #ifdef CONFIG_PREADV
-    return preadv(fd, iov, iovcnt, offset);
+    return preadv(fs->fd, iov, iovcnt, offset);
 #else
-    int err = lseek(fd, offset, SEEK_SET);
+    int err = lseek(fs->fd, offset, SEEK_SET);
     if (err == -1) {
         return err;
     } else {
-        return readv(fd, iov, iovcnt);
+        return readv(fs->fd, iov, iovcnt);
     }
 #endif
 }
 
-static ssize_t handle_pwritev(FsContext *ctx, int fd, const struct iovec *iov,
+static ssize_t handle_pwritev(FsContext *ctx, V9fsFidOpenState *fs,
+                              const struct iovec *iov,
                               int iovcnt, off_t offset)
 {
+    ssize_t ret;
 #ifdef CONFIG_PREADV
-    return pwritev(fd, iov, iovcnt, offset);
+    ret = pwritev(fs->fd, iov, iovcnt, offset);
 #else
-    int err = lseek(fd, offset, SEEK_SET);
+    int err = lseek(fs->fd, offset, SEEK_SET);
     if (err == -1) {
         return err;
     } else {
-        return writev(fd, iov, iovcnt);
+        ret = writev(fs->fd, iov, iovcnt);
     }
 #endif
+#ifdef CONFIG_SYNC_FILE_RANGE
+    if (ret > 0 && ctx->export_flags & V9FS_IMMEDIATE_WRITEOUT) {
+        /*
+         * Initiate a writeback. This is not a data integrity sync.
+         * We want to ensure that we don't leave dirty pages in the cache
+         * after write when writeout=immediate is sepcified.
+         */
+        sync_file_range(fs->fd, offset, ret,
+                        SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE);
+    }
+#endif
+    return ret;
 }
 
 static int handle_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
@@ -254,13 +284,14 @@ static int handle_mkdir(FsContext *fs_ctx, V9fsPath *dir_path,
     return ret;
 }
 
-static int handle_fstat(FsContext *fs_ctx, int fd, struct stat *stbuf)
+static int handle_fstat(FsContext *fs_ctx, V9fsFidOpenState *fs,
+                        struct stat *stbuf)
 {
-    return fstat(fd, stbuf);
+    return fstat(fs->fd, stbuf);
 }
 
 static int handle_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
-                       int flags, FsCred *credp)
+                        int flags, FsCred *credp, V9fsFidOpenState *fs)
 {
     int ret;
     int dirfd, fd;
@@ -276,6 +307,8 @@ static int handle_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
         if (ret < 0) {
             close(fd);
             fd = ret;
+        } else {
+            fs->fd = fd;
         }
     }
     close(dirfd);
@@ -367,7 +400,9 @@ static int handle_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
 static int handle_utimensat(FsContext *ctx, V9fsPath *fs_path,
                             const struct timespec *buf)
 {
-    int fd, ret;
+    int ret;
+#ifdef CONFIG_UTIMENSAT
+    int fd;
     struct handle_data *data = (struct handle_data *)ctx->private;
 
     fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK);
@@ -376,6 +411,10 @@ static int handle_utimensat(FsContext *ctx, V9fsPath *fs_path,
     }
     ret = futimens(fd, buf);
     close(fd);
+#else
+    ret = -1;
+    errno = ENOSYS;
+#endif
     return ret;
 }
 
@@ -385,12 +424,12 @@ static int handle_remove(FsContext *ctx, const char *path)
     return -1;
 }
 
-static int handle_fsync(FsContext *ctx, int fd, int datasync)
+static int handle_fsync(FsContext *ctx, V9fsFidOpenState *fs, int datasync)
 {
     if (datasync) {
-        return qemu_fdatasync(fd);
+        return qemu_fdatasync(fs->fd);
     } else {
-        return fsync(fd);
+        return fsync(fs->fd);
     }
 }
 
@@ -546,16 +585,51 @@ static int handle_unlinkat(FsContext *ctx, V9fsPath *dir,
     return ret;
 }
 
+static int handle_ioc_getversion(FsContext *ctx, V9fsPath *path,
+                                 mode_t st_mode, uint64_t *st_gen)
+{
+    int err;
+    V9fsFidOpenState fid_open;
+
+    /*
+     * Do not try to open special files like device nodes, fifos etc
+     * We can get fd for regular files and directories only
+     */
+    if (!S_ISREG(st_mode) && !S_ISDIR(st_mode)) {
+            return 0;
+    }
+    err = handle_open(ctx, path, O_RDONLY, &fid_open);
+    if (err < 0) {
+        return err;
+    }
+    err = ioctl(fid_open.fd, FS_IOC_GETVERSION, st_gen);
+    handle_close(ctx, &fid_open);
+    return err;
+}
+
 static int handle_init(FsContext *ctx)
 {
     int ret, mnt_id;
+    struct statfs stbuf;
     struct file_handle fh;
     struct handle_data *data = g_malloc(sizeof(struct handle_data));
+
     data->mountfd = open(ctx->fs_root, O_DIRECTORY);
     if (data->mountfd < 0) {
         ret = data->mountfd;
         goto err_out;
     }
+    ret = statfs(ctx->fs_root, &stbuf);
+    if (!ret) {
+        switch (stbuf.f_type) {
+        case EXT2_SUPER_MAGIC:
+        case BTRFS_SUPER_MAGIC:
+        case REISERFS_SUPER_MAGIC:
+        case XFS_SUPER_MAGIC:
+            ctx->exops.get_st_gen = handle_ioc_getversion;
+            break;
+        }
+    }
     memset(&fh, 0, sizeof(struct file_handle));
     ret = name_to_handle(data->mountfd, ".", &fh, &mnt_id, 0);
     if (ret && errno == EOVERFLOW) {
diff --git a/hw/9pfs/virtio-9p-local.c b/hw/9pfs/virtio-9p-local.c
index 9559ff6550..782dc0ab21 100644
--- a/hw/9pfs/virtio-9p-local.c
+++ b/hw/9pfs/virtio-9p-local.c
@@ -20,6 +20,24 @@
 #include <sys/socket.h>
 #include <sys/un.h>
 #include <attr/xattr.h>
+#include <linux/fs.h>
+#ifdef CONFIG_LINUX_MAGIC_H
+#include <linux/magic.h>
+#endif
+#include <sys/ioctl.h>
+
+#ifndef XFS_SUPER_MAGIC
+#define XFS_SUPER_MAGIC  0x58465342
+#endif
+#ifndef EXT2_SUPER_MAGIC
+#define EXT2_SUPER_MAGIC 0xEF53
+#endif
+#ifndef REISERFS_SUPER_MAGIC
+#define REISERFS_SUPER_MAGIC 0x52654973
+#endif
+#ifndef BTRFS_SUPER_MAGIC
+#define BTRFS_SUPER_MAGIC 0x9123683E
+#endif
 
 static int local_lstat(FsContext *fs_ctx, V9fsPath *fs_path, struct stat *stbuf)
 {
@@ -31,7 +49,7 @@ static int local_lstat(FsContext *fs_ctx, V9fsPath *fs_path, struct stat *stbuf)
     if (err) {
         return err;
     }
-    if (fs_ctx->fs_sm == SM_MAPPED) {
+    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
         /* Actual credentials are part of extended attrs */
         uid_t tmp_uid;
         gid_t tmp_gid;
@@ -106,7 +124,7 @@ static int local_post_create_passthrough(FsContext *fs_ctx, const char *path,
          * If we fail to change ownership and if we are
          * using security model none. Ignore the error
          */
-        if (fs_ctx->fs_sm != SM_NONE) {
+        if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) {
             return -1;
         }
     }
@@ -120,7 +138,7 @@ static ssize_t local_readlink(FsContext *fs_ctx, V9fsPath *fs_path,
     char buffer[PATH_MAX];
     char *path = fs_path->data;
 
-    if (fs_ctx->fs_sm == SM_MAPPED) {
+    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
         int fd;
         fd = open(rpath(fs_ctx, path, buffer), O_RDONLY);
         if (fd == -1) {
@@ -131,88 +149,112 @@ static ssize_t local_readlink(FsContext *fs_ctx, V9fsPath *fs_path,
         } while (tsize == -1 && errno == EINTR);
         close(fd);
         return tsize;
-    } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) ||
-               (fs_ctx->fs_sm == SM_NONE)) {
+    } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
+               (fs_ctx->export_flags & V9FS_SM_NONE)) {
         tsize = readlink(rpath(fs_ctx, path, buffer), buf, bufsz);
     }
     return tsize;
 }
 
-static int local_close(FsContext *ctx, int fd)
+static int local_close(FsContext *ctx, V9fsFidOpenState *fs)
 {
-    return close(fd);
+    return close(fs->fd);
 }
 
-static int local_closedir(FsContext *ctx, DIR *dir)
+static int local_closedir(FsContext *ctx, V9fsFidOpenState *fs)
 {
-    return closedir(dir);
+    return closedir(fs->dir);
 }
 
-static int local_open(FsContext *ctx, V9fsPath *fs_path, int flags)
+static int local_open(FsContext *ctx, V9fsPath *fs_path,
+                      int flags, V9fsFidOpenState *fs)
 {
     char buffer[PATH_MAX];
     char *path = fs_path->data;
 
-    return open(rpath(ctx, path, buffer), flags);
+    fs->fd = open(rpath(ctx, path, buffer), flags);
+    return fs->fd;
 }
 
-static DIR *local_opendir(FsContext *ctx, V9fsPath *fs_path)
+static int local_opendir(FsContext *ctx,
+                         V9fsPath *fs_path, V9fsFidOpenState *fs)
 {
     char buffer[PATH_MAX];
     char *path = fs_path->data;
 
-    return opendir(rpath(ctx, path, buffer));
+    fs->dir = opendir(rpath(ctx, path, buffer));
+    if (!fs->dir) {
+        return -1;
+    }
+    return 0;
 }
 
-static void local_rewinddir(FsContext *ctx, DIR *dir)
+static void local_rewinddir(FsContext *ctx, V9fsFidOpenState *fs)
 {
-    return rewinddir(dir);
+    return rewinddir(fs->dir);
 }
 
-static off_t local_telldir(FsContext *ctx, DIR *dir)
+static off_t local_telldir(FsContext *ctx, V9fsFidOpenState *fs)
 {
-    return telldir(dir);
+    return telldir(fs->dir);
 }
 
-static int local_readdir_r(FsContext *ctx, DIR *dir, struct dirent *entry,
+static int local_readdir_r(FsContext *ctx, V9fsFidOpenState *fs,
+                           struct dirent *entry,
                            struct dirent **result)
 {
-    return readdir_r(dir, entry, result);
+    return readdir_r(fs->dir, entry, result);
 }
 
-static void local_seekdir(FsContext *ctx, DIR *dir, off_t off)
+static void local_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off)
 {
-    return seekdir(dir, off);
+    return seekdir(fs->dir, off);
 }
 
-static ssize_t local_preadv(FsContext *ctx, int fd, const struct iovec *iov,
+static ssize_t local_preadv(FsContext *ctx, V9fsFidOpenState *fs,
+                            const struct iovec *iov,
                             int iovcnt, off_t offset)
 {
 #ifdef CONFIG_PREADV
-    return preadv(fd, iov, iovcnt, offset);
+    return preadv(fs->fd, iov, iovcnt, offset);
 #else
-    int err = lseek(fd, offset, SEEK_SET);
+    int err = lseek(fs->fd, offset, SEEK_SET);
     if (err == -1) {
         return err;
     } else {
-        return readv(fd, iov, iovcnt);
+        return readv(fs->fd, iov, iovcnt);
     }
 #endif
 }
 
-static ssize_t local_pwritev(FsContext *ctx, int fd, const struct iovec *iov,
+static ssize_t local_pwritev(FsContext *ctx, V9fsFidOpenState *fs,
+                             const struct iovec *iov,
                              int iovcnt, off_t offset)
 {
+    ssize_t ret
+;
 #ifdef CONFIG_PREADV
-    return pwritev(fd, iov, iovcnt, offset);
+    ret = pwritev(fs->fd, iov, iovcnt, offset);
 #else
-    int err = lseek(fd, offset, SEEK_SET);
+    int err = lseek(fs->fd, offset, SEEK_SET);
     if (err == -1) {
         return err;
     } else {
-        return writev(fd, iov, iovcnt);
+        ret = writev(fs->fd, iov, iovcnt);
     }
 #endif
+#ifdef CONFIG_SYNC_FILE_RANGE
+    if (ret > 0 && ctx->export_flags & V9FS_IMMEDIATE_WRITEOUT) {
+        /*
+         * Initiate a writeback. This is not a data integrity sync.
+         * We want to ensure that we don't leave dirty pages in the cache
+         * after write when writeout=immediate is sepcified.
+         */
+        sync_file_range(fs->fd, offset, ret,
+                        SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE);
+    }
+#endif
+    return ret;
 }
 
 static int local_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
@@ -220,10 +262,10 @@ static int local_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
     char buffer[PATH_MAX];
     char *path = fs_path->data;
 
-    if (fs_ctx->fs_sm == SM_MAPPED) {
+    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
         return local_set_xattr(rpath(fs_ctx, path, buffer), credp);
-    } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) ||
-               (fs_ctx->fs_sm == SM_NONE)) {
+    } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
+               (fs_ctx->export_flags & V9FS_SM_NONE)) {
         return chmod(rpath(fs_ctx, path, buffer), credp->fc_mode);
     }
     return -1;
@@ -243,19 +285,19 @@ static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path,
     path = fullname.data;
 
     /* Determine the security model */
-    if (fs_ctx->fs_sm == SM_MAPPED) {
+    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
         err = mknod(rpath(fs_ctx, path, buffer),
                 SM_LOCAL_MODE_BITS|S_IFREG, 0);
         if (err == -1) {
             goto out;
         }
-        local_set_xattr(rpath(fs_ctx, path, buffer), credp);
+        err = local_set_xattr(rpath(fs_ctx, path, buffer), credp);
         if (err == -1) {
             serrno = errno;
             goto err_end;
         }
-    } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) ||
-               (fs_ctx->fs_sm == SM_NONE)) {
+    } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
+               (fs_ctx->export_flags & V9FS_SM_NONE)) {
         err = mknod(rpath(fs_ctx, path, buffer), credp->fc_mode,
                 credp->fc_rdev);
         if (err == -1) {
@@ -291,7 +333,7 @@ static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path,
     path = fullname.data;
 
     /* Determine the security model */
-    if (fs_ctx->fs_sm == SM_MAPPED) {
+    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
         err = mkdir(rpath(fs_ctx, path, buffer), SM_LOCAL_DIR_MODE_BITS);
         if (err == -1) {
             goto out;
@@ -302,8 +344,8 @@ static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path,
             serrno = errno;
             goto err_end;
         }
-    } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) ||
-               (fs_ctx->fs_sm == SM_NONE)) {
+    } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
+               (fs_ctx->export_flags & V9FS_SM_NONE)) {
         err = mkdir(rpath(fs_ctx, path, buffer), credp->fc_mode);
         if (err == -1) {
             goto out;
@@ -324,30 +366,35 @@ out:
     return err;
 }
 
-static int local_fstat(FsContext *fs_ctx, int fd, struct stat *stbuf)
+static int local_fstat(FsContext *fs_ctx,
+                       V9fsFidOpenState *fs, struct stat *stbuf)
 {
     int err;
-    err = fstat(fd, stbuf);
+    err = fstat(fs->fd, stbuf);
     if (err) {
         return err;
     }
-    if (fs_ctx->fs_sm == SM_MAPPED) {
+    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
         /* Actual credentials are part of extended attrs */
         uid_t tmp_uid;
         gid_t tmp_gid;
         mode_t tmp_mode;
         dev_t tmp_dev;
 
-        if (fgetxattr(fd, "user.virtfs.uid", &tmp_uid, sizeof(uid_t)) > 0) {
+        if (fgetxattr(fs->fd, "user.virtfs.uid",
+                      &tmp_uid, sizeof(uid_t)) > 0) {
             stbuf->st_uid = tmp_uid;
         }
-        if (fgetxattr(fd, "user.virtfs.gid", &tmp_gid, sizeof(gid_t)) > 0) {
+        if (fgetxattr(fs->fd, "user.virtfs.gid",
+                      &tmp_gid, sizeof(gid_t)) > 0) {
             stbuf->st_gid = tmp_gid;
         }
-        if (fgetxattr(fd, "user.virtfs.mode", &tmp_mode, sizeof(mode_t)) > 0) {
+        if (fgetxattr(fs->fd, "user.virtfs.mode",
+                      &tmp_mode, sizeof(mode_t)) > 0) {
             stbuf->st_mode = tmp_mode;
         }
-        if (fgetxattr(fd, "user.virtfs.rdev", &tmp_dev, sizeof(dev_t)) > 0) {
+        if (fgetxattr(fs->fd, "user.virtfs.rdev",
+                      &tmp_dev, sizeof(dev_t)) > 0) {
                 stbuf->st_rdev = tmp_dev;
         }
     }
@@ -355,7 +402,7 @@ static int local_fstat(FsContext *fs_ctx, int fd, struct stat *stbuf)
 }
 
 static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
-                       int flags, FsCred *credp)
+                       int flags, FsCred *credp, V9fsFidOpenState *fs)
 {
     char *path;
     int fd = -1;
@@ -369,7 +416,7 @@ static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
     path = fullname.data;
 
     /* Determine the security model */
-    if (fs_ctx->fs_sm == SM_MAPPED) {
+    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
         fd = open(rpath(fs_ctx, path, buffer), flags, SM_LOCAL_MODE_BITS);
         if (fd == -1) {
             err = fd;
@@ -382,8 +429,8 @@ static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
             serrno = errno;
             goto err_end;
         }
-    } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) ||
-               (fs_ctx->fs_sm == SM_NONE)) {
+    } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
+               (fs_ctx->export_flags & V9FS_SM_NONE)) {
         fd = open(rpath(fs_ctx, path, buffer), flags, credp->fc_mode);
         if (fd == -1) {
             err = fd;
@@ -396,6 +443,7 @@ static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
         }
     }
     err = fd;
+    fs->fd = fd;
     goto out;
 
 err_end:
@@ -422,7 +470,7 @@ static int local_symlink(FsContext *fs_ctx, const char *oldpath,
     newpath = fullname.data;
 
     /* Determine the security model */
-    if (fs_ctx->fs_sm == SM_MAPPED) {
+    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
         int fd;
         ssize_t oldpath_size, write_size;
         fd = open(rpath(fs_ctx, newpath, buffer), O_CREAT|O_EXCL|O_RDWR,
@@ -451,8 +499,8 @@ static int local_symlink(FsContext *fs_ctx, const char *oldpath,
             serrno = errno;
             goto err_end;
         }
-    } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) ||
-               (fs_ctx->fs_sm == SM_NONE)) {
+    } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
+               (fs_ctx->export_flags & V9FS_SM_NONE)) {
         err = symlink(oldpath, rpath(fs_ctx, newpath, buffer));
         if (err) {
             goto out;
@@ -464,7 +512,7 @@ static int local_symlink(FsContext *fs_ctx, const char *oldpath,
              * If we fail to change ownership and if we are
              * using security model none. Ignore the error
              */
-            if (fs_ctx->fs_sm != SM_NONE) {
+            if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) {
                 serrno = errno;
                 goto err_end;
             } else
@@ -519,15 +567,12 @@ static int local_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
     char *path = fs_path->data;
 
     if ((credp->fc_uid == -1 && credp->fc_gid == -1) ||
-            (fs_ctx->fs_sm == SM_PASSTHROUGH)) {
-        return lchown(rpath(fs_ctx, path, buffer), credp->fc_uid,
-                credp->fc_gid);
-    } else if (fs_ctx->fs_sm == SM_MAPPED) {
+        (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
+        (fs_ctx->export_flags & V9FS_SM_NONE)) {
+        return lchown(rpath(fs_ctx, path, buffer),
+                      credp->fc_uid, credp->fc_gid);
+    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
         return local_set_xattr(rpath(fs_ctx, path, buffer), credp);
-    } else if ((fs_ctx->fs_sm == SM_PASSTHROUGH) ||
-               (fs_ctx->fs_sm == SM_NONE)) {
-        return lchown(rpath(fs_ctx, path, buffer), credp->fc_uid,
-                credp->fc_gid);
     }
     return -1;
 }
@@ -548,12 +593,12 @@ static int local_remove(FsContext *ctx, const char *path)
     return remove(rpath(ctx, path, buffer));
 }
 
-static int local_fsync(FsContext *ctx, int fd, int datasync)
+static int local_fsync(FsContext *ctx, V9fsFidOpenState *fs, int datasync)
 {
     if (datasync) {
-        return qemu_fdatasync(fd);
+        return qemu_fdatasync(fs->fd);
     } else {
-        return fsync(fd);
+        return fsync(fs->fd);
     }
 }
 
@@ -645,10 +690,46 @@ static int local_unlinkat(FsContext *ctx, V9fsPath *dir,
     return ret;
 }
 
+static int local_ioc_getversion(FsContext *ctx, V9fsPath *path,
+                                mode_t st_mode, uint64_t *st_gen)
+{
+    int err;
+    V9fsFidOpenState fid_open;
+
+    /*
+     * Do not try to open special files like device nodes, fifos etc
+     * We can get fd for regular files and directories only
+     */
+    if (!S_ISREG(st_mode) && !S_ISDIR(st_mode)) {
+            return 0;
+    }
+    err = local_open(ctx, path, O_RDONLY, &fid_open);
+    if (err < 0) {
+        return err;
+    }
+    err = ioctl(fid_open.fd, FS_IOC_GETVERSION, st_gen);
+    local_close(ctx, &fid_open);
+    return err;
+}
+
 static int local_init(FsContext *ctx)
 {
-    ctx->flags |= PATHNAME_FSCONTEXT;
-    return 0;
+    int err;
+    struct statfs stbuf;
+
+    ctx->export_flags |= V9FS_PATHNAME_FSCONTEXT;
+    err = statfs(ctx->fs_root, &stbuf);
+    if (!err) {
+        switch (stbuf.f_type) {
+        case EXT2_SUPER_MAGIC:
+        case BTRFS_SUPER_MAGIC:
+        case REISERFS_SUPER_MAGIC:
+        case XFS_SUPER_MAGIC:
+            ctx->exops.get_st_gen = local_ioc_getversion;
+            break;
+        }
+    }
+    return err;
 }
 
 FileOperations local_ops = {
diff --git a/hw/9pfs/virtio-9p-synth.c b/hw/9pfs/virtio-9p-synth.c
new file mode 100644
index 0000000000..f573616363
--- /dev/null
+++ b/hw/9pfs/virtio-9p-synth.c
@@ -0,0 +1,571 @@
+/*
+ * 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,
+};
+
+static QemuMutex  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_RCU(&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;
+    }
+    qemu_mutex_lock(&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:
+    qemu_mutex_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;
+    }
+
+    qemu_mutex_lock(&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_RCU(&parent->child, node, sibling);
+    ret = 0;
+err_out:
+    qemu_mutex_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;
+
+    rcu_read_lock();
+    QLIST_FOREACH(node, &dir->child, sibling) {
+        /* This is the off child of the directory */
+        if (i == off) {
+            break;
+        }
+        i++;
+    }
+    rcu_read_unlock();
+    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 */
+    rcu_read_lock();
+    QLIST_FOREACH(node, &dir_node->child, sibling) {
+        if (!strcmp(node->name, name)) {
+            break;
+        }
+    }
+    rcu_read_unlock();
+
+    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)
+{
+    QLIST_INIT(&v9fs_synth_root.child);
+    qemu_mutex_init(&v9fs_synth_mutex);
+
+    /* 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.c b/hw/9pfs/virtio-9p.c
index c01c31aa25..0777ece816 100644
--- a/hw/9pfs/virtio-9p.c
+++ b/hw/9pfs/virtio-9p.c
@@ -17,11 +17,10 @@
 #include "hw/virtio-pci.h"
 #include "virtio-9p.h"
 #include "fsdev/qemu-fsdev.h"
-#include "virtio-9p-debug.h"
 #include "virtio-9p-xattr.h"
 #include "virtio-9p-coth.h"
+#include "trace.h"
 
-int debug_9p_pdu;
 int open_fd_hw;
 int total_open_fd;
 static int open_fd_rc;
@@ -72,6 +71,55 @@ static int omode_to_uflags(int8_t mode)
     return ret;
 }
 
+static int dotl_to_at_flags(int flags)
+{
+    int rflags = 0;
+    if (flags & P9_DOTL_AT_REMOVEDIR) {
+        rflags |= AT_REMOVEDIR;
+    }
+    return rflags;
+}
+
+struct dotl_openflag_map {
+    int dotl_flag;
+    int open_flag;
+};
+
+static int dotl_to_open_flags(int flags)
+{
+    int i;
+    /*
+     * We have same bits for P9_DOTL_READONLY, P9_DOTL_WRONLY
+     * and P9_DOTL_NOACCESS
+     */
+    int oflags = flags & O_ACCMODE;
+
+    struct dotl_openflag_map dotl_oflag_map[] = {
+        { P9_DOTL_CREATE, O_CREAT },
+        { P9_DOTL_EXCL, O_EXCL },
+        { P9_DOTL_NOCTTY , O_NOCTTY },
+        { P9_DOTL_TRUNC, O_TRUNC },
+        { P9_DOTL_APPEND, O_APPEND },
+        { P9_DOTL_NONBLOCK, O_NONBLOCK } ,
+        { P9_DOTL_DSYNC, O_DSYNC },
+        { P9_DOTL_FASYNC, FASYNC },
+        { P9_DOTL_DIRECT, O_DIRECT },
+        { P9_DOTL_LARGEFILE, O_LARGEFILE },
+        { P9_DOTL_DIRECTORY, O_DIRECTORY },
+        { P9_DOTL_NOFOLLOW, O_NOFOLLOW },
+        { P9_DOTL_NOATIME, O_NOATIME },
+        { P9_DOTL_SYNC, O_SYNC },
+    };
+
+    for (i = 0; i < ARRAY_SIZE(dotl_oflag_map); i++) {
+        if (flags & dotl_oflag_map[i].dotl_flag) {
+            oflags |= dotl_oflag_map[i].open_flag;
+        }
+    }
+
+    return oflags;
+}
+
 void cred_init(FsCred *credp)
 {
     credp->fc_uid = -1;
@@ -80,6 +128,21 @@ void cred_init(FsCred *credp)
     credp->fc_rdev = -1;
 }
 
+static int get_dotl_openflags(V9fsState *s, int oflags)
+{
+    int flags;
+    /*
+     * Filter the client open flags
+     */
+    flags = dotl_to_open_flags(oflags);
+    flags &= ~(O_NOCTTY | O_ASYNC | O_CREAT);
+    /*
+     * Ignore direct disk access hint until the server supports it.
+     */
+    flags &= ~O_DIRECT;
+    return flags;
+}
+
 void v9fs_string_init(V9fsString *str)
 {
     str->data = NULL;
@@ -392,11 +455,11 @@ static int free_fid(V9fsPDU *pdu, V9fsFidState *fidp)
     if (fidp->fid_type == P9_FID_FILE) {
         /* If we reclaimed the fd no need to close */
         if (fidp->fs.fd != -1) {
-            retval = v9fs_co_close(pdu, fidp->fs.fd);
+            retval = v9fs_co_close(pdu, &fidp->fs);
         }
     } else if (fidp->fid_type == P9_FID_DIR) {
         if (fidp->fs.dir != NULL) {
-            retval = v9fs_co_closedir(pdu, fidp->fs.dir);
+            retval = v9fs_co_closedir(pdu, &fidp->fs);
         }
     } else if (fidp->fid_type == P9_FID_XATTR) {
         retval = v9fs_xattr_fid_clunk(pdu, fidp);
@@ -504,9 +567,9 @@ void v9fs_reclaim_fd(V9fsPDU *pdu)
         f = reclaim_list;
         reclaim_list = f->rclm_lst;
         if (f->fid_type == P9_FID_FILE) {
-            v9fs_co_close(pdu, f->fs_reclaim.fd);
+            v9fs_co_close(pdu, &f->fs_reclaim);
         } else if (f->fid_type == P9_FID_DIR) {
-            v9fs_co_closedir(pdu, f->fs_reclaim.dir);
+            v9fs_co_closedir(pdu, &f->fs_reclaim);
         }
         f->rclm_lst = NULL;
         /*
@@ -621,9 +684,6 @@ static V9fsPDU *alloc_pdu(V9fsState *s)
 static void free_pdu(V9fsState *s, V9fsPDU *pdu)
 {
     if (pdu) {
-        if (debug_9p_pdu) {
-            pprint_pdu(pdu);
-        }
         /*
          * Cancelled pdu are added back to the freelist
          * by flush request .
@@ -909,6 +969,7 @@ static void complete_pdu(V9fsState *s, V9fsPDU *pdu, ssize_t len)
         if (s->proto_version == V9FS_PROTO_2000L) {
             id = P9_RLERROR;
         }
+        trace_v9fs_rerror(pdu->tag, pdu->id, err); /* Trace ERROR */
     }
 
     /* fill out the header */
@@ -1210,6 +1271,11 @@ static void v9fs_fix_path(V9fsPath *dst, V9fsPath *src, int len)
     dst->size++;
 }
 
+static inline bool is_ro_export(FsContext *ctx)
+{
+    return ctx->export_flags & V9FS_RDONLY;
+}
+
 static void v9fs_version(void *opaque)
 {
     V9fsPDU *pdu = opaque;
@@ -1218,6 +1284,7 @@ static void v9fs_version(void *opaque)
     size_t offset = 7;
 
     pdu_unmarshal(pdu, offset, "ds", &s->msize, &version);
+    trace_v9fs_version(pdu->tag, pdu->id, s->msize, version.data);
 
     if (!strcmp(version.data, "9P2000.u")) {
         s->proto_version = V9FS_PROTO_2000U;
@@ -1228,6 +1295,8 @@ static void v9fs_version(void *opaque)
     }
 
     offset += pdu_marshal(pdu, offset, "ds", s->msize, &version);
+    trace_v9fs_version_return(pdu->tag, pdu->id, s->msize, version.data);
+
     complete_pdu(s, pdu, offset);
 
     v9fs_string_free(&version);
@@ -1246,6 +1315,7 @@ static void v9fs_attach(void *opaque)
     ssize_t err;
 
     pdu_unmarshal(pdu, offset, "ddssd", &fid, &afid, &uname, &aname, &n_uname);
+    trace_v9fs_attach(pdu->tag, pdu->id, fid, afid, uname.data, aname.data);
 
     fidp = alloc_fid(s, fid);
     if (fidp == NULL) {
@@ -1267,6 +1337,8 @@ static void v9fs_attach(void *opaque)
     }
     offset += pdu_marshal(pdu, offset, "Q", &qid);
     err = offset;
+    trace_v9fs_attach_return(pdu->tag, pdu->id,
+                             qid.type, qid.version, qid.path);
 out:
     put_fid(pdu, fidp);
 out_nofid:
@@ -1287,6 +1359,7 @@ static void v9fs_stat(void *opaque)
     V9fsState *s = pdu->s;
 
     pdu_unmarshal(pdu, offset, "d", &fid);
+    trace_v9fs_stat(pdu->tag, pdu->id, fid);
 
     fidp = get_fid(pdu, fid);
     if (fidp == NULL) {
@@ -1303,6 +1376,8 @@ static void v9fs_stat(void *opaque)
     }
     offset += pdu_marshal(pdu, offset, "wS", 0, &v9stat);
     err = offset;
+    trace_v9fs_stat_return(pdu->tag, pdu->id, v9stat.mode,
+                           v9stat.atime, v9stat.mtime, v9stat.length);
     v9fs_stat_free(&v9stat);
 out:
     put_fid(pdu, fidp);
@@ -1323,6 +1398,7 @@ static void v9fs_getattr(void *opaque)
     V9fsState *s = pdu->s;
 
     pdu_unmarshal(pdu, offset, "dq", &fid, &request_mask);
+    trace_v9fs_getattr(pdu->tag, pdu->id, fid, request_mask);
 
     fidp = get_fid(pdu, fid);
     if (fidp == NULL) {
@@ -1338,8 +1414,20 @@ static void v9fs_getattr(void *opaque)
         goto out;
     }
     stat_to_v9stat_dotl(s, &stbuf, &v9stat_dotl);
+
+    /*  fill st_gen if requested and supported by underlying fs */
+    if (request_mask & P9_STATS_GEN) {
+        retval = v9fs_co_st_gen(pdu, &fidp->path, stbuf.st_mode, &v9stat_dotl);
+        if (retval < 0) {
+            goto out;
+        }
+        v9stat_dotl.st_result_mask |= P9_STATS_GEN;
+    }
     retval = offset;
     retval += pdu_marshal(pdu, offset, "A", &v9stat_dotl);
+    trace_v9fs_getattr_return(pdu->tag, pdu->id, v9stat_dotl.st_result_mask,
+                              v9stat_dotl.st_mode, v9stat_dotl.st_uid,
+                              v9stat_dotl.st_gid);
 out:
     put_fid(pdu, fidp);
 out_nofid:
@@ -1470,6 +1558,8 @@ static void v9fs_walk(void *opaque)
     offset += pdu_unmarshal(pdu, offset, "ddw", &fid,
                             &newfid, &nwnames);
 
+    trace_v9fs_walk(pdu->tag, pdu->id, fid, newfid, nwnames);
+
     if (nwnames && nwnames <= P9_MAXWELEM) {
         wnames = g_malloc0(sizeof(wnames[0]) * nwnames);
         qids   = g_malloc0(sizeof(qids[0]) * nwnames);
@@ -1518,6 +1608,7 @@ static void v9fs_walk(void *opaque)
         v9fs_path_copy(&newfidp->path, &path);
     }
     err = v9fs_walk_marshal(pdu, nwnames, qids);
+    trace_v9fs_walk_return(pdu->tag, pdu->id, nwnames, qids);
 out:
     put_fid(pdu, fidp);
     if (newfidp) {
@@ -1560,10 +1651,10 @@ static int32_t get_iounit(V9fsPDU *pdu, V9fsPath *path)
 static void v9fs_open(void *opaque)
 {
     int flags;
-    int iounit;
     int32_t fid;
     int32_t mode;
     V9fsQID qid;
+    int iounit = 0;
     ssize_t err = 0;
     size_t offset = 7;
     struct stat stbuf;
@@ -1576,6 +1667,8 @@ static void v9fs_open(void *opaque)
     } else {
         pdu_unmarshal(pdu, offset, "db", &fid, &mode);
     }
+    trace_v9fs_open(pdu->tag, pdu->id, fid, mode);
+
     fidp = get_fid(pdu, fid);
     if (fidp == NULL) {
         err = -ENOENT;
@@ -1598,13 +1691,18 @@ static void v9fs_open(void *opaque)
         err = offset;
     } else {
         if (s->proto_version == V9FS_PROTO_2000L) {
-            flags = mode;
-            flags &= ~(O_NOCTTY | O_ASYNC | O_CREAT);
-            /* Ignore direct disk access hint until the server supports it. */
-            flags &= ~O_DIRECT;
+            flags = get_dotl_openflags(s, mode);
         } else {
             flags = omode_to_uflags(mode);
         }
+        if (is_ro_export(&s->ctx)) {
+            if (mode & O_WRONLY || mode & O_RDWR ||
+                mode & O_APPEND || mode & O_TRUNC) {
+                err = -EROFS;
+                goto out;
+            }
+            flags |= O_NOATIME;
+        }
         err = v9fs_co_open(pdu, fidp, flags);
         if (err < 0) {
             goto out;
@@ -1622,6 +1720,8 @@ static void v9fs_open(void *opaque)
         offset += pdu_marshal(pdu, offset, "Qd", &qid, iounit);
         err = offset;
     }
+    trace_v9fs_open_return(pdu->tag, pdu->id,
+                           qid.type, qid.version, qid.path, iounit);
 out:
     put_fid(pdu, fidp);
 out_nofid:
@@ -1643,6 +1743,7 @@ static void v9fs_lcreate(void *opaque)
 
     pdu_unmarshal(pdu, offset, "dsddd", &dfid, &name, &flags,
                   &mode, &gid);
+    trace_v9fs_lcreate(pdu->tag, pdu->id, dfid, flags, mode, gid);
 
     fidp = get_fid(pdu, dfid);
     if (fidp == NULL) {
@@ -1650,8 +1751,7 @@ static void v9fs_lcreate(void *opaque)
         goto out_nofid;
     }
 
-    /* Ignore direct disk access hint until the server supports it. */
-    flags &= ~O_DIRECT;
+    flags = get_dotl_openflags(pdu->s, flags);
     err = v9fs_co_open2(pdu, fidp, &name, gid,
                         flags | O_CREAT, mode, &stbuf);
     if (err < 0) {
@@ -1670,6 +1770,8 @@ static void v9fs_lcreate(void *opaque)
     stat_to_qid(&stbuf, &qid);
     offset += pdu_marshal(pdu, offset, "Qd", &qid, iounit);
     err = offset;
+    trace_v9fs_lcreate_return(pdu->tag, pdu->id,
+                              qid.type, qid.version, qid.path, iounit);
 out:
     put_fid(pdu, fidp);
 out_nofid:
@@ -1688,6 +1790,8 @@ static void v9fs_fsync(void *opaque)
     V9fsState *s = pdu->s;
 
     pdu_unmarshal(pdu, offset, "dd", &fid, &datasync);
+    trace_v9fs_fsync(pdu->tag, pdu->id, fid, datasync);
+
     fidp = get_fid(pdu, fid);
     if (fidp == NULL) {
         err = -ENOENT;
@@ -1712,6 +1816,7 @@ static void v9fs_clunk(void *opaque)
     V9fsState *s = pdu->s;
 
     pdu_unmarshal(pdu, offset, "d", &fid);
+    trace_v9fs_clunk(pdu->tag, pdu->id, fid);
 
     fidp = clunk_fid(s, fid);
     if (fidp == NULL) {
@@ -1828,6 +1933,7 @@ static void v9fs_read(void *opaque)
     V9fsState *s = pdu->s;
 
     pdu_unmarshal(pdu, offset, "dqd", &fid, &off, &max_count);
+    trace_v9fs_read(pdu->tag, pdu->id, fid, off, max_count);
 
     fidp = get_fid(pdu, fid);
     if (fidp == NULL) {
@@ -1883,6 +1989,7 @@ static void v9fs_read(void *opaque)
     } else {
         err = -EINVAL;
     }
+    trace_v9fs_read_return(pdu->tag, pdu->id, count, err);
 out:
     put_fid(pdu, fidp);
 out_nofid:
@@ -1970,6 +2077,8 @@ static void v9fs_readdir(void *opaque)
 
     pdu_unmarshal(pdu, offset, "dqd", &fid, &initial_offset, &max_count);
 
+    trace_v9fs_readdir(pdu->tag, pdu->id, fid, initial_offset, max_count);
+
     fidp = get_fid(pdu, fid);
     if (fidp == NULL) {
         retval = -EINVAL;
@@ -1992,6 +2101,7 @@ static void v9fs_readdir(void *opaque)
     retval = offset;
     retval += pdu_marshal(pdu, offset, "d", count);
     retval += count;
+    trace_v9fs_readdir_return(pdu->tag, pdu->id, count, retval);
 out:
     put_fid(pdu, fidp);
 out_nofid:
@@ -2059,6 +2169,7 @@ static void v9fs_write(void *opaque)
     V9fsState *s = pdu->s;
 
     pdu_unmarshal(pdu, offset, "dqdv", &fid, &off, &count, sg, &cnt);
+    trace_v9fs_write(pdu->tag, pdu->id, fid, off, count, cnt);
 
     fidp = get_fid(pdu, fid);
     if (fidp == NULL) {
@@ -2102,6 +2213,7 @@ static void v9fs_write(void *opaque)
     } while (total < count && len > 0);
     offset += pdu_marshal(pdu, offset, "d", total);
     err = offset;
+    trace_v9fs_write_return(pdu->tag, pdu->id, total, err);
 out:
     put_fid(pdu, fidp);
 out_nofid:
@@ -2129,6 +2241,8 @@ static void v9fs_create(void *opaque)
     pdu_unmarshal(pdu, offset, "dsdbs", &fid, &name,
                   &perm, &mode, &extension);
 
+    trace_v9fs_create(pdu->tag, pdu->id, fid, name.data, perm, mode);
+
     fidp = get_fid(pdu, fid);
     if (fidp == NULL) {
         err = -EINVAL;
@@ -2259,6 +2373,8 @@ static void v9fs_create(void *opaque)
     stat_to_qid(&stbuf, &qid);
     offset += pdu_marshal(pdu, offset, "Qd", &qid, iounit);
     err = offset;
+    trace_v9fs_create_return(pdu->tag, pdu->id,
+                             qid.type, qid.version, qid.path, iounit);
 out:
     put_fid(pdu, fidp);
 out_nofid:
@@ -2282,6 +2398,7 @@ static void v9fs_symlink(void *opaque)
     size_t offset = 7;
 
     pdu_unmarshal(pdu, offset, "dssd", &dfid, &name, &symname, &gid);
+    trace_v9fs_symlink(pdu->tag, pdu->id, dfid, name.data, symname.data, gid);
 
     dfidp = get_fid(pdu, dfid);
     if (dfidp == NULL) {
@@ -2295,6 +2412,8 @@ static void v9fs_symlink(void *opaque)
     stat_to_qid(&stbuf, &qid);
     offset += pdu_marshal(pdu, offset, "Q", &qid);
     err = offset;
+    trace_v9fs_symlink_return(pdu->tag, pdu->id,
+                              qid.type, qid.version, qid.path);
 out:
     put_fid(pdu, dfidp);
 out_nofid:
@@ -2312,6 +2431,7 @@ static void v9fs_flush(void *opaque)
     V9fsState *s = pdu->s;
 
     pdu_unmarshal(pdu, offset, "w", &tag);
+    trace_v9fs_flush(pdu->tag, pdu->id, tag);
 
     QLIST_FOREACH(cancel_pdu, &s->active_list, next) {
         if (cancel_pdu->tag == tag) {
@@ -2342,6 +2462,7 @@ static void v9fs_link(void *opaque)
     int err = 0;
 
     pdu_unmarshal(pdu, offset, "dds", &dfid, &oldfid, &name);
+    trace_v9fs_link(pdu->tag, pdu->id, dfid, oldfid, name.data);
 
     dfidp = get_fid(pdu, dfid);
     if (dfidp == NULL) {
@@ -2375,6 +2496,7 @@ static void v9fs_remove(void *opaque)
     V9fsPDU *pdu = opaque;
 
     pdu_unmarshal(pdu, offset, "d", &fid);
+    trace_v9fs_remove(pdu->tag, pdu->id, fid);
 
     fidp = get_fid(pdu, fid);
     if (fidp == NULL) {
@@ -2382,7 +2504,7 @@ static void v9fs_remove(void *opaque)
         goto out_nofid;
     }
     /* if fs driver is not path based, return EOPNOTSUPP */
-    if (!pdu->s->ctx.flags & PATHNAME_FSCONTEXT) {
+    if (!(pdu->s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT)) {
         err = -EOPNOTSUPP;
         goto out_err;
     }
@@ -2417,6 +2539,7 @@ static void v9fs_unlinkat(void *opaque)
     V9fsPDU *pdu = opaque;
 
     pdu_unmarshal(pdu, offset, "dsd", &dfid, &name, &flags);
+    flags = dotl_to_at_flags(flags);
 
     dfidp = get_fid(pdu, dfid);
     if (dfidp == NULL) {
@@ -2528,7 +2651,7 @@ static void v9fs_rename(void *opaque)
     }
     BUG_ON(fidp->fid_type != P9_FID_NONE);
     /* if fs driver is not path based, return EOPNOTSUPP */
-    if (!pdu->s->ctx.flags & PATHNAME_FSCONTEXT) {
+    if (!(pdu->s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT)) {
         err = -EOPNOTSUPP;
         goto out;
     }
@@ -2601,7 +2724,7 @@ static int v9fs_complete_renameat(V9fsPDU *pdu, int32_t olddirfid,
     if (err < 0) {
         goto out;
     }
-    if (s->ctx.flags & PATHNAME_FSCONTEXT) {
+    if (s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT) {
         /* Only for path based fid  we need to do the below fixup */
         v9fs_fix_fid_paths(pdu, &olddirfidp->path, old_name,
                            &newdirfidp->path, new_name);
@@ -2653,6 +2776,8 @@ static void v9fs_wstat(void *opaque)
     V9fsState *s = pdu->s;
 
     pdu_unmarshal(pdu, offset, "dwS", &fid, &unused, &v9stat);
+    trace_v9fs_wstat(pdu->tag, pdu->id, fid,
+                     v9stat.mode, v9stat.atime, v9stat.mtime);
 
     fidp = get_fid(pdu, fid);
     if (fidp == NULL) {
@@ -2821,6 +2946,7 @@ static void v9fs_mknod(void *opaque)
 
     pdu_unmarshal(pdu, offset, "dsdddd", &fid, &name, &mode,
                   &major, &minor, &gid);
+    trace_v9fs_mknod(pdu->tag, pdu->id, fid, mode, major, minor);
 
     fidp = get_fid(pdu, fid);
     if (fidp == NULL) {
@@ -2835,6 +2961,8 @@ static void v9fs_mknod(void *opaque)
     stat_to_qid(&stbuf, &qid);
     err = offset;
     err += pdu_marshal(pdu, offset, "Q", &qid);
+    trace_v9fs_mknod_return(pdu->tag, pdu->id,
+                            qid.type, qid.version, qid.path);
 out:
     put_fid(pdu, fidp);
 out_nofid:
@@ -2865,6 +2993,10 @@ static void v9fs_lock(void *opaque)
     pdu_unmarshal(pdu, offset, "dbdqqds", &fid, &flock->type,
                   &flock->flags, &flock->start, &flock->length,
                   &flock->proc_id, &flock->client_id);
+
+    trace_v9fs_lock(pdu->tag, pdu->id, fid,
+                    flock->type, flock->start, flock->length);
+
     status = P9_LOCK_ERROR;
 
     /* We support only block flag now (that too ignored currently) */
@@ -2877,7 +3009,7 @@ static void v9fs_lock(void *opaque)
         err = -ENOENT;
         goto out_nofid;
     }
-    err = v9fs_co_fstat(pdu, fidp->fs.fd, &stbuf);
+    err = v9fs_co_fstat(pdu, fidp, &stbuf);
     if (err < 0) {
         goto out;
     }
@@ -2887,6 +3019,7 @@ out:
 out_nofid:
     err = offset;
     err += pdu_marshal(pdu, offset, "b", status);
+    trace_v9fs_lock_return(pdu->tag, pdu->id, status);
     complete_pdu(s, pdu, err);
     v9fs_string_free(&flock->client_id);
     g_free(flock);
@@ -2911,20 +3044,25 @@ static void v9fs_getlock(void *opaque)
                   &glock->start, &glock->length, &glock->proc_id,
                   &glock->client_id);
 
+    trace_v9fs_getlock(pdu->tag, pdu->id, fid,
+                       glock->type, glock->start, glock->length);
+
     fidp = get_fid(pdu, fid);
     if (fidp == NULL) {
         err = -ENOENT;
         goto out_nofid;
     }
-    err = v9fs_co_fstat(pdu, fidp->fs.fd, &stbuf);
+    err = v9fs_co_fstat(pdu, fidp, &stbuf);
     if (err < 0) {
         goto out;
     }
-    glock->type = F_UNLCK;
+    glock->type = P9_LOCK_TYPE_UNLCK;
     offset += pdu_marshal(pdu, offset, "bqqds", glock->type,
                           glock->start, glock->length, glock->proc_id,
                           &glock->client_id);
     err = offset;
+    trace_v9fs_getlock_return(pdu->tag, pdu->id, glock->type, glock->start,
+                              glock->length, glock->proc_id);
 out:
     put_fid(pdu, fidp);
 out_nofid:
@@ -2948,6 +3086,8 @@ static void v9fs_mkdir(void *opaque)
 
     pdu_unmarshal(pdu, offset, "dsdd", &fid, &name, &mode, &gid);
 
+    trace_v9fs_mkdir(pdu->tag, pdu->id, fid, name.data, mode, gid);
+
     fidp = get_fid(pdu, fid);
     if (fidp == NULL) {
         err = -ENOENT;
@@ -2960,6 +3100,8 @@ static void v9fs_mkdir(void *opaque)
     stat_to_qid(&stbuf, &qid);
     offset += pdu_marshal(pdu, offset, "Q", &qid);
     err = offset;
+    trace_v9fs_mkdir_return(pdu->tag, pdu->id,
+                            qid.type, qid.version, qid.path, err);
 out:
     put_fid(pdu, fidp);
 out_nofid:
@@ -2980,6 +3122,8 @@ static void v9fs_xattrwalk(void *opaque)
     V9fsState *s = pdu->s;
 
     pdu_unmarshal(pdu, offset, "dds", &fid, &newfid, &name);
+    trace_v9fs_xattrwalk(pdu->tag, pdu->id, fid, newfid, name.data);
+
     file_fidp = get_fid(pdu, fid);
     if (file_fidp == NULL) {
         err = -ENOENT;
@@ -3050,6 +3194,7 @@ static void v9fs_xattrwalk(void *opaque)
         offset += pdu_marshal(pdu, offset, "q", size);
         err = offset;
     }
+    trace_v9fs_xattrwalk_return(pdu->tag, pdu->id, size);
 out:
     put_fid(pdu, file_fidp);
     if (xattr_fidp) {
@@ -3075,6 +3220,7 @@ static void v9fs_xattrcreate(void *opaque)
 
     pdu_unmarshal(pdu, offset, "dsqd",
                   &fid, &name, &size, &flags);
+    trace_v9fs_xattrcreate(pdu->tag, pdu->id, fid, name.data, size, flags);
 
     file_fidp = get_fid(pdu, fid);
     if (file_fidp == NULL) {
@@ -3111,6 +3257,7 @@ static void v9fs_readlink(void *opaque)
     V9fsFidState *fidp;
 
     pdu_unmarshal(pdu, offset, "d", &fid);
+    trace_v9fs_readlink(pdu->tag, pdu->id, fid);
     fidp = get_fid(pdu, fid);
     if (fidp == NULL) {
         err = -ENOENT;
@@ -3124,6 +3271,7 @@ static void v9fs_readlink(void *opaque)
     }
     offset += pdu_marshal(pdu, offset, "s", &target);
     err = offset;
+    trace_v9fs_readlink_return(pdu->tag, pdu->id, target.data);
     v9fs_string_free(&target);
 out:
     put_fid(pdu, fidp);
@@ -3174,20 +3322,54 @@ static void v9fs_op_not_supp(void *opaque)
     complete_pdu(pdu->s, pdu, -EOPNOTSUPP);
 }
 
+static void v9fs_fs_ro(void *opaque)
+{
+    V9fsPDU *pdu = opaque;
+    complete_pdu(pdu->s, pdu, -EROFS);
+}
+
+static inline bool is_read_only_op(V9fsPDU *pdu)
+{
+    switch (pdu->id) {
+    case P9_TREADDIR:
+    case P9_TSTATFS:
+    case P9_TGETATTR:
+    case P9_TXATTRWALK:
+    case P9_TLOCK:
+    case P9_TGETLOCK:
+    case P9_TREADLINK:
+    case P9_TVERSION:
+    case P9_TLOPEN:
+    case P9_TATTACH:
+    case P9_TSTAT:
+    case P9_TWALK:
+    case P9_TCLUNK:
+    case P9_TFSYNC:
+    case P9_TOPEN:
+    case P9_TREAD:
+    case P9_TAUTH:
+    case P9_TFLUSH:
+        return 1;
+    default:
+        return 0;
+    }
+}
+
 static void submit_pdu(V9fsState *s, V9fsPDU *pdu)
 {
     Coroutine *co;
     CoroutineEntry *handler;
 
-    if (debug_9p_pdu) {
-        pprint_pdu(pdu);
-    }
     if (pdu->id >= ARRAY_SIZE(pdu_co_handlers) ||
         (pdu_co_handlers[pdu->id] == NULL)) {
         handler = v9fs_op_not_supp;
     } else {
         handler = pdu_co_handlers[pdu->id];
     }
+
+    if (is_ro_export(&s->ctx) && !is_read_only_op(pdu)) {
+        handler = v9fs_fs_ro;
+    }
     co = qemu_coroutine_create(handler);
     qemu_coroutine_enter(co, pdu);
 }
diff --git a/hw/9pfs/virtio-9p.h b/hw/9pfs/virtio-9p.h
index 60b8a56e57..7f883563d6 100644
--- a/hw/9pfs/virtio-9p.h
+++ b/hw/9pfs/virtio-9p.h
@@ -204,20 +204,29 @@ typedef struct V9fsXattr
     int flags;
 } V9fsXattr;
 
+/*
+ * Filled by fs driver on open and other
+ * calls.
+ */
+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
 {
     int fid_type;
     int32_t fid;
     V9fsPath path;
-    union {
-        int fd;
-        DIR *dir;
-        V9fsXattr xattr;
-    } fs;
-    union {
-        int fd;
-        DIR *dir;
-    } fs_reclaim;
+    V9fsFidOpenState fs;
+    V9fsFidOpenState fs_reclaim;
     int flags;
     int open_flags;
     uid_t uid;
@@ -352,6 +361,35 @@ typedef struct V9fsMkState {
     V9fsString fullname;
 } V9fsMkState;
 
+/* 9p2000.L open flags */
+#define P9_DOTL_RDONLY        00000000
+#define P9_DOTL_WRONLY        00000001
+#define P9_DOTL_RDWR          00000002
+#define P9_DOTL_NOACCESS      00000003
+#define P9_DOTL_CREATE        00000100
+#define P9_DOTL_EXCL          00000200
+#define P9_DOTL_NOCTTY        00000400
+#define P9_DOTL_TRUNC         00001000
+#define P9_DOTL_APPEND        00002000
+#define P9_DOTL_NONBLOCK      00004000
+#define P9_DOTL_DSYNC         00010000
+#define P9_DOTL_FASYNC        00020000
+#define P9_DOTL_DIRECT        00040000
+#define P9_DOTL_LARGEFILE     00100000
+#define P9_DOTL_DIRECTORY     00200000
+#define P9_DOTL_NOFOLLOW      00400000
+#define P9_DOTL_NOATIME       01000000
+#define P9_DOTL_CLOEXEC       02000000
+#define P9_DOTL_SYNC          04000000
+
+/* 9p2000.L at flags */
+#define P9_DOTL_AT_REMOVEDIR         0x200
+
+/* 9P2000.L lock type */
+#define P9_LOCK_TYPE_RDLCK 0
+#define P9_LOCK_TYPE_WRLCK 1
+#define P9_LOCK_TYPE_UNLCK 2
+
 #define P9_LOCK_SUCCESS 0
 #define P9_LOCK_BLOCKED 1
 #define P9_LOCK_ERROR 2
@@ -393,21 +431,21 @@ static inline size_t do_pdu_unpack(void *dst, struct iovec *sg, int sg_count,
 
 static inline void v9fs_path_write_lock(V9fsState *s)
 {
-    if (s->ctx.flags & PATHNAME_FSCONTEXT) {
+    if (s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT) {
         qemu_co_rwlock_wrlock(&s->rename_lock);
     }
 }
 
 static inline void v9fs_path_read_lock(V9fsState *s)
 {
-    if (s->ctx.flags & PATHNAME_FSCONTEXT) {
+    if (s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT) {
         qemu_co_rwlock_rdlock(&s->rename_lock);
     }
 }
 
 static inline void v9fs_path_unlock(V9fsState *s)
 {
-    if (s->ctx.flags & PATHNAME_FSCONTEXT) {
+    if (s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT) {
         qemu_co_rwlock_unlock(&s->rename_lock);
     }
 }