PageRenderTime 70ms CodeModel.GetById 18ms app.highlight 45ms RepoModel.GetById 1ms app.codeStats 1ms

/core/10.4/fusefs/fuse_ipc.c

http://macfuse.googlecode.com/
C | 1102 lines | 850 code | 225 blank | 27 comment | 116 complexity | 27c1ba812fde08f4f2ad194aafffebbb MD5 | raw file
   1/*
   2 * Copyright (C) 2006-2008 Google. All Rights Reserved.
   3 * Amit Singh <singh@>
   4 */
   5
   6#include <sys/types.h>
   7#include <sys/malloc.h>
   8
   9#include "fuse.h"
  10#include "fuse_internal.h"
  11#include "fuse_ipc.h"
  12#include "fuse_locking.h"
  13#include "fuse_node.h"
  14#include "fuse_sysctl.h"
  15
  16static struct fuse_ticket *fticket_alloc(struct fuse_data *data);
  17static void                fticket_refresh(struct fuse_ticket *ftick);
  18static void                fticket_destroy(struct fuse_ticket *ftick);
  19static int                 fticket_wait_answer(struct fuse_ticket *ftick);
  20static __inline__ int      fticket_aw_pull_uio(struct fuse_ticket *ftick,
  21                                               uio_t uio);
  22static __inline__ void     fuse_push_freeticks(struct fuse_ticket *ftick);
  23
  24static __inline__ struct fuse_ticket *
  25fuse_pop_freeticks(struct fuse_data *data);
  26
  27static __inline__ void     fuse_push_allticks(struct fuse_ticket *ftick);
  28static __inline__ void     fuse_remove_allticks(struct fuse_ticket *ftick);
  29static struct fuse_ticket *fuse_pop_allticks(struct fuse_data *data);
  30
  31static int             fuse_body_audit(struct fuse_ticket *ftick, size_t blen);
  32static __inline__ void fuse_setup_ihead(struct fuse_in_header *ihead,
  33                                        struct fuse_ticket    *ftick,
  34                                        uint64_t               nid,
  35                                        enum fuse_opcode       op,
  36                                        size_t                 blen,
  37                                        vfs_context_t          context);
  38
  39static fuse_handler_t  fuse_standard_handler;
  40
  41void
  42fiov_init(struct fuse_iov *fiov, size_t size)
  43{
  44    size_t msize = FU_AT_LEAST(size);
  45
  46    fiov->len = 0;
  47
  48    fiov->base = FUSE_OSMalloc(msize, fuse_malloc_tag);
  49    if (!fiov->base) {
  50        panic("MacFUSE: OSMalloc failed in fiov_init");
  51    }
  52
  53    FUSE_OSAddAtomic(1, (SInt32 *)&fuse_iov_current);
  54
  55    bzero(fiov->base, msize);
  56
  57    fiov->allocated_size = msize;
  58    fiov->credit = fuse_iov_credit;
  59}
  60
  61void
  62fiov_teardown(struct fuse_iov *fiov)
  63{
  64    FUSE_OSFree(fiov->base, fiov->allocated_size, fuse_malloc_tag);
  65    fiov->allocated_size = 0;
  66
  67    FUSE_OSAddAtomic(-1, (SInt32 *)&fuse_iov_current);
  68}
  69
  70void
  71fiov_adjust(struct fuse_iov *fiov, size_t size)
  72{
  73    if (fiov->allocated_size < size ||
  74        (fuse_iov_permanent_bufsize >= 0 &&
  75         fiov->allocated_size - size > fuse_iov_permanent_bufsize &&
  76             --fiov->credit < 0)) {
  77
  78        fiov->base = FUSE_OSRealloc_nocopy(fiov->base, fiov->allocated_size,
  79                                           FU_AT_LEAST(size));
  80        if (!fiov->base) {
  81            panic("MacFUSE: realloc failed");
  82        }
  83
  84        fiov->allocated_size = FU_AT_LEAST(size);
  85        fiov->credit = fuse_iov_credit;
  86    }
  87
  88    fiov->len = size;
  89}
  90
  91int
  92fiov_adjust_canfail(struct fuse_iov *fiov, size_t size)
  93{
  94    if (fiov->allocated_size < size ||
  95        (fuse_iov_permanent_bufsize >= 0 &&
  96         fiov->allocated_size - size > fuse_iov_permanent_bufsize &&
  97             --fiov->credit < 0)) {
  98
  99        void *tmpbase = NULL;
 100
 101        tmpbase = FUSE_OSRealloc_nocopy_canfail(fiov->base,
 102                                                fiov->allocated_size,
 103                                                FU_AT_LEAST(size));
 104        if (!tmpbase) {
 105            return ENOMEM;
 106        }
 107
 108        fiov->base = tmpbase;
 109        fiov->allocated_size = FU_AT_LEAST(size);
 110        fiov->credit = fuse_iov_credit;
 111    }
 112
 113    fiov->len = size;
 114
 115    return 0;
 116}
 117
 118void
 119fiov_refresh(struct fuse_iov *fiov)
 120{
 121    bzero(fiov->base, fiov->len);    
 122    fiov_adjust(fiov, 0);
 123}
 124
 125static struct fuse_ticket *
 126fticket_alloc(struct fuse_data *data)
 127{
 128    struct fuse_ticket *ftick;
 129
 130    ftick = (struct fuse_ticket *)FUSE_OSMalloc(sizeof(struct fuse_ticket),
 131                                                fuse_malloc_tag);
 132    if (!ftick) {
 133        panic("MacFUSE: OSMalloc failed in fticket_alloc");
 134    }
 135
 136    FUSE_OSAddAtomic(1, (SInt32 *)&fuse_tickets_current);
 137
 138    bzero(ftick, sizeof(struct fuse_ticket));
 139
 140    ftick->tk_unique = data->ticketer++;
 141    ftick->tk_data = data;
 142
 143    fiov_init(&ftick->tk_ms_fiov, sizeof(struct fuse_in_header));
 144    ftick->tk_ms_type = FT_M_FIOV;
 145
 146    ftick->tk_aw_mtx = lck_mtx_alloc_init(fuse_lock_group, fuse_lock_attr);
 147    fiov_init(&ftick->tk_aw_fiov, 0);
 148    ftick->tk_aw_type = FT_A_FIOV;
 149
 150    return ftick;
 151}
 152
 153static __inline__
 154void
 155fticket_refresh(struct fuse_ticket *ftick)
 156{
 157    fiov_refresh(&ftick->tk_ms_fiov);
 158    ftick->tk_ms_bufdata = NULL;
 159    ftick->tk_ms_bufsize = 0;
 160    ftick->tk_ms_type = FT_M_FIOV;
 161
 162    bzero(&ftick->tk_aw_ohead, sizeof(struct fuse_out_header));
 163
 164    fiov_refresh(&ftick->tk_aw_fiov);
 165    ftick->tk_aw_errno = 0;
 166    ftick->tk_aw_bufdata = NULL;
 167    ftick->tk_aw_bufsize = 0;
 168    ftick->tk_aw_type = FT_A_FIOV;
 169
 170    ftick->tk_flag = 0;
 171    ftick->tk_age++;
 172}
 173
 174static void
 175fticket_destroy(struct fuse_ticket *ftick)
 176{
 177    fiov_teardown(&ftick->tk_ms_fiov);
 178
 179    lck_mtx_free(ftick->tk_aw_mtx, fuse_lock_group);
 180    ftick->tk_aw_mtx = NULL;
 181    fiov_teardown(&ftick->tk_aw_fiov);
 182
 183    FUSE_OSFree(ftick, sizeof(struct fuse_ticket), fuse_malloc_tag);
 184
 185    FUSE_OSAddAtomic(-1, (SInt32 *)&fuse_tickets_current);
 186}
 187
 188static int
 189fticket_wait_answer(struct fuse_ticket *ftick)
 190{
 191    int err = 0;
 192    struct fuse_data *data;
 193
 194    fuse_lck_mtx_lock(ftick->tk_aw_mtx);
 195
 196    if (fticket_answered(ftick)) {
 197        goto out;
 198    }
 199
 200    data = ftick->tk_data;
 201
 202    if (fdata_dead_get(data)) {
 203        err = ENOTCONN;
 204        fticket_set_answered(ftick);
 205        goto out;
 206    }
 207
 208again:
 209    err = fuse_msleep(ftick, ftick->tk_aw_mtx, PCATCH, "fu_ans",
 210                      data->daemon_timeout_p);
 211    if (err == EAGAIN) { /* same as EWOULDBLOCK */
 212
 213        kern_return_t kr;
 214        unsigned int rf;
 215
 216        fuse_lck_mtx_lock(data->timeout_mtx);
 217
 218        if (data->dataflags & FSESS_NO_ALERTS) {
 219            data->timeout_status = FUSE_DAEMON_TIMEOUT_DEAD;
 220            fuse_lck_mtx_unlock(data->timeout_mtx);
 221            goto alreadydead;
 222        }
 223
 224        switch (data->timeout_status) {
 225
 226        case FUSE_DAEMON_TIMEOUT_NONE:
 227            data->timeout_status = FUSE_DAEMON_TIMEOUT_PROCESSING;
 228            fuse_lck_mtx_unlock(data->timeout_mtx);
 229            break;
 230
 231        case FUSE_DAEMON_TIMEOUT_PROCESSING:
 232            fuse_lck_mtx_unlock(data->timeout_mtx);
 233            goto again;
 234            break; /* NOTREACHED */
 235
 236        case FUSE_DAEMON_TIMEOUT_DEAD:
 237            fuse_lck_mtx_unlock(data->timeout_mtx);
 238            goto alreadydead;
 239            break; /* NOTREACHED */
 240
 241        default:
 242            IOLog("MacFUSE: invalid timeout status (%d)\n",
 243                  data->timeout_status);
 244            fuse_lck_mtx_unlock(data->timeout_mtx);
 245            goto again;
 246            break; /* NOTREACHED */
 247        }
 248
 249        /*
 250         * We will "hang" while this is showing.
 251         */
 252
 253#if M_MACFUSE_ENABLE_KUNC
 254        kr = KUNCUserNotificationDisplayAlert(
 255                 FUSE_DAEMON_TIMEOUT_ALERT_TIMEOUT,   // timeout
 256                 0,                                   // flags (stop alert)
 257                 NULL,                                // iconPath
 258                 NULL,                                // soundPath
 259                 NULL,                                // localizationPath
 260                 data->volname,                       // alertHeader
 261                 FUSE_DAEMON_TIMEOUT_ALERT_MESSAGE,
 262                 FUSE_DAEMON_TIMEOUT_DEFAULT_BUTTON_TITLE,
 263                 FUSE_DAEMON_TIMEOUT_ALTERNATE_BUTTON_TITLE,
 264                 FUSE_DAEMON_TIMEOUT_OTHER_BUTTON_TITLE,
 265                 &rf);
 266#else
 267        kr = KERN_FAILURE;
 268#endif
 269
 270        if (kr != KERN_SUCCESS) {
 271            /* force ejection if we couldn't show the dialog */
 272            IOLog("MacFUSE: force ejecting (no response from user space %d)\n",
 273                  kr);
 274            rf = kKUNCOtherResponse;
 275        }
 276
 277        fuse_lck_mtx_lock(data->timeout_mtx);
 278        switch (rf) {
 279        case kKUNCOtherResponse:     /* Force Eject      */
 280            data->timeout_status = FUSE_DAEMON_TIMEOUT_DEAD;
 281            fuse_lck_mtx_unlock(data->timeout_mtx);
 282            break;
 283
 284        case kKUNCDefaultResponse:   /* Keep Trying      */
 285        case kKUNCAlternateResponse: /* Don't Warn Again */
 286        case kKUNCCancelResponse:    /* No Selection     */
 287            data->timeout_status = FUSE_DAEMON_TIMEOUT_NONE;
 288            if (rf == kKUNCAlternateResponse) {
 289                data->daemon_timeout_p = (struct timespec *)0;
 290            }
 291            fuse_lck_mtx_unlock(data->timeout_mtx);
 292            goto again;
 293            break; /* NOTREACHED */
 294
 295        default:
 296            IOLog("MacFUSE: unknown response from alert panel (kr=%d, rf=%d)\n",
 297                  kr, rf);
 298            data->timeout_status = FUSE_DAEMON_TIMEOUT_DEAD;
 299            fuse_lck_mtx_unlock(data->timeout_mtx);
 300            break;
 301        }
 302
 303alreadydead:
 304        if (!fdata_dead_get(data)) {
 305            fdata_set_dead(data);
 306        }
 307        err = ENOTCONN;
 308        fticket_set_answered(ftick);
 309
 310        goto out;
 311    }
 312
 313#if M_MACFUSE_ENABLE_INTERRUPT
 314    else if (err == -1) {
 315       /*
 316        * XXX: Stop gap! I really need to finish interruption plumbing.
 317        */
 318       fuse_internal_interrupt_send(ftick);
 319    }
 320#endif
 321
 322out:
 323    fuse_lck_mtx_unlock(ftick->tk_aw_mtx);
 324
 325    if (!(err || fticket_answered(ftick))) {
 326        IOLog("MacFUSE: requester was woken up but still no answer");
 327        err = ENXIO;
 328    }
 329
 330    return err;
 331}
 332
 333static __inline__
 334int
 335fticket_aw_pull_uio(struct fuse_ticket *ftick, uio_t uio)
 336{
 337    int err = 0;
 338    size_t len = (size_t)uio_resid(uio);
 339
 340    if (len) {
 341        switch (ftick->tk_aw_type) {
 342        case FT_A_FIOV:
 343            err = fiov_adjust_canfail(fticket_resp(ftick), len);
 344            if (err) {
 345                fticket_set_killl(ftick);
 346                IOLog("MacFUSE: failed to pull uio (error=%d)\n", err);
 347                break;
 348            }
 349            err = uiomove(fticket_resp(ftick)->base, (int)len, uio);
 350            if (err) {
 351                IOLog("MacFUSE: FT_A_FIOV error is %d (%p, %ld, %p)\n",
 352                      err, fticket_resp(ftick)->base, len, uio);
 353            }
 354            break;
 355
 356        case FT_A_BUF:
 357            ftick->tk_aw_bufsize = len;
 358            err = uiomove(ftick->tk_aw_bufdata, (int)len, uio);
 359            if (err) {
 360                IOLog("MacFUSE: FT_A_BUF error is %d (%p, %ld, %p)\n",
 361                      err, ftick->tk_aw_bufdata, len, uio);
 362            }
 363            break;
 364
 365        default:
 366            panic("MacFUSE: unknown answer type for ticket %p", ftick);
 367        }
 368    }
 369
 370    return err;
 371}
 372
 373int
 374fticket_pull(struct fuse_ticket *ftick, uio_t uio)
 375{
 376    int err = 0;
 377
 378    if (ftick->tk_aw_ohead.error) {
 379        return 0;
 380    }
 381
 382    err = fuse_body_audit(ftick, (size_t)uio_resid(uio));
 383    if (!err) {
 384        err = fticket_aw_pull_uio(ftick, uio);
 385    }
 386
 387    return err;
 388}
 389
 390struct fuse_data *
 391fdata_alloc(struct proc *p)
 392{
 393    struct fuse_data *data;
 394
 395    data = (struct fuse_data *)FUSE_OSMalloc(sizeof(struct fuse_data),
 396                                             fuse_malloc_tag);
 397    if (!data) {
 398        panic("MacFUSE: OSMalloc failed in fdata_alloc");
 399    }
 400
 401    bzero(data, sizeof(struct fuse_data));
 402
 403    data->mp            = NULL;
 404    data->rootvp        = NULLVP;
 405    data->mount_state   = FM_NOTMOUNTED;
 406    data->daemoncred    = proc_ucred(p);
 407    data->daemonpid     = proc_pid(p);
 408    data->dataflags     = 0;
 409    data->mountaltflags = 0ULL;
 410    data->noimplflags   = 0ULL;
 411
 412    data->rwlock        = lck_rw_alloc_init(fuse_lock_group, fuse_lock_attr);
 413    data->ms_mtx        = lck_mtx_alloc_init(fuse_lock_group, fuse_lock_attr);
 414    data->aw_mtx        = lck_mtx_alloc_init(fuse_lock_group, fuse_lock_attr);
 415    data->ticket_mtx    = lck_mtx_alloc_init(fuse_lock_group, fuse_lock_attr);
 416
 417    STAILQ_INIT(&data->ms_head);
 418    TAILQ_INIT(&data->aw_head);
 419    STAILQ_INIT(&data->freetickets_head);
 420    TAILQ_INIT(&data->alltickets_head);
 421
 422    data->freeticket_counter = 0;
 423    data->deadticket_counter = 0;
 424    data->ticketer           = 0;
 425
 426    kauth_cred_ref(data->daemoncred);
 427
 428#if M_MACFUSE_EXCPLICIT_RENAME_LOCK
 429    data->rename_lock = lck_rw_alloc_init(fuse_lock_group, fuse_lock_attr);
 430#endif
 431
 432    data->timeout_status = FUSE_DAEMON_TIMEOUT_NONE;
 433    data->timeout_mtx    = lck_mtx_alloc_init(fuse_lock_group, fuse_lock_attr);
 434
 435    return data;
 436}
 437
 438void
 439fdata_destroy(struct fuse_data *data)
 440{
 441    struct fuse_ticket *ftick;
 442
 443    lck_mtx_free(data->ms_mtx, fuse_lock_group);
 444    data->ms_mtx = NULL;
 445
 446    lck_mtx_free(data->aw_mtx, fuse_lock_group);
 447    data->aw_mtx = NULL;
 448
 449    lck_mtx_free(data->ticket_mtx, fuse_lock_group);
 450    data->ticket_mtx = NULL;
 451
 452#if M_MACFUSE_EXPLICIT_RENAME_LOCK
 453    lck_rw_free(data->rename_lock, fuse_lock_group);
 454    data->rename_lock = NULL;
 455#endif
 456
 457    data->timeout_status = FUSE_DAEMON_TIMEOUT_NONE;
 458    lck_mtx_free(data->timeout_mtx, fuse_lock_group);
 459
 460    while ((ftick = fuse_pop_allticks(data))) {
 461        fticket_destroy(ftick);
 462    }
 463
 464    /* XXX: deprecated; should use kauth_cred_unref() */
 465    kauth_cred_rele(data->daemoncred);
 466
 467    lck_rw_free(data->rwlock, fuse_lock_group);
 468
 469    FUSE_OSFree(data, sizeof(struct fuse_data), fuse_malloc_tag);
 470}
 471
 472int
 473fdata_dead_get(struct fuse_data *data)
 474{
 475    return (data->dataflags & FSESS_DEAD);
 476}
 477
 478void
 479fdata_set_dead(struct fuse_data *data)
 480{
 481    fuse_lck_mtx_lock(data->ms_mtx);
 482    if (fdata_dead_get(data)) { 
 483        fuse_lck_mtx_unlock(data->ms_mtx);
 484        return;
 485    }
 486
 487    data->dataflags |= FSESS_DEAD;
 488    fuse_wakeup_one((caddr_t)data);
 489#if M_MACFUSE_ENABLE_DSELECT
 490    selwakeup((struct selinfo*)&data->d_rsel);
 491#endif /* M_MACFUSE_ENABLE_DSELECT */
 492    fuse_lck_mtx_unlock(data->ms_mtx);
 493
 494    fuse_lck_mtx_lock(data->ticket_mtx);
 495    fuse_wakeup(&data->ticketer);
 496    fuse_lck_mtx_unlock(data->ticket_mtx);
 497
 498    vfs_event_signal(&vfs_statfs(data->mp)->f_fsid, VQ_DEAD, 0);
 499}
 500
 501static __inline__
 502void
 503fuse_push_freeticks(struct fuse_ticket *ftick)
 504{
 505    STAILQ_INSERT_TAIL(&ftick->tk_data->freetickets_head, ftick,
 506                       tk_freetickets_link);
 507    ftick->tk_data->freeticket_counter++;
 508}
 509
 510static __inline__
 511struct fuse_ticket *
 512fuse_pop_freeticks(struct fuse_data *data)
 513{
 514    struct fuse_ticket *ftick;
 515
 516    if ((ftick = STAILQ_FIRST(&data->freetickets_head))) {
 517        STAILQ_REMOVE_HEAD(&data->freetickets_head, tk_freetickets_link);
 518        data->freeticket_counter--;
 519    }
 520
 521    if (STAILQ_EMPTY(&data->freetickets_head) &&
 522        (data->freeticket_counter != 0)) {
 523        panic("MacFUSE: ticket count mismatch!");
 524    }
 525
 526    return ftick;
 527}
 528
 529static __inline__
 530void
 531fuse_push_allticks(struct fuse_ticket *ftick)
 532{
 533    TAILQ_INSERT_TAIL(&ftick->tk_data->alltickets_head, ftick,
 534                      tk_alltickets_link);
 535}
 536
 537static __inline__
 538void
 539fuse_remove_allticks(struct fuse_ticket *ftick)
 540{
 541    ftick->tk_data->deadticket_counter++;
 542    TAILQ_REMOVE(&ftick->tk_data->alltickets_head, ftick, tk_alltickets_link);
 543}
 544
 545static struct fuse_ticket *
 546fuse_pop_allticks(struct fuse_data *data)
 547{
 548    struct fuse_ticket *ftick;
 549
 550    if ((ftick = TAILQ_FIRST(&data->alltickets_head))) {
 551        fuse_remove_allticks(ftick);
 552    }
 553
 554    return ftick;
 555}
 556
 557struct fuse_ticket *
 558fuse_ticket_fetch(struct fuse_data *data)
 559{
 560    int err = 0;
 561    struct fuse_ticket *ftick;
 562
 563    fuse_lck_mtx_lock(data->ticket_mtx);
 564
 565    if (data->freeticket_counter == 0) {
 566        fuse_lck_mtx_unlock(data->ticket_mtx);
 567        ftick = fticket_alloc(data);
 568        if (!ftick) {
 569            panic("MacFUSE: ticket allocation failed");
 570        }
 571        fuse_lck_mtx_lock(data->ticket_mtx);
 572        fuse_push_allticks(ftick);
 573    } else {
 574        /* locked here */
 575        ftick = fuse_pop_freeticks(data);
 576        if (!ftick) {
 577            panic("MacFUSE: no free ticket despite the counter's value");
 578        }
 579    }
 580
 581    if (!(data->dataflags & FSESS_INITED) && data->ticketer > 1) {
 582        err = fuse_msleep(&data->ticketer, data->ticket_mtx, PCATCH | PDROP,
 583                          "fu_ini", 0);
 584    } else {
 585        if ((fuse_max_tickets != 0) &&
 586            ((data->ticketer - data->deadticket_counter) > fuse_max_tickets)) {
 587            err = 1;
 588        }
 589        fuse_lck_mtx_unlock(data->ticket_mtx);
 590    }
 591
 592    if (err) {
 593        fdata_set_dead(data);
 594    }
 595
 596    return ftick;
 597}
 598
 599void
 600fuse_ticket_drop(struct fuse_ticket *ftick)
 601{
 602    int die = 0;
 603
 604    fuse_lck_mtx_lock(ftick->tk_data->ticket_mtx);
 605
 606    if ((fuse_max_freetickets >= 0 &&
 607        fuse_max_freetickets <= ftick->tk_data->freeticket_counter) ||
 608        (ftick->tk_flag & FT_KILLL)) {
 609        die = 1;
 610    } else {
 611        fuse_lck_mtx_unlock(ftick->tk_data->ticket_mtx);
 612        fticket_refresh(ftick);
 613        fuse_lck_mtx_lock(ftick->tk_data->ticket_mtx);
 614    }
 615
 616    /* locked here */
 617
 618    if (die) {
 619        fuse_remove_allticks(ftick);
 620        fuse_lck_mtx_unlock(ftick->tk_data->ticket_mtx);
 621        fticket_destroy(ftick);
 622    } else {
 623        fuse_push_freeticks(ftick);
 624        fuse_lck_mtx_unlock(ftick->tk_data->ticket_mtx);
 625    }
 626}
 627
 628void
 629fuse_ticket_kill(struct fuse_ticket *ftick)
 630{
 631    fuse_lck_mtx_lock(ftick->tk_data->ticket_mtx);
 632    fuse_remove_allticks(ftick);
 633    fuse_lck_mtx_unlock(ftick->tk_data->ticket_mtx);
 634    fticket_destroy(ftick);
 635}
 636
 637void
 638fuse_ticket_drop_invalid(struct fuse_ticket *ftick)
 639{
 640    if (ftick->tk_flag & FT_INVAL) {
 641        fuse_ticket_drop(ftick);
 642    }
 643}
 644
 645void
 646fuse_insert_callback(struct fuse_ticket *ftick, fuse_handler_t *handler)
 647{
 648    if (fdata_dead_get(ftick->tk_data)) {
 649        return;
 650    }
 651
 652    ftick->tk_aw_handler = handler;
 653
 654    fuse_lck_mtx_lock(ftick->tk_data->aw_mtx);
 655    fuse_aw_push(ftick);
 656    fuse_lck_mtx_unlock(ftick->tk_data->aw_mtx);
 657}
 658
 659void
 660fuse_insert_message(struct fuse_ticket *ftick)
 661{
 662    if (ftick->tk_flag & FT_DIRTY) {
 663        panic("MacFUSE: ticket reused without being refreshed");
 664    }
 665
 666    ftick->tk_flag |= FT_DIRTY;
 667
 668    if (fdata_dead_get(ftick->tk_data)) {
 669        return;
 670    }
 671
 672    fuse_lck_mtx_lock(ftick->tk_data->ms_mtx);
 673    fuse_ms_push(ftick);
 674    fuse_wakeup_one((caddr_t)ftick->tk_data);
 675#if M_MACFUSE_ENABLE_DSELECT
 676    selwakeup((struct selinfo*)&ftick->tk_data->d_rsel);
 677#endif /* M_MACFUSE_ENABLE_DSELECT */
 678    fuse_lck_mtx_unlock(ftick->tk_data->ms_mtx);
 679}
 680
 681void
 682fuse_insert_message_head(struct fuse_ticket *ftick)
 683{
 684    if (ftick->tk_flag & FT_DIRTY) {
 685        panic("MacFUSE: ticket reused without being refreshed");
 686    }
 687
 688    ftick->tk_flag |= FT_DIRTY;
 689
 690    if (fdata_dead_get(ftick->tk_data)) {
 691        return;
 692    }
 693
 694    fuse_lck_mtx_lock(ftick->tk_data->ms_mtx);
 695    fuse_ms_push_head(ftick);
 696    fuse_wakeup_one((caddr_t)ftick->tk_data);
 697#if M_MACFUSE_ENABLE_DSELECT
 698    selwakeup((struct selinfo*)&ftick->tk_data->d_rsel);
 699#endif /* M_MACFUSE_ENABLE_DSELECT */
 700    fuse_lck_mtx_unlock(ftick->tk_data->ms_mtx);
 701}
 702
 703static int
 704fuse_body_audit(struct fuse_ticket *ftick, size_t blen)
 705{
 706    int err = 0;
 707    enum fuse_opcode opcode;
 708
 709    if (fdata_dead_get(ftick->tk_data)) {
 710        return ENOTCONN;
 711    }
 712
 713    opcode = fticket_opcode(ftick);
 714
 715    switch (opcode) {
 716    case FUSE_LOOKUP:
 717        err = (blen == sizeof(struct fuse_entry_out)) ? 0 : EINVAL;
 718        break;
 719
 720    case FUSE_FORGET:
 721        panic("MacFUSE: a handler has been intalled for FUSE_FORGET");
 722        break;
 723
 724    case FUSE_GETATTR:
 725        err = (blen == sizeof(struct fuse_attr_out)) ? 0 : EINVAL;
 726        break;
 727
 728    case FUSE_SETATTR:
 729        err = (blen == sizeof(struct fuse_attr_out)) ? 0 : EINVAL;
 730        break;
 731
 732    case FUSE_GETXTIMES:
 733        err = (blen == sizeof(struct fuse_getxtimes_out)) ? 0 : EINVAL;
 734        break;
 735
 736    case FUSE_READLINK:
 737        err = (PAGE_SIZE >= blen) ? 0 : EINVAL;
 738        break;
 739
 740    case FUSE_SYMLINK:
 741        err = (blen == sizeof(struct fuse_entry_out)) ? 0 : EINVAL;
 742        break;
 743
 744    case FUSE_MKNOD:
 745        err = (blen == sizeof(struct fuse_entry_out)) ? 0 : EINVAL;
 746        break;
 747
 748    case FUSE_MKDIR:
 749        err = (blen == sizeof(struct fuse_entry_out)) ? 0 : EINVAL;
 750        break;
 751
 752    case FUSE_UNLINK:
 753        err = (blen == 0) ? 0 : EINVAL;
 754        break;
 755
 756    case FUSE_RMDIR:
 757        err = (blen == 0) ? 0 : EINVAL;
 758        break;
 759
 760    case FUSE_RENAME:
 761        err = (blen == 0) ? 0 : EINVAL;
 762        break;
 763
 764    case FUSE_LINK:
 765        err = (blen == sizeof(struct fuse_entry_out)) ? 0 : EINVAL;
 766        break;
 767
 768    case FUSE_OPEN:
 769        err = (blen == sizeof(struct fuse_open_out)) ? 0 : EINVAL;
 770        break;
 771
 772    case FUSE_READ:
 773        err = (((struct fuse_read_in *)(
 774                (char *)ftick->tk_ms_fiov.base +
 775                        sizeof(struct fuse_in_header)
 776                  ))->size >= blen) ? 0 : EINVAL;
 777        break;
 778
 779    case FUSE_WRITE:
 780        err = (blen == sizeof(struct fuse_write_out)) ? 0 : EINVAL;
 781        break;
 782
 783    case FUSE_STATFS:
 784        if (fuse_libabi_geq(ftick->tk_data, 7, 4)) {
 785            err = (blen == sizeof(struct fuse_statfs_out)) ? 0 : EINVAL;
 786        } else {
 787            err = (blen == FUSE_COMPAT_STATFS_SIZE) ? 0 : EINVAL;
 788        }
 789        break;
 790
 791    case FUSE_RELEASE:
 792        err = (blen == 0) ? 0 : EINVAL;
 793        break;
 794
 795    case FUSE_FSYNC:
 796        err = (blen == 0) ? 0 : EINVAL;
 797        break;
 798
 799    case FUSE_SETXATTR:
 800        /* TBD */
 801        break;
 802
 803    case FUSE_GETXATTR:
 804        /* TBD */
 805        break;
 806
 807    case FUSE_LISTXATTR:
 808        /* TBD */
 809        break;
 810
 811    case FUSE_REMOVEXATTR:
 812        /* TBD */
 813        break;
 814
 815    case FUSE_FLUSH:
 816        err = (blen == 0) ? 0 : EINVAL;
 817        break;
 818
 819    case FUSE_INIT:
 820        if (blen == sizeof(struct fuse_init_out) || blen == 8) {
 821            err = 0;
 822        } else {
 823            err = EINVAL;
 824        }
 825        break;
 826
 827    case FUSE_OPENDIR:
 828        err = (blen == sizeof(struct fuse_open_out)) ? 0 : EINVAL;
 829        break;
 830
 831    case FUSE_READDIR:
 832        err = (((struct fuse_read_in *)(
 833                (char *)ftick->tk_ms_fiov.base +
 834                        sizeof(struct fuse_in_header)
 835                  ))->size >= blen) ? 0 : EINVAL;
 836        break;
 837
 838    case FUSE_RELEASEDIR:
 839        err = (blen == 0) ? 0 : EINVAL;
 840        break;
 841
 842    case FUSE_FSYNCDIR:
 843        err = (blen == 0) ? 0 : EINVAL;
 844        break;
 845
 846    case FUSE_GETLK:
 847        panic("MacFUSE: no response body format check for FUSE_GETLK");
 848        break;
 849
 850    case FUSE_SETLK:
 851        panic("MacFUSE: no response body format check for FUSE_SETLK");
 852        break;
 853
 854    case FUSE_SETLKW:
 855        panic("MacFUSE: no response body format check for FUSE_SETLKW");
 856        break;
 857
 858    case FUSE_ACCESS:
 859        err = (blen == 0) ? 0 : EINVAL;
 860        break;
 861
 862    case FUSE_CREATE:
 863        err = (blen == sizeof(struct fuse_entry_out) +
 864                           sizeof(struct fuse_open_out)) ? 0 : EINVAL;
 865        break;
 866
 867    case FUSE_INTERRUPT:
 868        /* TBD */
 869        break;
 870
 871    case FUSE_BMAP:
 872        /* TBD */
 873        break;
 874
 875    case FUSE_DESTROY:
 876        err = (blen == 0) ? 0 : EINVAL;
 877        break;
 878
 879    case FUSE_EXCHANGE:
 880        err = (blen == 0) ? 0 : EINVAL;
 881        break;
 882
 883    case FUSE_SETVOLNAME:
 884        err = (blen == 0) ? 0 : EINVAL;
 885        break;
 886
 887    default:
 888        IOLog("MacFUSE: opcodes out of sync (%d)\n", opcode);
 889        panic("MacFUSE: opcodes out of sync (%d)", opcode);
 890    }
 891
 892    return err;
 893}
 894
 895static void
 896fuse_setup_ihead(struct fuse_in_header *ihead,
 897                 struct fuse_ticket    *ftick,
 898                 uint64_t               nid,
 899                 enum fuse_opcode       op,
 900                 size_t                 blen,
 901                 vfs_context_t          context)
 902{
 903    ihead->len = (uint32_t)(sizeof(*ihead) + blen);
 904    ihead->unique = ftick->tk_unique;
 905    ihead->nodeid = nid;
 906    ihead->opcode = op;
 907
 908    if (context) {
 909        ihead->pid = vfs_context_pid(context);
 910        ihead->uid = vfs_context_ucred(context)->cr_uid;
 911        ihead->gid = vfs_context_ucred(context)->cr_gid;
 912    } else {
 913        /* XXX: could use more thought */
 914        ihead->pid = proc_pid((proc_t)current_proc());
 915        ihead->uid = kauth_cred_getuid(kauth_cred_get());
 916        ihead->gid = kauth_cred_getgid(kauth_cred_get());
 917    }
 918}
 919
 920static int
 921fuse_standard_handler(struct fuse_ticket *ftick, uio_t uio)
 922{
 923    int err = 0;
 924    int dropflag = 0;
 925
 926    err = fticket_pull(ftick, uio);
 927
 928    fuse_lck_mtx_lock(ftick->tk_aw_mtx);
 929
 930    if (fticket_answered(ftick)) {
 931        dropflag = 1;
 932    } else {
 933        fticket_set_answered(ftick);
 934        ftick->tk_aw_errno = err;
 935        fuse_wakeup(ftick);
 936    }
 937
 938    fuse_lck_mtx_unlock(ftick->tk_aw_mtx);
 939
 940    if (dropflag) {
 941        fuse_ticket_drop(ftick);
 942    }
 943
 944    return err;
 945}
 946
 947void
 948fdisp_make(struct fuse_dispatcher *fdip,
 949           enum fuse_opcode        op,
 950           mount_t                 mp,
 951           uint64_t                nid,
 952           vfs_context_t           context)
 953{
 954    struct fuse_data *data = fuse_get_mpdata(mp);
 955
 956    if (fdip->tick) {
 957        fticket_refresh(fdip->tick);
 958    } else {
 959        fdip->tick = fuse_ticket_fetch(data);
 960    }
 961
 962    if (fdip->tick == 0) {
 963        panic("MacFUSE: fuse_ticket_fetch() failed");
 964    }
 965
 966    FUSE_DIMALLOC(&fdip->tick->tk_ms_fiov, fdip->finh,
 967                  fdip->indata, fdip->iosize);
 968
 969    fuse_setup_ihead(fdip->finh, fdip->tick, nid, op, fdip->iosize, context);
 970}
 971
 972int
 973fdisp_make_canfail(struct fuse_dispatcher *fdip,
 974                   enum fuse_opcode        op,
 975                   mount_t                 mp,
 976                   uint64_t                nid,
 977                   vfs_context_t           context)
 978{
 979    int failed = 0;
 980    struct fuse_iov *fiov = NULL;
 981
 982    struct fuse_data *data = fuse_get_mpdata(mp);
 983
 984    if (fdip->tick) {
 985        fticket_refresh(fdip->tick);
 986    } else {
 987        fdip->tick = fuse_ticket_fetch(data);
 988    }
 989
 990    if (fdip->tick == 0) {
 991        panic("MacFUSE: fuse_ticket_fetch() failed");
 992    }
 993
 994    fiov = &fdip->tick->tk_ms_fiov;
 995
 996    failed = fiov_adjust_canfail(fiov,
 997                                 sizeof(struct fuse_in_header) + fdip->iosize);
 998
 999    if (failed) {
1000        fuse_ticket_kill(fdip->tick);
1001        return failed;
1002    }
1003
1004    fdip->finh = fiov->base;
1005    fdip->indata = (char *)(fiov->base) + sizeof(struct fuse_in_header);
1006
1007    fuse_setup_ihead(fdip->finh, fdip->tick, nid, op, fdip->iosize, context);
1008
1009    return 0;
1010}
1011
1012void
1013fdisp_make_vp(struct fuse_dispatcher *fdip,
1014              enum fuse_opcode        op,
1015              vnode_t                 vp,
1016              vfs_context_t           context)
1017{
1018    return fdisp_make(fdip, op, vnode_mount(vp), VTOI(vp), context);
1019}
1020
1021int
1022fdisp_make_vp_canfail(struct fuse_dispatcher *fdip,
1023                      enum fuse_opcode        op,
1024                      vnode_t                 vp,
1025                      vfs_context_t           context)
1026{
1027    return fdisp_make_canfail(fdip, op, vnode_mount(vp), VTOI(vp), context);
1028}
1029
1030int
1031fdisp_wait_answ(struct fuse_dispatcher *fdip)
1032{
1033    int err = 0;
1034
1035    fdip->answ_stat = 0;
1036    fuse_insert_callback(fdip->tick, fuse_standard_handler);
1037    fuse_insert_message(fdip->tick);
1038
1039    if ((err = fticket_wait_answer(fdip->tick))) { /* interrupted */
1040
1041#ifndef DONT_TRY_HARD_PREVENT_IO_IN_VAIN
1042        struct fuse_ticket *ftick;
1043        unsigned            age;
1044#endif
1045
1046        fuse_lck_mtx_lock(fdip->tick->tk_aw_mtx);
1047
1048        if (fticket_answered(fdip->tick)) {
1049            /* IPC: already answered */
1050            fuse_lck_mtx_unlock(fdip->tick->tk_aw_mtx);
1051            goto out;
1052        } else {
1053            /* IPC: explicitly setting to answered */
1054            age = fdip->tick->tk_age;
1055            fticket_set_answered(fdip->tick);
1056            fuse_lck_mtx_unlock(fdip->tick->tk_aw_mtx);
1057#ifndef DONT_TRY_HARD_PREVENT_IO_IN_VAIN
1058            fuse_lck_mtx_lock(fdip->tick->tk_data->aw_mtx);
1059            TAILQ_FOREACH(ftick, &fdip->tick->tk_data->aw_head, tk_aw_link) {
1060                if (ftick == fdip->tick) {
1061                    if (fdip->tick->tk_age == age) {
1062                        /* Succeeded preventing I/O in vain */
1063                        fdip->tick->tk_aw_handler = NULL;
1064                    }
1065                    break;
1066                }
1067            }
1068
1069            fuse_lck_mtx_unlock(fdip->tick->tk_data->aw_mtx);
1070#endif
1071            return err;
1072        }
1073    }
1074
1075    /* IPC was NOT interrupt */
1076
1077    if (fdip->tick->tk_aw_errno) {
1078
1079        /* Explicitly EIO-ing */
1080
1081        err = EIO;
1082        goto out;
1083    }
1084
1085    if ((err = fdip->tick->tk_aw_ohead.error)) {
1086
1087        /* Explicitly setting status */
1088
1089        fdip->answ_stat = err;
1090        goto out;
1091    }
1092
1093    fdip->answ = fticket_resp(fdip->tick)->base;
1094    fdip->iosize = fticket_resp(fdip->tick)->len;
1095
1096    return 0;
1097
1098out:
1099    fuse_ticket_drop(fdip->tick);
1100
1101    return err;
1102}