[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Please test qemu-kvm packages (9pfs changes)



Hi,
I've backported large parts of the current 9pfs from current qemu 2.8.1
to address several 9pfs related CVEs. It would be great if somebody
could test the packages at:

    https://people.debian.org/~agx/debian-lts/    

Especially if you're mapping host filesystem into guests using 9pfs any
feedback would be great. The debdiff is attached.

    https://anonscm.debian.org/cgit/users/agx/qemu-kvm.git/log/?h=patch-queue/update/9pfs-unsquashed

Cheers
 -- Guido

diff --git a/debian/changelog b/debian/changelog
index 8d7309b9c9..4f5cffd6ba 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,13 @@
+qemu-kvm (1.1.2+dfsg-6+deb7u22) wheezy-security; urgency=medium
+
+  * Backport large amounts of 9p code from 2.8.1
+    to fix CVE-2016-9602, CVE-2017-7471
+  * CVE-2017-8086: 9pfs: xattr: fix memory leak in v9fs_list_xattr
+  * CVE-2017-7377: 9pfs: fix file descriptor leak
+  * CVE-2017-7493: 9pfs: local: forbid client access to metadata
+
+ -- Guido Günther <agx@sigxcpu.org>  Fri, 26 May 2017 13:03:57 +0200
+
 qemu-kvm (1.1.2+dfsg-6+deb7u21) wheezy-security; urgency=medium
 
   * Backport cirrus code from 2.8.1
diff --git a/debian/patches/9p-Update-major-parts-from-2.8.1.1.patch b/debian/patches/9p-Update-major-parts-from-2.8.1.1.patch
new file mode 100644
index 0000000000..26ba02973b
--- /dev/null
+++ b/debian/patches/9p-Update-major-parts-from-2.8.1.1.patch
@@ -0,0 +1,4539 @@
+From: =?utf-8?q?Guido_G=C3=BCnther?= <agx@sigxcpu.org>
+Date: Fri, 5 May 2017 18:03:08 +0200
+Subject: 9p: Update major parts from 2.8.1.1
+
+to address CVE-2017-7493
+
+The following changes were made:
+
+    9p: Update files from 2.8.1.1
+    to fix CVE-2016-9602
+
+    9p: Disable 9pfs-proxy
+    It's unsupported in Wheezy since we don't ship virtfs-proxy-helper so
+    there's no need to keep it supported or even compiling.
+    This reduces the amount of code to backport.
+
+    9p-util.c: Include necessary headers
+    Don't mess with osdep.h (where they are in recent qemu) to not
+    trigger any unwanted side effects.
+
+    Include correct headers
+    There were lots of renames since 1.1.2
+
+    Use old header name
+    There's another 9p.h in hw/ and we don't want to change existing
+    users.
+
+    9p: introduce the V9fsDir type
+    Backport of f314ea4e30a1ef87bf8845da952c6dd0bac20b95
+
+    9p: add locking to V9fsDir
+    Backport of d7d027bb77471b3e3803a6adf75c231f1bf61331
+
+    9p: backport v9fs_path_sprintf
+    based on e3e83f2e2130a3afbd41a2893d23397f03f6d9d0
+
+    9pfs: add (dummy) cleanup operation in FileOperations
+    We add a dummy cleanup function. It is unused since we don't have
+    {,un}realize functions in this version of qemu. So the resource leak
+    happens already (e.g. for ctx.fs_root).
+    The upstream commit that adds this properly is
+    702dbcc274e2ca43be20ba64c758c0ca57dab91d
+
+    9p-local.c: Switch back to readdir_r in FileOperations
+    This reversed the effect of 635324e83e238598e86628dba5ab62ce910e6f72
+    since our version of QEMU uses readdir_r.
+---
+ Makefile.objs                  |    7 +-
+ fsdev/file-op-9p.h             |    1 +
+ fsdev/qemu-fsdev.c             |    1 -
+ hw/9pfs/9p-local.c             | 1379 ++++++++++++++++++++++++++++++++++++++++
+ hw/9pfs/9p-local.h             |   20 +
+ hw/9pfs/9p-posix-acl.c         |  156 +++++
+ hw/9pfs/9p-util.c              |   84 +++
+ hw/9pfs/9p-util.h              |   60 ++
+ hw/9pfs/9p-xattr-user.c        |  109 ++++
+ hw/9pfs/9p-xattr.c             |  318 +++++++++
+ hw/9pfs/9p-xattr.h             |   76 +++
+ hw/9pfs/virtio-9p-device.c     |    2 +-
+ hw/9pfs/virtio-9p-handle.c     |   20 +-
+ hw/9pfs/virtio-9p-local.c      | 1171 ----------------------------------
+ hw/9pfs/virtio-9p-posix-acl.c  |  159 -----
+ hw/9pfs/virtio-9p-proxy.c      |   27 +-
+ hw/9pfs/virtio-9p-synth.c      |    2 +-
+ hw/9pfs/virtio-9p-xattr-user.c |  112 ----
+ hw/9pfs/virtio-9p-xattr.c      |  160 -----
+ hw/9pfs/virtio-9p-xattr.h      |  105 ---
+ hw/9pfs/virtio-9p.c            |   54 +-
+ hw/9pfs/virtio-9p.h            |   23 +-
+ 22 files changed, 2296 insertions(+), 1750 deletions(-)
+ create mode 100644 hw/9pfs/9p-local.c
+ create mode 100644 hw/9pfs/9p-local.h
+ create mode 100644 hw/9pfs/9p-posix-acl.c
+ create mode 100644 hw/9pfs/9p-util.c
+ create mode 100644 hw/9pfs/9p-util.h
+ create mode 100644 hw/9pfs/9p-xattr-user.c
+ create mode 100644 hw/9pfs/9p-xattr.c
+ create mode 100644 hw/9pfs/9p-xattr.h
+ delete mode 100644 hw/9pfs/virtio-9p-local.c
+ delete mode 100644 hw/9pfs/virtio-9p-posix-acl.c
+ delete mode 100644 hw/9pfs/virtio-9p-xattr-user.c
+ delete mode 100644 hw/9pfs/virtio-9p-xattr.c
+ delete mode 100644 hw/9pfs/virtio-9p-xattr.h
+
+diff --git a/Makefile.objs b/Makefile.objs
+index 264f1fe..fdb8fd1 100644
+--- a/Makefile.objs
++++ b/Makefile.objs
+@@ -340,13 +340,12 @@ sound-obj-$(CONFIG_HDA) += intel-hda.o hda-audio.o
+ adlib.o fmopl.o: QEMU_CFLAGS += -DBUILD_Y8950=0
+ hw-obj-$(CONFIG_SOUND) += $(sound-obj-y)
+ 
+-9pfs-nested-$(CONFIG_VIRTFS)  = virtio-9p.o
+-9pfs-nested-$(CONFIG_VIRTFS) += virtio-9p-local.o virtio-9p-xattr.o
+-9pfs-nested-$(CONFIG_VIRTFS) += virtio-9p-xattr-user.o virtio-9p-posix-acl.o
++9pfs-nested-$(CONFIG_VIRTFS)  = virtio-9p.o 9p-util.o
++9pfs-nested-$(CONFIG_VIRTFS) += 9p-local.o 9p-xattr.o
++9pfs-nested-$(CONFIG_VIRTFS) += 9p-xattr-user.o 9p-posix-acl.o
+ 9pfs-nested-$(CONFIG_VIRTFS) += virtio-9p-coth.o cofs.o codir.o cofile.o
+ 9pfs-nested-$(CONFIG_VIRTFS) += coxattr.o virtio-9p-synth.o
+ 9pfs-nested-$(CONFIG_OPEN_BY_HANDLE) +=  virtio-9p-handle.o
+-9pfs-nested-$(CONFIG_VIRTFS) += virtio-9p-proxy.o
+ 
+ hw-obj-$(CONFIG_REALLY_VIRTFS) += $(addprefix 9pfs/, $(9pfs-nested-y))
+ 
+diff --git a/fsdev/file-op-9p.h b/fsdev/file-op-9p.h
+index 956fda0..16dd096 100644
+--- a/fsdev/file-op-9p.h
++++ b/fsdev/file-op-9p.h
+@@ -102,6 +102,7 @@ struct FileOperations
+ {
+     int (*parse_opts)(QemuOpts *, struct FsDriverEntry *);
+     int (*init)(struct FsContext *);
++    void (*cleanup)(struct FsContext *);
+     int (*lstat)(FsContext *, V9fsPath *, struct stat *);
+     ssize_t (*readlink)(FsContext *, V9fsPath *, char *, size_t);
+     int (*chmod)(FsContext *, V9fsPath *, FsCred *);
+diff --git a/fsdev/qemu-fsdev.c b/fsdev/qemu-fsdev.c
+index e20202a..00f48ab 100644
+--- a/fsdev/qemu-fsdev.c
++++ b/fsdev/qemu-fsdev.c
+@@ -27,7 +27,6 @@ static FsDriverTable FsDrivers[] = {
+     { .name = "handle", .ops = &handle_ops},
+ #endif
+     { .name = "synth", .ops = &synth_ops},
+-    { .name = "proxy", .ops = &proxy_ops},
+ };
+ 
+ int qemu_fsdev_add(QemuOpts *opts)
+diff --git a/hw/9pfs/9p-local.c b/hw/9pfs/9p-local.c
+new file mode 100644
+index 0000000..3437b34
+--- /dev/null
++++ b/hw/9pfs/9p-local.c
+@@ -0,0 +1,1379 @@
++/*
++ * 9p Posix callback
++ *
++ * 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 "osdep.h"
++#include "virtio-9p.h"
++#include "9p-local.h"
++#include "9p-xattr.h"
++#include "9p-util.h"
++#include "fsdev/qemu-fsdev.h"   /* local_ops */
++#include <arpa/inet.h>
++#include <pwd.h>
++#include <grp.h>
++#include <sys/socket.h>
++#include <sys/un.h>
++#include "qemu-xattr.h"
++#include "qemu-common.h"
++#include "qemu-error.h"
++#include <libgen.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
++
++typedef struct {
++    int mountfd;
++} LocalData;
++
++int local_open_nofollow(FsContext *fs_ctx, const char *path, int flags,
++                        mode_t mode)
++{
++    LocalData *data = fs_ctx->private;
++
++    /* All paths are relative to the path data->mountfd points to */
++    while (*path == '/') {
++        path++;
++    }
++
++    return relative_openat_nofollow(data->mountfd, path, flags, mode);
++}
++
++int local_opendir_nofollow(FsContext *fs_ctx, const char *path)
++{
++    return local_open_nofollow(fs_ctx, path, O_DIRECTORY | O_RDONLY, 0);
++}
++
++static void renameat_preserve_errno(int odirfd, const char *opath, int ndirfd,
++                                    const char *npath)
++{
++    int serrno = errno;
++    renameat(odirfd, opath, ndirfd, npath);
++    errno = serrno;
++}
++
++static void unlinkat_preserve_errno(int dirfd, const char *path, int flags)
++{
++    int serrno = errno;
++    unlinkat(dirfd, path, flags);
++    errno = serrno;
++}
++
++#define VIRTFS_META_DIR ".virtfs_metadata"
++
++static FILE *local_fopenat(int dirfd, const char *name, const char *mode)
++{
++    int fd, o_mode = 0;
++    FILE *fp;
++    int flags;
++    /*
++     * only supports two modes
++     */
++    if (mode[0] == 'r') {
++        flags = O_RDONLY;
++    } else if (mode[0] == 'w') {
++        flags = O_WRONLY | O_TRUNC | O_CREAT;
++        o_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
++    } else {
++        return NULL;
++    }
++    fd = openat_file(dirfd, name, flags, o_mode);
++    if (fd == -1) {
++        return NULL;
++    }
++    fp = fdopen(fd, mode);
++    if (!fp) {
++        close(fd);
++    }
++    return fp;
++}
++
++#define ATTR_MAX 100
++static void local_mapped_file_attr(int dirfd, const char *name,
++                                   struct stat *stbuf)
++{
++    FILE *fp;
++    char buf[ATTR_MAX];
++    int map_dirfd;
++
++    map_dirfd = openat_dir(dirfd, VIRTFS_META_DIR);
++    if (map_dirfd == -1) {
++        return;
++    }
++
++    fp = local_fopenat(map_dirfd, name, "r");
++    close_preserve_errno(map_dirfd);
++    if (!fp) {
++        return;
++    }
++    memset(buf, 0, ATTR_MAX);
++    while (fgets(buf, ATTR_MAX, fp)) {
++        if (!strncmp(buf, "virtfs.uid", 10)) {
++            stbuf->st_uid = atoi(buf+11);
++        } else if (!strncmp(buf, "virtfs.gid", 10)) {
++            stbuf->st_gid = atoi(buf+11);
++        } else if (!strncmp(buf, "virtfs.mode", 11)) {
++            stbuf->st_mode = atoi(buf+12);
++        } else if (!strncmp(buf, "virtfs.rdev", 11)) {
++            stbuf->st_rdev = atoi(buf+12);
++        }
++        memset(buf, 0, ATTR_MAX);
++    }
++    fclose(fp);
++}
++
++static int local_lstat(FsContext *fs_ctx, V9fsPath *fs_path, struct stat *stbuf)
++{
++    int err = -1;
++    char *dirpath = g_path_get_dirname(fs_path->data);
++    char *name = g_path_get_basename(fs_path->data);
++    int dirfd;
++
++    dirfd = local_opendir_nofollow(fs_ctx, dirpath);
++    if (dirfd == -1) {
++        goto out;
++    }
++
++    err = fstatat(dirfd, name, stbuf, AT_SYMLINK_NOFOLLOW);
++    if (err) {
++        goto err_out;
++    }
++    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 (fgetxattrat_nofollow(dirfd, name, "user.virtfs.uid", &tmp_uid,
++                                 sizeof(uid_t)) > 0) {
++            stbuf->st_uid = le32_to_cpu(tmp_uid);
++        }
++        if (fgetxattrat_nofollow(dirfd, name, "user.virtfs.gid", &tmp_gid,
++                                 sizeof(gid_t)) > 0) {
++            stbuf->st_gid = le32_to_cpu(tmp_gid);
++        }
++        if (fgetxattrat_nofollow(dirfd, name, "user.virtfs.mode", &tmp_mode,
++                                 sizeof(mode_t)) > 0) {
++            stbuf->st_mode = le32_to_cpu(tmp_mode);
++        }
++        if (fgetxattrat_nofollow(dirfd, name, "user.virtfs.rdev", &tmp_dev,
++                                 sizeof(dev_t)) > 0) {
++            stbuf->st_rdev = le64_to_cpu(tmp_dev);
++        }
++    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
++        local_mapped_file_attr(dirfd, name, stbuf);
++    }
++
++err_out:
++    close_preserve_errno(dirfd);
++out:
++    g_free(name);
++    g_free(dirpath);
++    return err;
++}
++
++static int local_set_mapped_file_attrat(int dirfd, const char *name,
++                                        FsCred *credp)
++{
++    FILE *fp;
++    int ret;
++    char buf[ATTR_MAX];
++    int uid = -1, gid = -1, mode = -1, rdev = -1;
++    int map_dirfd;
++
++    ret = mkdirat(dirfd, VIRTFS_META_DIR, 0700);
++    if (ret < 0 && errno != EEXIST) {
++        return -1;
++    }
++
++    map_dirfd = openat_dir(dirfd, VIRTFS_META_DIR);
++    if (map_dirfd == -1) {
++        return -1;
++    }
++
++    fp = local_fopenat(map_dirfd, name, "r");
++    if (!fp) {
++        if (errno == ENOENT) {
++            goto update_map_file;
++        } else {
++            close_preserve_errno(map_dirfd);
++            return -1;
++        }
++    }
++    memset(buf, 0, ATTR_MAX);
++    while (fgets(buf, ATTR_MAX, fp)) {
++        if (!strncmp(buf, "virtfs.uid", 10)) {
++            uid = atoi(buf + 11);
++        } else if (!strncmp(buf, "virtfs.gid", 10)) {
++            gid = atoi(buf + 11);
++        } else if (!strncmp(buf, "virtfs.mode", 11)) {
++            mode = atoi(buf + 12);
++        } else if (!strncmp(buf, "virtfs.rdev", 11)) {
++            rdev = atoi(buf + 12);
++        }
++        memset(buf, 0, ATTR_MAX);
++    }
++    fclose(fp);
++
++update_map_file:
++    fp = local_fopenat(map_dirfd, name, "w");
++    close_preserve_errno(map_dirfd);
++    if (!fp) {
++        return -1;
++    }
++
++    if (credp->fc_uid != -1) {
++        uid = credp->fc_uid;
++    }
++    if (credp->fc_gid != -1) {
++        gid = credp->fc_gid;
++    }
++    if (credp->fc_mode != -1) {
++        mode = credp->fc_mode;
++    }
++    if (credp->fc_rdev != -1) {
++        rdev = credp->fc_rdev;
++    }
++
++    if (uid != -1) {
++        fprintf(fp, "virtfs.uid=%d\n", uid);
++    }
++    if (gid != -1) {
++        fprintf(fp, "virtfs.gid=%d\n", gid);
++    }
++    if (mode != -1) {
++        fprintf(fp, "virtfs.mode=%d\n", mode);
++    }
++    if (rdev != -1) {
++        fprintf(fp, "virtfs.rdev=%d\n", rdev);
++    }
++    fclose(fp);
++
++    return 0;
++}
++
++static int fchmodat_nofollow(int dirfd, const char *name, mode_t mode)
++{
++    int fd, ret;
++
++    /* FIXME: this should be handled with fchmodat(AT_SYMLINK_NOFOLLOW).
++     * Unfortunately, the linux kernel doesn't implement it yet. As an
++     * alternative, let's open the file and use fchmod() instead. This
++     * may fail depending on the permissions of the file, but it is the
++     * best we can do to avoid TOCTTOU. We first try to open read-only
++     * in case name points to a directory. If that fails, we try write-only
++     * in case name doesn't point to a directory.
++     */
++    fd = openat_file(dirfd, name, O_RDONLY, 0);
++    if (fd == -1) {
++        /* In case the file is writable-only and isn't a directory. */
++        if (errno == EACCES) {
++            fd = openat_file(dirfd, name, O_WRONLY, 0);
++        }
++        if (fd == -1 && errno == EISDIR) {
++            errno = EACCES;
++        }
++    }
++    if (fd == -1) {
++        return -1;
++    }
++    ret = fchmod(fd, mode);
++    close_preserve_errno(fd);
++    return ret;
++}
++
++static int local_set_xattrat(int dirfd, const char *path, FsCred *credp)
++{
++    int err;
++
++    if (credp->fc_uid != -1) {
++        uint32_t tmp_uid = cpu_to_le32(credp->fc_uid);
++        err = fsetxattrat_nofollow(dirfd, path, "user.virtfs.uid", &tmp_uid,
++                                   sizeof(uid_t), 0);
++        if (err) {
++            return err;
++        }
++    }
++    if (credp->fc_gid != -1) {
++        uint32_t tmp_gid = cpu_to_le32(credp->fc_gid);
++        err = fsetxattrat_nofollow(dirfd, path, "user.virtfs.gid", &tmp_gid,
++                                   sizeof(gid_t), 0);
++        if (err) {
++            return err;
++        }
++    }
++    if (credp->fc_mode != -1) {
++        uint32_t tmp_mode = cpu_to_le32(credp->fc_mode);
++        err = fsetxattrat_nofollow(dirfd, path, "user.virtfs.mode", &tmp_mode,
++                                   sizeof(mode_t), 0);
++        if (err) {
++            return err;
++        }
++    }
++    if (credp->fc_rdev != -1) {
++        uint64_t tmp_rdev = cpu_to_le64(credp->fc_rdev);
++        err = fsetxattrat_nofollow(dirfd, path, "user.virtfs.rdev", &tmp_rdev,
++                                   sizeof(dev_t), 0);
++        if (err) {
++            return err;
++        }
++    }
++    return 0;
++}
++
++static int local_set_cred_passthrough(FsContext *fs_ctx, int dirfd,
++                                      const char *name, FsCred *credp)
++{
++    if (fchownat(dirfd, name, credp->fc_uid, credp->fc_gid,
++                 AT_SYMLINK_NOFOLLOW) < 0) {
++        /*
++         * If we fail to change ownership and if we are
++         * using security model none. Ignore the error
++         */
++        if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) {
++            return -1;
++        }
++    }
++
++    return fchmodat_nofollow(dirfd, name, credp->fc_mode & 07777);
++}
++
++static ssize_t local_readlink(FsContext *fs_ctx, V9fsPath *fs_path,
++                              char *buf, size_t bufsz)
++{
++    ssize_t tsize = -1;
++
++    if ((fs_ctx->export_flags & V9FS_SM_MAPPED) ||
++        (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE)) {
++        int fd;
++
++        fd = local_open_nofollow(fs_ctx, fs_path->data, O_RDONLY, 0);
++        if (fd == -1) {
++            return -1;
++        }
++        do {
++            tsize = read(fd, (void *)buf, bufsz);
++        } while (tsize == -1 && errno == EINTR);
++        close_preserve_errno(fd);
++    } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
++               (fs_ctx->export_flags & V9FS_SM_NONE)) {
++        char *dirpath = g_path_get_dirname(fs_path->data);
++        char *name = g_path_get_basename(fs_path->data);
++        int dirfd;
++
++        dirfd = local_opendir_nofollow(fs_ctx, dirpath);
++        if (dirfd == -1) {
++            goto out;
++        }
++
++        tsize = readlinkat(dirfd, name, buf, bufsz);
++        close_preserve_errno(dirfd);
++    out:
++        g_free(name);
++        g_free(dirpath);
++    }
++    return tsize;
++}
++
++static int local_close(FsContext *ctx, V9fsFidOpenState *fs)
++{
++    return close(fs->fd);
++}
++
++static int local_closedir(FsContext *ctx, V9fsFidOpenState *fs)
++{
++    return closedir(fs->dir.stream);
++}
++
++static int local_open(FsContext *ctx, V9fsPath *fs_path,
++                      int flags, V9fsFidOpenState *fs)
++{
++    int fd;
++
++    fd = local_open_nofollow(ctx, fs_path->data, flags, 0);
++    if (fd == -1) {
++        return -1;
++    }
++    fs->fd = fd;
++    return fs->fd;
++}
++
++static int local_opendir(FsContext *ctx,
++                         V9fsPath *fs_path, V9fsFidOpenState *fs)
++{
++    int dirfd;
++    DIR *stream;
++
++    dirfd = local_opendir_nofollow(ctx, fs_path->data);
++    if (dirfd == -1) {
++        return -1;
++    }
++
++    stream = fdopendir(dirfd);
++    if (!stream) {
++        close(dirfd);
++        return -1;
++    }
++    fs->dir.stream = stream;
++    return 0;
++}
++
++static void local_rewinddir(FsContext *ctx, V9fsFidOpenState *fs)
++{
++    rewinddir(fs->dir.stream);
++}
++
++static off_t local_telldir(FsContext *ctx, V9fsFidOpenState *fs)
++{
++    return telldir(fs->dir.stream);
++}
++
++static int local_readdir_r(FsContext *ctx, V9fsFidOpenState *fs,
++                           struct dirent *entry,
++                           struct dirent **result)
++{
++    int ret;
++
++again:
++    ret = readdir_r(fs->dir.stream, entry, result);
++    if (ctx->export_flags & V9FS_SM_MAPPED) {
++        entry->d_type = DT_UNKNOWN;
++    } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
++        if (!ret && *result != NULL &&
++            !strcmp(entry->d_name, VIRTFS_META_DIR)) {
++            /* skp the meta data directory */
++            goto again;
++        }
++        entry->d_type = DT_UNKNOWN;
++    }
++    return ret;
++}
++
++static void local_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off)
++{
++    seekdir(fs->dir.stream, off);
++}
++
++static ssize_t local_preadv(FsContext *ctx, V9fsFidOpenState *fs,
++                            const struct iovec *iov,
++                            int iovcnt, off_t offset)
++{
++#ifdef CONFIG_PREADV
++    return preadv(fs->fd, iov, iovcnt, offset);
++#else
++    int err = lseek(fs->fd, offset, SEEK_SET);
++    if (err == -1) {
++        return err;
++    } else {
++        return readv(fs->fd, iov, iovcnt);
++    }
++#endif
++}
++
++static ssize_t local_pwritev(FsContext *ctx, V9fsFidOpenState *fs,
++                             const struct iovec *iov,
++                             int iovcnt, off_t offset)
++{
++    ssize_t ret
++;
++#ifdef CONFIG_PREADV
++    ret = pwritev(fs->fd, iov, iovcnt, offset);
++#else
++    int err = lseek(fs->fd, offset, SEEK_SET);
++    if (err == -1) {
++        return err;
++    } else {
++        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)
++{
++    char *dirpath = g_path_get_dirname(fs_path->data);
++    char *name = g_path_get_basename(fs_path->data);
++    int ret = -1;
++    int dirfd;
++
++    dirfd = local_opendir_nofollow(fs_ctx, dirpath);
++    if (dirfd == -1) {
++        goto out;
++    }
++
++    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
++        ret = local_set_xattrat(dirfd, name, credp);
++    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
++        ret = local_set_mapped_file_attrat(dirfd, name, credp);
++    } else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH ||
++               fs_ctx->export_flags & V9FS_SM_NONE) {
++        ret = fchmodat_nofollow(dirfd, name, credp->fc_mode);
++    }
++    close_preserve_errno(dirfd);
++
++out:
++    g_free(dirpath);
++    g_free(name);
++    return ret;
++}
++
++static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path,
++                       const char *name, FsCred *credp)
++{
++    int err = -1;
++    int dirfd;
++
++    dirfd = local_opendir_nofollow(fs_ctx, dir_path->data);
++    if (dirfd == -1) {
++        return -1;
++    }
++
++    if (fs_ctx->export_flags & V9FS_SM_MAPPED ||
++        fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
++        err = mknodat(dirfd, name, SM_LOCAL_MODE_BITS | S_IFREG, 0);
++        if (err == -1) {
++            goto out;
++        }
++
++        if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
++            err = local_set_xattrat(dirfd, name, credp);
++        } else {
++            err = local_set_mapped_file_attrat(dirfd, name, credp);
++        }
++        if (err == -1) {
++            goto err_end;
++        }
++    } else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH ||
++               fs_ctx->export_flags & V9FS_SM_NONE) {
++        err = mknodat(dirfd, name, credp->fc_mode, credp->fc_rdev);
++        if (err == -1) {
++            goto out;
++        }
++        err = local_set_cred_passthrough(fs_ctx, dirfd, name, credp);
++        if (err == -1) {
++            goto err_end;
++        }
++    }
++    goto out;
++
++err_end:
++    unlinkat_preserve_errno(dirfd, name, 0);
++out:
++    close_preserve_errno(dirfd);
++    return err;
++}
++
++static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path,
++                       const char *name, FsCred *credp)
++{
++    int err = -1;
++    int dirfd;
++
++    dirfd = local_opendir_nofollow(fs_ctx, dir_path->data);
++    if (dirfd == -1) {
++        return -1;
++    }
++
++    if (fs_ctx->export_flags & V9FS_SM_MAPPED ||
++        fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
++        err = mkdirat(dirfd, name, SM_LOCAL_DIR_MODE_BITS);
++        if (err == -1) {
++            goto out;
++        }
++        credp->fc_mode = credp->fc_mode | S_IFDIR;
++
++        if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
++            err = local_set_xattrat(dirfd, name, credp);
++        } else {
++            err = local_set_mapped_file_attrat(dirfd, name, credp);
++        }
++        if (err == -1) {
++            goto err_end;
++        }
++    } else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH ||
++               fs_ctx->export_flags & V9FS_SM_NONE) {
++        err = mkdirat(dirfd, name, credp->fc_mode);
++        if (err == -1) {
++            goto out;
++        }
++        err = local_set_cred_passthrough(fs_ctx, dirfd, name, credp);
++        if (err == -1) {
++            goto err_end;
++        }
++    }
++    goto out;
++
++err_end:
++    unlinkat_preserve_errno(dirfd, name, AT_REMOVEDIR);
++out:
++    close_preserve_errno(dirfd);
++    return err;
++}
++
++static int local_fstat(FsContext *fs_ctx, int fid_type,
++                       V9fsFidOpenState *fs, struct stat *stbuf)
++{
++    int err, fd;
++
++    if (fid_type == P9_FID_DIR) {
++        fd = dirfd(fs->dir.stream);
++    } else {
++        fd = fs->fd;
++    }
++
++    err = fstat(fd, stbuf);
++    if (err) {
++        return err;
++    }
++    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) {
++            stbuf->st_uid = le32_to_cpu(tmp_uid);
++        }
++        if (fgetxattr(fd, "user.virtfs.gid", &tmp_gid, sizeof(gid_t)) > 0) {
++            stbuf->st_gid = le32_to_cpu(tmp_gid);
++        }
++        if (fgetxattr(fd, "user.virtfs.mode", &tmp_mode, sizeof(mode_t)) > 0) {
++            stbuf->st_mode = le32_to_cpu(tmp_mode);
++        }
++        if (fgetxattr(fd, "user.virtfs.rdev", &tmp_dev, sizeof(dev_t)) > 0) {
++            stbuf->st_rdev = le64_to_cpu(tmp_dev);
++        }
++    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
++        errno = EOPNOTSUPP;
++        return -1;
++    }
++    return err;
++}
++
++static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
++                       int flags, FsCred *credp, V9fsFidOpenState *fs)
++{
++    int fd = -1;
++    int err = -1;
++    int dirfd;
++
++    /*
++     * Mark all the open to not follow symlinks
++     */
++    flags |= O_NOFOLLOW;
++
++    dirfd = local_opendir_nofollow(fs_ctx, dir_path->data);
++    if (dirfd == -1) {
++        return -1;
++    }
++
++    /* Determine the security model */
++    if (fs_ctx->export_flags & V9FS_SM_MAPPED ||
++        fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
++        fd = openat_file(dirfd, name, flags, SM_LOCAL_MODE_BITS);
++        if (fd == -1) {
++            goto out;
++        }
++        credp->fc_mode = credp->fc_mode|S_IFREG;
++        if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
++            /* Set cleint credentials in xattr */
++            err = local_set_xattrat(dirfd, name, credp);
++        } else {
++            err = local_set_mapped_file_attrat(dirfd, name, credp);
++        }
++        if (err == -1) {
++            goto err_end;
++        }
++    } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
++               (fs_ctx->export_flags & V9FS_SM_NONE)) {
++        fd = openat_file(dirfd, name, flags, credp->fc_mode);
++        if (fd == -1) {
++            goto out;
++        }
++        err = local_set_cred_passthrough(fs_ctx, dirfd, name, credp);
++        if (err == -1) {
++            goto err_end;
++        }
++    }
++    err = fd;
++    fs->fd = fd;
++    goto out;
++
++err_end:
++    unlinkat_preserve_errno(dirfd, name,
++                            flags & O_DIRECTORY ? AT_REMOVEDIR : 0);
++    close_preserve_errno(fd);
++out:
++    close_preserve_errno(dirfd);
++    return err;
++}
++
++
++static int local_symlink(FsContext *fs_ctx, const char *oldpath,
++                         V9fsPath *dir_path, const char *name, FsCred *credp)
++{
++    int err = -1;
++    int dirfd;
++
++    dirfd = local_opendir_nofollow(fs_ctx, dir_path->data);
++    if (dirfd == -1) {
++        return -1;
++    }
++
++    /* Determine the security model */
++    if (fs_ctx->export_flags & V9FS_SM_MAPPED ||
++        fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
++        int fd;
++        ssize_t oldpath_size, write_size;
++
++        fd = openat_file(dirfd, name, O_CREAT | O_EXCL | O_RDWR,
++                         SM_LOCAL_MODE_BITS);
++        if (fd == -1) {
++            goto out;
++        }
++        /* Write the oldpath (target) to the file. */
++        oldpath_size = strlen(oldpath);
++        do {
++            write_size = write(fd, (void *)oldpath, oldpath_size);
++        } while (write_size == -1 && errno == EINTR);
++        close_preserve_errno(fd);
++
++        if (write_size != oldpath_size) {
++            goto err_end;
++        }
++        /* Set cleint credentials in symlink's xattr */
++        credp->fc_mode = credp->fc_mode | S_IFLNK;
++
++        if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
++            err = local_set_xattrat(dirfd, name, credp);
++        } else {
++            err = local_set_mapped_file_attrat(dirfd, name, credp);
++        }
++        if (err == -1) {
++            goto err_end;
++        }
++    } else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH ||
++               fs_ctx->export_flags & V9FS_SM_NONE) {
++        err = symlinkat(oldpath, dirfd, name);
++        if (err) {
++            goto out;
++        }
++        err = fchownat(dirfd, name, credp->fc_uid, credp->fc_gid,
++                       AT_SYMLINK_NOFOLLOW);
++        if (err == -1) {
++            /*
++             * If we fail to change ownership and if we are
++             * using security model none. Ignore the error
++             */
++            if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) {
++                goto err_end;
++            } else {
++                err = 0;
++            }
++        }
++    }
++    goto out;
++
++err_end:
++    unlinkat_preserve_errno(dirfd, name, 0);
++out:
++    close_preserve_errno(dirfd);
++    return err;
++}
++
++static int local_link(FsContext *ctx, V9fsPath *oldpath,
++                      V9fsPath *dirpath, const char *name)
++{
++    char *odirpath = g_path_get_dirname(oldpath->data);
++    char *oname = g_path_get_basename(oldpath->data);
++    int ret = -1;
++    int odirfd, ndirfd;
++
++    odirfd = local_opendir_nofollow(ctx, odirpath);
++    if (odirfd == -1) {
++        goto out;
++    }
++
++    ndirfd = local_opendir_nofollow(ctx, dirpath->data);
++    if (ndirfd == -1) {
++        close_preserve_errno(odirfd);
++        goto out;
++    }
++
++    ret = linkat(odirfd, oname, ndirfd, name, 0);
++    if (ret < 0) {
++        goto out_close;
++    }
++
++    /* now link the virtfs_metadata files */
++    if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
++        int omap_dirfd, nmap_dirfd;
++
++        ret = mkdirat(ndirfd, VIRTFS_META_DIR, 0700);
++        if (ret < 0 && errno != EEXIST) {
++            goto err_undo_link;
++        }
++
++        omap_dirfd = openat_dir(odirfd, VIRTFS_META_DIR);
++        if (omap_dirfd == -1) {
++            goto err;
++        }
++
++        nmap_dirfd = openat_dir(ndirfd, VIRTFS_META_DIR);
++        if (nmap_dirfd == -1) {
++            close_preserve_errno(omap_dirfd);
++            goto err;
++        }
++
++        ret = linkat(omap_dirfd, oname, nmap_dirfd, name, 0);
++        close_preserve_errno(nmap_dirfd);
++        close_preserve_errno(omap_dirfd);
++        if (ret < 0 && errno != ENOENT) {
++            goto err_undo_link;
++        }
++
++        ret = 0;
++    }
++    goto out_close;
++
++err:
++    ret = -1;
++err_undo_link:
++    unlinkat_preserve_errno(ndirfd, name, 0);
++out_close:
++    close_preserve_errno(ndirfd);
++    close_preserve_errno(odirfd);
++out:
++    g_free(oname);
++    g_free(odirpath);
++    return ret;
++}
++
++static int local_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size)
++{
++    int fd, ret;
++
++    fd = local_open_nofollow(ctx, fs_path->data, O_WRONLY, 0);
++    if (fd == -1) {
++        return -1;
++    }
++    ret = ftruncate(fd, size);
++    close_preserve_errno(fd);
++    return ret;
++}
++
++static int local_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
++{
++    char *dirpath = g_path_get_dirname(fs_path->data);
++    char *name = g_path_get_basename(fs_path->data);
++    int ret = -1;
++    int dirfd;
++
++    dirfd = local_opendir_nofollow(fs_ctx, dirpath);
++    if (dirfd == -1) {
++        goto out;
++    }
++
++    if ((credp->fc_uid == -1 && credp->fc_gid == -1) ||
++        (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
++        (fs_ctx->export_flags & V9FS_SM_NONE)) {
++        ret = fchownat(dirfd, name, credp->fc_uid, credp->fc_gid,
++                       AT_SYMLINK_NOFOLLOW);
++    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
++        ret = local_set_xattrat(dirfd, name, credp);
++    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
++        ret = local_set_mapped_file_attrat(dirfd, name, credp);
++    }
++
++    close_preserve_errno(dirfd);
++out:
++    g_free(name);
++    g_free(dirpath);
++    return ret;
++}
++
++static int local_utimensat(FsContext *s, V9fsPath *fs_path,
++                           const struct timespec *buf)
++{
++    char *dirpath = g_path_get_dirname(fs_path->data);
++    char *name = g_path_get_basename(fs_path->data);
++    int dirfd, ret = -1;
++
++    dirfd = local_opendir_nofollow(s, dirpath);
++    if (dirfd == -1) {
++        goto out;
++    }
++
++    ret = utimensat(dirfd, name, buf, AT_SYMLINK_NOFOLLOW);
++    close_preserve_errno(dirfd);
++out:
++    g_free(dirpath);
++    g_free(name);
++    return ret;
++}
++
++static int local_unlinkat_common(FsContext *ctx, int dirfd, const char *name,
++                                 int flags)
++{
++    int ret = -1;
++
++    if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
++        int map_dirfd;
++
++        if (flags == AT_REMOVEDIR) {
++            int fd;
++
++            fd = openat_dir(dirfd, name);
++            if (fd == -1) {
++                goto err_out;
++            }
++            /*
++             * If directory remove .virtfs_metadata contained in the
++             * directory
++             */
++            ret = unlinkat(fd, VIRTFS_META_DIR, AT_REMOVEDIR);
++            close_preserve_errno(fd);
++            if (ret < 0 && errno != ENOENT) {
++                /*
++                 * We didn't had the .virtfs_metadata file. May be file created
++                 * in non-mapped mode ?. Ignore ENOENT.
++                 */
++                goto err_out;
++            }
++        }
++        /*
++         * Now remove the name from parent directory
++         * .virtfs_metadata directory.
++         */
++        map_dirfd = openat_dir(dirfd, VIRTFS_META_DIR);
++        ret = unlinkat(map_dirfd, name, 0);
++        close_preserve_errno(map_dirfd);
++        if (ret < 0 && errno != ENOENT) {
++            /*
++             * We didn't had the .virtfs_metadata file. May be file created
++             * in non-mapped mode ?. Ignore ENOENT.
++             */
++            goto err_out;
++        }
++    }
++
++    ret = unlinkat(dirfd, name, flags);
++err_out:
++    return ret;
++}
++
++static int local_remove(FsContext *ctx, const char *path)
++{
++    struct stat stbuf;
++    char *dirpath = g_path_get_dirname(path);
++    char *name = g_path_get_basename(path);
++    int flags = 0;
++    int dirfd;
++    int err = -1;
++
++    dirfd = local_opendir_nofollow(ctx, dirpath);
++    if (dirfd == -1) {
++        goto out;
++    }
++
++    if (fstatat(dirfd, path, &stbuf, AT_SYMLINK_NOFOLLOW) < 0) {
++        goto err_out;
++    }
++
++    if (S_ISDIR(stbuf.st_mode)) {
++        flags |= AT_REMOVEDIR;
++    }
++
++    err = local_unlinkat_common(ctx, dirfd, name, flags);
++err_out:
++    close_preserve_errno(dirfd);
++out:
++    g_free(name);
++    g_free(dirpath);
++    return err;
++}
++
++static int local_fsync(FsContext *ctx, int fid_type,
++                       V9fsFidOpenState *fs, int datasync)
++{
++    int fd;
++
++    if (fid_type == P9_FID_DIR) {
++        fd = dirfd(fs->dir.stream);
++    } else {
++        fd = fs->fd;
++    }
++
++    if (datasync) {
++        return qemu_fdatasync(fd);
++    } else {
++        return fsync(fd);
++    }
++}
++
++static int local_statfs(FsContext *s, V9fsPath *fs_path, struct statfs *stbuf)
++{
++    int fd, ret;
++
++    fd = local_open_nofollow(s, fs_path->data, O_RDONLY, 0);
++    if (fd == -1) {
++        return -1;
++    }
++    ret = fstatfs(fd, stbuf);
++    close_preserve_errno(fd);
++    return ret;
++}
++
++static ssize_t local_lgetxattr(FsContext *ctx, V9fsPath *fs_path,
++                               const char *name, void *value, size_t size)
++{
++    char *path = fs_path->data;
++
++    return v9fs_get_xattr(ctx, path, name, value, size);
++}
++
++static ssize_t local_llistxattr(FsContext *ctx, V9fsPath *fs_path,
++                                void *value, size_t size)
++{
++    char *path = fs_path->data;
++
++    return v9fs_list_xattr(ctx, path, value, size);
++}
++
++static int local_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name,
++                           void *value, size_t size, int flags)
++{
++    char *path = fs_path->data;
++
++    return v9fs_set_xattr(ctx, path, name, value, size, flags);
++}
++
++static int local_lremovexattr(FsContext *ctx, V9fsPath *fs_path,
++                              const char *name)
++{
++    char *path = fs_path->data;
++
++    return v9fs_remove_xattr(ctx, path, name);
++}
++
++static int local_name_to_path(FsContext *ctx, V9fsPath *dir_path,
++                              const char *name, V9fsPath *target)
++{
++    if (dir_path) {
++        v9fs_path_sprintf(target, "%s/%s", dir_path->data, name);
++    } else if (strcmp(name, "/")) {
++        v9fs_path_sprintf(target, "%s", name);
++    } else {
++        /* We want the path of the export root to be relative, otherwise
++         * "*at()" syscalls would treat it as "/" in the host.
++         */
++        v9fs_path_sprintf(target, "%s", ".");
++    }
++    return 0;
++}
++
++static int local_renameat(FsContext *ctx, V9fsPath *olddir,
++                          const char *old_name, V9fsPath *newdir,
++                          const char *new_name)
++{
++    int ret;
++    int odirfd, ndirfd;
++
++    odirfd = local_opendir_nofollow(ctx, olddir->data);
++    if (odirfd == -1) {
++        return -1;
++    }
++
++    ndirfd = local_opendir_nofollow(ctx, newdir->data);
++    if (ndirfd == -1) {
++        close_preserve_errno(odirfd);
++        return -1;
++    }
++
++    ret = renameat(odirfd, old_name, ndirfd, new_name);
++    if (ret < 0) {
++        goto out;
++    }
++
++    if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
++        int omap_dirfd, nmap_dirfd;
++
++        ret = mkdirat(ndirfd, VIRTFS_META_DIR, 0700);
++        if (ret < 0 && errno != EEXIST) {
++            goto err_undo_rename;
++        }
++
++        omap_dirfd = openat_dir(odirfd, VIRTFS_META_DIR);
++        if (omap_dirfd == -1) {
++            goto err;
++        }
++
++        nmap_dirfd = openat_dir(ndirfd, VIRTFS_META_DIR);
++        if (nmap_dirfd == -1) {
++            close_preserve_errno(omap_dirfd);
++            goto err;
++        }
++
++        /* rename the .virtfs_metadata files */
++        ret = renameat(omap_dirfd, old_name, nmap_dirfd, new_name);
++        close_preserve_errno(nmap_dirfd);
++        close_preserve_errno(omap_dirfd);
++        if (ret < 0 && errno != ENOENT) {
++            goto err_undo_rename;
++        }
++
++        ret = 0;
++    }
++    goto out;
++
++err:
++    ret = -1;
++err_undo_rename:
++    renameat_preserve_errno(ndirfd, new_name, odirfd, old_name);
++out:
++    close_preserve_errno(ndirfd);
++    close_preserve_errno(odirfd);
++    return ret;
++}
++
++static void v9fs_path_init_dirname(V9fsPath *path, const char *str)
++{
++    path->data = g_path_get_dirname(str);
++    path->size = strlen(path->data) + 1;
++}
++
++static int local_rename(FsContext *ctx, const char *oldpath,
++                        const char *newpath)
++{
++    int err;
++    char *oname = g_path_get_basename(oldpath);
++    char *nname = g_path_get_basename(newpath);
++    V9fsPath olddir, newdir;
++
++    v9fs_path_init_dirname(&olddir, oldpath);
++    v9fs_path_init_dirname(&newdir, newpath);
++
++    err = local_renameat(ctx, &olddir, oname, &newdir, nname);
++
++    v9fs_path_free(&newdir);
++    v9fs_path_free(&olddir);
++    g_free(nname);
++    g_free(oname);
++
++    return err;
++}
++
++static int local_unlinkat(FsContext *ctx, V9fsPath *dir,
++                          const char *name, int flags)
++{
++    int ret;
++    int dirfd;
++
++    dirfd = local_opendir_nofollow(ctx, dir->data);
++    if (dirfd == -1) {
++        return -1;
++    }
++
++    ret = local_unlinkat_common(ctx, dirfd, name, flags);
++    close_preserve_errno(dirfd);
++    return ret;
++}
++
++static int local_ioc_getversion(FsContext *ctx, V9fsPath *path,
++                                mode_t st_mode, uint64_t *st_gen)
++{
++#ifdef FS_IOC_GETVERSION
++    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)) {
++        errno = ENOTTY;
++        return -1;
++    }
++    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;
++#else
++    errno = ENOTTY;
++    return -1;
++#endif
++}
++
++static int local_init(FsContext *ctx)
++{
++    struct statfs stbuf;
++    LocalData *data = g_malloc(sizeof(*data));
++
++    data->mountfd = open(ctx->fs_root, O_DIRECTORY | O_RDONLY);
++    if (data->mountfd == -1) {
++        goto err;
++    }
++
++#ifdef FS_IOC_GETVERSION
++    /*
++     * use ioc_getversion only if the ioctl is definied
++     */
++    if (fstatfs(data->mountfd, &stbuf) < 0) {
++        close_preserve_errno(data->mountfd);
++        goto 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;
++    }
++#endif
++
++    if (ctx->export_flags & V9FS_SM_PASSTHROUGH) {
++        ctx->xops = passthrough_xattr_ops;
++    } else if (ctx->export_flags & V9FS_SM_MAPPED) {
++        ctx->xops = mapped_xattr_ops;
++    } else if (ctx->export_flags & V9FS_SM_NONE) {
++        ctx->xops = none_xattr_ops;
++    } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
++        /*
++         * xattr operation for mapped-file and passthrough
++         * remain same.
++         */
++        ctx->xops = passthrough_xattr_ops;
++    }
++    ctx->export_flags |= V9FS_PATHNAME_FSCONTEXT;
++
++    ctx->private = data;
++    return 0;
++
++err:
++    g_free(data);
++    return -1;
++}
++
++static void local_cleanup(FsContext *ctx)
++{
++    LocalData *data = ctx->private;
++
++    close(data->mountfd);
++    g_free(data);
++}
++
++static int local_parse_opts(QemuOpts *opts, struct FsDriverEntry *fse)
++{
++    const char *sec_model = qemu_opt_get(opts, "security_model");
++    const char *path = qemu_opt_get(opts, "path");
++
++    if (!sec_model) {
++        error_report("Security model not specified, local fs needs security model");
++        error_printf("valid options are:"
++                     "\tsecurity_model=[passthrough|mapped-xattr|mapped-file|none]\n");
++        return -1;
++    }
++
++    if (!strcmp(sec_model, "passthrough")) {
++        fse->export_flags |= V9FS_SM_PASSTHROUGH;
++    } else if (!strcmp(sec_model, "mapped") ||
++               !strcmp(sec_model, "mapped-xattr")) {
++        fse->export_flags |= V9FS_SM_MAPPED;
++    } else if (!strcmp(sec_model, "none")) {
++        fse->export_flags |= V9FS_SM_NONE;
++    } else if (!strcmp(sec_model, "mapped-file")) {
++        fse->export_flags |= V9FS_SM_MAPPED_FILE;
++    } else {
++        error_report("Invalid security model %s specified", sec_model);
++        error_printf("valid options are:"
++                     "\t[passthrough|mapped-xattr|mapped-file|none]\n");
++        return -1;
++    }
++
++    if (!path) {
++        error_report("fsdev: No path specified");
++        return -1;
++    }
++    fse->path = g_strdup(path);
++
++    return 0;
++}
++
++FileOperations local_ops = {
++    .parse_opts = local_parse_opts,
++    .init  = local_init,
++    .cleanup = local_cleanup,
++    .lstat = local_lstat,
++    .readlink = local_readlink,
++    .close = local_close,
++    .closedir = local_closedir,
++    .open = local_open,
++    .opendir = local_opendir,
++    .rewinddir = local_rewinddir,
++    .telldir = local_telldir,
++    .readdir_r = local_readdir_r,
++    .seekdir = local_seekdir,
++    .preadv = local_preadv,
++    .pwritev = local_pwritev,
++    .chmod = local_chmod,
++    .mknod = local_mknod,
++    .mkdir = local_mkdir,
++    .fstat = local_fstat,
++    .open2 = local_open2,
++    .symlink = local_symlink,
++    .link = local_link,
++    .truncate = local_truncate,
++    .rename = local_rename,
++    .chown = local_chown,
++    .utimensat = local_utimensat,
++    .remove = local_remove,
++    .fsync = local_fsync,
++    .statfs = local_statfs,
++    .lgetxattr = local_lgetxattr,
++    .llistxattr = local_llistxattr,
++    .lsetxattr = local_lsetxattr,
++    .lremovexattr = local_lremovexattr,
++    .name_to_path = local_name_to_path,
++    .renameat  = local_renameat,
++    .unlinkat = local_unlinkat,
++};
+diff --git a/hw/9pfs/9p-local.h b/hw/9pfs/9p-local.h
+new file mode 100644
+index 0000000..32c7274
+--- /dev/null
++++ b/hw/9pfs/9p-local.h
+@@ -0,0 +1,20 @@
++/*
++ * 9p local backend utilities
++ *
++ * Copyright IBM, Corp. 2017
++ *
++ * Authors:
++ *  Greg Kurz <groug@kaod.org>
++ *
++ * This work is licensed under the terms of the GNU GPL, version 2 or later.
++ * See the COPYING file in the top-level directory.
++ */
++
++#ifndef QEMU_9P_LOCAL_H
++#define QEMU_9P_LOCAL_H
++
++int local_open_nofollow(FsContext *fs_ctx, const char *path, int flags,
++                        mode_t mode);
++int local_opendir_nofollow(FsContext *fs_ctx, const char *path);
++
++#endif
+diff --git a/hw/9pfs/9p-posix-acl.c b/hw/9pfs/9p-posix-acl.c
+new file mode 100644
+index 0000000..1da2b3d
+--- /dev/null
++++ b/hw/9pfs/9p-posix-acl.c
+@@ -0,0 +1,156 @@
++/*
++ * 9p system.posix* xattr callback
++ *
++ * Copyright IBM, Corp. 2010
++ *
++ * 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 "osdep.h"
++#include "qemu-xattr.h"
++#include "virtio-9p.h"
++#include "fsdev/file-op-9p.h"
++#include "9p-xattr.h"
++
++#define MAP_ACL_ACCESS "user.virtfs.system.posix_acl_access"
++#define MAP_ACL_DEFAULT "user.virtfs.system.posix_acl_default"
++#define ACL_ACCESS "system.posix_acl_access"
++#define ACL_DEFAULT "system.posix_acl_default"
++
++static ssize_t mp_pacl_getxattr(FsContext *ctx, const char *path,
++                                const char *name, void *value, size_t size)
++{
++    return local_getxattr_nofollow(ctx, path, MAP_ACL_ACCESS, value, size);
++}
++
++static ssize_t mp_pacl_listxattr(FsContext *ctx, const char *path,
++                                 char *name, void *value, size_t osize)
++{
++    ssize_t len = sizeof(ACL_ACCESS);
++
++    if (!value) {
++        return len;
++    }
++
++    if (osize < len) {
++        errno = ERANGE;
++        return -1;
++    }
++
++    /* len includes the trailing NUL */
++    memcpy(value, ACL_ACCESS, len);
++    return 0;
++}
++
++static int mp_pacl_setxattr(FsContext *ctx, const char *path, const char *name,
++                            void *value, size_t size, int flags)
++{
++    return local_setxattr_nofollow(ctx, path, MAP_ACL_ACCESS, value, size,
++                                   flags);
++}
++
++static int mp_pacl_removexattr(FsContext *ctx,
++                               const char *path, const char *name)
++{
++    int ret;
++
++    ret = local_removexattr_nofollow(ctx, path, MAP_ACL_ACCESS);
++    if (ret == -1 && errno == ENODATA) {
++        /*
++         * We don't get ENODATA error when trying to remove a
++         * posix acl that is not present. So don't throw the error
++         * even in case of mapped security model
++         */
++        errno = 0;
++        ret = 0;
++    }
++    return ret;
++}
++
++static ssize_t mp_dacl_getxattr(FsContext *ctx, const char *path,
++                                const char *name, void *value, size_t size)
++{
++    return local_getxattr_nofollow(ctx, path, MAP_ACL_DEFAULT, value, size);
++}
++
++static ssize_t mp_dacl_listxattr(FsContext *ctx, const char *path,
++                                 char *name, void *value, size_t osize)
++{
++    ssize_t len = sizeof(ACL_DEFAULT);
++
++    if (!value) {
++        return len;
++    }
++
++    if (osize < len) {
++        errno = ERANGE;
++        return -1;
++    }
++
++    /* len includes the trailing NUL */
++    memcpy(value, ACL_DEFAULT, len);
++    return 0;
++}
++
++static int mp_dacl_setxattr(FsContext *ctx, const char *path, const char *name,
++                            void *value, size_t size, int flags)
++{
++    return local_setxattr_nofollow(ctx, path, MAP_ACL_DEFAULT, value, size,
++                                   flags);
++}
++
++static int mp_dacl_removexattr(FsContext *ctx,
++                               const char *path, const char *name)
++{
++    int ret;
++
++    ret = local_removexattr_nofollow(ctx, path, MAP_ACL_DEFAULT);
++    if (ret == -1 && errno == ENODATA) {
++        /*
++         * We don't get ENODATA error when trying to remove a
++         * posix acl that is not present. So don't throw the error
++         * even in case of mapped security model
++         */
++        errno = 0;
++        ret = 0;
++    }
++    return ret;
++}
++
++
++XattrOperations mapped_pacl_xattr = {
++    .name = "system.posix_acl_access",
++    .getxattr = mp_pacl_getxattr,
++    .setxattr = mp_pacl_setxattr,
++    .listxattr = mp_pacl_listxattr,
++    .removexattr = mp_pacl_removexattr,
++};
++
++XattrOperations mapped_dacl_xattr = {
++    .name = "system.posix_acl_default",
++    .getxattr = mp_dacl_getxattr,
++    .setxattr = mp_dacl_setxattr,
++    .listxattr = mp_dacl_listxattr,
++    .removexattr = mp_dacl_removexattr,
++};
++
++XattrOperations passthrough_acl_xattr = {
++    .name = "system.posix_acl_",
++    .getxattr = pt_getxattr,
++    .setxattr = pt_setxattr,
++    .listxattr = pt_listxattr,
++    .removexattr = pt_removexattr,
++};
++
++XattrOperations none_acl_xattr = {
++    .name = "system.posix_acl_",
++    .getxattr = notsup_getxattr,
++    .setxattr = notsup_setxattr,
++    .listxattr = notsup_listxattr,
++    .removexattr = notsup_removexattr,
++};
+diff --git a/hw/9pfs/9p-util.c b/hw/9pfs/9p-util.c
+new file mode 100644
+index 0000000..b65c43e
+--- /dev/null
++++ b/hw/9pfs/9p-util.c
+@@ -0,0 +1,84 @@
++/*
++ * 9p utilities
++ *
++ * Copyright IBM, Corp. 2017
++ *
++ * Authors:
++ *  Greg Kurz <groug@kaod.org>
++ *
++ * This work is licensed under the terms of the GNU GPL, version 2 or later.
++ * See the COPYING file in the top-level directory.
++ */
++
++#include "osdep.h"
++#include "qemu-xattr.h"
++
++/* include via osdep.h in newer qemu */
++#include <unistd.h>
++#include <time.h>
++#include <ctype.h>
++#include <errno.h>
++#include <fcntl.h>
++#include <sys/stat.h>
++#include <sys/time.h>
++#include <assert.h>
++#include <string.h>
++
++#include <glib.h>
++
++#include "9p-util.h"
++
++
++int relative_openat_nofollow(int dirfd, const char *path, int flags,
++                             mode_t mode)
++{
++    int fd;
++
++    fd = dup(dirfd);
++    if (fd == -1) {
++        return -1;
++    }
++
++    while (*path) {
++        const char *c;
++        int next_fd;
++        char *head;
++
++        /* Only relative paths without consecutive slashes */
++        assert(path[0] != '/');
++
++        head = g_strdup(path);
++        c = strchr(path, '/');
++        if (c) {
++            head[c - path] = 0;
++            next_fd = openat_dir(fd, head);
++        } else {
++            next_fd = openat_file(fd, head, flags, mode);
++        }
++        g_free(head);
++        if (next_fd == -1) {
++            close_preserve_errno(fd);
++            return -1;
++        }
++        close(fd);
++        fd = next_fd;
++
++        if (!c) {
++            break;
++        }
++        path = c + 1;
++    }
++
++    return fd;
++}
++
++ssize_t fgetxattrat_nofollow(int dirfd, const char *filename, const char *name,
++                             void *value, size_t size)
++{
++    char *proc_path = g_strdup_printf("/proc/self/fd/%d/%s", dirfd, filename);
++    int ret;
++
++    ret = lgetxattr(proc_path, name, value, size);
++    g_free(proc_path);
++    return ret;
++}
+diff --git a/hw/9pfs/9p-util.h b/hw/9pfs/9p-util.h
+new file mode 100644
+index 0000000..517027c
+--- /dev/null
++++ b/hw/9pfs/9p-util.h
+@@ -0,0 +1,60 @@
++/*
++ * 9p utilities
++ *
++ * Copyright IBM, Corp. 2017
++ *
++ * Authors:
++ *  Greg Kurz <groug@kaod.org>
++ *
++ * This work is licensed under the terms of the GNU GPL, version 2 or later.
++ * See the COPYING file in the top-level directory.
++ */
++
++#ifndef QEMU_9P_UTIL_H
++#define QEMU_9P_UTIL_H
++
++static inline void close_preserve_errno(int fd)
++{
++    int serrno = errno;
++    close(fd);
++    errno = serrno;
++}
++
++static inline int openat_dir(int dirfd, const char *name)
++{
++#ifdef O_PATH
++#define OPENAT_DIR_O_PATH O_PATH
++#else
++#define OPENAT_DIR_O_PATH 0
++#endif
++    return openat(dirfd, name,
++                  O_DIRECTORY | O_RDONLY | O_NOFOLLOW | OPENAT_DIR_O_PATH);
++}
++
++static inline int openat_file(int dirfd, const char *name, int flags,
++                              mode_t mode)
++{
++    int fd, serrno, ret;
++
++    fd = openat(dirfd, name, flags | O_NOFOLLOW | O_NOCTTY | O_NONBLOCK,
++                mode);
++    if (fd == -1) {
++        return -1;
++    }
++
++    serrno = errno;
++    /* O_NONBLOCK was only needed to open the file. Let's drop it. */
++    ret = fcntl(fd, F_SETFL, flags);
++    assert(!ret);
++    errno = serrno;
++    return fd;
++}
++
++int relative_openat_nofollow(int dirfd, const char *path, int flags,
++                             mode_t mode);
++ssize_t fgetxattrat_nofollow(int dirfd, const char *path, const char *name,
++                             void *value, size_t size);
++int fsetxattrat_nofollow(int dirfd, const char *path, const char *name,
++                         void *value, size_t size, int flags);
++
++#endif
+diff --git a/hw/9pfs/9p-xattr-user.c b/hw/9pfs/9p-xattr-user.c
+new file mode 100644
+index 0000000..14a87a6
+--- /dev/null
++++ b/hw/9pfs/9p-xattr-user.c
+@@ -0,0 +1,109 @@
++/*
++ * 9p user. xattr callback
++ *
++ * Copyright IBM, Corp. 2010
++ *
++ * 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 "osdep.h"
++#include "virtio-9p.h"
++#include "fsdev/file-op-9p.h"
++#include "9p-xattr.h"
++
++
++static ssize_t mp_user_getxattr(FsContext *ctx, const char *path,
++                                const char *name, void *value, size_t size)
++{
++    if (strncmp(name, "user.virtfs.", 12) == 0) {
++        /*
++         * Don't allow fetch of user.virtfs namesapce
++         * in case of mapped security
++         */
++        errno = ENOATTR;
++        return -1;
++    }
++    return local_getxattr_nofollow(ctx, path, name, value, size);
++}
++
++static ssize_t mp_user_listxattr(FsContext *ctx, const char *path,
++                                 char *name, void *value, size_t size)
++{
++    int name_size = strlen(name) + 1;
++    if (strncmp(name, "user.virtfs.", 12) == 0) {
++
++        /*  check if it is a mapped posix acl */
++        if (strncmp(name, "user.virtfs.system.posix_acl_", 29) == 0) {
++            /* adjust the name and size */
++            name += 12;
++            name_size -= 12;
++        } else {
++            /*
++             * Don't allow fetch of user.virtfs namesapce
++             * in case of mapped security
++             */
++            return 0;
++        }
++    }
++    if (!value) {
++        return name_size;
++    }
++
++    if (size < name_size) {
++        errno = ERANGE;
++        return -1;
++    }
++
++    /* name_size includes the trailing NUL. */
++    memcpy(value, name, name_size);
++    return name_size;
++}
++
++static int mp_user_setxattr(FsContext *ctx, const char *path, const char *name,
++                            void *value, size_t size, int flags)
++{
++    if (strncmp(name, "user.virtfs.", 12) == 0) {
++        /*
++         * Don't allow fetch of user.virtfs namesapce
++         * in case of mapped security
++         */
++        errno = EACCES;
++        return -1;
++    }
++    return local_setxattr_nofollow(ctx, path, name, value, size, flags);
++}
++
++static int mp_user_removexattr(FsContext *ctx,
++                               const char *path, const char *name)
++{
++    if (strncmp(name, "user.virtfs.", 12) == 0) {
++        /*
++         * Don't allow fetch of user.virtfs namesapce
++         * in case of mapped security
++         */
++        errno = EACCES;
++        return -1;
++    }
++    return local_removexattr_nofollow(ctx, path, name);
++}
++
++XattrOperations mapped_user_xattr = {
++    .name = "user.",
++    .getxattr = mp_user_getxattr,
++    .setxattr = mp_user_setxattr,
++    .listxattr = mp_user_listxattr,
++    .removexattr = mp_user_removexattr,
++};
++
++XattrOperations passthrough_user_xattr = {
++    .name = "user.",
++    .getxattr = pt_getxattr,
++    .setxattr = pt_setxattr,
++    .listxattr = pt_listxattr,
++    .removexattr = pt_removexattr,
++};
+diff --git a/hw/9pfs/9p-xattr.c b/hw/9pfs/9p-xattr.c
+new file mode 100644
+index 0000000..40b644b
+--- /dev/null
++++ b/hw/9pfs/9p-xattr.c
+@@ -0,0 +1,318 @@
++/*
++ * 9p  xattr callback
++ *
++ * Copyright IBM, Corp. 2010
++ *
++ * 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 "osdep.h"
++#include "virtio-9p.h"
++#include "fsdev/file-op-9p.h"
++#include "9p-xattr.h"
++#include "9p-util.h"
++#include "9p-local.h"
++
++
++static XattrOperations *get_xattr_operations(XattrOperations **h,
++                                             const char *name)
++{
++    XattrOperations *xops;
++    for (xops = *(h)++; xops != NULL; xops = *(h)++) {
++        if (!strncmp(name, xops->name, strlen(xops->name))) {
++            return xops;
++        }
++    }
++    return NULL;
++}
++
++ssize_t v9fs_get_xattr(FsContext *ctx, const char *path,
++                       const char *name, void *value, size_t size)
++{
++    XattrOperations *xops = get_xattr_operations(ctx->xops, name);
++    if (xops) {
++        return xops->getxattr(ctx, path, name, value, size);
++    }
++    errno = EOPNOTSUPP;
++    return -1;
++}
++
++ssize_t pt_listxattr(FsContext *ctx, const char *path,
++                     char *name, void *value, size_t size)
++{
++    int name_size = strlen(name) + 1;
++    if (!value) {
++        return name_size;
++    }
++
++    if (size < name_size) {
++        errno = ERANGE;
++        return -1;
++    }
++
++    /* no need for strncpy: name_size is strlen(name)+1 */
++    memcpy(value, name, name_size);
++    return name_size;
++}
++
++static ssize_t flistxattrat_nofollow(int dirfd, const char *filename,
++                                     char *list, size_t size)
++{
++    char *proc_path = g_strdup_printf("/proc/self/fd/%d/%s", dirfd, filename);
++    int ret;
++
++    ret = llistxattr(proc_path, list, size);
++    g_free(proc_path);
++    return ret;
++}
++
++/*
++ * Get the list and pass to each layer to find out whether
++ * to send the data or not
++ */
++ssize_t v9fs_list_xattr(FsContext *ctx, const char *path,
++                        void *value, size_t vsize)
++{
++    ssize_t size = 0;
++    void *ovalue = value;
++    XattrOperations *xops;
++    char *orig_value, *orig_value_start;
++    ssize_t xattr_len, parsed_len = 0, attr_len;
++    char *dirpath, *name;
++    int dirfd;
++
++    /* Get the actual len */
++    dirpath = g_path_get_dirname(path);
++    dirfd = local_opendir_nofollow(ctx, dirpath);
++    g_free(dirpath);
++    if (dirfd == -1) {
++        return -1;
++    }
++
++    name = g_path_get_basename(path);
++    xattr_len = flistxattrat_nofollow(dirfd, name, value, 0);
++    if (xattr_len <= 0) {
++        g_free(name);
++        close_preserve_errno(dirfd);
++        return xattr_len;
++    }
++
++    /* Now fetch the xattr and find the actual size */
++    orig_value = g_malloc(xattr_len);
++    xattr_len = flistxattrat_nofollow(dirfd, name, orig_value, xattr_len);
++    g_free(name);
++    close_preserve_errno(dirfd);
++    if (xattr_len < 0) {
++        return -1;
++    }
++
++    /* store the orig pointer */
++    orig_value_start = orig_value;
++    while (xattr_len > parsed_len) {
++        xops = get_xattr_operations(ctx->xops, orig_value);
++        if (!xops) {
++            goto next_entry;
++        }
++
++        if (!value) {
++            size += xops->listxattr(ctx, path, orig_value, value, vsize);
++        } else {
++            size = xops->listxattr(ctx, path, orig_value, value, vsize);
++            if (size < 0) {
++                goto err_out;
++            }
++            value += size;
++            vsize -= size;
++        }
++next_entry:
++        /* Got the next entry */
++        attr_len = strlen(orig_value) + 1;
++        parsed_len += attr_len;
++        orig_value += attr_len;
++    }
++    if (value) {
++        size = value - ovalue;
++    }
++
++err_out:
++    g_free(orig_value_start);
++    return size;
++}
++
++int v9fs_set_xattr(FsContext *ctx, const char *path, const char *name,
++                   void *value, size_t size, int flags)
++{
++    XattrOperations *xops = get_xattr_operations(ctx->xops, name);
++    if (xops) {
++        return xops->setxattr(ctx, path, name, value, size, flags);
++    }
++    errno = EOPNOTSUPP;
++    return -1;
++
++}
++
++int v9fs_remove_xattr(FsContext *ctx,
++                      const char *path, const char *name)
++{
++    XattrOperations *xops = get_xattr_operations(ctx->xops, name);
++    if (xops) {
++        return xops->removexattr(ctx, path, name);
++    }
++    errno = EOPNOTSUPP;
++    return -1;
++
++}
++
++ssize_t local_getxattr_nofollow(FsContext *ctx, const char *path,
++                                const char *name, void *value, size_t size)
++{
++    char *dirpath = g_path_get_dirname(path);
++    char *filename = g_path_get_basename(path);
++    int dirfd;
++    ssize_t ret = -1;
++
++    dirfd = local_opendir_nofollow(ctx, dirpath);
++    if (dirfd == -1) {
++        goto out;
++    }
++
++    ret = fgetxattrat_nofollow(dirfd, filename, name, value, size);
++    close_preserve_errno(dirfd);
++out:
++    g_free(dirpath);
++    g_free(filename);
++    return ret;
++}
++
++ssize_t pt_getxattr(FsContext *ctx, const char *path, const char *name,
++                    void *value, size_t size)
++{
++    return local_getxattr_nofollow(ctx, path, name, value, size);
++}
++
++int fsetxattrat_nofollow(int dirfd, const char *filename, const char *name,
++                         void *value, size_t size, int flags)
++{
++    char *proc_path = g_strdup_printf("/proc/self/fd/%d/%s", dirfd, filename);
++    int ret;
++
++    ret = lsetxattr(proc_path, name, value, size, flags);
++    g_free(proc_path);
++    return ret;
++}
++
++ssize_t local_setxattr_nofollow(FsContext *ctx, const char *path,
++                                const char *name, void *value, size_t size,
++                                int flags)
++{
++    char *dirpath = g_path_get_dirname(path);
++    char *filename = g_path_get_basename(path);
++    int dirfd;
++    ssize_t ret = -1;
++
++    dirfd = local_opendir_nofollow(ctx, dirpath);
++    if (dirfd == -1) {
++        goto out;
++    }
++
++    ret = fsetxattrat_nofollow(dirfd, filename, name, value, size, flags);
++    close_preserve_errno(dirfd);
++out:
++    g_free(dirpath);
++    g_free(filename);
++    return ret;
++}
++
++int pt_setxattr(FsContext *ctx, const char *path, const char *name, void *value,
++                size_t size, int flags)
++{
++    return local_setxattr_nofollow(ctx, path, name, value, size, flags);
++}
++
++static ssize_t fremovexattrat_nofollow(int dirfd, const char *filename,
++                                       const char *name)
++{
++    char *proc_path = g_strdup_printf("/proc/self/fd/%d/%s", dirfd, filename);
++    int ret;
++
++    ret = lremovexattr(proc_path, name);
++    g_free(proc_path);
++    return ret;
++}
++
++ssize_t local_removexattr_nofollow(FsContext *ctx, const char *path,
++                                   const char *name)
++{
++    char *dirpath = g_path_get_dirname(path);
++    char *filename = g_path_get_basename(path);
++    int dirfd;
++    ssize_t ret = -1;
++
++    dirfd = local_opendir_nofollow(ctx, dirpath);
++    if (dirfd == -1) {
++        goto out;
++    }
++
++    ret = fremovexattrat_nofollow(dirfd, filename, name);
++    close_preserve_errno(dirfd);
++out:
++    g_free(dirpath);
++    g_free(filename);
++    return ret;
++}
++
++int pt_removexattr(FsContext *ctx, const char *path, const char *name)
++{
++    return local_removexattr_nofollow(ctx, path, name);
++}
++
++ssize_t notsup_getxattr(FsContext *ctx, const char *path, const char *name,
++                        void *value, size_t size)
++{
++    errno = ENOTSUP;
++    return -1;
++}
++
++int notsup_setxattr(FsContext *ctx, const char *path, const char *name,
++                    void *value, size_t size, int flags)
++{
++    errno = ENOTSUP;
++    return -1;
++}
++
++ssize_t notsup_listxattr(FsContext *ctx, const char *path, char *name,
++                         void *value, size_t size)
++{
++    return 0;
++}
++
++int notsup_removexattr(FsContext *ctx, const char *path, const char *name)
++{
++    errno = ENOTSUP;
++    return -1;
++}
++
++XattrOperations *mapped_xattr_ops[] = {
++    &mapped_user_xattr,
++    &mapped_pacl_xattr,
++    &mapped_dacl_xattr,
++    NULL,
++};
++
++XattrOperations *passthrough_xattr_ops[] = {
++    &passthrough_user_xattr,
++    &passthrough_acl_xattr,
++    NULL,
++};
++
++/* for .user none model should be same as passthrough */
++XattrOperations *none_xattr_ops[] = {
++    &passthrough_user_xattr,
++    &none_acl_xattr,
++    NULL,
++};
+diff --git a/hw/9pfs/9p-xattr.h b/hw/9pfs/9p-xattr.h
+new file mode 100644
+index 0000000..6185927
+--- /dev/null
++++ b/hw/9pfs/9p-xattr.h
+@@ -0,0 +1,76 @@
++/*
++ * 9p
++ *
++ * Copyright IBM, Corp. 2010
++ *
++ * 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.
++ *
++ */
++
++#ifndef QEMU_9P_XATTR_H
++#define QEMU_9P_XATTR_H
++
++#include "qemu-xattr.h"
++
++typedef struct xattr_operations
++{
++    const char *name;
++    ssize_t (*getxattr)(FsContext *ctx, const char *path,
++                        const char *name, void *value, size_t size);
++    ssize_t (*listxattr)(FsContext *ctx, const char *path,
++                         char *name, void *value, size_t size);
++    int (*setxattr)(FsContext *ctx, const char *path, const char *name,
++                    void *value, size_t size, int flags);
++    int (*removexattr)(FsContext *ctx,
++                       const char *path, const char *name);
++} XattrOperations;
++
++ssize_t local_getxattr_nofollow(FsContext *ctx, const char *path,
++                                const char *name, void *value, size_t size);
++ssize_t local_setxattr_nofollow(FsContext *ctx, const char *path,
++                                const char *name, void *value, size_t size,
++                                int flags);
++ssize_t local_removexattr_nofollow(FsContext *ctx, const char *path,
++                                   const char *name);
++
++extern XattrOperations mapped_user_xattr;
++extern XattrOperations passthrough_user_xattr;
++
++extern XattrOperations mapped_pacl_xattr;
++extern XattrOperations mapped_dacl_xattr;
++extern XattrOperations passthrough_acl_xattr;
++extern XattrOperations none_acl_xattr;
++
++extern XattrOperations *mapped_xattr_ops[];
++extern XattrOperations *passthrough_xattr_ops[];
++extern XattrOperations *none_xattr_ops[];
++
++ssize_t v9fs_get_xattr(FsContext *ctx, const char *path, const char *name,
++                       void *value, size_t size);
++ssize_t v9fs_list_xattr(FsContext *ctx, const char *path, void *value,
++                        size_t vsize);
++int v9fs_set_xattr(FsContext *ctx, const char *path, const char *name,
++                          void *value, size_t size, int flags);
++int v9fs_remove_xattr(FsContext *ctx, const char *path, const char *name);
++
++ssize_t pt_listxattr(FsContext *ctx, const char *path, char *name, void *value,
++                     size_t size);
++ssize_t pt_getxattr(FsContext *ctx, const char *path, const char *name,
++                    void *value, size_t size);
++int pt_setxattr(FsContext *ctx, const char *path, const char *name, void *value,
++                size_t size, int flags);
++int pt_removexattr(FsContext *ctx, const char *path, const char *name);
++
++ssize_t notsup_getxattr(FsContext *ctx, const char *path, const char *name,
++                        void *value, size_t size);
++int notsup_setxattr(FsContext *ctx, const char *path, const char *name,
++                    void *value, size_t size, int flags);
++ssize_t notsup_listxattr(FsContext *ctx, const char *path, char *name,
++                         void *value, size_t size);
++int notsup_removexattr(FsContext *ctx, const char *path, const char *name);
++
++#endif
+diff --git a/hw/9pfs/virtio-9p-device.c b/hw/9pfs/virtio-9p-device.c
+index b8220ab..c84bae8 100644
+--- a/hw/9pfs/virtio-9p-device.c
++++ b/hw/9pfs/virtio-9p-device.c
+@@ -17,7 +17,7 @@
+ #include "hw/virtio-pci.h"
+ #include "virtio-9p.h"
+ #include "fsdev/qemu-fsdev.h"
+-#include "virtio-9p-xattr.h"
++#include "9p-xattr.h"
+ #include "virtio-9p-coth.h"
+ 
+ static uint32_t virtio_9p_get_features(VirtIODevice *vdev, uint32_t features)
+diff --git a/hw/9pfs/virtio-9p-handle.c b/hw/9pfs/virtio-9p-handle.c
+index f96d17a..7ec989e 100644
+--- a/hw/9pfs/virtio-9p-handle.c
++++ b/hw/9pfs/virtio-9p-handle.c
+@@ -13,7 +13,7 @@
+ 
+ #include "hw/virtio.h"
+ #include "virtio-9p.h"
+-#include "virtio-9p-xattr.h"
++#include "9p-xattr.h"
+ #include <arpa/inet.h>
+ #include <pwd.h>
+ #include <grp.h>
+@@ -111,7 +111,7 @@ static int handle_close(FsContext *ctx, V9fsFidOpenState *fs)
+ 
+ static int handle_closedir(FsContext *ctx, V9fsFidOpenState *fs)
+ {
+-    return closedir(fs->dir);
++    return closedir(fs->dir.stream);
+ }
+ 
+ static int handle_open(FsContext *ctx, V9fsPath *fs_path,
+@@ -131,8 +131,8 @@ static int handle_opendir(FsContext *ctx,
+     if (ret < 0) {
+         return -1;
+     }
+-    fs->dir = fdopendir(ret);
+-    if (!fs->dir) {
++    fs->dir.stream = fdopendir(ret);
++    if (!fs->dir.stream) {
+         return -1;
+     }
+     return 0;
+@@ -140,24 +140,24 @@ static int handle_opendir(FsContext *ctx,
+ 
+ static void handle_rewinddir(FsContext *ctx, V9fsFidOpenState *fs)
+ {
+-    return rewinddir(fs->dir);
++    return rewinddir(fs->dir.stream);
+ }
+ 
+ static off_t handle_telldir(FsContext *ctx, V9fsFidOpenState *fs)
+ {
+-    return telldir(fs->dir);
++    return telldir(fs->dir.stream);
+ }
+ 
+ static int handle_readdir_r(FsContext *ctx, V9fsFidOpenState *fs,
+                             struct dirent *entry,
+                             struct dirent **result)
+ {
+-    return readdir_r(fs->dir, entry, result);
++    return readdir_r(fs->dir.stream, entry, result);
+ }
+ 
+ static void handle_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off)
+ {
+-    return seekdir(fs->dir, off);
++    return seekdir(fs->dir.stream, off);
+ }
+ 
+ static ssize_t handle_preadv(FsContext *ctx, V9fsFidOpenState *fs,
+@@ -261,7 +261,7 @@ static int handle_fstat(FsContext *fs_ctx, int fid_type,
+     int fd;
+ 
+     if (fid_type == P9_FID_DIR) {
+-        fd = dirfd(fs->dir);
++        fd = dirfd(fs->dir.stream);
+     } else {
+         fd = fs->fd;
+     }
+@@ -408,7 +408,7 @@ static int handle_fsync(FsContext *ctx, int fid_type,
+     int fd;
+ 
+     if (fid_type == P9_FID_DIR) {
+-        fd = dirfd(fs->dir);
++        fd = dirfd(fs->dir.stream);
+     } else {
+         fd = fs->fd;
+     }
+diff --git a/hw/9pfs/virtio-9p-local.c b/hw/9pfs/virtio-9p-local.c
+deleted file mode 100644
+index 33a41d2..0000000
+--- a/hw/9pfs/virtio-9p-local.c
++++ /dev/null
+@@ -1,1171 +0,0 @@
+-/*
+- * Virtio 9p Posix callback
+- *
+- * 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 "virtio-9p.h"
+-#include "virtio-9p-xattr.h"
+-#include <arpa/inet.h>
+-#include <pwd.h>
+-#include <grp.h>
+-#include <sys/socket.h>
+-#include <sys/un.h>
+-#include "qemu-xattr.h"
+-#include <libgen.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
+-
+-#define VIRTFS_META_DIR ".virtfs_metadata"
+-
+-static const char *local_mapped_attr_path(FsContext *ctx,
+-                                          const char *path, char *buffer)
+-{
+-    char *dir_name;
+-    char *tmp_path = strdup(path);
+-    char *base_name = basename(tmp_path);
+-
+-    /* NULL terminate the directory */
+-    dir_name = tmp_path;
+-    *(base_name - 1) = '\0';
+-
+-    snprintf(buffer, PATH_MAX, "%s/%s/%s/%s",
+-             ctx->fs_root, dir_name, VIRTFS_META_DIR, base_name);
+-    free(tmp_path);
+-    return buffer;
+-}
+-
+-#define ATTR_MAX 100
+-static void local_mapped_file_attr(FsContext *ctx, const char *path,
+-                                   struct stat *stbuf)
+-{
+-    FILE *fp;
+-    char buf[ATTR_MAX];
+-    char attr_path[PATH_MAX];
+-
+-    local_mapped_attr_path(ctx, path, attr_path);
+-    fp = fopen(attr_path, "r");
+-    if (!fp) {
+-        return;
+-    }
+-    memset(buf, 0, ATTR_MAX);
+-    while (fgets(buf, ATTR_MAX, fp)) {
+-        if (!strncmp(buf, "virtfs.uid", 10)) {
+-            stbuf->st_uid = atoi(buf+11);
+-        } else if (!strncmp(buf, "virtfs.gid", 10)) {
+-            stbuf->st_gid = atoi(buf+11);
+-        } else if (!strncmp(buf, "virtfs.mode", 11)) {
+-            stbuf->st_mode = atoi(buf+12);
+-        } else if (!strncmp(buf, "virtfs.rdev", 11)) {
+-            stbuf->st_rdev = atoi(buf+12);
+-        }
+-        memset(buf, 0, ATTR_MAX);
+-    }
+-    fclose(fp);
+-}
+-
+-static int local_lstat(FsContext *fs_ctx, V9fsPath *fs_path, struct stat *stbuf)
+-{
+-    int err;
+-    char buffer[PATH_MAX];
+-    char *path = fs_path->data;
+-
+-    err =  lstat(rpath(fs_ctx, path, buffer), stbuf);
+-    if (err) {
+-        return err;
+-    }
+-    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 (getxattr(rpath(fs_ctx, path, buffer), "user.virtfs.uid", &tmp_uid,
+-                    sizeof(uid_t)) > 0) {
+-            stbuf->st_uid = tmp_uid;
+-        }
+-        if (getxattr(rpath(fs_ctx, path, buffer), "user.virtfs.gid", &tmp_gid,
+-                    sizeof(gid_t)) > 0) {
+-            stbuf->st_gid = tmp_gid;
+-        }
+-        if (getxattr(rpath(fs_ctx, path, buffer), "user.virtfs.mode",
+-                    &tmp_mode, sizeof(mode_t)) > 0) {
+-            stbuf->st_mode = tmp_mode;
+-        }
+-        if (getxattr(rpath(fs_ctx, path, buffer), "user.virtfs.rdev", &tmp_dev,
+-                        sizeof(dev_t)) > 0) {
+-                stbuf->st_rdev = tmp_dev;
+-        }
+-    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
+-        local_mapped_file_attr(fs_ctx, path, stbuf);
+-    }
+-    return err;
+-}
+-
+-static int local_create_mapped_attr_dir(FsContext *ctx, const char *path)
+-{
+-    int err;
+-    char attr_dir[PATH_MAX];
+-    char *tmp_path = strdup(path);
+-
+-    snprintf(attr_dir, PATH_MAX, "%s/%s/%s",
+-             ctx->fs_root, dirname(tmp_path), VIRTFS_META_DIR);
+-
+-    err = mkdir(attr_dir, 0700);
+-    if (err < 0 && errno == EEXIST) {
+-        err = 0;
+-    }
+-    free(tmp_path);
+-    return err;
+-}
+-
+-static int local_set_mapped_file_attr(FsContext *ctx,
+-                                      const char *path, FsCred *credp)
+-{
+-    FILE *fp;
+-    int ret = 0;
+-    char buf[ATTR_MAX];
+-    char attr_path[PATH_MAX];
+-    int uid = -1, gid = -1, mode = -1, rdev = -1;
+-
+-    fp = fopen(local_mapped_attr_path(ctx, path, attr_path), "r");
+-    if (!fp) {
+-        goto create_map_file;
+-    }
+-    memset(buf, 0, ATTR_MAX);
+-    while (fgets(buf, ATTR_MAX, fp)) {
+-        if (!strncmp(buf, "virtfs.uid", 10)) {
+-            uid = atoi(buf+11);
+-        } else if (!strncmp(buf, "virtfs.gid", 10)) {
+-            gid = atoi(buf+11);
+-        } else if (!strncmp(buf, "virtfs.mode", 11)) {
+-            mode = atoi(buf+12);
+-        } else if (!strncmp(buf, "virtfs.rdev", 11)) {
+-            rdev = atoi(buf+12);
+-        }
+-        memset(buf, 0, ATTR_MAX);
+-    }
+-    fclose(fp);
+-    goto update_map_file;
+-
+-create_map_file:
+-    ret = local_create_mapped_attr_dir(ctx, path);
+-    if (ret < 0) {
+-        goto err_out;
+-    }
+-
+-update_map_file:
+-    fp = fopen(attr_path, "w");
+-    if (!fp) {
+-        ret = -1;
+-        goto err_out;
+-    }
+-
+-    if (credp->fc_uid != -1) {
+-        uid = credp->fc_uid;
+-    }
+-    if (credp->fc_gid != -1) {
+-        gid = credp->fc_gid;
+-    }
+-    if (credp->fc_mode != -1) {
+-        mode = credp->fc_mode;
+-    }
+-    if (credp->fc_rdev != -1) {
+-        rdev = credp->fc_rdev;
+-    }
+-
+-
+-    if (uid != -1) {
+-        fprintf(fp, "virtfs.uid=%d\n", uid);
+-    }
+-    if (gid != -1) {
+-        fprintf(fp, "virtfs.gid=%d\n", gid);
+-    }
+-    if (mode != -1) {
+-        fprintf(fp, "virtfs.mode=%d\n", mode);
+-    }
+-    if (rdev != -1) {
+-        fprintf(fp, "virtfs.rdev=%d\n", rdev);
+-    }
+-    fclose(fp);
+-
+-err_out:
+-    return ret;
+-}
+-
+-static int local_set_xattr(const char *path, FsCred *credp)
+-{
+-    int err;
+-
+-    if (credp->fc_uid != -1) {
+-        err = setxattr(path, "user.virtfs.uid", &credp->fc_uid, sizeof(uid_t),
+-                0);
+-        if (err) {
+-            return err;
+-        }
+-    }
+-    if (credp->fc_gid != -1) {
+-        err = setxattr(path, "user.virtfs.gid", &credp->fc_gid, sizeof(gid_t),
+-                0);
+-        if (err) {
+-            return err;
+-        }
+-    }
+-    if (credp->fc_mode != -1) {
+-        err = setxattr(path, "user.virtfs.mode", &credp->fc_mode,
+-                sizeof(mode_t), 0);
+-        if (err) {
+-            return err;
+-        }
+-    }
+-    if (credp->fc_rdev != -1) {
+-        err = setxattr(path, "user.virtfs.rdev", &credp->fc_rdev,
+-                sizeof(dev_t), 0);
+-        if (err) {
+-            return err;
+-        }
+-    }
+-    return 0;
+-}
+-
+-static int local_post_create_passthrough(FsContext *fs_ctx, const char *path,
+-                                         FsCred *credp)
+-{
+-    char buffer[PATH_MAX];
+-
+-    if (lchown(rpath(fs_ctx, path, buffer), credp->fc_uid,
+-                credp->fc_gid) < 0) {
+-        /*
+-         * If we fail to change ownership and if we are
+-         * using security model none. Ignore the error
+-         */
+-        if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) {
+-            return -1;
+-        }
+-    }
+-
+-    if (chmod(rpath(fs_ctx, path, buffer), credp->fc_mode & 07777) < 0) {
+-        return -1;
+-    }
+-    return 0;
+-}
+-
+-static ssize_t local_readlink(FsContext *fs_ctx, V9fsPath *fs_path,
+-                              char *buf, size_t bufsz)
+-{
+-    ssize_t tsize = -1;
+-    char buffer[PATH_MAX];
+-    char *path = fs_path->data;
+-
+-    if ((fs_ctx->export_flags & V9FS_SM_MAPPED) ||
+-        (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE)) {
+-        int fd;
+-        fd = open(rpath(fs_ctx, path, buffer), O_RDONLY);
+-        if (fd == -1) {
+-            return -1;
+-        }
+-        do {
+-            tsize = read(fd, (void *)buf, bufsz);
+-        } while (tsize == -1 && errno == EINTR);
+-        close(fd);
+-        return tsize;
+-    } 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, V9fsFidOpenState *fs)
+-{
+-    return close(fs->fd);
+-}
+-
+-static int local_closedir(FsContext *ctx, V9fsFidOpenState *fs)
+-{
+-    return closedir(fs->dir);
+-}
+-
+-static int local_open(FsContext *ctx, V9fsPath *fs_path,
+-                      int flags, V9fsFidOpenState *fs)
+-{
+-    char buffer[PATH_MAX];
+-    char *path = fs_path->data;
+-
+-    fs->fd = open(rpath(ctx, path, buffer), flags);
+-    return fs->fd;
+-}
+-
+-static int local_opendir(FsContext *ctx,
+-                         V9fsPath *fs_path, V9fsFidOpenState *fs)
+-{
+-    char buffer[PATH_MAX];
+-    char *path = fs_path->data;
+-
+-    fs->dir = opendir(rpath(ctx, path, buffer));
+-    if (!fs->dir) {
+-        return -1;
+-    }
+-    return 0;
+-}
+-
+-static void local_rewinddir(FsContext *ctx, V9fsFidOpenState *fs)
+-{
+-    return rewinddir(fs->dir);
+-}
+-
+-static off_t local_telldir(FsContext *ctx, V9fsFidOpenState *fs)
+-{
+-    return telldir(fs->dir);
+-}
+-
+-static int local_readdir_r(FsContext *ctx, V9fsFidOpenState *fs,
+-                           struct dirent *entry,
+-                           struct dirent **result)
+-{
+-    int ret;
+-
+-again:
+-    ret = readdir_r(fs->dir, entry, result);
+-    if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
+-        if (!ret && *result != NULL &&
+-            !strcmp(entry->d_name, VIRTFS_META_DIR)) {
+-            /* skp the meta data directory */
+-            goto again;
+-        }
+-    }
+-    return ret;
+-}
+-
+-static void local_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off)
+-{
+-    return seekdir(fs->dir, off);
+-}
+-
+-static ssize_t local_preadv(FsContext *ctx, V9fsFidOpenState *fs,
+-                            const struct iovec *iov,
+-                            int iovcnt, off_t offset)
+-{
+-#ifdef CONFIG_PREADV
+-    return preadv(fs->fd, iov, iovcnt, offset);
+-#else
+-    int err = lseek(fs->fd, offset, SEEK_SET);
+-    if (err == -1) {
+-        return err;
+-    } else {
+-        return readv(fs->fd, iov, iovcnt);
+-    }
+-#endif
+-}
+-
+-static ssize_t local_pwritev(FsContext *ctx, V9fsFidOpenState *fs,
+-                             const struct iovec *iov,
+-                             int iovcnt, off_t offset)
+-{
+-    ssize_t ret
+-;
+-#ifdef CONFIG_PREADV
+-    ret = pwritev(fs->fd, iov, iovcnt, offset);
+-#else
+-    int err = lseek(fs->fd, offset, SEEK_SET);
+-    if (err == -1) {
+-        return err;
+-    } else {
+-        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)
+-{
+-    char buffer[PATH_MAX];
+-    char *path = fs_path->data;
+-
+-    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
+-        return local_set_xattr(rpath(fs_ctx, path, buffer), credp);
+-    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
+-        return local_set_mapped_file_attr(fs_ctx, path, credp);
+-    } 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;
+-}
+-
+-static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path,
+-                       const char *name, FsCred *credp)
+-{
+-    char *path;
+-    int err = -1;
+-    int serrno = 0;
+-    V9fsString fullname;
+-    char buffer[PATH_MAX];
+-
+-    v9fs_string_init(&fullname);
+-    v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
+-    path = fullname.data;
+-
+-    /* Determine the security model */
+-    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;
+-        }
+-        err = local_set_xattr(rpath(fs_ctx, path, buffer), credp);
+-        if (err == -1) {
+-            serrno = errno;
+-            goto err_end;
+-        }
+-    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
+-
+-        err = mknod(rpath(fs_ctx, path, buffer),
+-                    SM_LOCAL_MODE_BITS|S_IFREG, 0);
+-        if (err == -1) {
+-            goto out;
+-        }
+-        err = local_set_mapped_file_attr(fs_ctx, path, credp);
+-        if (err == -1) {
+-            serrno = errno;
+-            goto err_end;
+-        }
+-    } 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) {
+-            goto out;
+-        }
+-        err = local_post_create_passthrough(fs_ctx, path, credp);
+-        if (err == -1) {
+-            serrno = errno;
+-            goto err_end;
+-        }
+-    }
+-    goto out;
+-
+-err_end:
+-    remove(rpath(fs_ctx, path, buffer));
+-    errno = serrno;
+-out:
+-    v9fs_string_free(&fullname);
+-    return err;
+-}
+-
+-static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path,
+-                       const char *name, FsCred *credp)
+-{
+-    char *path;
+-    int err = -1;
+-    int serrno = 0;
+-    V9fsString fullname;
+-    char buffer[PATH_MAX];
+-
+-    v9fs_string_init(&fullname);
+-    v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
+-    path = fullname.data;
+-
+-    /* Determine the security model */
+-    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;
+-        }
+-        credp->fc_mode = credp->fc_mode|S_IFDIR;
+-        err = local_set_xattr(rpath(fs_ctx, path, buffer), credp);
+-        if (err == -1) {
+-            serrno = errno;
+-            goto err_end;
+-        }
+-    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
+-        err = mkdir(rpath(fs_ctx, path, buffer), SM_LOCAL_DIR_MODE_BITS);
+-        if (err == -1) {
+-            goto out;
+-        }
+-        credp->fc_mode = credp->fc_mode|S_IFDIR;
+-        err = local_set_mapped_file_attr(fs_ctx, path, credp);
+-        if (err == -1) {
+-            serrno = errno;
+-            goto err_end;
+-        }
+-    } 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;
+-        }
+-        err = local_post_create_passthrough(fs_ctx, path, credp);
+-        if (err == -1) {
+-            serrno = errno;
+-            goto err_end;
+-        }
+-    }
+-    goto out;
+-
+-err_end:
+-    remove(rpath(fs_ctx, path, buffer));
+-    errno = serrno;
+-out:
+-    v9fs_string_free(&fullname);
+-    return err;
+-}
+-
+-static int local_fstat(FsContext *fs_ctx, int fid_type,
+-                       V9fsFidOpenState *fs, struct stat *stbuf)
+-{
+-    int err, fd;
+-
+-    if (fid_type == P9_FID_DIR) {
+-        fd = dirfd(fs->dir);
+-    } else {
+-        fd = fs->fd;
+-    }
+-
+-    err = fstat(fd, stbuf);
+-    if (err) {
+-        return err;
+-    }
+-    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) {
+-            stbuf->st_uid = tmp_uid;
+-        }
+-        if (fgetxattr(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) {
+-            stbuf->st_mode = tmp_mode;
+-        }
+-        if (fgetxattr(fd, "user.virtfs.rdev",
+-                      &tmp_dev, sizeof(dev_t)) > 0) {
+-                stbuf->st_rdev = tmp_dev;
+-        }
+-    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
+-        errno = EOPNOTSUPP;
+-        return -1;
+-    }
+-    return err;
+-}
+-
+-static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
+-                       int flags, FsCred *credp, V9fsFidOpenState *fs)
+-{
+-    char *path;
+-    int fd = -1;
+-    int err = -1;
+-    int serrno = 0;
+-    V9fsString fullname;
+-    char buffer[PATH_MAX];
+-
+-    v9fs_string_init(&fullname);
+-    v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
+-    path = fullname.data;
+-
+-    /* Determine the security model */
+-    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;
+-            goto out;
+-        }
+-        credp->fc_mode = credp->fc_mode|S_IFREG;
+-        /* Set cleint credentials in xattr */
+-        err = local_set_xattr(rpath(fs_ctx, path, buffer), credp);
+-        if (err == -1) {
+-            serrno = errno;
+-            goto err_end;
+-        }
+-    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
+-        fd = open(rpath(fs_ctx, path, buffer), flags, SM_LOCAL_MODE_BITS);
+-        if (fd == -1) {
+-            err = fd;
+-            goto out;
+-        }
+-        credp->fc_mode = credp->fc_mode|S_IFREG;
+-        /* Set client credentials in .virtfs_metadata directory files */
+-        err = local_set_mapped_file_attr(fs_ctx, path, credp);
+-        if (err == -1) {
+-            serrno = errno;
+-            goto err_end;
+-        }
+-    } 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;
+-            goto out;
+-        }
+-        err = local_post_create_passthrough(fs_ctx, path, credp);
+-        if (err == -1) {
+-            serrno = errno;
+-            goto err_end;
+-        }
+-    }
+-    err = fd;
+-    fs->fd = fd;
+-    goto out;
+-
+-err_end:
+-    close(fd);
+-    remove(rpath(fs_ctx, path, buffer));
+-    errno = serrno;
+-out:
+-    v9fs_string_free(&fullname);
+-    return err;
+-}
+-
+-
+-static int local_symlink(FsContext *fs_ctx, const char *oldpath,
+-                         V9fsPath *dir_path, const char *name, FsCred *credp)
+-{
+-    int err = -1;
+-    int serrno = 0;
+-    char *newpath;
+-    V9fsString fullname;
+-    char buffer[PATH_MAX];
+-
+-    v9fs_string_init(&fullname);
+-    v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
+-    newpath = fullname.data;
+-
+-    /* Determine the security model */
+-    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,
+-                SM_LOCAL_MODE_BITS);
+-        if (fd == -1) {
+-            err = fd;
+-            goto out;
+-        }
+-        /* Write the oldpath (target) to the file. */
+-        oldpath_size = strlen(oldpath);
+-        do {
+-            write_size = write(fd, (void *)oldpath, oldpath_size);
+-        } while (write_size == -1 && errno == EINTR);
+-
+-        if (write_size != oldpath_size) {
+-            serrno = errno;
+-            close(fd);
+-            err = -1;
+-            goto err_end;
+-        }
+-        close(fd);
+-        /* Set cleint credentials in symlink's xattr */
+-        credp->fc_mode = credp->fc_mode|S_IFLNK;
+-        err = local_set_xattr(rpath(fs_ctx, newpath, buffer), credp);
+-        if (err == -1) {
+-            serrno = errno;
+-            goto err_end;
+-        }
+-    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
+-        int fd;
+-        ssize_t oldpath_size, write_size;
+-        fd = open(rpath(fs_ctx, newpath, buffer), O_CREAT|O_EXCL|O_RDWR,
+-                  SM_LOCAL_MODE_BITS);
+-        if (fd == -1) {
+-            err = fd;
+-            goto out;
+-        }
+-        /* Write the oldpath (target) to the file. */
+-        oldpath_size = strlen(oldpath);
+-        do {
+-            write_size = write(fd, (void *)oldpath, oldpath_size);
+-        } while (write_size == -1 && errno == EINTR);
+-
+-        if (write_size != oldpath_size) {
+-            serrno = errno;
+-            close(fd);
+-            err = -1;
+-            goto err_end;
+-        }
+-        close(fd);
+-        /* Set cleint credentials in symlink's xattr */
+-        credp->fc_mode = credp->fc_mode|S_IFLNK;
+-        err = local_set_mapped_file_attr(fs_ctx, newpath, credp);
+-        if (err == -1) {
+-            serrno = errno;
+-            goto err_end;
+-        }
+-    } 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;
+-        }
+-        err = lchown(rpath(fs_ctx, newpath, buffer), credp->fc_uid,
+-                     credp->fc_gid);
+-        if (err == -1) {
+-            /*
+-             * If we fail to change ownership and if we are
+-             * using security model none. Ignore the error
+-             */
+-            if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) {
+-                serrno = errno;
+-                goto err_end;
+-            } else
+-                err = 0;
+-        }
+-    }
+-    goto out;
+-
+-err_end:
+-    remove(rpath(fs_ctx, newpath, buffer));
+-    errno = serrno;
+-out:
+-    v9fs_string_free(&fullname);
+-    return err;
+-}
+-
+-static int local_link(FsContext *ctx, V9fsPath *oldpath,
+-                      V9fsPath *dirpath, const char *name)
+-{
+-    int ret;
+-    V9fsString newpath;
+-    char buffer[PATH_MAX], buffer1[PATH_MAX];
+-
+-    v9fs_string_init(&newpath);
+-    v9fs_string_sprintf(&newpath, "%s/%s", dirpath->data, name);
+-
+-    ret = link(rpath(ctx, oldpath->data, buffer),
+-               rpath(ctx, newpath.data, buffer1));
+-
+-    /* now link the virtfs_metadata files */
+-    if (!ret && (ctx->export_flags & V9FS_SM_MAPPED_FILE)) {
+-        /* Link the .virtfs_metadata files. Create the metada directory */
+-        ret = local_create_mapped_attr_dir(ctx, newpath.data);
+-        if (ret < 0) {
+-            goto err_out;
+-        }
+-        ret = link(local_mapped_attr_path(ctx, oldpath->data, buffer),
+-                   local_mapped_attr_path(ctx, newpath.data, buffer1));
+-        if (ret < 0 && errno != ENOENT) {
+-            goto err_out;
+-        }
+-    }
+-err_out:
+-    v9fs_string_free(&newpath);
+-    return ret;
+-}
+-
+-static int local_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size)
+-{
+-    char buffer[PATH_MAX];
+-    char *path = fs_path->data;
+-
+-    return truncate(rpath(ctx, path, buffer), size);
+-}
+-
+-static int local_rename(FsContext *ctx, const char *oldpath,
+-                        const char *newpath)
+-{
+-    int err;
+-    char buffer[PATH_MAX], buffer1[PATH_MAX];
+-
+-    if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
+-        err = local_create_mapped_attr_dir(ctx, newpath);
+-        if (err < 0) {
+-            return err;
+-        }
+-        /* rename the .virtfs_metadata files */
+-        err = rename(local_mapped_attr_path(ctx, oldpath, buffer),
+-                     local_mapped_attr_path(ctx, newpath, buffer1));
+-        if (err < 0 && errno != ENOENT) {
+-            return err;
+-        }
+-    }
+-    return rename(rpath(ctx, oldpath, buffer), rpath(ctx, newpath, buffer1));
+-}
+-
+-static int local_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
+-{
+-    char buffer[PATH_MAX];
+-    char *path = fs_path->data;
+-
+-    if ((credp->fc_uid == -1 && credp->fc_gid == -1) ||
+-        (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->export_flags & V9FS_SM_MAPPED_FILE) {
+-        return local_set_mapped_file_attr(fs_ctx, path, credp);
+-    }
+-    return -1;
+-}
+-
+-static int local_utimensat(FsContext *s, V9fsPath *fs_path,
+-                           const struct timespec *buf)
+-{
+-    char buffer[PATH_MAX];
+-    char *path = fs_path->data;
+-
+-    return qemu_utimens(rpath(s, path, buffer), buf);
+-}
+-
+-static int local_remove(FsContext *ctx, const char *path)
+-{
+-    int err;
+-    struct stat stbuf;
+-    char buffer[PATH_MAX];
+-
+-    if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
+-        err =  lstat(rpath(ctx, path, buffer), &stbuf);
+-        if (err) {
+-            goto err_out;
+-        }
+-        /*
+-         * If directory remove .virtfs_metadata contained in the
+-         * directory
+-         */
+-        if (S_ISDIR(stbuf.st_mode)) {
+-            sprintf(buffer, "%s/%s/%s", ctx->fs_root, path, VIRTFS_META_DIR);
+-            err = remove(buffer);
+-            if (err < 0 && errno != ENOENT) {
+-                /*
+-                 * We didn't had the .virtfs_metadata file. May be file created
+-                 * in non-mapped mode ?. Ignore ENOENT.
+-                 */
+-                goto err_out;
+-            }
+-        }
+-        /*
+-         * Now remove the name from parent directory
+-         * .virtfs_metadata directory
+-         */
+-        err = remove(local_mapped_attr_path(ctx, path, buffer));;
+-        if (err < 0 && errno != ENOENT) {
+-            /*
+-             * We didn't had the .virtfs_metadata file. May be file created
+-             * in non-mapped mode ?. Ignore ENOENT.
+-             */
+-            goto err_out;
+-        }
+-    }
+-    return remove(rpath(ctx, path, buffer));
+-err_out:
+-    return err;
+-}
+-
+-static int local_fsync(FsContext *ctx, int fid_type,
+-                       V9fsFidOpenState *fs, int datasync)
+-{
+-    int fd;
+-
+-    if (fid_type == P9_FID_DIR) {
+-        fd = dirfd(fs->dir);
+-    } else {
+-        fd = fs->fd;
+-    }
+-
+-    if (datasync) {
+-        return qemu_fdatasync(fd);
+-    } else {
+-        return fsync(fd);
+-    }
+-}
+-
+-static int local_statfs(FsContext *s, V9fsPath *fs_path, struct statfs *stbuf)
+-{
+-    char buffer[PATH_MAX];
+-    char *path = fs_path->data;
+-
+-    return statfs(rpath(s, path, buffer), stbuf);
+-}
+-
+-static ssize_t local_lgetxattr(FsContext *ctx, V9fsPath *fs_path,
+-                               const char *name, void *value, size_t size)
+-{
+-    char *path = fs_path->data;
+-
+-    return v9fs_get_xattr(ctx, path, name, value, size);
+-}
+-
+-static ssize_t local_llistxattr(FsContext *ctx, V9fsPath *fs_path,
+-                                void *value, size_t size)
+-{
+-    char *path = fs_path->data;
+-
+-    return v9fs_list_xattr(ctx, path, value, size);
+-}
+-
+-static int local_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name,
+-                           void *value, size_t size, int flags)
+-{
+-    char *path = fs_path->data;
+-
+-    return v9fs_set_xattr(ctx, path, name, value, size, flags);
+-}
+-
+-static int local_lremovexattr(FsContext *ctx, V9fsPath *fs_path,
+-                              const char *name)
+-{
+-    char *path = fs_path->data;
+-
+-    return v9fs_remove_xattr(ctx, path, name);
+-}
+-
+-static int local_name_to_path(FsContext *ctx, V9fsPath *dir_path,
+-                              const char *name, V9fsPath *target)
+-{
+-    if (dir_path) {
+-        v9fs_string_sprintf((V9fsString *)target, "%s/%s",
+-                            dir_path->data, name);
+-    } else {
+-        v9fs_string_sprintf((V9fsString *)target, "%s", name);
+-    }
+-    /* Bump the size for including terminating NULL */
+-    target->size++;
+-    return 0;
+-}
+-
+-static int local_renameat(FsContext *ctx, V9fsPath *olddir,
+-                          const char *old_name, V9fsPath *newdir,
+-                          const char *new_name)
+-{
+-    int ret;
+-    V9fsString old_full_name, new_full_name;
+-
+-    v9fs_string_init(&old_full_name);
+-    v9fs_string_init(&new_full_name);
+-
+-    v9fs_string_sprintf(&old_full_name, "%s/%s", olddir->data, old_name);
+-    v9fs_string_sprintf(&new_full_name, "%s/%s", newdir->data, new_name);
+-
+-    ret = local_rename(ctx, old_full_name.data, new_full_name.data);
+-    v9fs_string_free(&old_full_name);
+-    v9fs_string_free(&new_full_name);
+-    return ret;
+-}
+-
+-static int local_unlinkat(FsContext *ctx, V9fsPath *dir,
+-                          const char *name, int flags)
+-{
+-    int ret;
+-    V9fsString fullname;
+-    char buffer[PATH_MAX];
+-
+-    v9fs_string_init(&fullname);
+-
+-    v9fs_string_sprintf(&fullname, "%s/%s", dir->data, name);
+-    if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
+-        if (flags == AT_REMOVEDIR) {
+-            /*
+-             * If directory remove .virtfs_metadata contained in the
+-             * directory
+-             */
+-            sprintf(buffer, "%s/%s/%s", ctx->fs_root,
+-                    fullname.data, VIRTFS_META_DIR);
+-            ret = remove(buffer);
+-            if (ret < 0 && errno != ENOENT) {
+-                /*
+-                 * We didn't had the .virtfs_metadata file. May be file created
+-                 * in non-mapped mode ?. Ignore ENOENT.
+-                 */
+-                goto err_out;
+-            }
+-        }
+-        /*
+-         * Now remove the name from parent directory
+-         * .virtfs_metadata directory.
+-         */
+-        ret = remove(local_mapped_attr_path(ctx, fullname.data, buffer));
+-        if (ret < 0 && errno != ENOENT) {
+-            /*
+-             * We didn't had the .virtfs_metadata file. May be file created
+-             * in non-mapped mode ?. Ignore ENOENT.
+-             */
+-            goto err_out;
+-        }
+-    }
+-    /* Remove the name finally */
+-    ret = remove(rpath(ctx, fullname.data, buffer));
+-    v9fs_string_free(&fullname);
+-
+-err_out:
+-    return ret;
+-}
+-
+-static int local_ioc_getversion(FsContext *ctx, V9fsPath *path,
+-                                mode_t st_mode, uint64_t *st_gen)
+-{
+-    int err;
+-#ifdef FS_IOC_GETVERSION
+-    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);
+-#else
+-    err = -ENOTTY;
+-#endif
+-    return err;
+-}
+-
+-static int local_init(FsContext *ctx)
+-{
+-    int err = 0;
+-    struct statfs stbuf;
+-
+-    if (ctx->export_flags & V9FS_SM_PASSTHROUGH) {
+-        ctx->xops = passthrough_xattr_ops;
+-    } else if (ctx->export_flags & V9FS_SM_MAPPED) {
+-        ctx->xops = mapped_xattr_ops;
+-    } else if (ctx->export_flags & V9FS_SM_NONE) {
+-        ctx->xops = none_xattr_ops;
+-    } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
+-        /*
+-         * xattr operation for mapped-file and passthrough
+-         * remain same.
+-         */
+-        ctx->xops = passthrough_xattr_ops;
+-    }
+-    ctx->export_flags |= V9FS_PATHNAME_FSCONTEXT;
+-#ifdef FS_IOC_GETVERSION
+-    /*
+-     * use ioc_getversion only if the iocl is definied
+-     */
+-    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;
+-        }
+-    }
+-#endif
+-    return err;
+-}
+-
+-static int local_parse_opts(QemuOpts *opts, struct FsDriverEntry *fse)
+-{
+-    const char *sec_model = qemu_opt_get(opts, "security_model");
+-    const char *path = qemu_opt_get(opts, "path");
+-
+-    if (!sec_model) {
+-        fprintf(stderr, "security model not specified, "
+-                "local fs needs security model\nvalid options are:"
+-                "\tsecurity_model=[passthrough|mapped|none]\n");
+-        return -1;
+-    }
+-
+-    if (!strcmp(sec_model, "passthrough")) {
+-        fse->export_flags |= V9FS_SM_PASSTHROUGH;
+-    } else if (!strcmp(sec_model, "mapped") ||
+-               !strcmp(sec_model, "mapped-xattr")) {
+-        fse->export_flags |= V9FS_SM_MAPPED;
+-    } else if (!strcmp(sec_model, "none")) {
+-        fse->export_flags |= V9FS_SM_NONE;
+-    } else if (!strcmp(sec_model, "mapped-file")) {
+-        fse->export_flags |= V9FS_SM_MAPPED_FILE;
+-    } else {
+-        fprintf(stderr, "Invalid security model %s specified, valid options are"
+-                "\n\t [passthrough|mapped-xattr|mapped-file|none]\n",
+-                sec_model);
+-        return -1;
+-    }
+-
+-    if (!path) {
+-        fprintf(stderr, "fsdev: No path specified.\n");
+-        return -1;
+-    }
+-    fse->path = g_strdup(path);
+-
+-    return 0;
+-}
+-
+-FileOperations local_ops = {
+-    .parse_opts = local_parse_opts,
+-    .init  = local_init,
+-    .lstat = local_lstat,
+-    .readlink = local_readlink,
+-    .close = local_close,
+-    .closedir = local_closedir,
+-    .open = local_open,
+-    .opendir = local_opendir,
+-    .rewinddir = local_rewinddir,
+-    .telldir = local_telldir,
+-    .readdir_r = local_readdir_r,
+-    .seekdir = local_seekdir,
+-    .preadv = local_preadv,
+-    .pwritev = local_pwritev,
+-    .chmod = local_chmod,
+-    .mknod = local_mknod,
+-    .mkdir = local_mkdir,
+-    .fstat = local_fstat,
+-    .open2 = local_open2,
+-    .symlink = local_symlink,
+-    .link = local_link,
+-    .truncate = local_truncate,
+-    .rename = local_rename,
+-    .chown = local_chown,
+-    .utimensat = local_utimensat,
+-    .remove = local_remove,
+-    .fsync = local_fsync,
+-    .statfs = local_statfs,
+-    .lgetxattr = local_lgetxattr,
+-    .llistxattr = local_llistxattr,
+-    .lsetxattr = local_lsetxattr,
+-    .lremovexattr = local_lremovexattr,
+-    .name_to_path = local_name_to_path,
+-    .renameat  = local_renameat,
+-    .unlinkat = local_unlinkat,
+-};
+diff --git a/hw/9pfs/virtio-9p-posix-acl.c b/hw/9pfs/virtio-9p-posix-acl.c
+deleted file mode 100644
+index a1948e3..0000000
+--- a/hw/9pfs/virtio-9p-posix-acl.c
++++ /dev/null
+@@ -1,159 +0,0 @@
+-/*
+- * Virtio 9p system.posix* xattr callback
+- *
+- * Copyright IBM, Corp. 2010
+- *
+- * 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 <sys/types.h>
+-#include "qemu-xattr.h"
+-#include "hw/virtio.h"
+-#include "virtio-9p.h"
+-#include "fsdev/file-op-9p.h"
+-#include "virtio-9p-xattr.h"
+-
+-#define MAP_ACL_ACCESS "user.virtfs.system.posix_acl_access"
+-#define MAP_ACL_DEFAULT "user.virtfs.system.posix_acl_default"
+-#define ACL_ACCESS "system.posix_acl_access"
+-#define ACL_DEFAULT "system.posix_acl_default"
+-
+-static ssize_t mp_pacl_getxattr(FsContext *ctx, const char *path,
+-                                const char *name, void *value, size_t size)
+-{
+-    char buffer[PATH_MAX];
+-    return lgetxattr(rpath(ctx, path, buffer), MAP_ACL_ACCESS, value, size);
+-}
+-
+-static ssize_t mp_pacl_listxattr(FsContext *ctx, const char *path,
+-                                 char *name, void *value, size_t osize)
+-{
+-    ssize_t len = sizeof(ACL_ACCESS);
+-
+-    if (!value) {
+-        return len;
+-    }
+-
+-    if (osize < len) {
+-        errno = ERANGE;
+-        return -1;
+-    }
+-
+-    strncpy(value, ACL_ACCESS, len);
+-    return 0;
+-}
+-
+-static int mp_pacl_setxattr(FsContext *ctx, const char *path, const char *name,
+-                            void *value, size_t size, int flags)
+-{
+-    char buffer[PATH_MAX];
+-    return lsetxattr(rpath(ctx, path, buffer), MAP_ACL_ACCESS, value,
+-            size, flags);
+-}
+-
+-static int mp_pacl_removexattr(FsContext *ctx,
+-                               const char *path, const char *name)
+-{
+-    int ret;
+-    char buffer[PATH_MAX];
+-    ret  = lremovexattr(rpath(ctx, path, buffer), MAP_ACL_ACCESS);
+-    if (ret == -1 && errno == ENODATA) {
+-        /*
+-         * We don't get ENODATA error when trying to remove a
+-         * posix acl that is not present. So don't throw the error
+-         * even in case of mapped security model
+-         */
+-        errno = 0;
+-        ret = 0;
+-    }
+-    return ret;
+-}
+-
+-static ssize_t mp_dacl_getxattr(FsContext *ctx, const char *path,
+-                                const char *name, void *value, size_t size)
+-{
+-    char buffer[PATH_MAX];
+-    return lgetxattr(rpath(ctx, path, buffer), MAP_ACL_DEFAULT, value, size);
+-}
+-
+-static ssize_t mp_dacl_listxattr(FsContext *ctx, const char *path,
+-                                 char *name, void *value, size_t osize)
+-{
+-    ssize_t len = sizeof(ACL_DEFAULT);
+-
+-    if (!value) {
+-        return len;
+-    }
+-
+-    if (osize < len) {
+-        errno = ERANGE;
+-        return -1;
+-    }
+-
+-    strncpy(value, ACL_DEFAULT, len);
+-    return 0;
+-}
+-
+-static int mp_dacl_setxattr(FsContext *ctx, const char *path, const char *name,
+-                            void *value, size_t size, int flags)
+-{
+-    char buffer[PATH_MAX];
+-    return lsetxattr(rpath(ctx, path, buffer), MAP_ACL_DEFAULT, value,
+-            size, flags);
+-}
+-
+-static int mp_dacl_removexattr(FsContext *ctx,
+-                               const char *path, const char *name)
+-{
+-    int ret;
+-    char buffer[PATH_MAX];
+-    ret  = lremovexattr(rpath(ctx, path, buffer), MAP_ACL_DEFAULT);
+-    if (ret == -1 && errno == ENODATA) {
+-        /*
+-         * We don't get ENODATA error when trying to remove a
+-         * posix acl that is not present. So don't throw the error
+-         * even in case of mapped security model
+-         */
+-        errno = 0;
+-        ret = 0;
+-    }
+-    return ret;
+-}
+-
+-
+-XattrOperations mapped_pacl_xattr = {
+-    .name = "system.posix_acl_access",
+-    .getxattr = mp_pacl_getxattr,
+-    .setxattr = mp_pacl_setxattr,
+-    .listxattr = mp_pacl_listxattr,
+-    .removexattr = mp_pacl_removexattr,
+-};
+-
+-XattrOperations mapped_dacl_xattr = {
+-    .name = "system.posix_acl_default",
+-    .getxattr = mp_dacl_getxattr,
+-    .setxattr = mp_dacl_setxattr,
+-    .listxattr = mp_dacl_listxattr,
+-    .removexattr = mp_dacl_removexattr,
+-};
+-
+-XattrOperations passthrough_acl_xattr = {
+-    .name = "system.posix_acl_",
+-    .getxattr = pt_getxattr,
+-    .setxattr = pt_setxattr,
+-    .listxattr = pt_listxattr,
+-    .removexattr = pt_removexattr,
+-};
+-
+-XattrOperations none_acl_xattr = {
+-    .name = "system.posix_acl_",
+-    .getxattr = notsup_getxattr,
+-    .setxattr = notsup_setxattr,
+-    .listxattr = notsup_listxattr,
+-    .removexattr = notsup_removexattr,
+-};
+diff --git a/hw/9pfs/virtio-9p-proxy.c b/hw/9pfs/virtio-9p-proxy.c
+index d5ad208..6011eda 100644
+--- a/hw/9pfs/virtio-9p-proxy.c
++++ b/hw/9pfs/virtio-9p-proxy.c
+@@ -631,7 +631,7 @@ static int proxy_close(FsContext *ctx, V9fsFidOpenState *fs)
+ 
+ static int proxy_closedir(FsContext *ctx, V9fsFidOpenState *fs)
+ {
+-    return closedir(fs->dir);
++    return closedir(fs->dir.stream);
+ }
+ 
+ static int proxy_open(FsContext *ctx, V9fsPath *fs_path,
+@@ -650,14 +650,14 @@ static int proxy_opendir(FsContext *ctx,
+ {
+     int serrno, fd;
+ 
+-    fs->dir = NULL;
++    fs->dir.stream = NULL;
+     fd = v9fs_request(ctx->private, T_OPEN, NULL, "sd", fs_path, O_DIRECTORY);
+     if (fd < 0) {
+         errno = -fd;
+         return -1;
+     }
+-    fs->dir = fdopendir(fd);
+-    if (!fs->dir) {
++    fs->dir.stream = fdopendir(fd);
++    if (!fs->dir.stream) {
+         serrno = errno;
+         close(fd);
+         errno = serrno;
+@@ -668,24 +668,24 @@ static int proxy_opendir(FsContext *ctx,
+ 
+ static void proxy_rewinddir(FsContext *ctx, V9fsFidOpenState *fs)
+ {
+-    return rewinddir(fs->dir);
++    return rewinddir(fs->dir.stream);
+ }
+ 
+ static off_t proxy_telldir(FsContext *ctx, V9fsFidOpenState *fs)
+ {
+-    return telldir(fs->dir);
++    return telldir(fs->dir.stream);
+ }
+ 
+ static int proxy_readdir_r(FsContext *ctx, V9fsFidOpenState *fs,
+                            struct dirent *entry,
+                            struct dirent **result)
+ {
+-    return readdir_r(fs->dir, entry, result);
++    return readdir_r(fs->dir.stream, entry, result);
+ }
+ 
+ static void proxy_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off)
+ {
+-    return seekdir(fs->dir, off);
++    return seekdir(fs->dir.stream, off);
+ }
+ 
+ static ssize_t proxy_preadv(FsContext *ctx, V9fsFidOpenState *fs,
+@@ -791,7 +791,7 @@ static int proxy_fstat(FsContext *fs_ctx, int fid_type,
+     int fd;
+ 
+     if (fid_type == P9_FID_DIR) {
+-        fd = dirfd(fs->dir);
++        fd = dirfd(fs->dir.stream);
+     } else {
+         fd = fs->fd;
+     }
+@@ -936,7 +936,7 @@ static int proxy_fsync(FsContext *ctx, int fid_type,
+     int fd;
+ 
+     if (fid_type == P9_FID_DIR) {
+-        fd = dirfd(fs->dir);
++        fd = dirfd(fs->dir.stream);
+     } else {
+         fd = fs->fd;
+     }
+@@ -1033,13 +1033,10 @@ static int proxy_name_to_path(FsContext *ctx, V9fsPath *dir_path,
+                               const char *name, V9fsPath *target)
+ {
+     if (dir_path) {
+-        v9fs_string_sprintf((V9fsString *)target, "%s/%s",
+-                            dir_path->data, name);
++        v9fs_path_sprintf(target, "%s/%s", dir_path->data, name);
+     } else {
+-        v9fs_string_sprintf((V9fsString *)target, "%s", name);
++        v9fs_path_sprintf(target, "%s", name);
+     }
+-    /* Bump the size for including terminating NULL */
+-    target->size++;
+     return 0;
+ }
+ 
+diff --git a/hw/9pfs/virtio-9p-synth.c b/hw/9pfs/virtio-9p-synth.c
+index 92e0b09..30aa933 100644
+--- a/hw/9pfs/virtio-9p-synth.c
++++ b/hw/9pfs/virtio-9p-synth.c
+@@ -14,7 +14,7 @@
+ 
+ #include "hw/virtio.h"
+ #include "virtio-9p.h"
+-#include "virtio-9p-xattr.h"
++#include "9p-xattr.h"
+ #include "fsdev/qemu-fsdev.h"
+ #include "virtio-9p-synth.h"
+ 
+diff --git a/hw/9pfs/virtio-9p-xattr-user.c b/hw/9pfs/virtio-9p-xattr-user.c
+deleted file mode 100644
+index 5044a3e..0000000
+--- a/hw/9pfs/virtio-9p-xattr-user.c
++++ /dev/null
+@@ -1,112 +0,0 @@
+-/*
+- * Virtio 9p user. xattr callback
+- *
+- * Copyright IBM, Corp. 2010
+- *
+- * 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 <sys/types.h>
+-#include "hw/virtio.h"
+-#include "virtio-9p.h"
+-#include "fsdev/file-op-9p.h"
+-#include "virtio-9p-xattr.h"
+-
+-
+-static ssize_t mp_user_getxattr(FsContext *ctx, const char *path,
+-                                const char *name, void *value, size_t size)
+-{
+-    char buffer[PATH_MAX];
+-    if (strncmp(name, "user.virtfs.", 12) == 0) {
+-        /*
+-         * Don't allow fetch of user.virtfs namesapce
+-         * in case of mapped security
+-         */
+-        errno = ENOATTR;
+-        return -1;
+-    }
+-    return lgetxattr(rpath(ctx, path, buffer), name, value, size);
+-}
+-
+-static ssize_t mp_user_listxattr(FsContext *ctx, const char *path,
+-                                 char *name, void *value, size_t size)
+-{
+-    int name_size = strlen(name) + 1;
+-    if (strncmp(name, "user.virtfs.", 12) == 0) {
+-
+-        /*  check if it is a mapped posix acl */
+-        if (strncmp(name, "user.virtfs.system.posix_acl_", 29) == 0) {
+-            /* adjust the name and size */
+-            name += 12;
+-            name_size -= 12;
+-        } else {
+-            /*
+-             * Don't allow fetch of user.virtfs namesapce
+-             * in case of mapped security
+-             */
+-            return 0;
+-        }
+-    }
+-    if (!value) {
+-        return name_size;
+-    }
+-
+-    if (size < name_size) {
+-        errno = ERANGE;
+-        return -1;
+-    }
+-
+-    strncpy(value, name, name_size);
+-    return name_size;
+-}
+-
+-static int mp_user_setxattr(FsContext *ctx, const char *path, const char *name,
+-                            void *value, size_t size, int flags)
+-{
+-    char buffer[PATH_MAX];
+-    if (strncmp(name, "user.virtfs.", 12) == 0) {
+-        /*
+-         * Don't allow fetch of user.virtfs namesapce
+-         * in case of mapped security
+-         */
+-        errno = EACCES;
+-        return -1;
+-    }
+-    return lsetxattr(rpath(ctx, path, buffer), name, value, size, flags);
+-}
+-
+-static int mp_user_removexattr(FsContext *ctx,
+-                               const char *path, const char *name)
+-{
+-    char buffer[PATH_MAX];
+-    if (strncmp(name, "user.virtfs.", 12) == 0) {
+-        /*
+-         * Don't allow fetch of user.virtfs namesapce
+-         * in case of mapped security
+-         */
+-        errno = EACCES;
+-        return -1;
+-    }
+-    return lremovexattr(rpath(ctx, path, buffer), name);
+-}
+-
+-XattrOperations mapped_user_xattr = {
+-    .name = "user.",
+-    .getxattr = mp_user_getxattr,
+-    .setxattr = mp_user_setxattr,
+-    .listxattr = mp_user_listxattr,
+-    .removexattr = mp_user_removexattr,
+-};
+-
+-XattrOperations passthrough_user_xattr = {
+-    .name = "user.",
+-    .getxattr = pt_getxattr,
+-    .setxattr = pt_setxattr,
+-    .listxattr = pt_listxattr,
+-    .removexattr = pt_removexattr,
+-};
+diff --git a/hw/9pfs/virtio-9p-xattr.c b/hw/9pfs/virtio-9p-xattr.c
+deleted file mode 100644
+index 7f08f6e..0000000
+--- a/hw/9pfs/virtio-9p-xattr.c
++++ /dev/null
+@@ -1,160 +0,0 @@
+-/*
+- * Virtio 9p  xattr callback
+- *
+- * Copyright IBM, Corp. 2010
+- *
+- * 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 "hw/virtio.h"
+-#include "virtio-9p.h"
+-#include "fsdev/file-op-9p.h"
+-#include "virtio-9p-xattr.h"
+-
+-
+-static XattrOperations *get_xattr_operations(XattrOperations **h,
+-                                             const char *name)
+-{
+-    XattrOperations *xops;
+-    for (xops = *(h)++; xops != NULL; xops = *(h)++) {
+-        if (!strncmp(name, xops->name, strlen(xops->name))) {
+-            return xops;
+-        }
+-    }
+-    return NULL;
+-}
+-
+-ssize_t v9fs_get_xattr(FsContext *ctx, const char *path,
+-                       const char *name, void *value, size_t size)
+-{
+-    XattrOperations *xops = get_xattr_operations(ctx->xops, name);
+-    if (xops) {
+-        return xops->getxattr(ctx, path, name, value, size);
+-    }
+-    errno = -EOPNOTSUPP;
+-    return -1;
+-}
+-
+-ssize_t pt_listxattr(FsContext *ctx, const char *path,
+-                     char *name, void *value, size_t size)
+-{
+-    int name_size = strlen(name) + 1;
+-    if (!value) {
+-        return name_size;
+-    }
+-
+-    if (size < name_size) {
+-        errno = ERANGE;
+-        return -1;
+-    }
+-
+-    strncpy(value, name, name_size);
+-    return name_size;
+-}
+-
+-
+-/*
+- * Get the list and pass to each layer to find out whether
+- * to send the data or not
+- */
+-ssize_t v9fs_list_xattr(FsContext *ctx, const char *path,
+-                        void *value, size_t vsize)
+-{
+-    ssize_t size = 0;
+-    char buffer[PATH_MAX];
+-    void *ovalue = value;
+-    XattrOperations *xops;
+-    char *orig_value, *orig_value_start;
+-    ssize_t xattr_len, parsed_len = 0, attr_len;
+-
+-    /* Get the actual len */
+-    xattr_len = llistxattr(rpath(ctx, path, buffer), value, 0);
+-    if (xattr_len <= 0) {
+-        return xattr_len;
+-    }
+-
+-    /* Now fetch the xattr and find the actual size */
+-    orig_value = g_malloc(xattr_len);
+-    xattr_len = llistxattr(rpath(ctx, path, buffer), orig_value, xattr_len);
+-
+-    /* store the orig pointer */
+-    orig_value_start = orig_value;
+-    while (xattr_len > parsed_len) {
+-        xops = get_xattr_operations(ctx->xops, orig_value);
+-        if (!xops) {
+-            goto next_entry;
+-        }
+-
+-        if (!value) {
+-            size += xops->listxattr(ctx, path, orig_value, value, vsize);
+-        } else {
+-            size = xops->listxattr(ctx, path, orig_value, value, vsize);
+-            if (size < 0) {
+-                goto err_out;
+-            }
+-            value += size;
+-            vsize -= size;
+-        }
+-next_entry:
+-        /* Got the next entry */
+-        attr_len = strlen(orig_value) + 1;
+-        parsed_len += attr_len;
+-        orig_value += attr_len;
+-    }
+-    if (value) {
+-        size = value - ovalue;
+-    }
+-
+-err_out:
+-    g_free(orig_value_start);
+-    return size;
+-}
+-
+-int v9fs_set_xattr(FsContext *ctx, const char *path, const char *name,
+-                   void *value, size_t size, int flags)
+-{
+-    XattrOperations *xops = get_xattr_operations(ctx->xops, name);
+-    if (xops) {
+-        return xops->setxattr(ctx, path, name, value, size, flags);
+-    }
+-    errno = -EOPNOTSUPP;
+-    return -1;
+-
+-}
+-
+-int v9fs_remove_xattr(FsContext *ctx,
+-                      const char *path, const char *name)
+-{
+-    XattrOperations *xops = get_xattr_operations(ctx->xops, name);
+-    if (xops) {
+-        return xops->removexattr(ctx, path, name);
+-    }
+-    errno = -EOPNOTSUPP;
+-    return -1;
+-
+-}
+-
+-XattrOperations *mapped_xattr_ops[] = {
+-    &mapped_user_xattr,
+-    &mapped_pacl_xattr,
+-    &mapped_dacl_xattr,
+-    NULL,
+-};
+-
+-XattrOperations *passthrough_xattr_ops[] = {
+-    &passthrough_user_xattr,
+-    &passthrough_acl_xattr,
+-    NULL,
+-};
+-
+-/* for .user none model should be same as passthrough */
+-XattrOperations *none_xattr_ops[] = {
+-    &passthrough_user_xattr,
+-    &none_acl_xattr,
+-    NULL,
+-};
+diff --git a/hw/9pfs/virtio-9p-xattr.h b/hw/9pfs/virtio-9p-xattr.h
+deleted file mode 100644
+index 9437280..0000000
+--- a/hw/9pfs/virtio-9p-xattr.h
++++ /dev/null
+@@ -1,105 +0,0 @@
+-/*
+- * Virtio 9p
+- *
+- * Copyright IBM, Corp. 2010
+- *
+- * 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.
+- *
+- */
+-#ifndef _QEMU_VIRTIO_9P_XATTR_H
+-#define _QEMU_VIRTIO_9P_XATTR_H
+-
+-#include "qemu-xattr.h"
+-
+-typedef struct xattr_operations
+-{
+-    const char *name;
+-    ssize_t (*getxattr)(FsContext *ctx, const char *path,
+-                        const char *name, void *value, size_t size);
+-    ssize_t (*listxattr)(FsContext *ctx, const char *path,
+-                         char *name, void *value, size_t size);
+-    int (*setxattr)(FsContext *ctx, const char *path, const char *name,
+-                    void *value, size_t size, int flags);
+-    int (*removexattr)(FsContext *ctx,
+-                       const char *path, const char *name);
+-} XattrOperations;
+-
+-
+-extern XattrOperations mapped_user_xattr;
+-extern XattrOperations passthrough_user_xattr;
+-
+-extern XattrOperations mapped_pacl_xattr;
+-extern XattrOperations mapped_dacl_xattr;
+-extern XattrOperations passthrough_acl_xattr;
+-extern XattrOperations none_acl_xattr;
+-
+-extern XattrOperations *mapped_xattr_ops[];
+-extern XattrOperations *passthrough_xattr_ops[];
+-extern XattrOperations *none_xattr_ops[];
+-
+-ssize_t v9fs_get_xattr(FsContext *ctx, const char *path, const char *name,
+-                       void *value, size_t size);
+-ssize_t v9fs_list_xattr(FsContext *ctx, const char *path, void *value,
+-                        size_t vsize);
+-int v9fs_set_xattr(FsContext *ctx, const char *path, const char *name,
+-                          void *value, size_t size, int flags);
+-int v9fs_remove_xattr(FsContext *ctx, const char *path, const char *name);
+-ssize_t pt_listxattr(FsContext *ctx, const char *path, char *name, void *value,
+-                     size_t size);
+-
+-static inline ssize_t pt_getxattr(FsContext *ctx, const char *path,
+-                                  const char *name, void *value, size_t size)
+-{
+-    char buffer[PATH_MAX];
+-    return lgetxattr(rpath(ctx, path, buffer), name, value, size);
+-}
+-
+-static inline int pt_setxattr(FsContext *ctx, const char *path,
+-                              const char *name, void *value,
+-                              size_t size, int flags)
+-{
+-    char buffer[PATH_MAX];
+-    return lsetxattr(rpath(ctx, path, buffer), name, value, size, flags);
+-}
+-
+-static inline int pt_removexattr(FsContext *ctx,
+-                                 const char *path, const char *name)
+-{
+-    char buffer[PATH_MAX];
+-    return lremovexattr(rpath(ctx, path, buffer), name);
+-}
+-
+-static inline ssize_t notsup_getxattr(FsContext *ctx, const char *path,
+-                                      const char *name, void *value,
+-                                      size_t size)
+-{
+-    errno = ENOTSUP;
+-    return -1;
+-}
+-
+-static inline int notsup_setxattr(FsContext *ctx, const char *path,
+-                                  const char *name, void *value,
+-                                  size_t size, int flags)
+-{
+-    errno = ENOTSUP;
+-    return -1;
+-}
+-
+-static inline ssize_t notsup_listxattr(FsContext *ctx, const char *path,
+-                                       char *name, void *value, size_t size)
+-{
+-    return 0;
+-}
+-
+-static inline int notsup_removexattr(FsContext *ctx,
+-                                     const char *path, const char *name)
+-{
+-    errno = ENOTSUP;
+-    return -1;
+-}
+-
+-#endif
+diff --git a/hw/9pfs/virtio-9p.c b/hw/9pfs/virtio-9p.c
+index bce5015..345c9a8 100644
+--- a/hw/9pfs/virtio-9p.c
++++ b/hw/9pfs/virtio-9p.c
+@@ -12,12 +12,13 @@
+  */
+ 
+ #include "hw/virtio.h"
++#include <glib/gprintf.h>
+ #include "hw/pc.h"
+ #include "qemu_socket.h"
+ #include "hw/virtio-pci.h"
+ #include "virtio-9p.h"
+ #include "fsdev/qemu-fsdev.h"
+-#include "virtio-9p-xattr.h"
++#include "9p-xattr.h"
+ #include "virtio-9p-coth.h"
+ #include "trace.h"
+ #include "migration.h"
+@@ -148,6 +149,20 @@ void v9fs_path_free(V9fsPath *path)
+     path->size = 0;
+ }
+ 
++
++void GCC_FMT_ATTR(2, 3)
++v9fs_path_sprintf(V9fsPath *path, const char *fmt, ...)
++{
++    va_list ap;
++
++    v9fs_path_free(path);
++
++    va_start(ap, fmt);
++    /* Bump the size for including terminating NULL */
++    path->size = g_vasprintf(&path->data, fmt, ap) + 1;
++    va_end(ap);
++}
++
+ void v9fs_path_copy(V9fsPath *lhs, V9fsPath *rhs)
+ {
+     v9fs_path_free(lhs);
+@@ -200,7 +215,7 @@ static int v9fs_reopen_fid(V9fsPDU *pdu, V9fsFidState *f)
+             } while (err == -EINTR && !pdu->cancelled);
+         }
+     } else if (f->fid_type == P9_FID_DIR) {
+-        if (f->fs.dir == NULL) {
++        if (f->fs.dir.stream == NULL) {
+             do {
+                 err = v9fs_co_opendir(pdu, f);
+             } while (err == -EINTR && !pdu->cancelled);
+@@ -269,6 +284,9 @@ static V9fsFidState *alloc_fid(V9fsState *s, int32_t fid)
+     f->next = s->fid_list;
+     s->fid_list = f;
+ 
++    v9fs_readdir_init(&f->fs.dir);
++    v9fs_readdir_init(&f->fs_reclaim.dir);
++
+     return f;
+ }
+ 
+@@ -316,7 +334,7 @@ static int free_fid(V9fsPDU *pdu, V9fsFidState *fidp)
+             retval = v9fs_co_close(pdu, &fidp->fs);
+         }
+     } else if (fidp->fid_type == P9_FID_DIR) {
+-        if (fidp->fs.dir != NULL) {
++        if (fidp->fs.dir.stream != NULL) {
+             retval = v9fs_co_closedir(pdu, &fidp->fs);
+         }
+     } else if (fidp->fid_type == P9_FID_XATTR) {
+@@ -413,7 +431,7 @@ void v9fs_reclaim_fd(V9fsPDU *pdu)
+                 reclaim_count++;
+             }
+         } else if (f->fid_type == P9_FID_DIR) {
+-            if (f->fs.dir != NULL) {
++            if (f->fs.dir.stream != NULL) {
+                 /*
+                  * Up the reference count so that
+                  * a clunk request won't free this fid
+@@ -421,8 +439,8 @@ void v9fs_reclaim_fd(V9fsPDU *pdu)
+                 f->ref++;
+                 f->rclm_lst = reclaim_list;
+                 reclaim_list = f;
+-                f->fs_reclaim.dir = f->fs.dir;
+-                f->fs.dir = NULL;
++                f->fs_reclaim.dir.stream = f->fs.dir.stream;
++                f->fs.dir.stream = NULL;
+                 reclaim_count++;
+             }
+         }
+@@ -887,10 +905,8 @@ 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_sprintf(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)
+@@ -1634,6 +1650,9 @@ static int v9fs_do_readdir_with_stat(V9fsPDU *pdu,
+ 
+     while (1) {
+         v9fs_path_init(&path);
++
++        v9fs_readdir_lock(&fidp->fs.dir);
++
+         err = v9fs_co_readdir_r(pdu, fidp, dent, &result);
+         if (err || !result) {
+             break;
+@@ -1652,6 +1671,9 @@ static int v9fs_do_readdir_with_stat(V9fsPDU *pdu,
+         }
+         /* 11 = 7 + 4 (7 = start offset, 4 = space for storing count) */
+         len = pdu_marshal(pdu, 11 + count, "S", &v9stat);
++
++        v9fs_readdir_unlock(&fidp->fs.dir);
++
+         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);
+@@ -1666,6 +1688,8 @@ static int v9fs_do_readdir_with_stat(V9fsPDU *pdu,
+         saved_dir_pos = dent->d_off;
+     }
+ out:
++    v9fs_readdir_unlock(&fidp->fs.dir);
++
+     g_free(dent);
+     v9fs_path_free(&path);
+     if (err < 0) {
+@@ -1820,6 +1844,8 @@ static int v9fs_do_readdir(V9fsPDU *pdu,
+     dent = g_malloc(sizeof(struct dirent));
+ 
+     while (1) {
++        v9fs_readdir_lock(&fidp->fs.dir);
++
+         err = v9fs_co_readdir_r(pdu, fidp, dent, &result);
+         if (err || !result) {
+             break;
+@@ -1827,6 +1853,8 @@ static int v9fs_do_readdir(V9fsPDU *pdu,
+         v9fs_string_init(&name);
+         v9fs_string_sprintf(&name, "%s", dent->d_name);
+         if ((count + v9fs_readdir_data_size(&name)) > max_count) {
++            v9fs_readdir_unlock(&fidp->fs.dir);
++
+             /* Ran out of buffer. Set dir back to old position and return */
+             v9fs_co_seekdir(pdu, fidp, saved_dir_pos);
+             v9fs_string_free(&name);
+@@ -1848,6 +1876,9 @@ static int v9fs_do_readdir(V9fsPDU *pdu,
+         len = pdu_marshal(pdu, 11 + count, "Qqbs",
+                           &qid, dent->d_off,
+                           dent->d_type, &name);
++
++        v9fs_readdir_unlock(&fidp->fs.dir);
++
+         if (len < 0) {
+             v9fs_co_seekdir(pdu, fidp, saved_dir_pos);
+             v9fs_string_free(&name);
+@@ -1858,6 +1889,9 @@ static int v9fs_do_readdir(V9fsPDU *pdu,
+         v9fs_string_free(&name);
+         saved_dir_pos = dent->d_off;
+     }
++
++    v9fs_readdir_unlock(&fidp->fs.dir);
++
+     g_free(dent);
+     if (err < 0) {
+         return err;
+@@ -1889,7 +1923,7 @@ static void v9fs_readdir(void *opaque)
+         retval = -EINVAL;
+         goto out_nofid;
+     }
+-    if (!fidp->fs.dir) {
++    if (!fidp->fs.dir.stream) {
+         retval = -EINVAL;
+         goto out;
+     }
+diff --git a/hw/9pfs/virtio-9p.h b/hw/9pfs/virtio-9p.h
+index c667363..b2d5ddf 100644
+--- a/hw/9pfs/virtio-9p.h
++++ b/hw/9pfs/virtio-9p.h
+@@ -172,13 +172,33 @@ typedef struct V9fsXattr
+     int flags;
+ } V9fsXattr;
+ 
++typedef struct V9fsDir {
++    DIR *stream;
++    QemuMutex readdir_mutex;
++} V9fsDir;
++
++static inline void v9fs_readdir_lock(V9fsDir *dir)
++{
++    qemu_mutex_lock(&dir->readdir_mutex);
++}
++
++static inline void v9fs_readdir_unlock(V9fsDir *dir)
++{
++    qemu_mutex_unlock(&dir->readdir_mutex);
++}
++
++static inline void v9fs_readdir_init(V9fsDir *dir)
++{
++    qemu_mutex_init(&dir->readdir_mutex);
++}
++
+ /*
+  * Filled by fs driver on open and other
+  * calls.
+  */
+ union V9fsFidOpenState {
+     int fd;
+-    DIR *dir;
++    V9fsDir dir;
+     V9fsXattr xattr;
+     /*
+      * private pointer for fs drivers, that
+@@ -394,6 +414,7 @@ extern void virtio_9p_set_fd_limit(void);
+ extern void v9fs_reclaim_fd(V9fsPDU *pdu);
+ extern void v9fs_path_init(V9fsPath *path);
+ extern void v9fs_path_free(V9fsPath *path);
++extern void v9fs_path_sprintf(V9fsPath *path, const char *fmt, ...);
+ extern void v9fs_path_copy(V9fsPath *lhs, V9fsPath *rhs);
+ extern int v9fs_name_to_path(V9fsState *s, V9fsPath *dirpath,
+                              const char *name, V9fsPath *path);
diff --git a/debian/patches/security/CVE-2017-7377-9pfs-fix-file-descriptor-leak.patch b/debian/patches/security/CVE-2017-7377-9pfs-fix-file-descriptor-leak.patch
new file mode 100644
index 0000000000..42ab1db17b
--- /dev/null
+++ b/debian/patches/security/CVE-2017-7377-9pfs-fix-file-descriptor-leak.patch
@@ -0,0 +1,45 @@
+From: Li Qiang <liq3ea@gmail.com>
+Date: Mon, 27 Mar 2017 21:13:19 +0200
+Subject: CVE-2017-7377: 9pfs: fix file descriptor leak
+
+The v9fs_create() and v9fs_lcreate() functions are used to create a file
+on the backend and to associate it to a fid. The fid shouldn't be already
+in-use, otherwise both functions may silently leak a file descriptor or
+allocated memory. The current code doesn't check that.
+
+This patch ensures that the fid isn't already associated to anything
+before using it.
+
+Signed-off-by: Li Qiang <liqiang6-s@360.cn>
+(reworded the changelog, Greg Kurz)
+Signed-off-by: Greg Kurz <groug@kaod.org>
+---
+ hw/9pfs/virtio-9p.c | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+diff --git a/hw/9pfs/virtio-9p.c b/hw/9pfs/virtio-9p.c
+index 345c9a8..e270b84 100644
+--- a/hw/9pfs/virtio-9p.c
++++ b/hw/9pfs/virtio-9p.c
+@@ -1505,6 +1505,10 @@ static void v9fs_lcreate(void *opaque)
+         err = -ENOENT;
+         goto out_nofid;
+     }
++    if (fidp->fid_type != P9_FID_NONE) {
++        err = -EINVAL;
++        goto out;
++    }
+ 
+     flags = get_dotl_openflags(pdu->s, flags);
+     err = v9fs_co_open2(pdu, fidp, &name, gid,
+@@ -2114,6 +2118,10 @@ static void v9fs_create(void *opaque)
+         err = -EINVAL;
+         goto out_nofid;
+     }
++    if (fidp->fid_type != P9_FID_NONE) {
++        err = -EINVAL;
++        goto out;
++    }
+     if (perm & P9_STAT_MODE_DIR) {
+         err = v9fs_co_mkdir(pdu, fidp, &name, perm & 0777,
+                             fidp->uid, -1, &stbuf);
diff --git a/debian/patches/security/CVE-2017-7493-9pfs-local-forbid-client-access-to-metadata.patch b/debian/patches/security/CVE-2017-7493-9pfs-local-forbid-client-access-to-metadata.patch
new file mode 100644
index 0000000000..ffb10ee466
--- /dev/null
+++ b/debian/patches/security/CVE-2017-7493-9pfs-local-forbid-client-access-to-metadata.patch
@@ -0,0 +1,168 @@
+From: Greg Kurz <groug@kaod.org>
+Date: Fri, 5 May 2017 14:48:08 +0200
+Subject: CVE-2017-7493: 9pfs: local: forbid client access to metadata
+
+When using the mapped-file security mode, we shouldn't let the client mess
+with the metadata. The current code already tries to hide the metadata dir
+from the client by skipping it in local_readdir(). But the client can still
+access or modify it through several other operations. This can be used to
+escalate privileges in the guest.
+
+Affected backend operations are:
+- local_mknod()
+- local_mkdir()
+- local_open2()
+- local_symlink()
+- local_link()
+- local_unlinkat()
+- local_renameat()
+- local_rename()
+- local_name_to_path()
+
+Other operations are safe because they are only passed a fid path, which
+is computed internally in local_name_to_path().
+
+This patch converts all the functions listed above to fail and return
+EINVAL when being passed the name of the metadata dir. This may look
+like a poor choice for errno, but there's no such thing as an illegal
+path name on Linux and I could not think of anything better.
+
+This fixes CVE-2017-7493.
+
+Reported-by: Leo Gaspard <leo@gaspard.io>
+Signed-off-by: Greg Kurz <groug@kaod.org>
+Reviewed-by: Eric Blake <eblake@redhat.com>
+---
+ hw/9pfs/9p-local.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 55 insertions(+), 1 deletion(-)
+
+diff --git a/hw/9pfs/9p-local.c b/hw/9pfs/9p-local.c
+index 3437b34..7d4436a 100644
+--- a/hw/9pfs/9p-local.c
++++ b/hw/9pfs/9p-local.c
+@@ -452,6 +452,11 @@ static off_t local_telldir(FsContext *ctx, V9fsFidOpenState *fs)
+     return telldir(fs->dir.stream);
+ }
+ 
++static bool local_is_mapped_file_metadata(FsContext *fs_ctx, const char *name)
++{
++    return !strcmp(name, VIRTFS_META_DIR);
++}
++
+ static int local_readdir_r(FsContext *ctx, V9fsFidOpenState *fs,
+                            struct dirent *entry,
+                            struct dirent **result)
+@@ -464,7 +469,7 @@ again:
+         entry->d_type = DT_UNKNOWN;
+     } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
+         if (!ret && *result != NULL &&
+-            !strcmp(entry->d_name, VIRTFS_META_DIR)) {
++            local_is_mapped_file_metadata(ctx, entry->d_name)) {
+             /* skp the meta data directory */
+             goto again;
+         }
+@@ -558,6 +563,12 @@ static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path,
+     int err = -1;
+     int dirfd;
+ 
++    if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE &&
++        local_is_mapped_file_metadata(fs_ctx, name)) {
++        errno = EINVAL;
++        return -1;
++    }
++
+     dirfd = local_opendir_nofollow(fs_ctx, dir_path->data);
+     if (dirfd == -1) {
+         return -1;
+@@ -604,6 +615,12 @@ static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path,
+     int err = -1;
+     int dirfd;
+ 
++    if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE &&
++        local_is_mapped_file_metadata(fs_ctx, name)) {
++        errno = EINVAL;
++        return -1;
++    }
++
+     dirfd = local_opendir_nofollow(fs_ctx, dir_path->data);
+     if (dirfd == -1) {
+         return -1;
+@@ -693,6 +710,12 @@ static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
+     int err = -1;
+     int dirfd;
+ 
++    if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE &&
++        local_is_mapped_file_metadata(fs_ctx, name)) {
++        errno = EINVAL;
++        return -1;
++    }
++
+     /*
+      * Mark all the open to not follow symlinks
+      */
+@@ -751,6 +774,12 @@ static int local_symlink(FsContext *fs_ctx, const char *oldpath,
+     int err = -1;
+     int dirfd;
+ 
++    if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE &&
++        local_is_mapped_file_metadata(fs_ctx, name)) {
++        errno = EINVAL;
++        return -1;
++    }
++
+     dirfd = local_opendir_nofollow(fs_ctx, dir_path->data);
+     if (dirfd == -1) {
+         return -1;
+@@ -825,6 +854,12 @@ static int local_link(FsContext *ctx, V9fsPath *oldpath,
+     int ret = -1;
+     int odirfd, ndirfd;
+ 
++    if (ctx->export_flags & V9FS_SM_MAPPED_FILE &&
++        local_is_mapped_file_metadata(ctx, name)) {
++        errno = EINVAL;
++        return -1;
++    }
++
+     odirfd = local_opendir_nofollow(ctx, odirpath);
+     if (odirfd == -1) {
+         goto out;
+@@ -1095,6 +1130,12 @@ static int local_lremovexattr(FsContext *ctx, V9fsPath *fs_path,
+ static int local_name_to_path(FsContext *ctx, V9fsPath *dir_path,
+                               const char *name, V9fsPath *target)
+ {
++    if (ctx->export_flags & V9FS_SM_MAPPED_FILE &&
++        local_is_mapped_file_metadata(ctx, name)) {
++        errno = EINVAL;
++        return -1;
++    }
++
+     if (dir_path) {
+         v9fs_path_sprintf(target, "%s/%s", dir_path->data, name);
+     } else if (strcmp(name, "/")) {
+@@ -1115,6 +1156,13 @@ static int local_renameat(FsContext *ctx, V9fsPath *olddir,
+     int ret;
+     int odirfd, ndirfd;
+ 
++    if (ctx->export_flags & V9FS_SM_MAPPED_FILE &&
++        (local_is_mapped_file_metadata(ctx, old_name) ||
++         local_is_mapped_file_metadata(ctx, new_name))) {
++        errno = EINVAL;
++        return -1;
++    }
++
+     odirfd = local_opendir_nofollow(ctx, olddir->data);
+     if (odirfd == -1) {
+         return -1;
+@@ -1205,6 +1253,12 @@ static int local_unlinkat(FsContext *ctx, V9fsPath *dir,
+     int ret;
+     int dirfd;
+ 
++    if (ctx->export_flags & V9FS_SM_MAPPED_FILE &&
++        local_is_mapped_file_metadata(ctx, name)) {
++        errno = EINVAL;
++        return -1;
++    }
++
+     dirfd = local_opendir_nofollow(ctx, dir->data);
+     if (dirfd == -1) {
+         return -1;
diff --git a/debian/patches/security/CVE-2017-8086-9pfs-xattr-fix-memory-leak-in-v9fs_list_xat.patch b/debian/patches/security/CVE-2017-8086-9pfs-xattr-fix-memory-leak-in-v9fs_list_xat.patch
new file mode 100644
index 0000000000..3aebffd013
--- /dev/null
+++ b/debian/patches/security/CVE-2017-8086-9pfs-xattr-fix-memory-leak-in-v9fs_list_xat.patch
@@ -0,0 +1,24 @@
+From: Li Qiang <liq3ea@gmail.com>
+Date: Fri, 7 Apr 2017 03:48:52 -0700
+Subject: CVE-2017-8086: 9pfs: xattr: fix memory leak in v9fs_list_xattr
+
+Free 'orig_value' in error path.
+
+Signed-off-by: Li Qiang <liqiang6-s@360.cn>
+Signed-off-by: Greg Kurz <groug@kaod.org>
+---
+ hw/9pfs/9p-xattr.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/hw/9pfs/9p-xattr.c b/hw/9pfs/9p-xattr.c
+index 40b644b..9b3e906 100644
+--- a/hw/9pfs/9p-xattr.c
++++ b/hw/9pfs/9p-xattr.c
+@@ -108,6 +108,7 @@ ssize_t v9fs_list_xattr(FsContext *ctx, const char *path,
+     g_free(name);
+     close_preserve_errno(dirfd);
+     if (xattr_len < 0) {
++        g_free(orig_value);
+         return -1;
+     }
+ 
diff --git a/debian/patches/series b/debian/patches/series
index 5bb56de70b..0a1734c718 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -146,3 +146,7 @@ security/CVE-2017-2615-cirrus-fix-oob-access-issue.patch
 security/display-cirrus-ignore-source-pitch-value-as-needed-in-bli.patch
 security/CVE-2017-2620-cirrus-add-blit_is_unsafe-call-to-cirrus_bi.patch
 Update-cirrus-code-from-2.8.1.patch
+9p-Update-major-parts-from-2.8.1.1.patch
+security/CVE-2017-8086-9pfs-xattr-fix-memory-leak-in-v9fs_list_xat.patch
+security/CVE-2017-7377-9pfs-fix-file-descriptor-leak.patch
+security/CVE-2017-7493-9pfs-local-forbid-client-access-to-metadata.patch

Reply to: