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