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