PageRenderTime 100ms CodeModel.GetById 24ms app.highlight 65ms RepoModel.GetById 1ms app.codeStats 1ms

/core/10.5/fusefs/fuse_internal.c

http://macfuse.googlecode.com/
C | 1686 lines | 1189 code | 321 blank | 176 comment | 229 complexity | 46c89690522011a7aff5d520886ee8ee MD5 | raw file
   1/*
   2 * Copyright (C) 2006-2008 Google. All Rights Reserved.
   3 * Amit Singh <singh@>
   4 */
   5
   6#include <sys/param.h>
   7#include <kern/assert.h>
   8#include <libkern/libkern.h>
   9#include <libkern/OSMalloc.h>
  10#include <libkern/locks.h>
  11#include <mach/mach_types.h>
  12#include <sys/dirent.h>
  13#include <sys/disk.h>
  14#include <sys/errno.h>
  15#include <sys/fcntl.h>
  16#include <sys/kernel_types.h>
  17#include <sys/mount.h>
  18#include <sys/proc.h>
  19#include <sys/stat.h>
  20#include <sys/ubc.h>
  21#include <sys/unistd.h>
  22#include <sys/vnode.h>
  23#include <sys/vnode_if.h>
  24#include <sys/xattr.h>
  25#include <sys/buf.h>
  26#include <sys/namei.h>
  27#include <sys/mman.h>
  28
  29#include "fuse.h"
  30#include "fuse_file.h"
  31#include "fuse_internal.h"
  32#include "fuse_ipc.h"
  33#include "fuse_locking.h"
  34#include "fuse_node.h"
  35#include "fuse_file.h"
  36#include "fuse_nodehash.h"
  37#include "fuse_sysctl.h"
  38#include "fuse_kludges.h"
  39
  40/* access */
  41
  42__private_extern__
  43int
  44fuse_internal_access(vnode_t                   vp,
  45                     int                       action,
  46                     vfs_context_t             context,
  47                     struct fuse_access_param *facp)
  48{
  49    int err = 0;
  50    int default_error = 0;
  51    uint32_t mask = 0;
  52    int dataflags;
  53    mount_t mp;
  54    struct fuse_dispatcher fdi;
  55    struct fuse_access_in *fai;
  56    struct fuse_data      *data;
  57
  58    fuse_trace_printf_func();
  59
  60    mp = vnode_mount(vp);
  61
  62    data = fuse_get_mpdata(mp);
  63    dataflags = data->dataflags;
  64
  65    /* Allow for now; let checks be handled inline later. */
  66    if (fuse_isdeferpermissions_mp(mp)) {
  67        return 0;
  68    }
  69
  70    if (facp->facc_flags & FACCESS_FROM_VNOP) {
  71        default_error = ENOTSUP;
  72    }
  73
  74    /*
  75     * (action & KAUTH_VNODE_GENERIC_WRITE_BITS) on a read-only file system
  76     * would have been handled by higher layers.
  77     */
  78
  79    if (!fuse_implemented(data, FSESS_NOIMPLBIT(ACCESS))) {
  80        return default_error;
  81    }
  82
  83    /* Unless explicitly permitted, deny everyone except the fs owner. */
  84    if (!vnode_isvroot(vp) && !(facp->facc_flags & FACCESS_NOCHECKSPY)) {
  85        if (!(dataflags & FSESS_ALLOW_OTHER)) {
  86            int denied = fuse_match_cred(data->daemoncred,
  87                                         vfs_context_ucred(context));
  88            if (denied) {
  89                return EPERM;
  90            }
  91        }
  92        facp->facc_flags |= FACCESS_NOCHECKSPY;
  93    }
  94
  95    if (!(facp->facc_flags & FACCESS_DO_ACCESS)) {
  96        return default_error;
  97    }
  98
  99    if (vnode_isdir(vp)) {
 100        if (action & (KAUTH_VNODE_LIST_DIRECTORY   |
 101                      KAUTH_VNODE_READ_EXTATTRIBUTES)) {
 102            mask |= R_OK;
 103        }
 104        if (action & (KAUTH_VNODE_ADD_FILE         |
 105                      KAUTH_VNODE_ADD_SUBDIRECTORY |
 106                      KAUTH_VNODE_DELETE_CHILD)) {
 107            mask |= W_OK;
 108        }
 109        if (action & KAUTH_VNODE_SEARCH) {
 110            mask |= X_OK;
 111        }
 112    } else {
 113        if (action & (KAUTH_VNODE_READ_DATA | KAUTH_VNODE_READ_EXTATTRIBUTES)) {
 114            mask |= R_OK;
 115        }
 116        if (action & (KAUTH_VNODE_WRITE_DATA | KAUTH_VNODE_APPEND_DATA)) {
 117            mask |= W_OK;
 118        }
 119        if (action & KAUTH_VNODE_EXECUTE) {
 120            mask |= X_OK;
 121        }
 122    }
 123
 124    if (action & (KAUTH_VNODE_WRITE_ATTRIBUTES    |
 125                  KAUTH_VNODE_WRITE_EXTATTRIBUTES |
 126                  KAUTH_VNODE_WRITE_SECURITY)) {
 127        mask |= W_OK;
 128    }
 129
 130    bzero(&fdi, sizeof(fdi));
 131
 132    fdisp_init(&fdi, sizeof(*fai));
 133    fdisp_make_vp(&fdi, FUSE_ACCESS, vp, context);
 134
 135    fai = fdi.indata;
 136    fai->mask = F_OK;
 137    fai->mask |= mask;
 138
 139    if (!(err = fdisp_wait_answ(&fdi))) {
 140        fuse_ticket_drop(fdi.tick);
 141    }
 142
 143    if (err == ENOSYS) {
 144        /*
 145         * Make sure we don't come in here again.
 146         */
 147        vfs_clearauthopaque(mp);
 148        fuse_clear_implemented(data, FSESS_NOIMPLBIT(ACCESS));
 149        err = default_error;
 150    }
 151
 152    if (err == ENOENT) {
 153
 154        const char *vname = NULL;
 155
 156#if M_MACFUSE_ENABLE_UNSUPPORTED
 157        vname = vnode_getname(vp);
 158#endif /* M_MACFUSE_ENABLE_UNSUPPORTED */
 159
 160        IOLog("MacFUSE: disappearing vnode %p (name=%s type=%d action=%x)\n",
 161              vp, (vname) ? vname : "?", vnode_vtype(vp), action);
 162
 163#if M_MACFUSE_ENABLE_UNSUPPORTED
 164        if (vname) {
 165            vnode_putname(vname);
 166        }
 167#endif /* M_MACFUSE_ENABLE_UNSUPPORTED */
 168
 169        /*
 170         * On 10.4, I think I can get Finder to lock because of /.Trashes/<uid>
 171         * unless I use REVOKE_NONE here.
 172         */
 173         
 174        fuse_internal_vnode_disappear(vp, context, REVOKE_SOFT);
 175    }
 176
 177    return err;
 178}
 179
 180#if M_MACFUSE_ENABLE_EXCHANGE
 181
 182/* exchange */
 183
 184__private_extern__
 185int
 186fuse_internal_exchange(vnode_t       fvp,
 187                       const char   *fname,
 188                       size_t        flen,
 189                       vnode_t       tvp,
 190                       const char   *tname,
 191                       size_t        tlen,
 192                       int           options,
 193                       vfs_context_t context)
 194{
 195    struct fuse_dispatcher fdi;
 196    struct fuse_exchange_in *fei;
 197    struct fuse_vnode_data *ffud = VTOFUD(fvp);
 198    struct fuse_vnode_data *tfud = VTOFUD(tvp);
 199    vnode_t fdvp = ffud->parentvp;
 200    vnode_t tdvp = tfud->parentvp;
 201    int err = 0;
 202
 203    fdisp_init(&fdi, sizeof(*fei) + flen + tlen + 2);
 204    fdisp_make_vp(&fdi, FUSE_EXCHANGE, fvp, context);
 205
 206    fei = fdi.indata;
 207    fei->olddir = VTOI(fdvp);
 208    fei->newdir = VTOI(tdvp);
 209    fei->options = (uint64_t)options;
 210
 211    memcpy((char *)fdi.indata + sizeof(*fei), fname, flen);
 212    ((char *)fdi.indata)[sizeof(*fei) + flen] = '\0';
 213
 214    memcpy((char *)fdi.indata + sizeof(*fei) + flen + 1, tname, tlen);
 215    ((char *)fdi.indata)[sizeof(*fei) + flen + tlen + 1] = '\0';
 216
 217    ubc_msync(fvp, (off_t)0, (off_t)ffud->filesize, (off_t*)0,
 218              UBC_PUSHALL | UBC_INVALIDATE | UBC_SYNC);
 219    ubc_msync(tvp, (off_t)0, (off_t)tfud->filesize, (off_t*)0,
 220              UBC_PUSHALL | UBC_INVALIDATE | UBC_SYNC);
 221        
 222    if (!(err = fdisp_wait_answ(&fdi))) {
 223        fuse_ticket_drop(fdi.tick);
 224    }
 225
 226    if (err == 0) {
 227        if (fdvp) {
 228            fuse_invalidate_attr(fdvp);
 229        }
 230        if (tdvp != fdvp) {
 231            if (tdvp) {
 232                fuse_invalidate_attr(tdvp);
 233            }
 234        }
 235
 236        fuse_invalidate_attr(fvp);
 237        fuse_invalidate_attr(tvp);
 238
 239        cache_purge(fvp);
 240        cache_purge(tvp);
 241
 242        /* Swap sizes */
 243        off_t tmpfilesize = ffud->filesize;
 244        ffud->filesize = tfud->filesize;
 245        tfud->filesize = tmpfilesize;
 246        ubc_setsize(fvp, (off_t)ffud->filesize);
 247        ubc_setsize(tvp, (off_t)tfud->filesize);
 248
 249        fuse_kludge_exchange(fvp, tvp);
 250
 251        /*
 252         * Another approach (will need additional kernel support to work):
 253         *
 254        vnode_t tmpvp = ffud->vp;
 255        ffud->vp = tfud->vp;
 256        tfud->vp = tmpvp;
 257
 258        vnode_t tmpparentvp = ffud->parentvp;
 259        ffud->parentvp = tfud->parentvp;
 260        tfud->parentvp = tmpparentvp;
 261
 262        off_t tmpfilesize = ffud->filesize;
 263        ffud->filesize = tfud->filesize;
 264        tfud->filesize = tmpfilesize;
 265
 266        struct fuse_vnode_data tmpfud;
 267        memcpy(&tmpfud, ffud, sizeof(struct fuse_vnode_data));
 268        memcpy(ffud, tfud, sizeof(struct fuse_vnode_data));
 269        memcpy(tfud, &tmpfud, sizeof(struct fuse_vnode_data));
 270        
 271        HNodeExchangeFromFSNode(ffud, tfud);
 272        *
 273        */
 274    }
 275
 276    return err;
 277}
 278
 279#endif /* M_MACFUSE_ENABLE_EXCHANGE */
 280
 281/* fsync */
 282
 283__private_extern__
 284int
 285fuse_internal_fsync_callback(struct fuse_ticket *ftick, __unused uio_t uio)
 286{
 287    fuse_trace_printf_func();
 288
 289    if (ftick->tk_aw_ohead.error == ENOSYS) {
 290        if (fticket_opcode(ftick) == FUSE_FSYNC) {
 291            fuse_clear_implemented(ftick->tk_data, FSESS_NOIMPLBIT(FSYNC));
 292        } else if (fticket_opcode(ftick) == FUSE_FSYNCDIR) {
 293            fuse_clear_implemented(ftick->tk_data, FSESS_NOIMPLBIT(FSYNCDIR));
 294        } else {
 295            IOLog("MacFUSE: unexpected opcode in sync handling\n");
 296        }
 297    }
 298
 299    fuse_ticket_drop(ftick);
 300
 301    return 0;
 302}
 303
 304__private_extern__
 305int
 306fuse_internal_fsync(vnode_t                 vp,
 307                    vfs_context_t           context,
 308                    struct fuse_filehandle *fufh,
 309                    void                   *param,
 310                    fuse_op_waitfor_t       waitfor)
 311{
 312    int err = 0;
 313    int op = FUSE_FSYNC;
 314    struct fuse_fsync_in *ffsi;
 315    struct fuse_dispatcher *fdip = param;
 316
 317    fuse_trace_printf_func();
 318
 319    fdip->iosize = sizeof(*ffsi);
 320    fdip->tick = NULL;
 321    if (vnode_isdir(vp)) {
 322        op = FUSE_FSYNCDIR;
 323    }
 324    
 325    fdisp_make_vp(fdip, op, vp, context);
 326    ffsi = fdip->indata;
 327    ffsi->fh = fufh->fh_id;
 328
 329    ffsi->fsync_flags = 1; /* datasync */
 330
 331    if (waitfor == FUSE_OP_FOREGROUNDED) {
 332        if ((err = fdisp_wait_answ(fdip))) {
 333            if (err == ENOSYS) {
 334                if (op == FUSE_FSYNC) {
 335                    fuse_clear_implemented(fdip->tick->tk_data,
 336                                           FSESS_NOIMPLBIT(FSYNC));
 337                } else if (op == FUSE_FSYNCDIR) {
 338                    fuse_clear_implemented(fdip->tick->tk_data,
 339                                           FSESS_NOIMPLBIT(FSYNCDIR));
 340                }
 341            }
 342            goto out;
 343        } else {
 344            fuse_ticket_drop(fdip->tick);
 345        }
 346    } else {
 347        fuse_insert_callback(fdip->tick, fuse_internal_fsync_callback);
 348        fuse_insert_message(fdip->tick);
 349    }
 350
 351out:
 352    return err;
 353}
 354
 355/* getattr sidekicks */
 356__private_extern__
 357int
 358fuse_internal_loadxtimes(vnode_t vp, struct vnode_attr *out_vap,
 359                         vfs_context_t context)
 360{
 361    struct vnode_attr *in_vap = VTOVA(vp);
 362    struct fuse_data *data = fuse_get_mpdata(vnode_mount(vp));
 363    struct fuse_dispatcher fdi;
 364    struct fuse_getxtimes_out *fgxo = NULL;
 365    int isvroot = vnode_isvroot(vp);
 366    struct timespec t = { 0, 0 };
 367    const struct timespec kZeroTime = { 0, 0 };
 368    int err = 0;
 369
 370    if (!(data->dataflags & FSESS_XTIMES)) {
 371        /* We don't return anything. */
 372        goto out;
 373    }
 374
 375    if (VTOFUD(vp)->c_flag & C_XTIMES_VALID) {
 376        VATTR_RETURN(out_vap, va_backup_time, in_vap->va_backup_time);
 377        VATTR_RETURN(out_vap, va_create_time, in_vap->va_create_time);
 378        goto out;
 379    }
 380
 381    if (!fuse_implemented(data, FSESS_NOIMPLBIT(GETXTIMES))) {
 382        goto fake;
 383    }
 384
 385    if (fuse_isdeadfs(vp) && isvroot) {
 386        goto fake;
 387    }
 388
 389    if (!(data->dataflags & FSESS_INITED) && isvroot) {
 390        goto fake;
 391    }
 392
 393    err = fdisp_simple_putget_vp(&fdi, FUSE_GETXTIMES, vp, context);
 394    if (err) {
 395        /* We don't ever treat this as a hard error. */
 396        err = 0;
 397        goto fake;
 398    }
 399
 400    fgxo = (struct fuse_getxtimes_out *)fdi.answ;
 401
 402    t.tv_sec = (time_t)fgxo->bkuptime; /* XXX: truncation */
 403    t.tv_nsec = fgxo->bkuptimensec;
 404    VATTR_RETURN(in_vap, va_backup_time, t);
 405    VATTR_RETURN(out_vap, va_backup_time, t);
 406
 407    t.tv_sec = (time_t)fgxo->crtime; /* XXX: truncation */
 408    t.tv_nsec = fgxo->crtimensec;
 409    VATTR_RETURN(in_vap, va_create_time, t);
 410    VATTR_RETURN(out_vap, va_create_time, t);
 411
 412    fuse_ticket_drop(fdi.tick);
 413
 414    VTOFUD(vp)->c_flag |= C_XTIMES_VALID;
 415
 416    goto out;
 417
 418fake:
 419    VATTR_RETURN(out_vap, va_backup_time, kZeroTime);
 420    VATTR_RETURN(out_vap, va_create_time, kZeroTime);
 421
 422out:
 423    return err;
 424}
 425
 426/* setattr sidekicks */
 427__private_extern__
 428int
 429fuse_internal_attr_vat2fsai(mount_t                 mp,
 430                            vnode_t                 vp,
 431                            struct vnode_attr      *vap,
 432                            struct fuse_setattr_in *fsai,
 433                            uint64_t               *newsize)
 434{
 435    /*
 436     * XXX: Locking
 437     *
 438     * We need to worry about the file size changing in setattr(). If the call
 439     * is indeed altering the size, then:
 440     *
 441     * lock_exclusive(truncatelock)
 442     *   lock(nodelock)
 443     *     set the new size
 444     *   unlock(nodelock)
 445     *   adjust ubc
 446     *   lock(nodelock)
 447     *     do cleanup
 448     *   unlock(nodelock)
 449     * unlock(truncatelock)
 450     * ...
 451     */
 452
 453    int sizechanged = 0;
 454    uid_t nuid;
 455    gid_t ngid;
 456
 457    fsai->valid = 0;
 458
 459    if (newsize) {
 460        *newsize = 0;
 461    }
 462
 463    nuid = VATTR_IS_ACTIVE(vap, va_uid) ? vap->va_uid : (uid_t)VNOVAL;
 464    if (nuid != (uid_t)VNOVAL) {
 465        fsai->uid = nuid;
 466        fsai->valid |= FATTR_UID;
 467    }
 468    VATTR_SET_SUPPORTED(vap, va_uid);
 469
 470    ngid = VATTR_IS_ACTIVE(vap, va_gid) ? vap->va_gid : (gid_t)VNOVAL;
 471    if (ngid != (gid_t)VNOVAL) {
 472        fsai->gid = ngid;
 473        fsai->valid |= FATTR_GID;
 474    }
 475    VATTR_SET_SUPPORTED(vap, va_gid);
 476
 477    if (VATTR_IS_ACTIVE(vap, va_data_size)) {
 478
 479        // Truncate to a new value.
 480        fsai->size = vap->va_data_size;
 481        sizechanged = 1;
 482	if (newsize) {
 483            *newsize = vap->va_data_size;
 484        }
 485        fsai->valid |= FATTR_SIZE;      
 486
 487        if (vp) {
 488            struct fuse_filehandle *fufh = NULL;
 489            fufh_type_t fufh_type = FUFH_WRONLY;
 490            struct fuse_vnode_data *fvdat = VTOFUD(vp);
 491
 492            fufh = &(fvdat->fufh[fufh_type]);
 493
 494            if (!FUFH_IS_VALID(fufh)) {
 495                fufh_type = FUFH_RDWR;
 496                fufh = &(fvdat->fufh[fufh_type]);
 497                if (!FUFH_IS_VALID(fufh)) {
 498                    fufh = NULL;
 499                }
 500            }
 501
 502            if (fufh) {
 503                fsai->fh = fufh->fh_id;
 504                fsai->valid |= FATTR_FH;
 505            }
 506        }
 507    }
 508    VATTR_SET_SUPPORTED(vap, va_data_size);
 509
 510    /*
 511     * Possible timestamps:
 512     *
 513     * Mac OS X                                          Linux  FUSE API
 514     *  
 515     * va_access_time    last access time                atime  atime
 516     * va_backup_time    last backup time                -      -
 517     * va_change_time    last metadata change time       ctime* -
 518     * va_create_time    creation time                   -      -
 519     * va_modify_time    last data modification time     mtime  mtime
 520     *
 521     */
 522
 523    if (VATTR_IS_ACTIVE(vap, va_access_time)) {
 524        fsai->atime = vap->va_access_time.tv_sec;
 525        /* XXX: truncation */
 526        fsai->atimensec = (uint32_t)vap->va_access_time.tv_nsec;
 527        fsai->valid |=  FATTR_ATIME;
 528    }
 529    VATTR_SET_SUPPORTED(vap, va_access_time);
 530
 531    if (VATTR_IS_ACTIVE(vap, va_modify_time)) {
 532        fsai->mtime = vap->va_modify_time.tv_sec;
 533        /* XXX: truncation */
 534        fsai->mtimensec = (uint32_t)vap->va_modify_time.tv_nsec;
 535        fsai->valid |=  FATTR_MTIME;
 536    }
 537    VATTR_SET_SUPPORTED(vap, va_modify_time);
 538
 539    if (VATTR_IS_ACTIVE(vap, va_backup_time) && fuse_isxtimes_mp(mp)) {
 540        fsai->bkuptime = vap->va_backup_time.tv_sec;
 541        /* XXX: truncation */
 542        fsai->bkuptimensec = (uint32_t)vap->va_backup_time.tv_nsec;
 543        fsai->valid |= FATTR_BKUPTIME;
 544        VATTR_SET_SUPPORTED(vap, va_backup_time);
 545    }
 546
 547    if (VATTR_IS_ACTIVE(vap, va_change_time)) {
 548        if (fuse_isxtimes_mp(mp)) {
 549            fsai->chgtime = vap->va_change_time.tv_sec;
 550            /* XXX: truncation */
 551            fsai->chgtimensec = (uint32_t)vap->va_change_time.tv_nsec;
 552            fsai->valid |=  FATTR_CHGTIME;
 553            VATTR_SET_SUPPORTED(vap, va_change_time);
 554        }
 555    }
 556
 557    if (VATTR_IS_ACTIVE(vap, va_create_time) && fuse_isxtimes_mp(mp)) {
 558        fsai->crtime = vap->va_create_time.tv_sec;
 559        /* XXX: truncation */
 560        fsai->crtimensec = (uint32_t)vap->va_create_time.tv_nsec;
 561        fsai->valid |= FATTR_CRTIME;
 562        VATTR_SET_SUPPORTED(vap, va_create_time);
 563    }
 564
 565    if (VATTR_IS_ACTIVE(vap, va_mode)) {
 566        fsai->mode = vap->va_mode & ALLPERMS;
 567        fsai->valid |= FATTR_MODE;
 568    }
 569    VATTR_SET_SUPPORTED(vap, va_mode);
 570
 571    if (VATTR_IS_ACTIVE(vap, va_flags)) {
 572        fsai->flags = vap->va_flags;
 573        fsai->valid |= FATTR_FLAGS;
 574    }
 575    VATTR_SET_SUPPORTED(vap, va_flags);
 576
 577    /*
 578     * We /are/ OK with va_acl, va_guuid, and va_uuuid passing through here.
 579     */
 580
 581    return sizechanged;
 582}
 583
 584/* ioctl */
 585__private_extern__
 586int
 587fuse_internal_ioctl_avfi(vnode_t vp, __unused vfs_context_t context,
 588                         struct fuse_avfi_ioctl *avfi)
 589{
 590    int ret = 0;
 591    uint32_t hint = 0;
 592
 593    if (!avfi) {
 594        return EINVAL;
 595    }
 596
 597    if (avfi->cmd & FUSE_AVFI_MARKGONE) {
 598
 599        /*
 600         * TBD
 601         */
 602        return EINVAL;
 603    }
 604
 605    /* The result of this /does/ alter our return value. */
 606    if (avfi->cmd & FUSE_AVFI_UBC) {
 607        int ubc_flags = avfi->ubc_flags & (UBC_PUSHDIRTY  | UBC_PUSHALL |
 608                                           UBC_INVALIDATE | UBC_SYNC);
 609        if (ubc_msync(vp, (off_t)0, ubc_getsize(vp), (off_t*)0,
 610                      ubc_flags) == 0) {
 611            /* failed */
 612            ret = EINVAL; /* don't really have a good error to return */
 613        }
 614    }
 615
 616    if (avfi->cmd & FUSE_AVFI_UBC_SETSIZE) {
 617        if (VTOFUD(vp)->filesize != avfi->size) {
 618            hint |= NOTE_WRITE;
 619            if (avfi->size > VTOFUD(vp)->filesize) {
 620                hint |= NOTE_EXTEND;
 621            }
 622            VTOFUD(vp)->filesize = avfi->size;
 623            ubc_setsize(vp, avfi->size);
 624        }
 625        (void)fuse_invalidate_attr(vp);
 626    }
 627
 628    /* The result of this doesn't alter our return value. */
 629    if (avfi->cmd & FUSE_AVFI_PURGEATTRCACHE) {
 630        hint |= NOTE_ATTRIB;
 631        (void)fuse_invalidate_attr(vp);
 632    }
 633
 634    /* The result of this doesn't alter our return value. */
 635    if (avfi->cmd & FUSE_AVFI_PURGEVNCACHE) {
 636        (void)fuse_vncache_purge(vp);
 637    }
 638
 639    if (avfi->cmd & FUSE_AVFI_KNOTE) {
 640        hint |= avfi->note;
 641    }
 642
 643    if (hint) {
 644        FUSE_KNOTE(vp, hint);
 645    }
 646
 647    return ret;
 648}
 649
 650/* readdir */
 651
 652__private_extern__
 653int
 654fuse_internal_readdir(vnode_t                 vp,
 655                      uio_t                   uio,
 656                      vfs_context_t           context,
 657                      struct fuse_filehandle *fufh,
 658                      struct fuse_iov        *cookediov,
 659                      int                    *numdirent)
 660{
 661    int err = 0;
 662    struct fuse_dispatcher fdi;
 663    struct fuse_read_in   *fri;
 664    struct fuse_data      *data;
 665
 666    if (uio_resid(uio) == 0) {
 667        return 0;
 668    }
 669
 670    fdisp_init(&fdi, 0);
 671
 672    /* Note that we DO NOT have a UIO_SYSSPACE here (so no need for p2p I/O). */
 673
 674    while (uio_resid(uio) > 0) {
 675
 676        fdi.iosize = sizeof(*fri);
 677        fdisp_make_vp(&fdi, FUSE_READDIR, vp, context);
 678
 679        fri = fdi.indata;
 680        fri->fh = fufh->fh_id;
 681        fri->offset = uio_offset(uio);
 682        data = fuse_get_mpdata(vnode_mount(vp));
 683        fri->size = (typeof(fri->size))min((size_t)uio_resid(uio), data->iosize);
 684
 685        if ((err = fdisp_wait_answ(&fdi))) {
 686            goto out;
 687        }
 688
 689        if ((err = fuse_internal_readdir_processdata(vp,
 690                                                     uio,
 691                                                     fri->size,
 692                                                     fdi.answ,
 693                                                     fdi.iosize,
 694                                                     cookediov,
 695                                                     numdirent))) {
 696            break;
 697        }
 698    }
 699
 700/* done: */
 701
 702    fuse_ticket_drop(fdi.tick);
 703
 704out:
 705    return ((err == -1) ? 0 : err);
 706}
 707
 708__private_extern__
 709int
 710fuse_internal_readdir_processdata(vnode_t          vp,
 711                                  uio_t            uio,
 712                         __unused size_t           reqsize,
 713                                  void            *buf,
 714                                  size_t           bufsize,
 715                                  struct fuse_iov *cookediov,
 716                                  int             *numdirent)
 717{
 718    int err = 0;
 719    int cou = 0;
 720    int n   = 0;
 721    size_t bytesavail;
 722    size_t freclen;
 723
 724    struct dirent      *de;
 725    struct fuse_dirent *fudge;
 726
 727    if (bufsize < FUSE_NAME_OFFSET) {
 728        return -1;
 729    }
 730
 731    for (;;) {
 732
 733        if (bufsize < FUSE_NAME_OFFSET) {
 734            err = -1;
 735            break;
 736        }
 737
 738        fudge = (struct fuse_dirent *)buf;
 739        freclen = FUSE_DIRENT_SIZE(fudge);
 740
 741        cou++;
 742
 743        if (bufsize < freclen) {
 744            err = ((cou == 1) ? -1 : 0);
 745            break;
 746        }
 747
 748        /*
 749         * if (isbzero(buf, FUSE_NAME_OFFSET)) {
 750         *     // zero-pad incomplete buffer
 751         *     ...
 752         *     err = -1;
 753         *     break;
 754         * }
 755         */
 756
 757        if (!fudge->namelen) { 
 758            err = EINVAL;
 759            break;
 760        }
 761
 762        if (fudge->namelen > FUSE_MAXNAMLEN) {
 763            err = EIO;
 764            break;
 765        }
 766
 767#define GENERIC_DIRSIZ(dp) \
 768  ((sizeof(struct dirent) - (FUSE_MAXNAMLEN + 1)) + \
 769   (((dp)->d_namlen + 1 + 3) & ~3))
 770
 771        bytesavail = GENERIC_DIRSIZ((struct pseudo_dirent *)&fudge->namelen); 
 772
 773        if (bytesavail > (size_t)uio_resid(uio)) {
 774            err = -1;
 775            break;
 776        }
 777
 778        fiov_refresh(cookediov);
 779        fiov_adjust(cookediov, bytesavail);
 780
 781        de = (struct dirent *)cookediov->base;
 782#if __DARWIN_64_BIT_INO_T
 783        de->d_fileno = fudge->ino;
 784#else
 785        de->d_fileno = (ino_t)fudge->ino; /* XXX: truncation */
 786#endif /* __DARWIN_64_BIT_INO_T */
 787        de->d_reclen = bytesavail;
 788        de->d_type   = fudge->type; 
 789        de->d_namlen = fudge->namelen;
 790
 791        /* Filter out any ._* files if the mount is configured as such. */
 792        if (fuse_skip_apple_double_mp(vnode_mount(vp),
 793                                      fudge->name, fudge->namelen)) {
 794            de->d_fileno = 0;
 795            de->d_type = DT_WHT;
 796        }
 797
 798        memcpy((char *)cookediov->base +
 799               sizeof(struct dirent) - FUSE_MAXNAMLEN - 1,
 800               (char *)buf + FUSE_NAME_OFFSET, fudge->namelen);
 801        ((char *)cookediov->base)[bytesavail] = '\0';
 802
 803        err = uiomove(cookediov->base, (int)cookediov->len, uio);
 804        if (err) {
 805            break;
 806        }
 807
 808        n++;
 809
 810        buf = (char *)buf + freclen;
 811        bufsize -= freclen;
 812        uio_setoffset(uio, fudge->off);
 813    }
 814
 815    if (!err && numdirent) {
 816        *numdirent = n;
 817    }
 818
 819    return err;
 820}
 821
 822/* remove */
 823
 824static int
 825fuse_internal_remove_callback(vnode_t vp, void *cargs)
 826{
 827    struct vnode_attr *vap;
 828    uint64_t target_nlink;
 829
 830    vap = VTOVA(vp);
 831
 832    target_nlink = *(uint64_t *)cargs;
 833
 834    /* somewhat lame "heuristics", but you got better ideas? */
 835    if ((vap->va_nlink == target_nlink) && vnode_isreg(vp)) {
 836        fuse_invalidate_attr(vp);
 837    }
 838
 839    return VNODE_RETURNED;
 840}
 841
 842__private_extern__
 843int
 844fuse_internal_remove(vnode_t               dvp,
 845                     vnode_t               vp,
 846                     struct componentname *cnp,
 847                     enum fuse_opcode      op,
 848                     vfs_context_t         context)
 849{
 850    struct fuse_dispatcher fdi;
 851
 852    struct vnode_attr *vap = VTOVA(vp);
 853    int need_invalidate = 0;
 854    uint64_t target_nlink = 0;
 855    mount_t mp = vnode_mount(vp);
 856
 857    int err = 0;
 858
 859    fdisp_init(&fdi, cnp->cn_namelen + 1);
 860    fdisp_make_vp(&fdi, op, dvp, context);
 861
 862    memcpy(fdi.indata, cnp->cn_nameptr, cnp->cn_namelen);
 863    ((char *)fdi.indata)[cnp->cn_namelen] = '\0';
 864
 865    if ((vap->va_nlink > 1) && vnode_isreg(vp)) {
 866        need_invalidate = 1;
 867        target_nlink = vap->va_nlink;
 868    }
 869
 870    if (!(err = fdisp_wait_answ(&fdi))) {
 871        fuse_ticket_drop(fdi.tick);
 872    }
 873
 874    fuse_invalidate_attr(dvp);
 875    fuse_invalidate_attr(vp);
 876
 877    /*
 878     * XXX: M_MACFUSE_INVALIDATE_CACHED_VATTRS_UPON_UNLINK
 879     *
 880     * Consider the case where vap->va_nlink > 1 for the entity being
 881     * removed. In our world, other in-memory vnodes that share a link
 882     * count each with this one may not know right way that this one just
 883     * got deleted. We should let them know, say, through a vnode_iterate()
 884     * here and a callback that does fuse_invalidate_attr(vp) on each
 885     * relevant vnode.
 886     */
 887    if (need_invalidate && !err) {
 888        if (!vfs_busy(mp, LK_NOWAIT)) {
 889            vnode_iterate(mp, 0, fuse_internal_remove_callback,
 890                          (void *)&target_nlink);
 891            vfs_unbusy(mp);
 892        } else {
 893            IOLog("MacFUSE: skipping link count fixup upon remove\n");
 894        }
 895    }
 896
 897    return err;
 898}
 899
 900/* rename */
 901
 902__private_extern__
 903int
 904fuse_internal_rename(vnode_t               fdvp,
 905            __unused vnode_t               fvp,
 906                     struct componentname *fcnp,
 907                     vnode_t               tdvp,
 908            __unused vnode_t               tvp,
 909                     struct componentname *tcnp,
 910                     vfs_context_t         context)
 911{
 912    struct fuse_dispatcher fdi;
 913    struct fuse_rename_in *fri;
 914    int err = 0;
 915
 916    fdisp_init(&fdi, sizeof(*fri) + fcnp->cn_namelen + tcnp->cn_namelen + 2);
 917    fdisp_make_vp(&fdi, FUSE_RENAME, fdvp, context);
 918
 919    fri = fdi.indata;
 920    fri->newdir = VTOI(tdvp);
 921    memcpy((char *)fdi.indata + sizeof(*fri), fcnp->cn_nameptr,
 922           fcnp->cn_namelen);
 923    ((char *)fdi.indata)[sizeof(*fri) + fcnp->cn_namelen] = '\0';
 924    memcpy((char *)fdi.indata + sizeof(*fri) + fcnp->cn_namelen + 1,
 925           tcnp->cn_nameptr, tcnp->cn_namelen);
 926    ((char *)fdi.indata)[sizeof(*fri) + fcnp->cn_namelen +
 927                         tcnp->cn_namelen + 1] = '\0';
 928        
 929    if (!(err = fdisp_wait_answ(&fdi))) {
 930        fuse_ticket_drop(fdi.tick);
 931    }
 932
 933    if (err == 0) {
 934        fuse_invalidate_attr(fdvp);
 935        if (tdvp != fdvp) {
 936            fuse_invalidate_attr(tdvp);
 937        }
 938    }
 939
 940    return err;
 941}
 942
 943/* revoke */
 944
 945__private_extern__
 946int
 947fuse_internal_revoke(vnode_t vp, int flags, vfs_context_t context, int how)
 948{
 949    int ret = 0;
 950    struct fuse_vnode_data *fvdat = VTOFUD(vp);
 951
 952    fvdat->flag |= FN_REVOKED;
 953
 954    if (how == REVOKE_HARD) {
 955        ret = vn_revoke(vp, flags, context);
 956    }
 957
 958    return ret;
 959}
 960
 961/* strategy */
 962
 963__private_extern__
 964int
 965fuse_internal_strategy(vnode_t vp, buf_t bp)
 966{
 967    size_t biosize;
 968    size_t chunksize;
 969    size_t respsize;
 970
 971    int mapped = FALSE;
 972    int mode;
 973    int op;
 974    int vtype = vnode_vtype(vp);
 975
 976    int err = 0;
 977
 978    caddr_t bufdat;
 979    off_t   left;
 980    off_t   offset;
 981    int32_t bflags = buf_flags(bp);
 982
 983    fufh_type_t             fufh_type;
 984    struct fuse_dispatcher  fdi;
 985    struct fuse_data       *data;
 986    struct fuse_vnode_data *fvdat = VTOFUD(vp);
 987    struct fuse_filehandle *fufh = NULL;
 988    mount_t mp = vnode_mount(vp);
 989
 990    data = fuse_get_mpdata(mp);
 991
 992    biosize = data->blocksize;
 993
 994    if (!(vtype == VREG || vtype == VDIR)) {
 995        return ENOTSUP;
 996    }
 997 
 998    if (bflags & B_READ) {
 999        mode = FREAD;
1000        fufh_type = FUFH_RDONLY; /* FUFH_RDWR will also do */
1001    } else {
1002        mode = FWRITE;
1003        fufh_type = FUFH_WRONLY; /* FUFH_RDWR will also do */
1004    }
1005
1006    if (fvdat->flag & FN_CREATING) {
1007        fuse_lck_mtx_lock(fvdat->createlock);
1008        if (fvdat->flag & FN_CREATING) {
1009            (void)fuse_msleep(fvdat->creator, fvdat->createlock,
1010                              PDROP | PINOD | PCATCH, "fuse_internal_strategy",
1011                              NULL);
1012        } else {
1013            fuse_lck_mtx_unlock(fvdat->createlock);
1014        }
1015    }
1016
1017    fufh = &(fvdat->fufh[fufh_type]);
1018
1019    if (!FUFH_IS_VALID(fufh)) {
1020        fufh_type = FUFH_RDWR;
1021        fufh = &(fvdat->fufh[fufh_type]);
1022        if (!FUFH_IS_VALID(fufh)) {
1023            fufh = NULL;
1024        } else {
1025            /* We've successfully fallen back to FUFH_RDWR. */
1026        }
1027    }
1028
1029    if (!fufh) {
1030
1031        if (mode == FREAD) {
1032            fufh_type = FUFH_RDONLY;
1033        } else {
1034            fufh_type = FUFH_RDWR;
1035        }
1036
1037        /*
1038         * Lets NOT do the filehandle preflight check here.
1039         */
1040
1041        err = fuse_filehandle_get(vp, NULL, fufh_type, 0 /* mode */);
1042
1043        if (!err) {
1044            fufh = &(fvdat->fufh[fufh_type]);
1045            FUFH_AUX_INC(fufh);
1046            /* We've created a NEW fufh of type fufh_type. open_count is 1. */
1047        }
1048
1049    } else { /* good fufh */
1050
1051        FUSE_OSAddAtomic(1, (SInt32 *)&fuse_fh_reuse_count);
1052
1053        /* We're using an existing fufh of type fufh_type. */
1054    }
1055
1056    if (err) {
1057
1058         /* A more typical error case. */
1059         if ((err == ENOTCONN) || fuse_isdeadfs(vp)) {
1060             buf_seterror(bp, EIO);
1061             buf_biodone(bp);
1062             return EIO;
1063         }
1064
1065         IOLog("MacFUSE: strategy failed to get fh "
1066               "(vtype=%d, fufh_type=%d, err=%d)\n", vtype, fufh_type, err);
1067
1068         if (!vfs_issynchronous(mp)) {
1069             IOLog("MacFUSE: asynchronous write failed!\n");
1070         }
1071
1072         buf_seterror(bp, EIO);
1073         buf_biodone(bp);
1074         return EIO;
1075    }
1076
1077    if (!fufh) {
1078        panic("MacFUSE: tried everything but still no fufh");
1079        /* NOTREACHED */
1080    }
1081
1082#define B_INVAL 0x00040000 /* Does not contain valid info. */
1083#define B_ERROR 0x00080000 /* I/O error occurred. */
1084
1085    if (bflags & B_INVAL) {
1086        IOLog("MacFUSE: buffer does not contain valid information\n");
1087    } 
1088
1089    if (bflags & B_ERROR) {
1090        IOLog("MacFUSE: an I/O error has occured\n");
1091    }
1092
1093    if (buf_count(bp) == 0) {
1094        return 0;
1095    }
1096
1097    fdisp_init(&fdi, 0);
1098
1099    if (mode == FREAD) {
1100
1101        struct fuse_read_in *fri;
1102
1103        buf_setresid(bp, buf_count(bp));
1104        offset = (off_t)((off_t)buf_blkno(bp) * biosize);
1105
1106        if (offset >= fvdat->filesize) {
1107            /* Trying to read at/after EOF? */           
1108            if (offset != fvdat->filesize) {
1109                /* Trying to read after EOF? */
1110                buf_seterror(bp, EINVAL);
1111            }
1112            buf_biodone(bp);
1113            return 0;
1114        }
1115
1116        /* Note that we just made sure that offset < fvdat->filesize. */
1117        if ((offset + buf_count(bp)) > fvdat->filesize) {
1118            /* Trimming read */
1119            buf_setcount(bp, (uint32_t)(fvdat->filesize - offset));
1120        }
1121
1122        if (buf_map(bp, &bufdat)) {
1123            IOLog("MacFUSE: failed to map buffer in strategy\n");
1124            return EFAULT;
1125        } else {
1126            mapped = TRUE;
1127        }
1128
1129        while (buf_resid(bp) > 0) {
1130
1131            chunksize = min((size_t)buf_resid(bp), data->iosize);
1132
1133            fdi.iosize = sizeof(*fri);
1134
1135            op = FUSE_READ;
1136            if (vtype == VDIR) {
1137                op = FUSE_READDIR;
1138            }
1139            fdisp_make_vp(&fdi, op, vp, (vfs_context_t)0);
1140        
1141            fri = fdi.indata;
1142            fri->fh = fufh->fh_id;
1143
1144            /*
1145             * Historical note:
1146             *
1147             * fri->offset = ((off_t)(buf_blkno(bp))) * biosize;
1148             *
1149             * This wasn't being incremented!?
1150             */
1151
1152            fri->offset = offset;
1153            fri->size = (typeof(fri->size))chunksize;
1154            fdi.tick->tk_aw_type = FT_A_BUF;
1155            fdi.tick->tk_aw_bufdata = bufdat;
1156        
1157            if ((err = fdisp_wait_answ(&fdi))) {
1158                /* There was a problem with reading. */
1159                goto out;
1160            }
1161
1162            respsize = fdi.tick->tk_aw_bufsize;
1163
1164            if (respsize < 0) { /* Cannot really happen... */
1165                err = EIO;
1166                goto out;
1167            }
1168
1169            buf_setresid(bp, (uint32_t)(buf_resid(bp) - respsize));
1170            bufdat += respsize;
1171            offset += respsize;
1172
1173            /* Did we hit EOF before being done? */
1174            if ((respsize == 0) && (buf_resid(bp) > 0)) {
1175                 /*
1176                  * Historical note:
1177                  * If we don't get enough data, just fill the rest with zeros.
1178                  * In NFS context, this would mean a hole in the file.
1179                  */
1180
1181                 /* Zero-pad the incomplete buffer. */
1182                 bzero(bufdat, buf_resid(bp));
1183                 buf_setresid(bp, 0);
1184                 break;
1185            }
1186        } /* while (buf_resid(bp) > 0) */
1187    } else {
1188        /* write */
1189        struct fuse_write_in  *fwi;
1190        struct fuse_write_out *fwo;
1191        int merr = 0;
1192        off_t diff;
1193
1194        if (buf_map(bp, &bufdat)) {
1195            IOLog("MacFUSE: failed to map buffer in strategy\n");
1196            return EFAULT;
1197        } else {
1198            mapped = TRUE;
1199        }
1200
1201        /* Write begin */
1202
1203        buf_setresid(bp, buf_count(bp));
1204        offset = (off_t)((off_t)buf_blkno(bp) * biosize);
1205
1206        /* XXX: TBD -- Check here for extension (writing past end) */
1207
1208        left = buf_count(bp);
1209
1210        while (left) {
1211
1212            fdi.iosize = sizeof(*fwi);
1213            op = FUSE_WRITE;
1214
1215            fdisp_make_vp(&fdi, op, vp, (vfs_context_t)0);
1216            chunksize = min((size_t)left, data->iosize);
1217
1218            fwi = fdi.indata;
1219            fwi->fh = fufh->fh_id;
1220            fwi->offset = offset;
1221            fwi->size = (typeof(fwi->size))chunksize;
1222
1223            fdi.tick->tk_ms_type = FT_M_BUF;
1224            fdi.tick->tk_ms_bufdata = bufdat;
1225            fdi.tick->tk_ms_bufsize = chunksize;
1226
1227            /* About to write <chunksize> at <offset> */
1228
1229            if ((err = fdisp_wait_answ(&fdi))) {
1230                merr = 1;
1231                break;
1232            }
1233    
1234            fwo = fdi.answ;
1235            diff = chunksize - fwo->size;
1236            if (diff < 0) {
1237                err = EINVAL;
1238                break;
1239            }
1240    
1241            left -= fwo->size;
1242            bufdat += fwo->size;
1243            offset += fwo->size;
1244            buf_setresid(bp, buf_resid(bp) - fwo->size);
1245        }
1246
1247        if (merr) {
1248            goto out;
1249        }
1250    }
1251
1252    if (fdi.tick) {
1253        fuse_ticket_drop(fdi.tick);
1254    } else {
1255        /* No ticket upon leaving */
1256    }
1257
1258out:
1259
1260    if (err) {
1261        buf_seterror(bp, err);
1262    }
1263
1264    if (mapped == TRUE) {
1265        buf_unmap(bp);
1266    }
1267
1268    buf_biodone(bp);
1269
1270    return err;
1271}    
1272
1273__private_extern__
1274errno_t
1275fuse_internal_strategy_buf(struct vnop_strategy_args *ap)
1276{
1277    int32_t   bflags;
1278    upl_t     bupl;
1279    daddr64_t blkno, lblkno;
1280    int       bmap_flags;
1281    buf_t     bp    = ap->a_bp;
1282    vnode_t   vp    = buf_vnode(bp);
1283    int       vtype = vnode_vtype(vp);
1284
1285    struct fuse_data *data;
1286
1287    if (!vp || vtype == VCHR || vtype == VBLK) {
1288        panic("MacFUSE: buf_strategy: b_vp == NULL || vtype == VCHR | VBLK\n");
1289    }
1290
1291    bflags = buf_flags(bp);
1292
1293    if (bflags & B_READ) {
1294        bmap_flags = VNODE_READ;
1295    } else {
1296        bmap_flags = VNODE_WRITE;
1297    }
1298
1299    bupl = buf_upl(bp);
1300    blkno = buf_blkno(bp);
1301    lblkno = buf_lblkno(bp);
1302
1303    if (!(bflags & B_CLUSTER)) {
1304
1305        if (bupl) {
1306            return cluster_bp(bp);
1307        }
1308
1309        if (blkno == lblkno) {
1310            off_t  f_offset;
1311            size_t contig_bytes;
1312
1313            data = fuse_get_mpdata(vnode_mount(vp));
1314
1315            // Still think this is a kludge?
1316            f_offset = lblkno * data->blocksize;
1317            blkno = f_offset / data->blocksize;
1318
1319            buf_setblkno(bp, blkno);
1320
1321            contig_bytes = buf_count(bp);
1322
1323            if (blkno == -1) {
1324                buf_clear(bp);
1325            }
1326                        
1327            /*
1328             * Our "device" is always /all contiguous/. We don't wanna be
1329             * doing things like:
1330             *
1331             * ...
1332             *     else if ((long)contig_bytes < buf_count(bp)) {
1333             *         ret = buf_strategy_fragmented(devvp, bp, f_offset,
1334             *                                       contig_bytes));
1335             *         return ret;
1336             *      }
1337             */
1338        }
1339
1340        if (blkno == -1) {
1341            buf_biodone(bp);
1342            return 0;
1343        }
1344    }
1345
1346    // Issue the I/O
1347
1348    return fuse_internal_strategy(vp, bp);
1349}
1350
1351/* entity creation */
1352
1353__private_extern__
1354void
1355fuse_internal_newentry_makerequest(mount_t                 mp,
1356                                   uint64_t                dnid,
1357                                   struct componentname   *cnp,
1358                                   enum fuse_opcode        op,
1359                                   void                   *buf,
1360                                   size_t                  bufsize,
1361                                   struct fuse_dispatcher *fdip,
1362                                   vfs_context_t           context)
1363{
1364    fdisp_init(fdip, bufsize + cnp->cn_namelen + 1);
1365
1366    fdisp_make(fdip, op, mp, dnid, context);
1367    memcpy(fdip->indata, buf, bufsize);
1368    memcpy((char *)fdip->indata + bufsize, cnp->cn_nameptr, cnp->cn_namelen);
1369    ((char *)fdip->indata)[bufsize + cnp->cn_namelen] = '\0';
1370}
1371
1372__private_extern__
1373int
1374fuse_internal_newentry_core(vnode_t                 dvp,
1375                            vnode_t                *vpp,
1376                            struct componentname   *cnp,
1377                            enum vtype              vtyp,
1378                            struct fuse_dispatcher *fdip,
1379                            vfs_context_t           context)
1380{
1381    int err = 0;
1382    struct fuse_entry_out *feo;
1383    mount_t mp = vnode_mount(dvp);
1384
1385    if ((err = fdisp_wait_answ(fdip))) {
1386        return err;
1387    }
1388        
1389    feo = fdip->answ;
1390
1391    if ((err = fuse_internal_checkentry(feo, vtyp))) {
1392        goto out;
1393    }
1394
1395    err = fuse_vget_i(vpp, 0 /* flags */, feo, cnp, dvp, mp, context);
1396    if (err) {
1397        fuse_internal_forget_send(mp, context, feo->nodeid, 1, fdip);
1398        return err;
1399    }
1400
1401    cache_attrs(*vpp, feo);
1402
1403out:
1404    fuse_ticket_drop(fdip->tick);
1405
1406    return err;
1407}
1408
1409__private_extern__
1410int
1411fuse_internal_newentry(vnode_t               dvp,
1412                       vnode_t              *vpp,
1413                       struct componentname *cnp,
1414                       enum fuse_opcode      op,
1415                       void                 *buf,
1416                       size_t                bufsize,
1417                       enum vtype            vtype,
1418                       vfs_context_t         context)
1419{   
1420    int err;
1421    struct fuse_dispatcher fdi;
1422    mount_t mp = vnode_mount(dvp);
1423    
1424    if (fuse_skip_apple_double_mp(mp, cnp->cn_nameptr, cnp->cn_namelen)) {
1425        return EACCES;
1426    }
1427    
1428    fdisp_init(&fdi, 0);
1429    fuse_internal_newentry_makerequest(mp, VTOI(dvp), cnp, op, buf,
1430                                       bufsize, &fdi, context);
1431    err = fuse_internal_newentry_core(dvp, vpp, cnp, vtype, &fdi, context);
1432    fuse_invalidate_attr(dvp);            
1433                   
1434    return err;  
1435}         
1436
1437/* entity destruction */
1438
1439__private_extern__
1440int
1441fuse_internal_forget_callback(struct fuse_ticket *ftick, __unused uio_t uio)
1442{
1443    struct fuse_dispatcher fdi;
1444
1445    fdi.tick = ftick;
1446
1447    fuse_internal_forget_send(ftick->tk_data->mp, (vfs_context_t)0, 
1448        ((struct fuse_in_header *)ftick->tk_ms_fiov.base)->nodeid, 1, &fdi);
1449
1450    return 0;
1451}
1452
1453__private_extern__
1454void
1455fuse_internal_forget_send(mount_t                 mp,
1456                          vfs_context_t           context,
1457                          uint64_t                nodeid,
1458                          uint64_t                nlookup,
1459                          struct fuse_dispatcher *fdip)
1460{
1461    struct fuse_forget_in *ffi;
1462
1463    /*
1464     * KASSERT(nlookup > 0, ("zero-times forget for vp #%llu",
1465     *         (long long unsigned) nodeid));
1466     */
1467
1468    fdisp_init(fdip, sizeof(*ffi));
1469    fdisp_make(fdip, FUSE_FORGET, mp, nodeid, context);
1470
1471    ffi = fdip->indata;
1472    ffi->nlookup = nlookup;
1473
1474    fticket_invalidate(fdip->tick);
1475    fuse_insert_message(fdip->tick);
1476}
1477
1478__private_extern__
1479void
1480fuse_internal_interrupt_send(struct fuse_ticket *ftick)
1481{
1482    struct fuse_dispatcher fdi;
1483    struct fuse_interrupt_in *fii;
1484
1485    fdi.tick = ftick;
1486    fdisp_init(&fdi, sizeof(*fii));
1487    fdisp_make(&fdi, FUSE_INTERRUPT, ftick->tk_data->mp, (uint64_t)0,
1488               (vfs_context_t)0);
1489    fii = fdi.indata;
1490    fii->unique = ftick->tk_unique;
1491    fticket_invalidate(fdi.tick);
1492    fuse_insert_message(fdi.tick);
1493}
1494
1495__private_extern__
1496void
1497fuse_internal_vnode_disappear(vnode_t vp, vfs_context_t context, int how)
1498{   
1499    int err = 0;
1500
1501    fuse_vncache_purge(vp);
1502
1503    if (how != REVOKE_NONE) {
1504        err = fuse_internal_revoke(vp, REVOKEALL, context, how);
1505        if (err) {
1506            IOLog("MacFUSE: disappearing act: revoke failed (%d)\n", err);
1507        }
1508
1509        err = vnode_recycle(vp);
1510        if (err) {
1511            IOLog("MacFUSE: disappearing act: recycle failed (%d)\n", err);
1512        }
1513    }
1514}
1515
1516/* fuse start/stop */
1517
1518__private_extern__
1519int
1520fuse_internal_init_synchronous(struct fuse_ticket *ftick)
1521{
1522    int err = 0;
1523    struct fuse_init_out *fiio;
1524    struct fuse_data *data = ftick->tk_data;
1525
1526    if ((err = ftick->tk_aw_ohead.error)) {
1527        goto out;
1528    }
1529
1530    fiio = fticket_resp(ftick)->base;
1531
1532    if ((fiio->major < MACFUSE_MIN_USER_VERSION_MAJOR) ||
1533        (fiio->minor < MACFUSE_MIN_USER_VERSION_MINOR)){
1534        IOLog("MacFUSE: user-space library has too low a version\n");
1535        err = EPROTONOSUPPORT;
1536        goto out;
1537    }
1538
1539    data->fuse_libabi_major = fiio->major;
1540    data->fuse_libabi_minor = fiio->minor;
1541
1542    if (fuse_libabi_geq(data, MACFUSE_MIN_USER_VERSION_MAJOR,
1543                              MACFUSE_MIN_USER_VERSION_MINOR)) {
1544        if (fticket_resp(ftick)->len == sizeof(struct fuse_init_out)) {
1545            data->max_write = fiio->max_write;
1546        } else {
1547            err = EINVAL;
1548        }
1549    } else {
1550        /* Old fix values */
1551        data->max_write = 4096;
1552    }
1553
1554    if (fiio->flags & FUSE_CASE_INSENSITIVE) {
1555        data->dataflags |= FSESS_CASE_INSENSITIVE;
1556    }
1557
1558    if (fiio->flags & FUSE_VOL_RENAME) {
1559        data->dataflags |= FSESS_VOL_RENAME;
1560    }
1561
1562    if (fiio->flags & FUSE_XTIMES) {
1563        data->dataflags |= FSESS_XTIMES;
1564    }
1565
1566out:
1567    fuse_ticket_drop(ftick);
1568
1569    if (err) {
1570        fdata_set_dead(data);
1571    }
1572
1573    fuse_lck_mtx_lock(data->ticket_mtx);
1574    data->dataflags |= FSESS_INITED;
1575    fuse_wakeup(&data->ticketer);
1576    fuse_lck_mtx_unlock(data->ticket_mtx);
1577
1578    return 0;
1579}
1580
1581__private_extern__
1582int
1583fuse_internal_send_init(struct fuse_data *data, vfs_context_t context)
1584{
1585    int err = 0;
1586    struct fuse_init_in   *fiii;
1587    struct fuse_dispatcher fdi;
1588
1589    fdisp_init(&fdi, sizeof(*fiii));
1590    fdisp_make(&fdi, FUSE_INIT, data->mp, 0, context);
1591    fiii = fdi.indata;
1592    fiii->major = FUSE_KERNEL_VERSION;
1593    fiii->minor = FUSE_KERNEL_MINOR_VERSION;
1594    fiii->max_readahead = data->iosize * 16;
1595    fiii->flags = 0;
1596
1597    /* blocking FUSE_INIT up to user space */
1598
1599    err = fdisp_wait_answ(&fdi);
1600    if (err) {
1601        IOLog("MacFUSE: user-space initialization failed (%d)\n", err);
1602        return err;
1603    }
1604
1605    err = fuse_internal_init_synchronous(fdi.tick);
1606    if (err) {
1607        IOLog("MacFUSE: in-kernel initialization failed (%d)\n", err);
1608        return err;
1609    }
1610
1611    return 0;
1612}
1613
1614/* other */
1615
1616static int
1617fuse_internal_print_vnodes_callback(vnode_t vp, __unused void *cargs)
1618{
1619    const char *vname = NULL;
1620    struct fuse_vnode_data *fvdat = VTOFUD(vp);
1621
1622#if M_MACFUSE_ENABLE_UNSUPPORTED
1623    vname = vnode_getname(vp);
1624#endif /* M_MACFUSE_ENABLE_UNSUPPORTED */
1625
1626    if (vname) {
1627        IOLog("MacFUSE: vp=%p ino=%lld parent=%lld inuse=%d %s\n",
1628              vp, fvdat->nodeid, fvdat->parent_nodeid,
1629              vnode_isinuse(vp, 0), vname);
1630    } else {
1631        if (fvdat->nodeid == FUSE_ROOT_ID) {
1632            IOLog("MacFUSE: vp=%p ino=%lld parent=%lld inuse=%d /\n",
1633                  vp, fvdat->nodeid, fvdat->parent_nodeid,
1634                  vnode_isinuse(vp, 0));
1635        } else {
1636            IOLog("MacFUSE: vp=%p ino=%lld parent=%lld inuse=%d\n",
1637                  vp, fvdat->nodeid, fvdat->parent_nodeid,
1638                  vnode_isinuse(vp, 0));
1639        }
1640    }
1641
1642#if M_MACFUSE_ENABLE_UNSUPPORTED
1643    if (vname) {
1644        vnode_putname(vname);
1645    }
1646#endif /* M_MACFUSE_ENABLE_UNSUPPORTED */
1647 
1648    return VNODE_RETURNED;
1649}
1650
1651__private_extern__
1652void
1653fuse_internal_print_vnodes(mount_t mp)
1654{
1655    vnode_iterate(mp, VNODE_ITERATE_ALL,
1656                  fuse_internal_print_vnodes_callback, NULL);
1657}
1658
1659__private_extern__
1660void
1661fuse_preflight_log(vnode_t vp, fufh_type_t fufh_type, int err, char *message)
1662{
1663    const char *vname = NULL;
1664
1665#if M_MACFUSE_ENABLE_UNSUPPORTED
1666    vname = vnode_getname(vp);
1667#else
1668    (void)vname;
1669    (void)vp;
1670#endif /* M_MACFUSE_ENABLE_UNSUPPORTED */
1671
1672    if (vname) {
1673        IOLog("MacFUSE: file handle preflight "
1674              "(caller=%s, type=%d, err=%d, name=%s)\n",
1675              message, fufh_type, err, vname);
1676    } else {
1677        IOLog("MacFUSE: file handle preflight "
1678              "(caller=%s, type=%d, err=%d)\n", message, fufh_type, err);
1679    }
1680
1681#if M_MACFUSE_ENABLE_UNSUPPORTED
1682    if (vname) {
1683        vnode_putname(vname);
1684    }
1685#endif /* M_MACFUSE_ENABLE_UNSUPPORTED */
1686}