cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

9p-local.c (43695B)


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