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#ifdef CONFIG_UTIMENSAT
 382    int fd;
 383    struct handle_data *data = (struct handle_data *)ctx->private;
 384
 385    fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK);
 386    if (fd < 0) {
 387        return fd;
 388    }
 389    ret = futimens(fd, buf);
 390    close(fd);
 391#else
 392    ret = -1;
 393    errno = ENOSYS;
 394#endif
 395    return ret;
 396}
 397
 398static int handle_remove(FsContext *ctx, const char *path)
 399{
 400    errno = EOPNOTSUPP;
 401    return -1;
 402}
 403
 404static int handle_fsync(FsContext *ctx, int fid_type,
 405                        V9fsFidOpenState *fs, int datasync)
 406{
 407    int fd;
 408
 409    if (fid_type == P9_FID_DIR) {
 410        fd = dirfd(fs->dir.stream);
 411    } else {
 412        fd = fs->fd;
 413    }
 414
 415    if (datasync) {
 416        return qemu_fdatasync(fd);
 417    } else {
 418        return fsync(fd);
 419    }
 420}
 421
 422static int handle_statfs(FsContext *ctx, V9fsPath *fs_path,
 423                         struct statfs *stbuf)
 424{
 425    int fd, ret;
 426    struct handle_data *data = (struct handle_data *)ctx->private;
 427
 428    fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK);
 429    if (fd < 0) {
 430        return fd;
 431    }
 432    ret = fstatfs(fd, stbuf);
 433    close(fd);
 434    return ret;
 435}
 436
 437static ssize_t handle_lgetxattr(FsContext *ctx, V9fsPath *fs_path,
 438                                const char *name, void *value, size_t size)
 439{
 440    int fd, ret;
 441    struct handle_data *data = (struct handle_data *)ctx->private;
 442
 443    fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK);
 444    if (fd < 0) {
 445        return fd;
 446    }
 447    ret = fgetxattr(fd, name, value, size);
 448    close(fd);
 449    return ret;
 450}
 451
 452static ssize_t handle_llistxattr(FsContext *ctx, V9fsPath *fs_path,
 453                                 void *value, size_t size)
 454{
 455    int fd, ret;
 456    struct handle_data *data = (struct handle_data *)ctx->private;
 457
 458    fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK);
 459    if (fd < 0) {
 460        return fd;
 461    }
 462    ret = flistxattr(fd, value, size);
 463    close(fd);
 464    return ret;
 465}
 466
 467static int handle_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name,
 468                            void *value, size_t size, int flags)
 469{
 470    int fd, ret;
 471    struct handle_data *data = (struct handle_data *)ctx->private;
 472
 473    fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK);
 474    if (fd < 0) {
 475        return fd;
 476    }
 477    ret = fsetxattr(fd, name, value, size, flags);
 478    close(fd);
 479    return ret;
 480}
 481
 482static int handle_lremovexattr(FsContext *ctx, V9fsPath *fs_path,
 483                               const char *name)
 484{
 485    int fd, ret;
 486    struct handle_data *data = (struct handle_data *)ctx->private;
 487
 488    fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK);
 489    if (fd < 0) {
 490        return fd;
 491    }
 492    ret = fremovexattr(fd, name);
 493    close(fd);
 494    return ret;
 495}
 496
 497static int handle_name_to_path(FsContext *ctx, V9fsPath *dir_path,
 498                              const char *name, V9fsPath *target)
 499{
 500    char *buffer;
 501    struct file_handle *fh;
 502    int dirfd, ret, mnt_id;
 503    struct handle_data *data = (struct handle_data *)ctx->private;
 504
 505    /* "." and ".." are not allowed */
 506    if (!strcmp(name, ".") || !strcmp(name, "..")) {
 507        errno = EINVAL;
 508        return -1;
 509
 510    }
 511    if (dir_path) {
 512        dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH);
 513    } else {
 514        /* relative to export root */
 515        buffer = rpath(ctx, ".");
 516        dirfd = open(buffer, O_DIRECTORY);
 517        g_free(buffer);
 518    }
 519    if (dirfd < 0) {
 520        return dirfd;
 521    }
 522    fh = g_malloc(sizeof(struct file_handle) + data->handle_bytes);
 523    fh->handle_bytes = data->handle_bytes;
 524    /* add a "./" at the beginning of the path */
 525    buffer = g_strdup_printf("./%s", name);
 526    /* flag = 0 imply don't follow symlink */
 527    ret = name_to_handle(dirfd, buffer, fh, &mnt_id, 0);
 528    if (!ret) {
 529        target->data = (char *)fh;
 530        target->size = sizeof(struct file_handle) + data->handle_bytes;
 531    } else {
 532        g_free(fh);
 533    }
 534    close(dirfd);
 535    g_free(buffer);
 536    return ret;
 537}
 538
 539static int handle_renameat(FsContext *ctx, V9fsPath *olddir,
 540                           const char *old_name, V9fsPath *newdir,
 541                           const char *new_name)
 542{
 543    int olddirfd, newdirfd, ret;
 544    struct handle_data *data = (struct handle_data *)ctx->private;
 545
 546    olddirfd = open_by_handle(data->mountfd, olddir->data, O_PATH);
 547    if (olddirfd < 0) {
 548        return olddirfd;
 549    }
 550    newdirfd = open_by_handle(data->mountfd, newdir->data, O_PATH);
 551    if (newdirfd < 0) {
 552        close(olddirfd);
 553        return newdirfd;
 554    }
 555    ret = renameat(olddirfd, old_name, newdirfd, new_name);
 556    close(newdirfd);
 557    close(olddirfd);
 558    return ret;
 559}
 560
 561static int handle_unlinkat(FsContext *ctx, V9fsPath *dir,
 562                           const char *name, int flags)
 563{
 564    int dirfd, ret;
 565    struct handle_data *data = (struct handle_data *)ctx->private;
 566    int rflags;
 567
 568    dirfd = open_by_handle(data->mountfd, dir->data, O_PATH);
 569    if (dirfd < 0) {
 570        return dirfd;
 571    }
 572
 573    rflags = 0;
 574    if (flags & P9_DOTL_AT_REMOVEDIR) {
 575        rflags |= AT_REMOVEDIR;
 576    }
 577
 578    ret = unlinkat(dirfd, name, rflags);
 579
 580    close(dirfd);
 581    return ret;
 582}
 583
 584static int handle_ioc_getversion(FsContext *ctx, V9fsPath *path,
 585                                 mode_t st_mode, uint64_t *st_gen)
 586{
 587#ifdef FS_IOC_GETVERSION
 588    int err;
 589    V9fsFidOpenState fid_open;
 590
 591    /*
 592     * Do not try to open special files like device nodes, fifos etc
 593     * We can get fd for regular files and directories only
 594     */
 595    if (!S_ISREG(st_mode) && !S_ISDIR(st_mode)) {
 596        errno = ENOTTY;
 597        return -1;
 598    }
 599    err = handle_open(ctx, path, O_RDONLY, &fid_open);
 600    if (err < 0) {
 601        return err;
 602    }
 603    err = ioctl(fid_open.fd, FS_IOC_GETVERSION, st_gen);
 604    handle_close(ctx, &fid_open);
 605    return err;
 606#else
 607    errno = ENOTTY;
 608    return -1;
 609#endif
 610}
 611
 612static int handle_init(FsContext *ctx)
 613{
 614    int ret, mnt_id;
 615    struct statfs stbuf;
 616    struct file_handle fh;
 617    struct handle_data *data = g_malloc(sizeof(struct handle_data));
 618
 619    data->mountfd = open(ctx->fs_root, O_DIRECTORY);
 620    if (data->mountfd < 0) {
 621        ret = data->mountfd;
 622        goto err_out;
 623    }
 624    ret = statfs(ctx->fs_root, &stbuf);
 625    if (!ret) {
 626        switch (stbuf.f_type) {
 627        case EXT2_SUPER_MAGIC:
 628        case BTRFS_SUPER_MAGIC:
 629        case REISERFS_SUPER_MAGIC:
 630        case XFS_SUPER_MAGIC:
 631            ctx->exops.get_st_gen = handle_ioc_getversion;
 632            break;
 633        }
 634    }
 635    memset(&fh, 0, sizeof(struct file_handle));
 636    ret = name_to_handle(data->mountfd, ".", &fh, &mnt_id, 0);
 637    if (ret && errno == EOVERFLOW) {
 638        data->handle_bytes = fh.handle_bytes;
 639        ctx->private = data;
 640        ret = 0;
 641        goto out;
 642    }
 643    /* we got 0 byte handle ? */
 644    ret = -1;
 645    close(data->mountfd);
 646err_out:
 647    g_free(data);
 648out:
 649    return ret;
 650}
 651
 652static void handle_cleanup(FsContext *ctx)
 653{
 654    struct handle_data *data = ctx->private;
 655
 656    close(data->mountfd);
 657    g_free(data);
 658}
 659
 660static int handle_parse_opts(QemuOpts *opts, struct FsDriverEntry *fse)
 661{
 662    const char *sec_model = qemu_opt_get(opts, "security_model");
 663    const char *path = qemu_opt_get(opts, "path");
 664
 665    if (sec_model) {
 666        error_report("Invalid argument security_model specified with handle fsdriver");
 667        return -1;
 668    }
 669
 670    if (!path) {
 671        error_report("fsdev: No path specified");
 672        return -1;
 673    }
 674    fse->path = g_strdup(path);
 675    return 0;
 676
 677}
 678
 679FileOperations handle_ops = {
 680    .parse_opts   = handle_parse_opts,
 681    .init         = handle_init,
 682    .cleanup      = handle_cleanup,
 683    .lstat        = handle_lstat,
 684    .readlink     = handle_readlink,
 685    .close        = handle_close,
 686    .closedir     = handle_closedir,
 687    .open         = handle_open,
 688    .opendir      = handle_opendir,
 689    .rewinddir    = handle_rewinddir,
 690    .telldir      = handle_telldir,
 691    .readdir      = handle_readdir,
 692    .seekdir      = handle_seekdir,
 693    .preadv       = handle_preadv,
 694    .pwritev      = handle_pwritev,
 695    .chmod        = handle_chmod,
 696    .mknod        = handle_mknod,
 697    .mkdir        = handle_mkdir,
 698    .fstat        = handle_fstat,
 699    .open2        = handle_open2,
 700    .symlink      = handle_symlink,
 701    .link         = handle_link,
 702    .truncate     = handle_truncate,
 703    .rename       = handle_rename,
 704    .chown        = handle_chown,
 705    .utimensat    = handle_utimensat,
 706    .remove       = handle_remove,
 707    .fsync        = handle_fsync,
 708    .statfs       = handle_statfs,
 709    .lgetxattr    = handle_lgetxattr,
 710    .llistxattr   = handle_llistxattr,
 711    .lsetxattr    = handle_lsetxattr,
 712    .lremovexattr = handle_lremovexattr,
 713    .name_to_path = handle_name_to_path,
 714    .renameat     = handle_renameat,
 715    .unlinkat     = handle_unlinkat,
 716};
 717