PageRenderTime 157ms CodeModel.GetById 3ms app.highlight 133ms RepoModel.GetById 1ms app.codeStats 1ms

/core/10.5/fusefs/fuse_vnops.c

http://macfuse.googlecode.com/
C | 3792 lines | 2329 code | 703 blank | 760 comment | 498 complexity | 9ac337dac621a0b2874afb66ce10d9ae MD5 | raw file
   1/*
   2 * Copyright (C) 2006-2008 Google. All Rights Reserved.
   3 * Amit Singh <singh@>
   4 */
   5
   6#include <sys/param.h>
   7#include <kern/assert.h>
   8#include <libkern/libkern.h>
   9#include <libkern/OSMalloc.h>
  10#include <libkern/locks.h>
  11#include <mach/mach_types.h>
  12#include <sys/dirent.h>
  13#include <sys/disk.h>
  14#include <sys/errno.h>
  15#include <sys/fcntl.h>
  16#include <sys/kernel_types.h>
  17#include <sys/mount.h>
  18#include <sys/proc.h>
  19#include <sys/stat.h>
  20#include <sys/ubc.h>
  21#include <sys/unistd.h>
  22#include <sys/vnode.h>
  23#include <sys/vnode_if.h>
  24#include <sys/xattr.h>
  25#include <sys/buf.h>
  26#include <sys/namei.h>
  27#include <sys/mman.h>
  28#include <vfs/vfs_support.h>
  29
  30#include "fuse.h"
  31#include "fuse_file.h"
  32#include "fuse_internal.h"
  33#include <fuse_ioctl.h>
  34#include "fuse_ipc.h"
  35#include "fuse_kludges.h"
  36#include "fuse_knote.h"
  37#include "fuse_locking.h"
  38#include "fuse_node.h"
  39#include "fuse_nodehash.h"
  40#include <fuse_param.h>
  41#include "fuse_sysctl.h"
  42#include "fuse_vnops.h"
  43
  44/*
  45    struct vnop_access_args {
  46        struct vnodeop_desc *a_desc;
  47        vnode_t              a_vp;
  48        int                  a_action;
  49        vfs_context_t        a_context;
  50    };
  51*/
  52FUSE_VNOP_EXPORT
  53int
  54fuse_vnop_access(struct vnop_access_args *ap)
  55{
  56    vnode_t       vp      = ap->a_vp;
  57    int           action  = ap->a_action;
  58    vfs_context_t context = ap->a_context;
  59
  60    struct fuse_access_param facp;
  61    struct fuse_vnode_data *fvdat = VTOFUD(vp);
  62    struct fuse_data *data = fuse_get_mpdata(vnode_mount(vp));
  63
  64    fuse_trace_printf_vnop();
  65
  66    if (fuse_isdeadfs(vp)) {
  67        if (vnode_isvroot(vp)) {
  68            return 0;
  69        } else {
  70            return EBADF;
  71        }
  72    }
  73
  74    if (!(data->dataflags & FSESS_INITED)) {
  75        if (vnode_isvroot(vp)) {
  76            if (fuse_vfs_context_issuser(context) ||
  77               (fuse_match_cred(data->daemoncred,
  78                                vfs_context_ucred(context)) == 0)) {
  79                return 0;
  80            }
  81        }
  82        return EBADF;
  83    }
  84
  85    if (vnode_islnk(vp)) {
  86        return 0;
  87    }
  88
  89    bzero(&facp, sizeof(facp));
  90
  91    if (fvdat->flag & FN_ACCESS_NOOP) {
  92        fvdat->flag &= ~FN_ACCESS_NOOP;
  93    } else {
  94        facp.facc_flags |= FACCESS_DO_ACCESS;
  95    }   
  96
  97    facp.facc_flags |= FACCESS_FROM_VNOP;
  98
  99    return fuse_internal_access(vp, action, context, &facp);
 100}       
 101
 102/*
 103    struct vnop_blktooff_args {
 104        struct vnodeop_desc *a_desc;
 105        vnode_t              a_vp;
 106        daddr64_t            a_lblkno;
 107        off_t               *a_offset;
 108    };
 109*/
 110FUSE_VNOP_EXPORT
 111int
 112fuse_vnop_blktooff(struct vnop_blktooff_args *ap)
 113{       
 114    vnode_t    vp        = ap->a_vp;
 115    daddr64_t  lblkno    = ap->a_lblkno;
 116    off_t     *offsetPtr = ap->a_offset;
 117
 118    struct fuse_data *data; 
 119
 120    fuse_trace_printf_vnop();
 121
 122    if (fuse_isdeadfs(vp)) {
 123        return EIO;
 124    }
 125
 126    data = fuse_get_mpdata(vnode_mount(vp));
 127
 128    *offsetPtr = lblkno * (off_t)(data->blocksize);
 129
 130    return 0;
 131}
 132
 133/*
 134    struct vnop_blockmap_args {
 135        struct vnodeop_desc *a_desc;
 136        vnode_t              a_vp;
 137        off_t                a_foffset;
 138        size_t               a_size;
 139        daddr64_t           *a_bpn;
 140        size_t              *a_run;
 141        void                *a_poff;
 142        int                  a_flags;
 143        vfs_context_t        a_context;
 144    };
 145*/
 146FUSE_VNOP_EXPORT
 147int
 148fuse_vnop_blockmap(struct vnop_blockmap_args *ap)
 149{
 150    vnode_t       vp      = ap->a_vp;
 151    off_t         foffset = ap->a_foffset;
 152    size_t        size    = ap->a_size;
 153    daddr64_t    *bpnPtr  = ap->a_bpn;
 154    size_t       *runPtr  = ap->a_run;
 155    int          *poffPtr = (int *)ap->a_poff;
 156
 157    /* Ignoring flags and context */
 158
 159    struct fuse_vnode_data *fvdat;
 160    struct fuse_data       *data;
 161
 162    off_t contiguousPhysicalBytes;
 163
 164    fuse_trace_printf_vnop();
 165
 166    if (fuse_isdeadfs(vp)) {
 167        return EIO;
 168    }
 169
 170    if (vnode_isdir(vp)) {
 171        return ENOTSUP;
 172    }
 173
 174    if (ap->a_bpn == NULL) {
 175        return 0;
 176    }
 177
 178    fvdat = VTOFUD(vp);
 179    data = fuse_get_mpdata(vnode_mount(vp));
 180
 181    /*
 182     * We could assert that:
 183     *
 184     * (foffset % data->blocksize) == 0
 185     * (foffset < fvdat->filesize)
 186     * (size % data->blocksize) == 0)
 187     */
 188
 189    *bpnPtr = foffset / data->blocksize;
 190
 191    contiguousPhysicalBytes = \
 192        fvdat->filesize - (off_t)(*bpnPtr * data->blocksize);
 193
 194    /* contiguousPhysicalBytes cannot really be negative (could assert) */
 195
 196    if (contiguousPhysicalBytes > (off_t)size) {
 197        contiguousPhysicalBytes = (off_t)size;
 198    }
 199
 200    if (runPtr != NULL) {
 201        *runPtr = (size_t)contiguousPhysicalBytes;
 202    }
 203
 204    if (poffPtr != NULL) {
 205        *poffPtr = 0;
 206    }
 207
 208    return 0;
 209}
 210
 211/*
 212    struct vnop_close_args {
 213        struct vnodeop_desc *a_desc;
 214        vnode_t              a_vp;
 215        int                  a_fflag;
 216        vfs_context_t        a_context;
 217    };
 218*/
 219FUSE_VNOP_EXPORT
 220int
 221fuse_vnop_close(struct vnop_close_args *ap)
 222{
 223    vnode_t       vp      = ap->a_vp;
 224    int           fflag   = ap->a_fflag;
 225    vfs_context_t context = ap->a_context;
 226
 227    int err   = 0;
 228    int isdir = (vnode_isdir(vp)) ? 1 : 0;
 229
 230    fufh_type_t fufh_type;
 231    struct fuse_data *data;
 232
 233    struct fuse_vnode_data *fvdat = VTOFUD(vp);
 234    struct fuse_filehandle *fufh  = NULL;
 235
 236    fuse_trace_printf_vnop();
 237
 238    if (fuse_isdeadfs(vp)) {
 239        return 0;
 240    }
 241
 242    /* vclean() calls VNOP_CLOSE with fflag set to IO_NDELAY. */
 243    if (fflag == IO_NDELAY) {
 244        return 0;
 245    }
 246
 247    if (isdir) {
 248        fufh_type = FUFH_RDONLY;
 249    } else {
 250        fufh_type = fuse_filehandle_xlate_from_fflags(fflag);
 251    }
 252
 253    fufh = &(fvdat->fufh[fufh_type]);
 254
 255    if (!FUFH_IS_VALID(fufh)) {
 256        IOLog("MacFUSE: fufh invalid in close [type=%d oc=%d vtype=%d cf=%d]\n",
 257              fufh_type, fufh->open_count, vnode_vtype(vp), fflag);
 258        return 0;
 259    }
 260
 261    if (isdir) {
 262        goto skipdir;
 263    }
 264
 265    /*
 266     * Enforce sync-on-close unless explicitly told not to.
 267     *
 268     * We do this to maintain correct semantics in the not so common case when
 269     * you create a file with O_RDWR but without write permissions--you /are/
 270     * supposed to be able to write to such a file given the descriptor you
 271     * you got from open()/create(). Therefore, if we don't finish all our
 272     * writing before we close this precious writable descriptor, we might
 273     * be doomed.
 274     */
 275    if (vnode_hasdirtyblks(vp) && !fuse_isnosynconclose(vp)) {
 276        (void)cluster_push(vp, IO_SYNC | IO_CLOSE);
 277    }
 278
 279    data = fuse_get_mpdata(vnode_mount(vp));
 280    if (fuse_implemented(data, FSESS_NOIMPLBIT(FLUSH))) {
 281
 282        struct fuse_dispatcher  fdi;
 283        struct fuse_flush_in   *ffi;
 284
 285        fdisp_init(&fdi, sizeof(*ffi));
 286        fdisp_make_vp(&fdi, FUSE_FLUSH, vp, context);
 287
 288        ffi = fdi.indata;
 289        ffi->fh = fufh->fh_id;
 290        ffi->unused = 0;
 291        ffi->padding = 0;
 292        ffi->lock_owner = 0;
 293
 294        err = fdisp_wait_answ(&fdi);
 295
 296        if (!err) {
 297            fuse_ticket_drop(fdi.tick);
 298        } else {
 299            if (err == ENOSYS) {
 300                fuse_clear_implemented(data, FSESS_NOIMPLBIT(FLUSH));
 301                err = 0;
 302            }
 303        }
 304    }
 305
 306skipdir:
 307
 308    /* This must be done after we have flushed any pending I/O. */
 309    FUFH_USE_DEC(fufh);
 310
 311    if (!FUFH_IS_VALID(fufh)) {
 312        (void)fuse_filehandle_put(vp, context, fufh_type, FUSE_OP_FOREGROUNDED);
 313    }
 314
 315    return err;
 316}
 317
 318/*
 319    struct vnop_create_args {
 320        struct vnodeop_desc  *a_desc;
 321        vnode_t               a_dvp;
 322        vnode_t              *a_vpp;
 323        struct componentname *a_cnp;
 324        struct vnode_attr    *a_vap;
 325        vfs_context_t         a_context;
 326    };
 327*/
 328FUSE_VNOP_EXPORT
 329int
 330fuse_vnop_create(struct vnop_create_args *ap)
 331{
 332    vnode_t               dvp     = ap->a_dvp;
 333    vnode_t              *vpp     = ap->a_vpp;
 334    struct componentname *cnp     = ap->a_cnp;
 335    struct vnode_attr    *vap     = ap->a_vap;
 336    vfs_context_t         context = ap->a_context;
 337
 338    struct fuse_open_in    *foi;
 339    struct fuse_mknod_in    fmni;
 340    struct fuse_entry_out  *feo;
 341    struct fuse_dispatcher  fdi;
 342    struct fuse_dispatcher *fdip = &fdi;
 343
 344    int err;
 345    int gone_good_old = 0;
 346
 347    struct fuse_data *data = NULL;
 348
 349    mount_t mp = vnode_mount(dvp);
 350    uint64_t parent_nodeid = VTOFUD(dvp)->nodeid;
 351    mode_t mode = MAKEIMODE(vap->va_type, vap->va_mode);
 352
 353    fuse_trace_printf_vnop_novp();
 354
 355    if (fuse_isdeadfs_fs(dvp)) {
 356        panic("MacFUSE: fuse_vnop_create(): called on a dead file system");
 357    }
 358
 359    CHECK_BLANKET_DENIAL(dvp, context, EPERM);
 360
 361    if (fuse_skip_apple_double_mp(mp, cnp->cn_nameptr, cnp->cn_namelen)) {
 362        return EPERM;
 363    }
 364
 365    bzero(&fdi, sizeof(fdi));
 366
 367    data = fuse_get_mpdata(mp);
 368
 369    if (!fuse_implemented(data, FSESS_NOIMPLBIT(CREATE)) ||
 370        (vap->va_type != VREG)) {
 371
 372        /* User-space file system does not implement CREATE */
 373
 374        goto good_old;
 375    }
 376
 377    fdisp_init(fdip, sizeof(*foi) + cnp->cn_namelen + 1);
 378    fdisp_make(fdip, FUSE_CREATE, vnode_mount(dvp), parent_nodeid, context);
 379
 380    foi = fdip->indata;
 381    foi->mode = mode;
 382
 383    /* XXX: We /always/ creat() like this. Wish we were on Linux. */
 384    foi->flags = O_CREAT | O_RDWR;
 385
 386    memcpy((char *)fdip->indata + sizeof(*foi), cnp->cn_nameptr,
 387           cnp->cn_namelen);
 388    ((char *)fdip->indata)[sizeof(*foi) + cnp->cn_namelen] = '\0';
 389
 390    err = fdisp_wait_answ(fdip);
 391
 392    if (err == ENOSYS) {
 393        fuse_clear_implemented(data, FSESS_NOIMPLBIT(CREATE));
 394        fdip->tick = NULL;
 395        goto good_old;
 396    } else if (err) {
 397        goto undo;
 398    }
 399
 400    goto bringup;
 401
 402good_old:
 403    gone_good_old = 1;
 404    fmni.mode = mode; /* fvdat->flags; */
 405    fmni.rdev = 0;
 406    fuse_internal_newentry_makerequest(vnode_mount(dvp), parent_nodeid, cnp,
 407                                       FUSE_MKNOD, &fmni, sizeof(fmni),
 408                                       fdip, context);
 409    err = fdisp_wait_answ(fdip);
 410    if (err) {
 411        goto undo;
 412    }
 413
 414bringup:
 415    feo = fdip->answ;
 416
 417    if ((err = fuse_internal_checkentry(feo, VREG))) { // VBLK/VCHR not allowed
 418        fuse_ticket_drop(fdip->tick);
 419        goto undo;
 420    }
 421
 422    err = FSNodeGetOrCreateFileVNodeByID(
 423              vpp, (gone_good_old) ? 0 : FN_CREATING,
 424              feo, mp, dvp, context, NULL /* oflags */);
 425    if (err) {
 426       if (gone_good_old) {
 427           fuse_internal_forget_send(mp, context, feo->nodeid, 1, fdip);
 428       } else {
 429           struct fuse_release_in *fri;
 430           uint64_t nodeid = feo->nodeid;
 431           uint64_t fh_id = ((struct fuse_open_out *)(feo + 1))->fh;
 432
 433           fdisp_init(fdip, sizeof(*fri));
 434           fdisp_make(fdip, FUSE_RELEASE, mp, nodeid, context);
 435           fri = fdip->indata;
 436           fri->fh = fh_id;
 437           fri->flags = OFLAGS(mode);
 438           fuse_insert_callback(fdip->tick, fuse_internal_forget_callback);
 439           fuse_insert_message(fdip->tick);
 440       }
 441       return err;
 442    }
 443
 444    fdip->answ = gone_good_old ? NULL : feo + 1;
 445
 446    if (!gone_good_old) {
 447
 448        uint64_t x_fh_id = ((struct fuse_open_out *)(feo + 1))->fh;
 449        uint32_t x_open_flags = ((struct fuse_open_out *)(feo + 1))->open_flags;
 450        struct fuse_vnode_data *fvdat = VTOFUD(*vpp);
 451        struct fuse_filehandle *fufh = &(fvdat->fufh[FUFH_RDWR]);
 452
 453        fufh->fh_id = x_fh_id;
 454        fufh->open_flags = x_open_flags;
 455
 456        /*
 457         * We're stashing this to be picked up by open. Meanwhile, we set
 458         * the use count to 1 because that's what it is. The use count will
 459         * later transfer to the slot that this handle ends up falling in.
 460         */
 461        fufh->open_count = 1;
 462
 463        FUSE_OSAddAtomic(1, (SInt32 *)&fuse_fh_current);
 464    }
 465
 466    cache_purge_negatives(dvp);
 467
 468    fuse_ticket_drop(fdip->tick);
 469
 470    FUSE_KNOTE(dvp, NOTE_WRITE);
 471
 472    return 0;
 473
 474undo:
 475    return err;
 476}
 477
 478/*
 479    struct vnop_exchange_args {
 480        struct vnodeop_desc *a_desc;
 481        vnode_t              a_fvp;
 482        vnode_t              a_tvp;
 483        int                  a_options;
 484        vfs_context_t        a_context;
 485    };
 486*/
 487FUSE_VNOP_EXPORT
 488int
 489fuse_vnop_exchange(struct vnop_exchange_args *ap)
 490{
 491
 492#if M_MACFUSE_ENABLE_EXCHANGE
 493
 494    vnode_t       fvp     = ap->a_fvp;
 495    vnode_t       tvp     = ap->a_tvp;
 496    int           options = ap->a_options;
 497    vfs_context_t context = ap->a_context;
 498
 499    const char *fname = NULL;
 500    const char *tname = NULL;
 501    size_t flen = 0;
 502    size_t tlen = 0;
 503
 504    struct fuse_data *data = fuse_get_mpdata(vnode_mount(fvp));
 505 
 506    int err = 0;
 507
 508    fuse_trace_printf_vnop_novp();
 509
 510    if (vnode_mount(fvp) != vnode_mount(tvp)) {
 511        return EXDEV;
 512    }
 513
 514    /* We now know f and t are on the same volume. */
 515
 516    if (!fuse_implemented(data, FSESS_NOIMPLBIT(EXCHANGE))) {
 517        return ENOTSUP;
 518    }
 519
 520    if (fuse_isnovncache(fvp)) {
 521        return ENOTSUP;
 522    }
 523
 524    if (fvp == tvp) {
 525        return EINVAL;
 526    }
 527
 528    if (!vnode_isreg(fvp) || !vnode_isreg(tvp)) {
 529        return EINVAL;
 530    }
 531
 532    if (fuse_isdeadfs_fs(fvp)) {
 533        panic("MacFUSE: fuse_vnop_exchange(): called on a dead file system");
 534    }
 535
 536    fname = vnode_getname(fvp);
 537    if (!fname) {
 538        return EIO;
 539    }
 540
 541    tname = vnode_getname(tvp);
 542    if (!tname) {
 543        vnode_putname(fname);
 544        return EIO;
 545    }
 546
 547    flen = strlen(fname);
 548    tlen = strlen(tname);
 549
 550    if ((flen > 2) && (*fname == '.') && (*(fname + 1) == '_')) {
 551        err = EINVAL;
 552        goto out;
 553    }
 554
 555    if ((tlen > 2) && (*tname == '.') && (*(tname + 1) == '_')) {
 556        err = EINVAL;
 557        goto out;
 558    }
 559
 560    err = fuse_internal_exchange(fvp, fname, flen, tvp, tname, tlen, options,
 561                                 context);
 562
 563    if (err == ENOSYS) {
 564        fuse_clear_implemented(data, FSESS_NOIMPLBIT(EXCHANGE));
 565        err = ENOTSUP;
 566    }
 567
 568out:
 569
 570    vnode_putname(fname);
 571    vnode_putname(tname);
 572
 573    if (err == 0) {
 574        FUSE_KNOTE(fvp, NOTE_ATTRIB);
 575        FUSE_KNOTE(tvp, NOTE_ATTRIB);
 576    }
 577
 578    return err;
 579
 580#else /* !M_MACFUSE_ENABLE_EXCHANGE */
 581
 582    (void)ap;
 583
 584    return ENOTSUP;
 585
 586#endif /* M_MACFUSE_ENABLE_EXCHANGE */
 587
 588}
 589
 590/*
 591 * Our vnop_fsync roughly corresponds to the FUSE_FSYNC method. The Linux
 592 * version of FUSE also has a FUSE_FLUSH method.
 593 *
 594 * On Linux, fsync() synchronizes a file's complete in-core state with that
 595 * on disk. The call is not supposed to return until the system has completed 
 596 * that action or until an error is detected.
 597 *
 598 * Linux also has an fdatasync() call that is similar to fsync() but is not
 599 * required to update the metadata such as access time and modification time.
 600 */
 601
 602/*
 603    struct vnop_fsync_args {
 604        struct vnodeop_desc *a_desc;
 605        vnode_t              a_vp;
 606        int                  a_waitfor;
 607        vfs_context_t        a_context;
 608    };
 609*/
 610FUSE_VNOP_EXPORT
 611int
 612fuse_vnop_fsync(struct vnop_fsync_args *ap)
 613{
 614    vnode_t       vp      = ap->a_vp;
 615    int           waitfor = ap->a_waitfor;
 616    vfs_context_t context = ap->a_context;
 617
 618    struct fuse_dispatcher  fdi;
 619    struct fuse_filehandle *fufh;
 620    struct fuse_vnode_data *fvdat = VTOFUD(vp);
 621
 622    int type, err = 0, tmp_err = 0;
 623    (void)waitfor;
 624
 625    fuse_trace_printf_vnop();
 626
 627    if (fuse_isdeadfs(vp)) {
 628        return 0;
 629    }
 630
 631    cluster_push(vp, 0);
 632
 633    /*
 634     * struct timeval tv;
 635     * int wait = (waitfor == MNT_WAIT)
 636     *
 637     * In another world, we could be doing something like:
 638     *
 639     * buf_flushdirtyblks(vp, wait, 0, (char *)"fuse_fsync");
 640     * microtime(&tv);
 641     * ...
 642     */
 643
 644    /*
 645     * - UBC and vnode are in lock-step.
 646     * - Can call vnode_isinuse().
 647     * - Can call ubc_msync().
 648     */
 649
 650    mount_t mp = vnode_mount(vp);
 651
 652    if (!fuse_implemented(fuse_get_mpdata(mp), ((vnode_isdir(vp)) ?
 653                FSESS_NOIMPLBIT(FSYNCDIR) : FSESS_NOIMPLBIT(FSYNC)))) {
 654        err = ENOSYS;
 655        goto out;
 656    }
 657
 658    fdisp_init(&fdi, 0);
 659    for (type = 0; type < FUFH_MAXTYPE; type++) {
 660        fufh = &(fvdat->fufh[type]);
 661        if (FUFH_IS_VALID(fufh)) {
 662            tmp_err = fuse_internal_fsync(vp, context, fufh, &fdi,
 663                                          FUSE_OP_FOREGROUNDED);
 664            if (tmp_err) {
 665                err = tmp_err;
 666            }
 667        }
 668    }
 669
 670out:
 671    if ((err == ENOSYS) && !fuse_isnosyncwrites_mp(mp)) {
 672        err = 0;
 673    }
 674
 675    return err;
 676}
 677
 678/*
 679    struct vnop_getattr_args {
 680        struct vnodeop_desc *a_desc;
 681        vnode_t              a_vp;
 682        struct vnode_attr   *a_vap;
 683        vfs_context_t        a_context;
 684    };
 685*/
 686FUSE_VNOP_EXPORT
 687int
 688fuse_vnop_getattr(struct vnop_getattr_args *ap)
 689{
 690    vnode_t            vp      = ap->a_vp;
 691    struct vnode_attr *vap     = ap->a_vap;
 692    vfs_context_t      context = ap->a_context;
 693
 694    int err = 0;
 695    int dataflags;
 696    struct timespec uptsp;
 697    struct fuse_dispatcher fdi;
 698    struct fuse_data *data;
 699
 700    data = fuse_get_mpdata(vnode_mount(vp));
 701
 702    fuse_trace_printf_vnop();
 703
 704    if (fuse_isdeadfs(vp)) {
 705        if (vnode_isvroot(vp)) {
 706            goto fake;
 707        } else {
 708            return EBADF;
 709        }
 710    }
 711
 712    if (!vnode_isvroot(vp) || !fuse_vfs_context_issuser(context)) {
 713        CHECK_BLANKET_DENIAL(vp, context, ENOENT);
 714    }
 715
 716    dataflags = data->dataflags;
 717
 718    /* Note that we are not bailing out on a dead file system just yet. */
 719
 720    /* look for cached attributes */
 721    nanouptime(&uptsp);
 722    if (fuse_timespec_cmp(&uptsp, &VTOFUD(vp)->attr_valid, <=)) {
 723        if (vap != VTOVA(vp)) {
 724            fuse_internal_attr_loadvap(vp, vap, context);
 725        }
 726        return 0;
 727    }
 728
 729    if (!(dataflags & FSESS_INITED)) {
 730        if (!vnode_isvroot(vp)) {
 731            fdata_set_dead(data);
 732            err = ENOTCONN;
 733            return err;
 734        } else {
 735            goto fake;
 736        }
 737    }
 738
 739    if ((err = fdisp_simple_putget_vp(&fdi, FUSE_GETATTR, vp, context))) {
 740        if ((err == ENOTCONN) && vnode_isvroot(vp)) {
 741            /* see comment at similar place in fuse_statfs() */
 742            goto fake;
 743        }
 744        if (err == ENOENT) {
 745            fuse_internal_vnode_disappear(vp, context, REVOKE_SOFT);
 746        }
 747        return err;
 748    }
 749
 750    /* XXX: Could check the sanity/volatility of va_mode here. */
 751
 752    if ((((struct fuse_attr_out *)fdi.answ)->attr.mode & S_IFMT) == 0) {
 753        return EIO;
 754    }
 755
 756    cache_attrs(vp, (struct fuse_attr_out *)fdi.answ);
 757
 758    VTOFUD(vp)->c_flag &= ~C_XTIMES_VALID;
 759
 760    fuse_internal_attr_loadvap(vp, vap, context);
 761
 762#if M_MACFUSE_EXPERIMENTAL_JUNK
 763    if (vap != VTOVA(vp)) {
 764        memcpy(vap, VTOVA(vp), sizeof(*vap));
 765    }
 766#endif
 767
 768    /* ATTR_FUDGE_CASE */
 769    if (vnode_isreg(vp) && fuse_isnoubc(vp)) {
 770        /*
 771         * This is for those cases when the file size changed without us
 772         * knowing, and we want to catch up.
 773         *
 774         * For the sake of sanity, we don't want to do it with UBC.
 775         * We also don't want to do it when we have asynchronous writes
 776         * enabled because we might have pending writes on *our* side.
 777         * We're not researching distributed file systems here!
 778         *
 779         */
 780
 781        struct fuse_vnode_data *fvdat = VTOFUD(vp);
 782        off_t new_filesize = ((struct fuse_attr_out *)fdi.answ)->attr.size;
 783        fvdat->filesize = new_filesize;
 784    }
 785
 786    fuse_ticket_drop(fdi.tick);
 787
 788    if (vnode_vtype(vp) != vap->va_type) {
 789        if ((vnode_vtype(vp) == VNON) && (vap->va_type != VNON)) {
 790            /*
 791             * We should be doing the following:
 792             *
 793             * vp->vtype = vap->v_type
 794             */
 795        } else {
 796
 797            /*
 798             * STALE vnode, ditch
 799             *
 800             * The vnode has changed its type "behind our back". There's
 801             * nothing really we can do, so let us just force an internal
 802             * revocation.
 803             */
 804
 805            fuse_internal_vnode_disappear(vp, context, REVOKE_SOFT);
 806            return EIO;
 807        }
 808    }
 809
 810    return 0;
 811
 812fake:
 813    bzero(vap, sizeof(*vap));
 814    VATTR_RETURN(vap, va_type, vnode_vtype(vp));
 815    VATTR_RETURN(vap, va_uid, data->daemoncred->cr_uid);
 816    VATTR_RETURN(vap, va_gid, data->daemoncred->cr_gid);
 817    VATTR_RETURN(vap, va_mode, S_IRWXU);
 818
 819    return 0;
 820}
 821
 822#if M_MACFUSE_ENABLE_XATTR
 823/*
 824    struct vnop_getxattr_args {
 825        struct vnodeop_desc *a_desc;
 826        vnode_t              a_vp;
 827        char                *a_name;
 828        uio_t                a_uio;
 829        size_t              *a_size;
 830        int                  a_options;
 831        vfs_context_t        a_context;
 832    };
 833*/
 834FUSE_VNOP_EXPORT
 835int
 836fuse_vnop_getxattr(struct vnop_getxattr_args *ap)
 837{
 838    vnode_t       vp      = ap->a_vp;
 839    const char   *name    = ap->a_name;
 840    uio_t         uio     = ap->a_uio;
 841    vfs_context_t context = ap->a_context;
 842
 843    struct fuse_dispatcher    fdi;
 844    struct fuse_getxattr_in  *fgxi; 
 845    struct fuse_getxattr_out *fgxo;
 846    struct fuse_data         *data;
 847    mount_t mp;
 848
 849    int err = 0;
 850    size_t namelen;      
 851    
 852    fuse_trace_printf_vnop();
 853
 854    if (fuse_isdeadfs(vp)) {
 855        return EBADF;
 856    }
 857
 858    CHECK_BLANKET_DENIAL(vp, context, ENOENT);
 859
 860    if (name == NULL || name[0] == '\0') {
 861        return EINVAL;
 862    }
 863
 864    mp = vnode_mount(vp);
 865    data = fuse_get_mpdata(mp);
 866
 867    if (fuse_skip_apple_xattr_mp(mp, name)) {
 868        return EPERM;
 869    }
 870
 871    if (data->dataflags & FSESS_AUTO_XATTR) {
 872        return ENOTSUP;
 873    }
 874
 875    if (!fuse_implemented(data, FSESS_NOIMPLBIT(GETXATTR))) {
 876        return ENOTSUP;
 877    }
 878
 879    namelen = strlen(name);
 880
 881    fdisp_init(&fdi, sizeof(*fgxi) + namelen + 1);
 882    fdisp_make_vp(&fdi, FUSE_GETXATTR, vp, context);
 883    fgxi = fdi.indata;
 884    
 885    if (uio) {
 886        fgxi->size = (uint32_t)uio_resid(uio);
 887    } else {
 888        fgxi->size = 0;
 889    }
 890
 891    fgxi->position = (uint32_t)uio_offset(uio);
 892    
 893    memcpy((char *)fdi.indata + sizeof(*fgxi), name, namelen);
 894    ((char *)fdi.indata)[sizeof(*fgxi) + namelen] = '\0';
 895
 896    if (fgxi->size > FUSE_REASONABLE_XATTRSIZE) {
 897        fticket_set_killl(fdi.tick);
 898    }
 899
 900    err = fdisp_wait_answ(&fdi);
 901    if (err) {
 902        if (err == ENOSYS) {
 903            fuse_clear_implemented(data, FSESS_NOIMPLBIT(GETXATTR));
 904            return ENOTSUP;
 905        }
 906        return err;  
 907    }                
 908                     
 909    if (uio) {       
 910        *ap->a_size = fdi.iosize;
 911        if ((user_ssize_t)fdi.iosize > uio_resid(uio)) {
 912            err = ERANGE;
 913        } else {
 914            err = uiomove((char *)fdi.answ, (int)fdi.iosize, uio);
 915        }
 916    } else {
 917        fgxo = (struct fuse_getxattr_out *)fdi.answ;
 918        *ap->a_size = fgxo->size;
 919    }
 920
 921    fuse_ticket_drop(fdi.tick);
 922
 923    return err;
 924}
 925#endif /* M_MACFUSE_ENABLE_XATTR */
 926
 927/*
 928    struct vnop_inactive_args {
 929        struct vnodeop_desc *a_desc;
 930        vnode_t              a_vp;
 931        vfs_context_t        a_context;
 932    };
 933*/
 934FUSE_VNOP_EXPORT
 935int
 936fuse_vnop_inactive(struct vnop_inactive_args *ap)
 937{
 938    vnode_t       vp      = ap->a_vp;
 939    vfs_context_t context = ap->a_context;
 940
 941    struct fuse_vnode_data *fvdat = VTOFUD(vp);
 942    struct fuse_filehandle *fufh = NULL;
 943
 944    int fufh_type;
 945
 946    fuse_trace_printf_vnop();
 947
 948    /*
 949     * Cannot do early bail out on a dead file system in this case.
 950     */
 951
 952    for (fufh_type = 0; fufh_type < FUFH_MAXTYPE; fufh_type++) {
 953
 954        fufh = &(fvdat->fufh[fufh_type]);
 955
 956        if (FUFH_IS_VALID(fufh)) {
 957            FUFH_USE_RESET(fufh);
 958            (void)fuse_filehandle_put(vp, context, fufh_type,
 959                                      FUSE_OP_FOREGROUNDED);
 960        }
 961    }
 962
 963    return 0;
 964}
 965
 966extern int fuse_setextendedsecurity(mount_t mp, int state);
 967
 968/*
 969    struct vnop_ioctl_args {
 970        struct vnodeop_desc *a_desc;
 971        vnode_t              a_vp;
 972        u_long               a_command;
 973        caddr_t              a_data;
 974        int                  a_fflag;
 975        vfs_context_t        a_context;
 976    };
 977*/
 978FUSE_VNOP_EXPORT
 979int
 980fuse_vnop_ioctl(struct vnop_ioctl_args *ap)
 981{
 982    vnode_t       vp      = ap->a_vp;
 983    u_long        cmd     = ap->a_command;
 984    vfs_context_t context = ap->a_context;
 985
 986    int ret = EINVAL;
 987
 988    fuse_trace_printf_vnop();
 989
 990    if (fuse_isdeadfs(vp)) {
 991        return EBADF;
 992    }
 993
 994    switch (cmd) {
 995    case FSCTLSETACLSTATE:
 996        {
 997            int state;
 998            mount_t mp;
 999            struct fuse_data *data;
1000
1001            if (ap->a_data == NULL) {
1002                return EINVAL;
1003            }
1004
1005            mp = vnode_mount(vp);
1006            data = fuse_get_mpdata(mp);
1007
1008            if (!fuse_vfs_context_issuser(context) &&
1009                !(fuse_match_cred(data->daemoncred,
1010                                  vfs_context_ucred(context)))) {
1011                return EPERM;
1012            }
1013
1014            state = *(int *)ap->a_data;
1015
1016            return fuse_setextendedsecurity(mp, state);
1017        }
1018        break;
1019
1020    case FSCTLALTERVNODEFORINODE:
1021        /*
1022         * This is the fsctl() version of the AVFI device ioctl's in
1023         * fuse_device.c. Since the device ioctl's must be used from
1024         * within the file system (we don't allow multiple device opens),
1025         * it's rather painful to test/experiment with them. The fsctl
1026         * version is easier to use. To simplify things, the "path" in
1027         * the fsctl() call /must/ be the root of the file system.
1028         */
1029        if (!vnode_isvroot(vp)) {
1030            return EINVAL;
1031        }
1032
1033        ret = fuse_internal_ioctl_avfi(vp, context,
1034                                       (struct fuse_avfi_ioctl *)(ap->a_data));
1035        break;
1036
1037    default:
1038        break;
1039    }
1040
1041    return ret;
1042}
1043
1044#if M_MACFUSE_ENABLE_KQUEUE
1045
1046#include "fuse_knote.h"
1047
1048/*
1049    struct vnop_kqfilt_add_args {
1050        struct vnodeop_desc  *a_desc;
1051        vnode_t               a_vp;
1052        struct knote         *a_kn;
1053        struct proc          *p;
1054        vfs_context_t         a_context;
1055    };
1056 */
1057FUSE_VNOP_EXPORT
1058int
1059fuse_vnop_kqfilt_add(struct vnop_kqfilt_add_args *ap)
1060{
1061    vnode_t       vp = ap->a_vp;
1062    struct knote *kn = ap->a_kn;
1063
1064    fuse_trace_printf_vnop();
1065
1066    if (fuse_isdeadfs(vp)) {
1067        return EBADF;
1068    }
1069
1070    switch (kn->kn_filter) {
1071    case EVFILT_READ:
1072        if (vnode_isreg(vp)) {
1073            kn->kn_fop = &fuseread_filtops;
1074        } else {
1075            return EINVAL;
1076        }
1077        break;
1078
1079    case EVFILT_WRITE:
1080        if (vnode_isreg(vp)) {
1081            kn->kn_fop = &fusewrite_filtops;
1082        } else {
1083            return EINVAL;
1084        }
1085        break;
1086
1087    case EVFILT_VNODE:
1088        kn->kn_fop = &fusevnode_filtops;
1089        break;
1090
1091    default:
1092        return 1;
1093    }
1094
1095    kn->kn_hook = (caddr_t)vp;
1096    kn->kn_hookid = vnode_vid(vp);
1097
1098    /* lock */
1099    KNOTE_ATTACH(&VTOFUD(vp)->c_knotes, kn);
1100    /* unlock */
1101
1102    return 0;
1103}
1104
1105/*
1106    struct vnop_kqfilt_remove_args {
1107        struct vnodeop_desc  *a_desc;
1108        vnode_t               a_vp;
1109        uintptr_t             ident;
1110        vfs_context_t         a_context;
1111    };
1112*/
1113FUSE_VNOP_EXPORT
1114int
1115fuse_vnop_kqfilt_remove(__unused struct vnop_kqfilt_remove_args *ap)
1116{
1117    fuse_trace_printf_vnop_novp();
1118
1119    return ENOTSUP;
1120}
1121
1122#endif /* M_MACFUSE_ENABLE_KQUEUE */
1123
1124/*
1125    struct vnop_link_args {
1126        struct vnodeop_desc  *a_desc;
1127        vnode_t               a_vp;
1128        vnode_t               a_tdvp;
1129        struct componentname *a_cnp;
1130        vfs_context_t         a_context;
1131    };
1132*/
1133FUSE_VNOP_EXPORT
1134int
1135fuse_vnop_link(struct vnop_link_args *ap)
1136{
1137    vnode_t               vp      = ap->a_vp;
1138    vnode_t               tdvp    = ap->a_tdvp;
1139    struct componentname *cnp     = ap->a_cnp;
1140    vfs_context_t         context = ap->a_context;
1141
1142    struct vnode_attr *vap = VTOVA(vp);
1143
1144    struct fuse_dispatcher fdi;
1145    struct fuse_entry_out *feo;
1146    struct fuse_link_in    fli;
1147
1148    int err;
1149
1150    fuse_trace_printf_vnop();
1151
1152    if (fuse_isdeadfs_fs(vp)) {
1153        panic("MacFUSE: fuse_vnop_link(): called on a dead file system");
1154    }
1155
1156    if (vnode_mount(tdvp) != vnode_mount(vp)) {
1157        return EXDEV;
1158    }
1159
1160    if (vap->va_nlink >= FUSE_LINK_MAX) {
1161        return EMLINK;
1162    }
1163
1164    CHECK_BLANKET_DENIAL(vp, context, EPERM);
1165
1166    fli.oldnodeid = VTOI(vp);
1167
1168    fdisp_init(&fdi, 0);
1169    fuse_internal_newentry_makerequest(vnode_mount(tdvp), VTOI(tdvp), cnp,
1170                                       FUSE_LINK, &fli, sizeof(fli), &fdi,
1171                                       context);
1172    if ((err = fdisp_wait_answ(&fdi))) {
1173        return err;
1174    }
1175
1176    feo = fdi.answ;
1177
1178    err = fuse_internal_checkentry(feo, vnode_vtype(vp));
1179    fuse_ticket_drop(fdi.tick);
1180    fuse_invalidate_attr(tdvp);
1181    fuse_invalidate_attr(vp);
1182    
1183    if (err == 0) {
1184        FUSE_KNOTE(vp, NOTE_LINK);
1185        FUSE_KNOTE(tdvp, NOTE_WRITE);
1186        VTOFUD(vp)->nlookup++;
1187    }
1188
1189    return err;
1190}
1191
1192#if M_MACFUSE_ENABLE_XATTR
1193/*
1194    struct vnop_listxattr_args {
1195        struct vnodeop_desc *a_desc;
1196        vnode_t              a_vp;
1197        uio_t                a_uio;
1198        size_t              *a_size;
1199        int                  a_options;
1200        vfs_context_t        a_context;
1201    };
1202*/
1203FUSE_VNOP_EXPORT
1204int
1205fuse_vnop_listxattr(struct vnop_listxattr_args *ap)
1206{
1207    vnode_t       vp      = ap->a_vp;
1208    uio_t         uio     = ap->a_uio;
1209    vfs_context_t context = ap->a_context;
1210
1211    struct fuse_dispatcher    fdi;
1212    struct fuse_getxattr_in  *fgxi;
1213    struct fuse_getxattr_out *fgxo;
1214    struct fuse_data         *data;
1215
1216    int err = 0;
1217
1218    fuse_trace_printf_vnop();
1219
1220    if (fuse_isdeadfs(vp)) {
1221        return EBADF;
1222    }
1223
1224    CHECK_BLANKET_DENIAL(vp, context, ENOENT);
1225
1226    data = fuse_get_mpdata(vnode_mount(vp));
1227
1228    if (data->dataflags & FSESS_AUTO_XATTR) {
1229        return ENOTSUP;
1230    }
1231
1232    if (!fuse_implemented(data, FSESS_NOIMPLBIT(LISTXATTR))) {
1233        return ENOTSUP;
1234    }
1235
1236    fdisp_init(&fdi, sizeof(*fgxi));
1237    fdisp_make_vp(&fdi, FUSE_LISTXATTR, vp, context);
1238    fgxi = fdi.indata;
1239    if (uio) {
1240        fgxi->size = (uint32_t)uio_resid(uio);
1241    } else {
1242        fgxi->size = 0;
1243    }
1244
1245    err = fdisp_wait_answ(&fdi);
1246    if (err) {
1247        if (err == ENOSYS) {
1248            fuse_clear_implemented(data, FSESS_NOIMPLBIT(LISTXATTR));
1249            return ENOTSUP;
1250        }
1251        return err;
1252    }
1253
1254    if (uio) {
1255        *ap->a_size = fdi.iosize;
1256        if ((user_ssize_t)fdi.iosize > uio_resid(uio)) {
1257            err = ERANGE;
1258        } else {
1259            err = uiomove((char *)fdi.answ, (int)fdi.iosize, uio);
1260        }
1261    } else {
1262        fgxo = (struct fuse_getxattr_out *)fdi.answ;
1263        *ap->a_size = fgxo->size;
1264    }
1265
1266    fuse_ticket_drop(fdi.tick);
1267
1268    return err;
1269}
1270#endif /* M_MACFUSE_ENABLE_XATTR */
1271
1272/*
1273    struct vnop_lookup_args {
1274        struct vnodeop_desc  *a_desc;
1275        vnode_t               a_dvp;
1276        vnode_t              *a_vpp;
1277        struct componentname *a_cnp;
1278        vfs_context_t         a_context;
1279    };
1280*/
1281FUSE_VNOP_EXPORT
1282int
1283fuse_vnop_lookup(struct vnop_lookup_args *ap)
1284{
1285    vnode_t dvp               = ap->a_dvp;
1286    vnode_t *vpp              = ap->a_vpp;
1287    struct componentname *cnp = ap->a_cnp;
1288    vfs_context_t context     = ap->a_context;
1289
1290    int nameiop               = cnp->cn_nameiop;
1291    int flags                 = cnp->cn_flags;
1292    int wantparent            = flags & (LOCKPARENT|WANTPARENT);
1293    int islastcn              = flags & ISLASTCN;
1294    int isdot                 = FALSE;
1295    int isdotdot              = FALSE;
1296    mount_t mp                = vnode_mount(dvp);
1297
1298    int err                   = 0;
1299    int lookup_err            = 0;
1300    vnode_t vp                = NULL;
1301    vnode_t pdp               = (vnode_t)NULL;
1302    uint64_t size             = FUSE_ZERO_SIZE;
1303
1304    struct fuse_dispatcher fdi;
1305    enum   fuse_opcode     op;
1306
1307    uint64_t nodeid;
1308    uint64_t parent_nodeid;
1309
1310    *vpp = NULLVP;
1311
1312    fuse_trace_printf_vnop_novp();
1313
1314    if (fuse_isdeadfs(dvp)) {
1315        *ap->a_vpp = NULLVP;
1316        return ENOTDIR;
1317    }
1318
1319    if (fuse_skip_apple_double_mp(mp, cnp->cn_nameptr, cnp->cn_namelen)) {
1320        return ENOENT;
1321    }
1322
1323    if (!vnode_isdir(dvp)) {
1324        return ENOTDIR;
1325    }
1326
1327    if (islastcn && vfs_isrdonly(mp) && (nameiop != LOOKUP)) {
1328        return EROFS;
1329    }
1330
1331    if (cnp->cn_namelen > FUSE_MAXNAMLEN) {
1332        return ENAMETOOLONG;
1333    }
1334
1335    if (flags & ISDOTDOT) {
1336        isdotdot = TRUE;
1337    } else if ((cnp->cn_nameptr[0] == '.') && (cnp->cn_namelen == 1)) {
1338        isdot = TRUE;
1339    } 
1340
1341    if (isdotdot) {
1342        pdp = VTOFUD(dvp)->parentvp;
1343        nodeid = VTOI(pdp);
1344        parent_nodeid = VTOFUD(dvp)->parent_nodeid;
1345        fdisp_init(&fdi, 0);
1346        op = FUSE_GETATTR;
1347        goto calldaemon;
1348    } else if (isdot) {
1349        nodeid = VTOI(dvp);
1350        parent_nodeid = VTOFUD(dvp)->parent_nodeid;
1351        fdisp_init(&fdi, 0);
1352        op = FUSE_GETATTR;
1353        goto calldaemon;
1354    } else {
1355        err = fuse_vncache_lookup(dvp, vpp, cnp);
1356        switch (err) {
1357
1358        case -1: /* positive match */
1359            if (fuse_isnovncache(*vpp)) {
1360                fuse_vncache_purge(*vpp);
1361                vnode_put(*vpp);
1362                *vpp = NULL;
1363                FUSE_OSAddAtomic(1, (SInt32 *)&fuse_lookup_cache_overrides);
1364                err = 0;
1365                break; /* pretend it's a miss */
1366            }
1367            FUSE_OSAddAtomic(1, (SInt32 *)&fuse_lookup_cache_hits);
1368            return 0;
1369
1370        case 0: /* no match in cache (or aged out) */
1371            FUSE_OSAddAtomic(1, (SInt32 *)&fuse_lookup_cache_misses);
1372            break;
1373
1374        case ENOENT: /* negative match */
1375             /* fall through */
1376        default:
1377             return err;
1378        }
1379    }
1380
1381    nodeid = VTOI(dvp);
1382    parent_nodeid = VTOI(dvp);
1383    fdisp_init(&fdi, cnp->cn_namelen + 1);
1384    op = FUSE_LOOKUP;
1385
1386calldaemon:
1387    fdisp_make(&fdi, op, mp, nodeid, context);
1388
1389    if (op == FUSE_LOOKUP) {
1390        memcpy(fdi.indata, cnp->cn_nameptr, cnp->cn_namelen);
1391        ((char *)fdi.indata)[cnp->cn_namelen] = '\0';
1392    }
1393
1394    lookup_err = fdisp_wait_answ(&fdi);
1395
1396    if ((op == FUSE_LOOKUP) && !lookup_err) { /* lookup call succeeded */
1397        nodeid = ((struct fuse_entry_out *)fdi.answ)->nodeid;
1398        size = ((struct fuse_entry_out *)fdi.answ)->attr.size;
1399        if (!nodeid) {
1400            fdi.answ_stat = ENOENT; /* XXX: negative_timeout case */
1401            lookup_err = ENOENT;
1402        } else if (nodeid == FUSE_ROOT_ID) {
1403            lookup_err = EINVAL;
1404        }
1405    }
1406
1407    /*
1408     * If we get (lookup_err != 0), that means we didn't find what we were
1409     * looking for. This can still be OK if we're creating or renaming and
1410     * are at the end of the pathname.
1411     */
1412
1413    if (lookup_err &&
1414        (!fdi.answ_stat || lookup_err != ENOENT || op != FUSE_LOOKUP)) {
1415        return lookup_err;
1416    }
1417
1418    /* lookup_err, if non-zero, must be ENOENT at this point */
1419
1420    if (lookup_err) {
1421
1422        if ((nameiop == CREATE || nameiop == RENAME) && islastcn
1423            /* && directory dvp has not been removed */) {
1424
1425            /*
1426             * EROFS case has already been covered.
1427             *
1428             * if (vfs_isrdonly(mp)) {
1429             *     err = EROFS;
1430             *     goto out;
1431             * }
1432             */
1433
1434            err = EJUSTRETURN;
1435            goto out;
1436        }
1437
1438        if (fuse_isnegativevncache_mp(mp)) {
1439            if ((cnp->cn_flags & MAKEENTRY) && (nameiop != CREATE)) {
1440                fuse_vncache_enter(dvp, NULLVP, cnp);
1441            }
1442        }
1443
1444        err = ENOENT;
1445        goto out;
1446
1447    } else {
1448
1449        /* !lookup_err */
1450
1451        struct fuse_entry_out *feo   = NULL;
1452        struct fuse_attr      *fattr = NULL;
1453
1454        if (op == FUSE_GETATTR) {
1455            fattr = &((struct fuse_attr_out *)fdi.answ)->attr;
1456        } else {
1457            feo = (struct fuse_entry_out *)fdi.answ;
1458            fattr = &(feo->attr);
1459        }
1460
1461        /* Sanity check(s) */
1462
1463        if ((fattr->mode & S_IFMT) == 0) {
1464            err = EIO;
1465            goto out;
1466        }
1467
1468        if ((nameiop == DELETE) && islastcn) {
1469
1470            if (isdot) {
1471                err = vnode_get(dvp);
1472                if (err == 0) {
1473                    *vpp = dvp;
1474                }
1475                goto out;
1476            }
1477
1478            if ((err  = fuse_vget_i(&vp, 0 /* flags */, feo, cnp, dvp,
1479                                    mp, context))) {
1480                goto out;
1481            }
1482
1483            *vpp = vp;
1484        
1485            goto out;
1486
1487        }
1488
1489        if ((nameiop == RENAME) && islastcn && wantparent) {
1490
1491            if (isdot) {
1492                err = EISDIR;
1493                goto out;
1494            }
1495
1496            if ((err  = fuse_vget_i(&vp, 0 /* flags */, feo, cnp, dvp,
1497                                    mp, context))) {
1498                goto out;
1499            }
1500
1501            *vpp = vp;
1502
1503            goto out;
1504        }
1505
1506        if (isdotdot) {
1507            err = vnode_get(pdp);
1508            if (err == 0) {
1509                *vpp = pdp;
1510            }
1511        } else if (isdot) { /* nodeid == VTOI(dvp) */
1512            err = vnode_get(dvp);
1513            if (err == 0) {
1514                *vpp = dvp;
1515            }
1516        } else {
1517            if ((err  = fuse_vget_i(&vp, 0 /* flags */, feo, cnp, dvp,
1518                                    mp, context))) {
1519                goto out;
1520            }
1521            *vpp = vp;
1522        }
1523
1524        if (op == FUSE_GETATTR) {
1525
1526            /* ATTR_FUDGE_CASE */
1527            if (vnode_isreg(*vpp) && fuse_isnoubc(vp)) {
1528                VTOFUD(*vpp)->filesize =
1529                    ((struct fuse_attr_out *)fdi.answ)->attr.size;
1530            }
1531
1532            cache_attrs(*vpp, (struct fuse_attr_out *)fdi.answ);
1533        } else {
1534
1535            /* ATTR_FUDGE_CASE */
1536            if (vnode_isreg(*vpp) && fuse_isnoubc(vp)) {
1537                VTOFUD(*vpp)->filesize =
1538                    ((struct fuse_entry_out *)fdi.answ)->attr.size;
1539            }
1540
1541            cache_attrs(*vpp, (struct fuse_entry_out *)fdi.answ);
1542        }
1543
1544        /*
1545         * We do this elsewhere...
1546         *
1547         * if (cnp->cn_flags & MAKEENTRY) {
1548         *     fuse_vncache_enter(dvp, *vpp, cnp);
1549         * }
1550         */
1551    }
1552
1553out:
1554    if (!lookup_err) {
1555
1556        /* No lookup error; need to clean up. */
1557
1558        if (err) { /* Found inode; exit with no vnode. */
1559            if (op == FUSE_LOOKUP) {
1560                fuse_internal_forget_send(vnode_mount(dvp), context,
1561                                          nodeid, 1, &fdi);
1562            }
1563            return err;
1564        } else {
1565
1566            if (!islastcn) {
1567
1568                int tmpvtype = vnode_vtype(*vpp);
1569
1570                if ((tmpvtype != VDIR) && (tmpvtype != VLNK)) {
1571                    err = ENOTDIR;
1572                }
1573
1574                /* if (!err && !vnode_mountedhere(*vpp)) { ... */
1575
1576                if (err) {
1577                    vnode_put(*vpp);
1578                    *vpp = NULL;
1579                }
1580            }
1581        }
1582            
1583        fuse_ticket_drop(fdi.tick);
1584    }
1585
1586    return err;
1587}
1588
1589/*
1590    struct vnop_mkdir_args {
1591        struct vnodeop_desc  *a_desc;
1592        vnode_t               a_dvp;
1593        vnode_t              *a_vpp;
1594        struct componentname *a_cnp;
1595        struct vnode_attr    *a_vap;
1596        vfs_context_t         a_context;
1597    };
1598*/
1599FUSE_VNOP_EXPORT
1600int
1601fuse_vnop_mkdir(struct vnop_mkdir_args *ap)
1602{
1603    vnode_t               dvp     = ap->a_dvp;
1604    vnode_t              *vpp     = ap->a_vpp;
1605    struct componentname *cnp     = ap->a_cnp;
1606    struct vnode_attr    *vap     = ap->a_vap;
1607    vfs_context_t         context = ap->a_context;
1608
1609    int err = 0;
1610
1611    struct fuse_mkdir_in fmdi;
1612
1613    fuse_trace_printf_vnop_novp();
1614
1615    if (fuse_isdeadfs_fs(dvp)) {
1616        panic("MacFUSE: fuse_vnop_mkdir(): called on a dead file system");
1617    }
1618
1619    CHECK_BLANKET_DENIAL(dvp, context, EPERM);
1620
1621    fmdi.mode = MAKEIMODE(vap->va_type, vap->va_mode);
1622
1623    err = fuse_internal_newentry(dvp, vpp, cnp, FUSE_MKDIR, &fmdi,
1624                                 sizeof(fmdi), VDIR, context);
1625
1626    if (err == 0) {
1627        fuse_invalidate_attr(dvp);
1628        FUSE_KNOTE(dvp, NOTE_WRITE | NOTE_LINK);
1629    }
1630
1631    return err;
1632}
1633
1634/*
1635    struct vnop_mknod_args {
1636        struct vnodeop_desc  *a_desc;
1637        vnode_t               a_dvp;
1638        vnode_t              *a_vpp;
1639        struct componentname *a_cnp;
1640        struct vnode_attr    *a_vap;
1641        vfs_context_t         a_context;
1642    };
1643*/
1644FUSE_VNOP_EXPORT
1645int
1646fuse_vnop_mknod(struct vnop_mknod_args *ap)
1647{
1648    vnode_t               dvp     = ap->a_dvp;
1649    vnode_t              *vpp     = ap->a_vpp;
1650    struct componentname *cnp     = ap->a_cnp;
1651    struct vnode_attr    *vap     = ap->a_vap;
1652    vfs_context_t         context = ap->a_context;
1653
1654    struct fuse_mknod_in fmni;
1655
1656    int err;
1657
1658    fuse_trace_printf_vnop_novp();
1659
1660    if (fuse_isdeadfs_fs(dvp)) {
1661        panic("MacFUSE: fuse_vnop_mknod(): called on a dead file system");
1662    }
1663
1664    CHECK_BLANKET_DENIAL(dvp, context, EPERM);
1665
1666    fmni.mode = MAKEIMODE(vap->va_type, vap->va_mode);
1667    fmni.rdev = vap->va_rdev;
1668
1669    err = fuse_internal_newentry(dvp, vpp, cnp, FUSE_MKNOD, &fmni,
1670                                 sizeof(fmni), vap->va_type, context);
1671
1672    if (err== 0) {
1673        fuse_invalidate_attr(dvp);
1674        FUSE_KNOTE(dvp, NOTE_WRITE);
1675    }
1676
1677    return err;
1678}
1679
1680/*
1681    struct vnop_mmap_args {
1682        struct vnodeop_desc *a_desc;
1683        vnode_t              a_vp;
1684        int                  a_fflags;
1685        vfs_context_t        a_context;
1686    };
1687*/
1688FUSE_VNOP_EXPORT
1689int
1690fuse_vnop_mmap(struct vnop_mmap_args *ap)
1691{
1692    vnode_t       vp      = ap->a_vp;
1693    int           fflags  = ap->a_fflags;
1694    vfs_context_t context = ap->a_context;
1695
1696    struct fuse_vnode_data *fvdat = VTOFUD(vp);
1697    struct fuse_filehandle *fufh = NULL;
1698    fufh_type_t fufh_type = fuse_filehandle_xlate_from_mmap(fflags);
1699
1700    int err = 0;
1701    int deleted = 0;
1702    int retried = 0;
1703
1704    fuse_trace_printf_vnop();
1705
1706    if (fuse_isdeadfs_fs(vp)) {
1707        panic("MacFUSE: fuse_vnop_mmap(): called on a dead file system");
1708    }
1709
1710    if (fuse_isdirectio(vp)) {
1711        /*
1712         * We should be returning ENODEV here, but ubc_map() translates
1713         * all errors except ENOPERM to 0. Even then, this is not going
1714         * to prevent the mmap()!
1715         */
1716        return EPERM;
1717    }
1718
1719    CHECK_BLANKET_DENIAL(vp, context, ENOENT);
1720
1721    if (fufh_type == FUFH_INVALID) { /* nothing to do */
1722        return 0;
1723    }
1724
1725    /* XXX: For PROT_WRITE, we should only care if file is mapped MAP_SHARED. */
1726
1727retry:
1728    fufh = &(fvdat->fufh[fufh_type]);
1729
1730    if (FUFH_IS_VALID(fufh)) {
1731        FUFH_USE_INC(fufh);
1732        FUSE_OSAddAtomic(1, (SInt32 *)&fuse_fh_reuse_count);
1733        goto out;
1734    }
1735
1736    if (!deleted) {
1737        err = fuse_filehandle_preflight_status(vp, fvdat->parentvp,
1738                                               context, fufh_type);
1739        if (err == ENOENT) {
1740            deleted = 1;
1741            err = 0;
1742        }
1743    }
1744
1745#if FUSE_DEBUG
1746    fuse_preflight_log(vp, fufh_type, err, "mmap");
1747#endif /* FUSE_DEBUG */
1748
1749    if (!err) {
1750        err = fuse_filehandle_get(vp, context, fufh_type, 0 /* mode */);
1751    }
1752
1753    if (err) {
1754        /*
1755         * XXX: This is a kludge because xnu doesn't tell us whether this
1756         *      is a MAP_SHARED or MAP_PRIVATE mapping. If we want shared
1757         *      library mapping to go well, we need to do this.
1758         */
1759        if (!retried && (err == EACCES) &&
1760            ((fufh_type == FUFH_RDWR) || (fufh_type == FUFH_WRONLY))) {
1761            IOLog("MacFUSE: filehandle_get retrying (type=%d, err=%d)\n",
1762                  fufh_type, err);
1763            fufh_type = FUFH_RDONLY;
1764            retried = 1;
1765            goto retry;
1766        } else {
1767            IOLog("MacFUSE: filehandle_get failed in mmap (type=%d, err=%d)\n",
1768                  fufh_type, err);
1769        }
1770        return EPERM;
1771    }
1772
1773out:
1774
1775    return 0;
1776}
1777
1778/*
1779    struct vnop_mnomap_args {
1780        struct vnodeop_desc *a_desc;
1781        vnode_t              a_vp;
1782        vfs_context_t        a_context;
1783    };
1784*/
1785FUSE_VNOP_EXPORT
1786int
1787fuse_vnop_mnomap(struct vnop_mnomap_args *ap)
1788{
1789    vnode_t vp = ap->a_vp;
1790
1791    fuse_trace_printf_vnop();
1792
1793    if (fuse_isdeadfs(vp)) {
1794        return 0;
1795    }
1796
1797    if (fuse_isdirectio(vp)) {
1798        /*
1799         * ubc_unmap() doesn't care about the return value.
1800         */
1801        return ENODEV;
1802    }
1803
1804    /*
1805     * XXX
1806     *
1807     * What behavior do we want here?
1808     *
1809     * I once noted that sync() is not going to help here, but I think
1810     * I've forgotten the context. Need to think about this again.
1811     *
1812     * ubc_msync(vp, (off_t)0, ubc_getsize(vp), (off_t*)0, UBC_PUSHDIRTY);
1813     */
1814
1815    /*
1816     * Earlier, we used to go through our vnode's fufh list here, doing
1817     * something like the following:
1818     *
1819     * for (type = 0; type < FUFH_MAXTYPE; type++) {
1820     *     fufh = &(fvdat->fufh[type]);
1821     *     if ((fufh->fufh_flags & FUFH_VALID) &&
1822     *         (fufh->fufh_flags & FUFH_MAPPED)) {
1823     *         fufh->fufh_flags &= ~FUFH_MAPPED;
1824     *         if (fufh->open_count == 0) {
1825     *             (void)fuse_filehandle_put(vp, context, type,
1826     *                                       FUSE_OP_BACKGROUNDED);
1827     *         }
1828     *     }
1829     * }
1830     *
1831     * Now, cleanup is all taken care of in vnop_inactive/reclaim.
1832     *
1833     */
1834
1835    return 0;
1836}
1837
1838/*
1839    struct vnop_offtoblk_args {
1840        struct vnodeop_desc *a_desc;
1841        vnode_t              a_vp;
1842        off_t                a_offset;
1843        daddr64_t           *a_lblkno;
1844    };
1845*/
1846FUSE_VNOP_EXPORT
1847int
1848fuse_vnop_offtoblk(struct vnop_offtoblk_args *ap)
1849{
1850    vnode_t    vp        = ap->a_vp;
1851    off_t      offset    = ap->a_offset;
1852    daddr64_t *lblknoPtr = ap->a_lblkno;
1853
1854    struct fuse_data *data;
1855
1856    fuse_trace_printf_vnop();
1857
1858    if (fuse_isdeadfs(vp)) {
1859        return EIO;
1860    }
1861
1862    data = fuse_get_mpdata(vnode_mount(vp));
1863
1864    *lblknoPtr = offset / data->blocksize;
1865
1866    return 0;
1867}
1868
1869/*
1870    struct vnop_open_args {
1871        struct vnodeop_desc *a_desc;
1872        vnode_t              a_vp;
1873        int                  a_mode;
1874        vfs_context_t        a_context;
1875    };
1876*/
1877FUSE_VNOP_EXPORT
1878int
1879fuse_vnop_open(struct vnop_open_args *ap)
1880{
1881    vnode_t       vp      = ap->a_vp;
1882    int           mode    = ap->a_mode;
1883    vfs_context_t context = ap->a_context;
1884
1885    fufh_type_t             fufh_type;
1886    struct fuse_vnode_data *fvdat;
1887    struct fuse_filehandle *fufh = NULL;
1888    struct fuse_filehandle *fufh_rw = NULL;
1889
1890    int error, isdir = 0;
1891    long hint = 0;
1892
1893    fuse_trace_printf_vnop();
1894
1895    if (fuse_isdeadfs(vp)) {
1896        return ENXIO;
1897    }
1898
1899#if !M_MACFUSE_ENABLE_FIFOFS
1900    if (vnode_isfifo(vp)) {
1901        return EPERM;
1902    }
1903#endif
1904
1905    CHECK_BLANKET_DENIAL(vp, context, ENOENT);
1906
1907    fvdat = VTOFUD(vp);
1908
1909    if (vnode_isdir(vp)) {
1910        isdir = 1;
1911    }
1912
1913    if (isdir) {
1914        fufh_type = FUFH_RDONLY;
1915    } else {
1916        fufh_type = fuse_filehandle_xlate_from_fflags(mode);
1917    }
1918
1919    fufh = &(fvdat->fufh[fufh_type]);
1920
1921    if (!isdir && (fvdat->flag & FN_CREATING)) {
1922
1923        fuse_lck_mtx_lock(fvdat->createlock);
1924
1925        if (fvdat->flag & FN_CREATING) { // check again
1926            if (fvdat->creator == current_thread()) {
1927
1928                /*
1929                 * For testing the race condition we want to prevent here,
1930                 * try something like the following:
1931                 *
1932                 *     int dummyctr = 0;
1933                 *
1934                 *     for (; dummyctr < 2048000000; dummyctr++);
1935                 */
1936
1937                fufh_rw = &(fvdat->fufh[FUFH_RDWR]);
1938
1939                fufh->open_flags = fufh_rw->open_flags;
1940                fufh->fh_id = fufh_rw->fh_id;
1941
1942                /* Note that fufh_rw can be the same as fufh! Order is key. */
1943                fufh_rw->open_count = 0;
1944                fufh->open_count = 1;
1945
1946                /*
1947                 * Creator has picked up stashed handle and moved it to the
1948                 * fufh_type slot.
1949                 */
1950                
1951                fvdat->flag &= ~FN_CREATING;
1952
1953                fuse_lck_mtx_unlock(fvdat->createlock);
1954                fuse_wakeup((caddr_t)fvdat->creator); // wake up all
1955                goto ok; /* return 0 */
1956            } else {
1957
1958                /* Contender is going to sleep now. */
1959
1960                error = fuse_msleep(fvdat->creator, fvdat->createlock,
1961                                    PDROP | PINOD | PCATCH, "fuse_open", NULL);
1962                /*
1963                 * msleep will drop the mutex. since we have PDROP specified,
1964                 * it will NOT regrab the mutex when it returns.
1965                 */
1966
1967                /* Contender is awake now. */
1968
1969                if (error) {
1970                    /*
1971                     * Since we specified PCATCH above, we'll be woken up in
1972                     * case a signal arrives. The value of error could be
1973                     * EINTR or ERESTART.
1974                     */
1975                    return error;
1976                }
1977            }
1978        } else {
1979            fuse_lck_mtx_unlock(fvdat->createlock);
1980            /* Can proceed from here. */
1981        }
1982    }
1983
1984    if (FUFH_IS_VALID(fufh)) {
1985        FUFH_USE_INC(fufh);
1986        FUSE_OSAddAtomic(1, (SInt32 *)&fuse_fh_reuse_count);
1987        goto ok; /* return 0 */
1988    }
1989
1990    error = fuse_filehandle_get(vp, context, fufh_type, mode);
1991    if (error) {
1992        IOLog("MacFUSE: filehandle_get failed in open (type=%d, err=%d)\n",
1993              fufh_type, error);
1994        if (error == ENOENT) {
1995            cache_purge(vp);
1996        }
1997        return error;
1998    }
1999
2000ok:
2001    /*
2002     * Doing this here because when a vnode goes inactive, things like
2003     * no-cache and no-readahead are cleared by the kernel.
2004     */
2005
2006    if ((fufh->fuse_open_flags & FOPEN_DIRECT_IO) || (fuse_isdirectio(vp))) {
2007        /* 
2008         * direct_io for a vnode implies:
2009         * - no ubc for the vnode
2010         * - no readahead for the vnode
2011         * - nosyncwrites disabled FOR THE ENTIRE MOUNT
2012         * - no vncache for the vnode (handled in lookup)
2013         */
2014        ubc_msync(vp, (off_t)0, ubc_getsize(vp), (off_t*)0,
2015                  UBC_PUSHALL | UBC_INVALIDATE);
2016        vnode_setnocache(vp);
2017        vnode_setnoreadahead(vp);
2018        fuse_clearnosyncwrites_mp(vnode_mount(vp));
2019        fvdat->flag |= FN_DIRECT_IO;
2020        goto out;
2021    } else if (fufh->fuse_open_flags & FOPEN_PURGE_UBC) {
2022        ubc_msync(vp, (off_t)0, ubc_getsize(vp), (off_t*)0,
2023                  UBC_PUSHALL | UBC_INVALIDATE);
2024        fufh->fuse_open_flags &= ~FOPEN_PURGE_UBC;
2025        hint |= NOTE_WRITE;
2026        if (fufh->fuse_open_flags & FOPEN_PURGE_ATTR) {
2027            int serr = 0;
2028            struct fuse_dispatcher fdi;
2029            fuse_invalidate_attr(vp);
2030            hint |= NOTE_ATTRIB;
2031            serr = fdisp_simple_putget_vp(&fdi, FUSE_GETATTR, vp, context);
2032            if (!serr) {
2033                /* XXX: Could check the sanity/volatility of va_mode here. */
2034                if ((((struct fuse_attr_out*)fdi.answ)->attr.mode & S_IFMT)) {
2035                    cache_attrs(vp, (struct fuse_attr_out *)fdi.answ);
2036                    off_t new_filesize =
2037                        ((struct fuse_attr_out *)fdi.answ)->attr.size;
2038                    if (new_filesize > VTOFUD(vp)->filesize) {
2039                        hint |= NOTE_EXTEND;
2040                    }
2041                    VTOFUD(vp)->filesize = new_filesize;
2042                    ubc_setsize(vp, (off_t)new_filesize);
2043                }
2044                fuse_ticket_drop(fdi.tick);
2045            }
2046            fufh->fuse_open_flags &= ~FOPEN_PURGE_ATTR;
2047        }
2048    }
2049
2050    if (hint) {
2051        FUSE_KNOTE(vp, hint);
2052    }
2053
2054    if (fuse_isnoreadahead(vp)) {
2055        vnode_setnoreadahead(vp);
2056    }
2057
2058    if (fuse_isnoubc(vp)) {
2059        vnode_setnocache(vp);
2060    }
2061
2062out:
2063    return 0;
2064}
2065
2066/*
2067    struct vnop_pagein_args {
2068        struct vnodeop_desc *a_desc;
2069        vnode_t              a_vp;
2070        upl_t                a_pl;
2071        vm_offset_t          a_pl_offset;
2072        off_t                a_f_offset;
2073        size_t               a_size;
2074        int                  a_flags;
2075        vfs_context_t        a_context;
2076    };
2077*/
2078FUSE_VNOP_EXPORT
2079int
2080fuse_vnop_pagein(struct vnop_pagein_args *ap)
2081{
2082    vnode_t       vp        = ap->a_vp;
2083    upl_t         pl        = ap->a_pl;
2084    vm_offset_t   pl_offset = ap->a_pl_offset;
2085    off_t         f_offset  = ap->a_f_offset;
2086    size_t        size      = ap->a_size;
2087    int           flags     = ap->a_flags;
2088
2089    struct fuse_vnode_data *fvdat;
2090    int err;
2091
2092    fuse_trace_printf_vnop();
2093
2094    if (fuse_isdeadfs(vp) || fuse_isdirectio(vp)) {
2095        if (!(flags & UPL_NOCOMMIT)) {
2096            ubc_upl_abort_range(pl, (upl_offset_t)pl_offset, (int)size,
2097                                UPL_ABORT_FREE_ON_EMPTY | UPL_ABORT_ERROR);
2098        }
2099        /*
2100         * Will cause PAGER_ERROR (pager unable to read or write page).
2101         */
2102        return ENOTSUP;
2103    }
2104
2105    fvdat = VTOFUD(vp);
2106    if (!fvdat) {
2107        return EIO;
2108    }
2109
2110    err = cluster_pagein(vp, pl, (upl_offset_t)pl_offset, f_offset, (int)size,
2111                         fvdat->filesize, flags);
2112
2113    return err;
2114}
2115
2116/*
2117    struct vnop_pageout_args {
2118        struct vnodeop_desc *a_desc;
2119        vnode_t              a_vp;
2120        upl_t                a_pl;
2121        vm_offset_t          a_pl_offset;
2122        off_t                a_f_offset;
2123        size_t               a_size;
2124        int                  a_flags;
2125        vfs_context_t        a_context;
2126    };
2127*/
2128FUSE_VNOP_EXPORT
2129int
2130fuse_vnop_pageout(struct vnop_pageout_args *ap)
2131{
2132    vnode_t       vp        = ap->a_vp;
2133    upl_t         pl        = ap->a_pl;
2134    vm_offset_t   pl_offset = ap->a_pl_offset;
2135    off_t         f_offset  = ap->a_f_offset;
2136    size_t        size      = ap->a_size;
2137    int           flags     = ap->a_flags;
2138
2139    struct fuse_vnode_data *fvdat = VTOFUD(vp);
2140    int error;
2141
2142    fuse_trace_printf_vnop();
2143
2144    if (fuse_isdeadfs(vp) || fuse_isdirectio(vp)) {
2145        if (!(flags & UPL_NOCOMMIT)) {
2146            ubc_upl_abort_range(pl, (upl_offset_t)pl_offset, (upl_size_t)size,
2147                                UPL_ABORT_FREE_ON_EMPTY | UPL_ABORT_ERROR);
2148        }
2149        /*
2150         * Will cause PAGER_ERROR (pager unable to read or write page).
2151         */
2152        return ENOTSUP;
2153    }
2154
2155    error = cluster_pageout(vp, pl, (upl_offset_t)pl_offset, f_offset,
2156                            (int)size, (off_t)fvdat->filesize, flags);
2157
2158    return error;
2159}
2160
2161/*
2162    struct vnop_pathconf_args {
2163        struct vnodeop_desc *a_desc;
2164        vnode_t              a_vp;
2165        int                  a_name;
2166        int                 *a_retval;
2167        vfs_context_t        a_context;
2168    };
2169*/
2170FUSE_VNOP_EXPORT
2171int
2172fuse_vnop_pathconf(struct vnop_pathconf_args *ap)
2173{
2174    vnode_t        vp        = ap->a_vp;
2175    int            name      = ap->a_name;
2176    int           *retvalPtr = ap->a_retval;
2177    vfs_context_t  context   = ap->a_context;
2178
2179    int err;
2180
2181    fuse_trace_printf_vnop();
2182
2183    if (fuse_isdeadfs(vp)) {
2184        return EBADF;
2185    }
2186
2187    CHECK_BLANKET_DENIAL(vp, context, ENOENT);
2188
2189    err = 0;
2190    switch (name) {
2191        case _PC_LINK_MAX:
2192            *retvalPtr = FUSE_LINK_MAX;
2193            break;
2194        case _PC_NAME_MAX:
2195            *retvalPtr = FUSE_MAXNAMLEN;
2196            break;
2197        case _PC_PATH_MAX:
2198            *retvalPtr = MAXPATHLEN;
2199            break;
2200        case _PC_PIPE_BUF:
2201            *retvalPtr = PIPE_BUF;
2202            break;
2203        case _PC_CHOWN_RESTRICTED:
2204            *retvalPtr = 1;
2205            break;
2206        case _PC_NO_TRUNC:
2207            *retvalPtr = 0;
2208            break;
2209        case _PC_NAME_CHARS_MAX:
2210            *retvalPtr = 255;   // chars as opposed to bytes
2211            break;
2212        case _PC_CASE_SENSITIVE:
2213            *retvalPtr = 1;
2214            break;
2215        case _PC_CASE_PRESERVING:
2216            *retvalPtr = 1;
2217            break;
2218
2219        /*
2220         * _PC_EXTENDED_SECURITY_NP and _PC_AUTH_OPAQUE_NP are handled
2221         * by the VFS.
2222         */
2223
2224        // The following are terminal device stuff that we don't support:
2225
2226        case _PC_MAX_CANON:
2227        case _PC_MAX_INPUT:
2228        case _PC_VDISABLE:
2229        default:
2230            err = EINVAL;
2231            break;
2232    }
2233
2234    return err;
2235}
2236
2237/*
2238    struct vnop_read_args {
2239        struct vnodeop_desc *a_desc;
2240        vnode_t              a_vp;
2241        struct uio          *a_uio;
2242        int                  a_ioflag;
2243        vfs_context_t        a_context;
2244    };
2245*/
2246FUSE_VNOP_EXPORT
2247int
2248fuse_vnop_read(struct vnop_read_args *ap)
2249{
2250    vnode_t       vp      = ap->a_vp;
2251    uio_t         uio     = ap->a_uio;
2252    int           ioflag  = ap->a_ioflag;
2253    vfs_context_t context = ap->a_context;
2254
2255    struct fuse_vnode_data *fvdat;
2256    struct fuse_data       *data;
2257
2258    off_t orig_resid;
2259    off_t orig_offset;
2260
2261    int err = EIO;
2262
2263    /*
2264     * XXX: Locking
2265     *
2266     * lock_shared(truncatelock)
2267     * call the cluster layer (note that we are always block-aligned)
2268     * lock(nodelock)
2269     * do cleanup 
2270     * unlock(nodelock)
2271     * unlock(truncatelock)
2272     */
2273
2274    fuse_trace_printf_vnop();
2275
2276    if (fuse_isdeadfs(vp)) {
2277        if (!vnode_ischr(vp)) {
2278            return EIO;
2279        } else {
2280            return 0;
2281        }
2282    }
2283
2284    if (!vnode_isreg(vp)) {
2285        if (vnode_isdir(vp)) {
2286            return EISDIR;
2287        } else {
2288            return EPERM;
2289        }
2290    }
2291
2292    /*
2293     * if (uio_offset(uio) > SOME_MAXIMUM_SIZE) {
2294     *     return 0;
2295     * }
2296     */
2297
2298    orig_resid = uio_resid(uio);
2299    if (orig_resid == 0) {
2300        return 0;
2301    }
2302
2303    orig_offset = uio_offset(uio);
2304    if (orig_offset < 0) {
2305        return EINVAL;
2306    }
2307
2308    fvdat = VTOFUD(vp);
2309    if (!fvdat) {
2310        return EINVAL;
2311    }
2312
2313    /* Protect against size change here. */
2314
2315    data = fuse_get_mpdata(vnode_mount(vp));
2316
2317    if (!fuse_isdirectio(vp)) {
2318        if (fuse_isnoubc(vp)) {
2319            /* In case we get here through a short cut (e.g. no open). */
2320            ioflag |= IO_NOCACHE;
2321        }
2322        return cluster_read(vp, uio, fvdat->filesize, ioflag);
2323    }
2324
2325    /* direct_io */
2326    {
2327        fufh_type_t             fufh_type = FUFH_RDONLY;
2328        struct fuse_dispatcher  fdi;
2329        struct fuse_filehandle *fufh = NULL;
2330        struct fuse_read_in    *fri = NULL;
2331        off_t                   rounded_iolength;
2332
2333        fufh = &(fvdat->fufh[fufh_type]);
2334
2335        if (!FUFH_IS_VALID(fufh)) {
2336            fufh_type = FUFH_RDWR;
2337            fufh = &(fvdat->fufh[fufh_type]);
2338            if (!FUFH_IS_VALID(fufh)) {
2339                fufh = NULL;
2340            } else {
2341                /* Read falling back to FUFH_RDWR. */
2342            }
2343        }
2344
2345        if (!fufh) {
2346            /* Failing direct I/O because of no fufh. */
2347            return EIO;
2348        } else {
2349            /* Using existing fufh of type fufh_type. */
2350        }
2351
2352        rounded_iolength = (off_t)round_page_64(uio_offset(uio) +
2353                                                uio_resid(uio));
2354        fdisp_init(&fdi, 0);
2355
2356        while (uio_resid(uio) > 0) {
2357            fdi.iosize = sizeof(*fri);
2358            fdisp_make_vp(&fdi, FUSE_READ, vp, context);
2359            fri = fdi.indata;
2360            fri->fh = fufh->fh_id;
2361            fri->offset = uio_offset(uio);
2362            fri->size = (uint32_t)min((size_t)uio_resid(uio), data->iosize);
2363
2364            if ((err = fdisp_wait_answ(&fdi))) {
2365                return err;
2366            }
2367
2368            if ((err = uiomove(fdi.answ, (int)min(fri->size, fdi.iosize),
2369                               uio))) {
2370                break;
2371            }
2372
2373            if (fdi.iosize < fri->size) {
2374                err = -1;
2375                break;
2376            }
2377        }
2378
2379        fuse_ticket_drop(fdi.tick);
2380
2381    } /* direct_io */
2382
2383    return ((err == -1) ? 0 : err);
2384}
2385
2386/*
2387    struct vnop_readdir_args {
2388        struct vnodeop_desc *a_desc;
2389        vnode_t              a_vp;
2390        struct uio          *a_uio;
2391        int                  a_flags;
2392        int                 *a_eofflag;
2393        int                 *a_numdirent;
2394        vfs_context_t        a_context;
2395    };
2396*/
2397FUSE_VNOP_EXPORT
2398int
2399fuse_vnop_readdir(struct vnop_readdir_args *ap)
2400{
2401    vnode_t        vp           = ap->a_vp;
2402    uio_t          uio          = ap->a_uio;
2403    int            flags        = ap->a_flags;
2404    __unused int  *eofflagPtr   = ap->a_eofflag;
2405    int           *numdirentPtr = ap->a_numdirent;
2406    vfs_context_t  context      = ap->a_context;
2407
2408    struct fuse_filehandle *fufh = NULL;
2409    struct fuse_vnode_data *fvdat;
2410    struct fuse_iov         cookediov;
2411
2412    int err = 0;
2413    int freefufh = 0;
2414
2415    fuse_trace_printf_vnop();
2416
2417    if (fuse_isdeadfs(vp)) {
2418        return EBADF;
2419    }
2420
2421    CHECK_BLANKET_DENIAL(vp, context, EPERM);
2422
2423    /* No cookies yet. */
2424    if (flags & (VNODE_READDIR_REQSEEKOFF | VNODE_READDIR_EXTENDED)) {
2425        return EINVAL;
2426    }
2427
2428#define DE_SIZE (int)(sizeof(struct fuse_dirent))
2429
2430    if ((uio_iovcnt(uio) > 1) ||
2431        (uio_resid(uio) < (user_ssize_t)DE_SIZE)) {
2432        return EINVAL;
2433    }
2434
2435    /*
2436     *  if ((uio_offset(uio) % DE_SIZE) != 0) { ...
2437     */
2438
2439    fvdat = VTOFUD(vp);
2440
2441    fufh = &(fvdat->fufh[FUFH_RDONLY]);
2442
2443    if (!FUFH_IS_VALID(fufh)) {
2444        err = fuse_filehandle_get(vp, context, FUFH_RDONLY, 0 /* mode */);
2445        if (err) {
2446            IOLog("MacFUSE: filehandle_get failed in readdir (err=%d)\n", err);
2447            return err;
2448        }
2449        freefufh = 1;
2450    } else {
2451        FUSE_OSAddAtomic(1, (SInt32 *)&fuse_fh_reuse_count);
2452    }
2453
2454#define DIRCOOKEDSIZE FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + MAXNAMLEN + 1)
2455
2456    fiov_init(&cookediov, DIRCOOKEDSIZE);
2457
2458    err = fuse_internal_readdir(vp, uio, context, fufh, &cookediov,
2459                                numdirentPtr);
2460
2461    fiov_teardown(&cookediov);
2462
2463    if (freefufh) {
2464        FUFH_USE_DEC(fufh);
2465        (void)fuse_filehandle_put(vp, context, FUFH_RDONLY,
2466                                  FUSE_OP_FOREGROUNDED);
2467    }
2468
2469    fuse_invalidate_attr(vp);
2470
2471    return err;
2472}
2473
2474/*
2475    struct vnop_readlink_args {
2476        struct vnodeop_desc *a_desc;
2477        vnode_t              a_vp;
2478        struct uio          *a_uio;
2479        vfs_context_t        a_context;
2480    };
2481*/
2482FUSE_VNOP_EXPORT
2483int
2484fuse_vnop_readlink(struct vnop_readlink_args *ap)
2485{
2486    vnode_t       vp      = ap->a_vp;
2487    uio_t         uio     = ap->a_uio;
2488    vfs_context_t context = ap->a_context;
2489
2490    struct fuse_dispatcher fdi;
2491    int err;
2492
2493    fuse_trace_printf_vnop();
2494
2495    if (fuse_isdeadfs(vp)) {
2496        return EBADF;
2497    }
2498
2499    CHECK_BLANKET_DENIAL(vp, context, ENOENT);
2500
2501    if (!vnode_islnk(vp)) {
2502        return EINVAL;
2503    }
2504
2505    if ((err = fdisp_simple_putget_vp(&fdi, FUSE_READLINK, vp, context))) {
2506        return err;
2507    }
2508
2509    if (((char *)fdi.answ)[0] == '/' &&
2510        fuse_get_mpdata(vnode_mount(vp))->dataflags & FSESS_JAIL_SYMLINKS) {
2511            char *mpth = vfs_statfs(vnode_mount(vp))->f_mntonname;
2512            err = uiomove(mpth, (int)strlen(mpth), uio);
2513    }
2514
2515    if (!err) {
2516        err = uiomove(fdi.answ, (int)fdi.iosize, uio);
2517    }
2518
2519    fuse_ticket_drop(fdi.tick);
2520    fuse_invalidate_attr(vp);
2521
2522    return err;
2523}
2524
2525/*
2526    struct vnop_reclaim_args {
2527        struct vnodeop_desc *a_desc;
2528        vnode_t              a_vp;
2529        vfs_context_t        a_context;
2530    };
2531*/
2532FUSE_VNOP_EXPORT
2533int
2534fuse_vnop_reclaim(struct vnop_reclaim_args *ap)
2535{
2536    vnode_t       vp      = ap->a_vp;
2537    vfs_context_t context = ap->a_context;
2538
2539    struct fuse_vnode_data *fvdat = VTOFUD(vp);
2540    struct fuse_filehandle *fufh = NULL;
2541
2542    int type;
2543    HNodeRef hn;
2544
2545    fuse_trace_printf_vnop();
2546
2547    if (!fvdat) {
2548        panic("MacFUSE: no vnode data during recycling");
2549    }
2550
2551    /*
2552     * Cannot do early bail out on a dead file system in this case.
2553     */
2554
2555    for (type = 0; type < FUFH_MAXTYPE; type++) {
2556        fufh = &(fvdat->fufh[type]);
2557        if (FUFH_IS_VALID(fufh)) {
2558            int open_count = fufh->open_count;
2559            int aux_count = fufh->aux_count;
2560            FUFH_USE_RESET(fufh);
2561            if (vfs_isforce(vnode_mount(vp))) {
2562                (void)fuse_filehandle_put(vp, context, type,
2563                                          FUSE_OP_FOREGROUNDED);
2564            } else {
2565
2566                /*
2567                 * This is not a forced unmount. So why is the vnode being
2568                 * reclaimed if a fufh is valid? Well...
2569                 *
2570                 * One reason is that we are dead.
2571                 *
2572                 * Another reason is an unmount-time vlush race with ongoing
2573                 * vnops. Typically happens for a VDIR here.
2574                 *
2575                 * More often, the following happened:
2576                 *
2577                 *     open()
2578                 *     mmap()
2579                 *     close()
2580                 *     pagein... read... strategy
2581                 *     done... reclaim
2582                 */
2583
2584                if (!fuse_isdeadfs(vp)) {
2585
2586                    /*
2587                     * Miselading symptoms (can be seen at unmount time):
2588                     *
2589                     * open
2590                     * close
2591                     * inactive
2592                     * open
2593                     * reclaim <--
2594                     *
2595                     */
2596
2597                    if (open_count != aux_count) {
2598#if M_MACFUSE_ENABLE_UNSUPPORTED
2599                        const char *vname = vnode_getname(vp);
2600                        IOLog("MacFUSE: vnode reclaimed with valid fufh "
2601                              "(%s type=%d, vtype=%d, open_count=%d, busy=%d, "
2602                              "aux_count=%d)\n",
2603                              (vname) ? vname : "?", type, vnode_vtype(vp),
2604                              open_count, vnode_isinuse(vp, 0), aux_count);
2605                        if (vname) {
2606                            vnode_putname(vname);
2607                        }
2608#else
2609                        IOLog("MacFUSE: vnode reclaimed with valid fufh "
2610                              "(type=%d, vtype=%d, open_count=%d, busy=%d, "
2611                              "aux_count=%d)\n",
2612                              type, vnode_vtype(vp), open_count,
2613                              vnode_isinuse(vp, 0), aux_count);
2614#endif /* M_MACFUSE_ENABLE_UNSUPPORTED */
2615                    } /* if counts did not match (both=1 for match currently) */
2616                    FUSE_OSAddAtomic(1, (SInt32 *)&fuse_fh_zombies);
2617                } /* !deadfs */
2618
2619                (void)fuse_filehandle_put(vp, context, type,
2620                                          FUSE_OP_FOREGROUNDED);
2621
2622            } /* !forced unmount */
2623        } /* valid fufh */
2624    } /* fufh loop */
2625
2626    if ((!fuse_isdeadfs(vp)) && (fvdat->nlookup)) {
2627        struct fuse_dispatcher fdi;
2628        fdi.tick = NULL;
2629        fuse_internal_forget_send(vnode_mount(vp), context, VTOI(vp),
2630                                  fvdat->nlookup, &fdi);
2631    }
2632
2633    fuse_vncache_purge(vp);
2634
2635    hn = HNodeFromVNode(vp);
2636    if (HNodeDetachVNode(hn, vp)) {
2637        FSNodeScrub(fvdat);
2638        HNodeScrubDone(hn);
2639        FUSE_OSAddAtomic(-1, (SInt32 *)&fuse_vnodes_current);
2640    }
2641
2642    return 0;
2643}
2644
2645/*
2646    struct vnop_remove_args {
2647        struct vnodeop_desc  *a_desc;
2648        vnode_t               a_dvp;
2649        vnode_t               a_vp;
2650        struct componentname *a_cnp;
2651        int                   a_flags;
2652        vfs_context_t         a_context;
2653    };
2654*/
2655FUSE_VNOP_EXPORT
2656int
2657fuse_vnop_remove(struct vnop_remove_args *ap)
2658{
2659    vnode_t               dvp     = ap->a_dvp;
2660    vnode_t               vp      = ap->a_vp;
2661    struct componentname *cnp     = ap->a_cnp;
2662    int                   flags   = ap->a_flags;
2663    vfs_context_t         context = ap->a_context;
2664
2665    int err;
2666
2667    fuse_trace_printf_vnop();
2668
2669    if (fuse_isdeadfs_fs(vp)) {
2670        panic("MacFUSE: fuse_vnop_remove(): called on a dead file system");
2671    }
2672
2673    CHECK_BLANKET_DENIAL(vp, context, ENOENT);
2674
2675    if (vnode_isdir(vp)) {
2676        return EPERM;
2677    }
2678
2679    /* Check for Carbon delete semantics. */
2680    if ((flags & VNODE_REMOVE_NODELETEBUSY) && vnode_isinuse(vp, 0)) {
2681        return EBUSY;
2682    }
2683
2684    fuse_vncache_purge(vp);
2685
2686    err = fuse_internal_remove(dvp, vp, cnp, FUSE_UNLINK, context);
2687
2688    if (err == 0) {
2689        FUSE_KNOTE(vp, NOTE_DELETE);
2690        FUSE_KNOTE(dvp, NOTE_WRITE);
2691        fuse_vncache_purge(vp);
2692        fuse_invalidate_attr(dvp);
2693        /*
2694         * If we really want, we could...
2695         * if (!vnode_isinuse(vp, 0)) {
2696         *     vnode_recycle(vp);
2697         * }
2698         */
2699    }
2700
2701    return err;
2702}
2703
2704#if M_MACFUSE_ENABLE_XATTR
2705/*
2706    struct vnop_removexattr_args {
2707        struct vnodeop_desc *a_desc;
2708        vnode_t              a_vp;
2709        char                *a_name;
2710        int                  a_options;
2711        vfs_context_t        a_context;
2712    };
2713*/
2714FUSE_VNOP_EXPORT
2715int
2716fuse_vnop_removexattr(struct vnop_removexattr_args *ap)
2717{
2718    vnode_t        vp      = ap->a_vp;
2719    const char    *name    = ap->a_name;
2720    vfs_context_t  context = ap->a_context;
2721
2722    struct fuse_dispatcher fdi;
2723    struct fuse_data      *data;
2724
2725    mount_t mp;
2726    size_t  namelen;
2727
2728    int err = 0;
2729
2730    fuse_trace_printf_vnop();
2731
2732    if (fuse_isdeadfs(vp)) {
2733        return EBADF;
2734    }
2735
2736    CHECK_BLANKET_DENIAL(vp, context, ENOENT);
2737
2738    if (name == NULL || name[0] == '\0') {
2739        return EINVAL;  /* invalid name */
2740    }
2741
2742    mp = vnode_mount(vp);
2743    data = fuse_get_mpdata(mp);
2744
2745    if (fuse_skip_apple_xattr_mp(mp, name)) {
2746        return EPERM;
2747    }
2748
2749    if (data->dataflags & FSESS_AUTO_XATTR) {
2750        return ENOTSUP;
2751    }
2752
2753    if (!fuse_implemented(data, FSESS_NOIMPLBIT(REMOVEXATTR))) {
2754        return ENOTSUP;
2755    }
2756
2757    namelen = strlen(name);
2758
2759    fdisp_init(&fdi, namelen + 1);
2760    fdisp_make_vp(&fdi, FUSE_REMOVEXATTR, vp, context);
2761
2762    memcpy((char *)fdi.indata, name, namelen);
2763    ((char *)fdi.indata)[namelen] = '\0';
2764
2765    err = fdisp_wait_answ(&fdi);
2766    if (!err) {
2767        fuse_ticket_drop(fdi.tick);
2768        VTOFUD(vp)->c_flag |= C_TOUCH_CHGTIME;
2769        fuse_invalidate_attr(vp);
2770        FUSE_KNOTE(vp, NOTE_ATTRIB);
2771    } else {
2772        if (err == ENOSYS) {
2773            fuse_clear_implemented(data, FSESS_NOIMPLBIT(REMOVEXATTR));
2774            return ENOTSUP;
2775        }
2776    }
2777
2778    return err;
2779}
2780#endif /* M_MACFUSE_ENABLE_XATTR */
2781
2782/*
2783    struct vnop_rename_args {
2784        struct vnodeop_desc  *a_desc;
2785        vnode_t               a_fdvp;
2786        vnode_t               a_fvp;
2787        struct componentname *a_fcnp;
2788        vnode_t               a_tdvp;
2789        vnode_t               a_tvp;
2790        struct componentname *a_tcnp;
2791        vfs_context_t         a_context;
2792    };
2793*/
2794FUSE_VNOP_EXPORT
2795int
2796fuse_vnop_rename(struct vnop_rename_args *ap)
2797{
2798    vnode_t fdvp               = ap->a_fdvp;
2799    vnode_t fvp                = ap->a_fvp;
2800    struct componentname *fcnp = ap->a_fcnp;
2801    vnode_t tdvp               = ap->a_tdvp;
2802    vnode_t tvp                = ap->a_tvp;
2803    struct componentname *tcnp = ap->a_tcnp;
2804    vfs_context_t context      = ap->a_context;
2805
2806    int err = 0;
2807
2808    fuse_trace_printf_vnop_novp();
2809
2810    if (fuse_isdeadfs_fs(fdvp)) {
2811        panic("MacFUSE: fuse_vnop_rename(): called on a dead file system");
2812    }
2813
2814    CHECK_BLANKET_DENIAL(fdvp, context, ENOENT);
2815
2816    fuse_vncache_purge(fvp);
2817
2818    err = fuse_internal_rename(fdvp, fvp, fcnp, tdvp, tvp, tcnp, ap->a_context);
2819
2820    if (err == 0) {
2821        FUSE_KNOTE(fdvp, NOTE_WRITE);
2822        fuse_invalidate_attr(fdvp);
2823        if (tdvp != fdvp) {
2824            fuse_invalidate_attr(tdvp);
2825            FUSE_KNOTE(tdvp, NOTE_WRITE);
2826        }
2827    }
2828
2829    if (tvp != NULLVP) {
2830        if (tvp != fvp) {
2831            fuse_vncache_purge(tvp);
2832            FUSE_KNOTE(tvp, NOTE_DELETE);
2833        }
2834        if (err == 0) {
2835
2836            /*
2837             * If we want the file to just "disappear" from the standpoint
2838             * of those who might have it open, we can do a revoke/recycle
2839             * here. Otherwise, don't do anything. Only doing a recycle will
2840             * make our fufh-checking code in reclaim unhappy, leading us to
2841             * proactively panic.
2842             */
2843
2844            /*
2845             * 1. revoke
2846             * 2. recycle
2847             */
2848        }
2849    }
2850
2851    if (vnode_isdir(fvp)) {
2852        if ((tvp != NULLVP) && vnode_isdir(tvp)) {
2853            fuse_vncache_purge(tdvp);
2854        }
2855        fuse_vncache_purge(fdvp);
2856    }
2857
2858    if (err == 0) {
2859        FUSE_KNOTE(fvp, NOTE_RENAME);
2860    }
2861
2862    return err;
2863}
2864
2865/*
2866 *  struct vnop_revoke_args {
2867 *      struct vnodeop_desc  *a_desc;
2868 *      vnode_t               a_vp;
2869 *      int                   a_flags;
2870 *      vfs_context_t         a_context;
2871 *  };
2872 */
2873FUSE_VNOP_EXPORT
2874int
2875fuse_vnop_revoke(struct vnop_revoke_args *ap)
2876{
2877    vnode_t       vp      = ap->a_vp;
2878    vfs_context_t context = ap->a_context;
2879
2880    fuse_trace_printf_vnop();
2881
2882    CHECK_BLANKET_DENIAL(vp, context, ENOENT);
2883
2884    return fuse_internal_revoke(ap->a_vp, ap->a_flags, ap->a_context, 1);
2885}
2886
2887/*
2888    struct vnop_rmdir_args {
2889        struct vnodeop_desc  *a_desc;
2890        vnode_t               a_dvp;
2891        vnode_t               a_vp;
2892        struct componentname *a_cnp;
2893        vfs_context_t         a_context;
2894    };
2895*/
2896FUSE_VNOP_EXPORT
2897int
2898fuse_vnop_rmdir(struct vnop_rmdir_args *ap)
2899{
2900    vnode_t               dvp     = ap->a_dvp;
2901    vnode_t               vp      = ap->a_vp;
2902    struct componentname *cnp     = ap->a_cnp;
2903    vfs_context_t         context = ap->a_context;
2904
2905    int err;
2906
2907    fuse_trace_printf_vnop();
2908
2909    if (fuse_isdeadfs_fs(vp)) {
2910        panic("MacFUSE: fuse_vnop_rmdir(): called on a dead file system");
2911    }
2912
2913    CHECK_BLANKET_DENIAL(vp, context, ENOENT);
2914
2915    if (VTOFUD(vp) == VTOFUD(dvp)) {
2916        return EINVAL;
2917    }
2918
2919    fuse_vncache_purge(vp);
2920
2921    err = fuse_internal_remove(dvp, vp, cnp, FUSE_RMDIR, context);
2922
2923    if (err == 0) {
2924        fuse_invalidate_attr(dvp);
2925        FUSE_KNOTE(dvp, NOTE_WRITE | NOTE_LINK);
2926        FUSE_KNOTE(vp, NOTE_DELETE);
2927    }
2928
2929    return err;
2930}
2931
2932/*
2933struct vnop_select_args {
2934    struct vnodeop_desc *a_desc;
2935    vnode_t              a_vp;
2936    int                  a_which;
2937    int                  a_fflags;
2938    void                *a_wql;
2939    vfs_context_t        a_context;
2940};
2941*/
2942FUSE_VNOP_EXPORT
2943int
2944fuse_vnop_select(__unused struct vnop_select_args *ap)
2945{
2946    fuse_trace_printf_vnop_novp();
2947
2948    return 1;
2949}
2950
2951/*
2952    struct vnop_setattr_args {
2953        struct vnodeop_desc *a_desc;
2954        vnode_t              a_vp;
2955        struct vnode_attr   *a_vap;
2956        vfs_context_t        a_context;
2957    };
2958*/
2959FUSE_VNOP_EXPORT
2960int
2961fuse_vnop_setattr(struct vnop_setattr_args *ap)
2962{
2963    vnode_t            vp      = ap->a_vp;
2964    struct vnode_attr *vap     = ap->a_vap;
2965    vfs_context_t      context = ap->a_context;
2966
2967    struct fuse_dispatcher  fdi;
2968    struct fuse_setattr_in *fsai;
2969
2970    int err = 0;
2971    enum vtype vtyp;
2972    int sizechanged = 0;
2973    uint64_t newsize = 0;
2974
2975    fuse_trace_printf_vnop();
2976
2977    /*
2978     * XXX: Locking
2979     *
2980     * We need to worry about the file size changing in setattr(). If the call
2981     * is indeed altering the size, then:
2982     *
2983     * lock_exclusive(truncatelock)
2984     *   lock(nodelock)
2985     *     set the new size
2986     *   unlock(nodelock)
2987     *   adjust ubc
2988     *   lock(nodelock)
2989     *     do cleanup
2990     *   unlock(nodelock)
2991     * unlock(truncatelock)
2992     * ...
2993     */
2994
2995    if (fuse_isdeadfs(vp)) {
2996        return EBADF;
2997    }
2998
2999    CHECK_BLANKET_DENIAL(vp, context, ENOENT);
3000
3001    fdisp_init(&fdi, sizeof(*fsai));
3002    fdisp_make_vp(&fdi, FUSE_SETATTR, vp, context);
3003    fsai = fdi.indata;
3004
3005    sizechanged = fuse_internal_attr_vat2fsai(vnode_mount(vp), vp, vap,
3006                                              fsai, &newsize);
3007
3008    if (!fsai->valid) {
3009        goto out;
3010    }
3011
3012    vtyp = vnode_vtype(vp);
3013
3014    if (fsai->valid & FATTR_SIZE && vtyp == VDIR) {
3015        err = EISDIR;
3016        goto out;
3017    }
3018
3019    if (vnode_vfsisrdonly(vp) && (fsai->valid & ~FATTR_SIZE || vtyp == VREG)) {
3020        err = EROFS;
3021        goto out;
3022    }
3023
3024    if ((err = fdisp_wait_answ(&fdi))) {
3025        fuse_invalidate_attr(vp);
3026        return err;
3027    }
3028
3029    vtyp = IFTOVT(((struct fuse_attr_out *)fdi.answ)->attr.mode);
3030
3031    if (vnode_vtype(vp) != vtyp) {
3032        if ((vnode_vtype(vp) == VNON) && (vtyp != VNON)) {
3033            /* What just happened here? */
3034        } else {
3035
3036            /*
3037             * STALE vnode, ditch
3038             *
3039             * The vnode has changed its type "behind our back". There's
3040             * nothing really we can do, so let us just force an internal
3041             * revocation and tell the caller to try again, if interested.
3042             */
3043
3044            fuse_internal_vnode_disappear(vp, context, REVOKE_SOFT);
3045
3046            err = EAGAIN;
3047        }
3048    }
3049
3050    if (!err) {
3051        if (sizechanged) {
3052            fuse_invalidate_attr(vp);
3053        } else {
3054            cache_attrs(vp, (struct fuse_attr_out *)fdi.answ);
3055            if (fsai->valid & FATTR_BKUPTIME || fsai->valid & FATTR_CRTIME) {
3056                VTOFUD(vp)->c_flag &= ~C_XTIMES_VALID;
3057            }
3058        }
3059    }
3060
3061out:
3062    fuse_ticket_drop(fdi.tick);
3063    if (!err && sizechanged) {
3064        VTOFUD(vp)->filesize = newsize;
3065        ubc_setsize(vp, (off_t)newsize);
3066    }
3067
3068    if (err == 0) {
3069        FUSE_KNOTE(vp, NOTE_ATTRIB);
3070    }
3071
3072    return err;
3073}
3074
3075#if M_MACFUSE_ENABLE_XATTR
3076/*
3077    struct vnop_setxattr_args {
3078        struct vnodeop_desc *a_desc;
3079        vnode_t              a_vp;
3080        char                *a_name;
3081        uio_t                a_uio;
3082        int                  a_options;
3083        vfs_context_t        a_context;
3084    };
3085*/
3086FUSE_VNOP_EXPORT
3087int
3088fuse_vnop_setxattr(struct vnop_setxattr_args *ap)
3089{
3090    vnode_t       vp      = ap->a_vp;
3091    const char   *name    = ap->a_name;
3092    uio_t         uio     = ap->a_uio;
3093    vfs_context_t context = ap->a_context;
3094
3095    struct fuse_dispatcher   fdi;
3096    struct fuse_setxattr_in *fsxi;
3097    struct fuse_data        *data;
3098
3099    user_addr_t a_baseaddr[FUSE_UIO_BACKUP_MAX];
3100    user_size_t a_length[FUSE_UIO_BACKUP_MAX];
3101
3102    mount_t mp;
3103
3104    int err = 0;
3105    int iov_err = 0;
3106    int i, iov_cnt;
3107    size_t namelen;
3108    size_t attrsize;
3109    off_t  saved_offset;
3110
3111    fuse_trace_printf_vnop();
3112
3113    if (fuse_isdeadfs(vp)) {
3114        return EBADF;
3115    }
3116
3117    CHECK_BLANKET_DENIAL(vp, context, ENOENT);
3118
3119    if (name == NULL || name[0] == '\0') {
3120        return EINVAL;
3121    }
3122
3123    mp = vnode_mount(vp);
3124    data = fuse_get_mpdata(mp);
3125
3126    if (fuse_skip_apple_xattr_mp(mp, name)) {
3127        return EPERM;
3128    }
3129
3130    if (data->dataflags & FSESS_AUTO_XATTR) {
3131        return ENOTSUP;
3132    }
3133
3134    if (!fuse_implemented(data, FSESS_NOIMPLBIT(SETXATTR))) {
3135        return ENOTSUP;
3136    }
3137
3138    attrsize = (size_t)uio_resid(uio);
3139    saved_offset = uio_offset(uio);
3140
3141    iov_cnt = uio_iovcnt(uio);
3142    if (iov_cnt > FUSE_UIO_BACKUP_MAX) {
3143        /* no need to make it more complicated */
3144        iov_cnt = FUSE_UIO_BACKUP_MAX;
3145    }
3146
3147    for (i = 0; i < iov_cnt; i++) {
3148        iov_err = uio_getiov(uio, i, &(a_baseaddr[i]), &(a_length[i]));
3149    }
3150
3151    /*
3152     * Check attrsize for some sane maximum: otherwise, we can fail malloc()
3153     * in fdisp_make_vp().
3154     */
3155    if (attrsize > data->userkernel_bufsize) {
3156        return E2BIG;
3157    }
3158
3159    namelen = strlen(name);
3160
3161    fdisp_init(&fdi, sizeof(*fsxi) + namelen + 1 + attrsize);
3162    err = fdisp_make_vp_canfail(&fdi, FUSE_SETXATTR, vp, ap->a_context);
3163    if (err) {
3164        IOLog("MacFUSE: setxattr failed for too large attribute (%lu)\n",
3165              attrsize);
3166        return ERANGE;
3167    }
3168    fsxi = fdi.indata;
3169
3170    fsxi->size = (uint32_t)attrsize;
3171    fsxi->flags = ap->a_options;
3172    fsxi->position = (uint32_t)saved_offset;
3173
3174    if (attrsize > FUSE_REASONABLE_XATTRSIZE) {
3175        fticket_set_killl(fdi.tick);
3176    }
3177
3178    memcpy((char *)fdi.indata + sizeof(*fsxi), name, namelen);
3179    ((char *)fdi.indata)[sizeof(*fsxi) + namelen] = '\0';
3180
3181    err = uiomove((char *)fdi.indata + sizeof(*fsxi) + namelen + 1,
3182                  (int)attrsize, uio);
3183    if (!err) {
3184        err = fdisp_wait_answ(&fdi);
3185    }
3186
3187    if (!err) {
3188        fuse_ticket_drop(fdi.tick);
3189        fuse_invalidate_attr(vp);
3190        FUSE_KNOTE(vp, NOTE_ATTRIB);
3191        VTOFUD(vp)->c_flag |= C_TOUCH_CHGTIME;
3192    } else {
3193        if ((err == ENOSYS) || (err == ENOTSUP)) {
3194
3195            int a_spacetype = UIO_USERSPACE;
3196
3197            if (err == ENOSYS) {
3198                fuse_clear_implemented(data, FSESS_NOIMPLBIT(SETXATTR));
3199            }
3200
3201            if (iov_err) {
3202                return EAGAIN;
3203            }
3204
3205            if (!uio_isuserspace(uio)) {
3206                a_spacetype = UIO_SYSSPACE;
3207            }
3208
3209            uio_reset(uio, saved_offset, a_spacetype, uio_rw(uio));
3210            for (i = 0; i < iov_cnt; i++) {
3211                uio_addiov(uio, CAST_USER_ADDR_T(a_baseaddr[i]), a_length[i]);
3212            }
3213
3214            return ENOTSUP;
3215        }
3216    }
3217
3218    return err;
3219}
3220#endif /* M_MACFUSE_ENABLE_XATTR */
3221
3222/*
3223    struct vnop_strategy_args {
3224        struct vnodeop_desc *a_desc;
3225        struct buf          *a_bp;
3226    };
3227*/
3228FUSE_VNOP_EXPORT
3229int
3230fuse_vnop_strategy(struct vnop_strategy_args *ap)
3231{
3232    buf_t   bp = ap->a_bp;
3233    vnode_t vp = buf_vnode(bp);
3234
3235    fuse_trace_printf_vnop();
3236
3237    if (!vp || fuse_isdeadfs(vp)) {
3238        buf_seterror(bp, EIO);
3239        buf_biodone(bp);
3240        return EIO;
3241    }
3242
3243    return fuse_internal_strategy_buf(ap);
3244}
3245
3246/*
3247    struct vnop_symlink_args {
3248        struct vnodeop_desc  *a_desc;
3249        vnode_t               a_dvp;
3250        vnode_t              *a_vpp;
3251        struct componentname *a_cnp;
3252        struct vnode_attr    *a_vap;
3253        char                 *a_target;
3254        vfs_context_t         a_context;
3255    };
3256*/
3257FUSE_VNOP_EXPORT
3258int  
3259fuse_vnop_symlink(struct vnop_symlink_args *ap)
3260{           
3261    vnode_t               dvp     = ap->a_dvp; 
3262    vnode_t              *vpp     = ap->a_vpp;
3263    struct componentname *cnp     = ap->a_cnp;
3264    char                 *target  = ap->a_target;
3265    vfs_context_t         context = ap->a_context;
3266            
3267    struct fuse_dispatcher fdi;
3268
3269    int err;
3270    size_t len;
3271        
3272    fuse_trace_printf_vnop_novp();
3273
3274    if (fuse_isdeadfs_fs(dvp)) {
3275        panic("MacFUSE: fuse_vnop_symlink(): called on a dead file system");
3276    }
3277            
3278    CHECK_BLANKET_DENIAL(dvp, context, EPERM);
3279
3280    len = strlen(target) + 1;
3281    fdisp_init(&fdi, len + cnp->cn_namelen + 1);
3282    fdisp_make_vp(&fdi, FUSE_SYMLINK, dvp, context);
3283
3284    memcpy(fdi.indata, cnp->cn_nameptr, cnp->cn_namelen);
3285    ((char *)fdi.indata)[cnp->cn_namelen] = '\0';
3286    memcpy((char *)fdi.indata + cnp->cn_namelen + 1, target, len);
3287
3288    /* XXX: Need to take vap into account. */
3289
3290    err = fuse_internal_newentry_core(dvp, vpp, cnp, VLNK, &fdi, context);
3291
3292    if (err == 0) {
3293        fuse_invalidate_attr(dvp);
3294        FUSE_KNOTE(dvp, NOTE_WRITE);
3295    }
3296
3297    return err;
3298}       
3299
3300/*
3301    struct vnop_write_args {
3302        struct vnodeop_desc *a_desc;
3303        vnode_t              a_vp;
3304        struct uio          *a_uio;
3305        int                  a_ioflag;
3306        vfs_context_t        a_context;
3307    };
3308*/
3309FUSE_VNOP_EXPORT
3310int
3311fuse_vnop_write(struct vnop_write_args *ap)
3312{
3313    vnode_t       vp      = ap->a_vp;
3314    uio_t         uio     = ap->a_uio;
3315    int           ioflag  = ap->a_ioflag;
3316    vfs_context_t context = ap->a_context;
3317
3318    int          error = 0;
3319    int          lflag;
3320    off_t        offset;
3321    off_t        zero_off;
3322    off_t        filesize;
3323    off_t        original_offset;
3324    off_t        original_size;
3325    user_ssize_t original_resid;
3326
3327    struct fuse_vnode_data *fvdat;
3328
3329    /*
3330     * XXX: Locking
3331     *
3332     * lock_shared(truncatelock)
3333     * lock(nodelock)
3334     * if (file is being extended) {
3335     *     unlock(nodelock)
3336     *     unlock(truncatelock)
3337     *     lock_exclusive(truncatelock)
3338     *     lock(nodelock)
3339     *     current_size = the file's current size
3340     * }
3341     * if (file is being extended) { // check again
3342     *     // do whatever needs to be done to allocate storage
3343     * }
3344     * // We are always block-aligned
3345     * unlock(nodelock)
3346     * call the cluster layer
3347     * adjust ubc
3348     * lock(nodelock)
3349     * do cleanup 
3350     * unlock(nodelock)
3351     * unlock(truncatelock)
3352     */
3353
3354    fuse_trace_printf_vnop();
3355
3356    if (fuse_isdeadfs(vp)) {
3357        return EIO;
3358    }
3359
3360    fvdat = VTOFUD(vp);
3361
3362    switch (vnode_vtype(vp)) {
3363    case VREG:
3364        break;
3365
3366    case VDIR:
3367        return EISDIR;
3368
3369    default:
3370        return EPERM; /* or EINVAL? panic? */
3371    }
3372
3373    original_resid = uio_resid(uio);
3374    original_offset = uio_offset(uio);
3375    offset = original_offset;
3376
3377    if (original_resid == 0) {
3378        return E_NONE;
3379    }
3380
3381    if (original_offset < 0) {
3382        return EINVAL;
3383    }
3384
3385    if (fuse_isdirectio(vp)) { /* direct_io */
3386        fufh_type_t             fufh_type = FUFH_WRONLY;
3387        struct fuse_dispatcher  fdi;
3388        struct fuse_filehandle *fufh = NULL;
3389        struct fuse_write_in   *fwi  = NULL;
3390        struct fuse_write_out  *fwo  = NULL;
3391        struct fuse_data       *data = fuse_get_mpdata(vnode_mount(vp));
3392
3393        size_t chunksize;
3394        off_t  diff;
3395
3396        fufh = &(fvdat->fufh[fufh_type]);
3397
3398        if (!FUFH_IS_VALID(fufh)) {
3399            fufh_type = FUFH_RDWR;
3400            fufh = &(fvdat->fufh[fufh_type]);
3401            if (!FUFH_IS_VALID(fufh)) {
3402                fufh = NULL;
3403            } else {
3404                /* Write falling back to FUFH_RDWR. */
3405            }
3406        }
3407
3408        if (!fufh) {
3409            /* Failing direct I/O because of no fufh. */
3410            return EIO;
3411        } else {
3412            /* Using existing fufh of type fufh_type. */
3413        }
3414
3415        fdisp_init(&fdi, 0);
3416
3417        while (uio_resid(uio) > 0) {
3418            chunksize = min((size_t)uio_resid(uio), data->iosize);
3419            fdi.iosize = sizeof(*fwi) + chunksize;
3420            fdisp_make_vp(&fdi, FUSE_WRITE, vp, context);
3421            fwi = fdi.indata;
3422            fwi->fh = fufh->fh_id;
3423            fwi->offset = uio_offset(uio);
3424            fwi->size = (uint32_t)chunksize;
3425
3426            error = uiomove((char *)fdi.indata + sizeof(*fwi), (int)chunksize,
3427                            uio);
3428            if (error) {
3429                break;
3430            }
3431
3432            error = fdisp_wait_answ(&fdi);
3433            if (error) {
3434                return error;
3435            }
3436
3437            fwo = (struct fuse_write_out *)fdi.answ;
3438
3439            diff = chunksize - fwo->size;
3440            if (diff < 0) {
3441                error = EINVAL;
3442                break;
3443            }
3444
3445            uio_setresid(uio, (uio_resid(uio) + diff));
3446            uio_setoffset(uio, (uio_offset(uio) - diff));
3447
3448        } /* while */
3449
3450        if (!error) {
3451            fuse_invalidate_attr(vp);
3452        }
3453
3454        fuse_ticket_drop(fdi.tick);
3455
3456        return error;
3457
3458    } /* direct_io */
3459
3460    /* !direct_io */
3461
3462    /* Be wary of a size change here. */
3463
3464    original_size = fvdat->filesize;
3465
3466    if (ioflag & IO_APPEND) {
3467        /* Arrange for append */
3468        uio_setoffset(uio, fvdat->filesize);
3469        offset = fvdat->filesize;
3470    }
3471
3472    if (offset < 0) {
3473        return EFBIG;
3474    }
3475
3476#if M_MACFUSE_EXPERIMENTAL_JUNK
3477    if (original_resid == 0) {
3478        return 0;
3479    }
3480
3481    if (offset + original_resid > /* some maximum file size */) {
3482        return EFBIG;
3483    }
3484#endif
3485
3486    if (offset + original_resid > original_size) {
3487        /* Need to extend the file. */
3488        filesize = offset + original_resid;
3489        fvdat->filesize = filesize;
3490    } else {
3491        /* Original size OK. */
3492        filesize = original_size;
3493    }
3494
3495    lflag = (ioflag & (IO_SYNC | IO_NOCACHE));
3496
3497    if (fuse_isnoubc(vp)) {
3498        lflag |= (IO_SYNC | IO_NOCACHE);
3499    } else if (vfs_issynchronous(vnode_mount(vp))) {
3500        lflag |= IO_SYNC;
3501    }
3502
3503    if (offset > original_size) {
3504        zero_off = original_size;
3505        lflag |= IO_HEADZEROFILL;
3506        /* Zero-filling enabled. */
3507    } else {
3508        zero_off = 0;
3509    }
3510
3511    error = cluster_write(vp, uio, (off_t)original_size, (off_t)filesize,
3512                          (off_t)zero_off, (off_t)0, lflag);
3513        
3514    if (!error) {
3515        if (uio_offset(uio) > original_size) {
3516            /* Updating to new size. */
3517            fvdat->filesize = uio_offset(uio);
3518            ubc_setsize(vp, (off_t)fvdat->filesize);
3519            FUSE_KNOTE(vp, NOTE_WRITE | NOTE_EXTEND);
3520        } else {
3521            fvdat->filesize = original_size;
3522            FUSE_KNOTE(vp, NOTE_WRITE);
3523        }
3524        fuse_invalidate_attr(vp);
3525    }
3526
3527    /*
3528     * If original_resid > uio_resid(uio), we could set an internal
3529     * flag bit to "update" (e.g., dep->de_flag |= DE_UPDATE).
3530     */
3531        
3532    /*
3533     * If the write failed and they want us to, truncate the file back
3534     * to the size it was before the write was attempted.
3535     */
3536
3537/* errexit: */
3538
3539    if (error) {
3540        if (ioflag & IO_UNIT) {
3541            /*
3542             * e.g.: detrunc(dep, original_size, ioflag & IO_SYNC, context);
3543             */
3544            uio_setoffset(uio, original_offset);
3545            uio_setresid(uio, original_resid);
3546        } else {
3547            /*
3548             * e.g.: detrunc(dep, dep->de_FileSize, ioflag & IO_SYNC, context);
3549             */
3550            if (uio_resid(uio) != original_resid) {
3551                error = 0;
3552            }
3553        }
3554    } else if (ioflag & IO_SYNC) {
3555        /*
3556         * e.g.: error = deupdat(dep, 1, context);
3557         */
3558    }
3559
3560    /*
3561    if ((original_resid > uio_resid(uio)) &&
3562        !fuse_vfs_context_issuser(context)) {
3563        // clear setuid/setgid here
3564    }
3565     */
3566
3567    return error;
3568}
3569
3570#if M_MACFUSE_ENABLE_FIFOFS
3571
3572/* fifofs */
3573
3574FUSE_VNOP_EXPORT
3575int
3576fuse_fifo_vnop_close(struct vnop_close_args *ap)
3577{
3578    if (vnode_isinuse(ap->a_vp, 1)) {
3579        /* XXX: TBD */
3580    }
3581
3582    return fifo_close(ap);
3583}
3584
3585FUSE_VNOP_EXPORT
3586int
3587fuse_fifo_vnop_read(struct vnop_read_args *ap)
3588{
3589    VTOFUD(ap->a_vp)->c_flag |= C_TOUCH_ACCTIME;
3590
3591    return fifo_read(ap);
3592}
3593
3594FUSE_VNOP_EXPORT
3595int
3596fuse_fifo_vnop_write(struct vnop_write_args *ap)
3597{
3598    VTOFUD(ap->a_vp)->c_flag |= (C_TOUCH_CHGTIME | C_TOUCH_MODTIME);
3599
3600    return fifo_write(ap);
3601}
3602
3603#endif /* M_MACFUSE_ENABLE_FIFOFS */
3604
3605#if M_MACFUSE_ENABLE_SPECFS
3606
3607/* specfs */
3608
3609FUSE_VNOP_EXPORT
3610int
3611fuse_spec_vnop_close(struct vnop_close_args *ap)
3612{
3613    if (vnode_isinuse(ap->a_vp, 1)) {
3614        /* XXX: TBD */
3615    }
3616
3617    return spec_close(ap);
3618}
3619
3620FUSE_VNOP_EXPORT
3621int
3622fuse_spec_vnop_read(struct vnop_read_args *ap)
3623{
3624    VTOFUD(ap->a_vp)->c_flag |= C_TOUCH_ACCTIME;
3625
3626    return spec_read(ap);
3627}
3628
3629FUSE_VNOP_EXPORT
3630int
3631fuse_spec_vnop_write(struct vnop_write_args *ap)
3632{
3633    VTOFUD(ap->a_vp)->c_flag |= (C_TOUCH_CHGTIME | C_TOUCH_MODTIME);
3634
3635    return spec_write(ap);
3636}
3637
3638#endif /* M_MACFUSE_ENABLE_SPECFS */
3639
3640struct vnodeopv_entry_desc fuse_vnode_operation_entries[] = {
3641    { &vnop_access_desc,        (fuse_vnode_op_t) fuse_vnop_access        },
3642    { &vnop_advlock_desc,       (fuse_vnode_op_t) err_advlock             },
3643//  { &vnop_allocate_desc,      (fuse_vnode_op_t) fuse_vnop_allocate      },
3644    { &vnop_blktooff_desc,      (fuse_vnode_op_t) fuse_vnop_blktooff      },
3645    { &vnop_blockmap_desc,      (fuse_vnode_op_t) fuse_vnop_blockmap      },
3646//  { &vnop_bwrite_desc,        (fuse_vnode_op_t) fuse_vnop_bwrite        },
3647    { &vnop_close_desc,         (fuse_vnode_op_t) fuse_vnop_close         },
3648//  { &vnop_copyfile_desc,      (fuse_vnode_op_t) fuse_vnop_copyfile      },
3649    { &vnop_create_desc,        (fuse_vnode_op_t) fuse_vnop_create        },
3650    { &vnop_default_desc,       (fuse_vnode_op_t) vn_default_error        },
3651    { &vnop_exchange_desc,      (fuse_vnode_op_t) fuse_vnop_exchange      },
3652    { &vnop_fsync_desc,         (fuse_vnode_op_t) fuse_vnop_fsync         },
3653    { &vnop_getattr_desc,       (fuse_vnode_op_t) fuse_vnop_getattr       },
3654//  { &vnop_getattrlist_desc,   (fuse_vnode_op_t) fuse_vnop_getattrlist   },
3655#if M_MACFUSE_ENABLE_XATTR
3656    { &vnop_getxattr_desc,      (fuse_vnode_op_t) fuse_vnop_getxattr      },
3657#endif /* M_MACFUSE_ENABLE_XATTR */
3658    { &vnop_inactive_desc,      (fuse_vnode_op_t) fuse_vnop_inactive      },
3659    { &vnop_ioctl_desc,         (fuse_vnode_op_t) fuse_vnop_ioctl         },
3660    { &vnop_link_desc,          (fuse_vnode_op_t) fuse_vnop_link          },
3661#if M_MACFUSE_ENABLE_XATTR
3662    { &vnop_listxattr_desc,     (fuse_vnode_op_t) fuse_vnop_listxattr     },
3663#endif /* M_MACFUSE_ENABLE_XATTR */
3664    { &vnop_lookup_desc,        (fuse_vnode_op_t) fuse_vnop_lookup        },
3665#if M_MACFUSE_ENABLE_KQUEUE
3666    { &vnop_kqfilt_add_desc,    (fuse_vnode_op_t) fuse_vnop_kqfilt_add    },
3667    { &vnop_kqfilt_remove_desc, (fuse_vnode_op_t) fuse_vnop_kqfilt_remove },
3668#endif /* M_MACFUSE_ENABLE_KQUEUE */
3669    { &vnop_mkdir_desc,         (fuse_vnode_op_t) fuse_vnop_mkdir         },
3670    { &vnop_mknod_desc,         (fuse_vnode_op_t) fuse_vnop_mknod         },
3671    { &vnop_mmap_desc,          (fuse_vnode_op_t) fuse_vnop_mmap          },
3672    { &vnop_mnomap_desc,        (fuse_vnode_op_t) fuse_vnop_mnomap        },
3673    { &vnop_offtoblk_desc,      (fuse_vnode_op_t) fuse_vnop_offtoblk      },
3674    { &vnop_open_desc,          (fuse_vnode_op_t) fuse_vnop_open          },
3675    { &vnop_pagein_desc,        (fuse_vnode_op_t) fuse_vnop_pagein        },
3676    { &vnop_pageout_desc,       (fuse_vnode_op_t) fuse_vnop_pageout       },
3677    { &vnop_pathconf_desc,      (fuse_vnode_op_t) fuse_vnop_pathconf      },
3678    { &vnop_read_desc,          (fuse_vnode_op_t) fuse_vnop_read          },
3679    { &vnop_readdir_desc,       (fuse_vnode_op_t) fuse_vnop_readdir       },
3680//  { &vnop_readdirattr_desc,   (fuse_vnode_op_t) fuse_vnop_readdirattr   },
3681    { &vnop_readlink_desc,      (fuse_vnode_op_t) fuse_vnop_readlink      },
3682    { &vnop_reclaim_desc,       (fuse_vnode_op_t) fuse_vnop_reclaim       },
3683    { &vnop_remove_desc,        (fuse_vnode_op_t) fuse_vnop_remove        },
3684#if M_MACFUSE_ENABLE_XATTR
3685    { &vnop_removexattr_desc,   (fuse_vnode_op_t) fuse_vnop_removexattr   },
3686#endif /* M_MACFUSE_ENABLE_XATTR */
3687    { &vnop_rename_desc,        (fuse_vnode_op_t) fuse_vnop_rename        },
3688    { &vnop_revoke_desc,        (fuse_vnode_op_t) fuse_vnop_revoke        },
3689    { &vnop_rmdir_desc,         (fuse_vnode_op_t) fuse_vnop_rmdir         },
3690//  { &vnop_searchfs_desc,      (fuse_vnode_op_t) fuse_vnop_searchfs      },
3691    { &vnop_select_desc,        (fuse_vnode_op_t) fuse_vnop_select        },
3692    { &vnop_setattr_desc,       (fuse_vnode_op_t) fuse_vnop_setattr       },
3693//  { &vnop_setattrlist_desc,   (fuse_vnode_op_t) fuse_vnop_setattrlist   }, 
3694#if M_MACFUSE_ENABLE_XATTR
3695    { &vnop_setxattr_desc,      (fuse_vnode_op_t) fuse_vnop_setxattr      },
3696#endif /* M_MACFUSE_ENABLE_XATTR */
3697    { &vnop_strategy_desc,      (fuse_vnode_op_t) fuse_vnop_strategy      },
3698    { &vnop_symlink_desc,       (fuse_vnode_op_t) fuse_vnop_symlink       },
3699//  { &vnop_whiteout_desc,      (fuse_vnode_op_t) fuse_vnop_whiteout      },
3700    { &vnop_write_desc,         (fuse_vnode_op_t) fuse_vnop_write         },
3701    { NULL, NULL }
3702};
3703
3704#if M_MACFUSE_ENABLE_FIFOFS
3705
3706/* fifofs */
3707
3708struct vnodeopv_entry_desc fuse_fifo_operation_entries[] = {
3709    { &vnop_advlock_desc,       (fuse_fifo_op_t)err_advlock             },
3710    { &vnop_blktooff_desc,      (fuse_fifo_op_t)err_blktooff            },
3711    { &vnop_blockmap_desc,      (fuse_fifo_op_t)err_blockmap            },
3712    { &vnop_bwrite_desc,        (fuse_fifo_op_t)fifo_bwrite             },
3713    { &vnop_close_desc,         (fuse_fifo_op_t)fuse_fifo_vnop_close    }, // c
3714    { &vnop_copyfile_desc,      (fuse_fifo_op_t)err_copyfile            },
3715    { &vnop_create_desc,        (fuse_fifo_op_t)fifo_create             },
3716    { &vnop_default_desc,       (fuse_fifo_op_t)vn_default_error        },
3717    { &vnop_fsync_desc,         (fuse_fifo_op_t)fuse_vnop_fsync         },
3718    { &vnop_getattr_desc,       (fuse_fifo_op_t)fuse_vnop_getattr       },
3719    { &vnop_inactive_desc,      (fuse_fifo_op_t)fuse_vnop_inactive      },    
3720    { &vnop_ioctl_desc,         (fuse_fifo_op_t)fifo_ioctl              },
3721#if M_MACFUSE_ENABLE_KQUEUE
3722    { &vnop_kqfilt_add_desc,    (fuse_fifo_op_t)fuse_vnop_kqfilt_add    },
3723    { &vnop_kqfilt_remove_desc, (fuse_fifo_op_t)fuse_vnop_kqfilt_remove },
3724#endif
3725    { &vnop_link_desc,          (fuse_fifo_op_t)fifo_link               },
3726    { &vnop_lookup_desc,        (fuse_fifo_op_t)fifo_lookup             },
3727    { &vnop_mkdir_desc,         (fuse_fifo_op_t)fifo_mkdir              },
3728    { &vnop_mknod_desc,         (fuse_fifo_op_t)fifo_mknod              },
3729    { &vnop_mmap_desc,          (fuse_fifo_op_t)fifo_mmap               },
3730    { &vnop_offtoblk_desc,      (fuse_fifo_op_t)err_offtoblk            },
3731    { &vnop_open_desc,          (fuse_fifo_op_t)fifo_open               },
3732    { &vnop_pagein_desc,        (fuse_fifo_op_t)fuse_vnop_pagein        }, // n
3733    { &vnop_pageout_desc,       (fuse_fifo_op_t)fuse_vnop_pageout       }, // n
3734    { &vnop_pathconf_desc,      (fuse_fifo_op_t)fifo_pathconf           },
3735    { &vnop_read_desc,          (fuse_fifo_op_t)fuse_fifo_vnop_read     }, // c
3736    { &vnop_readdir_desc,       (fuse_fifo_op_t)fifo_readdir            },
3737    { &vnop_readlink_desc,      (fuse_fifo_op_t)fifo_readlink           },
3738    { &vnop_reclaim_desc,       (fuse_fifo_op_t)fuse_vnop_reclaim       }, // n
3739    { &vnop_remove_desc,        (fuse_fifo_op_t)fifo_remove             },
3740    { &vnop_rename_desc,        (fuse_fifo_op_t)fifo_rename             },
3741    { &vnop_revoke_desc,        (fuse_fifo_op_t)fifo_revoke             },
3742    { &vnop_rmdir_desc,         (fuse_fifo_op_t)fifo_rmdir              },
3743    { &vnop_select_desc,        (fuse_fifo_op_t)fifo_select             },
3744    { &vnop_setattr_desc,       (fuse_fifo_op_t)fuse_vnop_setattr       }, // n
3745    { &vnop_strategy_desc,      (fuse_fifo_op_t)fifo_strategy           },
3746    { &vnop_symlink_desc,       (fuse_fifo_op_t)fifo_symlink            },
3747    { &vnop_write_desc,         (fuse_fifo_op_t)fuse_fifo_vnop_write    },
3748    { (struct vnodeop_desc*)NULL, (fuse_fifo_op_t)NULL                  }
3749};
3750#endif /* M_MACFUSE_ENABLE_FIFOFS */
3751
3752#if M_MACFUSE_ENABLE_SPECFS
3753
3754/* specfs */
3755
3756struct vnodeopv_entry_desc fuse_spec_operation_entries[] = {
3757    { &vnop_advlock_desc,  (fuse_spec_op_t)err_advlock          },
3758    { &vnop_blktooff_desc, (fuse_spec_op_t)fuse_vnop_blktooff   }, // native
3759    { &vnop_close_desc,    (fuse_spec_op_t)fuse_spec_vnop_close }, // custom
3760    { &vnop_copyfile_desc, (fuse_spec_op_t)err_copyfile         },
3761    { &vnop_create_desc,   (fuse_spec_op_t)spec_create          },
3762    { &vnop_default_desc,  (fuse_spec_op_t)vn_default_error     },
3763    { &vnop_fsync_desc,    (fuse_spec_op_t)fuse_vnop_fsync      }, // native
3764    { &vnop_getattr_desc,  (fuse_spec_op_t)fuse_vnop_getattr    }, // native
3765    { &vnop_inactive_desc, (fuse_spec_op_t)fuse_vnop_inactive   }, // native
3766    { &vnop_ioctl_desc,    (fuse_spec_op_t)spec_ioctl           },
3767    { &vnop_link_desc,     (fuse_spec_op_t)spec_link            },
3768    { &vnop_lookup_desc,   (fuse_spec_op_t)spec_lookup          },
3769    { &vnop_mkdir_desc,    (fuse_spec_op_t)spec_mkdir           },
3770    { &vnop_mknod_desc,    (fuse_spec_op_t)spec_mknod           },
3771    { &vnop_mmap_desc,     (fuse_spec_op_t)spec_mmap            },
3772    { &vnop_offtoblk_desc, (fuse_spec_op_t)fuse_vnop_offtoblk   }, // native
3773    { &vnop_open_desc,     (fuse_spec_op_t)spec_open            },
3774    { &vnop_pagein_desc,   (fuse_spec_op_t)fuse_vnop_pagein     }, // native
3775    { &vnop_pageout_desc,  (fuse_spec_op_t)fuse_vnop_pageout    }, // native
3776    { &vnop_pathconf_desc, (fuse_spec_op_t)spec_pathconf        },
3777    { &vnop_read_desc,     (fuse_spec_op_t)fuse_spec_vnop_read  }, // custom
3778    { &vnop_readdir_desc,  (fuse_spec_op_t)spec_readdir         },
3779    { &vnop_readlink_desc, (fuse_spec_op_t)spec_readlink        },
3780    { &vnop_reclaim_desc,  (fuse_spec_op_t)fuse_vnop_reclaim    }, // native
3781    { &vnop_remove_desc,   (fuse_spec_op_t)spec_remove          },
3782    { &vnop_rename_desc,   (fuse_spec_op_t)spec_rename          },
3783    { &vnop_revoke_desc,   (fuse_spec_op_t)spec_revoke          },
3784    { &vnop_rmdir_desc,    (fuse_spec_op_t)spec_rmdir           },
3785    { &vnop_select_desc,   (fuse_spec_op_t)spec_select          },
3786    { &vnop_setattr_desc,  (fuse_spec_op_t)fuse_vnop_setattr    }, // native
3787    { &vnop_strategy_desc, (fuse_spec_op_t)spec_strategy        },
3788    { &vnop_symlink_desc,  (fuse_spec_op_t)spec_symlink         },
3789    { &vnop_write_desc,    (fuse_spec_op_t)fuse_spec_vnop_write }, // custom
3790    { (struct vnodeop_desc*)NULL, (fuse_spec_op_t)NULL          },
3791};
3792#endif /* M_MACFUSE_ENABLE_SPECFS */