]> git.proxmox.com Git - qemu.git/blobdiff - hw/9pfs/virtio-9p.c
hw/9pfs: Abstract open state of fid to V9fsFidOpenState
[qemu.git] / hw / 9pfs / virtio-9p.c
index add889e7d879416032b2a96a51fa82976b56a96e..0777ece8165ea6b7dd26590329f15d37a817a890 100644 (file)
 #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;
 
 enum {
     Oread   = 0x00,
@@ -69,54 +71,92 @@ static int omode_to_uflags(int8_t mode)
     return ret;
 }
 
-void cred_init(FsCred *credp)
+static int dotl_to_at_flags(int flags)
 {
-    credp->fc_uid = -1;
-    credp->fc_gid = -1;
-    credp->fc_mode = -1;
-    credp->fc_rdev = -1;
+    int rflags = 0;
+    if (flags & P9_DOTL_AT_REMOVEDIR) {
+        rflags |= AT_REMOVEDIR;
+    }
+    return rflags;
 }
 
-static int v9fs_do_lstat(V9fsState *s, V9fsString *path, struct stat *stbuf)
-{
-    return s->ops->lstat(&s->ctx, path->data, stbuf);
-}
+struct dotl_openflag_map {
+    int dotl_flag;
+    int open_flag;
+};
 
-static void v9fs_do_rewinddir(V9fsState *s, DIR *dir)
+static int dotl_to_open_flags(int flags)
 {
-    return s->ops->rewinddir(&s->ctx, dir);
-}
+    int i;
+    /*
+     * We have same bits for P9_DOTL_READONLY, P9_DOTL_WRONLY
+     * and P9_DOTL_NOACCESS
+     */
+    int oflags = flags & O_ACCMODE;
 
-static off_t v9fs_do_telldir(V9fsState *s, DIR *dir)
-{
-    return s->ops->telldir(&s->ctx, dir);
+    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;
 }
 
-static void v9fs_do_seekdir(V9fsState *s, DIR *dir, off_t off)
+void cred_init(FsCred *credp)
 {
-    return s->ops->seekdir(&s->ctx, dir, off);
+    credp->fc_uid = -1;
+    credp->fc_gid = -1;
+    credp->fc_mode = -1;
+    credp->fc_rdev = -1;
 }
 
-static int v9fs_do_preadv(V9fsState *s, int fd, const struct iovec *iov,
-                            int iovcnt, int64_t offset)
+static int get_dotl_openflags(V9fsState *s, int oflags)
 {
-    return s->ops->preadv(&s->ctx, fd, iov, iovcnt, offset);
+    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;
 }
 
-static void v9fs_string_init(V9fsString *str)
+void v9fs_string_init(V9fsString *str)
 {
     str->data = NULL;
     str->size = 0;
 }
 
-static void v9fs_string_free(V9fsString *str)
+void v9fs_string_free(V9fsString *str)
 {
     g_free(str->data);
     str->data = NULL;
     str->size = 0;
 }
 
-static void v9fs_string_null(V9fsString *str)
+void v9fs_string_null(V9fsString *str)
 {
     v9fs_string_free(str);
 }
@@ -215,7 +255,7 @@ alloc_print:
     return vsprintf(*strp, fmt, ap);
 }
 
-static void GCC_FMT_ATTR(2, 3)
+void GCC_FMT_ATTR(2, 3)
 v9fs_string_sprintf(V9fsString *str, const char *fmt, ...)
 {
     va_list ap;
@@ -231,22 +271,54 @@ v9fs_string_sprintf(V9fsString *str, const char *fmt, ...)
     str->size = err;
 }
 
-static void v9fs_string_copy(V9fsString *lhs, V9fsString *rhs)
+void v9fs_string_copy(V9fsString *lhs, V9fsString *rhs)
 {
     v9fs_string_free(lhs);
     v9fs_string_sprintf(lhs, "%s", rhs->data);
 }
 
+void v9fs_path_init(V9fsPath *path)
+{
+    path->data = NULL;
+    path->size = 0;
+}
+
+void v9fs_path_free(V9fsPath *path)
+{
+    g_free(path->data);
+    path->data = NULL;
+    path->size = 0;
+}
+
+void v9fs_path_copy(V9fsPath *lhs, V9fsPath *rhs)
+{
+    v9fs_path_free(lhs);
+    lhs->data = g_malloc(rhs->size);
+    memcpy(lhs->data, rhs->data, rhs->size);
+    lhs->size = rhs->size;
+}
+
+int v9fs_name_to_path(V9fsState *s, V9fsPath *dirpath,
+                      const char *name, V9fsPath *path)
+{
+    int err;
+    err = s->ops->name_to_path(&s->ctx, dirpath, name, path);
+    if (err < 0) {
+        err = -errno;
+    }
+    return err;
+}
+
 /*
  * Return TRUE if s1 is an ancestor of s2.
  *
  * E.g. "a/b" is an ancestor of "a/b/c" but not of "a/bc/d".
  * As a special case, We treat s1 as ancestor of s2 if they are same!
  */
-static int v9fs_path_is_ancestor(V9fsString *s1, V9fsString *s2)
+static int v9fs_path_is_ancestor(V9fsPath *s1, V9fsPath *s2)
 {
-    if (!strncmp(s1->data, s2->data, s1->size)) {
-        if (s2->data[s1->size] == '\0' || s2->data[s1->size] == '/') {
+    if (!strncmp(s1->data, s2->data, s1->size - 1)) {
+        if (s2->data[s1->size - 1] == '\0' || s2->data[s1->size - 1] == '/') {
             return 1;
         }
     }
@@ -258,16 +330,61 @@ static size_t v9fs_string_size(V9fsString *str)
     return str->size;
 }
 
-static V9fsFidState *lookup_fid(V9fsState *s, int32_t fid)
+/*
+ * returns 0 if fid got re-opened, 1 if not, < 0 on error */
+static int v9fs_reopen_fid(V9fsPDU *pdu, V9fsFidState *f)
+{
+    int err = 1;
+    if (f->fid_type == P9_FID_FILE) {
+        if (f->fs.fd == -1) {
+            do {
+                err = v9fs_co_open(pdu, f, f->open_flags);
+            } while (err == -EINTR && !pdu->cancelled);
+        }
+    } else if (f->fid_type == P9_FID_DIR) {
+        if (f->fs.dir == NULL) {
+            do {
+                err = v9fs_co_opendir(pdu, f);
+            } while (err == -EINTR && !pdu->cancelled);
+        }
+    }
+    return err;
+}
+
+static V9fsFidState *get_fid(V9fsPDU *pdu, int32_t fid)
 {
+    int err;
     V9fsFidState *f;
+    V9fsState *s = pdu->s;
 
     for (f = s->fid_list; f; f = f->next) {
+        BUG_ON(f->clunked);
         if (f->fid == fid) {
+            /*
+             * Update the fid ref upfront so that
+             * we don't get reclaimed when we yield
+             * in open later.
+             */
+            f->ref++;
+            /*
+             * check whether we need to reopen the
+             * file. We might have closed the fd
+             * while trying to free up some file
+             * descriptors.
+             */
+            err = v9fs_reopen_fid(pdu, f);
+            if (err < 0) {
+                f->ref--;
+                return NULL;
+            }
+            /*
+             * Mark the fid as referenced so that the LRU
+             * reclaim won't close the file descriptor
+             */
+            f->flags |= FID_REFERENCED;
             return f;
         }
     }
-
     return NULL;
 }
 
@@ -275,23 +392,29 @@ static V9fsFidState *alloc_fid(V9fsState *s, int32_t fid)
 {
     V9fsFidState *f;
 
-    f = lookup_fid(s, fid);
-    if (f) {
-        return NULL;
+    for (f = s->fid_list; f; f = f->next) {
+        /* If fid is already there return NULL */
+        BUG_ON(f->clunked);
+        if (f->fid == fid) {
+            return NULL;
+        }
     }
-
     f = g_malloc0(sizeof(V9fsFidState));
-
     f->fid = fid;
     f->fid_type = P9_FID_NONE;
-
+    f->ref = 1;
+    /*
+     * Mark the fid as referenced so that the LRU
+     * reclaim won't close the file descriptor
+     */
+    f->flags |= FID_REFERENCED;
     f->next = s->fid_list;
     s->fid_list = f;
 
     return f;
 }
 
-static int v9fs_xattr_fid_clunk(V9fsState *s, V9fsFidState *fidp)
+static int v9fs_xattr_fid_clunk(V9fsPDU *pdu, V9fsFidState *fidp)
 {
     int retval = 0;
 
@@ -309,12 +432,12 @@ static int v9fs_xattr_fid_clunk(V9fsState *s, V9fsFidState *fidp)
         goto free_out;
     }
     if (fidp->fs.xattr.len) {
-        retval = v9fs_co_lsetxattr(s, &fidp->path, &fidp->fs.xattr.name,
+        retval = v9fs_co_lsetxattr(pdu, &fidp->path, &fidp->fs.xattr.name,
                                    fidp->fs.xattr.value,
                                    fidp->fs.xattr.len,
                                    fidp->fs.xattr.flags);
     } else {
-        retval = v9fs_co_lremovexattr(s, &fidp->path, &fidp->fs.xattr.name);
+        retval = v9fs_co_lremovexattr(pdu, &fidp->path, &fidp->fs.xattr.name);
     }
 free_out:
     v9fs_string_free(&fidp->fs.xattr.name);
@@ -325,9 +448,41 @@ free_value:
     return retval;
 }
 
-static int free_fid(V9fsState *s, int32_t fid)
+static int free_fid(V9fsPDU *pdu, V9fsFidState *fidp)
 {
     int retval = 0;
+
+    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);
+        }
+    } else if (fidp->fid_type == P9_FID_DIR) {
+        if (fidp->fs.dir != NULL) {
+            retval = v9fs_co_closedir(pdu, &fidp->fs);
+        }
+    } else if (fidp->fid_type == P9_FID_XATTR) {
+        retval = v9fs_xattr_fid_clunk(pdu, fidp);
+    }
+    v9fs_path_free(&fidp->path);
+    g_free(fidp);
+    return retval;
+}
+
+static void put_fid(V9fsPDU *pdu, V9fsFidState *fidp)
+{
+    BUG_ON(!fidp->ref);
+    fidp->ref--;
+    /*
+     * Don't free the fid if it is in reclaim list
+     */
+    if (!fidp->ref && fidp->clunked) {
+        free_fid(pdu, fidp);
+    }
+}
+
+static V9fsFidState *clunk_fid(V9fsState *s, int32_t fid)
+{
     V9fsFidState **fidpp, *fidp;
 
     for (fidpp = &s->fid_list; *fidpp; fidpp = &(*fidpp)->next) {
@@ -335,24 +490,127 @@ static int free_fid(V9fsState *s, int32_t fid)
             break;
         }
     }
