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