]> git.proxmox.com Git - mirror_qemu.git/commitdiff
Merge remote-tracking branch 'remotes/gkurz/tags/cve-2016-9602-for-upstream' into...
authorPeter Maydell <peter.maydell@linaro.org>
Wed, 1 Mar 2017 13:53:20 +0000 (13:53 +0000)
committerPeter Maydell <peter.maydell@linaro.org>
Wed, 1 Mar 2017 13:53:20 +0000 (13:53 +0000)
This pull request have all the fixes for CVE-2016-9602, so that it can
be easily picked up by downstreams, as suggested by Michel Tokarev.

# gpg: Signature made Tue 28 Feb 2017 10:21:32 GMT
# gpg:                using DSA key 0x02FC3AEB0101DBC2
# gpg: Good signature from "Greg Kurz <groug@kaod.org>"
# gpg:                 aka "Greg Kurz <groug@free.fr>"
# gpg:                 aka "Greg Kurz <gkurz@linux.vnet.ibm.com>"
# gpg:                 aka "Gregory Kurz (Groug) <groug@free.fr>"
# gpg:                 aka "[jpeg image of size 3330]"
# gpg: WARNING: This key is not certified with a trusted signature!
# gpg:          There is no indication that the signature belongs to the owner.
# Primary key fingerprint: 2BD4 3B44 535E C0A7 9894  DBA2 02FC 3AEB 0101 DBC2

* remotes/gkurz/tags/cve-2016-9602-for-upstream: (28 commits)
  9pfs: local: drop unused code
  9pfs: local: open2: don't follow symlinks
  9pfs: local: mkdir: don't follow symlinks
  9pfs: local: mknod: don't follow symlinks
  9pfs: local: symlink: don't follow symlinks
  9pfs: local: chown: don't follow symlinks
  9pfs: local: chmod: don't follow symlinks
  9pfs: local: link: don't follow symlinks
  9pfs: local: improve error handling in link op
  9pfs: local: rename: use renameat
  9pfs: local: renameat: don't follow symlinks
  9pfs: local: lstat: don't follow symlinks
  9pfs: local: readlink: don't follow symlinks
  9pfs: local: truncate: don't follow symlinks
  9pfs: local: statfs: don't follow symlinks
  9pfs: local: utimensat: don't follow symlinks
  9pfs: local: remove: don't follow symlinks
  9pfs: local: unlinkat: don't follow symlinks
  9pfs: local: lremovexattr: don't follow symlinks
  9pfs: local: lsetxattr: don't follow symlinks
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
1  2 
hw/9pfs/9p-local.c

diff --combined hw/9pfs/9p-local.c
index 2369b918aa3f91fbb0cca0dbd92fc1c419988488,432a30c3c1bb4649ecee2a017ebc748131661c47..f22a3c3654db63b650d6191b90735a9029c35a84
@@@ -13,7 -13,9 +13,9 @@@
  
  #include "qemu/osdep.h"
  #include "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>
  #define BTRFS_SUPER_MAGIC 0x9123683E
  #endif
  
- #define VIRTFS_META_DIR ".virtfs_metadata"
+ typedef struct {
+     int mountfd;
+ } LocalData;
  
