qemu/hw/9pfs/9p-local.c
<<
>>
Prefs
   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#include "qemu/osdep.h"
  15#include "9p.h"
  16#include "9p-xattr.h"
  17#include "fsdev/qemu-fsdev.h"   /* local_ops */
  18#include <arpa/inet.h>
  19#include <pwd.h>
  20#include <grp.h>
  21#include <sys/socket.h>
  22#include <sys/un.h>
  23#include "qemu/xattr.h"
  24#include "qemu/cutils.h"
  25#include "qemu/error-report.h"
  26#include <libgen.h>
  27#include <linux/fs.h>
  28#ifdef CONFIG_LINUX_MAGIC_H
  29#include <linux/magic.h>
  30#endif
  31#include <sys/ioctl.h>
  32
  33#ifndef XFS_SUPER_MAGIC
  34#define XFS_SUPER_MAGIC  0x58465342
  35#endif
  36#ifndef EXT2_SUPER_MAGIC
  37#define EXT2_SUPER_MAGIC 0xEF53
  38#endif
  39#ifndef REISERFS_SUPER_MAGIC
  40#define REISERFS_SUPER_MAGIC 0x52654973
  41#endif
  42#ifndef BTRFS_SUPER_MAGIC
  43#define BTRFS_SUPER_MAGIC 0x9123683E
  44#endif
  45
  46#define VIRTFS_META_DIR ".virtfs_metadata"
  47
  48static char *local_mapped_attr_path(FsContext *ctx, const char *path)
  49{
  50    int dirlen;
  51    const char *name = strrchr(path, '/');
  52    if (name) {
  53        dirlen = name - path;
  54        ++name;
  55    } else {
  56        name = path;
  57        dirlen = 0;
  58    }
  59    return g_strdup_printf("%s/%.*s/%s/%s", ctx->fs_root,
  60                           dirlen, path, VIRTFS_META_DIR, name);
  61}
  62
  63static FILE *local_fopen(const char *path, const char *mode)
  64{
  65    int fd, o_mode = 0;
  66    FILE *fp;
  67    int flags = O_NOFOLLOW;
  68    /*
  69     * only supports two modes
  70     */
  71    if (mode[0] == 'r') {
  72        flags |= O_RDONLY;
  73    } else if (mode[0] == 'w') {
  74        flags |= O_WRONLY | O_TRUNC | O_CREAT;
  75        o_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
  76    } else {
  77        return NULL;
  78    }
  79    fd = open(path, flags, o_mode);
  80    if (fd == -1) {
  81        return NULL;
  82    }
  83    fp = fdopen(fd, mode);
  84    if (!fp) {
  85        close(fd);
  86    }
  87    return fp;
  88}
  89
  90#define ATTR_MAX 100
  91static void local_mapped_file_attr(FsContext *ctx, const char *path,
  92                                   struct stat *stbuf)
  93{
  94    FILE *fp;
  95    char buf[ATTR_MAX];
  96    char *attr_path;
  97
  98    attr_path = local_mapped_attr_path(ctx, path);
  99    fp = local_fopen(attr_path, "r");
 100    g_free(attr_path);
 101    if (!fp) {
 102        return;
 103    }
 104    memset(buf, 0, ATTR_MAX);
 105    while (fgets(buf, ATTR_MAX, fp)) {
 106        if (!strncmp(buf, "virtfs.uid", 10)) {
 107            stbuf->st_uid = atoi(buf+11);
 108        } else if (!strncmp(buf, "virtfs.gid", 10)) {
 109            stbuf->st_gid = atoi(buf+11);
 110        } else if (!strncmp(buf, "virtfs.mode", 11)) {
 111            stbuf->st_mode = atoi(buf+12);
 112        } else if (!strncmp(buf, "virtfs.rdev", 11)) {
 113            stbuf->st_rdev = atoi(buf+12);
 114        }
 115        memset(buf, 0, ATTR_MAX);
 116    }
 117    fclose(fp);
 118}
 119
 120static int local_lstat(FsContext *fs_ctx, V9fsPath *fs_path, struct stat *stbuf)
 121{
 122    int err;
 123    char *buffer;
 124    char *path = fs_path->data;
 125
 126    buffer = rpath(fs_ctx, path);
 127    err =  lstat(buffer, stbuf);
 128    if (err) {
 129        goto err_out;
 130    }
 131    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
 132        /* Actual credentials are part of extended attrs */
 133        uid_t tmp_uid;
 134        gid_t tmp_gid;
 135        mode_t tmp_mode;
 136        dev_t tmp_dev;
 137        if (getxattr(buffer, "user.virtfs.uid", &tmp_uid, sizeof(uid_t)) > 0) {
 138            stbuf->st_uid = le32_to_cpu(tmp_uid);
 139        }
 140        if (getxattr(buffer, "user.virtfs.gid", &tmp_gid, sizeof(gid_t)) > 0) {
 141            stbuf->st_gid = le32_to_cpu(tmp_gid);
 142        }
 143        if (getxattr(buffer, "user.virtfs.mode",
 144                    &tmp_mode, sizeof(mode_t)) > 0) {
 145            stbuf->st_mode = le32_to_cpu(tmp_mode);
 146        }
 147        if (getxattr(buffer, "user.virtfs.rdev", &tmp_dev, sizeof(dev_t)) > 0) {
 148            stbuf->st_rdev = le64_to_cpu(tmp_dev);
 149        }
 150    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
 151        local_mapped_file_attr(fs_ctx, path, stbuf);
 152    }
 153
 154err_out:
 155    g_free(buffer);
 156    return err;
 157}
 158
 159static int local_create_mapped_attr_dir(FsContext *ctx, const char *path)
 160{
 161    int err;
 162    char *attr_dir;
 163    char *tmp_path = g_strdup(path);
 164
 165    attr_dir = g_strdup_printf("%s/%s/%s",
 166             ctx->fs_root, dirname(tmp_path), VIRTFS_META_DIR);
 167
 168    err = mkdir(attr_dir, 0700);
 169    if (err < 0 && errno == EEXIST) {
 170        err = 0;
 171    }
 172    g_free(attr_dir);
 173    g_free(tmp_path);
 174    return err;
 175}
 176
 177static int local_set_mapped_file_attr(FsContext *ctx,
 178                                      const char *path, FsCred *credp)
 179{
 180    FILE *fp;
 181    int ret = 0;
 182    char buf[ATTR_MAX];
 183    char *attr_path;
 184    int uid = -1, gid = -1, mode = -1, rdev = -1;
 185
 186    attr_path = local_mapped_attr_path(ctx, path);
 187    fp = local_fopen(attr_path, "r");
 188    if (!fp) {
 189        goto create_map_file;
 190    }
 191    memset(buf, 0, ATTR_MAX);
 192    while (fgets(buf, ATTR_MAX, fp)) {
 193        if (!strncmp(buf, "virtfs.uid", 10)) {
 194            uid = atoi(buf+11);
 195        } else if (!strncmp(buf, "virtfs.gid", 10)) {
 196            gid = atoi(buf+11);
 197        } else if (!strncmp(buf, "virtfs.mode", 11)) {
 198            mode = atoi(buf+12);
 199        } else if (!strncmp(buf, "virtfs.rdev", 11)) {
 200            rdev = atoi(buf+12);
 201        }
 202        memset(buf, 0, ATTR_MAX);
 203    }
 204    fclose(fp);
 205    goto update_map_file;
 206
 207create_map_file:
 208    ret = local_create_mapped_attr_dir(ctx, path);
 209    if (ret < 0) {
 210        goto err_out;
 211    }
 212
 213update_map_file:
 214    fp = local_fopen(attr_path, "w");
 215    if (!fp) {
 216        ret = -1;
 217        goto err_out;
 218    }
 219
 220    if (credp->fc_uid != -1) {
 221        uid = credp->fc_uid;
 222    }
 223    if (credp->fc_gid != -1) {
 224        gid = credp->fc_gid;
 225    }
 226    if (credp->fc_mode != -1) {
 227        mode = credp->fc_mode;
 228    }
 229    if (credp->fc_rdev != -1) {
 230        rdev = credp->fc_rdev;
 231    }
 232
 233
 234    if (uid != -1) {
 235        fprintf(fp, "virtfs.uid=%d\n", uid);
 236    }
 237    if (gid != -1) {
 238        fprintf(fp, "virtfs.gid=%d\n", gid);
 239    }
 240    if (mode != -1) {
 241        fprintf(fp, "virtfs.mode=%d\n", mode);
 242    }
 243    if (rdev != -1) {
 244        fprintf(fp, "virtfs.rdev=%d\n", rdev);
 245    }
 246    fclose(fp);
 247
 248err_out:
 249    g_free(attr_path);
 250    return ret;
 251}
 252
 253static int local_set_xattr(const char *path, FsCred *credp)
 254{
 255    int err;
 256
 257    if (credp->fc_uid != -1) {
 258        uint32_t tmp_uid = cpu_to_le32(credp->fc_uid);
 259        err = setxattr(path, "user.virtfs.uid", &tmp_uid, sizeof(uid_t), 0);
 260        if (err) {
 261            return err;
 262        }
 263    }
 264    if (credp->fc_gid != -1) {
 265        uint32_t tmp_gid = cpu_to_le32(credp->fc_gid);
 266        err = setxattr(path, "user.virtfs.gid", &tmp_gid, sizeof(gid_t), 0);
 267        if (err) {
 268            return err;
 269        }
 270    }
 271    if (credp->fc_mode != -1) {
 272        uint32_t tmp_mode = cpu_to_le32(credp->fc_mode);
 273        err = setxattr(path, "user.virtfs.mode", &tmp_mode, sizeof(mode_t), 0);
 274        if (err) {
 275            return err;
 276        }
 277    }
 278    if (credp->fc_rdev != -1) {
 279        uint64_t tmp_rdev = cpu_to_le64(credp->fc_rdev);
 280        err = setxattr(path, "user.virtfs.rdev", &tmp_rdev, sizeof(dev_t), 0);
 281        if (err) {
 282            return err;
 283        }
 284    }
 285    return 0;
 286}
 287
 288static int local_post_create_passthrough(FsContext *fs_ctx, const char *path,
 289                                         FsCred *credp)
 290{
 291    char *buffer;
 292
 293    buffer = rpath(fs_ctx, path);
 294    if (lchown(buffer, credp->fc_uid, credp->fc_gid) < 0) {
 295        /*
 296         * If we fail to change ownership and if we are
 297         * using security model none. Ignore the error
 298         */
 299        if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) {
 300            goto err;
 301        }
 302    }
 303
 304    if (chmod(buffer, credp->fc_mode & 07777) < 0) {
 305        goto err;
 306    }
 307
 308    g_free(buffer);
 309    return 0;
 310err:
 311    g_free(buffer);
 312    return -1;
 313}
 314
 315static ssize_t local_readlink(FsContext *fs_ctx, V9fsPath *fs_path,
 316                              char *buf, size_t bufsz)
 317{
 318    ssize_t tsize = -1;
 319    char *buffer;
 320    char *path = fs_path->data;
 321
 322    if ((fs_ctx->export_flags & V9FS_SM_MAPPED) ||
 323        (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE)) {
 324        int fd;
 325        buffer = rpath(fs_ctx, path);
 326        fd = open(buffer, O_RDONLY | O_NOFOLLOW);
 327        g_free(buffer);
 328        if (fd == -1) {
 329            return -1;
 330        }
 331        do {
 332            tsize = read(fd, (void *)buf, bufsz);
 333        } while (tsize == -1 && errno == EINTR);
 334        close(fd);
 335    } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
 336               (fs_ctx->export_flags & V9FS_SM_NONE)) {
 337        buffer = rpath(fs_ctx, path);
 338        tsize = readlink(buffer, buf, bufsz);
 339        g_free(buffer);
 340    }
 341    return tsize;
 342}
 343
 344static int local_close(FsContext *ctx, V9fsFidOpenState *fs)
 345{
 346    return close(fs->fd);
 347}
 348
 349static int local_closedir(FsContext *ctx, V9fsFidOpenState *fs)
 350{
 351    return closedir(fs->dir);
 352}
 353
 354static int local_open(FsContext *ctx, V9fsPath *fs_path,
 355                      int flags, V9fsFidOpenState *fs)
 356{
 357    char *buffer;
 358    char *path = fs_path->data;
 359
 360    buffer = rpath(ctx, path);
 361    fs->fd = open(buffer, flags | O_NOFOLLOW);
 362    g_free(buffer);
 363    return fs->fd;
 364}
 365
 366static int local_opendir(FsContext *ctx,
 367                         V9fsPath *fs_path, V9fsFidOpenState *fs)
 368{
 369    char *buffer;
 370    char *path = fs_path->data;
 371
 372    buffer = rpath(ctx, path);
 373    fs->dir = opendir(buffer);
 374    g_free(buffer);
 375    if (!fs->dir) {
 376        return -1;
 377    }
 378    return 0;
 379}
 380
 381static void local_rewinddir(FsContext *ctx, V9fsFidOpenState *fs)
 382{
 383    rewinddir(fs->dir);
 384}
 385
 386static off_t local_telldir(FsContext *ctx, V9fsFidOpenState *fs)
 387{
 388    return telldir(fs->dir);
 389}
 390
 391static int local_readdir_r(FsContext *ctx, V9fsFidOpenState *fs,
 392                           struct dirent *entry,
 393                           struct dirent **result)
 394{
 395    int ret;
 396
 397again:
 398    ret = readdir_r(fs->dir, entry, result);
 399    if (ctx->export_flags & V9FS_SM_MAPPED) {
 400        entry->d_type = DT_UNKNOWN;
 401    } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
 402        if (!ret && *result != NULL &&
 403            !strcmp(entry->d_name, VIRTFS_META_DIR)) {
 404            /* skp the meta data directory */
 405            goto again;
 406        }
 407        entry->d_type = DT_UNKNOWN;
 408    }
 409    return ret;
 410}
 411
 412static void local_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off)
 413{
 414    seekdir(fs->dir, off);
 415}
 416
 417static ssize_t local_preadv(FsContext *ctx, V9fsFidOpenState *fs,
 418                            const struct iovec *iov,
 419                            int iovcnt, off_t offset)
 420{
 421#ifdef CONFIG_PREADV
 422    return preadv(fs->fd, iov, iovcnt, offset);
 423#else
 424    int err = lseek(fs->fd, offset, SEEK_SET);
 425    if (err == -1) {
 426        return err;
 427    } else {
 428        return readv(fs->fd, iov, iovcnt);
 429    }
 430#endif
 431}
 432
 433static ssize_t local_pwritev(FsContext *ctx, V9fsFidOpenState *fs,
 434                             const struct iovec *iov,
 435                             int iovcnt, off_t offset)
 436{
 437    ssize_t ret
 438;
 439#ifdef CONFIG_PREADV
 440    ret = pwritev(fs->fd, iov, iovcnt, offset);
 441#else
 442    int err = lseek(fs->fd, offset, SEEK_SET);
 443    if (err == -1) {
 444        return err;
 445    } else {
 446        ret = writev(fs->fd, iov, iovcnt);
 447    }
 448#endif
 449#ifdef CONFIG_SYNC_FILE_RANGE
 450    if (ret > 0 && ctx->export_flags & V9FS_IMMEDIATE_WRITEOUT) {
 451        /*
 452         * Initiate a writeback. This is not a data integrity sync.
 453         * We want to ensure that we don't leave dirty pages in the cache
 454         * after write when writeout=immediate is sepcified.
 455         */
 456        sync_file_range(fs->fd, offset, ret,
 457                        SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE);
 458    }
 459#endif
 460    return ret;
 461}
 462
 463static int local_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
 464{
 465    char *buffer;
 466    int ret = -1;
 467    char *path = fs_path->data;
 468
 469    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
 470        buffer = rpath(fs_ctx, path);
 471        ret = local_set_xattr(buffer, credp);
 472        g_free(buffer);
 473    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
 474        return local_set_mapped_file_attr(fs_ctx, path, credp);
 475    } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
 476               (fs_ctx->export_flags & V9FS_SM_NONE)) {
 477        buffer = rpath(fs_ctx, path);
 478        ret = chmod(buffer, credp->fc_mode);
 479        g_free(buffer);
 480    }
 481    return ret;
 482}
 483
 484static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path,
 485                       const char *name, FsCred *credp)
 486{
 487    char *path;
 488    int err = -1;
 489    int serrno = 0;
 490    V9fsString fullname;
 491    char *buffer = NULL;
 492
 493    v9fs_string_init(&fullname);
 494    v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
 495    path = fullname.data;
 496
 497    /* Determine the security model */
 498    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
 499        buffer = rpath(fs_ctx, path);
 500        err = mknod(buffer, SM_LOCAL_MODE_BITS|S_IFREG, 0);
 501        if (err == -1) {
 502            goto out;
 503        }
 504        err = local_set_xattr(buffer, credp);
 505        if (err == -1) {
 506            serrno = errno;
 507            goto err_end;
 508        }
 509    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
 510
 511        buffer = rpath(fs_ctx, path);
 512        err = mknod(buffer, SM_LOCAL_MODE_BITS|S_IFREG, 0);
 513        if (err == -1) {
 514            goto out;
 515        }
 516        err = local_set_mapped_file_attr(fs_ctx, path, credp);
 517        if (err == -1) {
 518            serrno = errno;
 519            goto err_end;
 520        }
 521    } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
 522               (fs_ctx->export_flags & V9FS_SM_NONE)) {
 523        buffer = rpath(fs_ctx, path);
 524        err = mknod(buffer, credp->fc_mode, credp->fc_rdev);
 525        if (err == -1) {
 526            goto out;
 527        }
 528        err = local_post_create_passthrough(fs_ctx, path, credp);
 529        if (err == -1) {
 530            serrno = errno;
 531            goto err_end;
 532        }
 533    }
 534    goto out;
 535
 536err_end:
 537    remove(buffer);
 538    errno = serrno;
 539out:
 540    g_free(buffer);
 541    v9fs_string_free(&fullname);
 542    return err;
 543}
 544
 545static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path,
 546                       const char *name, FsCred *credp)
 547{
 548    char *path;
 549    int err = -1;
 550    int serrno = 0;
 551    V9fsString fullname;
 552    char *buffer = NULL;
 553
 554    v9fs_string_init(&fullname);
 555    v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
 556    path = fullname.data;
 557
 558    /* Determine the security model */
 559    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
 560        buffer = rpath(fs_ctx, path);
 561        err = mkdir(buffer, SM_LOCAL_DIR_MODE_BITS);
 562        if (err == -1) {
 563            goto out;
 564        }
 565        credp->fc_mode = credp->fc_mode|S_IFDIR;
 566        err = local_set_xattr(buffer, credp);
 567        if (err == -1) {
 568            serrno = errno;
 569            goto err_end;
 570        }
 571    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
 572        buffer = rpath(fs_ctx, path);
 573        err = mkdir(buffer, SM_LOCAL_DIR_MODE_BITS);
 574        if (err == -1) {
 575            goto out;
 576        }
 577        credp->fc_mode = credp->fc_mode|S_IFDIR;
 578        err = local_set_mapped_file_attr(fs_ctx, path, credp);
 579        if (err == -1) {
 580            serrno = errno;
 581            goto err_end;
 582        }
 583    } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
 584               (fs_ctx->export_flags & V9FS_SM_NONE)) {
 585        buffer = rpath(fs_ctx, path);
 586        err = mkdir(buffer, credp->fc_mode);
 587        if (err == -1) {
 588            goto out;
 589        }
 590        err = local_post_create_passthrough(fs_ctx, path, credp);
 591        if (err == -1) {
 592            serrno = errno;
 593            goto err_end;
 594        }
 595    }
 596    goto out;
 597
 598err_end:
 599    remove(buffer);
 600    errno = serrno;
 601out:
 602    g_free(buffer);
 603    v9fs_string_free(&fullname);
 604    return err;
 605}
 606
 607static int local_fstat(FsContext *fs_ctx, int fid_type,
 608                       V9fsFidOpenState *fs, struct stat *stbuf)
 609{
 610    int err, fd;
 611
 612    if (fid_type == P9_FID_DIR) {
 613        fd = dirfd(fs->dir);
 614    } else {
 615        fd = fs->fd;
 616    }
 617
 618    err = fstat(fd, stbuf);
 619    if (err) {
 620        return err;
 621    }
 622    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
 623        /* Actual credentials are part of extended attrs */
 624        uid_t tmp_uid;
 625        gid_t tmp_gid;
 626        mode_t tmp_mode;
 627        dev_t tmp_dev;
 628
 629        if (fgetxattr(fd, "user.virtfs.uid", &tmp_uid, sizeof(uid_t)) > 0) {
 630            stbuf->st_uid = le32_to_cpu(tmp_uid);
 631        }
 632        if (fgetxattr(fd, "user.virtfs.gid", &tmp_gid, sizeof(gid_t)) > 0) {
 633            stbuf->st_gid = le32_to_cpu(tmp_gid);
 634        }
 635        if (fgetxattr(fd, "user.virtfs.mode", &tmp_mode, sizeof(mode_t)) > 0) {
 636            stbuf->st_mode = le32_to_cpu(tmp_mode);
 637        }
 638        if (fgetxattr(fd, "user.virtfs.rdev", &tmp_dev, sizeof(dev_t)) > 0) {
 639            stbuf->st_rdev = le64_to_cpu(tmp_dev);
 640        }
 641    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
 642        errno = EOPNOTSUPP;
 643        return -1;
 644    }
 645    return err;
 646}
 647
 648static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
 649                       int flags, FsCred *credp, V9fsFidOpenState *fs)
 650{
 651    char *path;
 652    int fd = -1;
 653    int err = -1;
 654    int serrno = 0;
 655    V9fsString fullname;
 656    char *buffer = NULL;
 657
 658    /*
 659     * Mark all the open to not follow symlinks
 660     */
 661    flags |= O_NOFOLLOW;
 662
 663    v9fs_string_init(&fullname);
 664    v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
 665    path = fullname.data;
 666
 667    /* Determine the security model */
 668    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
 669        buffer = rpath(fs_ctx, path);
 670        fd = open(buffer, flags, SM_LOCAL_MODE_BITS);
 671        if (fd == -1) {
 672            err = fd;
 673            goto out;
 674        }
 675        credp->fc_mode = credp->fc_mode|S_IFREG;
 676        /* Set cleint credentials in xattr */
 677        err = local_set_xattr(buffer, credp);
 678        if (err == -1) {
 679            serrno = errno;
 680            goto err_end;
 681        }
 682    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
 683        buffer = rpath(fs_ctx, path);
 684        fd = open(buffer, flags, SM_LOCAL_MODE_BITS);
 685        if (fd == -1) {
 686            err = fd;
 687            goto out;
 688        }
 689        credp->fc_mode = credp->fc_mode|S_IFREG;
 690        /* Set client credentials in .virtfs_metadata directory files */
 691        err = local_set_mapped_file_attr(fs_ctx, path, credp);
 692        if (err == -1) {
 693            serrno = errno;
 694            goto err_end;
 695        }
 696    } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
 697               (fs_ctx->export_flags & V9FS_SM_NONE)) {
 698        buffer = rpath(fs_ctx, path);
 699        fd = open(buffer, flags, credp->fc_mode);
 700        if (fd == -1) {
 701            err = fd;
 702            goto out;
 703        }
 704        err = local_post_create_passthrough(fs_ctx, path, credp);
 705        if (err == -1) {
 706            serrno = errno;
 707            goto err_end;
 708        }
 709    }
 710    err = fd;
 711    fs->fd = fd;
 712    goto out;
 713
 714err_end:
 715    close(fd);
 716    remove(buffer);
 717    errno = serrno;
 718out:
 719    g_free(buffer);
 720    v9fs_string_free(&fullname);
 721    return err;
 722}
 723
 724
 725static int local_symlink(FsContext *fs_ctx, const char *oldpath,
 726                         V9fsPath *dir_path, const char *name, FsCred *credp)
 727{
 728    int err = -1;
 729    int serrno = 0;
 730    char *newpath;
 731    V9fsString fullname;
 732    char *buffer = NULL;
 733
 734    v9fs_string_init(&fullname);
 735    v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
 736    newpath = fullname.data;
 737
 738    /* Determine the security model */
 739    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
 740        int fd;
 741        ssize_t oldpath_size, write_size;
 742        buffer = rpath(fs_ctx, newpath);
 743        fd = open(buffer, O_CREAT|O_EXCL|O_RDWR|O_NOFOLLOW, SM_LOCAL_MODE_BITS);
 744        if (fd == -1) {
 745            err = fd;
 746            goto out;
 747        }
 748        /* Write the oldpath (target) to the file. */
 749        oldpath_size = strlen(oldpath);
 750        do {
 751            write_size = write(fd, (void *)oldpath, oldpath_size);
 752        } while (write_size == -1 && errno == EINTR);
 753
 754        if (write_size != oldpath_size) {
 755            serrno = errno;
 756            close(fd);
 757            err = -1;
 758            goto err_end;
 759        }
 760        close(fd);
 761        /* Set cleint credentials in symlink's xattr */
 762        credp->fc_mode = credp->fc_mode|S_IFLNK;
 763        err = local_set_xattr(buffer, credp);
 764        if (err == -1) {
 765            serrno = errno;
 766            goto err_end;
 767        }
 768    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
 769        int fd;
 770        ssize_t oldpath_size, write_size;
 771        buffer = rpath(fs_ctx, newpath);
 772        fd = open(buffer, O_CREAT|O_EXCL|O_RDWR|O_NOFOLLOW, SM_LOCAL_MODE_BITS);
 773        if (fd == -1) {
 774            err = fd;
 775            goto out;
 776        }
 777        /* Write the oldpath (target) to the file. */
 778        oldpath_size = strlen(oldpath);
 779        do {
 780            write_size = write(fd, (void *)oldpath, oldpath_size);
 781        } while (write_size == -1 && errno == EINTR);
 782
 783        if (write_size != oldpath_size) {
 784            serrno = errno;
 785            close(fd);
 786            err = -1;
 787            goto err_end;
 788        }
 789        close(fd);
 790        /* Set cleint credentials in symlink's xattr */
 791        credp->fc_mode = credp->fc_mode|S_IFLNK;
 792        err = local_set_mapped_file_attr(fs_ctx, newpath, credp);
 793        if (err == -1) {
 794            serrno = errno;
 795            goto err_end;
 796        }
 797    } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
 798               (fs_ctx->export_flags & V9FS_SM_NONE)) {
 799        buffer = rpath(fs_ctx, newpath);
 800        err = symlink(oldpath, buffer);
 801        if (err) {
 802            goto out;
 803        }
 804        err = lchown(buffer, credp->fc_uid, credp->fc_gid);
 805        if (err == -1) {
 806            /*
 807             * If we fail to change ownership and if we are
 808             * using security model none. Ignore the error
 809             */
 810            if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) {
 811                serrno = errno;
 812                goto err_end;
 813            } else
 814                err = 0;
 815        }
 816    }
 817    goto out;
 818
 819err_end:
 820    remove(buffer);
 821    errno = serrno;
 822out:
 823    g_free(buffer);
 824    v9fs_string_free(&fullname);
 825    return err;
 826}
 827
 828static int local_link(FsContext *ctx, V9fsPath *oldpath,
 829                      V9fsPath *dirpath, const char *name)
 830{
 831    int ret;
 832    V9fsString newpath;
 833    char *buffer, *buffer1;
 834
 835    v9fs_string_init(&newpath);
 836    v9fs_string_sprintf(&newpath, "%s/%s", dirpath->data, name);
 837
 838    buffer = rpath(ctx, oldpath->data);
 839    buffer1 = rpath(ctx, newpath.data);
 840    ret = link(buffer, buffer1);
 841    g_free(buffer);
 842    g_free(buffer1);
 843
 844    /* now link the virtfs_metadata files */
 845    if (!ret && (ctx->export_flags & V9FS_SM_MAPPED_FILE)) {
 846        /* Link the .virtfs_metadata files. Create the metada directory */
 847        ret = local_create_mapped_attr_dir(ctx, newpath.data);
 848        if (ret < 0) {
 849            goto err_out;
 850        }
 851        buffer = local_mapped_attr_path(ctx, oldpath->data);
 852        buffer1 = local_mapped_attr_path(ctx, newpath.data);
 853        ret = link(buffer, buffer1);
 854        g_free(buffer);
 855        g_free(buffer1);
 856        if (ret < 0 && errno != ENOENT) {
 857            goto err_out;
 858        }
 859    }
 860err_out:
 861    v9fs_string_free(&newpath);
 862    return ret;
 863}
 864
 865static int local_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size)
 866{
 867    char *buffer;
 868    int ret;
 869    char *path = fs_path->data;
 870
 871    buffer = rpath(ctx, path);
 872    ret = truncate(buffer, size);
 873    g_free(buffer);
 874    return ret;
 875}
 876
 877static int local_rename(FsContext *ctx, const char *oldpath,
 878                        const char *newpath)
 879{
 880    int err;
 881    char *buffer, *buffer1;
 882
 883    if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
 884        err = local_create_mapped_attr_dir(ctx, newpath);
 885        if (err < 0) {
 886            return err;
 887        }
 888        /* rename the .virtfs_metadata files */
 889        buffer = local_mapped_attr_path(ctx, oldpath);
 890        buffer1 = local_mapped_attr_path(ctx, newpath);
 891        err = rename(buffer, buffer1);
 892        g_free(buffer);
 893        g_free(buffer1);
 894        if (err < 0 && errno != ENOENT) {
 895            return err;
 896        }
 897    }
 898
 899    buffer = rpath(ctx, oldpath);
 900    buffer1 = rpath(ctx, newpath);
 901    err = rename(buffer, buffer1);
 902    g_free(buffer);
 903    g_free(buffer1);
 904    return err;
 905}
 906
 907static int local_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
 908{
 909    char *buffer;
 910    int ret = -1;
 911    char *path = fs_path->data;
 912
 913    if ((credp->fc_uid == -1 && credp->fc_gid == -1) ||
 914        (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
 915        (fs_ctx->export_flags & V9FS_SM_NONE)) {
 916        buffer = rpath(fs_ctx, path);
 917        ret = lchown(buffer, credp->fc_uid, credp->fc_gid);
 918        g_free(buffer);
 919    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
 920        buffer = rpath(fs_ctx, path);
 921        ret = local_set_xattr(buffer, credp);
 922        g_free(buffer);
 923    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
 924        return local_set_mapped_file_attr(fs_ctx, path, credp);
 925    }
 926    return ret;
 927}
 928
 929static int local_utimensat(FsContext *s, V9fsPath *fs_path,
 930                           const struct timespec *buf)
 931{
 932    char *buffer;
 933    int ret;
 934    char *path = fs_path->data;
 935
 936    buffer = rpath(s, path);
 937    ret = qemu_utimens(buffer, buf);
 938    g_free(buffer);
 939    return ret;
 940}
 941
 942static int local_remove(FsContext *ctx, const char *path)
 943{
 944    int err;
 945    struct stat stbuf;
 946    char *buffer;
 947
 948    if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
 949        buffer = rpath(ctx, path);
 950        err =  lstat(buffer, &stbuf);
 951        g_free(buffer);
 952        if (err) {
 953            goto err_out;
 954        }
 955        /*
 956         * If directory remove .virtfs_metadata contained in the
 957         * directory
 958         */
 959        if (S_ISDIR(stbuf.st_mode)) {
 960            buffer = g_strdup_printf("%s/%s/%s", ctx->fs_root,
 961                                     path, VIRTFS_META_DIR);
 962            err = remove(buffer);
 963            g_free(buffer);
 964            if (err < 0 && errno != ENOENT) {
 965                /*
 966                 * We didn't had the .virtfs_metadata file. May be file created
 967                 * in non-mapped mode ?. Ignore ENOENT.
 968                 */
 969                goto err_out;
 970            }
 971        }
 972        /*
 973         * Now remove the name from parent directory
 974         * .virtfs_metadata directory
 975         */
 976        buffer = local_mapped_attr_path(ctx, path);
 977        err = remove(buffer);
 978        g_free(buffer);
 979        if (err < 0 && errno != ENOENT) {
 980            /*
 981             * We didn't had the .virtfs_metadata file. May be file created
 982             * in non-mapped mode ?. Ignore ENOENT.
 983             */
 984            goto err_out;
 985        }
 986    }
 987
 988    buffer = rpath(ctx, path);
 989    err = remove(buffer);
 990    g_free(buffer);
 991err_out:
 992    return err;
 993}
 994
 995static int local_fsync(FsContext *ctx, int fid_type,
 996                       V9fsFidOpenState *fs, int datasync)
 997{
 998    int fd;
 999
1000    if (fid_type == P9_FID_DIR) {
1001        fd = dirfd(fs->dir);
1002    } else {
1003        fd = fs->fd;
1004    }
1005
1006    if (datasync) {
1007        return qemu_fdatasync(fd);
1008    } else {
1009        return fsync(fd);
1010    }
1011}
1012
1013static int local_statfs(FsContext *s, V9fsPath *fs_path, struct statfs *stbuf)
1014{
1015    char *buffer;
1016    int ret;
1017    char *path = fs_path->data;
1018
1019    buffer = rpath(s, path);
1020    ret = statfs(buffer, stbuf);
1021    g_free(buffer);
1022    return ret;
1023}
1024
1025static ssize_t local_lgetxattr(FsContext *ctx, V9fsPath *fs_path,
1026                               const char *name, void *value, size_t size)
1027{
1028    char *path = fs_path->data;
1029
1030    return v9fs_get_xattr(ctx, path, name, value, size);
1031}
1032
1033static ssize_t local_llistxattr(FsContext *ctx, V9fsPath *fs_path,
1034                                void *value, size_t size)
1035{
1036    char *path = fs_path->data;
1037
1038    return v9fs_list_xattr(ctx, path, value, size);
1039}
1040
1041static int local_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name,
1042                           void *value, size_t size, int flags)
1043{
1044    char *path = fs_path->data;
1045
1046    return v9fs_set_xattr(ctx, path, name, value, size, flags);
1047}
1048
1049static int local_lremovexattr(FsContext *ctx, V9fsPath *fs_path,
1050                              const char *name)
1051{
1052    char *path = fs_path->data;
1053
1054    return v9fs_remove_xattr(ctx, path, name);
1055}
1056
1057static int local_name_to_path(FsContext *ctx, V9fsPath *dir_path,
1058                              const char *name, V9fsPath *target)
1059{
1060    if (dir_path) {
1061        v9fs_string_sprintf((V9fsString *)target, "%s/%s",
1062                            dir_path->data, name);
1063    } else {
1064        v9fs_string_sprintf((V9fsString *)target, "%s", name);
1065    }
1066    /* Bump the size for including terminating NULL */
1067    target->size++;
1068    return 0;
1069}
1070
1071static int local_renameat(FsContext *ctx, V9fsPath *olddir,
1072                          const char *old_name, V9fsPath *newdir,
1073                          const char *new_name)
1074{
1075    int ret;
1076    V9fsString old_full_name, new_full_name;
1077
1078    v9fs_string_init(&old_full_name);
1079    v9fs_string_init(&new_full_name);
1080
1081    v9fs_string_sprintf(&old_full_name, "%s/%s", olddir->data, old_name);
1082    v9fs_string_sprintf(&new_full_name, "%s/%s", newdir->data, new_name);
1083
1084    ret = local_rename(ctx, old_full_name.data, new_full_name.data);
1085    v9fs_string_free(&old_full_name);
1086    v9fs_string_free(&new_full_name);
1087    return ret;
1088}
1089
1090static int local_unlinkat(FsContext *ctx, V9fsPath *dir,
1091                          const char *name, int flags)
1092{
1093    int ret;
1094    V9fsString fullname;
1095    char *buffer;
1096
1097    v9fs_string_init(&fullname);
1098
1099    v9fs_string_sprintf(&fullname, "%s/%s", dir->data, name);
1100    if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
1101        if (flags == AT_REMOVEDIR) {
1102            /*
1103             * If directory remove .virtfs_metadata contained in the
1104             * directory
1105             */
1106            buffer = g_strdup_printf("%s/%s/%s", ctx->fs_root,
1107                                     fullname.data, VIRTFS_META_DIR);
1108            ret = remove(buffer);
1109            g_free(buffer);
1110            if (ret < 0 && errno != ENOENT) {
1111                /*
1112                 * We didn't had the .virtfs_metadata file. May be file created
1113                 * in non-mapped mode ?. Ignore ENOENT.
1114                 */
1115                goto err_out;
1116            }
1117        }
1118        /*
1119         * Now remove the name from parent directory
1120         * .virtfs_metadata directory.
1121         */
1122        buffer = local_mapped_attr_path(ctx, fullname.data);
1123        ret = remove(buffer);
1124        g_free(buffer);
1125        if (ret < 0 && errno != ENOENT) {
1126            /*
1127             * We didn't had the .virtfs_metadata file. May be file created
1128             * in non-mapped mode ?. Ignore ENOENT.
1129             */
1130            goto err_out;
1131        }
1132    }
1133    /* Remove the name finally */
1134    buffer = rpath(ctx, fullname.data);
1135    ret = remove(buffer);
1136    g_free(buffer);
1137
1138err_out:
1139    v9fs_string_free(&fullname);
1140    return ret;
1141}
1142
1143static int local_ioc_getversion(FsContext *ctx, V9fsPath *path,
1144                                mode_t st_mode, uint64_t *st_gen)
1145{
1146#ifdef FS_IOC_GETVERSION
1147    int err;
1148    V9fsFidOpenState fid_open;
1149
1150    /*
1151     * Do not try to open special files like device nodes, fifos etc
1152     * We can get fd for regular files and directories only
1153     */
1154    if (!S_ISREG(st_mode) && !S_ISDIR(st_mode)) {
1155        errno = ENOTTY;
1156        return -1;
1157    }
1158    err = local_open(ctx, path, O_RDONLY, &fid_open);
1159    if (err < 0) {
1160        return err;
1161    }
1162    err = ioctl(fid_open.fd, FS_IOC_GETVERSION, st_gen);
1163    local_close(ctx, &fid_open);
1164    return err;
1165#else
1166    errno = ENOTTY;
1167    return -1;
1168#endif
1169}
1170
1171static int local_init(FsContext *ctx)
1172{
1173    int err = 0;
1174    struct statfs stbuf;
1175
1176    if (ctx->export_flags & V9FS_SM_PASSTHROUGH) {
1177        ctx->xops = passthrough_xattr_ops;
1178    } else if (ctx->export_flags & V9FS_SM_MAPPED) {
1179        ctx->xops = mapped_xattr_ops;
1180    } else if (ctx->export_flags & V9FS_SM_NONE) {
1181        ctx->xops = none_xattr_ops;
1182    } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
1183        /*
1184         * xattr operation for mapped-file and passthrough
1185         * remain same.
1186         */
1187        ctx->xops = passthrough_xattr_ops;
1188    }
1189    ctx->export_flags |= V9FS_PATHNAME_FSCONTEXT;
1190#ifdef FS_IOC_GETVERSION
1191    /*
1192     * use ioc_getversion only if the iocl is definied
1193     */
1194    err = statfs(ctx->fs_root, &stbuf);
1195    if (!err) {
1196        switch (stbuf.f_type) {
1197        case EXT2_SUPER_MAGIC:
1198        case BTRFS_SUPER_MAGIC:
1199        case REISERFS_SUPER_MAGIC:
1200        case XFS_SUPER_MAGIC:
1201            ctx->exops.get_st_gen = local_ioc_getversion;
1202            break;
1203        }
1204    }
1205#endif
1206    return err;
1207}
1208
1209static int local_parse_opts(QemuOpts *opts, struct FsDriverEntry *fse)
1210{
1211    const char *sec_model = qemu_opt_get(opts, "security_model");
1212    const char *path = qemu_opt_get(opts, "path");
1213
1214    if (!sec_model) {
1215        error_report("Security model not specified, local fs needs security model");
1216        error_printf("valid options are:"
1217                     "\tsecurity_model=[passthrough|mapped-xattr|mapped-file|none]\n");
1218        return -1;
1219    }
1220
1221    if (!strcmp(sec_model, "passthrough")) {
1222        fse->export_flags |= V9FS_SM_PASSTHROUGH;
1223    } else if (!strcmp(sec_model, "mapped") ||
1224               !strcmp(sec_model, "mapped-xattr")) {
1225        fse->export_flags |= V9FS_SM_MAPPED;
1226    } else if (!strcmp(sec_model, "none")) {
1227        fse->export_flags |= V9FS_SM_NONE;
1228    } else if (!strcmp(sec_model, "mapped-file")) {
1229        fse->export_flags |= V9FS_SM_MAPPED_FILE;
1230    } else {
1231        error_report("Invalid security model %s specified", sec_model);
1232        error_printf("valid options are:"
1233                     "\t[passthrough|mapped-xattr|mapped-file|none]\n");
1234        return -1;
1235    }
1236
1237    if (!path) {
1238        error_report("fsdev: No path specified");
1239        return -1;
1240    }
1241    fse->path = g_strdup(path);
1242
1243    return 0;
1244}
1245
1246FileOperations local_ops = {
1247    .parse_opts = local_parse_opts,
1248    .init  = local_init,
1249    .lstat = local_lstat,
1250    .readlink = local_readlink,
1251    .close = local_close,
1252    .closedir = local_closedir,
1253    .open = local_open,
1254    .opendir = local_opendir,
1255    .rewinddir = local_rewinddir,
1256    .telldir = local_telldir,
1257    .readdir_r = local_readdir_r,
1258    .seekdir = local_seekdir,
1259    .preadv = local_preadv,
1260    .pwritev = local_pwritev,
1261    .chmod = local_chmod,
1262    .mknod = local_mknod,
1263    .mkdir = local_mkdir,
1264    .fstat = local_fstat,
1265    .open2 = local_open2,
1266    .symlink = local_symlink,
1267    .link = local_link,
1268    .truncate = local_truncate,
1269    .rename = local_rename,
1270    .chown = local_chown,
1271    .utimensat = local_utimensat,
1272    .remove = local_remove,
1273    .fsync = local_fsync,
1274    .statfs = local_statfs,
1275    .lgetxattr = local_lgetxattr,
1276    .llistxattr = local_llistxattr,
1277    .lsetxattr = local_lsetxattr,
1278    .lremovexattr = local_lremovexattr,
1279    .name_to_path = local_name_to_path,
1280    .renameat  = local_renameat,
1281    .unlinkat = local_unlinkat,
1282};
1283