-
     if (*fidpp == NULL) {
-        return -ENOENT;
+        return NULL;
     }
-
     fidp = *fidpp;
     *fidpp = fidp->next;
+    fidp->clunked = 1;
+    return fidp;
+}
 
-    if (fidp->fid_type == P9_FID_FILE) {
-        retval = v9fs_co_close(s, fidp);
-    } else if (fidp->fid_type == P9_FID_DIR) {
-        retval = v9fs_co_closedir(s, fidp);
-    } else if (fidp->fid_type == P9_FID_XATTR) {
-        retval = v9fs_xattr_fid_clunk(s, fidp);
+void v9fs_reclaim_fd(V9fsPDU *pdu)
+{
+    int reclaim_count = 0;
+    V9fsState *s = pdu->s;
+    V9fsFidState *f, *reclaim_list = NULL;
+
+    for (f = s->fid_list; f; f = f->next) {
+        /*
+         * Unlink fids cannot be reclaimed. Check
+         * for them and skip them. Also skip fids
+         * currently being operated on.
+         */
+        if (f->ref || f->flags & FID_NON_RECLAIMABLE) {
+            continue;
+        }
+        /*
+         * if it is a recently referenced fid
+         * we leave the fid untouched and clear the
+         * reference bit. We come back to it later
+         * in the next iteration. (a simple LRU without
+         * moving list elements around)
+         */
+        if (f->flags & FID_REFERENCED) {
+            f->flags &= ~FID_REFERENCED;
+            continue;
+        }
+        /*
+         * Add fids to reclaim list.
+         */
+        if (f->fid_type == P9_FID_FILE) {
+            if (f->fs.fd != -1) {
+                /*
+                 * Up the reference count so that
+                 * a clunk request won't free this fid
+                 */
+                f->ref++;
+                f->rclm_lst = reclaim_list;
+                reclaim_list = f;
+                f->fs_reclaim.fd = f->fs.fd;
+                f->fs.fd = -1;
+                reclaim_count++;
+            }
+        } else if (f->fid_type == P9_FID_DIR) {
+            if (f->fs.dir != NULL) {
+                /*
+                 * Up the reference count so that
+                 * a clunk request won't free this fid
+                 */
+                f->ref++;
+                f->rclm_lst = reclaim_list;
+                reclaim_list = f;
+                f->fs_reclaim.dir = f->fs.dir;
+                f->fs.dir = NULL;
+                reclaim_count++;
+            }
+        }
+        if (reclaim_count >= open_fd_rc) {
+            break;
+        }
     }
-    v9fs_string_free(&fidp->path);
-    g_free(fidp);
-    return retval;
+    /*
+     * Now close the fid in reclaim list. Free them if they
+     * are already clunked.
+     */
+    while (reclaim_list) {
+        f = reclaim_list;
+        reclaim_list = f->rclm_lst;
+        if (f->fid_type == P9_FID_FILE) {
+            v9fs_co_close(pdu, &f->fs_reclaim);
+        } else if (f->fid_type == P9_FID_DIR) {
+            v9fs_co_closedir(pdu, &f->fs_reclaim);
+        }
+        f->rclm_lst = NULL;
+        /*
+         * Now drop the fid reference, free it
+         * if clunked.
+         */
+        put_fid(pdu, f);
+    }
+}
+
+static int v9fs_mark_fids_unreclaim(V9fsPDU *pdu, V9fsPath *path)
+{
+    int err;
+    V9fsState *s = pdu->s;
+    V9fsFidState *fidp, head_fid;
+
+    head_fid.next = s->fid_list;
+    for (fidp = s->fid_list; fidp; fidp = fidp->next) {
+        if (fidp->path.size != path->size) {
+            continue;
+        }
+        if (!memcmp(fidp->path.data, path->data, path->size)) {
+            /* Mark the fid non reclaimable. */
+            fidp->flags |= FID_NON_RECLAIMABLE;
+
+            /* reopen the file/dir if already closed */
+            err = v9fs_reopen_fid(pdu, fidp);
+            if (err < 0) {
+                return -1;
+            }
+            /*
+             * Go back to head of fid list because
+             * the list could have got updated when
+             * switched to the worker thread
+             */
+            if (err == 0) {
+                fidp = &head_fid;
+            }
+        }
+    }
+    return 0;
 }
 
 #define P9_QID_TYPE_DIR         0x80
@@ -385,6 +643,7 @@ static void stat_to_qid(const struct stat *stbuf, V9fsQID *qidp)
 {
     size_t size;
 
+    memset(&qidp->path, 0, sizeof(qidp->path));
     size = MIN(sizeof(stbuf->st_ino), sizeof(qidp->path));
     memcpy(&qidp->path, &stbuf->st_ino, size);
     qidp->version = stbuf->st_mtime ^ (stbuf->st_size << 8);
@@ -397,12 +656,12 @@ static void stat_to_qid(const struct stat *stbuf, V9fsQID *qidp)
     }
 }
 
-static int fid_to_qid(V9fsState *s, V9fsFidState *fidp, V9fsQID *qidp)
+static int fid_to_qid(V9fsPDU *pdu, V9fsFidState *fidp, V9fsQID *qidp)
 {
     struct stat stbuf;
     int err;
 
-    err = v9fs_co_lstat(s, &fidp->path, &stbuf);
+    err = v9fs_co_lstat(pdu, &fidp->path, &stbuf);
     if (err < 0) {
         return err;
     }
@@ -415,8 +674,9 @@ static V9fsPDU *alloc_pdu(V9fsState *s)
     V9fsPDU *pdu = NULL;
 
     if (!QLIST_EMPTY(&s->free_list)) {
-       pdu = QLIST_FIRST(&s->free_list);
-       QLIST_REMOVE(pdu, next);
+        pdu = QLIST_FIRST(&s->free_list);
+        QLIST_REMOVE(pdu, next);
+        QLIST_INSERT_HEAD(&s->active_list, pdu, next);
     }
     return pdu;
 }
@@ -424,10 +684,14 @@ 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 .
+         */
+        if (!pdu->cancelled) {
+            QLIST_REMOVE(pdu, next);
+            QLIST_INSERT_HEAD(&s->free_list, pdu, next);
         }
-        QLIST_INSERT_HEAD(&s->free_list, pdu, next);
     }
 }
 
@@ -705,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 */
@@ -720,6 +985,9 @@ static void complete_pdu(V9fsState *s, V9fsPDU *pdu, ssize_t len)
     /* FIXME: we should batch these completions */
     virtio_notify(&s->vdev, s->vq);
 
+    /* Now wakeup anybody waiting in flush for this request */
+    qemu_co_queue_next(&pdu->complete);
+
     free_pdu(s, pdu);
 }
 
@@ -839,7 +1107,7 @@ static uint32_t stat_to_v9mode(const struct stat *stbuf)
     return mode;
 }
 
-static int stat_to_v9stat(V9fsState *s, V9fsString *name,
+static int stat_to_v9stat(V9fsPDU *pdu, V9fsPath *name,
                             const struct stat *stbuf,
                             V9fsStat *v9stat)
 {
@@ -865,7 +1133,7 @@ static int stat_to_v9stat(V9fsState *s, V9fsString *name,
     v9fs_string_null(&v9stat->extension);
 
     if (v9stat->mode & P9_STAT_MODE_SYMLINK) {
-        err = v9fs_co_readlink(s, name, &v9stat->extension);
+        err = v9fs_co_readlink(pdu, name, &v9stat->extension);
         if (err < 0) {
             return err;
         }
@@ -991,13 +1259,21 @@ static void print_sg(struct iovec *sg, int cnt)
     printf("}\n");
 }
 
-static void v9fs_fix_path(V9fsString *dst, V9fsString *src, int len)
+/* Will call this only for path name based fid */
+static void v9fs_fix_path(V9fsPath *dst, V9fsPath *src, int len)
+{
+    V9fsPath str;
+    v9fs_path_init(&str);
+    v9fs_path_copy(&str, dst);
+    v9fs_string_sprintf((V9fsString *)dst, "%s%s", src->data, str.data+len);
+    v9fs_path_free(&str);
+    /* +1 to include terminating NULL */
+    dst->size++;
+}
+
+static inline bool is_ro_export(FsContext *ctx)
 {
-    V9fsString str;
-    v9fs_string_init(&str);
-    v9fs_string_copy(&str, dst);
-    v9fs_string_sprintf(dst, "%s%s", src->data, str.data+len);
-    v9fs_string_free(&str);
+    return ctx->export_flags & V9FS_RDONLY;
 }
 
 static void v9fs_version(void *opaque)
@@ -1008,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;
@@ -1018,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);
@@ -1036,23 +1315,33 @@ 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) {
         err = -EINVAL;
-        goto out;
+        goto out_nofid;
     }
     fidp->uid = n_uname;
-    v9fs_string_sprintf(&fidp->path, "%s", "/");
-    err = fid_to_qid(s, fidp, &qid);
+    err = v9fs_co_name_to_path(pdu, NULL, "/", &fidp->path);
+    if (err < 0) {
+        err = -EINVAL;
+        clunk_fid(s, fid);
+        goto out;
+    }
+    err = fid_to_qid(pdu, fidp, &qid);
     if (err < 0) {
         err = -EINVAL;
-        free_fid(s, fid);
+        clunk_fid(s, fid);
         goto out;
     }
     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:
     complete_pdu(s, pdu, err);
     v9fs_string_free(&uname);
     v9fs_string_free(&aname);
@@ -1070,23 +1359,29 @@ static void v9fs_stat(void *opaque)
     V9fsState *s = pdu->s;
 
     pdu_unmarshal(pdu, offset, "d", &fid);
-    fidp = lookup_fid(s, fid);
+    trace_v9fs_stat(pdu->tag, pdu->id, fid);
+
+    fidp = get_fid(pdu, fid);
     if (fidp == NULL) {
         err = -ENOENT;
-        goto out;
+        goto out_nofid;
     }
-    err = v9fs_co_lstat(s, &fidp->path, &stbuf);
+    err = v9fs_co_lstat(pdu, &fidp->path, &stbuf);
     if (err < 0) {
         goto out;
     }
-    err = stat_to_v9stat(s, &fidp->path, &stbuf, &v9stat);
+    err = stat_to_v9stat(pdu, &fidp->path, &stbuf, &v9stat);
     if (err < 0) {
         goto out;
     }
     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);
+out_nofid:
     complete_pdu(s, pdu, err);
 }
 
@@ -1103,24 +1398,39 @@ 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 = lookup_fid(s, fid);
+    fidp = get_fid(pdu, fid);
     if (fidp == NULL) {
         retval = -ENOENT;
-        goto out;
+        goto out_nofid;
     }
     /*
      * Currently we only support BASIC fields in stat, so there is no
      * need to look at request_mask.
      */
-    retval = v9fs_co_lstat(s, &fidp->path, &stbuf);
+    retval = v9fs_co_lstat(pdu, &fidp->path, &stbuf);
     if (retval < 0) {
         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:
     complete_pdu(s, pdu, retval);
 }
 
@@ -1148,13 +1458,13 @@ static void v9fs_setattr(void *opaque)
 
     pdu_unmarshal(pdu, offset, "dI", &fid, &v9iattr);
 
-    fidp = lookup_fid(s, fid);
+    fidp = get_fid(pdu, fid);
     if (fidp == NULL) {
         err = -EINVAL;
-        goto out;
+        goto out_nofid;
     }
     if (v9iattr.valid & ATTR_MODE) {
-        err = v9fs_co_chmod(s, &fidp->path, v9iattr.mode);
+        err = v9fs_co_chmod(pdu, &fidp->path, v9iattr.mode);
         if (err < 0) {
             goto out;
         }
@@ -1181,7 +1491,7 @@ static void v9fs_setattr(void *opaque)
         } else {
             times[1].tv_nsec = UTIME_OMIT;
         }
-        err = v9fs_co_utimensat(s, &fidp->path, times);
+        err = v9fs_co_utimensat(pdu, &fidp->path, times);
         if (err < 0) {
             goto out;
         }
@@ -1199,20 +1509,22 @@ static void v9fs_setattr(void *opaque)
         if (!(v9iattr.valid & ATTR_GID)) {
             v9iattr.gid = -1;
         }
-        err = v9fs_co_chown(s, &fidp->path, v9iattr.uid,
+        err = v9fs_co_chown(pdu, &fidp->path, v9iattr.uid,
                             v9iattr.gid);
         if (err < 0) {
             goto out;
         }
     }
     if (v9iattr.valid & (ATTR_SIZE)) {
-        err = v9fs_co_truncate(s, &fidp->path, v9iattr.size);
+        err = v9fs_co_truncate(pdu, &fidp->path, v9iattr.size);
         if (err < 0) {
             goto out;
         }
     }
     err = offset;
 out:
+    put_fid(pdu, fidp);
+out_nofid:
     complete_pdu(s, pdu, err);
 }
 
@@ -1232,52 +1544,60 @@ static void v9fs_walk(void *opaque)
     int name_idx;
     V9fsQID *qids = NULL;
     int i, err = 0;
-    V9fsString path;
+    V9fsPath dpath, path;
     uint16_t nwnames;
     struct stat stbuf;
     size_t offset = 7;
     int32_t fid, newfid;
     V9fsString *wnames = NULL;
     V9fsFidState *fidp;
-    V9fsFidState *newfidp;
+    V9fsFidState *newfidp = NULL;;
     V9fsPDU *pdu = opaque;
     V9fsState *s = pdu->s;
 
     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);
         for (i = 0; i < nwnames; i++) {
             offset += pdu_unmarshal(pdu, offset, "s", &wnames[i]);
         }
-
     } else if (nwnames > P9_MAXWELEM) {
         err = -EINVAL;
-        goto out;
+        goto out_nofid;
     }
-    fidp = lookup_fid(s, fid);
+    fidp = get_fid(pdu, fid);
     if (fidp == NULL) {
         err = -ENOENT;
-        goto out;
+        goto out_nofid;
+    }
+    v9fs_path_init(&dpath);
+    v9fs_path_init(&path);
+    /*
+     * Both dpath and path initially poin to fidp.
+     * Needed to handle request with nwnames == 0
+     */
+    v9fs_path_copy(&dpath, &fidp->path);
+    v9fs_path_copy(&path, &fidp->path);
+    for (name_idx = 0; name_idx < nwnames; name_idx++) {
+        err = v9fs_co_name_to_path(pdu, &dpath, wnames[name_idx].data, &path);
+        if (err < 0) {
+            goto out;
+        }
+        err = v9fs_co_lstat(pdu, &path, &stbuf);
+        if (err < 0) {
+            goto out;
+        }
+        stat_to_qid(&stbuf, &qids[name_idx]);
+        v9fs_path_copy(&dpath, &path);
     }
     if (fid == newfid) {
         BUG_ON(fidp->fid_type != P9_FID_NONE);
-        v9fs_string_init(&path);
-        for (name_idx = 0; name_idx < nwnames; name_idx++) {
-            v9fs_string_sprintf(&path, "%s/%s",
-                                fidp->path.data, wnames[name_idx].data);
-            v9fs_string_copy(&fidp->path, &path);
-
-            err = v9fs_co_lstat(s, &fidp->path, &stbuf);
-            if (err < 0) {
-                v9fs_string_free(&path);
-                goto out;
-            }
-            stat_to_qid(&stbuf, &qids[name_idx]);
-        }
-        v9fs_string_free(&path);
+        v9fs_path_copy(&fidp->path, &path);
     } else {
         newfidp = alloc_fid(s, newfid);
         if (newfidp == NULL) {
@@ -1285,24 +1605,18 @@ static void v9fs_walk(void *opaque)
             goto out;
         }
         newfidp->uid = fidp->uid;
-        v9fs_string_init(&path);
-        v9fs_string_copy(&newfidp->path, &fidp->path);
-        for (name_idx = 0; name_idx < nwnames; name_idx++) {
-            v9fs_string_sprintf(&path, "%s/%s", newfidp->path.data,
-                                wnames[name_idx].data);
-            v9fs_string_copy(&newfidp->path, &path);
-            err = v9fs_co_lstat(s, &newfidp->path, &stbuf);
-            if (err < 0) {
-                free_fid(s, newfidp->fid);
-                v9fs_string_free(&path);
-                goto out;
-            }
-            stat_to_qid(&stbuf, &qids[name_idx]);
-        }
-        v9fs_string_free(&path);
+        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) {
+        put_fid(pdu, newfidp);
+    }
+    v9fs_path_free(&dpath);
+    v9fs_path_free(&path);
+out_nofid:
     complete_pdu(s, pdu, err);
     if (nwnames && nwnames <= P9_MAXWELEM) {
         for (name_idx = 0; name_idx < nwnames; name_idx++) {
@@ -1311,18 +1625,20 @@ out:
         g_free(wnames);
         g_free(qids);
     }
+    return;
 }
 
-static int32_t get_iounit(V9fsState *s, V9fsString *name)
+static int32_t get_iounit(V9fsPDU *pdu, V9fsPath *path)
 {
     struct statfs stbuf;
     int32_t iounit = 0;
+    V9fsState *s = pdu->s;
 
     /*
      * iounit should be multiples of f_bsize (host filesystem block size
      * and as well as less than (client msize - P9_IOHDRSZ))
      */
-    if (!v9fs_co_statfs(s, name, &stbuf)) {
+    if (!v9fs_co_statfs(pdu, path, &stbuf)) {
         iounit = stbuf.f_bsize;
         iounit *= (s->msize - P9_IOHDRSZ)/stbuf.f_bsize;
     }
@@ -1335,10 +1651,10 @@ static int32_t get_iounit(V9fsState *s, V9fsString *name)
 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;
@@ -1351,20 +1667,22 @@ static void v9fs_open(void *opaque)
     } else {
         pdu_unmarshal(pdu, offset, "db", &fid, &mode);
     }
-    fidp = lookup_fid(s, fid);
+    trace_v9fs_open(pdu->tag, pdu->id, fid, mode);
+
+    fidp = get_fid(pdu, fid);
     if (fidp == NULL) {
         err = -ENOENT;
-        goto out;
+        goto out_nofid;
     }
     BUG_ON(fidp->fid_type != P9_FID_NONE);
 
-    err = v9fs_co_lstat(s, &fidp->path, &stbuf);
+    err = v9fs_co_lstat(pdu, &fidp->path, &stbuf);
     if (err < 0) {
         goto out;
     }
     stat_to_qid(&stbuf, &qid);
     if (S_ISDIR(stbuf.st_mode)) {
-        err = v9fs_co_opendir(s, fidp);
+        err = v9fs_co_opendir(pdu, fidp);
         if (err < 0) {
             goto out;
         }
@@ -1373,23 +1691,40 @@ 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);
         }
-        err = v9fs_co_open(s, fidp, flags);
+        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;
         }
         fidp->fid_type = P9_FID_FILE;
-        iounit = get_iounit(s, &fidp->path);
+        fidp->open_flags = flags;
+        if (flags & O_EXCL) {
+            /*
+             * We let the host file system do O_EXCL check
+             * We should not reclaim such fd
+             */
+            fidp->flags |= FID_NON_RECLAIMABLE;
+        }
+        iounit = get_iounit(pdu, &fidp->path);
         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:
     complete_pdu(s, pdu, err);
 }
 
@@ -1399,7 +1734,6 @@ static void v9fs_lcreate(void *opaque)
     gid_t gid;
     ssize_t err = 0;
     ssize_t offset = 7;
-    V9fsString fullname;
     V9fsString name;
     V9fsFidState *fidp;
     struct stat stbuf;
@@ -1407,43 +1741,42 @@ static void v9fs_lcreate(void *opaque)
     int32_t iounit;
     V9fsPDU *pdu = opaque;
 
-    v9fs_string_init(&fullname);
     pdu_unmarshal(pdu, offset, "dsddd", &dfid, &name, &flags,
                   &mode, &gid);
+    trace_v9fs_lcreate(pdu->tag, pdu->id, dfid, flags, mode, gid);
 
-    fidp = lookup_fid(pdu->s, dfid);
+    fidp = get_fid(pdu, dfid);
     if (fidp == NULL) {
         err = -ENOENT;
-        goto out;
+        goto out_nofid;
     }
-    v9fs_string_sprintf(&fullname, "%s/%s", fidp->path.data, name.data);
-
-    /* Ignore direct disk access hint until the server supports it. */
-    flags &= ~O_DIRECT;
 
-    err = v9fs_co_open2(pdu->s, fidp, fullname.data, gid, flags, mode);
+    flags = get_dotl_openflags(pdu->s, flags);
+    err = v9fs_co_open2(pdu, fidp, &name, gid,
+                        flags | O_CREAT, mode, &stbuf);
     if (err < 0) {
         goto out;
     }
     fidp->fid_type = P9_FID_FILE;
-    iounit =  get_iounit(pdu->s, &fullname);
-
-    err = v9fs_co_lstat(pdu->s, &fullname, &stbuf);
-    if (err < 0) {
-        fidp->fid_type = P9_FID_NONE;
-        if (fidp->fs.fd > 0) {
-            close(fidp->fs.fd);
-        }
-        goto out;
+    fidp->open_flags = flags;
+    if (flags & O_EXCL) {
+        /*
+         * We let the host file system do O_EXCL check
+         * We should not reclaim such fd
+         */
+        fidp->flags |= FID_NON_RECLAIMABLE;
     }
-    v9fs_string_copy(&fidp->path, &fullname);
+    iounit =  get_iounit(pdu, &fidp->path);
     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:
     complete_pdu(pdu->s, pdu, err);
     v9fs_string_free(&name);
-    v9fs_string_free(&fullname);
 }
 
 static void v9fs_fsync(void *opaque)
