qemu/hw/9pfs/virtio-9p-handle.c
<<
>>
Prefs
   1/*
   2 * Virtio 9p handle callback
   3 *
   4 * Copyright IBM, Corp. 2011
   5 *
   6 * Authors:
   7 *    Aneesh Kumar K.V <aneesh.kumar@linux.vnet.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 <unistd.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
  43struct handle_data {
  44    int mountfd;
  45    int handle_bytes;
  46};
  47
  48static inline int name_to_handle(int dirfd, const char *name,
  49                                 struct file_handle *fh, int *mnt_id, int flags)
  50{
  51    return name_to_handle_at(dirfd, name, fh, mnt_id, flags);
  52}
  53
  54static inline int open_by_handle(int mountfd, const char *fh, int flags)
  55{
  56    return open_by_handle_at(mountfd, (struct file_handle *)fh, flags);
  57}
  58
  59static int handle_update_file_cred(int dirfd, const char *name, FsCred *credp)
  60{
  61    int fd, ret;
  62    fd = openat(dirfd, name, O_NONBLOCK | O_NOFOLLOW);
  63    if (fd < 0) {
  64        return fd;
  65    }
  66    ret = fchownat(fd, "", credp->fc_uid, credp->fc_gid, AT_EMPTY_PATH);
  67    if (ret < 0) {
  68        goto err_out;
  69    }
  70    ret = fchmod(fd, credp->fc_mode & 07777);
  71err_out:
  72    close(fd);
  73    return ret;
  74}
  75
  76
  77static int handle_lstat(FsContext *fs_ctx, V9fsPath *fs_path,
  78                        struct stat *stbuf)
  79{
  80    int fd, ret;
  81    struct handle_data *data = (struct handle_data *)fs_ctx->private;
  82
  83    fd = open_by_handle(data->mountfd, fs_path->data, O_PATH);
  84    if (fd < 0) {
  85        return fd;
  86    }
  87    ret = fstatat(fd, "", stbuf, AT_EMPTY_PATH);
  88    close(fd);
  89    return ret;
  90}
  91
  92static ssize_t handle_readlink(FsContext *fs_ctx, V9fsPath *fs_path,
  93                               char *buf, size_t bufsz)
  94{
  95    int fd, ret;
  96    struct handle_data *data = (struct handle_data *)fs_ctx->private;
  97
  98    fd = open_by_handle(data->mountfd, fs_path->data, O_PATH);
  99    if (fd < 0) {
 100        return fd;
 101    }
 102    ret = readlinkat(fd, "", buf, bufsz);
 103    close(fd);
 104    return ret;
 105}
 106
 107static int handle_close(FsContext *ctx, V9fsFidOpenState *fs)
 108{
 109    return close(fs->fd);
 110}
 111
 112static int handle_closedir(FsContext *ctx, V9fsFidOpenState *fs)
 113{
 114    return closedir(fs->dir);
 115}
 116
 117static int handle_open(FsContext *ctx, V9fsPath *fs_path,
 118                       int flags, V9fsFidOpenState *fs)
 119{
 120    struct handle_data *data = (struct handle_data *)ctx->private;
 121
 122    fs->fd = open_by_handle(data->mountfd, fs_path->data, flags);
 123    return fs->fd;
 124}
 125
 126static int handle_opendir(FsContext *ctx,
 127                          V9fsPath *fs_path, V9fsFidOpenState *fs)
 128{
 129    int ret;
 130    ret = handle_open(ctx, fs_path, O_DIRECTORY, fs);
 131    if (ret < 0) {
 132        return -1;
 133    }
 134    fs->dir = fdopendir(ret);
 135    if (!fs->dir) {
 136        return -1;
 137    }
 138    return 0;
 139}
 140
 141static void handle_rewinddir(FsContext *ctx, V9fsFidOpenState *fs)
 142{
 143    return rewinddir(fs->dir);
 144}
 145
 146static off_t handle_telldir(FsContext *ctx, V9fsFidOpenState *fs)
 147{
 148    return telldir(fs->dir);
 149}
 150
 151static int handle_readdir_r(FsContext *ctx, V9fsFidOpenState *fs,
 152                            struct dirent *entry,
 153                            struct dirent **result)
 154{
 155    return readdir_r(fs->dir, entry, result);
 156}
 157
 158static void handle_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off)
 159{
 160    return seekdir(fs->dir, off);
 161}
 162
 163static ssize_t handle_preadv(FsContext *ctx, V9fsFidOpenState *fs,
 164                             const struct iovec *iov,
 165                             int iovcnt, off_t offset)
 166{
 167#ifdef CONFIG_PREADV
 168    return preadv(fs->fd, iov, iovcnt, offset);
 169#else
 170    int err = lseek(fs->fd, offset, SEEK_SET);
 171    if (err == -1) {
 172        return err;
 173    } else {
 174        return readv(fs->fd, iov, iovcnt);
 175    }
 176#endif
 177}
 178
 179static ssize_t handle_pwritev(FsContext *ctx, V9fsFidOpenState *fs,
 180                              const struct iovec *iov,
 181                              int iovcnt, off_t offset)
 182{
 183    ssize_t ret;
 184#ifdef CONFIG_PREADV
 185    ret = pwritev(fs->fd, iov, iovcnt, offset);
 186#else
 187    int err = lseek(fs->fd, offset, SEEK_SET);
 188    if (err == -1) {
 189        return err;
 190    } else {
 191        ret = writev(fs->fd, iov, iovcnt);
 192    }
 193#endif
 194#ifdef CONFIG_SYNC_FILE_RANGE
 195    if (ret > 0 && ctx->export_flags & V9FS_IMMEDIATE_WRITEOUT) {
 196        /*
 197         * Initiate a writeback. This is not a data integrity sync.
 198         * We want to ensure that we don't leave dirty pages in the cache
 199         * after write when writeout=immediate is sepcified.
 200         */
 201        sync_file_range(fs->fd, offset, ret,
 202                        SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE);
 203    }
 204#endif
 205    return ret;
 206}
 207
 208static int handle_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
 209{
 210    int fd, ret;
 211    struct handle_data *data = (struct handle_data *)fs_ctx->private;
 212
 213    fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK);
 214    if (fd < 0) {
 215        return fd;
 216    }
 217    ret = fchmod(fd, credp->fc_mode);
 218    close(fd);
 219    return ret;
 220}
 221
 222static int handle_mknod(FsContext *fs_ctx, V9fsPath *dir_path,
 223                       const char *name, FsCred *credp)
 224{
 225    int dirfd, ret;
 226    struct handle_data *data = (struct handle_data *)fs_ctx->private;
 227
 228    dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH);
 229    if (dirfd < 0) {
 230        return dirfd;
 231    }
 232    ret = mknodat(dirfd, name, credp->fc_mode, credp->fc_rdev);
 233    if (!ret) {
 234        ret = handle_update_file_cred(dirfd, name, credp);
 235    }
 236    close(dirfd);
 237    return ret;
 238}
 239
 240static int handle_mkdir(FsContext *fs_ctx, V9fsPath *dir_path,
 241                       const char *name, FsCred *credp)
 242{
 243    int dirfd, ret;
 244    struct handle_data *data = (struct handle_data *)fs_ctx->private;
 245
 246    dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH);
 247    if (dirfd < 0) {
 248        return dirfd;
 249    }
 250    ret = mkdirat(dirfd, name, credp->fc_mode);
 251    if (!ret) {
 252        ret = handle_update_file_cred(dirfd, name, credp);
 253    }
 254    close(dirfd);
 255    return ret;
 256}
 257
 258static int handle_fstat(FsContext *fs_ctx, int fid_type,
 259                        V9fsFidOpenState *fs, struct stat *stbuf)
 260{
 261    int fd;
 262
 263    if (fid_type == P9_FID_DIR) {
 264        fd = dirfd(fs->dir);
 265    } else {
 266        fd = fs->fd;
 267    }
 268    return fstat(fd, stbuf);
 269}
 270
 271static int handle_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
 272                        int flags, FsCred *credp, V9fsFidOpenState *fs)
 273{
 274    int ret;
 275    int dirfd, fd;
 276    struct handle_data *data = (struct handle_data *)fs_ctx->private;
 277
 278    dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH);
 279    if (dirfd < 0) {
 280        return dirfd;
 281    }
 282    fd = openat(dirfd, name, flags | O_NOFOLLOW, credp->fc_mode);
 283    if (fd >= 0) {
 284        ret = handle_update_file_cred(dirfd, name, credp);
 285        if (ret < 0) {
 286            close(fd);
 287            fd = ret;
 288        } else {
 289            fs->fd = fd;
 290        }
 291    }
 292    close(dirfd);
 293    return fd;
 294}
 295
 296
 297static int handle_symlink(FsContext *fs_ctx, const char *oldpath,
 298                          V9fsPath *dir_path, const char *name, FsCred *credp)
 299{
 300    int fd, dirfd, ret;
 301    struct handle_data *data = (struct handle_data *)fs_ctx->private;
 302
 303    dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH);
 304    if (dirfd < 0) {
 305        return dirfd;
 306    }
 307    ret = symlinkat(oldpath, dirfd, name);
 308    if (!ret) {
 309        fd = openat(dirfd, name, O_PATH | O_NOFOLLOW);
 310        if (fd < 0) {
 311            ret = fd;
 312            goto err_out;
 313        }
 314        ret = fchownat(fd, "", credp->fc_uid, credp->fc_gid, AT_EMPTY_PATH);
 315        close(fd);
 316    }
 317err_out:
 318    close(dirfd);
 319    return ret;
 320}
 321
 322static int handle_link(FsContext *ctx, V9fsPath *oldpath,
 323                       V9fsPath *dirpath, const char *name)
 324{
 325    int oldfd, newdirfd, ret;
 326    struct handle_data *data = (struct handle_data *)ctx->private;
 327
 328    oldfd = open_by_handle(data->mountfd, oldpath->data, O_PATH);
 329    if (oldfd < 0) {
 330        return oldfd;
 331    }
 332    newdirfd = open_by_handle(data->mountfd, dirpath->data, O_PATH);
 333    if (newdirfd < 0) {
 334        close(oldfd);
 335        return newdirfd;
 336    }
 337    ret = linkat(oldfd, "", newdirfd, name, AT_EMPTY_PATH);
 338    close(newdirfd);
 339    close(oldfd);
 340    return ret;
 341}
 342
 343static int handle_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size)
 344{
 345    int fd, ret;
 346    struct handle_data *data = (struct handle_data *)ctx->private;
 347
 348    fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK | O_WRONLY);
 349    if (fd < 0) {
 350        return fd;
 351    }
 352    ret = ftruncate(fd, size);
 353    close(fd);
 354    return ret;
 355}
 356
 357static int handle_rename(FsContext *ctx, const char *oldpath,
 358                         const char *newpath)
 359{
 360    errno = EOPNOTSUPP;
 361    return -1;
 362}
 363
 364static int handle_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
 365{
 366    int fd, ret;
 367    struct handle_data *data = (struct handle_data *)fs_ctx->private;
 368
 369    fd = open_by_handle(data->mountfd, fs_path->data, O_PATH);
 370    if (fd < 0) {
 371        return fd;
 372    }
 373    ret = fchownat(fd, "", credp->fc_uid, credp->fc_gid, AT_EMPTY_PATH);
 374    close(fd);
 375    return ret;
 376}
 377
 378static int handle_utimensat(FsContext *ctx, V9fsPath *fs_path,
 379                            const struct timespec *buf)
 380{
 381    int ret;
 382#ifdef CONFIG_UTIMENSAT
 383    int fd;
 384    struct handle_data *data = (struct handle_data *)ctx->private;
 385
 386    fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK);
 387    if (fd < 0) {
 388        return fd;
 389    }
 390    ret = futimens(fd, buf);
 391    close(fd);
 392#else
 393    ret = -1;
 394    errno = ENOSYS;
 395#endif
 396    return ret;
 397}
 398
 399static int handle_remove(FsContext *ctx, const char *path)
 400{
 401    errno = EOPNOTSUPP;
 402    return -1;
 403}
 404
 405static int handle_fsync(FsContext *ctx, int fid_type,
 406                        V9fsFidOpenState *fs, int datasync)
 407{
 408    int fd;
 409
 410    if (fid_type == P9_FID_DIR) {
 411        fd = dirfd(fs->dir);
 412    } else {
 413        fd = fs->fd;
 414    }
 415
 416    if (datasync) {
 417        return qemu_fdatasync(fd);
 418    } else {
 419        return fsync(fd);
 420    }
 421}
 422
 423static int handle_statfs(FsContext *ctx, V9fsPath *fs_path,
 424                         struct statfs *stbuf)
 425{
 426    int fd, ret;
 427    struct handle_data *data = (struct handle_data *)ctx->private;
 428
 429    fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK);
 430    if (fd < 0) {
 431        return fd;
 432    }
 433    ret = fstatfs(fd, stbuf);
 434    close(fd);
 435    return ret;
 436}
 437
 438static ssize_t handle_lgetxattr(FsContext *ctx, V9fsPath *fs_path,
 439                                const char *name, void *value, size_t size)
 440{
 441    int fd, ret;
 442    struct handle_data *data = (struct handle_data *)ctx->private;
 443
 444    fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK);
 445    if (fd < 0) {
 446        return fd;
 447    }
 448    ret = fgetxattr(fd, name, value, size);
 449    close(fd);
 450    return ret;
 451}
 452
 453static ssize_t handle_llistxattr(FsContext *ctx, V9fsPath *fs_path,
 454                                 void *value, size_t size)
 455{
 456    int fd, ret;
 457    struct handle_data *data = (struct handle_data *)ctx->private;
 458
 459    fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK);
 460    if (fd < 0) {
 461        return fd;
 462    }
 463    ret = flistxattr(fd, value, size);
 464    close(fd);
 465    return ret;
 466}
 467
 468static int handle_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name,
 469                            void *value, size_t size, int flags)
 470{
 471    int fd, ret;
 472    struct handle_data *data = (struct handle_data *)ctx->private;
 473
 474    fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK);
 475    if (fd < 0) {
 476        return fd;
 477    }
 478    ret = fsetxattr(fd, name, value, size, flags);
 479    close(fd);
 480    return ret;
 481}
 482
 483static int handle_lremovexattr(FsContext *ctx, V9fsPath *fs_path,
 484                               const char *name)
 485{
 486    int fd, ret;
 487    struct handle_data *data = (struct handle_data *)ctx->private;
 488
 489    fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK);
 490    if (fd < 0) {
 491        return fd;
 492    }
 493    ret = fremovexattr(fd, name);
 494    close(fd);
 495    return ret;
 496}
 497
 498static int handle_name_to_path(FsContext *ctx, V9fsPath *dir_path,
 499                              const char *name, V9fsPath *target)
 500{
 501    char buffer[PATH_MAX];
 502    struct file_handle *fh;
 503    int dirfd, ret, mnt_id;
 504    struct handle_data *data = (struct handle_data *)ctx->private;
 505
 506    /* "." and ".." are not allowed */
 507    if (!strcmp(name, ".") || !strcmp(name, "..")) {
 508        errno = EINVAL;
 509        return -1;
 510
 511    }
 512    if (dir_path) {
 513        dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH);
 514    } else {
 515        /* relative to export root */
 516        dirfd = open(rpath(ctx, ".", buffer), O_DIRECTORY);
 517    }
 518    if (dirfd < 0) {
 519        return dirfd;
 520    }
 521    fh = g_malloc(sizeof(struct file_handle) + data->handle_bytes);
 522    fh->handle_bytes = data->handle_bytes;
 523    /* add a "./" at the beginning of the path */
 524    snprintf(buffer, PATH_MAX, "./%s", name);
 525    /* flag = 0 imply don't follow symlink */
 526    ret = name_to_handle(dirfd, buffer, fh, &mnt_id, 0);
 527    if (!ret) {
 528        target->data = (char *)fh;
 529        target->size = sizeof(struct file_handle) + data->handle_bytes;
 530    } else {
 531        g_free(fh);
 532    }
 533    close(dirfd);
 534    return ret;
 535}
 536
 537static int handle_renameat(FsContext *ctx, V9fsPath *olddir,
 538                           const char *old_name, V9fsPath *newdir,
 539                           const char *new_name)
 540{
 541    int olddirfd, newdirfd, ret;
 542    struct handle_data *data = (struct handle_data *)ctx->private;
 543
 544    olddirfd = open_by_handle(data->mountfd, olddir->data, O_PATH);
 545    if (olddirfd < 0) {
 546        return olddirfd;
 547    }
 548    newdirfd = open_by_handle(data->mountfd, newdir->data, O_PATH);
 549    if (newdirfd < 0) {
 550        close(olddirfd);
 551        return newdirfd;
 552    }
 553    ret = renameat(olddirfd, old_name, newdirfd, new_name);
 554    close(newdirfd);
 555    close(olddirfd);
 556    return ret;
 557}
 558
 559static int handle_unlinkat(FsContext *ctx, V9fsPath *dir,
 560                           const char *name, int flags)
 561{
 562    int dirfd, ret;
 563    struct handle_data *data = (struct handle_data *)ctx->private;
 564    int rflags;
 565
 566    dirfd = open_by_handle(data->mountfd, dir->data, O_PATH);
 567    if (dirfd < 0) {
 568        return dirfd;
 569    }
 570
 571    rflags = 0;
 572    if (flags & P9_DOTL_AT_REMOVEDIR) {
 573        rflags |= AT_REMOVEDIR;
 574    }
 575
 576    ret = unlinkat(dirfd, name, rflags);
 577
 578    close(dirfd);
 579    return ret;
 580}
 581
 582static int handle_ioc_getversion(FsContext *ctx, V9fsPath *path,
 583                                 mode_t st_mode, uint64_t *st_gen)
 584{
 585    int err;
 586    V9fsFidOpenState fid_open;
 587
 588    /*
 589     * Do not try to open special files like device nodes, fifos etc
 590     * We can get fd for regular files and directories only
 591     */
 592    if (!S_ISREG(st_mode) && !S_ISDIR(st_mode)) {
 593            return 0;
 594    }
 595    err = handle_open(ctx, path, O_RDONLY, &fid_open);
 596    if (err < 0) {
 597        return err;
 598    }
 599    err = ioctl(fid_open.fd, FS_IOC_GETVERSION, st_gen);
 600    handle_close(ctx, &fid_open);
 601    return err;
 602}
 603
 604static int handle_init(FsContext *ctx)
 605{
 606    int ret, mnt_id;
 607    struct statfs stbuf;
 608    struct file_handle fh;
 609    struct handle_data *data = g_malloc(sizeof(struct handle_data));
 610
 611    data->mountfd = open(ctx->fs_root, O_DIRECTORY);
 612    if (data->mountfd < 0) {
 613        ret = data->mountfd;
 614        goto err_out;
 615    }
 616    ret = statfs(ctx->fs_root, &stbuf);
 617    if (!ret) {
 618        switch (stbuf.f_type) {
 619        case EXT2_SUPER_MAGIC:
 620        case BTRFS_SUPER_MAGIC:
 621        case REISERFS_SUPER_MAGIC:
 622        case XFS_SUPER_MAGIC:
 623            ctx->exops.get_st_gen = handle_ioc_getversion;
 624            break;
 625        }
 626    }
 627    memset(&fh, 0, sizeof(struct file_handle));
 628    ret = name_to_handle(data->mountfd, ".", &fh, &mnt_id, 0);
 629    if (ret && errno == EOVERFLOW) {
 630        data->handle_bytes = fh.handle_bytes;
 631        ctx->private = data;
 632        ret = 0;
 633        goto out;
 634    }
 635    /* we got 0 byte handle ? */
 636    ret = -1;
 637    close(data->mountfd);
 638err_out:
 639    g_free(data);
 640out:
 641    return ret;
 642}
 643
 644static int handle_parse_opts(QemuOpts *opts, struct FsDriverEntry *fse)
 645{
 646    const char *sec_model = qemu_opt_get(opts, "security_model");
 647    const char *path = qemu_opt_get(opts, "path");
 648
 649    if (sec_model) {
 650        fprintf(stderr, "Invalid argument security_model specified with handle fsdriver\n");
 651        return -1;
 652    }
 653
 654    if (!path) {
 655        fprintf(stderr, "fsdev: No path specified.\n");
 656        return -1;
 657    }
 658    fse->path = g_strdup(path);
 659    return 0;
 660
 661}
 662
 663FileOperations handle_ops = {
 664    .parse_opts   = handle_parse_opts,
 665    .init         = handle_init,
 666    .lstat        = handle_lstat,
 667    .readlink     = handle_readlink,
 668    .close        = handle_close,
 669    .closedir     = handle_closedir,
 670    .open         = handle_open,
 671    .opendir      = handle_opendir,
 672    .rewinddir    = handle_rewinddir,
 673    .telldir      = handle_telldir,
 674    .readdir_r    = handle_readdir_r,
 675    .seekdir      = handle_seekdir,
 676    .preadv       = handle_preadv,
 677    .pwritev      = handle_pwritev,
 678    .chmod        = handle_chmod,
 679    .mknod        = handle_mknod,
 680    .mkdir        = handle_mkdir,
 681    .fstat        = handle_fstat,
 682    .open2        = handle_open2,
 683    .symlink      = handle_symlink,
 684    .link         = handle_link,
 685    .truncate     = handle_truncate,
 686    .rename       = handle_rename,
 687    .chown        = handle_chown,
 688    .utimensat    = handle_utimensat,
 689    .remove       = handle_remove,
 690    .fsync        = handle_fsync,
 691    .statfs       = handle_statfs,
 692    .lgetxattr    = handle_lgetxattr,
 693    .llistxattr   = handle_llistxattr,
 694    .lsetxattr    = handle_lsetxattr,
 695    .lremovexattr = handle_lremovexattr,
 696    .name_to_path = handle_name_to_path,
 697    .renameat     = handle_renameat,
 698    .unlinkat     = handle_unlinkat,
 699};
 700