qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

9p-local.c (44067B)


      1 /*
      2  * 9p Posix callback
      3  *
      4  * Copyright IBM, Corp. 2010
      5  *
      6  * Authors:
      7  *  Anthony Liguori   <aliguori@us.ibm.com>
      8  *
      9  * This work is licensed under the terms of the GNU GPL, version 2.  See
     10  * the COPYING file in the top-level directory.
     11  */
     12 
     13 /*
     14  * Not so fast! You might want to read the 9p developer docs first:
     15  * https://wiki.qemu.org/Documentation/9p
     16  */
     17 
     18 #include "qemu/osdep.h"
     19 #include "9p.h"
     20 #include "9p-local.h"
     21 #include "9p-xattr.h"
     22 #include "9p-util.h"
     23 #include "fsdev/qemu-fsdev.h"   /* local_ops */
     24 #include <arpa/inet.h>
     25 #include <pwd.h>
     26 #include <grp.h>
     27 #include <sys/socket.h>
     28 #include <sys/un.h>
     29 #include "qemu/xattr.h"
     30 #include "qapi/error.h"
     31 #include "qemu/cutils.h"
     32 #include "qemu/error-report.h"
     33 #include "qemu/option.h"
     34 #include <libgen.h>
     35 #ifdef CONFIG_LINUX
     36 #include <linux/fs.h>
     37 #ifdef CONFIG_LINUX_MAGIC_H
     38 #include <linux/magic.h>
     39 #endif
     40 #endif
     41 #include <sys/ioctl.h>
     42 
     43 #ifndef XFS_SUPER_MAGIC
     44 #define XFS_SUPER_MAGIC  0x58465342
     45 #endif
     46 #ifndef EXT2_SUPER_MAGIC
     47 #define EXT2_SUPER_MAGIC 0xEF53
     48 #endif
     49 #ifndef REISERFS_SUPER_MAGIC
     50 #define REISERFS_SUPER_MAGIC 0x52654973
     51 #endif
     52 #ifndef BTRFS_SUPER_MAGIC
     53 #define BTRFS_SUPER_MAGIC 0x9123683E
     54 #endif
     55 
     56 typedef struct {
     57     int mountfd;
     58 } LocalData;
     59 
     60 int local_open_nofollow(FsContext *fs_ctx, const char *path, int flags,
     61                         mode_t mode)
     62 {
     63     LocalData *data = fs_ctx->private;
     64     int fd = data->mountfd;
     65 
     66     while (*path && fd != -1) {
     67         const char *c;
     68         int next_fd;
     69         char *head;
     70 
     71         /* Only relative paths without consecutive slashes */
     72         assert(*path != '/');
     73 
     74         head = g_strdup(path);
     75         c = qemu_strchrnul(path, '/');
     76         if (*c) {
     77             /* Intermediate path element */
     78             head[c - path] = 0;
     79             path = c + 1;
     80             next_fd = openat_dir(fd, head);
     81         } else {
     82             /* Rightmost path element */
     83             next_fd = openat_file(fd, head, flags, mode);
     84             path = c;
     85         }
     86         g_free(head);
     87         if (fd != data->mountfd) {
     88             close_preserve_errno(fd);
     89         }
     90         fd = next_fd;
     91     }
     92 
     93     assert(fd != data->mountfd);
     94     return fd;
     95 }
     96 
     97 int local_opendir_nofollow(FsContext *fs_ctx, const char *path)
     98 {
     99     return local_open_nofollow(fs_ctx, path, O_DIRECTORY | O_RDONLY, 0);
    100 }
    101 
    102 static void renameat_preserve_errno(int odirfd, const char *opath, int ndirfd,
    103                                     const char *npath)
    104 {
    105     int serrno = errno;
    106     renameat(odirfd, opath, ndirfd, npath);
    107     errno = serrno;
    108 }
    109 
    110 static void unlinkat_preserve_errno(int dirfd, const char *path, int flags)
    111 {
    112     int serrno = errno;
    113     unlinkat(dirfd, path, flags);
    114     errno = serrno;
    115 }
    116 
    117 #define VIRTFS_META_DIR ".virtfs_metadata"
    118 #define VIRTFS_META_ROOT_FILE VIRTFS_META_DIR "_root"
    119 
    120 static FILE *local_fopenat(int dirfd, const char *name, const char *mode)
    121 {
    122     int fd, o_mode = 0;
    123     FILE *fp;
    124     int flags;
    125     /*
    126      * only supports two modes
    127      */
    128     if (mode[0] == 'r') {
    129         flags = O_RDONLY;
    130     } else if (mode[0] == 'w') {
    131         flags = O_WRONLY | O_TRUNC | O_CREAT;
    132         o_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
    133     } else {
    134         return NULL;
    135     }
    136     fd = openat_file(dirfd, name, flags, o_mode);
    137     if (fd == -1) {
    138         return NULL;
    139     }
    140     fp = fdopen(fd, mode);
    141     if (!fp) {
    142         close(fd);
    143     }
    144     return fp;
    145 }
    146 
    147 #define ATTR_MAX 100
    148 static void local_mapped_file_attr(int dirfd, const char *name,
    149                                    struct stat *stbuf)
    150 {
    151     FILE *fp;
    152     char buf[ATTR_MAX];
    153     int map_dirfd;
    154 
    155     if (strcmp(name, ".")) {
    156         map_dirfd = openat_dir(dirfd, VIRTFS_META_DIR);
    157         if (map_dirfd == -1) {
    158             return;
    159         }
    160 
    161         fp = local_fopenat(map_dirfd, name, "r");
    162         close_preserve_errno(map_dirfd);
    163     } else {
    164         fp = local_fopenat(dirfd, VIRTFS_META_ROOT_FILE, "r");
    165     }
    166     if (!fp) {
    167         return;
    168     }
    169     memset(buf, 0, ATTR_MAX);
    170     while (fgets(buf, ATTR_MAX, fp)) {
    171         if (!strncmp(buf, "virtfs.uid", 10)) {
    172             stbuf->st_uid = atoi(buf + 11);
    173         } else if (!strncmp(buf, "virtfs.gid", 10)) {
    174             stbuf->st_gid = atoi(buf + 11);
    175         } else if (!strncmp(buf, "virtfs.mode", 11)) {
    176             stbuf->st_mode = atoi(buf + 12);
    177         } else if (!strncmp(buf, "virtfs.rdev", 11)) {
    178             stbuf->st_rdev = atoi(buf + 12);
    179         }
    180         memset(buf, 0, ATTR_MAX);
    181     }
    182     fclose(fp);
    183 }
    184 
    185 static int local_lstat(FsContext *fs_ctx, V9fsPath *fs_path, struct stat *stbuf)
    186 {
    187     int err = -1;
    188     char *dirpath = g_path_get_dirname(fs_path->data);
    189     char *name = g_path_get_basename(fs_path->data);
    190     int dirfd;
    191 
    192     dirfd = local_opendir_nofollow(fs_ctx, dirpath);
    193     if (dirfd == -1) {
    194         goto out;
    195     }
    196 
    197     err = fstatat(dirfd, name, stbuf, AT_SYMLINK_NOFOLLOW);
    198     if (err) {
    199         goto err_out;
    200     }
    201     if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
    202         /* Actual credentials are part of extended attrs */
    203         uid_t tmp_uid;
    204         gid_t tmp_gid;
    205         mode_t tmp_mode;
    206         dev_t tmp_dev;
    207 
    208         if (fgetxattrat_nofollow(dirfd, name, "user.virtfs.uid", &tmp_uid,
    209                                  sizeof(uid_t)) > 0) {
    210             stbuf->st_uid = le32_to_cpu(tmp_uid);
    211         }
    212         if (fgetxattrat_nofollow(dirfd, name, "user.virtfs.gid", &tmp_gid,
    213                                  sizeof(gid_t)) > 0) {
    214             stbuf->st_gid = le32_to_cpu(tmp_gid);
    215         }
    216         if (fgetxattrat_nofollow(dirfd, name, "user.virtfs.mode", &tmp_mode,
    217                                  sizeof(mode_t)) > 0) {
    218             stbuf->st_mode = le32_to_cpu(tmp_mode);
    219         }
    220         if (fgetxattrat_nofollow(dirfd, name, "user.virtfs.rdev", &tmp_dev,
    221                                  sizeof(dev_t)) > 0) {
    222             stbuf->st_rdev = le64_to_cpu(tmp_dev);
    223         }
    224     } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
    225         local_mapped_file_attr(dirfd, name, stbuf);
    226     }
    227 
    228 err_out:
    229     close_preserve_errno(dirfd);
    230 out:
    231     g_free(name);
    232     g_free(dirpath);
    233     return err;
    234 }
    235 
    236 static int local_set_mapped_file_attrat(int dirfd, const char *name,
    237                                         FsCred *credp)
    238 {
    239     FILE *fp;
    240     int ret;
    241     char buf[ATTR_MAX];
    242     int uid = -1, gid = -1, mode = -1, rdev = -1;
    243     int map_dirfd = -1, map_fd;
    244     bool is_root = !strcmp(name, ".");
    245 
    246     if (is_root) {
    247         fp = local_fopenat(dirfd, VIRTFS_META_ROOT_FILE, "r");
    248         if (!fp) {
    249             if (errno == ENOENT) {
    250                 goto update_map_file;
    251             } else {
    252                 return -1;
    253             }
    254         }
    255     } else {
    256         ret = mkdirat(dirfd, VIRTFS_META_DIR, 0700);
    257         if (ret < 0 && errno != EEXIST) {
    258             return -1;
    259         }
    260 
    261         map_dirfd = openat_dir(dirfd, VIRTFS_META_DIR);
    262         if (map_dirfd == -1) {
    263             return -1;
    264         }
    265 
    266         fp = local_fopenat(map_dirfd, name, "r");
    267         if (!fp) {
    268             if (errno == ENOENT) {
    269                 goto update_map_file;
    270             } else {
    271                 close_preserve_errno(map_dirfd);
    272                 return -1;
    273             }
    274         }
    275     }
    276     memset(buf, 0, ATTR_MAX);
    277     while (fgets(buf, ATTR_MAX, fp)) {
    278         if (!strncmp(buf, "virtfs.uid", 10)) {
    279             uid = atoi(buf + 11);
    280         } else if (!strncmp(buf, "virtfs.gid", 10)) {
    281             gid = atoi(buf + 11);
    282         } else if (!strncmp(buf, "virtfs.mode", 11)) {
    283             mode = atoi(buf + 12);
    284         } else if (!strncmp(buf, "virtfs.rdev", 11)) {
    285             rdev = atoi(buf + 12);
    286         }
    287         memset(buf, 0, ATTR_MAX);
    288     }
    289     fclose(fp);
    290 
    291 update_map_file:
    292     if (is_root) {
    293         fp = local_fopenat(dirfd, VIRTFS_META_ROOT_FILE, "w");
    294     } else {
    295         fp = local_fopenat(map_dirfd, name, "w");
    296         /* We can't go this far with map_dirfd not being a valid file descriptor
    297          * but some versions of gcc aren't smart enough to see it.
    298          */
    299         if (map_dirfd != -1) {
    300             close_preserve_errno(map_dirfd);
    301         }
    302     }
    303     if (!fp) {
    304         return -1;
    305     }
    306 
    307     map_fd = fileno(fp);
    308     assert(map_fd != -1);
    309     ret = fchmod(map_fd, 0600);
    310     assert(ret == 0);
    311 
    312     if (credp->fc_uid != -1) {
    313         uid = credp->fc_uid;
    314     }
    315     if (credp->fc_gid != -1) {
    316         gid = credp->fc_gid;
    317     }
    318     if (credp->fc_mode != (mode_t)-1) {
    319         mode = credp->fc_mode;
    320     }
    321     if (credp->fc_rdev != -1) {
    322         rdev = credp->fc_rdev;
    323     }
    324 
    325     if (uid != -1) {
    326         fprintf(fp, "virtfs.uid=%d\n", uid);
    327     }
    328     if (gid != -1) {
    329         fprintf(fp, "virtfs.gid=%d\n", gid);
    330     }
    331     if (mode != -1) {
    332         fprintf(fp, "virtfs.mode=%d\n", mode);
    333     }
    334     if (rdev != -1) {
    335         fprintf(fp, "virtfs.rdev=%d\n", rdev);
    336     }
    337     fclose(fp);
    338 
    339     return 0;
    340 }
    341 
    342 static int fchmodat_nofollow(int dirfd, const char *name, mode_t mode)
    343 {
    344     struct stat stbuf;
    345     int fd, ret;
    346 
    347     /* FIXME: this should be handled with fchmodat(AT_SYMLINK_NOFOLLOW).
    348      * Unfortunately, the linux kernel doesn't implement it yet.
    349      */
    350 
    351      /* First, we clear non-racing symlinks out of the way. */
    352     if (fstatat(dirfd, name, &stbuf, AT_SYMLINK_NOFOLLOW)) {
    353         return -1;
    354     }
    355     if (S_ISLNK(stbuf.st_mode)) {
    356         errno = ELOOP;
    357         return -1;
    358     }
    359 
    360     fd = openat_file(dirfd, name, O_RDONLY | O_PATH_9P_UTIL | O_NOFOLLOW, 0);
    361 #if O_PATH_9P_UTIL == 0
    362     /* Fallback for systems that don't support O_PATH: we depend on the file
    363      * being readable or writable.
    364      */
    365     if (fd == -1) {
    366         /* In case the file is writable-only and isn't a directory. */
    367         if (errno == EACCES) {
    368             fd = openat_file(dirfd, name, O_WRONLY, 0);
    369         }
    370         if (fd == -1 && errno == EISDIR) {
    371             errno = EACCES;
    372         }
    373     }
    374     if (fd == -1) {
    375         return -1;
    376     }
    377     ret = fchmod(fd, mode);
    378 #else
    379     /* Access modes are ignored when O_PATH is supported. If name is a symbolic
    380      * link, O_PATH | O_NOFOLLOW causes openat(2) to return a file descriptor
    381      * referring to the symbolic link.
    382      */
    383     if (fd == -1) {
    384         return -1;
    385     }
    386 
    387     /* Now we handle racing symlinks. */
    388     ret = fstat(fd, &stbuf);
    389     if (!ret) {
    390         if (S_ISLNK(stbuf.st_mode)) {
    391             errno = ELOOP;
    392             ret = -1;
    393         } else {
    394             char *proc_path = g_strdup_printf("/proc/self/fd/%d", fd);
    395             ret = chmod(proc_path, mode);
    396             g_free(proc_path);
    397         }
    398     }
    399 #endif
    400     close_preserve_errno(fd);
    401     return ret;
    402 }
    403 
    404 static int local_set_xattrat(int dirfd, const char *path, FsCred *credp)
    405 {
    406     int err;
    407 
    408     if (credp->fc_uid != -1) {
    409         uint32_t tmp_uid = cpu_to_le32(credp->fc_uid);
    410         err = fsetxattrat_nofollow(dirfd, path, "user.virtfs.uid", &tmp_uid,
    411                                    sizeof(uid_t), 0);
    412         if (err) {
    413             return err;
    414         }
    415     }
    416     if (credp->fc_gid != -1) {
    417         uint32_t tmp_gid = cpu_to_le32(credp->fc_gid);
    418         err = fsetxattrat_nofollow(dirfd, path, "user.virtfs.gid", &tmp_gid,
    419                                    sizeof(gid_t), 0);
    420         if (err) {
    421             return err;
    422         }
    423     }
    424     if (credp->fc_mode != (mode_t)-1) {
    425         uint32_t tmp_mode = cpu_to_le32(credp->fc_mode);
    426         err = fsetxattrat_nofollow(dirfd, path, "user.virtfs.mode", &tmp_mode,
    427                                    sizeof(mode_t), 0);
    428         if (err) {
    429             return err;
    430         }
    431     }
    432     if (credp->fc_rdev != -1) {
    433         uint64_t tmp_rdev = cpu_to_le64(credp->fc_rdev);
    434         err = fsetxattrat_nofollow(dirfd, path, "user.virtfs.rdev", &tmp_rdev,
    435                                    sizeof(dev_t), 0);
    436         if (err) {
    437             return err;
    438         }
    439     }
    440     return 0;
    441 }
    442 
    443 static int local_set_cred_passthrough(FsContext *fs_ctx, int dirfd,
    444                                       const char *name, FsCred *credp)
    445 {
    446     if (fchownat(dirfd, name, credp->fc_uid, credp->fc_gid,
    447                  AT_SYMLINK_NOFOLLOW) < 0) {
    448         /*
    449          * If we fail to change ownership and if we are
    450          * using security model none. Ignore the error
    451          */
    452         if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) {
    453             return -1;
    454         }
    455     }
    456 
    457     return fchmodat_nofollow(dirfd, name, credp->fc_mode & 07777);
    458 }
    459 
    460 static ssize_t local_readlink(FsContext *fs_ctx, V9fsPath *fs_path,
    461                               char *buf, size_t bufsz)
    462 {
    463     ssize_t tsize = -1;
    464 
    465     if ((fs_ctx->export_flags & V9FS_SM_MAPPED) ||
    466         (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE)) {
    467         int fd;
    468 
    469         fd = local_open_nofollow(fs_ctx, fs_path->data, O_RDONLY, 0);
    470         if (fd == -1) {
    471             return -1;
    472         }
    473         do {
    474             tsize = read(fd, (void *)buf, bufsz);
    475         } while (tsize == -1 && errno == EINTR);
    476         close_preserve_errno(fd);
    477     } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
    478                (fs_ctx->export_flags & V9FS_SM_NONE)) {
    479         char *dirpath = g_path_get_dirname(fs_path->data);
    480         char *name = g_path_get_basename(fs_path->data);
    481         int dirfd;
    482 
    483         dirfd = local_opendir_nofollow(fs_ctx, dirpath);
    484         if (dirfd == -1) {
    485             goto out;
    486         }
    487 
    488         tsize = readlinkat(dirfd, name, buf, bufsz);
    489         close_preserve_errno(dirfd);
    490     out:
    491         g_free(name);
    492         g_free(dirpath);
    493     }
    494     return tsize;
    495 }
    496 
    497 static int local_close(FsContext *ctx, V9fsFidOpenState *fs)
    498 {
    499     return close(fs->fd);
    500 }
    501 
    502 static int local_closedir(FsContext *ctx, V9fsFidOpenState *fs)
    503 {
    504     return closedir(fs->dir.stream);
    505 }
    506 
    507 static int local_open(FsContext *ctx, V9fsPath *fs_path,
    508                       int flags, V9fsFidOpenState *fs)
    509 {
    510     int fd;
    511 
    512     fd = local_open_nofollow(ctx, fs_path->data, flags, 0);
    513     if (fd == -1) {
    514         return -1;
    515     }
    516     fs->fd = fd;
    517     return fs->fd;
    518 }
    519 
    520 static int local_opendir(FsContext *ctx,
    521                          V9fsPath *fs_path, V9fsFidOpenState *fs)
    522 {
    523     int dirfd;
    524     DIR *stream;
    525 
    526     dirfd = local_opendir_nofollow(ctx, fs_path->data);
    527     if (dirfd == -1) {
    528         return -1;
    529     }
    530 
    531     stream = fdopendir(dirfd);
    532     if (!stream) {
    533         close(dirfd);
    534         return -1;
    535     }
    536     fs->dir.stream = stream;
    537     return 0;
    538 }
    539 
    540 static void local_rewinddir(FsContext *ctx, V9fsFidOpenState *fs)
    541 {
    542     rewinddir(fs->dir.stream);
    543 }
    544 
    545 static off_t local_telldir(FsContext *ctx, V9fsFidOpenState *fs)
    546 {
    547     return telldir(fs->dir.stream);
    548 }
    549 
    550 static bool local_is_mapped_file_metadata(FsContext *fs_ctx, const char *name)
    551 {
    552     return
    553         !strcmp(name, VIRTFS_META_DIR) || !strcmp(name, VIRTFS_META_ROOT_FILE);
    554 }
    555 
    556 static struct dirent *local_readdir(FsContext *ctx, V9fsFidOpenState *fs)
    557 {
    558     struct dirent *entry;
    559 
    560 again:
    561     entry = readdir(fs->dir.stream);
    562     if (!entry) {
    563         return NULL;
    564     }
    565 #ifdef CONFIG_DARWIN
    566     int off;
    567     off = telldir(fs->dir.stream);
    568     /* If telldir fails, fail the entire readdir call */
    569     if (off < 0) {
    570         return NULL;
    571     }
    572     entry->d_seekoff = off;
    573 #endif
    574 
    575     if (ctx->export_flags & V9FS_SM_MAPPED) {
    576         entry->d_type = DT_UNKNOWN;
    577     } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
    578         if (local_is_mapped_file_metadata(ctx, entry->d_name)) {
    579             /* skip the meta data */
    580             goto again;
    581         }
    582         entry->d_type = DT_UNKNOWN;
    583     }
    584 
    585     return entry;
    586 }
    587 
    588 static void local_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off)
    589 {
    590     seekdir(fs->dir.stream, off);
    591 }
    592 
    593 static ssize_t local_preadv(FsContext *ctx, V9fsFidOpenState *fs,
    594                             const struct iovec *iov,
    595                             int iovcnt, off_t offset)
    596 {
    597 #ifdef CONFIG_PREADV
    598     return preadv(fs->fd, iov, iovcnt, offset);
    599 #else
    600     int err = lseek(fs->fd, offset, SEEK_SET);
    601     if (err == -1) {
    602         return err;
    603     } else {
    604         return readv(fs->fd, iov, iovcnt);
    605     }
    606 #endif
    607 }
    608 
    609 static ssize_t local_pwritev(FsContext *ctx, V9fsFidOpenState *fs,
    610                              const struct iovec *iov,
    611                              int iovcnt, off_t offset)
    612 {
    613     ssize_t ret;
    614 #ifdef CONFIG_PREADV
    615     ret = pwritev(fs->fd, iov, iovcnt, offset);
    616 #else
    617     int err = lseek(fs->fd, offset, SEEK_SET);
    618     if (err == -1) {
    619         return err;
    620     } else {
    621         ret = writev(fs->fd, iov, iovcnt);
    622     }
    623 #endif
    624 #ifdef CONFIG_SYNC_FILE_RANGE
    625     if (ret > 0 && ctx->export_flags & V9FS_IMMEDIATE_WRITEOUT) {
    626         /*
    627          * Initiate a writeback. This is not a data integrity sync.
    628          * We want to ensure that we don't leave dirty pages in the cache
    629          * after write when writeout=immediate is sepcified.
    630          */
    631         sync_file_range(fs->fd, offset, ret,
    632                         SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE);
    633     }
    634 #endif
    635     return ret;
    636 }
    637 
    638 static int local_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
    639 {
    640     char *dirpath = g_path_get_dirname(fs_path->data);
    641     char *name = g_path_get_basename(fs_path->data);
    642     int ret = -1;
    643     int dirfd;
    644 
    645     dirfd = local_opendir_nofollow(fs_ctx, dirpath);
    646     if (dirfd == -1) {
    647         goto out;
    648     }
    649 
    650     if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
    651         ret = local_set_xattrat(dirfd, name, credp);
    652     } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
    653         ret = local_set_mapped_file_attrat(dirfd, name, credp);
    654     } else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH ||
    655                fs_ctx->export_flags & V9FS_SM_NONE) {
    656         ret = fchmodat_nofollow(dirfd, name, credp->fc_mode);
    657     }
    658     close_preserve_errno(dirfd);
    659 
    660 out:
    661     g_free(dirpath);
    662     g_free(name);
    663     return ret;
    664 }
    665 
    666 static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path,
    667                        const char *name, FsCred *credp)
    668 {
    669     int err = -1;
    670     int dirfd;
    671 
    672     if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE &&
    673         local_is_mapped_file_metadata(fs_ctx, name)) {
    674         errno = EINVAL;
    675         return -1;
    676     }
    677 
    678     dirfd = local_opendir_nofollow(fs_ctx, dir_path->data);
    679     if (dirfd == -1) {
    680         return -1;
    681     }
    682 
    683     if (fs_ctx->export_flags & V9FS_SM_MAPPED ||
    684         fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
    685         err = qemu_mknodat(dirfd, name, fs_ctx->fmode | S_IFREG, 0);
    686         if (err == -1) {
    687             goto out;
    688         }
    689 
    690         if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
    691             err = local_set_xattrat(dirfd, name, credp);
    692         } else {
    693             err = local_set_mapped_file_attrat(dirfd, name, credp);
    694         }
    695         if (err == -1) {
    696             goto err_end;
    697         }
    698     } else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH ||
    699                fs_ctx->export_flags & V9FS_SM_NONE) {
    700         err = qemu_mknodat(dirfd, name, credp->fc_mode, credp->fc_rdev);
    701         if (err == -1) {
    702             goto out;
    703         }
    704         err = local_set_cred_passthrough(fs_ctx, dirfd, name, credp);
    705         if (err == -1) {
    706             goto err_end;
    707         }
    708     }
    709     goto out;
    710 
    711 err_end:
    712     unlinkat_preserve_errno(dirfd, name, 0);
    713 out:
    714     close_preserve_errno(dirfd);
    715     return err;
    716 }
    717 
    718 static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path,
    719                        const char *name, FsCred *credp)
    720 {
    721     int err = -1;
    722     int dirfd;
    723 
    724     if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE &&
    725         local_is_mapped_file_metadata(fs_ctx, name)) {
    726         errno = EINVAL;
    727         return -1;
    728     }
    729 
    730     dirfd = local_opendir_nofollow(fs_ctx, dir_path->data);
    731     if (dirfd == -1) {
    732         return -1;
    733     }
    734 
    735     if (fs_ctx->export_flags & V9FS_SM_MAPPED ||
    736         fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
    737         err = mkdirat(dirfd, name, fs_ctx->dmode);
    738         if (err == -1) {
    739             goto out;
    740         }
    741         credp->fc_mode = credp->fc_mode | S_IFDIR;
    742 
    743         if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
    744             err = local_set_xattrat(dirfd, name, credp);
    745         } else {
    746             err = local_set_mapped_file_attrat(dirfd, name, credp);
    747         }
    748         if (err == -1) {
    749             goto err_end;
    750         }
    751     } else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH ||
    752                fs_ctx->export_flags & V9FS_SM_NONE) {
    753         err = mkdirat(dirfd, name, credp->fc_mode);
    754         if (err == -1) {
    755             goto out;
    756         }
    757         err = local_set_cred_passthrough(fs_ctx, dirfd, name, credp);
    758         if (err == -1) {
    759             goto err_end;
    760         }
    761     }
    762     goto out;
    763 
    764 err_end:
    765     unlinkat_preserve_errno(dirfd, name, AT_REMOVEDIR);
    766 out:
    767     close_preserve_errno(dirfd);
    768     return err;
    769 }
    770 
    771 static int local_fstat(FsContext *fs_ctx, int fid_type,
    772                        V9fsFidOpenState *fs, struct stat *stbuf)
    773 {
    774     int err, fd;
    775 
    776     if (fid_type == P9_FID_DIR) {
    777         fd = dirfd(fs->dir.stream);
    778     } else {
    779         fd = fs->fd;
    780     }
    781 
    782     err = fstat(fd, stbuf);
    783     if (err) {
    784         return err;
    785     }
    786     if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
    787         /* Actual credentials are part of extended attrs */
    788         uid_t tmp_uid;
    789         gid_t tmp_gid;
    790         mode_t tmp_mode;
    791         dev_t tmp_dev;
    792 
    793         if (qemu_fgetxattr(fd, "user.virtfs.uid",
    794                            &tmp_uid, sizeof(uid_t)) > 0) {
    795             stbuf->st_uid = le32_to_cpu(tmp_uid);
    796         }
    797         if (qemu_fgetxattr(fd, "user.virtfs.gid",
    798                            &tmp_gid, sizeof(gid_t)) > 0) {
    799             stbuf->st_gid = le32_to_cpu(tmp_gid);
    800         }
    801         if (qemu_fgetxattr(fd, "user.virtfs.mode",
    802                            &tmp_mode, sizeof(mode_t)) > 0) {
    803             stbuf->st_mode = le32_to_cpu(tmp_mode);
    804         }
    805         if (qemu_fgetxattr(fd, "user.virtfs.rdev",
    806                            &tmp_dev, sizeof(dev_t)) > 0) {
    807             stbuf->st_rdev = le64_to_cpu(tmp_dev);
    808         }
    809     } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
    810         errno = EOPNOTSUPP;
    811         return -1;
    812     }
    813     return err;
    814 }
    815 
    816 static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
    817                        int flags, FsCred *credp, V9fsFidOpenState *fs)
    818 {
    819     int fd = -1;
    820     int err = -1;
    821     int dirfd;
    822 
    823     if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE &&
    824         local_is_mapped_file_metadata(fs_ctx, name)) {
    825         errno = EINVAL;
    826         return -1;
    827     }
    828 
    829     /*
    830      * Mark all the open to not follow symlinks
    831      */
    832     flags |= O_NOFOLLOW;
    833 
    834     dirfd = local_opendir_nofollow(fs_ctx, dir_path->data);
    835     if (dirfd == -1) {
    836         return -1;
    837     }
    838 
    839     /* Determine the security model */
    840     if (fs_ctx->export_flags & V9FS_SM_MAPPED ||
    841         fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
    842         fd = openat_file(dirfd, name, flags, fs_ctx->fmode);
    843         if (fd == -1) {
    844             goto out;
    845         }
    846         credp->fc_mode = credp->fc_mode | S_IFREG;
    847         if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
    848             /* Set cleint credentials in xattr */
    849             err = local_set_xattrat(dirfd, name, credp);
    850         } else {
    851             err = local_set_mapped_file_attrat(dirfd, name, credp);
    852         }
    853         if (err == -1) {
    854             goto err_end;
    855         }
    856     } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
    857                (fs_ctx->export_flags & V9FS_SM_NONE)) {
    858         fd = openat_file(dirfd, name, flags, credp->fc_mode);
    859         if (fd == -1) {
    860             goto out;
    861         }
    862         err = local_set_cred_passthrough(fs_ctx, dirfd, name, credp);
    863         if (err == -1) {
    864             goto err_end;
    865         }
    866     }
    867     err = fd;
    868     fs->fd = fd;
    869     goto out;
    870 
    871 err_end:
    872     unlinkat_preserve_errno(dirfd, name,
    873                             flags & O_DIRECTORY ? AT_REMOVEDIR : 0);
    874     close_preserve_errno(fd);
    875 out:
    876     close_preserve_errno(dirfd);
    877     return err;
    878 }
    879 
    880 
    881 static int local_symlink(FsContext *fs_ctx, const char *oldpath,
    882                          V9fsPath *dir_path, const char *name, FsCred *credp)
    883 {
    884     int err = -1;
    885     int dirfd;
    886 
    887     if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE &&
    888         local_is_mapped_file_metadata(fs_ctx, name)) {
    889         errno = EINVAL;
    890         return -1;
    891     }
    892 
    893     dirfd = local_opendir_nofollow(fs_ctx, dir_path->data);
    894     if (dirfd == -1) {
    895         return -1;
    896     }
    897 
    898     /* Determine the security model */
    899     if (fs_ctx->export_flags & V9FS_SM_MAPPED ||
    900         fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
    901         int fd;
    902         ssize_t oldpath_size, write_size;
    903 
    904         fd = openat_file(dirfd, name, O_CREAT | O_EXCL | O_RDWR,
    905                          fs_ctx->fmode);
    906         if (fd == -1) {
    907             goto out;
    908         }
    909         /* Write the oldpath (target) to the file. */
    910         oldpath_size = strlen(oldpath);
    911         do {
    912             write_size = write(fd, (void *)oldpath, oldpath_size);
    913         } while (write_size == -1 && errno == EINTR);
    914         close_preserve_errno(fd);
    915 
    916         if (write_size != oldpath_size) {
    917             goto err_end;
    918         }
    919         /* Set cleint credentials in symlink's xattr */
    920         credp->fc_mode = credp->fc_mode | S_IFLNK;
    921 
    922         if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
    923             err = local_set_xattrat(dirfd, name, credp);
    924         } else {
    925             err = local_set_mapped_file_attrat(dirfd, name, credp);
    926         }
    927         if (err == -1) {
    928             goto err_end;
    929         }
    930     } else if (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH ||
    931                fs_ctx->export_flags & V9FS_SM_NONE) {
    932         err = symlinkat(oldpath, dirfd, name);
    933         if (err) {
    934             goto out;
    935         }
    936         err = fchownat(dirfd, name, credp->fc_uid, credp->fc_gid,
    937                        AT_SYMLINK_NOFOLLOW);
    938         if (err == -1) {
    939             /*
    940              * If we fail to change ownership and if we are
    941              * using security model none. Ignore the error
    942              */
    943             if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) {
    944                 goto err_end;
    945             } else {
    946                 err = 0;
    947             }
    948         }
    949     }
    950     goto out;
    951 
    952 err_end:
    953     unlinkat_preserve_errno(dirfd, name, 0);
    954 out:
    955     close_preserve_errno(dirfd);
    956     return err;
    957 }
    958 
    959 static int local_link(FsContext *ctx, V9fsPath *oldpath,
    960                       V9fsPath *dirpath, const char *name)
    961 {
    962     char *odirpath = g_path_get_dirname(oldpath->data);
    963     char *oname = g_path_get_basename(oldpath->data);
    964     int ret = -1;
    965     int odirfd, ndirfd;
    966 
    967     if (ctx->export_flags & V9FS_SM_MAPPED_FILE &&
    968         local_is_mapped_file_metadata(ctx, name)) {
    969         errno = EINVAL;
    970         goto out;
    971     }
    972 
    973     odirfd = local_opendir_nofollow(ctx, odirpath);
    974     if (odirfd == -1) {
    975         goto out;
    976     }
    977 
    978     ndirfd = local_opendir_nofollow(ctx, dirpath->data);
    979     if (ndirfd == -1) {
    980         close_preserve_errno(odirfd);
    981         goto out;
    982     }
    983 
    984     ret = linkat(odirfd, oname, ndirfd, name, 0);
    985     if (ret < 0) {
    986         goto out_close;
    987     }
    988 
    989     /* now link the virtfs_metadata files */
    990     if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
    991         int omap_dirfd, nmap_dirfd;
    992 
    993         ret = mkdirat(ndirfd, VIRTFS_META_DIR, 0700);
    994         if (ret < 0 && errno != EEXIST) {
    995             goto err_undo_link;
    996         }
    997 
    998         omap_dirfd = openat_dir(odirfd, VIRTFS_META_DIR);
    999         if (omap_dirfd == -1) {
   1000             goto err;
   1001         }
   1002 
   1003         nmap_dirfd = openat_dir(ndirfd, VIRTFS_META_DIR);
   1004         if (nmap_dirfd == -1) {
   1005             close_preserve_errno(omap_dirfd);
   1006             goto err;
   1007         }
   1008 
   1009         ret = linkat(omap_dirfd, oname, nmap_dirfd, name, 0);
   1010         close_preserve_errno(nmap_dirfd);
   1011         close_preserve_errno(omap_dirfd);
   1012         if (ret < 0 && errno != ENOENT) {
   1013             goto err_undo_link;
   1014         }
   1015 
   1016         ret = 0;
   1017     }
   1018     goto out_close;
   1019 
   1020 err:
   1021     ret = -1;
   1022 err_undo_link:
   1023     unlinkat_preserve_errno(ndirfd, name, 0);
   1024 out_close:
   1025     close_preserve_errno(ndirfd);
   1026     close_preserve_errno(odirfd);
   1027 out:
   1028     g_free(oname);
   1029     g_free(odirpath);
   1030     return ret;
   1031 }
   1032 
   1033 static int local_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size)
   1034 {
   1035     int fd, ret;
   1036 
   1037     fd = local_open_nofollow(ctx, fs_path->data, O_WRONLY, 0);
   1038     if (fd == -1) {
   1039         return -1;
   1040     }
   1041     ret = ftruncate(fd, size);
   1042     close_preserve_errno(fd);
   1043     return ret;
   1044 }
   1045 
   1046 static int local_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
   1047 {
   1048     char *dirpath = g_path_get_dirname(fs_path->data);
   1049     char *name = g_path_get_basename(fs_path->data);
   1050     int ret = -1;
   1051     int dirfd;
   1052 
   1053     dirfd = local_opendir_nofollow(fs_ctx, dirpath);
   1054     if (dirfd == -1) {
   1055         goto out;
   1056     }
   1057 
   1058     if ((credp->fc_uid == -1 && credp->fc_gid == -1) ||
   1059         (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
   1060         (fs_ctx->export_flags & V9FS_SM_NONE)) {
   1061         ret = fchownat(dirfd, name, credp->fc_uid, credp->fc_gid,
   1062                        AT_SYMLINK_NOFOLLOW);
   1063     } else if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
   1064         ret = local_set_xattrat(dirfd, name, credp);
   1065     } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
   1066         ret = local_set_mapped_file_attrat(dirfd, name, credp);
   1067     }
   1068 
   1069     close_preserve_errno(dirfd);
   1070 out:
   1071     g_free(name);
   1072     g_free(dirpath);
   1073     return ret;
   1074 }
   1075 
   1076 static int local_utimensat(FsContext *s, V9fsPath *fs_path,
   1077                            const struct timespec *buf)
   1078 {
   1079     char *dirpath = g_path_get_dirname(fs_path->data);
   1080     char *name = g_path_get_basename(fs_path->data);
   1081     int dirfd, ret = -1;
   1082 
   1083     dirfd = local_opendir_nofollow(s, dirpath);
   1084     if (dirfd == -1) {
   1085         goto out;
   1086     }
   1087 
   1088     ret = utimensat(dirfd, name, buf, AT_SYMLINK_NOFOLLOW);
   1089     close_preserve_errno(dirfd);
   1090 out:
   1091     g_free(dirpath);
   1092     g_free(name);
   1093     return ret;
   1094 }
   1095 
   1096 static int local_unlinkat_common(FsContext *ctx, int dirfd, const char *name,
   1097                                  int flags)
   1098 {
   1099     int ret;
   1100 
   1101     if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
   1102         int map_dirfd;
   1103 
   1104         /* We need to remove the metadata as well:
   1105          * - the metadata directory if we're removing a directory
   1106          * - the metadata file in the parent's metadata directory
   1107          *
   1108          * If any of these are missing (ie, ENOENT) then we're probably
   1109          * trying to remove something that wasn't created in mapped-file
   1110          * mode. We just ignore the error.
   1111          */
   1112         if (flags == AT_REMOVEDIR) {
   1113             int fd;
   1114 
   1115             fd = openat_dir(dirfd, name);
   1116             if (fd == -1) {
   1117                 return -1;
   1118             }
   1119             ret = unlinkat(fd, VIRTFS_META_DIR, AT_REMOVEDIR);
   1120             close_preserve_errno(fd);
   1121             if (ret < 0 && errno != ENOENT) {
   1122                 return -1;
   1123             }
   1124         }
   1125         map_dirfd = openat_dir(dirfd, VIRTFS_META_DIR);
   1126         if (map_dirfd != -1) {
   1127             ret = unlinkat(map_dirfd, name, 0);
   1128             close_preserve_errno(map_dirfd);
   1129             if (ret < 0 && errno != ENOENT) {
   1130                 return -1;
   1131             }
   1132         } else if (errno != ENOENT) {
   1133             return -1;
   1134         }
   1135     }
   1136 
   1137     return unlinkat(dirfd, name, flags);
   1138 }
   1139 
   1140 static int local_remove(FsContext *ctx, const char *path)
   1141 {
   1142     struct stat stbuf;
   1143     char *dirpath = g_path_get_dirname(path);
   1144     char *name = g_path_get_basename(path);
   1145     int flags = 0;
   1146     int dirfd;
   1147     int err = -1;
   1148 
   1149     dirfd = local_opendir_nofollow(ctx, dirpath);
   1150     if (dirfd == -1) {
   1151         goto out;
   1152     }
   1153 
   1154     if (fstatat(dirfd, name, &stbuf, AT_SYMLINK_NOFOLLOW) < 0) {
   1155         goto err_out;
   1156     }
   1157 
   1158     if (S_ISDIR(stbuf.st_mode)) {
   1159         flags |= AT_REMOVEDIR;
   1160     }
   1161 
   1162     err = local_unlinkat_common(ctx, dirfd, name, flags);
   1163 err_out:
   1164     close_preserve_errno(dirfd);
   1165 out:
   1166     g_free(name);
   1167     g_free(dirpath);
   1168     return err;
   1169 }
   1170 
   1171 static int local_fsync(FsContext *ctx, int fid_type,
   1172                        V9fsFidOpenState *fs, int datasync)
   1173 {
   1174     int fd;
   1175 
   1176     if (fid_type == P9_FID_DIR) {
   1177         fd = dirfd(fs->dir.stream);
   1178     } else {
   1179         fd = fs->fd;
   1180     }
   1181 
   1182     if (datasync) {
   1183         return qemu_fdatasync(fd);
   1184     } else {
   1185         return fsync(fd);
   1186     }
   1187 }
   1188 
   1189 static int local_statfs(FsContext *s, V9fsPath *fs_path, struct statfs *stbuf)
   1190 {
   1191     int fd, ret;
   1192 
   1193     fd = local_open_nofollow(s, fs_path->data, O_RDONLY, 0);
   1194     if (fd == -1) {
   1195         return -1;
   1196     }
   1197     ret = fstatfs(fd, stbuf);
   1198     close_preserve_errno(fd);
   1199     return ret;
   1200 }
   1201 
   1202 static ssize_t local_lgetxattr(FsContext *ctx, V9fsPath *fs_path,
   1203                                const char *name, void *value, size_t size)
   1204 {
   1205     char *path = fs_path->data;
   1206 
   1207     return v9fs_get_xattr(ctx, path, name, value, size);
   1208 }
   1209 
   1210 static ssize_t local_llistxattr(FsContext *ctx, V9fsPath *fs_path,
   1211                                 void *value, size_t size)
   1212 {
   1213     char *path = fs_path->data;
   1214 
   1215     return v9fs_list_xattr(ctx, path, value, size);
   1216 }
   1217 
   1218 static int local_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name,
   1219                            void *value, size_t size, int flags)
   1220 {
   1221     char *path = fs_path->data;
   1222 
   1223     return v9fs_set_xattr(ctx, path, name, value, size, flags);
   1224 }
   1225 
   1226 static int local_lremovexattr(FsContext *ctx, V9fsPath *fs_path,
   1227                               const char *name)
   1228 {
   1229     char *path = fs_path->data;
   1230 
   1231     return v9fs_remove_xattr(ctx, path, name);
   1232 }
   1233 
   1234 static int local_name_to_path(FsContext *ctx, V9fsPath *dir_path,
   1235                               const char *name, V9fsPath *target)
   1236 {
   1237     if (ctx->export_flags & V9FS_SM_MAPPED_FILE &&
   1238         local_is_mapped_file_metadata(ctx, name)) {
   1239         errno = EINVAL;
   1240         return -1;
   1241     }
   1242 
   1243     if (dir_path) {
   1244         if (!strcmp(name, ".")) {
   1245             /* "." relative to "foo/bar" is "foo/bar" */
   1246             v9fs_path_copy(target, dir_path);
   1247         } else if (!strcmp(name, "..")) {
   1248             if (!strcmp(dir_path->data, ".")) {
   1249                 /* ".." relative to the root is "." */
   1250                 v9fs_path_sprintf(target, ".");
   1251             } else {
   1252                 char *tmp = g_path_get_dirname(dir_path->data);
   1253                 /* Symbolic links are resolved by the client. We can assume
   1254                  * that ".." relative to "foo/bar" is equivalent to "foo"
   1255                  */
   1256                 v9fs_path_sprintf(target, "%s", tmp);
   1257                 g_free(tmp);
   1258             }
   1259         } else {
   1260             assert(!strchr(name, '/'));
   1261             v9fs_path_sprintf(target, "%s/%s", dir_path->data, name);
   1262         }
   1263     } else if (!strcmp(name, "/") || !strcmp(name, ".") ||
   1264                !strcmp(name, "..")) {
   1265             /* This is the root fid */
   1266         v9fs_path_sprintf(target, ".");
   1267     } else {
   1268         assert(!strchr(name, '/'));
   1269         v9fs_path_sprintf(target, "./%s", name);
   1270     }
   1271     return 0;
   1272 }
   1273 
   1274 static int local_renameat(FsContext *ctx, V9fsPath *olddir,
   1275                           const char *old_name, V9fsPath *newdir,
   1276                           const char *new_name)
   1277 {
   1278     int ret;
   1279     int odirfd, ndirfd;
   1280 
   1281     if (ctx->export_flags & V9FS_SM_MAPPED_FILE &&
   1282         (local_is_mapped_file_metadata(ctx, old_name) ||
   1283          local_is_mapped_file_metadata(ctx, new_name))) {
   1284         errno = EINVAL;
   1285         return -1;
   1286     }
   1287 
   1288     odirfd = local_opendir_nofollow(ctx, olddir->data);
   1289     if (odirfd == -1) {
   1290         return -1;
   1291     }
   1292 
   1293     ndirfd = local_opendir_nofollow(ctx, newdir->data);
   1294     if (ndirfd == -1) {
   1295         close_preserve_errno(odirfd);
   1296         return -1;
   1297     }
   1298 
   1299     ret = renameat(odirfd, old_name, ndirfd, new_name);
   1300     if (ret < 0) {
   1301         goto out;
   1302     }
   1303 
   1304     if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
   1305         int omap_dirfd, nmap_dirfd;
   1306 
   1307         ret = mkdirat(ndirfd, VIRTFS_META_DIR, 0700);
   1308         if (ret < 0 && errno != EEXIST) {
   1309             goto err_undo_rename;
   1310         }
   1311 
   1312         omap_dirfd = openat_dir(odirfd, VIRTFS_META_DIR);
   1313         if (omap_dirfd == -1) {
   1314             goto err;
   1315         }
   1316 
   1317         nmap_dirfd = openat_dir(ndirfd, VIRTFS_META_DIR);
   1318         if (nmap_dirfd == -1) {
   1319             close_preserve_errno(omap_dirfd);
   1320             goto err;
   1321         }
   1322 
   1323         /* rename the .virtfs_metadata files */
   1324         ret = renameat(omap_dirfd, old_name, nmap_dirfd, new_name);
   1325         close_preserve_errno(nmap_dirfd);
   1326         close_preserve_errno(omap_dirfd);
   1327         if (ret < 0 && errno != ENOENT) {
   1328             goto err_undo_rename;
   1329         }
   1330 
   1331         ret = 0;
   1332     }
   1333     goto out;
   1334 
   1335 err:
   1336     ret = -1;
   1337 err_undo_rename:
   1338     renameat_preserve_errno(ndirfd, new_name, odirfd, old_name);
   1339 out:
   1340     close_preserve_errno(ndirfd);
   1341     close_preserve_errno(odirfd);
   1342     return ret;
   1343 }
   1344 
   1345 static void v9fs_path_init_dirname(V9fsPath *path, const char *str)
   1346 {
   1347     path->data = g_path_get_dirname(str);
   1348     path->size = strlen(path->data) + 1;
   1349 }
   1350 
   1351 static int local_rename(FsContext *ctx, const char *oldpath,
   1352                         const char *newpath)
   1353 {
   1354     int err;
   1355     char *oname = g_path_get_basename(oldpath);
   1356     char *nname = g_path_get_basename(newpath);
   1357     V9fsPath olddir, newdir;
   1358 
   1359     v9fs_path_init_dirname(&olddir, oldpath);
   1360     v9fs_path_init_dirname(&newdir, newpath);
   1361 
   1362     err = local_renameat(ctx, &olddir, oname, &newdir, nname);
   1363 
   1364     v9fs_path_free(&newdir);
   1365     v9fs_path_free(&olddir);
   1366     g_free(nname);
   1367     g_free(oname);
   1368 
   1369     return err;
   1370 }
   1371 
   1372 static int local_unlinkat(FsContext *ctx, V9fsPath *dir,
   1373                           const char *name, int flags)
   1374 {
   1375     int ret;
   1376     int dirfd;
   1377 
   1378     if (ctx->export_flags & V9FS_SM_MAPPED_FILE &&
   1379         local_is_mapped_file_metadata(ctx, name)) {
   1380         errno = EINVAL;
   1381         return -1;
   1382     }
   1383 
   1384     dirfd = local_opendir_nofollow(ctx, dir->data);
   1385     if (dirfd == -1) {
   1386         return -1;
   1387     }
   1388 
   1389     ret = local_unlinkat_common(ctx, dirfd, name, flags);
   1390     close_preserve_errno(dirfd);
   1391     return ret;
   1392 }
   1393 
   1394 #ifdef FS_IOC_GETVERSION
   1395 static int local_ioc_getversion(FsContext *ctx, V9fsPath *path,
   1396                                 mode_t st_mode, uint64_t *st_gen)
   1397 {
   1398     int err;
   1399     V9fsFidOpenState fid_open;
   1400 
   1401     /*
   1402      * Do not try to open special files like device nodes, fifos etc
   1403      * We can get fd for regular files and directories only
   1404      */
   1405     if (!S_ISREG(st_mode) && !S_ISDIR(st_mode)) {
   1406         errno = ENOTTY;
   1407         return -1;
   1408     }
   1409     err = local_open(ctx, path, O_RDONLY, &fid_open);
   1410     if (err < 0) {
   1411         return err;
   1412     }
   1413     err = ioctl(fid_open.fd, FS_IOC_GETVERSION, st_gen);
   1414     local_close(ctx, &fid_open);
   1415     return err;
   1416 }
   1417 #endif
   1418 
   1419 static int local_ioc_getversion_init(FsContext *ctx, LocalData *data, Error **errp)
   1420 {
   1421 #ifdef FS_IOC_GETVERSION
   1422     struct statfs stbuf;
   1423 
   1424     /*
   1425      * use ioc_getversion only if the ioctl is definied
   1426      */
   1427     if (fstatfs(data->mountfd, &stbuf) < 0) {
   1428         error_setg_errno(errp, errno,
   1429                          "failed to stat file system at '%s'", ctx->fs_root);
   1430         return -1;
   1431     }
   1432     switch (stbuf.f_type) {
   1433     case EXT2_SUPER_MAGIC:
   1434     case BTRFS_SUPER_MAGIC:
   1435     case REISERFS_SUPER_MAGIC:
   1436     case XFS_SUPER_MAGIC:
   1437         ctx->exops.get_st_gen = local_ioc_getversion;
   1438         break;
   1439     }
   1440 #endif
   1441     return 0;
   1442 }
   1443 
   1444 static int local_init(FsContext *ctx, Error **errp)
   1445 {
   1446     LocalData *data = g_malloc(sizeof(*data));
   1447 
   1448     data->mountfd = open(ctx->fs_root, O_DIRECTORY | O_RDONLY);
   1449     if (data->mountfd == -1) {
   1450         error_setg_errno(errp, errno, "failed to open '%s'", ctx->fs_root);
   1451         goto err;
   1452     }
   1453 
   1454     if (local_ioc_getversion_init(ctx, data, errp) < 0) {
   1455         close(data->mountfd);
   1456         goto err;
   1457     }
   1458 
   1459     if (ctx->export_flags & V9FS_SM_PASSTHROUGH) {
   1460         ctx->xops = passthrough_xattr_ops;
   1461     } else if (ctx->export_flags & V9FS_SM_MAPPED) {
   1462         ctx->xops = mapped_xattr_ops;
   1463     } else if (ctx->export_flags & V9FS_SM_NONE) {
   1464         ctx->xops = none_xattr_ops;
   1465     } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
   1466         /*
   1467          * xattr operation for mapped-file and passthrough
   1468          * remain same.
   1469          */
   1470         ctx->xops = passthrough_xattr_ops;
   1471     }
   1472     ctx->export_flags |= V9FS_PATHNAME_FSCONTEXT;
   1473 
   1474     ctx->private = data;
   1475     return 0;
   1476 
   1477 err:
   1478     g_free(data);
   1479     return -1;
   1480 }
   1481 
   1482 static void local_cleanup(FsContext *ctx)
   1483 {
   1484     LocalData *data = ctx->private;
   1485 
   1486     if (!data) {
   1487         return;
   1488     }
   1489 
   1490     close(data->mountfd);
   1491     g_free(data);
   1492 }
   1493 
   1494 static void error_append_security_model_hint(Error *const *errp)
   1495 {
   1496     error_append_hint(errp, "Valid options are: security_model="
   1497                       "[passthrough|mapped-xattr|mapped-file|none]\n");
   1498 }
   1499 
   1500 static int local_parse_opts(QemuOpts *opts, FsDriverEntry *fse, Error **errp)
   1501 {
   1502     ERRP_GUARD();
   1503     const char *sec_model = qemu_opt_get(opts, "security_model");
   1504     const char *path = qemu_opt_get(opts, "path");
   1505     const char *multidevs = qemu_opt_get(opts, "multidevs");
   1506 
   1507     if (!sec_model) {
   1508         error_setg(errp, "security_model property not set");
   1509         error_append_security_model_hint(errp);
   1510         return -1;
   1511     }
   1512 
   1513     if (!strcmp(sec_model, "passthrough")) {
   1514         fse->export_flags |= V9FS_SM_PASSTHROUGH;
   1515     } else if (!strcmp(sec_model, "mapped") ||
   1516                !strcmp(sec_model, "mapped-xattr")) {
   1517         fse->export_flags |= V9FS_SM_MAPPED;
   1518     } else if (!strcmp(sec_model, "none")) {
   1519         fse->export_flags |= V9FS_SM_NONE;
   1520     } else if (!strcmp(sec_model, "mapped-file")) {
   1521         fse->export_flags |= V9FS_SM_MAPPED_FILE;
   1522     } else {
   1523         error_setg(errp, "invalid security_model property '%s'", sec_model);
   1524         error_append_security_model_hint(errp);
   1525         return -1;
   1526     }
   1527 
   1528     if (multidevs) {
   1529         if (!strcmp(multidevs, "remap")) {
   1530             fse->export_flags &= ~V9FS_FORBID_MULTIDEVS;
   1531             fse->export_flags |= V9FS_REMAP_INODES;
   1532         } else if (!strcmp(multidevs, "forbid")) {
   1533             fse->export_flags &= ~V9FS_REMAP_INODES;
   1534             fse->export_flags |= V9FS_FORBID_MULTIDEVS;
   1535         } else if (!strcmp(multidevs, "warn")) {
   1536             fse->export_flags &= ~V9FS_FORBID_MULTIDEVS;
   1537             fse->export_flags &= ~V9FS_REMAP_INODES;
   1538         } else {
   1539             error_setg(errp, "invalid multidevs property '%s'",
   1540                        multidevs);
   1541             error_append_hint(errp, "Valid options are: multidevs="
   1542                               "[remap|forbid|warn]\n");
   1543             return -1;
   1544         }
   1545     }
   1546 
   1547     if (!path) {
   1548         error_setg(errp, "path property not set");
   1549         return -1;
   1550     }
   1551 
   1552     if (fsdev_throttle_parse_opts(opts, &fse->fst, errp)) {
   1553         error_prepend(errp, "invalid throttle configuration: ");
   1554         return -1;
   1555     }
   1556 
   1557     if (fse->export_flags & V9FS_SM_MAPPED ||
   1558         fse->export_flags & V9FS_SM_MAPPED_FILE) {
   1559         fse->fmode =
   1560             qemu_opt_get_number(opts, "fmode", SM_LOCAL_MODE_BITS) & 0777;
   1561         fse->dmode =
   1562             qemu_opt_get_number(opts, "dmode", SM_LOCAL_DIR_MODE_BITS) & 0777;
   1563     } else {
   1564         if (qemu_opt_find(opts, "fmode")) {
   1565             error_setg(errp, "fmode is only valid for mapped security modes");
   1566             return -1;
   1567         }
   1568         if (qemu_opt_find(opts, "dmode")) {
   1569             error_setg(errp, "dmode is only valid for mapped security modes");
   1570             return -1;
   1571         }
   1572     }
   1573 
   1574     fse->path = g_strdup(path);
   1575 
   1576     return 0;
   1577 }
   1578 
   1579 FileOperations local_ops = {
   1580     .parse_opts = local_parse_opts,
   1581     .init  = local_init,
   1582     .cleanup = local_cleanup,
   1583     .lstat = local_lstat,
   1584     .readlink = local_readlink,
   1585     .close = local_close,
   1586     .closedir = local_closedir,
   1587     .open = local_open,
   1588     .opendir = local_opendir,
   1589     .rewinddir = local_rewinddir,
   1590     .telldir = local_telldir,
   1591     .readdir = local_readdir,
   1592     .seekdir = local_seekdir,
   1593     .preadv = local_preadv,
   1594     .pwritev = local_pwritev,
   1595     .chmod = local_chmod,
   1596     .mknod = local_mknod,
   1597     .mkdir = local_mkdir,
   1598     .fstat = local_fstat,
   1599     .open2 = local_open2,
   1600     .symlink = local_symlink,
   1601     .link = local_link,
   1602     .truncate = local_truncate,
   1603     .rename = local_rename,
   1604     .chown = local_chown,
   1605     .utimensat = local_utimensat,
   1606     .remove = local_remove,
   1607     .fsync = local_fsync,
   1608     .statfs = local_statfs,
   1609     .lgetxattr = local_lgetxattr,
   1610     .llistxattr = local_llistxattr,
   1611     .lsetxattr = local_lsetxattr,
   1612     .lremovexattr = local_lremovexattr,
   1613     .name_to_path = local_name_to_path,
   1614     .renameat  = local_renameat,
   1615     .unlinkat = local_unlinkat,
   1616 };