@@ -1457,16 +1790,19 @@ static void v9fs_fsync(void *opaque)
     V9fsState *s = pdu->s;
 
     pdu_unmarshal(pdu, offset, "dd", &fid, &datasync);
-    fidp = lookup_fid(s, fid);
+    trace_v9fs_fsync(pdu->tag, pdu->id, fid, datasync);
+
+    fidp = get_fid(pdu, fid);
     if (fidp == NULL) {
         err = -ENOENT;
-        goto out;
+        goto out_nofid;
     }
-    err = v9fs_co_fsync(s, fidp, datasync);
+    err = v9fs_co_fsync(pdu, fidp, datasync);
     if (!err) {
         err = offset;
     }
-out:
+    put_fid(pdu, fidp);
+out_nofid:
     complete_pdu(s, pdu, err);
 }
 
@@ -1475,220 +1811,189 @@ static void v9fs_clunk(void *opaque)
     int err;
     int32_t fid;
     size_t offset = 7;
+    V9fsFidState *fidp;
     V9fsPDU *pdu = opaque;
     V9fsState *s = pdu->s;
 
     pdu_unmarshal(pdu, offset, "d", &fid);
-    err = free_fid(s, fid);
-    if (err < 0) {
-        goto out;
+    trace_v9fs_clunk(pdu->tag, pdu->id, fid);
+
+    fidp = clunk_fid(s, fid);
+    if (fidp == NULL) {
+        err = -ENOENT;
+        goto out_nofid;
     }
+    /*
+     * Bump the ref so that put_fid will
+     * free the fid.
+     */
+    fidp->ref++;
     err = offset;
-out:
+
+    put_fid(pdu, fidp);
+out_nofid:
     complete_pdu(s, pdu, err);
 }
 
-static void v9fs_read_post_readdir(V9fsState *, V9fsReadState *, ssize_t);
-
-static void v9fs_read_post_seekdir(V9fsState *s, V9fsReadState *vs, ssize_t err)
+static int v9fs_xattr_read(V9fsState *s, V9fsPDU *pdu,
+                           V9fsFidState *fidp, int64_t off, int32_t max_count)
 {
-    if (err) {
-        goto out;
-    }
-    vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", vs->count);
-    vs->offset += vs->count;
-    err = vs->offset;
-out:
-    complete_pdu(s, vs->pdu, err);
-    v9fs_stat_free(&vs->v9stat);
-    v9fs_string_free(&vs->name);
-    g_free(vs);
-    return;
-}
+    size_t offset = 7;
+    int read_count;
+    int64_t xattr_len;
 
-static void v9fs_read_post_dir_lstat(V9fsState *s, V9fsReadState *vs,
-                                    ssize_t err)
-{
-    if (err) {
-        err = -errno;
-        goto out;
-    }
-    err = stat_to_v9stat(s, &vs->name, &vs->stbuf, &vs->v9stat);
-    if (err) {
-        goto out;
+    xattr_len = fidp->fs.xattr.len;
+    read_count = xattr_len - off;
+    if (read_count > max_count) {
+        read_count = max_count;
+    } else if (read_count < 0) {
+        /*
+         * read beyond XATTR value
+         */
+        read_count = 0;
     }
-
-    vs->len = pdu_marshal(vs->pdu, vs->offset + 4 + vs->count, "S",
-                            &vs->v9stat);
-    if ((vs->len != (vs->v9stat.size + 2)) ||
-            ((vs->count + vs->len) > vs->max_count)) {
-        v9fs_do_seekdir(s, vs->fidp->fs.dir, vs->dir_pos);
-        v9fs_read_post_seekdir(s, vs, err);
-        return;
-    }
-    vs->count += vs->len;
-    v9fs_stat_free(&vs->v9stat);
-    v9fs_string_free(&vs->name);
-    vs->dir_pos = vs->dent->d_off;
-    v9fs_co_readdir(s, vs->fidp, &vs->dent);
-    v9fs_read_post_readdir(s, vs, err);
-    return;
-out:
-    v9fs_do_seekdir(s, vs->fidp->fs.dir, vs->dir_pos);
-    v9fs_read_post_seekdir(s, vs, err);
-    return;
-
+    offset += pdu_marshal(pdu, offset, "d", read_count);
+    offset += pdu_pack(pdu, offset,
+                       ((char *)fidp->fs.xattr.value) + off,
+                       read_count);
+    return offset;
 }
 
-static void v9fs_read_post_readdir(V9fsState *s, V9fsReadState *vs, ssize_t err)
+static int v9fs_do_readdir_with_stat(V9fsPDU *pdu,
+                                     V9fsFidState *fidp, int32_t max_count)
 {
-    if (vs->dent) {
-        memset(&vs->v9stat, 0, sizeof(vs->v9stat));
-        v9fs_string_init(&vs->name);
-        v9fs_string_sprintf(&vs->name, "%s/%s", vs->fidp->path.data,
-                            vs->dent->d_name);
-        err = v9fs_do_lstat(s, &vs->name, &vs->stbuf);
-        v9fs_read_post_dir_lstat(s, vs, err);
-        return;
-    }
+    V9fsPath path;
+    V9fsStat v9stat;
+    int len, err = 0;
+    int32_t count = 0;
+    struct stat stbuf;
+    off_t saved_dir_pos;
+    struct dirent *dent, *result;
 
-    vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", vs->count);
-    vs->offset += vs->count;
-    err = vs->offset;
-    complete_pdu(s, vs->pdu, err);
-    g_free(vs);
-    return;
-}
+    /* save the directory position */
+    saved_dir_pos = v9fs_co_telldir(pdu, fidp);
+    if (saved_dir_pos < 0) {
+        return saved_dir_pos;
+    }
 
-static void v9fs_read_post_telldir(V9fsState *s, V9fsReadState *vs, ssize_t err)
-{
-    v9fs_co_readdir(s, vs->fidp, &vs->dent);
-    v9fs_read_post_readdir(s, vs, err);
-    return;
-}
+    dent = g_malloc(sizeof(struct dirent));
 
-static void v9fs_read_post_rewinddir(V9fsState *s, V9fsReadState *vs,
-                                       ssize_t err)
-{
-    vs->dir_pos = v9fs_do_telldir(s, vs->fidp->fs.dir);
-    v9fs_read_post_telldir(s, vs, err);
-    return;
-}
-
-static void v9fs_read_post_preadv(V9fsState *s, V9fsReadState *vs, ssize_t err)
-{
-    if (err  < 0) {
-        /* IO error return the error */
-        err = -errno;
-        goto out;
-    }
-    vs->total += vs->len;
-    vs->sg = adjust_sg(vs->sg, vs->len, &vs->cnt);
-    if (vs->total < vs->count && vs->len > 0) {
-        do {
-            if (0) {
-                print_sg(vs->sg, vs->cnt);
-            }
-            vs->len = v9fs_do_preadv(s, vs->fidp->fs.fd, vs->sg, vs->cnt,
-                      vs->off);
-            if (vs->len > 0) {
-                vs->off += vs->len;
-            }
-        } while (vs->len == -1 && errno == EINTR);
-        if (vs->len == -1) {
-            err  = -errno;
+    while (1) {
+        v9fs_path_init(&path);
+        err = v9fs_co_readdir_r(pdu, fidp, dent, &result);
+        if (err || !result) {
+            break;
+        }
+        err = v9fs_co_name_to_path(pdu, &fidp->path, dent->d_name, &path);
+        if (err < 0) {
+            goto out;
+        }
+        err = v9fs_co_lstat(pdu, &path, &stbuf);
+        if (err < 0) {
+            goto out;
+        }
+        err = stat_to_v9stat(pdu, &path, &stbuf, &v9stat);
+        if (err < 0) {
+            goto out;
+        }
+        /* 11 = 7 + 4 (7 = start offset, 4 = space for storing count) */
+        len = pdu_marshal(pdu, 11 + count, "S", &v9stat);
+        if ((len != (v9stat.size + 2)) || ((count + len) > max_count)) {
+            /* Ran out of buffer. Set dir back to old position and return */
+            v9fs_co_seekdir(pdu, fidp, saved_dir_pos);
+            v9fs_stat_free(&v9stat);
+            v9fs_path_free(&path);
+            g_free(dent);
+            return count;
         }
-        v9fs_read_post_preadv(s, vs, err);
-        return;
+        count += len;
+        v9fs_stat_free(&v9stat);
+        v9fs_path_free(&path);
+        saved_dir_pos = dent->d_off;
     }
-    vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", vs->total);
-    vs->offset += vs->count;
-    err = vs->offset;
-
 out:
-    complete_pdu(s, vs->pdu, err);
-    g_free(vs);
-}
-
-static void v9fs_xattr_read(V9fsState *s, V9fsReadState *vs)
-{
-    ssize_t err = 0;
-    int read_count;
-    int64_t xattr_len;
-
-    xattr_len = vs->fidp->fs.xattr.len;
-    read_count = xattr_len - vs->off;
-    if (read_count > vs->count) {
-        read_count = vs->count;
-    } else if (read_count < 0) {
-        /*
-         * read beyond XATTR value
-         */
-        read_count = 0;
+    g_free(dent);
+    v9fs_path_free(&path);
+    if (err < 0) {
+        return err;
     }
-    vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", read_count);
-    vs->offset += pdu_pack(vs->pdu, vs->offset,
-                           ((char *)vs->fidp->fs.xattr.value) + vs->off,
-                           read_count);
-    err = vs->offset;
-    complete_pdu(s, vs->pdu, err);
-    g_free(vs);
+    return count;
 }
 
 static void v9fs_read(void *opaque)
 {
-    V9fsPDU *pdu = opaque;
-    V9fsState *s = pdu->s;
     int32_t fid;
-    V9fsReadState *vs;
+    int64_t off;
     ssize_t err = 0;
+    int32_t count = 0;
+    size_t offset = 7;
+    int32_t max_count;
+    V9fsFidState *fidp;
+    V9fsPDU *pdu = opaque;
+    V9fsState *s = pdu->s;
 
-    vs = g_malloc(sizeof(*vs));
-    vs->pdu = pdu;
-    vs->offset = 7;
-    vs->total = 0;
-    vs->len = 0;
-    vs->count = 0;
-
-    pdu_unmarshal(vs->pdu, vs->offset, "dqd", &fid, &vs->off, &vs->count);
+    pdu_unmarshal(pdu, offset, "dqd", &fid, &off, &max_count);
+    trace_v9fs_read(pdu->tag, pdu->id, fid, off, max_count);
 
-    vs->fidp = lookup_fid(s, fid);
-    if (vs->fidp == NULL) {
+    fidp = get_fid(pdu, fid);
+    if (fidp == NULL) {
         err = -EINVAL;
-        goto out;
+        goto out_nofid;
     }
+    if (fidp->fid_type == P9_FID_DIR) {
 
-    if (vs->fidp->fid_type == P9_FID_DIR) {
-        vs->max_count = vs->count;
-        vs->count = 0;
-        if (vs->off == 0) {
-            v9fs_do_rewinddir(s, vs->fidp->fs.dir);
-        }
-        v9fs_read_post_rewinddir(s, vs, err);
-        return;
-    } else if (vs->fidp->fid_type == P9_FID_FILE) {
-        vs->sg = vs->iov;
-        pdu_marshal(vs->pdu, vs->offset + 4, "v", vs->sg, &vs->cnt);
-        vs->sg = cap_sg(vs->sg, vs->count, &vs->cnt);
-        if (vs->total <= vs->count) {
-            vs->len = v9fs_do_preadv(s, vs->fidp->fs.fd, vs->sg, vs->cnt,
-                                    vs->off);
-            if (vs->len > 0) {
-                vs->off += vs->len;
-            }
-            err = vs->len;
-            v9fs_read_post_preadv(s, vs, err);
+        if (off == 0) {
+            v9fs_co_rewinddir(pdu, fidp);
         }
-        return;
-    } else if (vs->fidp->fid_type == P9_FID_XATTR) {
-        v9fs_xattr_read(s, vs);
-        return;
+        count = v9fs_do_readdir_with_stat(pdu, fidp, max_count);
+        if (count < 0) {
+            err = count;
+            goto out;
+        }
+        err = offset;
+        err += pdu_marshal(pdu, offset, "d", count);
+        err += count;
+    } else if (fidp->fid_type == P9_FID_FILE) {
+        int32_t cnt;
+        int32_t len;
+        struct iovec *sg;
+        struct iovec iov[128]; /* FIXME: bad, bad, bad */
+
+        sg = iov;
+        pdu_marshal(pdu, offset + 4, "v", sg, &cnt);
+        sg = cap_sg(sg, max_count, &cnt);
+        do {
+            if (0) {
+                print_sg(sg, cnt);
+            }
+            /* Loop in case of EINTR */
+            do {
+                len = v9fs_co_preadv(pdu, fidp, sg, cnt, off);
+                if (len >= 0) {
+                    off   += len;
+                    count += len;
+                }
+            } while (len == -EINTR && !pdu->cancelled);
+            if (len < 0) {
+                /* IO error return the error */
+                err = len;
+                goto out;
+            }
+            sg = adjust_sg(sg, len, &cnt);
+        } while (count < max_count && len > 0);
+        err = offset;
+        err += pdu_marshal(pdu, offset, "d", count);
+        err += count;
+    } else if (fidp->fid_type == P9_FID_XATTR) {
+        err = v9fs_xattr_read(s, pdu, fidp, off, max_count);
     } else {
         err = -EINVAL;
     }
+    trace_v9fs_read_return(pdu->tag, pdu->id, count, err);
 out:
+    put_fid(pdu, fidp);
+out_nofid:
     complete_pdu(s, pdu, err);
-    g_free(vs);
 }
 
 static size_t v9fs_readdir_data_size(V9fsString *name)
@@ -1700,7 +2005,7 @@ static size_t v9fs_readdir_data_size(V9fsString *name)
     return 24 + v9fs_string_size(name);
 }
 
-static int v9fs_do_readdir(V9fsState *s, V9fsPDU *pdu,
+static int v9fs_do_readdir(V9fsPDU *pdu,
                            V9fsFidState *fidp, int32_t max_count)
 {
     size_t size;
@@ -1709,24 +2014,28 @@ static int v9fs_do_readdir(V9fsState *s, V9fsPDU *pdu,
     int len, err = 0;
     int32_t count = 0;
     off_t saved_dir_pos;
-    struct dirent *dent;
+    struct dirent *dent, *result;
 
     /* save the directory position */
-    saved_dir_pos = v9fs_co_telldir(s, fidp);
+    saved_dir_pos = v9fs_co_telldir(pdu, fidp);
     if (saved_dir_pos < 0) {
         return saved_dir_pos;
     }
+
+    dent = g_malloc(sizeof(struct dirent));
+
     while (1) {
-        err = v9fs_co_readdir(s, fidp, &dent);
-        if (err || !dent) {
+        err = v9fs_co_readdir_r(pdu, fidp, dent, &result);
+        if (err || !result) {
             break;
         }
         v9fs_string_init(&name);
         v9fs_string_sprintf(&name, "%s", dent->d_name);
         if ((count + v9fs_readdir_data_size(&name)) > max_count) {
             /* Ran out of buffer. Set dir back to old position and return */
-            v9fs_co_seekdir(s, fidp, saved_dir_pos);
+            v9fs_co_seekdir(pdu, fidp, saved_dir_pos);
             v9fs_string_free(&name);
+            g_free(dent);
             return count;
         }
         /*
@@ -1748,6 +2057,7 @@ static int v9fs_do_readdir(V9fsState *s, V9fsPDU *pdu,
         v9fs_string_free(&name);
         saved_dir_pos = dent->d_off;
     }
+    g_free(dent);
     if (err < 0) {
         return err;
     }
@@ -1767,17 +2077,23 @@ static void v9fs_readdir(void *opaque)
 
     pdu_unmarshal(pdu, offset, "dqd", &fid, &initial_offset, &max_count);
 
-    fidp = lookup_fid(s, fid);
-    if (fidp == NULL || !fidp->fs.dir) {
+    trace_v9fs_readdir(pdu->tag, pdu->id, fid, initial_offset, max_count);
+
+    fidp = get_fid(pdu, fid);
+    if (fidp == NULL) {
+        retval = -EINVAL;
+        goto out_nofid;
+    }
+    if (!fidp->fs.dir) {
         retval = -EINVAL;
         goto out;
     }
     if (initial_offset == 0) {
-        v9fs_co_rewinddir(s, fidp);
+        v9fs_co_rewinddir(pdu, fidp);
     } else {
-        v9fs_co_seekdir(s, fidp, initial_offset);
+        v9fs_co_seekdir(pdu, fidp, initial_offset);
     }
-    count = v9fs_do_readdir(s, pdu, fidp, max_count);
+    count = v9fs_do_readdir(pdu, fidp, max_count);
     if (count < 0) {
         retval = count;
         goto out;
@@ -1785,7 +2101,10 @@ 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:
     complete_pdu(s, pdu, retval);
 }
 
@@ -1850,10 +2169,12 @@ static void v9fs_write(void *opaque)
     V9fsState *s = pdu->s;
 
     pdu_unmarshal(pdu, offset, "dqdv", &fid, &off, &count, sg, &cnt);
-    fidp = lookup_fid(s, fid);
+    trace_v9fs_write(pdu->tag, pdu->id, fid, off, count, cnt);
+
+    fidp = get_fid(pdu, fid);
     if (fidp == NULL) {
         err = -EINVAL;
-        goto out;
+        goto out_nofid;
     }
     if (fidp->fid_type == P9_FID_FILE) {
         if (fidp->fs.fd == -1) {
@@ -1877,12 +2198,12 @@ static void v9fs_write(void *opaque)
         }
         /* Loop in case of EINTR */
         do {
-            len = v9fs_co_pwritev(s, fidp, sg, cnt, off);
+            len = v9fs_co_pwritev(pdu, fidp, sg, cnt, off);
             if (len >= 0) {
                 off   += len;
                 total += len;
             }
-        } while (len == -EINTR);
+        } while (len == -EINTR && !pdu->cancelled);
         if (len < 0) {
             /* IO error return the error */
             err = len;
@@ -1892,7 +2213,10 @@ 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:
     complete_pdu(s, pdu, err);
 }
 
@@ -1905,58 +2229,73 @@ static void v9fs_create(void *opaque)
     V9fsQID qid;
     int32_t perm;
     int8_t mode;
+    V9fsPath path;
     struct stat stbuf;
     V9fsString name;
     V9fsString extension;
-    V9fsString fullname;
     int iounit;
     V9fsPDU *pdu = opaque;
 
-    v9fs_string_init(&fullname);
+    v9fs_path_init(&path);
 
     pdu_unmarshal(pdu, offset, "dsdbs", &fid, &name,
                   &perm, &mode, &extension);
 
-    fidp = lookup_fid(pdu->s, fid);
+    trace_v9fs_create(pdu->tag, pdu->id, fid, name.data, perm, mode);
+
+    fidp = get_fid(pdu, fid);
     if (fidp == NULL) {
         err = -EINVAL;
-        goto out;
-    }
-
-    v9fs_string_sprintf(&fullname, "%s/%s", fidp->path.data, name.data);
-    err = v9fs_co_lstat(pdu->s, &fullname, &stbuf);
-    if (!err) {
-        err = -EEXIST;
-        goto out;
-    } else if (err != -ENOENT) {
-        goto out;
+        goto out_nofid;
     }
     if (perm & P9_STAT_MODE_DIR) {
-        err = v9fs_co_mkdir(pdu->s, fullname.data, perm & 0777,
-                            fidp->uid, -1);
+        err = v9fs_co_mkdir(pdu, fidp, &name, perm & 0777,
+                            fidp->uid, -1, &stbuf);
         if (err < 0) {
             goto out;
         }
-        err = v9fs_co_opendir(pdu->s, fidp);
+        err = v9fs_co_name_to_path(pdu, &fidp->path, name.data, &path);
+        if (err < 0) {
+            goto out;
+        }
+        v9fs_path_copy(&fidp->path, &path);
+        err = v9fs_co_opendir(pdu, fidp);
         if (err < 0) {
             goto out;
         }
         fidp->fid_type = P9_FID_DIR;
     } else if (perm & P9_STAT_MODE_SYMLINK) {
-        err = v9fs_co_symlink(pdu->s, fidp, extension.data,
-                              fullname.data, -1);
+        err = v9fs_co_symlink(pdu, fidp, &name,
+                              extension.data, -1 , &stbuf);
+        if (err < 0) {
+            goto out;
+        }
+        err = v9fs_co_name_to_path(pdu, &fidp->path, name.data, &path);
         if (err < 0) {
             goto out;
         }
+        v9fs_path_copy(&fidp->path, &path);
     } else if (perm & P9_STAT_MODE_LINK) {
-        int32_t nfid = atoi(extension.data);
-        V9fsFidState *nfidp = lookup_fid(pdu->s, nfid);
-        if (nfidp == NULL) {
+        int32_t ofid = atoi(extension.data);
+        V9fsFidState *ofidp = get_fid(pdu, ofid);
+        if (ofidp == NULL) {
             err = -EINVAL;
             goto out;
         }
-        err = v9fs_co_link(pdu->s, &nfidp->path, &fullname);
+        err = v9fs_co_link(pdu, ofidp, fidp, &name);
+        put_fid(pdu, ofidp);
+        if (err < 0) {
+            goto out;
+        }
+        err = v9fs_co_name_to_path(pdu, &fidp->path, name.data, &path);
         if (err < 0) {
+            fidp->fid_type = P9_FID_NONE;
+            goto out;
+        }
+        v9fs_path_copy(&fidp->path, &path);
+        err = v9fs_co_lstat(pdu, &fidp->path, &stbuf);
+        if (err < 0) {
+            fidp->fid_type = P9_FID_NONE;
             goto out;
         }
     } else if (perm & P9_STAT_MODE_DEVICE) {
@@ -1982,49 +2321,67 @@ static void v9fs_create(void *opaque)
         }
 
         nmode |= perm & 0777;
-        err = v9fs_co_mknod(pdu->s, &fullname, fidp->uid, -1,
-                            makedev(major, minor), nmode);
+        err = v9fs_co_mknod(pdu, fidp, &name, fidp->uid, -1,
+                            makedev(major, minor), nmode, &stbuf);
         if (err < 0) {
             goto out;
         }
+        err = v9fs_co_name_to_path(pdu, &fidp->path, name.data, &path);
+        if (err < 0) {
+            goto out;
+        }
+        v9fs_path_copy(&fidp->path, &path);
     } else if (perm & P9_STAT_MODE_NAMED_PIPE) {
-        err = v9fs_co_mknod(pdu->s, &fullname, fidp->uid, -1,
-                            0, S_IFIFO | (perm & 0777));
+        err = v9fs_co_mknod(pdu, fidp, &name, fidp->uid, -1,
+                            0, S_IFIFO | (perm & 0777), &stbuf);
+        if (err < 0) {
+            goto out;
+        }
+        err = v9fs_co_name_to_path(pdu, &fidp->path, name.data, &path);
         if (err < 0) {
             goto out;
         }
+        v9fs_path_copy(&fidp->path, &path);
     } else if (perm & P9_STAT_MODE_SOCKET) {
-        err = v9fs_co_mknod(pdu->s, &fullname, fidp->uid, -1,
-                            0, S_IFSOCK | (perm & 0777));
+        err = v9fs_co_mknod(pdu, fidp, &name, fidp->uid, -1,
+                            0, S_IFSOCK | (perm & 0777), &stbuf);
+        if (err < 0) {
+            goto out;
+        }
+        err = v9fs_co_name_to_path(pdu, &fidp->path, name.data, &path);
         if (err < 0) {
             goto out;
         }
+        v9fs_path_copy(&fidp->path, &path);
     } else {
-        err = v9fs_co_open2(pdu->s, fidp, fullname.data, -1,
-                            omode_to_uflags(mode)|O_CREAT, perm);
+        err = v9fs_co_open2(pdu, fidp, &name, -1,
+                            omode_to_uflags(mode)|O_CREAT, perm, &stbuf);
         if (err < 0) {
             goto out;
         }
         fidp->fid_type = P9_FID_FILE;
-    }
-    err = v9fs_co_lstat(pdu->s, &fullname, &stbuf);
-    if (err < 0) {
-        fidp->fid_type = P9_FID_NONE;
-        if (fidp->fs.fd) {
-            close(fidp->fs.fd);
+        fidp->open_flags = omode_to_uflags(mode);
+        if (fidp->open_flags & O_EXCL) {
+            /*
+             * We let the host file system do O_EXCL check
+             * We should not reclaim such fd
+             */
+            fidp->flags |= FID_NON_RECLAIMABLE;
         }
-        goto out;
     }
-    iounit = get_iounit(pdu->s, &fidp->path);
-    v9fs_string_copy(&fidp->path, &fullname);
+    iounit = get_iounit(pdu, &fidp->path);
     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:
    complete_pdu(pdu->s, pdu, err);
    v9fs_string_free(&name);
    v9fs_string_free(&extension);
-   v9fs_string_free(&fullname);
+   v9fs_path_free(&path);
 }
 
 static void v9fs_symlink(void *opaque)
@@ -2032,7 +2389,6 @@ static void v9fs_symlink(void *opaque)
     V9fsPDU *pdu = opaque;
     V9fsString name;
     V9fsString symname;
-    V9fsString fullname;
     V9fsFidState *dfidp;
     V9fsQID qid;
     struct stat stbuf;
@@ -2041,39 +2397,56 @@ static void v9fs_symlink(void *opaque)
     gid_t gid;
     size_t offset = 7;
 
-    v9fs_string_init(&fullname);
     pdu_unmarshal(pdu, offset, "dssd", &dfid, &name, &symname, &gid);
+    trace_v9fs_symlink(pdu->tag, pdu->id, dfid, name.data, symname.data, gid);
 
-    dfidp = lookup_fid(pdu->s, dfid);
+    dfidp = get_fid(pdu, dfid);
     if (dfidp == NULL) {
         err = -EINVAL;
-        goto out;
+        goto out_nofid;
     }
-
-    v9fs_string_sprintf(&fullname, "%s/%s", dfidp->path.data, name.data);
-    err = v9fs_co_symlink(pdu->s, dfidp, symname.data, fullname.data, gid);
-    if (err < 0) {
-        goto out;
-    }
-    err = v9fs_co_lstat(pdu->s, &fullname, &stbuf);
+    err = v9fs_co_symlink(pdu, dfidp, &name, symname.data, gid, &stbuf);
     if (err < 0) {
         goto out;
     }
     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:
     complete_pdu(pdu->s, pdu, err);
     v9fs_string_free(&name);
     v9fs_string_free(&symname);
-    v9fs_string_free(&fullname);
 }
 
 static void v9fs_flush(void *opaque)
 {
+    int16_t tag;
+    size_t offset = 7;
+    V9fsPDU *cancel_pdu;
     V9fsPDU *pdu = opaque;
     V9fsState *s = pdu->s;
-    /* A nop call with no return */
+
+    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) {
+            break;
+        }
+    }
+    if (cancel_pdu) {
+        cancel_pdu->cancelled = 1;
+        /*
+         * Wait for pdu to complete.
+         */
+        qemu_co_queue_wait(&cancel_pdu->complete);
+        cancel_pdu->cancelled = 0;
+        free_pdu(pdu->s, cancel_pdu);
+    }
     complete_pdu(s, pdu, 7);
     return;
 }
@@ -2084,38 +2457,36 @@ static void v9fs_link(void *opaque)
     V9fsState *s = pdu->s;
     int32_t dfid, oldfid;
     V9fsFidState *dfidp, *oldfidp;
-    V9fsString name, fullname;
+    V9fsString name;;
     size_t offset = 7;
     int err = 0;
 
-    v9fs_string_init(&fullname);
-
     pdu_unmarshal(pdu, offset, "dds", &dfid, &oldfid, &name);
+    trace_v9fs_link(pdu->tag, pdu->id, dfid, oldfid, name.data);
 
-    dfidp = lookup_fid(s, dfid);
+    dfidp = get_fid(pdu, dfid);
     if (dfidp == NULL) {
         err = -ENOENT;
-        goto out;
+        goto out_nofid;
     }
 
-    oldfidp = lookup_fid(s, oldfid);
+    oldfidp = get_fid(pdu, oldfid);
     if (oldfidp == NULL) {
         err = -ENOENT;
         goto out;
     }
-
-    v9fs_string_sprintf(&fullname, "%s/%s", dfidp->path.data, name.data);
-    err = v9fs_co_link(s, &oldfidp->path, &fullname);
+    err = v9fs_co_link(pdu, oldfidp, dfidp, &name);
     if (!err) {
         err = offset;
     }
-    v9fs_string_free(&fullname);
-
 out:
+    put_fid(pdu, dfidp);
+out_nofid:
     v9fs_string_free(&name);
     complete_pdu(s, pdu, err);
 }
 
+/* Only works with path name based fid */
 static void v9fs_remove(void *opaque)
 {
     int32_t fid;
@@ -2125,44 +2496,103 @@ static void v9fs_remove(void *opaque)
     V9fsPDU *pdu = opaque;
 
     pdu_unmarshal(pdu, offset, "d", &fid);
+    trace_v9fs_remove(pdu->tag, pdu->id, fid);
 
-    fidp = lookup_fid(pdu->s, fid);
+    fidp = get_fid(pdu, fid);
     if (fidp == NULL) {
         err = -EINVAL;
-        goto out;
+        goto out_nofid;
     }
-    err = v9fs_co_remove(pdu->s, &fidp->path);
+    /* if fs driver is not path based, return EOPNOTSUPP */
+    if (!(pdu->s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT)) {
+        err = -EOPNOTSUPP;
+        goto out_err;
+    }
+    /*
+     * IF the file is unlinked, we cannot reopen
+     * the file later. So don't reclaim fd
+     */
+    err = v9fs_mark_fids_unreclaim(pdu, &fidp->path);
+    if (err < 0) {
+        goto out_err;
+    }
+    err = v9fs_co_remove(pdu, &fidp->path);
     if (!err) {
         err = offset;
     }
-
+out_err:
     /* For TREMOVE we need to clunk the fid even on failed remove */
-    free_fid(pdu->s, fidp->fid);
-out:
+    clunk_fid(pdu->s, fidp->fid);
+    put_fid(pdu, fidp);
+out_nofid:
     complete_pdu(pdu->s, pdu, err);
 }
 
-static int v9fs_complete_rename(V9fsState *s, V9fsFidState *fidp,
+static void v9fs_unlinkat(void *opaque)
+{
+    int err = 0;
+    V9fsString name;
+    int32_t dfid, flags;
+    size_t offset = 7;
+    V9fsPath path;
+    V9fsFidState *dfidp;
+    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) {
+        err = -EINVAL;
+        goto out_nofid;
+    }
+    /*
+     * IF the file is unlinked, we cannot reopen
+     * the file later. So don't reclaim fd
+     */
+    v9fs_path_init(&path);
+    err = v9fs_co_name_to_path(pdu, &dfidp->path, name.data, &path);
+    if (err < 0) {
+        goto out_err;
+    }
+    err = v9fs_mark_fids_unreclaim(pdu, &path);
+    if (err < 0) {
+        goto out_err;
+    }
+    err = v9fs_co_unlinkat(pdu, &dfidp->path, &name, flags);
+    if (!err) {
+        err = offset;
+    }
+out_err:
+    put_fid(pdu, dfidp);
+    v9fs_path_free(&path);
+out_nofid:
+    complete_pdu(pdu->s, pdu, err);
+    v9fs_string_free(&name);
+}
+
+
+/* Only works with path name based fid */
+static int v9fs_complete_rename(V9fsPDU *pdu, V9fsFidState *fidp,
                                 int32_t newdirfid, V9fsString *name)
 {
     char *end;
     int err = 0;
+    V9fsPath new_path;
+    V9fsFidState *tfidp;
+    V9fsState *s = pdu->s;
+    V9fsFidState *dirfidp = NULL;
     char *old_name, *new_name;
 
+    v9fs_path_init(&new_path);
     if (newdirfid != -1) {
-        V9fsFidState *dirfidp;
-        dirfidp = lookup_fid(s, newdirfid);
+        dirfidp = get_fid(pdu, newdirfid);
         if (dirfidp == NULL) {
             err = -ENOENT;
-            goto out;
+            goto out_nofid;
         }
         BUG_ON(dirfidp->fid_type != P9_FID_NONE);
-
-        new_name = g_malloc0(dirfidp->path.size + name->size + 2);
-
-        strcpy(new_name, dirfidp->path.data);
-        strcat(new_name, "/");
-        strcat(new_name + dirfidp->path.size, name->data);
+        v9fs_co_name_to_path(pdu, &dirfidp->path, name->data, &new_path);
     } else {
         old_name = fidp->path.data;
         end = strrchr(old_name, '/');
@@ -2172,44 +2602,35 @@ static int v9fs_complete_rename(V9fsState *s, V9fsFidState *fidp,
             end = old_name;
         }
         new_name = g_malloc0(end - old_name + name->size + 1);
-
         strncat(new_name, old_name, end - old_name);
         strncat(new_name + (end - old_name), name->data, name->size);
+        v9fs_co_name_to_path(pdu, NULL, new_name, &new_path);
+        g_free(new_name);
     }
-
-    v9fs_string_free(name);
-    name->data = new_name;
-    name->size = strlen(new_name);
-
-    if (strcmp(new_name, fidp->path.data) != 0) {
-        err = v9fs_co_rename(s, &fidp->path, name);
-        if (err < 0) {
-            goto out;
-        }
-        V9fsFidState *tfidp;
-        /*
-         * Fixup fid's pointing to the old name to
-         * start pointing to the new name
-         */
-        for (tfidp = s->fid_list; tfidp; tfidp = tfidp->next) {
-            if (fidp == tfidp) {
-                /*
-                 * we replace name of this fid towards the end
-                 * so that our below strcmp will work
-                 */
-                continue;
-            }
-            if (v9fs_path_is_ancestor(&fidp->path, &tfidp->path)) {
-                /* replace the name */
-                v9fs_fix_path(&tfidp->path, name, strlen(fidp->path.data));
-            }
+    err = v9fs_co_rename(pdu, &fidp->path, &new_path);
+    if (err < 0) {
+        goto out;
+    }
+    /*
+     * Fixup fid's pointing to the old name to
+     * start pointing to the new name
+     */
+    for (tfidp = s->fid_list; tfidp; tfidp = tfidp->next) {
+        if (v9fs_path_is_ancestor(&fidp->path, &tfidp->path)) {
+            /* replace the name */
+            v9fs_fix_path(&tfidp->path, &new_path, strlen(fidp->path.data));
         }
-        v9fs_string_copy(&fidp->path, name);
     }
 out:
+    if (dirfidp) {
+        put_fid(pdu, dirfidp);
+    }
+    v9fs_path_free(&new_path);
+out_nofid:
     return err;
 }
 
+/* Only works with path name based fid */
 static void v9fs_rename(void *opaque)
 {
     int32_t fid;
@@ -2223,22 +2644,125 @@ static void v9fs_rename(void *opaque)
 
     pdu_unmarshal(pdu, offset, "dds", &fid, &newdirfid, &name);
 
-    fidp = lookup_fid(s, fid);
+    fidp = get_fid(pdu, fid);
     if (fidp == NULL) {
         err = -ENOENT;
-        goto out;
+        goto out_nofid;
     }
     BUG_ON(fidp->fid_type != P9_FID_NONE);
-
-    err = v9fs_complete_rename(s, fidp, newdirfid, &name);
+    /* if fs driver is not path based, return EOPNOTSUPP */
+    if (!(pdu->s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT)) {
+        err = -EOPNOTSUPP;
+        goto out;
+    }
+    v9fs_path_write_lock(s);
+    err = v9fs_complete_rename(pdu, fidp, newdirfid, &name);
+    v9fs_path_unlock(s);
     if (!err) {
         err = offset;
     }
 out:
+    put_fid(pdu, fidp);
+out_nofid:
     complete_pdu(s, pdu, err);
     v9fs_string_free(&name);
 }
 
+static void v9fs_fix_fid_paths(V9fsPDU *pdu, V9fsPath *olddir,
+                               V9fsString *old_name, V9fsPath *newdir,
+                               V9fsString *new_name)
+{
+    V9fsFidState *tfidp;
+    V9fsPath oldpath, newpath;
+    V9fsState *s = pdu->s;
+
+
+    v9fs_path_init(&oldpath);
+    v9fs_path_init(&newpath);
+    v9fs_co_name_to_path(pdu, olddir, old_name->data, &oldpath);
+    v9fs_co_name_to_path(pdu, newdir, new_name->data, &newpath);
+
+    /*
+     * Fixup fid's pointing to the old name to
+     * start pointing to the new name
+     */
+    for (tfidp = s->fid_list; tfidp; tfidp = tfidp->next) {
+        if (v9fs_path_is_ancestor(&oldpath, &tfidp->path)) {
+            /* replace the name */
+            v9fs_fix_path(&tfidp->path, &newpath, strlen(oldpath.data));
+        }
+    }
+    v9fs_path_free(&oldpath);
+    v9fs_path_free(&newpath);
+}
+
+static int v9fs_complete_renameat(V9fsPDU *pdu, int32_t olddirfid,
+                                  V9fsString *old_name, int32_t newdirfid,
+                                  V9fsString *new_name)
+{
+    int err = 0;
+    V9fsState *s = pdu->s;
+    V9fsFidState *newdirfidp = NULL, *olddirfidp = NULL;
+
+    olddirfidp = get_fid(pdu, olddirfid);
+    if (olddirfidp == NULL) {
+        err = -ENOENT;
+        goto out;
+    }
+    if (newdirfid != -1) {
+        newdirfidp = get_fid(pdu, newdirfid);
+        if (newdirfidp == NULL) {
+            err = -ENOENT;
+            goto out;
+        }
+    } else {
+        newdirfidp = get_fid(pdu, olddirfid);
+    }
+
+    err = v9fs_co_renameat(pdu, &olddirfidp->path, old_name,
+                           &newdirfidp->path, new_name);
+    if (err < 0) {
+        goto out;
+    }
+    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);
+    }
+out:
+    if (olddirfidp) {
+        put_fid(pdu, olddirfidp);
+    }
+    if (newdirfidp) {
+        put_fid(pdu, newdirfidp);
+    }
+    return err;
+}
+
+static void v9fs_renameat(void *opaque)
+{
+    ssize_t err = 0;
+    size_t offset = 7;
+    V9fsPDU *pdu = opaque;
+    V9fsState *s = pdu->s;
+    int32_t olddirfid, newdirfid;
+    V9fsString old_name, new_name;
+
+    pdu_unmarshal(pdu, offset, "dsds", &olddirfid,
+                  &old_name, &newdirfid, &new_name);
+
+    v9fs_path_write_lock(s);
+    err = v9fs_complete_renameat(pdu, olddirfid,
+                                 &old_name, newdirfid, &new_name);
+    v9fs_path_unlock(s);
+    if (!err) {
+        err = offset;
+    }
+    complete_pdu(s, pdu, err);
+    v9fs_string_free(&old_name);
+    v9fs_string_free(&new_name);
+}
+
 static void v9fs_wstat(void *opaque)
 {
     int32_t fid;
@@ -2252,19 +2776,22 @@ static void v9fs_wstat(void *opaque)
     V9fsState *s = pdu->s;
 
     pdu_unmarshal(pdu, offset, "dwS", &fid, &unused, &v9stat);
-    fidp = lookup_fid(s, fid);
+    trace_v9fs_wstat(pdu->tag, pdu->id, fid,
+                     v9stat.mode, v9stat.atime, v9stat.mtime);
+
+    fidp = get_fid(pdu, fid);
     if (fidp == NULL) {
         err = -EINVAL;
-        goto out;
+        goto out_nofid;
     }
     /* do we need to sync the file? */
     if (donttouch_stat(&v9stat)) {
-        err = v9fs_co_fsync(s, fidp, 0);
+        err = v9fs_co_fsync(pdu, fidp, 0);
         goto out;
     }
     if (v9stat.mode != -1) {
         uint32_t v9_mode;
-        err = v9fs_co_lstat(s, &fidp->path, &stbuf);
+        err = v9fs_co_lstat(pdu, &fidp->path, &stbuf);
         if (err < 0) {
             goto out;
         }
@@ -2275,7 +2802,7 @@ static void v9fs_wstat(void *opaque)
             err = -EIO;
             goto out;
         }
-        err = v9fs_co_chmod(s, &fidp->path,
+        err = v9fs_co_chmod(pdu, &fidp->path,
                             v9mode_to_mode(v9stat.mode,
                                            &v9stat.extension));
         if (err < 0) {
@@ -2296,31 +2823,33 @@ static void v9fs_wstat(void *opaque)
         } else {
             times[1].tv_nsec = UTIME_OMIT;
         }
-        err = v9fs_co_utimensat(s, &fidp->path, times);
+        err = v9fs_co_utimensat(pdu, &fidp->path, times);
         if (err < 0) {
             goto out;
         }
     }
     if (v9stat.n_gid != -1 || v9stat.n_uid != -1) {
-        err = v9fs_co_chown(s, &fidp->path, v9stat.n_uid, v9stat.n_gid);
+        err = v9fs_co_chown(pdu, &fidp->path, v9stat.n_uid, v9stat.n_gid);
         if (err < 0) {
             goto out;
         }
     }
     if (v9stat.name.size != 0) {
-        err = v9fs_complete_rename(s, fidp, -1, &v9stat.name);
+        err = v9fs_complete_rename(pdu, fidp, -1, &v9stat.name);
         if (err < 0) {
             goto out;
         }
     }
     if (v9stat.length != -1) {
-        err = v9fs_co_truncate(s, &fidp->path, v9stat.length);
+        err = v9fs_co_truncate(pdu, &fidp->path, v9stat.length);
         if (err < 0) {
             goto out;
         }
     }
     err = offset;
 out:
+    put_fid(pdu, fidp);
+out_nofid:
     v9fs_stat_free(&v9stat);
     complete_pdu(s, pdu, err);
 }
@@ -2381,18 +2910,20 @@ static void v9fs_statfs(void *opaque)
     V9fsState *s = pdu->s;
 
     pdu_unmarshal(pdu, offset, "d", &fid);
-    fidp = lookup_fid(s, fid);
+    fidp = get_fid(pdu, fid);
     if (fidp == NULL) {
         retval = -ENOENT;
-        goto out;
+        goto out_nofid;
     }
-    retval = v9fs_co_statfs(s, &fidp->path, &stbuf);
+    retval = v9fs_co_statfs(pdu, &fidp->path, &stbuf);
     if (retval < 0) {
         goto out;
     }
     retval = offset;
     retval += v9fs_fill_statfs(s, pdu, &stbuf);
 out:
+    put_fid(pdu, fidp);
+out_nofid:
     complete_pdu(s, pdu, retval);
     return;
 }
@@ -2409,36 +2940,33 @@ static void v9fs_mknod(void *opaque)
     size_t offset = 7;
     V9fsString name;
     struct stat stbuf;
-    V9fsString fullname;
     V9fsFidState *fidp;
     V9fsPDU *pdu = opaque;
     V9fsState *s = pdu->s;
 
-    v9fs_string_init(&fullname);
     pdu_unmarshal(pdu, offset, "dsdddd", &fid, &name, &mode,
                   &major, &minor, &gid);
+    trace_v9fs_mknod(pdu->tag, pdu->id, fid, mode, major, minor);
 
-    fidp = lookup_fid(s, fid);
+    fidp = get_fid(pdu, fid);
     if (fidp == NULL) {
         err = -ENOENT;
-        goto out;
-    }
-    v9fs_string_sprintf(&fullname, "%s/%s", fidp->path.data, name.data);
-    err = v9fs_co_mknod(s, &fullname, fidp->uid, gid,
-                        makedev(major, minor), mode);
-    if (err < 0) {
-        goto out;
+        goto out_nofid;
     }
-    err = v9fs_co_lstat(s, &fullname, &stbuf);
+    err = v9fs_co_mknod(pdu, fidp, &name, fidp->uid, gid,
+                        makedev(major, minor), mode, &stbuf);
     if (err < 0) {
         goto out;
     }
     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:
     complete_pdu(s, pdu, err);
-    v9fs_string_free(&fullname);
     v9fs_string_free(&name);
 }
 
@@ -2465,27 +2993,35 @@ 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) */
     if (flock->flags & ~P9_LOCK_FLAGS_BLOCK) {
         err = -EINVAL;
-        goto out;
+        goto out_nofid;
     }
-    fidp = lookup_fid(s, fid);
+    fidp = get_fid(pdu, fid);
     if (fidp == NULL) {
         err = -ENOENT;
-        goto out;
+        goto out_nofid;
     }
-    err = v9fs_co_fstat(s, fidp->fs.fd, &stbuf);
+    err = v9fs_co_fstat(pdu, fidp, &stbuf);
     if (err < 0) {
         goto out;
     }
     status = P9_LOCK_SUCCESS;
 out:
+    put_fid(pdu, fidp);
+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);
 }
 
@@ -2508,22 +3044,30 @@ static void v9fs_getlock(void *opaque)
                   &glock->start, &glock->length, &glock->proc_id,
                   &glock->client_id);
 
-    fidp = lookup_fid(s, fid);
+    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;
+        goto out_nofid;
     }
-    err = v9fs_co_fstat(s, 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:
     complete_pdu(s, pdu, err);
+    v9fs_string_free(&glock->client_id);
     g_free(glock);
 }
 
@@ -2533,36 +3077,35 @@ static void v9fs_mkdir(void *opaque)
     size_t offset = 7;
     int32_t fid;
     struct stat stbuf;
-    V9fsString name, fullname;
     V9fsQID qid;
+    V9fsString name;
     V9fsFidState *fidp;
     gid_t gid;
     int mode;
     int err = 0;
 
-    v9fs_string_init(&fullname);
     pdu_unmarshal(pdu, offset, "dsdd", &fid, &name, &mode, &gid);
 
-    fidp = lookup_fid(pdu->s, fid);
+    trace_v9fs_mkdir(pdu->tag, pdu->id, fid, name.data, mode, gid);
+
+    fidp = get_fid(pdu, fid);
     if (fidp == NULL) {
         err = -ENOENT;
-        goto out;
+        goto out_nofid;
     }
-    v9fs_string_sprintf(&fullname, "%s/%s", fidp->path.data, name.data);
-    err = v9fs_co_mkdir(pdu->s, fullname.data, mode, fidp->uid, gid);
-    if (err < 0) {
-        goto out;
-    }
-    err = v9fs_co_lstat(pdu->s, &fullname, &stbuf);
+    err = v9fs_co_mkdir(pdu, fidp, &name, mode, fidp->uid, gid, &stbuf);
     if (err < 0) {
         goto out;
     }
     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:
     complete_pdu(pdu->s, pdu, err);
-    v9fs_string_free(&fullname);
     v9fs_string_free(&name);
 }
 
@@ -2574,30 +3117,32 @@ static void v9fs_xattrwalk(void *opaque)
     size_t offset = 7;
     int32_t fid, newfid;
     V9fsFidState *file_fidp;
-    V9fsFidState *xattr_fidp;
+    V9fsFidState *xattr_fidp = NULL;
     V9fsPDU *pdu = opaque;
     V9fsState *s = pdu->s;
 
     pdu_unmarshal(pdu, offset, "dds", &fid, &newfid, &name);
-    file_fidp = lookup_fid(s, fid);
+    trace_v9fs_xattrwalk(pdu->tag, pdu->id, fid, newfid, name.data);
+
+    file_fidp = get_fid(pdu, fid);
     if (file_fidp == NULL) {
         err = -ENOENT;
-        goto out;
+        goto out_nofid;
     }
     xattr_fidp = alloc_fid(s, newfid);
     if (xattr_fidp == NULL) {
         err = -EINVAL;
         goto out;
     }
-    v9fs_string_copy(&xattr_fidp->path, &file_fidp->path);
+    v9fs_path_copy(&xattr_fidp->path, &file_fidp->path);
     if (name.data[0] == 0) {
         /*
          * listxattr request. Get the size first
          */
-        size = v9fs_co_llistxattr(s, &xattr_fidp->path, NULL, 0);
+        size = v9fs_co_llistxattr(pdu, &xattr_fidp->path, NULL, 0);
         if (size < 0) {
             err = size;
-            free_fid(s, xattr_fidp->fid);
+            clunk_fid(s, xattr_fidp->fid);
             goto out;
         }
         /*
@@ -2608,11 +3153,11 @@ static void v9fs_xattrwalk(void *opaque)
         xattr_fidp->fs.xattr.copied_len = -1;
         if (size) {
             xattr_fidp->fs.xattr.value = g_malloc(size);
-            err = v9fs_co_llistxattr(s, &xattr_fidp->path,
+            err = v9fs_co_llistxattr(pdu, &xattr_fidp->path,
                                      xattr_fidp->fs.xattr.value,
                                      xattr_fidp->fs.xattr.len);
             if (err < 0) {
-                free_fid(s, xattr_fidp->fid);
+                clunk_fid(s, xattr_fidp->fid);
                 goto out;
             }
         }
@@ -2623,11 +3168,11 @@ static void v9fs_xattrwalk(void *opaque)
          * specific xattr fid. We check for xattr
          * presence also collect the xattr size
          */
-        size = v9fs_co_lgetxattr(s, &xattr_fidp->path,
+        size = v9fs_co_lgetxattr(pdu, &xattr_fidp->path,
                                  &name, NULL, 0);
         if (size < 0) {
             err = size;
-            free_fid(s, xattr_fidp->fid);
+            clunk_fid(s, xattr_fidp->fid);
             goto out;
         }
         /*
@@ -2638,18 +3183,24 @@ static void v9fs_xattrwalk(void *opaque)
         xattr_fidp->fs.xattr.copied_len = -1;
         if (size) {
             xattr_fidp->fs.xattr.value = g_malloc(size);
-            err = v9fs_co_lgetxattr(s, &xattr_fidp->path,
+            err = v9fs_co_lgetxattr(pdu, &xattr_fidp->path,
                                     &name, xattr_fidp->fs.xattr.value,
                                     xattr_fidp->fs.xattr.len);
             if (err < 0) {
-                free_fid(s, xattr_fidp->fid);
+                clunk_fid(s, xattr_fidp->fid);
                 goto out;
             }
         }
         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) {
+        put_fid(pdu, xattr_fidp);
+    }
+out_nofid:
     complete_pdu(s, pdu, err);
     v9fs_string_free(&name);
 }
@@ -2669,11 +3220,12 @@ 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 = lookup_fid(s, fid);
+    file_fidp = get_fid(pdu, fid);
     if (file_fidp == NULL) {
         err = -EINVAL;
-        goto out;
+        goto out_nofid;
     }
     /* Make the file fid point to xattr */
     xattr_fidp = file_fidp;
@@ -2689,7 +3241,8 @@ static void v9fs_xattrcreate(void *opaque)
         xattr_fidp->fs.xattr.value = NULL;
     }
     err = offset;
-out:
+    put_fid(pdu, file_fidp);
+out_nofid:
     complete_pdu(s, pdu, err);
     v9fs_string_free(&name);
 }