- static char *local_mapped_attr_path(FsContext *ctx, const char *path)
+ int local_open_nofollow(FsContext *fs_ctx, const char *path, int flags,
+                         mode_t mode)
  {
-     int dirlen;
-     const char *name = strrchr(path, '/');
-     if (name) {
-         dirlen = name - path;
-         ++name;
-     } else {
-         name = path;
-         dirlen = 0;
+     LocalData *data = fs_ctx->private;
+     /* All paths are relative to the path data->mountfd points to */
+     while (*path == '/') {
+         path++;
      }
-     return g_strdup_printf("%s/%.*s/%s/%s", ctx->fs_root,
-                            dirlen, path, VIRTFS_META_DIR, name);
+     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 FILE *local_fopen(const char *path, const char *mode)
+ 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 = O_NOFOLLOW;
+     int flags;
      /*
       * only supports two modes
       */
      if (mode[0] == 'r') {
-         flags |= O_RDONLY;
+         flags = O_RDONLY;
      } else if (mode[0] == 'w') {
-         flags |= O_WRONLY | O_TRUNC | O_CREAT;
+         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 = open(path, flags, o_mode);
+     fd = openat_file(dirfd, name, flags, o_mode);
      if (fd == -1) {
          return NULL;
      }
  }
  
  #define ATTR_MAX 100
- static void local_mapped_file_attr(FsContext *ctx, const char *path,
+ static void local_mapped_file_attr(int dirfd, const char *name,
                                     struct stat *stbuf)
  {
      FILE *fp;
      char buf[ATTR_MAX];
-     char *attr_path;
+     int map_dirfd;
  
-     attr_path = local_mapped_attr_path(ctx, path);
-     fp = local_fopen(attr_path, "r");
-     g_free(attr_path);
+     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;
      }
  
  static int local_lstat(FsContext *fs_ctx, V9fsPath *fs_path, struct stat *stbuf)
  {
-     int err;
-     char *buffer;
-     char *path = fs_path->data;
+     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;
+     }
  
-     buffer = rpath(fs_ctx, path);
-     err =  lstat(buffer, stbuf);
+     err = fstatat(dirfd, name, stbuf, AT_SYMLINK_NOFOLLOW);
      if (err) {
          goto err_out;
      }
          gid_t tmp_gid;
          mode_t tmp_mode;
          dev_t tmp_dev;
-         if (getxattr(buffer, "user.virtfs.uid", &tmp_uid, sizeof(uid_t)) > 0) {
+         if (fgetxattrat_nofollow(dirfd, name, "user.virtfs.uid", &tmp_uid,
+                                  sizeof(uid_t)) > 0) {
              stbuf->st_uid = le32_to_cpu(tmp_uid);
          }
-         if (getxattr(buffer, "user.virtfs.gid", &tmp_gid, sizeof(gid_t)) > 0) {
+         if (fgetxattrat_nofollow(dirfd, name, "user.virtfs.gid", &tmp_gid,
+                                  sizeof(gid_t)) > 0) {
              stbuf->st_gid = le32_to_cpu(tmp_gid);
          }
-         if (getxattr(buffer, "user.virtfs.mode",
-                     &tmp_mode, sizeof(mode_t)) > 0) {
+         if (fgetxattrat_nofollow(dirfd, name, "user.virtfs.mode", &tmp_mode,
+                                  sizeof(mode_t)) > 0) {
              stbuf->st_mode = le32_to_cpu(tmp_mode);
          }
-         if (getxattr(buffer, "user.virtfs.rdev", &tmp_dev, sizeof(dev_t)) > 0) {
+         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(fs_ctx, path, stbuf);
+         local_mapped_file_attr(dirfd, name, stbuf);
      }
  
  err_out:
-     g_free(buffer);
-     return err;
- }
- static int local_create_mapped_attr_dir(FsContext *ctx, const char *path)
- {
-     int err;
-     char *attr_dir;
-     char *tmp_path = g_strdup(path);
-     attr_dir = g_strdup_printf("%s/%s/%s",
-              ctx->fs_root, dirname(tmp_path), VIRTFS_META_DIR);
-     err = mkdir(attr_dir, 0700);
-     if (err < 0 && errno == EEXIST) {
-         err = 0;
-     }
-     g_free(attr_dir);
-     g_free(tmp_path);
+     close_preserve_errno(dirfd);
+ out:
+     g_free(name);
+     g_free(dirpath);
      return err;
  }
  
- static int local_set_mapped_file_attr(FsContext *ctx,
-                                       const char *path, FsCred *credp)
+ static int local_set_mapped_file_attrat(int dirfd, const char *name,
+                                         FsCred *credp)
  {
      FILE *fp;
-     int ret = 0;
+     int ret;
      char buf[ATTR_MAX];
-     char *attr_path;
      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;
+     }
  
-     attr_path = local_mapped_attr_path(ctx, path);
-     fp = local_fopen(attr_path, "r");
+     map_dirfd = openat_dir(dirfd, VIRTFS_META_DIR);
+     if (map_dirfd == -1) {
+         return -1;
+     }
+     fp = local_fopenat(map_dirfd, name, "r");
      if (!fp) {
-         goto create_map_file;
+         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);
+             uid = atoi(buf + 11);
          } else if (!strncmp(buf, "virtfs.gid", 10)) {
-             gid = atoi(buf+11);
+             gid = atoi(buf + 11);
          } else if (!strncmp(buf, "virtfs.mode", 11)) {
-             mode = atoi(buf+12);
+             mode = atoi(buf + 12);
          } else if (!strncmp(buf, "virtfs.rdev", 11)) {
-             rdev = atoi(buf+12);
+             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 = local_fopen(attr_path, "w");
+     fp = local_fopenat(map_dirfd, name, "w");
+     close_preserve_errno(map_dirfd);
      if (!fp) {
-         ret = -1;
-         goto err_out;
+         return -1;
      }
  
      if (credp->fc_uid != -1) {
          rdev = credp->fc_rdev;
      }
  
      if (uid != -1) {
          fprintf(fp, "virtfs.uid=%d\n", uid);
      }
      }
      fclose(fp);
  
- err_out:
-     g_free(attr_path);
+     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_xattr(const char *path, FsCred *credp)
+ 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 = setxattr(path, "user.virtfs.uid", &tmp_uid, sizeof(uid_t), 0);
+         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 = setxattr(path, "user.virtfs.gid", &tmp_gid, sizeof(gid_t), 0);
+         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 = setxattr(path, "user.virtfs.mode", &tmp_mode, sizeof(mode_t), 0);
+         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 = setxattr(path, "user.virtfs.rdev", &tmp_rdev, sizeof(dev_t), 0);
+         err = fsetxattrat_nofollow(dirfd, path, "user.virtfs.rdev", &tmp_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)
+ static int local_set_cred_passthrough(FsContext *fs_ctx, int dirfd,
+                                       const char *name, FsCred *credp)
  {
-     char *buffer;
-     buffer = rpath(fs_ctx, path);
-     if (lchown(buffer, credp->fc_uid, credp->fc_gid) < 0) {
+     if (fchownat(dirfd, name, credp->fc_uid, credp->fc_gid,
+                  AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH) < 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) {
-             goto err;
+             return -1;
          }
      }
  
-     if (chmod(buffer, credp->fc_mode & 07777) < 0) {
-         goto err;
-     }
-     g_free(buffer);
-     return 0;
- err:
-     g_free(buffer);
-     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;
-     char *buffer;
-     char *path = fs_path->data;
  
      if ((fs_ctx->export_flags & V9FS_SM_MAPPED) ||
          (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE)) {
          int fd;
-         buffer = rpath(fs_ctx, path);
-         fd = open(buffer, O_RDONLY | O_NOFOLLOW);
-         g_free(buffer);
+         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(fd);
+         close_preserve_errno(fd);
      } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
                 (fs_ctx->export_flags & V9FS_SM_NONE)) {
-         buffer = rpath(fs_ctx, path);
-         tsize = readlink(buffer, buf, bufsz);
-         g_free(buffer);
+         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;
  }
@@@ -354,27 -412,32 +412,32 @@@ static int local_closedir(FsContext *ct
  static int local_open(FsContext *ctx, V9fsPath *fs_path,
                        int flags, V9fsFidOpenState *fs)
  {
-     char *buffer;
-     char *path = fs_path->data;
+     int fd;
  
-     buffer = rpath(ctx, path);
-     fs->fd = open(buffer, flags | O_NOFOLLOW);
-     g_free(buffer);
+     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)
  {
-     char *buffer;
-     char *path = fs_path->data;
+     int dirfd;
+     DIR *stream;
+     dirfd = local_opendir_nofollow(ctx, fs_path->data);
+     if (dirfd == -1) {
+         return -1;
+     }
  
-     buffer = rpath(ctx, path);
-     fs->dir.stream = opendir(buffer);
-     g_free(buffer);
-     if (!fs->dir.stream) {
+     stream = fdopendir(dirfd);
+     if (!stream) {
          return -1;
      }
+     fs->dir.stream = stream;
      return 0;
  }
  
@@@ -463,145 -526,122 +526,122 @@@ static ssize_t local_pwritev(FsContext 
  
  static int local_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
  {
-     char *buffer;
+     char *dirpath = g_path_get_dirname(fs_path->data);
+     char *name = g_path_get_basename(fs_path->data);
      int ret = -1;
-     char *path = fs_path->data;
+     int dirfd;
+     dirfd = local_opendir_nofollow(fs_ctx, dirpath);
+     if (dirfd == -1) {
+         goto out;
+     }
  
      if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
-         buffer = rpath(fs_ctx, path);
-         ret = local_set_xattr(buffer, credp);
-         g_free(buffer);
+         ret = local_set_xattrat(dirfd, name, 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)) {
-         buffer = rpath(fs_ctx, path);
-         ret = chmod(buffer, credp->fc_mode);
-         g_free(buffer);
+         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)
  {
-     char *path;
      int err = -1;
-     int serrno = 0;
-     V9fsString fullname;
-     char *buffer = NULL;
+     int dirfd;
  
-     v9fs_string_init(&fullname);
-     v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
-     path = fullname.data;
+     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) {
-         buffer = rpath(fs_ctx, path);
-         err = mknod(buffer, SM_LOCAL_MODE_BITS|S_IFREG, 0);
+     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;
          }
-         err = local_set_xattr(buffer, credp);
-         if (err == -1) {
-             serrno = errno;
-             goto err_end;
-         }
-     } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
  
-         buffer = rpath(fs_ctx, path);
-         err = mknod(buffer, 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);
          }
-         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)) {
-         buffer = rpath(fs_ctx, path);
-         err = mknod(buffer, credp->fc_mode, credp->fc_rdev);
+     } 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_post_create_passthrough(fs_ctx, path, credp);
+         err = local_set_cred_passthrough(fs_ctx, dirfd, name, credp);
          if (err == -1) {
-             serrno = errno;
              goto err_end;
          }
      }
      goto out;
  
  err_end:
-     remove(buffer);
-     errno = serrno;
+     unlinkat_preserve_errno(dirfd, name, 0);
  out:
-     g_free(buffer);
-     v9fs_string_free(&fullname);
+     close_preserve_errno(dirfd);
      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 = NULL;
+     int dirfd;
  
-     v9fs_string_init(&fullname);
-     v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
-     path = fullname.data;
+     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) {
-         buffer = rpath(fs_ctx, path);
-         err = mkdir(buffer, SM_LOCAL_DIR_MODE_BITS);
+     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;
-         err = local_set_xattr(buffer, credp);
-         if (err == -1) {
-             serrno = errno;
-             goto err_end;
-         }
-     } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
-         buffer = rpath(fs_ctx, path);
-         err = mkdir(buffer, 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);
          }
