PageRenderTime 128ms CodeModel.GetById 22ms app.highlight 92ms 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

Large files files are truncated, but you can click here to view the full 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         * - n…

Large files files are truncated, but you can click here to view the full file