@@ -2704,21 +3257,25 @@ static void v9fs_readlink(void *opaque)
     V9fsFidState *fidp;
 
     pdu_unmarshal(pdu, offset, "d", &fid);
-    fidp = lookup_fid(pdu->s, fid);
+    trace_v9fs_readlink(pdu->tag, pdu->id, fid);
+    fidp = get_fid(pdu, fid);
     if (fidp == NULL) {
         err = -ENOENT;
-        goto out;
+        goto out_nofid;
     }
 
     v9fs_string_init(&target);
-    err = v9fs_co_readlink(pdu->s, &fidp->path, &target);
+    err = v9fs_co_readlink(pdu, &fidp->path, &target);
     if (err < 0) {
         goto out;
     }
     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);
+out_nofid:
     complete_pdu(pdu->s, pdu, err);
 }
 
@@ -2733,7 +3290,9 @@ static CoroutineEntry *pdu_co_handlers[] = {
     [P9_TRENAME] = v9fs_rename,
     [P9_TLOCK] = v9fs_lock,
     [P9_TGETLOCK] = v9fs_getlock,
+    [P9_TRENAMEAT] = v9fs_renameat,
     [P9_TREADLINK] = v9fs_readlink,
+    [P9_TUNLINKAT] = v9fs_unlinkat,
     [P9_TMKDIR] = v9fs_mkdir,
     [P9_TVERSION] = v9fs_version,
     [P9_TLOPEN] = v9fs_open,
@@ -2763,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);
 }
@@ -2799,7 +3392,19 @@ void handle_9p_output(VirtIODevice *vdev, VirtQueue *vq)
         memcpy(&pdu->size, ptr, 4);
         pdu->id = ptr[4];
         memcpy(&pdu->tag, ptr + 5, 2);
+        qemu_co_queue_init(&pdu->complete);
         submit_pdu(s, pdu);
     }
     free_pdu(s, pdu);
 }
+
+void virtio_9p_set_fd_limit(void)
+{
+    struct rlimit rlim;
+    if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) {
+        fprintf(stderr, "Failed to get the resource limit\n");
+        exit(1);
+    }
+    open_fd_hw = rlim.rlim_cur - MIN(400, rlim.rlim_cur/3);
+    open_fd_rc = rlim.rlim_cur/2;
+}