-         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)) {
-         buffer = rpath(fs_ctx, path);
-         err = mkdir(buffer, credp->fc_mode);
+     } 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_post_create_passthrough(fs_ctx, path, credp);
+         err = local_set_cred_passthrough(fs_ctx, dirfd, name, credp);
          if (err == -1) {
-             serrno = errno;
              goto err_end;
          }
      }
      goto out;
  
  err_end:
-     remove(buffer);
-     errno = serrno;
+     unlinkat_preserve_errno(dirfd, name, AT_REMOVEDIR);
  out:
-     g_free(buffer);
-     v9fs_string_free(&fullname);
+     close_preserve_errno(dirfd);
      return err;
  }
  
@@@ -649,62 -689,45 +689,45 @@@ static int local_fstat(FsContext *fs_ct
  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 = NULL;
+     int dirfd;
  
      /*
       * Mark all the open to not follow symlinks
       */
      flags |= O_NOFOLLOW;
  
-     v9fs_string_init(&fullname);
-     v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
-     path = fullname.data;
+     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) {
-         buffer = rpath(fs_ctx, path);
-         fd = open(buffer, flags, SM_LOCAL_MODE_BITS);
+     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) {
-             err = fd;
              goto out;
          }
          credp->fc_mode = credp->fc_mode|S_IFREG;
-         /* Set cleint credentials in xattr */
-         err = local_set_xattr(buffer, credp);
-         if (err == -1) {
-             serrno = errno;
-             goto err_end;
-         }
-     } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
-         buffer = rpath(fs_ctx, path);
-         fd = open(buffer, flags, SM_LOCAL_MODE_BITS);
-         if (fd == -1) {
-             err = fd;
-             goto out;
+         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);
          }
-         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)) {
-         buffer = rpath(fs_ctx, path);
-         fd = open(buffer, flags, credp->fc_mode);
+         fd = openat_file(dirfd, name, flags, credp->fc_mode);
          if (fd == -1) {
-             err = fd;
              goto out;
          }
-         err = local_post_create_passthrough(fs_ctx, path, credp);
+         err = local_set_cred_passthrough(fs_ctx, dirfd, name, credp);
          if (err == -1) {
-             serrno = errno;
              goto err_end;
          }
      }
      goto out;
  
  err_end:
-     close(fd);
-     remove(buffer);
-     errno = serrno;
+     unlinkat_preserve_errno(dirfd, name,
+                             flags & O_DIRECTORY ? AT_REMOVEDIR : 0);
+     close_preserve_errno(fd);
  out:
-     g_free(buffer);
-     v9fs_string_free(&fullname);
+     close_preserve_errno(dirfd);
      return err;
  }
  
@@@ -727,23 -749,22 +749,22 @@@ static int local_symlink(FsContext *fs_
                           V9fsPath *dir_path, const char *name, FsCred *credp)
  {
      int err = -1;
-     int serrno = 0;
-     char *newpath;
-     V9fsString fullname;
-     char *buffer = NULL;
+     int dirfd;
  
-     v9fs_string_init(&fullname);
-     v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
-     newpath = fullname.data;
+     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) {
+     if (fs_ctx->export_flags & V9FS_SM_MAPPED ||
+         fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
          int fd;
          ssize_t oldpath_size, write_size;
-         buffer = rpath(fs_ctx, newpath);
-         fd = open(buffer, O_CREAT|O_EXCL|O_RDWR|O_NOFOLLOW, SM_LOCAL_MODE_BITS);
+         fd = openat_file(dirfd, name, O_CREAT | O_EXCL | O_RDWR,
+                          SM_LOCAL_MODE_BITS);
          if (fd == -1) {
-             err = fd;
              goto out;
          }
          /* Write the oldpath (target) to the file. */
          do {
              write_size = write(fd, (void *)oldpath, oldpath_size);
          } while (write_size == -1 && errno == EINTR);
+         close_preserve_errno(fd);
  
          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(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;
-         buffer = rpath(fs_ctx, newpath);
-         fd = open(buffer, O_CREAT|O_EXCL|O_RDWR|O_NOFOLLOW, 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);
+         credp->fc_mode = credp->fc_mode | S_IFLNK;
  
-         if (write_size != oldpath_size) {
-             serrno = errno;
-             close(fd);
-             err = -1;
-             goto err_end;
+         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);
          }
-         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)) {
-         buffer = rpath(fs_ctx, newpath);
-         err = symlink(oldpath, buffer);
+     } 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 = lchown(buffer, credp->fc_uid, credp->fc_gid);
+         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) {
-                 serrno = errno;
                  goto err_end;
-             } else
+             } else {
                  err = 0;
+             }
          }
      }
      goto out;
  
  err_end:
-     remove(buffer);
-     errno = serrno;
+     unlinkat_preserve_errno(dirfd, name, 0);
  out:
-     g_free(buffer);
-     v9fs_string_free(&fullname);
+     close_preserve_errno(dirfd);
      return err;
  }
  
  static int local_link(FsContext *ctx, V9fsPath *oldpath,
                        V9fsPath *dirpath, const char *name)
  {
-     int ret;
-     V9fsString newpath;
-     char *buffer, *buffer1;
+     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;
+     }
  
-     v9fs_string_init(&newpath);
-     v9fs_string_sprintf(&newpath, "%s/%s", dirpath->data, name);
+     ndirfd = local_opendir_nofollow(ctx, dirpath->data);
+     if (ndirfd == -1) {
+         close_preserve_errno(odirfd);
+         goto out;
+     }
  
