PageRenderTime 120ms CodeModel.GetById 10ms app.highlight 94ms RepoModel.GetById 1ms app.codeStats 1ms

/core/10.4/fusefs/fuse_vnops.c

http://macfuse.googlecode.com/
C | 3875 lines | 2360 code | 724 blank | 791 comment | 516 complexity | d78dbbfeaecb7cf092ab887dbfd75d5a 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*/
  52static int
  53fuse_vnop_access(struct vnop_access_args *ap)
  54{
  55    vnode_t       vp      = ap->a_vp;
  56    int           action  = ap->a_action;
  57    vfs_context_t context = ap->a_context;
  58
  59    struct fuse_access_param facp;
  60    struct fuse_vnode_data *fvdat = VTOFUD(vp);
  61    struct fuse_data *data = fuse_get_mpdata(vnode_mount(vp));
  62
  63    fuse_trace_printf_vnop();
  64
  65    if (fuse_isdeadfs(vp)) {
  66        if (vnode_isvroot(vp)) {
  67            return 0;
  68        } else {
  69            return EBADF;
  70        }
  71    }
  72
  73    if (!(data->dataflags & FSESS_INITED)) {
  74        if (vnode_isvroot(vp)) {
  75            if (fuse_vfs_context_issuser(context) ||
  76               (fuse_match_cred(data->daemoncred,
  77                                vfs_context_ucred(context)) == 0)) {
  78                return 0;
  79            }
  80        }
  81        return EBADF;
  82    }
  83
  84    if (vnode_islnk(vp)) {
  85        return 0;
  86    }
  87
  88    bzero(&facp, sizeof(facp));
  89
  90    if (fvdat->flag & FN_ACCESS_NOOP) {
  91        fvdat->flag &= ~FN_ACCESS_NOOP;
  92    } else {
  93        facp.facc_flags |= FACCESS_DO_ACCESS;
  94    }   
  95
  96    facp.facc_flags |= FACCESS_FROM_VNOP;
  97
  98    return fuse_internal_access(vp, action, context, &facp);
  99}       
 100
 101/*
 102    struct vnop_blktooff_args {
 103        struct vnodeop_desc *a_desc;
 104        vnode_t              a_vp;
 105        daddr64_t            a_lblkno;
 106        off_t               *a_offset;
 107    };
 108*/
 109static int 
 110fuse_vnop_blktooff(struct vnop_blktooff_args *ap)
 111{       
 112    vnode_t    vp        = ap->a_vp;
 113    daddr64_t  lblkno    = ap->a_lblkno;
 114    off_t     *offsetPtr = ap->a_offset;
 115
 116    struct fuse_data *data; 
 117
 118    fuse_trace_printf_vnop();
 119
 120    if (fuse_isdeadfs(vp)) {
 121        return EIO;
 122    }
 123
 124    data = fuse_get_mpdata(vnode_mount(vp));
 125
 126    *offsetPtr = lblkno * (off_t)(data->blocksize);
 127
 128    return 0;
 129}
 130
 131/*
 132    struct vnop_blockmap_args {
 133        struct vnodeop_desc *a_desc;
 134        vnode_t              a_vp;
 135        off_t                a_foffset;
 136        size_t               a_size;
 137        daddr64_t           *a_bpn;
 138        size_t              *a_run;
 139        void                *a_poff;
 140        int                  a_flags;
 141        vfs_context_t        a_context;
 142    };
 143*/
 144static int
 145fuse_vnop_blockmap(struct vnop_blockmap_args *ap)
 146{
 147    vnode_t       vp      = ap->a_vp;
 148    off_t         foffset = ap->a_foffset;
 149    size_t        size    = ap->a_size;
 150    daddr64_t    *bpnPtr  = ap->a_bpn;
 151    size_t       *runPtr  = ap->a_run;
 152    int          *poffPtr = (int *)ap->a_poff;
 153
 154    /* Ignoring flags and context */
 155
 156    struct fuse_vnode_data *fvdat;
 157    struct fuse_data       *data;
 158
 159    off_t contiguousPhysicalBytes;
 160
 161    fuse_trace_printf_vnop();
 162
 163    if (fuse_isdeadfs(vp)) {
 164        return EIO;
 165    }
 166
 167    if (vnode_isdir(vp)) {
 168        return ENOTSUP;
 169    }
 170
 171    if (ap->a_bpn == NULL) {
 172        return 0;
 173    }
 174
 175    fvdat = VTOFUD(vp);
 176    data = fuse_get_mpdata(vnode_mount(vp));
 177
 178    /*
 179     * We could assert that:
 180     *
 181     * (foffset % data->blocksize) == 0
 182     * (foffset < fvdat->filesize)
 183     * (size % data->blocksize) == 0)
 184     */
 185
 186    *bpnPtr = foffset / data->blocksize;
 187
 188    contiguousPhysicalBytes = \
 189        fvdat->filesize - (off_t)(*bpnPtr * data->blocksize);
 190
 191    /* contiguousPhysicalBytes cannot really be negative (could assert) */
 192
 193    if (contiguousPhysicalBytes > (off_t)size) {
 194        contiguousPhysicalBytes = (off_t)size;
 195    }
 196
 197    if (runPtr != NULL) {
 198        *runPtr = (size_t)contiguousPhysicalBytes;
 199    }
 200
 201    if (poffPtr != NULL) {
 202        *poffPtr = 0;
 203    }
 204
 205    return 0;
 206}
 207
 208/*
 209    struct vnop_close_args {
 210        struct vnodeop_desc *a_desc;
 211        vnode_t              a_vp;
 212        int                  a_fflag;
 213        vfs_context_t        a_context;
 214    };
 215*/
 216static int
 217fuse_vnop_close(struct vnop_close_args *ap)
 218{
 219    vnode_t       vp      = ap->a_vp;
 220    int           fflag   = ap->a_fflag;
 221    vfs_context_t context = ap->a_context;
 222
 223    int err   = 0;
 224    int isdir = (vnode_isdir(vp)) ? 1 : 0;
 225
 226    fufh_type_t fufh_type;
 227    struct fuse_data *data;
 228
 229    struct fuse_vnode_data *fvdat = VTOFUD(vp);
 230    struct fuse_filehandle *fufh  = NULL;
 231
 232    fuse_trace_printf_vnop();
 233
 234    if (fuse_isdeadfs(vp)) {
 235        return 0;
 236    }
 237
 238    /* vclean() calls VNOP_CLOSE with fflag set to IO_NDELAY. */
 239    if (fflag == IO_NDELAY) {
 240        return 0;
 241    }
 242
 243    if (isdir) {
 244        fufh_type = FUFH_RDONLY;
 245    } else {
 246        fufh_type = fuse_filehandle_xlate_from_fflags(fflag);
 247    }
 248
 249    fufh = &(fvdat->fufh[fufh_type]);
 250
 251    if (!FUFH_IS_VALID(fufh)) {
 252        IOLog("MacFUSE: fufh invalid in close [type=%d oc=%d vtype=%d cf=%d]\n",
 253              fufh_type, fufh->open_count, vnode_vtype(vp), fflag);
 254        return 0;
 255    }
 256
 257    if (isdir) {
 258        goto skipdir;
 259    }
 260
 261    /*
 262     * Enforce sync-on-close unless explicitly told not to.
 263     *
 264     * We do this to maintain correct semantics in the not so common case when
 265     * you create a file with O_RDWR but without write permissions--you /are/
 266     * supposed to be able to write to such a file given the descriptor you
 267     * you got from open()/create(). Therefore, if we don't finish all our
 268     * writing before we close this precious writable descriptor, we might
 269     * be doomed.
 270     */
 271    if (vnode_hasdirtyblks(vp) && !fuse_isnosynconclose(vp)) {
 272        (void)cluster_push(vp, IO_SYNC | IO_CLOSE);
 273    }
 274
 275    data = fuse_get_mpdata(vnode_mount(vp));
 276    if (fuse_implemented(data, FSESS_NOIMPLBIT(FLUSH))) {
 277
 278        struct fuse_dispatcher  fdi;
 279        struct fuse_flush_in   *ffi;
 280
 281        fdisp_init(&fdi, sizeof(*ffi));
 282        fdisp_make_vp(&fdi, FUSE_FLUSH, vp, context);
 283
 284        ffi = fdi.indata;
 285        ffi->fh = fufh->fh_id;
 286        ffi->unused = 0;
 287        ffi->padding = 0;
 288        ffi->lock_owner = 0;
 289
 290        err = fdisp_wait_answ(&fdi);
 291
 292        if (!err) {
 293            fuse_ticket_drop(fdi.tick);
 294        } else {
 295            if (err == ENOSYS) {
 296                fuse_clear_implemented(data, FSESS_NOIMPLBIT(FLUSH));
 297                err = 0;
 298            }
 299        }
 300    }
 301
 302skipdir:
 303
 304    /* This must be done after we have flushed any pending I/O. */
 305    FUFH_USE_DEC(fufh);
 306
 307    if (!FUFH_IS_VALID(fufh)) {
 308        (void)fuse_filehandle_put(vp, context, fufh_type, FUSE_OP_FOREGROUNDED);
 309    }
 310
 311    return err;
 312}
 313
 314/*
 315    struct vnop_create_args {
 316        struct vnodeop_desc  *a_desc;
 317        vnode_t               a_dvp;
 318        vnode_t              *a_vpp;
 319        struct componentname *a_cnp;
 320        struct vnode_attr    *a_vap;
 321        vfs_context_t         a_context;
 322    };
 323*/
 324static int
 325fuse_vnop_create(struct vnop_create_args *ap)
 326{
 327    vnode_t               dvp     = ap->a_dvp;
 328    vnode_t              *vpp     = ap->a_vpp;
 329    struct componentname *cnp     = ap->a_cnp;
 330    struct vnode_attr    *vap     = ap->a_vap;
 331    vfs_context_t         context = ap->a_context;
 332
 333    struct fuse_open_in    *foi;
 334    struct fuse_mknod_in    fmni;
 335    struct fuse_entry_out  *feo;
 336    struct fuse_dispatcher  fdi;
 337    struct fuse_dispatcher *fdip = &fdi;
 338
 339    int err;
 340    int gone_good_old = 0;
 341
 342    struct fuse_data *data = NULL;
 343
 344    mount_t mp = vnode_mount(dvp);
 345    uint64_t parent_nodeid = VTOFUD(dvp)->nodeid;
 346    mode_t mode = MAKEIMODE(vap->va_type, vap->va_mode);
 347
 348    fuse_trace_printf_vnop_novp();
 349
 350    if (fuse_isdeadfs_fs(dvp)) {
 351        panic("MacFUSE: fuse_vnop_create(): called on a dead file system");
 352    }
 353
 354    CHECK_BLANKET_DENIAL(dvp, context, EPERM);
 355
 356    if (fuse_skip_apple_double_mp(mp, cnp->cn_nameptr, cnp->cn_namelen)) {
 357        return EPERM;
 358    }
 359
 360    bzero(&fdi, sizeof(fdi));
 361
 362    data = fuse_get_mpdata(mp);
 363
 364    if (!fuse_implemented(data, FSESS_NOIMPLBIT(CREATE)) ||
 365        (vap->va_type != VREG)) {
 366
 367        /* User-space file system does not implement CREATE */
 368
 369        goto good_old;
 370    }
 371
 372    fdisp_init(fdip, sizeof(*foi) + cnp->cn_namelen + 1);
 373    fdisp_make(fdip, FUSE_CREATE, vnode_mount(dvp), parent_nodeid, context);
 374
 375    foi = fdip->indata;
 376    foi->mode = mode;
 377
 378    /* XXX: We /always/ creat() like this. Wish we were on Linux. */
 379    foi->flags = O_CREAT | O_RDWR;
 380
 381    memcpy((char *)fdip->indata + sizeof(*foi), cnp->cn_nameptr,
 382           cnp->cn_namelen);
 383    ((char *)fdip->indata)[sizeof(*foi) + cnp->cn_namelen] = '\0';
 384
 385    err = fdisp_wait_answ(fdip);
 386
 387    if (err == ENOSYS) {
 388        fuse_clear_implemented(data, FSESS_NOIMPLBIT(CREATE));
 389        fdip->tick = NULL;
 390        goto good_old;
 391    } else if (err) {
 392        goto undo;
 393    }
 394
 395    goto bringup;
 396
 397good_old:
 398    gone_good_old = 1;
 399    fmni.mode = mode; /* fvdat->flags; */
 400    fmni.rdev = 0;
 401    fuse_internal_newentry_makerequest(vnode_mount(dvp), parent_nodeid, cnp,
 402                                       FUSE_MKNOD, &fmni, sizeof(fmni),
 403                                       fdip, context);
 404    err = fdisp_wait_answ(fdip);
 405    if (err) {
 406        goto undo;
 407    }
 408
 409bringup:
 410    feo = fdip->answ;
 411
 412    if ((err = fuse_internal_checkentry(feo, VREG))) { // VBLK/VCHR not allowed
 413        fuse_ticket_drop(fdip->tick);
 414        goto undo;
 415    }
 416
 417    err = FSNodeGetOrCreateFileVNodeByID(
 418              vpp, (gone_good_old) ? 0 : FN_CREATING,
 419              feo, mp, dvp, context, NULL /* oflags */);
 420    if (err) {
 421       if (gone_good_old) {
 422           fuse_internal_forget_send(mp, context, feo->nodeid, 1, fdip);
 423       } else {
 424           struct fuse_release_in *fri;
 425           uint64_t nodeid = feo->nodeid;
 426           uint64_t fh_id = ((struct fuse_open_out *)(feo + 1))->fh;
 427
 428           fdisp_init(fdip, sizeof(*fri));
 429           fdisp_make(fdip, FUSE_RELEASE, mp, nodeid, context);
 430           fri = fdip->indata;
 431           fri->fh = fh_id;
 432           fri->flags = OFLAGS(mode);
 433           fuse_insert_callback(fdip->tick, fuse_internal_forget_callback);
 434           fuse_insert_message(fdip->tick);
 435       }
 436       return err;
 437    }
 438
 439    fdip->answ = gone_good_old ? NULL : feo + 1;
 440
 441    if (!gone_good_old) {
 442
 443        uint64_t x_fh_id = ((struct fuse_open_out *)(feo + 1))->fh;
 444        uint32_t x_open_flags = ((struct fuse_open_out *)(feo + 1))->open_flags;
 445        struct fuse_vnode_data *fvdat = VTOFUD(*vpp);
 446        struct fuse_filehandle *fufh = &(fvdat->fufh[FUFH_RDWR]);
 447
 448        fufh->fh_id = x_fh_id;
 449        fufh->open_flags = x_open_flags;
 450
 451        /*
 452         * We're stashing this to be picked up by open. Meanwhile, we set
 453         * the use count to 1 because that's what it is. The use count will
 454         * later transfer to the slot that this handle ends up falling in.
 455         */
 456        fufh->open_count = 1;
 457
 458        FUSE_OSAddAtomic(1, (SInt32 *)&fuse_fh_current);
 459    }
 460
 461    cache_purge_negatives(dvp);
 462
 463    fuse_ticket_drop(fdip->tick);
 464
 465    FUSE_KNOTE(dvp, NOTE_WRITE);
 466
 467    return 0;
 468
 469undo:
 470    return err;
 471}
 472
 473/*
 474    struct vnop_exchange_args {
 475        struct vnodeop_desc *a_desc;
 476        vnode_t              a_fvp;
 477        vnode_t              a_tvp;
 478        int                  a_options;
 479        vfs_context_t        a_context;
 480    };
 481*/
 482static int
 483fuse_vnop_exchange(struct vnop_exchange_args *ap)
 484{
 485
 486#if M_MACFUSE_ENABLE_EXCHANGE
 487
 488    vnode_t       fvp     = ap->a_fvp;
 489    vnode_t       tvp     = ap->a_tvp;
 490    int           options = ap->a_options;
 491    vfs_context_t context = ap->a_context;
 492
 493    const char *fname = NULL;
 494    const char *tname = NULL;
 495    size_t flen = 0;
 496    size_t tlen = 0;
 497
 498    struct fuse_data *data = fuse_get_mpdata(vnode_mount(fvp));
 499 
 500    int err = 0;
 501
 502    fuse_trace_printf_vnop_novp();
 503
 504    if (vnode_mount(fvp) != vnode_mount(tvp)) {
 505        return EXDEV;
 506    }
 507
 508    /* We now know f and t are on the same volume. */
 509
 510    if (!fuse_implemented(data, FSESS_NOIMPLBIT(EXCHANGE))) {
 511        return ENOTSUP;
 512    }
 513
 514    if (fuse_isnovncache(fvp)) {
 515        return ENOTSUP;
 516    }
 517
 518    if (fvp == tvp) {
 519        return EINVAL;
 520    }
 521
 522    if (!vnode_isreg(fvp) || !vnode_isreg(tvp)) {
 523        return EINVAL;
 524    }
 525
 526    if (fuse_isdeadfs_fs(fvp)) {
 527        panic("MacFUSE: fuse_vnop_exchange(): called on a dead file system");
 528    }
 529
 530    fname = vnode_getname(fvp);
 531    if (!fname) {
 532        return EIO;
 533    }
 534
 535    tname = vnode_getname(tvp);
 536    if (!tname) {
 537        vnode_putname(fname);
 538        return EIO;
 539    }
 540
 541    flen = strlen(fname);
 542    tlen = strlen(tname);
 543
 544    if ((flen > 2) && (*fname == '.') && (*(fname + 1) == '_')) {
 545        err = EINVAL;
 546        goto out;
 547    }
 548
 549    if ((tlen > 2) && (*tname == '.') && (*(tname + 1) == '_')) {
 550        err = EINVAL;
 551        goto out;
 552    }
 553
 554    err = fuse_internal_exchange(fvp, fname, flen, tvp, tname, tlen, options,
 555                                 context);
 556
 557    if (err == ENOSYS) {
 558        fuse_clear_implemented(data, FSESS_NOIMPLBIT(EXCHANGE));
 559        err = ENOTSUP;
 560    }
 561
 562out:
 563
 564    vnode_putname(fname);
 565    vnode_putname(tname);
 566
 567    if (err == 0) {
 568        FUSE_KNOTE(fvp, NOTE_ATTRIB);
 569        FUSE_KNOTE(tvp, NOTE_ATTRIB);
 570    }
 571
 572    return err;
 573
 574#else /* !M_MACFUSE_ENABLE_EXCHANGE */
 575
 576    (void)ap;
 577
 578    return ENOTSUP;
 579
 580#endif /* M_MACFUSE_ENABLE_EXCHANGE */
 581
 582}
 583
 584/*
 585 * Our vnop_fsync roughly corresponds to the FUSE_FSYNC method. The Linux
 586 * version of FUSE also has a FUSE_FLUSH method.
 587 *
 588 * On Linux, fsync() synchronizes a file's complete in-core state with that
 589 * on disk. The call is not supposed to return until the system has completed 
 590 * that action or until an error is detected.
 591 *
 592 * Linux also has an fdatasync() call that is similar to fsync() but is not
 593 * required to update the metadata such as access time and modification time.
 594 */
 595
 596/*
 597    struct vnop_fsync_args {
 598        struct vnodeop_desc *a_desc;
 599        vnode_t              a_vp;
 600        int                  a_waitfor;
 601        vfs_context_t        a_context;
 602    };
 603*/
 604static int
 605fuse_vnop_fsync(struct vnop_fsync_args *ap)
 606{
 607    vnode_t       vp      = ap->a_vp;
 608    int           waitfor = ap->a_waitfor;
 609    vfs_context_t context = ap->a_context;
 610
 611    struct fuse_dispatcher  fdi;
 612    struct fuse_filehandle *fufh;
 613    struct fuse_vnode_data *fvdat = VTOFUD(vp);
 614
 615    int type, err = 0, tmp_err = 0;
 616    (void)waitfor;
 617
 618    fuse_trace_printf_vnop();
 619
 620    if (fuse_isdeadfs(vp)) {
 621        return 0;
 622    }
 623
 624    cluster_push(vp, 0);
 625
 626    /*
 627     * struct timeval tv;
 628     * int wait = (waitfor == MNT_WAIT)
 629     *
 630     * In another world, we could be doing something like:
 631     *
 632     * buf_flushdirtyblks(vp, wait, 0, (char *)"fuse_fsync");
 633     * microtime(&tv);
 634     * ...
 635     */
 636
 637    /*
 638     * - UBC and vnode are in lock-step.
 639     * - Can call vnode_isinuse().
 640     * - Can call ubc_sync_range().
 641     */
 642
 643    mount_t mp = vnode_mount(vp);
 644
 645    if (!fuse_implemented(fuse_get_mpdata(mp), ((vnode_isdir(vp)) ?
 646                FSESS_NOIMPLBIT(FSYNCDIR) : FSESS_NOIMPLBIT(FSYNC)))) {
 647        err = ENOSYS;
 648        goto out;
 649    }
 650
 651    fdisp_init(&fdi, 0);
 652    for (type = 0; type < FUFH_MAXTYPE; type++) {
 653        fufh = &(fvdat->fufh[type]);
 654        if (FUFH_IS_VALID(fufh)) {
 655            tmp_err = fuse_internal_fsync(vp, context, fufh, &fdi,
 656                                          FUSE_OP_FOREGROUNDED);
 657            if (tmp_err) {
 658                err = tmp_err;
 659            }
 660        }
 661    }
 662
 663out:
 664    if ((err == ENOSYS) && !fuse_isnosyncwrites_mp(mp)) {
 665        err = 0;
 666    }
 667
 668    return err;
 669}
 670
 671/*
 672    struct vnop_getattr_args {
 673        struct vnodeop_desc *a_desc;
 674        vnode_t              a_vp;
 675        struct vnode_attr   *a_vap;
 676        vfs_context_t        a_context;
 677    };
 678*/
 679static int
 680fuse_vnop_getattr(struct vnop_getattr_args *ap)
 681{
 682    vnode_t            vp      = ap->a_vp;
 683    struct vnode_attr *vap     = ap->a_vap;
 684    vfs_context_t      context = ap->a_context;
 685
 686    int err = 0;
 687    int dataflags;
 688    struct timespec uptsp;
 689    struct fuse_dispatcher fdi;
 690    struct fuse_data *data;
 691
 692    data = fuse_get_mpdata(vnode_mount(vp));
 693
 694    fuse_trace_printf_vnop();
 695
 696    if (fuse_isdeadfs(vp)) {
 697        if (vnode_isvroot(vp)) {
 698            goto fake;
 699        } else {
 700            return EBADF;
 701        }
 702    }
 703
 704    if (!vnode_isvroot(vp) || !fuse_vfs_context_issuser(context)) {
 705        CHECK_BLANKET_DENIAL(vp, context, ENOENT);
 706    }
 707
 708    dataflags = data->dataflags;
 709
 710    /* Note that we are not bailing out on a dead file system just yet. */
 711
 712    /* look for cached attributes */
 713    nanouptime(&uptsp);
 714    if (fuse_timespec_cmp(&uptsp, &VTOFUD(vp)->attr_valid, <=)) {
 715        if (vap != VTOVA(vp)) {
 716            fuse_internal_attr_loadvap(vp, vap, context);
 717        }
 718        return 0;
 719    }
 720
 721    if (!(dataflags & FSESS_INITED)) {
 722        if (!vnode_isvroot(vp)) {
 723            fdata_set_dead(data);
 724            err = ENOTCONN;
 725            return err;
 726        } else {
 727            goto fake;
 728        }
 729    }
 730
 731    if ((err = fdisp_simple_putget_vp(&fdi, FUSE_GETATTR, vp, context))) {
 732        if ((err == ENOTCONN) && vnode_isvroot(vp)) {
 733            /* see comment at similar place in fuse_statfs() */
 734            goto fake;
 735        }
 736        if (err == ENOENT) {
 737            fuse_internal_vnode_disappear(vp, context, REVOKE_SOFT);
 738        }
 739        return err;
 740    }
 741
 742    /* XXX: Could check the sanity/volatility of va_mode here. */
 743
 744    if ((((struct fuse_attr_out *)fdi.answ)->attr.mode & S_IFMT) == 0) {
 745        return EIO;
 746    }
 747
 748    cache_attrs(vp, (struct fuse_attr_out *)fdi.answ);
 749
 750    VTOFUD(vp)->c_flag &= ~C_XTIMES_VALID;
 751
 752    fuse_internal_attr_loadvap(vp, vap, context);
 753
 754#if M_MACFUSE_EXPERIMENTAL_JUNK
 755    if (vap != VTOVA(vp)) {
 756        memcpy(vap, VTOVA(vp), sizeof(*vap));
 757    }
 758#endif
 759
 760    /* ATTR_FUDGE_CASE */
 761    if (vnode_isreg(vp) && fuse_isnoubc(vp)) {
 762        /*
 763         * This is for those cases when the file size changed without us
 764         * knowing, and we want to catch up.
 765         *
 766         * For the sake of sanity, we don't want to do it with UBC.
 767         * We also don't want to do it when we have asynchronous writes
 768         * enabled because we might have pending writes on *our* side.
 769         * We're not researching distributed file systems here!
 770         *
 771         */
 772
 773        struct fuse_vnode_data *fvdat = VTOFUD(vp);
 774        off_t new_filesize = ((struct fuse_attr_out *)fdi.answ)->attr.size;
 775        fvdat->filesize = new_filesize;
 776    }
 777
 778    fuse_ticket_drop(fdi.tick);
 779
 780    if (vnode_vtype(vp) != vap->va_type) {
 781        if ((vnode_vtype(vp) == VNON) && (vap->va_type != VNON)) {
 782            /*
 783             * We should be doing the following:
 784             *
 785             * vp->vtype = vap->v_type
 786             */
 787        } else {
 788
 789            /*
 790             * STALE vnode, ditch
 791             *
 792             * The vnode has changed its type "behind our back". There's
 793             * nothing really we can do, so let us just force an internal
 794             * revocation.
 795             */
 796
 797            fuse_internal_vnode_disappear(vp, context, REVOKE_SOFT);
 798            return EIO;
 799        }
 800    }
 801
 802    return 0;
 803
 804fake:
 805    bzero(vap, sizeof(*vap));
 806    VATTR_RETURN(vap, va_type, vnode_vtype(vp));
 807    VATTR_RETURN(vap, va_uid, data->daemoncred->cr_uid);
 808    VATTR_RETURN(vap, va_gid, data->daemoncred->cr_gid);
 809    VATTR_RETURN(vap, va_mode, S_IRWXU);
 810
 811    return 0;
 812}
 813
 814#if M_MACFUSE_ENABLE_XATTR
 815/*
 816    struct vnop_getxattr_args {
 817        struct vnodeop_desc *a_desc;
 818        vnode_t              a_vp;
 819        char                *a_name;
 820        uio_t                a_uio;
 821        size_t              *a_size;
 822        int                  a_options;
 823        vfs_context_t        a_context;
 824    };
 825*/
 826static int
 827fuse_vnop_getxattr(struct vnop_getxattr_args *ap)
 828{
 829    vnode_t       vp      = ap->a_vp;
 830    const char   *name    = ap->a_name;
 831    uio_t         uio     = ap->a_uio;
 832    vfs_context_t context = ap->a_context;
 833
 834    struct fuse_dispatcher    fdi;
 835    struct fuse_getxattr_in  *fgxi; 
 836    struct fuse_getxattr_out *fgxo;
 837    struct fuse_data         *data;
 838    mount_t mp;
 839
 840    int err = 0;
 841    size_t namelen;      
 842    
 843    fuse_trace_printf_vnop();
 844
 845    if (fuse_isdeadfs(vp)) {
 846        return EBADF;
 847    }
 848
 849    CHECK_BLANKET_DENIAL(vp, context, ENOENT);
 850
 851    if (name == NULL || name[0] == '\0') {
 852        return EINVAL;
 853    }
 854
 855    mp = vnode_mount(vp);
 856    data = fuse_get_mpdata(mp);
 857
 858    if (fuse_skip_apple_xattr_mp(mp, name)) {
 859        return EPERM;
 860    }
 861
 862    if (data->dataflags & FSESS_AUTO_XATTR) {
 863        return ENOTSUP;
 864    }
 865
 866    if (!fuse_implemented(data, FSESS_NOIMPLBIT(GETXATTR))) {
 867        return ENOTSUP;
 868    }
 869
 870    namelen = strlen(name);
 871
 872    fdisp_init(&fdi, sizeof(*fgxi) + namelen + 1);
 873    fdisp_make_vp(&fdi, FUSE_GETXATTR, vp, context);
 874    fgxi = fdi.indata;
 875    
 876    if (uio) {
 877        fgxi->size = (uint32_t)uio_resid(uio);
 878    } else {
 879        fgxi->size = 0;
 880    }
 881
 882    fgxi->position = (uint32_t)uio_offset(uio);
 883    
 884    memcpy((char *)fdi.indata + sizeof(*fgxi), name, namelen);
 885    ((char *)fdi.indata)[sizeof(*fgxi) + namelen] = '\0';
 886
 887    if (fgxi->size > FUSE_REASONABLE_XATTRSIZE) {
 888        fticket_set_killl(fdi.tick);
 889    }
 890
 891    err = fdisp_wait_answ(&fdi);
 892    if (err) {
 893        if (err == ENOSYS) {
 894            fuse_clear_implemented(data, FSESS_NOIMPLBIT(GETXATTR));
 895            return ENOTSUP;
 896        }
 897        return err;  
 898    }                
 899                     
 900    if (uio) {       
 901        *ap->a_size = fdi.iosize;
 902        if ((user_ssize_t)fdi.iosize > uio_resid(uio)) {
 903            err = ERANGE;
 904        } else {
 905            err = uiomove((char *)fdi.answ, (int)fdi.iosize, uio);
 906        }
 907    } else {
 908        fgxo = (struct fuse_getxattr_out *)fdi.answ;
 909        *ap->a_size = fgxo->size;
 910    }
 911
 912    fuse_ticket_drop(fdi.tick);
 913
 914    return err;
 915}
 916#endif /* M_MACFUSE_ENABLE_XATTR */
 917
 918/*
 919    struct vnop_inactive_args {
 920        struct vnodeop_desc *a_desc;
 921        vnode_t              a_vp;
 922        vfs_context_t        a_context;
 923    };
 924*/
 925static int
 926fuse_vnop_inactive(struct vnop_inactive_args *ap)
 927{
 928    vnode_t       vp      = ap->a_vp;
 929    vfs_context_t context = ap->a_context;
 930
 931    struct fuse_vnode_data *fvdat = VTOFUD(vp);
 932    struct fuse_filehandle *fufh = NULL;
 933
 934    int fufh_type;
 935
 936    fuse_trace_printf_vnop();
 937
 938    /*
 939     * Cannot do early bail out on a dead file system in this case.
 940     */
 941
 942    for (fufh_type = 0; fufh_type < FUFH_MAXTYPE; fufh_type++) {
 943
 944        fufh = &(fvdat->fufh[fufh_type]);
 945
 946        if (FUFH_IS_VALID(fufh)) {
 947            FUFH_USE_RESET(fufh);
 948            (void)fuse_filehandle_put(vp, context, fufh_type,
 949                                      FUSE_OP_FOREGROUNDED);
 950        }
 951    }
 952
 953    return 0;
 954}
 955
 956extern int fuse_setextendedsecurity(mount_t mp, int state);
 957
 958/*
 959    struct vnop_ioctl_args {
 960        struct vnodeop_desc *a_desc;
 961        vnode_t              a_vp;
 962        u_long               a_command;
 963        caddr_t              a_data;
 964        int                  a_fflag;
 965        vfs_context_t        a_context;
 966    };
 967*/
 968static int
 969fuse_vnop_ioctl(struct vnop_ioctl_args *ap)
 970{
 971    vnode_t       vp      = ap->a_vp;
 972    u_long        cmd     = ap->a_command;
 973    vfs_context_t context = ap->a_context;
 974
 975    int ret = EINVAL;
 976
 977    fuse_trace_printf_vnop();
 978
 979    if (fuse_isdeadfs(vp)) {
 980        return EBADF;
 981    }
 982
 983    switch (cmd) {
 984    case FSCTLSETACLSTATE:
 985        {
 986            int state;
 987            mount_t mp;
 988            struct fuse_data *data;
 989
 990            if (ap->a_data == NULL) {
 991                return EINVAL;
 992            }
 993
 994            mp = vnode_mount(vp);
 995            data = fuse_get_mpdata(mp);
 996
 997            if (!fuse_vfs_context_issuser(context) &&
 998                !(fuse_match_cred(data->daemoncred,
 999                                  vfs_context_ucred(context)))) {
1000                return EPERM;
1001            }
1002
1003            state = *(int *)ap->a_data;
1004
1005            return fuse_setextendedsecurity(mp, state);
1006        }
1007        break;
1008
1009    case FSCTLALTERVNODEFORINODE:
1010        /*
1011         * This is the fsctl() version of the AVFI device ioctl's in
1012         * fuse_device.c. Since the device ioctl's must be used from
1013         * within the file system (we don't allow multiple device opens),
1014         * it's rather painful to test/experiment with them. The fsctl
1015         * version is easier to use. To simplify things, the "path" in
1016         * the fsctl() call /must/ be the root of the file system.
1017         */
1018        if (!vnode_isvroot(vp)) {
1019            return EINVAL;
1020        }
1021
1022        ret = fuse_internal_ioctl_avfi(vp, context,
1023                                       (struct fuse_avfi_ioctl *)(ap->a_data));
1024        break;
1025
1026    default:
1027        break;
1028    }
1029
1030    return ret;
1031}
1032
1033
1034#if M_MACFUSE_ENABLE_KQUEUE
1035
1036#include "fuse_knote.h"
1037
1038/*
1039    struct vnop_kqfilt_add_args {
1040        struct vnodeop_desc  *a_desc;
1041        vnode_t               a_vp;
1042        struct knote         *a_kn;
1043        struct proc          *p;
1044        vfs_context_t         a_context;
1045    };
1046 */
1047static int
1048fuse_vnop_kqfilt_add(struct vnop_kqfilt_add_args *ap)
1049{
1050    vnode_t       vp = ap->a_vp;
1051    struct knote *kn = ap->a_kn;
1052
1053    fuse_trace_printf_vnop();
1054
1055    if (fuse_isdeadfs(vp)) {
1056        return EBADF;
1057    }
1058
1059    switch (kn->kn_filter) {
1060    case EVFILT_READ:
1061        if (vnode_isreg(vp)) {
1062            kn->kn_fop = &fuseread_filtops;
1063        } else {
1064            return EINVAL;
1065        }
1066        break;
1067
1068    case EVFILT_WRITE:
1069        if (vnode_isreg(vp)) {
1070            kn->kn_fop = &fusewrite_filtops;
1071        } else {
1072            return EINVAL;
1073        }
1074        break;
1075
1076    case EVFILT_VNODE:
1077        kn->kn_fop = &fusevnode_filtops;
1078        break;
1079
1080    default:
1081        return 1;
1082    }
1083
1084    kn->kn_hook = (caddr_t)vp;
1085    kn->kn_hookid = vnode_vid(vp);
1086
1087    /* lock */
1088    KNOTE_ATTACH(&VTOFUD(vp)->c_knotes, kn);
1089    /* unlock */
1090
1091    return 0;
1092}
1093
1094
1095/*
1096    struct vnop_kqfilt_add_args {
1097        struct vnodeop_desc  *a_desc;
1098        vnode_t               a_vp;
1099        uintptr_t             ident;
1100        vfs_context_t         a_context;
1101    };
1102*/
1103static int
1104fuse_vnop_kqfilt_remove(__unused struct vnop_kqfilt_remove_args *ap)
1105{
1106    fuse_trace_printf_vnop_novp();
1107
1108    return ENOTSUP;
1109}
1110
1111#endif /* M_MACFUSE_ENABLE_KQUEUE */
1112
1113
1114/*
1115    struct vnop_link_args {
1116        struct vnodeop_desc  *a_desc;
1117        vnode_t               a_vp;
1118        vnode_t               a_tdvp;
1119        struct componentname *a_cnp;
1120        vfs_context_t         a_context;
1121    };
1122*/
1123static int
1124fuse_vnop_link(struct vnop_link_args *ap)
1125{
1126    vnode_t               vp      = ap->a_vp;
1127    vnode_t               tdvp    = ap->a_tdvp;
1128    struct componentname *cnp     = ap->a_cnp;
1129    vfs_context_t         context = ap->a_context;
1130
1131    struct vnode_attr *vap = VTOVA(vp);
1132
1133    struct fuse_dispatcher fdi;
1134    struct fuse_entry_out *feo;
1135    struct fuse_link_in    fli;
1136
1137    int err;
1138
1139    fuse_trace_printf_vnop();
1140
1141    if (fuse_isdeadfs_fs(vp)) {
1142        panic("MacFUSE: fuse_vnop_link(): called on a dead file system");
1143    }
1144
1145    if (vnode_mount(tdvp) != vnode_mount(vp)) {
1146        return EXDEV;
1147    }
1148
1149    if (vap->va_nlink >= FUSE_LINK_MAX) {
1150        return EMLINK;
1151    }
1152
1153    CHECK_BLANKET_DENIAL(vp, context, EPERM);
1154
1155    fli.oldnodeid = VTOI(vp);
1156
1157    fdisp_init(&fdi, 0);
1158    fuse_internal_newentry_makerequest(vnode_mount(tdvp), VTOI(tdvp), cnp,
1159                                       FUSE_LINK, &fli, sizeof(fli), &fdi,
1160                                       context);
1161    if ((err = fdisp_wait_answ(&fdi))) {
1162        return err;
1163    }
1164
1165    feo = fdi.answ;
1166
1167    err = fuse_internal_checkentry(feo, vnode_vtype(vp));
1168    fuse_ticket_drop(fdi.tick);
1169    fuse_invalidate_attr(tdvp);
1170    fuse_invalidate_attr(vp);
1171    
1172    if (err == 0) {
1173        FUSE_KNOTE(vp, NOTE_LINK);
1174        FUSE_KNOTE(tdvp, NOTE_WRITE);
1175        VTOFUD(vp)->nlookup++;
1176    }
1177
1178    return err;
1179}
1180
1181#if M_MACFUSE_ENABLE_XATTR
1182/*
1183    struct vnop_listxattr_args {
1184        struct vnodeop_desc *a_desc;
1185        vnode_t              a_vp;
1186        uio_t                a_uio;
1187        size_t              *a_size;
1188        int                  a_options;
1189        vfs_context_t        a_context;
1190    };
1191*/
1192static int
1193fuse_vnop_listxattr(struct vnop_listxattr_args *ap)
1194/*
1195    struct vnop_listxattr_args {
1196        struct vnodeop_desc *a_desc;
1197        vnode_t              a_vp;
1198        uio_t                a_uio;
1199        size_t              *a_size;
1200        int                  a_options;
1201        vfs_context_t        a_context;
1202    };
1203*/
1204{
1205    vnode_t       vp      = ap->a_vp;
1206    uio_t         uio     = ap->a_uio;
1207    vfs_context_t context = ap->a_context;
1208
1209    struct fuse_dispatcher    fdi;
1210    struct fuse_getxattr_in  *fgxi;
1211    struct fuse_getxattr_out *fgxo;
1212    struct fuse_data         *data;
1213
1214    int err = 0;
1215
1216    fuse_trace_printf_vnop();
1217
1218    if (fuse_isdeadfs(vp)) {
1219        return EBADF;
1220    }
1221
1222    CHECK_BLANKET_DENIAL(vp, context, ENOENT);
1223
1224    data = fuse_get_mpdata(vnode_mount(vp));
1225
1226    if (data->dataflags & FSESS_AUTO_XATTR) {
1227        return ENOTSUP;
1228    }
1229
1230    if (!fuse_implemented(data, FSESS_NOIMPLBIT(LISTXATTR))) {
1231        return ENOTSUP;
1232    }
1233
1234    fdisp_init(&fdi, sizeof(*fgxi));
1235    fdisp_make_vp(&fdi, FUSE_LISTXATTR, vp, context);
1236    fgxi = fdi.indata;
1237    if (uio) {
1238        fgxi->size = (uint32_t)uio_resid(uio);
1239    } else {
1240        fgxi->size = 0;
1241    }
1242
1243    err = fdisp_wait_answ(&fdi);
1244    if (err) {
1245        if (err == ENOSYS) {
1246            fuse_clear_implemented(data, FSESS_NOIMPLBIT(LISTXATTR));
1247            return ENOTSUP;
1248        }
1249        return err;
1250    }
1251
1252    if (uio) {
1253        *ap->a_size = fdi.iosize;
1254        if ((user_ssize_t)fdi.iosize > uio_resid(uio)) {
1255            err = ERANGE;
1256        } else {
1257            err = uiomove((char *)fdi.answ, (int)fdi.iosize, uio);
1258        }
1259    } else {
1260        fgxo = (struct fuse_getxattr_out *)fdi.answ;
1261        *ap->a_size = fgxo->size;
1262    }
1263
1264    fuse_ticket_drop(fdi.tick);
1265
1266    return err;
1267}
1268#endif /* M_MACFUSE_ENABLE_XATTR */
1269
1270/*
1271    struct vnop_lookup_args {
1272        struct vnodeop_desc  *a_desc;
1273        vnode_t               a_dvp;
1274        vnode_t              *a_vpp;
1275        struct componentname *a_cnp;
1276        vfs_context_t         a_context;
1277    };
1278*/
1279static int
1280fuse_vnop_lookup(struct vnop_lookup_args *ap)
1281{
1282    vnode_t dvp               = ap->a_dvp;
1283    vnode_t *vpp              = ap->a_vpp;
1284    struct componentname *cnp = ap->a_cnp;
1285    vfs_context_t context     = ap->a_context;
1286
1287    int nameiop               = cnp->cn_nameiop;
1288    int flags                 = cnp->cn_flags;
1289    int wantparent            = flags & (LOCKPARENT|WANTPARENT);
1290    int islastcn              = flags & ISLASTCN;
1291    int isdot                 = FALSE;
1292    int isdotdot              = FALSE;
1293    mount_t mp                = vnode_mount(dvp);
1294
1295    int err                   = 0;
1296    int lookup_err            = 0;
1297    vnode_t vp                = NULL;
1298    vnode_t pdp               = (vnode_t)NULL;
1299    uint64_t size             = FUSE_ZERO_SIZE;
1300
1301    struct fuse_dispatcher fdi;
1302    enum   fuse_opcode     op;
1303
1304    uint64_t nodeid;
1305    uint64_t parent_nodeid;
1306
1307    *vpp = NULLVP;
1308
1309    fuse_trace_printf_vnop_novp();
1310
1311    if (fuse_isdeadfs(dvp)) {
1312        *ap->a_vpp = NULLVP;
1313        return ENOTDIR;
1314    }
1315
1316    if (fuse_skip_apple_double_mp(mp, cnp->cn_nameptr, cnp->cn_namelen)) {
1317        return ENOENT;
1318    }
1319
1320    if (!vnode_isdir(dvp)) {
1321        return ENOTDIR;
1322    }
1323
1324    if (islastcn && vfs_isrdonly(mp) && (nameiop != LOOKUP)) {
1325        return EROFS;
1326    }
1327
1328    if (cnp->cn_namelen > MAXNAMLEN) {
1329        return ENAMETOOLONG;
1330    }
1331
1332    if (flags & ISDOTDOT) {
1333        isdotdot = TRUE;
1334    } else if ((cnp->cn_nameptr[0] == '.') && (cnp->cn_namelen == 1)) {
1335        isdot = TRUE;
1336    } 
1337
1338    if (isdotdot) {
1339        pdp = VTOFUD(dvp)->parentvp;
1340        nodeid = VTOI(pdp);
1341        parent_nodeid = VTOFUD(dvp)->parent_nodeid;
1342        fdisp_init(&fdi, 0);
1343        op = FUSE_GETATTR;
1344        goto calldaemon;
1345    } else if (isdot) {
1346        nodeid = VTOI(dvp);
1347        parent_nodeid = VTOFUD(dvp)->parent_nodeid;
1348        fdisp_init(&fdi, 0);
1349        op = FUSE_GETATTR;
1350        goto calldaemon;
1351    } else {
1352        err = fuse_vncache_lookup(dvp, vpp, cnp);
1353        switch (err) {
1354
1355        case -1: /* positive match */
1356            if (fuse_isnovncache(*vpp)) {
1357                fuse_vncache_purge(*vpp);
1358                vnode_put(*vpp);
1359                *vpp = NULL;
1360                FUSE_OSAddAtomic(1, (SInt32 *)&fuse_lookup_cache_overrides);
1361                err = 0;
1362                break; /* pretend it's a miss */
1363            }
1364            FUSE_OSAddAtomic(1, (SInt32 *)&fuse_lookup_cache_hits);
1365            return 0;
1366
1367        case 0: /* no match in cache (or aged out) */
1368            FUSE_OSAddAtomic(1, (SInt32 *)&fuse_lookup_cache_misses);
1369            break;
1370
1371        case ENOENT: /* negative match */
1372             /* fall through */
1373        default:
1374             return err;
1375        }
1376    }
1377
1378    nodeid = VTOI(dvp);
1379    parent_nodeid = VTOI(dvp);
1380    fdisp_init(&fdi, cnp->cn_namelen + 1);
1381    op = FUSE_LOOKUP;
1382
1383calldaemon:
1384    fdisp_make(&fdi, op, mp, nodeid, context);
1385
1386    if (op == FUSE_LOOKUP) {
1387        memcpy(fdi.indata, cnp->cn_nameptr, cnp->cn_namelen);
1388        ((char *)fdi.indata)[cnp->cn_namelen] = '\0';
1389    }
1390
1391    lookup_err = fdisp_wait_answ(&fdi);
1392
1393    if ((op == FUSE_LOOKUP) && !lookup_err) { /* lookup call succeeded */
1394        nodeid = ((struct fuse_entry_out *)fdi.answ)->nodeid;
1395        size = ((struct fuse_entry_out *)fdi.answ)->attr.size;
1396        if (!nodeid) {
1397            fdi.answ_stat = ENOENT; /* XXX: negative_timeout case */
1398            lookup_err = ENOENT;
1399        } else if (nodeid == FUSE_ROOT_ID) {
1400            lookup_err = EINVAL;
1401        }
1402    }
1403
1404    /*
1405     * If we get (lookup_err != 0), that means we didn't find what we were
1406     * looking for. This can still be OK if we're creating or renaming and
1407     * are at the end of the pathname.
1408     */
1409
1410    if (lookup_err &&
1411        (!fdi.answ_stat || lookup_err != ENOENT || op != FUSE_LOOKUP)) {
1412        return lookup_err;
1413    }
1414
1415    /* lookup_err, if non-zero, must be ENOENT at this point */
1416
1417    if (lookup_err) {
1418
1419        if ((nameiop == CREATE || nameiop == RENAME) && islastcn
1420            /* && directory dvp has not been removed */) {
1421
1422            /*
1423             * EROFS case has already been covered.
1424             *
1425             * if (vfs_isrdonly(mp)) {
1426             *     err = EROFS;
1427             *     goto out;
1428             * }
1429             */
1430
1431            err = EJUSTRETURN;
1432            goto out;
1433        }
1434
1435        if (fuse_isnegativevncache_mp(mp)) {
1436            if ((cnp->cn_flags & MAKEENTRY) && (nameiop != CREATE)) {
1437                fuse_vncache_enter(dvp, NULLVP, cnp);
1438            }
1439        }
1440
1441        err = ENOENT;
1442        goto out;
1443
1444    } else {
1445
1446        /* !lookup_err */
1447
1448        struct fuse_entry_out *feo   = NULL;
1449        struct fuse_attr      *fattr = NULL;
1450
1451        if (op == FUSE_GETATTR) {
1452            fattr = &((struct fuse_attr_out *)fdi.answ)->attr;
1453        } else {
1454            feo = (struct fuse_entry_out *)fdi.answ;
1455            fattr = &(feo->attr);
1456        }
1457
1458        /* Sanity check(s) */
1459
1460        if ((fattr->mode & S_IFMT) == 0) {
1461            err = EIO;
1462            goto out;
1463        }
1464
1465        if ((nameiop == DELETE) && islastcn) {
1466
1467            if (isdot) {
1468                err = vnode_get(dvp);
1469                if (err == 0) {
1470                    *vpp = dvp;
1471                }
1472                goto out;
1473            }
1474
1475            if ((err  = fuse_vget_i(&vp, 0 /* flags */, feo, cnp, dvp,
1476                                    mp, context))) {
1477                goto out;
1478            }
1479
1480            *vpp = vp;
1481        
1482            goto out;
1483
1484        }
1485
1486        if ((nameiop == RENAME) && islastcn && wantparent) {
1487
1488            if (isdot) {
1489                err = EISDIR;
1490                goto out;
1491            }
1492
1493            if ((err  = fuse_vget_i(&vp, 0 /* flags */, feo, cnp, dvp,
1494                                    mp, context))) {
1495                goto out;
1496            }
1497
1498            *vpp = vp;
1499
1500            goto out;
1501        }
1502
1503        if (isdotdot) {
1504            err = vnode_get(pdp);
1505            if (err == 0) {
1506                *vpp = pdp;
1507            }
1508        } else if (isdot) { /* nodeid == VTOI(dvp) */
1509            err = vnode_get(dvp);
1510            if (err == 0) {
1511                *vpp = dvp;
1512            }
1513        } else {
1514            if ((err  = fuse_vget_i(&vp, 0 /* flags */, feo, cnp, dvp,
1515                                    mp, context))) {
1516                goto out;
1517            }
1518            *vpp = vp;
1519        }
1520
1521        if (op == FUSE_GETATTR) {
1522
1523            /* ATTR_FUDGE_CASE */
1524            if (vnode_isreg(*vpp) && fuse_isnoubc(vp)) {
1525                VTOFUD(*vpp)->filesize =
1526                    ((struct fuse_attr_out *)fdi.answ)->attr.size;
1527            }
1528
1529            cache_attrs(*vpp, (struct fuse_attr_out *)fdi.answ);
1530        } else {
1531
1532            /* ATTR_FUDGE_CASE */
1533            if (vnode_isreg(*vpp) && fuse_isnoubc(vp)) {
1534                VTOFUD(*vpp)->filesize =
1535                    ((struct fuse_entry_out *)fdi.answ)->attr.size;
1536            }
1537
1538            cache_attrs(*vpp, (struct fuse_entry_out *)fdi.answ);
1539        }
1540
1541        /*
1542         * We do this elsewhere...
1543         *
1544         * if (cnp->cn_flags & MAKEENTRY) {
1545         *     fuse_vncache_enter(dvp, *vpp, cnp);
1546         * }
1547         */
1548    }
1549
1550out:
1551    if (!lookup_err) {
1552
1553        /* No lookup error; need to clean up. */
1554
1555        if (err) { /* Found inode; exit with no vnode. */
1556            if (op == FUSE_LOOKUP) {
1557                fuse_internal_forget_send(vnode_mount(dvp), context,
1558                                          nodeid, 1, &fdi);
1559            }
1560            return err;
1561        } else {
1562
1563            if (!islastcn) {
1564
1565                int tmpvtype = vnode_vtype(*vpp);
1566
1567                if ((tmpvtype != VDIR) && (tmpvtype != VLNK)) {
1568                    err = ENOTDIR;
1569                }
1570
1571                /* if (!err && !vnode_mountedhere(*vpp)) { ... */
1572
1573                if (err) {
1574                    vnode_put(*vpp);
1575                    *vpp = NULL;
1576                }
1577            }
1578        }
1579            
1580        fuse_ticket_drop(fdi.tick);
1581    }
1582
1583    return err;
1584}
1585
1586/*
1587    struct vnop_mkdir_args {
1588        struct vnodeop_desc  *a_desc;
1589        vnode_t               a_dvp;
1590        vnode_t              *a_vpp;
1591        struct componentname *a_cnp;
1592        struct vnode_attr    *a_vap;
1593        vfs_context_t         a_context;
1594    };
1595*/
1596static int
1597fuse_vnop_mkdir(struct vnop_mkdir_args *ap)
1598{
1599    vnode_t               dvp     = ap->a_dvp;
1600    vnode_t              *vpp     = ap->a_vpp;
1601    struct componentname *cnp     = ap->a_cnp;
1602    struct vnode_attr    *vap     = ap->a_vap;
1603    vfs_context_t         context = ap->a_context;
1604
1605    int err = 0;
1606
1607    struct fuse_mkdir_in fmdi;
1608
1609    fuse_trace_printf_vnop_novp();
1610
1611    if (fuse_isdeadfs_fs(dvp)) {
1612        panic("MacFUSE: fuse_vnop_mkdir(): called on a dead file system");
1613    }
1614
1615    CHECK_BLANKET_DENIAL(dvp, context, EPERM);
1616
1617    fmdi.mode = MAKEIMODE(vap->va_type, vap->va_mode);
1618
1619    err = fuse_internal_newentry(dvp, vpp, cnp, FUSE_MKDIR, &fmdi,
1620                                 sizeof(fmdi), VDIR, context);
1621
1622    if (err == 0) {
1623        fuse_invalidate_attr(dvp);
1624        FUSE_KNOTE(dvp, NOTE_WRITE | NOTE_LINK);
1625    }
1626
1627    return err;
1628}
1629
1630/*
1631    struct vnop_mknod_args {
1632        struct vnodeop_desc  *a_desc;
1633        vnode_t               a_dvp;
1634        vnode_t              *a_vpp;
1635        struct componentname *a_cnp;
1636        struct vnode_attr    *a_vap;
1637        vfs_context_t         a_context;
1638    };
1639*/
1640static int
1641fuse_vnop_mknod(struct vnop_mknod_args *ap)
1642{
1643    vnode_t               dvp     = ap->a_dvp;
1644    vnode_t              *vpp     = ap->a_vpp;
1645    struct componentname *cnp     = ap->a_cnp;
1646    struct vnode_attr    *vap     = ap->a_vap;
1647    vfs_context_t         context = ap->a_context;
1648
1649    struct fuse_mknod_in fmni;
1650
1651    int err;
1652
1653    fuse_trace_printf_vnop_novp();
1654
1655    if (fuse_isdeadfs_fs(dvp)) {
1656        panic("MacFUSE: fuse_vnop_mknod(): called on a dead file system");
1657    }
1658
1659    CHECK_BLANKET_DENIAL(dvp, context, EPERM);
1660
1661    fmni.mode = MAKEIMODE(vap->va_type, vap->va_mode);
1662    fmni.rdev = vap->va_rdev;
1663
1664    err = fuse_internal_newentry(dvp, vpp, cnp, FUSE_MKNOD, &fmni,
1665                                 sizeof(fmni), vap->va_type, context);
1666
1667    if (err== 0) {
1668        fuse_invalidate_attr(dvp);
1669        FUSE_KNOTE(dvp, NOTE_WRITE);
1670    }
1671
1672    return err;
1673}
1674
1675/*
1676    struct vnop_mmap_args {
1677        struct vnodeop_desc *a_desc;
1678        vnode_t              a_vp;
1679        int                  a_fflags;
1680        vfs_context_t        a_context;
1681    };
1682*/
1683static int
1684fuse_vnop_mmap(struct vnop_mmap_args *ap)
1685{
1686    vnode_t       vp      = ap->a_vp;
1687    int           fflags  = ap->a_fflags;
1688    vfs_context_t context = ap->a_context;
1689
1690    struct fuse_vnode_data *fvdat = VTOFUD(vp);
1691    struct fuse_filehandle *fufh = NULL;
1692    fufh_type_t fufh_type = fuse_filehandle_xlate_from_mmap(fflags);
1693
1694    int err = 0;
1695    int deleted = 0;
1696    int retried = 0;
1697
1698    fuse_trace_printf_vnop();
1699
1700    if (fuse_isdeadfs_fs(vp)) {
1701        panic("MacFUSE: fuse_vnop_mmap(): called on a dead file system");
1702    }
1703
1704    if (fuse_isdirectio(vp)) {
1705        /*
1706         * We should be returning ENODEV here, but ubc_map() translates
1707         * all errors except ENOPERM to 0. Even then, this is not going
1708         * to prevent the mmap()!
1709         */
1710        return EPERM;
1711    }
1712
1713    CHECK_BLANKET_DENIAL(vp, context, ENOENT);
1714
1715    if (fufh_type == FUFH_INVALID) { /* nothing to do */
1716        return 0;
1717    }
1718
1719    /* XXX: For PROT_WRITE, we should only care if file is mapped MAP_SHARED. */
1720
1721retry:
1722    fufh = &(fvdat->fufh[fufh_type]);
1723
1724    if (FUFH_IS_VALID(fufh)) {
1725        FUFH_USE_INC(fufh);
1726        FUSE_OSAddAtomic(1, (SInt32 *)&fuse_fh_reuse_count);
1727        goto out;
1728    }
1729
1730    if (!deleted) {
1731        err = fuse_filehandle_preflight_status(vp, fvdat->parentvp,
1732                                               context, fufh_type);
1733        if (err == ENOENT) {
1734            deleted = 1;
1735            err = 0;
1736        }
1737    }
1738
1739#if FUSE_DEBUG
1740    fuse_preflight_log(vp, fufh_type, err, "mmap");
1741#endif /* FUSE_DEBUG */
1742
1743    if (!err) {
1744        err = fuse_filehandle_get(vp, context, fufh_type, 0 /* mode */);
1745    }
1746
1747    if (err) {
1748        /*
1749         * XXX: This is a kludge because xnu doesn't tell us whether this
1750         *      is a MAP_SHARED or MAP_PRIVATE mapping. If we want shared
1751         *      library mapping to go well, we need to do this.
1752         */
1753        if (!retried && (err == EACCES) &&
1754            ((fufh_type == FUFH_RDWR) || (fufh_type == FUFH_WRONLY))) {
1755            IOLog("MacFUSE: filehandle_get retrying (type=%d, err=%d)\n",
1756                  fufh_type, err);
1757            fufh_type = FUFH_RDONLY;
1758            retried = 1;
1759            goto retry;
1760        } else {
1761            IOLog("MacFUSE: filehandle_get failed in mmap (type=%d, err=%d)\n",
1762                  fufh_type, err);
1763        }
1764        return EPERM;
1765    }
1766
1767out:
1768
1769    return 0;
1770}
1771
1772/*
1773    struct vnop_mnomap_args {
1774        struct vnodeop_desc *a_desc;
1775        vnode_t              a_vp;
1776        vfs_context_t        a_context;
1777    };
1778*/
1779static int
1780fuse_vnop_mnomap(struct vnop_mnomap_args *ap)
1781{
1782    vnode_t vp = ap->a_vp;
1783
1784    fuse_trace_printf_vnop();
1785
1786    if (fuse_isdeadfs(vp)) {
1787        return 0;
1788    }
1789
1790    if (fuse_isdirectio(vp)) {
1791        /*
1792         * ubc_unmap() doesn't care about the return value.
1793         */
1794        return ENODEV;
1795    }
1796
1797    /*
1798     * XXX
1799     *
1800     * What behavior do we want here?
1801     *
1802     * I once noted that sync() is not going to help here, but I think
1803     * I've forgotten the context. Need to think about this again.
1804     *
1805     * ubc_sync_range(vp, (off_t)0, ubc_getsize(vp), UBC_PUSHDIRTY);
1806     */
1807
1808    /*
1809     * Earlier, we used to go through our vnode's fufh list here, doing
1810     * something like the following:
1811     *
1812     * for (type = 0; type < FUFH_MAXTYPE; type++) {
1813     *     fufh = &(fvdat->fufh[type]);
1814     *     if ((fufh->fufh_flags & FUFH_VALID) &&
1815     *         (fufh->fufh_flags & FUFH_MAPPED)) {
1816     *         fufh->fufh_flags &= ~FUFH_MAPPED;
1817     *         if (fufh->open_count == 0) {
1818     *             (void)fuse_filehandle_put(vp, context, type,
1819     *                                       FUSE_OP_BACKGROUNDED);
1820     *         }
1821     *     }
1822     * }
1823     *
1824     * Now, cleanup is all taken care of in vnop_inactive/reclaim.
1825     *
1826     */
1827
1828    return 0;
1829}
1830
1831/*
1832    struct vnop_offtoblk_args {
1833        struct vnodeop_desc *a_desc;
1834        vnode_t              a_vp;
1835        off_t                a_offset;
1836        daddr64_t           *a_lblkno;
1837    };
1838*/
1839static int
1840fuse_vnop_offtoblk(struct vnop_offtoblk_args *ap)
1841{
1842    vnode_t    vp        = ap->a_vp;
1843    off_t      offset    = ap->a_offset;
1844    daddr64_t *lblknoPtr = ap->a_lblkno;
1845
1846    struct fuse_data *data;
1847
1848    fuse_trace_printf_vnop();
1849
1850    if (fuse_isdeadfs(vp)) {
1851        return EIO;
1852    }
1853
1854    data = fuse_get_mpdata(vnode_mount(vp));
1855
1856    *lblknoPtr = offset / data->blocksize;
1857
1858    return 0;
1859}
1860
1861/*
1862    struct vnop_open_args {
1863        struct vnodeop_desc *a_desc;
1864        vnode_t              a_vp;
1865        int                  a_mode;
1866        vfs_context_t        a_context;
1867    };
1868*/
1869static int
1870fuse_vnop_open(struct vnop_open_args *ap)
1871{
1872    vnode_t       vp      = ap->a_vp;
1873    int           mode    = ap->a_mode;
1874    vfs_context_t context = ap->a_context;
1875
1876    fufh_type_t             fufh_type;
1877    struct fuse_vnode_data *fvdat;
1878    struct fuse_filehandle *fufh = NULL;
1879    struct fuse_filehandle *fufh_rw = NULL;
1880
1881    int error, isdir = 0;
1882    long hint = 0;
1883
1884    fuse_trace_printf_vnop();
1885
1886    if (fuse_isdeadfs(vp)) {
1887        return ENXIO;
1888    }
1889
1890#if !M_MACFUSE_ENABLE_FIFOFS
1891    if (vnode_isfifo(vp)) {
1892        return EPERM;
1893    }
1894#endif
1895
1896    CHECK_BLANKET_DENIAL(vp, context, ENOENT);
1897
1898    fvdat = VTOFUD(vp);
1899
1900    if (vnode_isdir(vp)) {
1901        isdir = 1;
1902    }
1903
1904    if (isdir) {
1905        fufh_type = FUFH_RDONLY;
1906    } else {
1907        fufh_type = fuse_filehandle_xlate_from_fflags(mode);
1908    }
1909
1910    fufh = &(fvdat->fufh[fufh_type]);
1911
1912    if (!isdir && (fvdat->flag & FN_CREATING)) {
1913
1914        fuse_lck_mtx_lock(fvdat->createlock);
1915
1916        if (fvdat->flag & FN_CREATING) { // check again
1917            if (fvdat->creator == current_thread()) {
1918
1919                /*
1920                 * For testing the race condition we want to prevent here,
1921                 * try something like the following:
1922                 *
1923                 *     int dummyctr = 0;
1924                 *
1925                 *     for (; dummyctr < 2048000000; dummyctr++);
1926                 */
1927
1928                fufh_rw = &(fvdat->fufh[FUFH_RDWR]);
1929
1930                fufh->open_flags = fufh_rw->open_flags;
1931                fufh->fh_id = fufh_rw->fh_id;
1932
1933                /* Note that fufh_rw can be the same as fufh! Order is key. */
1934                fufh_rw->open_count = 0;
1935                fufh->open_count = 1;
1936
1937                /*
1938                 * Creator has picked up stashed handle and moved it to the
1939                 * fufh_type slot.
1940                 */
1941                
1942                fvdat->flag &= ~FN_CREATING;
1943
1944                fuse_lck_mtx_unlock(fvdat->createlock);
1945                fuse_wakeup((caddr_t)fvdat->creator); // wake up all
1946                goto ok; /* return 0 */
1947            } else {
1948
1949                /* Contender is going to sleep now. */
1950
1951                error = fuse_msleep(fvdat->creator, fvdat->createlock,
1952                                    PDROP | PINOD | PCATCH, "fuse_open", NULL);
1953                /*
1954                 * msleep will drop the mutex. since we have PDROP specified,
1955                 * it will NOT regrab the mutex when it returns.
1956                 */
1957
1958                /* Contender is awake now. */
1959
1960                if (error) {
1961                    /*
1962                     * Since we specified PCATCH above, we'll be woken up in
1963                     * case a signal arrives. The value of error could be
1964                     * EINTR or ERESTART.
1965                     */
1966                    return error;
1967                }
1968            }
1969        } else {
1970            fuse_lck_mtx_unlock(fvdat->createlock);
1971            /* Can proceed from here. */
1972        }
1973    }
1974
1975    if (FUFH_IS_VALID(fufh)) {
1976        FUFH_USE_INC(fufh);
1977        FUSE_OSAddAtomic(1, (SInt32 *)&fuse_fh_reuse_count);
1978        goto ok; /* return 0 */
1979    }
1980
1981    error = fuse_filehandle_get(vp, context, fufh_type, mode);
1982    if (error) {
1983        IOLog("MacFUSE: filehandle_get failed in open (type=%d, err=%d)\n",
1984              fufh_type, error);
1985        if (error == ENOENT) {
1986            cache_purge(vp);
1987        }
1988        return error;
1989    }
1990
1991ok:
1992    /*
1993     * Doing this here because when a vnode goes inactive, things like
1994     * no-cache and no-readahead are cleared by the kernel.
1995     */
1996
1997    if ((fufh->fuse_open_flags & FOPEN_DIRECT_IO) || (fuse_isdirectio(vp))) {
1998        /* 
1999         *…

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