-     buffer = rpath(ctx, oldpath->data);
-     buffer1 = rpath(ctx, newpath.data);
-     ret = link(buffer, buffer1);
-     g_free(buffer);
-     g_free(buffer1);
+     ret = linkat(odirfd, oname, ndirfd, name, 0);
+     if (ret < 0) {
+         goto out_close;
+     }
  
      /* 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;
+     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;
          }
-         buffer = local_mapped_attr_path(ctx, oldpath->data);
-         buffer1 = local_mapped_attr_path(ctx, newpath.data);
-         ret = link(buffer, buffer1);
-         g_free(buffer);
-         g_free(buffer1);
+         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_out;
+             goto err_undo_link;
          }
-     }
- err_out:
-     v9fs_string_free(&newpath);
-     return ret;
- }
  
- static int local_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size)
- {
-     char *buffer;
-     int ret;
-     char *path = fs_path->data;
+         ret = 0;
+     }
+     goto out_close;
  
-     buffer = rpath(ctx, path);
-     ret = truncate(buffer, size);
-     g_free(buffer);
+ 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_rename(FsContext *ctx, const char *oldpath,
-                         const char *newpath)
+ static int local_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size)
  {
-     int err;
-     char *buffer, *buffer1;
+     int fd, ret;
  
-     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 */
-         buffer = local_mapped_attr_path(ctx, oldpath);
-         buffer1 = local_mapped_attr_path(ctx, newpath);
-         err = rename(buffer, buffer1);
-         g_free(buffer);
-         g_free(buffer1);
-         if (err < 0 && errno != ENOENT) {
-             return err;
-         }
+     fd = local_open_nofollow(ctx, fs_path->data, O_WRONLY, 0);
+     if (fd == -1) {
+         return -1;
      }
-     buffer = rpath(ctx, oldpath);
-     buffer1 = rpath(ctx, newpath);
-     err = rename(buffer, buffer1);
-     g_free(buffer);
-     g_free(buffer1);
-     return err;
+     ret = ftruncate(fd, size);
+     close_preserve_errno(fd);
+     return ret;
  }
  
  static int local_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
  {
-     char *buffer;
+     char *dirpath = g_path_get_dirname(fs_path->data);
+     char *name = g_path_get_basename(fs_path->data);
      int ret = -1;
-     char *path = fs_path->data;
+     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)) {
-         buffer = rpath(fs_ctx, path);
-         ret = lchown(buffer, credp->fc_uid, credp->fc_gid);
-         g_free(buffer);
+         ret = fchownat(dirfd, name, credp->fc_uid, credp->fc_gid,
+                        AT_SYMLINK_NOFOLLOW);
      } else if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
-         buffer = rpath(fs_ctx, path);
-         ret = local_set_xattr(buffer, credp);
-         g_free(buffer);
+         ret = local_set_xattrat(dirfd, name, credp);
      } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
-         return local_set_mapped_file_attr(fs_ctx, path, credp);
+         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 *buffer;
-     int ret;
-     char *path = fs_path->data;
+     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;
+     }
  
-     buffer = rpath(s, path);
-     ret = qemu_utimens(buffer, buf);
-     g_free(buffer);
+     ret = utimensat(dirfd, name, buf, AT_SYMLINK_NOFOLLOW);
+     close_preserve_errno(dirfd);
+ out:
+     g_free(dirpath);
+     g_free(name);
      return ret;
  }
  
- static int local_remove(FsContext *ctx, const char *path)
+ static int local_unlinkat_common(FsContext *ctx, int dirfd, const char *name,
+                                  int flags)
  {
-     int err;
-     struct stat stbuf;
-     char *buffer;
+     int ret = -1;
  
      if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
-         buffer = rpath(ctx, path);
-         err =  lstat(buffer, &stbuf);
-         g_free(buffer);
-         if (err) {
-             goto err_out;
-         }
-         /*
-          * If directory remove .virtfs_metadata contained in the
-          * directory
-          */
-         if (S_ISDIR(stbuf.st_mode)) {
-             buffer = g_strdup_printf("%s/%s/%s", ctx->fs_root,
-                                      path, VIRTFS_META_DIR);
-             err = remove(buffer);
-             g_free(buffer);
-             if (err < 0 && errno != ENOENT) {
+         int map_dirfd;
+         if (flags == AT_REMOVEDIR) {
+             int fd;
+             fd = openat(dirfd, name, O_RDONLY | O_DIRECTORY | O_PATH);
+             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.
          }
          /*
           * Now remove the name from parent directory
-          * .virtfs_metadata directory
+          * .virtfs_metadata directory.
           */
-         buffer = local_mapped_attr_path(ctx, path);
-         err = remove(buffer);
-         g_free(buffer);
-         if (err < 0 && errno != ENOENT) {
+         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.
          }
      }
  
-     buffer = rpath(ctx, path);
-     err = remove(buffer);
-     g_free(buffer);
+     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) {
+         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;
  }
  
@@@ -1013,13 -1049,11 +1049,11 @@@ static int local_fsync(FsContext *ctx, 
  
  static int local_statfs(FsContext *s, V9fsPath *fs_path, struct statfs *stbuf)
  {
-     char *buffer;
-     int ret;
-     char *path = fs_path->data;
+     int fd, ret;
  
-     buffer = rpath(s, path);
-     ret = statfs(buffer, stbuf);
-     g_free(buffer);
+     fd = local_open_nofollow(s, fs_path->data, O_RDONLY, 0);
+     ret = fstatfs(fd, stbuf);
+     close_preserve_errno(fd);
      return ret;
  }
  
@@@ -1071,70 -1105,105 +1105,105 @@@ static int local_renameat(FsContext *ct
                            const char *new_name)
  {
      int ret;
-     V9fsString old_full_name, new_full_name;
+     int odirfd, ndirfd;
  
-     v9fs_string_init(&old_full_name);
-     v9fs_string_init(&new_full_name);
+     odirfd = local_opendir_nofollow(ctx, olddir->data);
+     if (odirfd == -1) {
+         return -1;
+     }
  
-     v9fs_string_sprintf(&old_full_name, "%s/%s", olddir->data, old_name);
-     v9fs_string_sprintf(&new_full_name, "%s/%s", newdir->data, new_name);
+     ndirfd = local_opendir_nofollow(ctx, newdir->data);
+     if (ndirfd == -1) {
+         close_preserve_errno(odirfd);
+         return -1;
+     }
  
-     ret = local_rename(ctx, old_full_name.data, new_full_name.data);
-     v9fs_string_free(&old_full_name);
-     v9fs_string_free(&new_full_name);
+     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;
-     V9fsString fullname;
-     char *buffer;
-     v9fs_string_init(&fullname);
+     int dirfd;
  
-     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
-              */
-             buffer = g_strdup_printf("%s/%s/%s", ctx->fs_root,
-                                      fullname.data, VIRTFS_META_DIR);
-             ret = remove(buffer);
-             g_free(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.
-          */
-         buffer = local_mapped_attr_path(ctx, fullname.data);
-         ret = remove(buffer);
-         g_free(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;
-         }
+     dirfd = local_opendir_nofollow(ctx, dir->data);
+     if (dirfd == -1) {
+         return -1;
      }
-     /* Remove the name finally */
-     buffer = rpath(ctx, fullname.data);
-     ret = remove(buffer);
-     g_free(buffer);
  
- err_out:
-     v9fs_string_free(&fullname);
+     ret = local_unlinkat_common(ctx, dirfd, name, flags);
+     close_preserve_errno(dirfd);
      return ret;
  }
  
@@@ -1168,8 -1237,31 +1237,31 @@@ static int local_ioc_getversion(FsConte
  
  static int local_init(FsContext *ctx)
  {
-     int err = 0;
      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;
          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;
+     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");
 +    Error *err = NULL;
  
      if (!sec_model) {
          error_report("Security model not specified, local fs needs security model");
          error_report("fsdev: No path specified");
          return -1;
      }
 +
 +    fsdev_throttle_parse_opts(opts, &fse->fst, &err);
 +    if (err) {
 +        error_reportf_err(err, "Throttle configuration is not valid: ");
 +        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,