PageRenderTime 56ms CodeModel.GetById 15ms app.highlight 33ms RepoModel.GetById 1ms app.codeStats 0ms

/core/10.4/fusefs/fuse_nodehash.c

http://macfuse.googlecode.com/
C | 1050 lines | 558 code | 217 blank | 275 comment | 178 complexity | d15dd9a04ec610a4d91a98dd2738606b MD5 | raw file
   1/*
   2 * Copyright (C) 2006-2008 Google. All Rights Reserved.
   3 * Amit Singh <singh@>
   4 */
   5
   6/*
   7 * Portions Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
   8 *
   9 * This file contains Original Code and/or Modifications of Original Code as
  10 * defined in and that are subject to the Apple Public Source License Version
  11 * 2.0 (the 'License'). You may not use this file except in compliance with
  12 * the License. Please obtain a copy of the License at
  13 * http://www.opensource.apple.com/apsl/ and read it before using this file.
  14 *
  15 * The Original Code and all software distributed under the License are
  16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
  18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS
  19 * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see
  20 * the License for the specific language governing rights and limitations
  21 * under the License.
  22 */
  23
  24#include "fuse.h"
  25#include "fuse_nodehash.h"
  26#include <fuse_param.h>
  27
  28#include <sys/vnode.h>
  29#include <kern/assert.h>
  30
  31#if M_MACFUSE_ENABLE_UNSUPPORTED
  32#define LCK_MTX_ASSERT lck_mtx_assert
  33#else
  34#define LCK_MTX_ASSERT(gHashMutex, LCK_MTX_ASSERT_OWNED) do { } while (0)
  35#endif
  36
  37/*
  38 * The HNode structure represents an entry in the VFS plug-ins hash table.
  39 * See the comments in the header file for a detailed description of the
  40 * relationship between this data structure and the vnodes that are maintained
  41 * by VFS itself. 
  42 *
  43 * The HNode and the FSNode are intimately tied together. In fact, they're
  44 * allocated from the same memory block. When we allocate an HNode, we allocate
  45 * extra space (gFSNodeSize bytes) for the FSNode.
  46 * 
  47 * This data structure is effectively reference counted by the forkVNodesCount 
  48 * field. When the last vnode that references this HNode is reclaimed, the
  49 * HNode itself is reclaimed (along with the associated FSNode).
  50 */
  51
  52struct HNode {
  53
  54    /* [1] -> gMagic, that is, client supplied magic number */
  55    uint32_t magic;
  56
  57    /* [2] next pointer for hash chain */
  58    LIST_ENTRY(HNode) hashLink;
  59
  60    /* [1] device number on which file system object (fsobj) resides */
  61    fuse_device_t dev;
  62
  63    /* [1] inode number of fsobj resides */
  64    uint64_t ino;
  65
  66    /* [2] [3] */
  67    boolean_t attachOutstanding;
  68
  69    /* [2] true if someone is waiting for attachOutstanding to go false */
  70    boolean_t waiting;
  71
  72    /* [2] size of forkVNodes array, must be non-zero */
  73    size_t forkVNodesSize;
  74
  75    /* [2] # of non-NULL vnodes in array (+1 if attachOutstanding is true) */
  76    size_t forkVNodesCount;
  77
  78    /* [2] array of vnodes, indexed by forkIndex */
  79    vnode_t *forkVNodes;
  80
  81    union {
  82        /* [2] if forkVNodesSize == 1, the vnode is stored internally */
  83        vnode_t  internal;
  84
  85        /*
  86         * [2] if forkVNodesSize > 1, the vnodes are stored in a separately
  87         * allocated array
  88         */
  89        vnode_t *external;
  90
  91    } forkVNodesStorage;
  92};
  93typedef struct HNode HNode;
  94
  95/*
  96 * [HNode Notes]
  97 *
  98 * [1] This field is immutable. That is, it's set up as part of the process of 
  99 *     creating an HNode, and is not modified after that. Thus, it doesn't need 
 100 *     to be protected from concurrent access.
 101 *
 102 * [2] The gHashMutex lock protects this field in /all/ HNodes.
 103 *
 104 * [3] This is true if HNodeLookupCreatingIfNecessary has return success but
 105 *     with a NULL vnode.  In this case, we're expecting the client to call
 106 *     either HNodeAttachVNodeSucceeded or HNodeAttachVNodeFailed at some time
 107 *     in the future. While this is true, forkVNodesCount is incremented to
 108 *     prevent the HNode from going away.
 109 */
 110
 111/*
 112 * The following client globals are set by the client when it calls HNodeInit.
 113 * See the header comments for HNodeInit for more details.
 114 */
 115
 116static uint32_t     gMagic;
 117static lck_grp_t   *gLockGroup;
 118static size_t       gFSNodeSize;
 119static OSMallocTag  gOSMallocTag;
 120
 121/*
 122 *gHashMutex is a single mutex that protects all fields (except the immutable
 123 * ones) of all HNodes, the hash table itself (all elements of the gHashTable
 124 * array), and gHashNodeCount.
 125 */
 126
 127static lck_mtx_t *gHashMutex;
 128
 129/*
 130 * gHashNodeCount is a count of the number of HNodes in the hash table. 
 131 * This is used solely for debugging (if it's non-zero when HNodeTerm is
 132 * called, the debug version of the code will panic).
 133 */
 134
 135static size_t gHashNodeCount;
 136
 137/*
 138 * gHashTable is a pointer to an array of HNodeHashHead structures that
 139 * represent the heads of all the hash chains. This structure, and the
 140 * associated gHashTableMask, are all a consequence of my use of hashinit.
 141 */
 142
 143static LIST_HEAD(HNodeHashHead, HNode) *gHashTable;
 144typedef struct HNodeHashHead HNodeHashHead;
 145
 146static u_long gHashTableMask;
 147
 148/*
 149 * Given a device number and an inode number, return a pointer to the 
 150 * hash chain head.
 151 */
 152static HNodeHashHead *
 153HNodeGetFirstFromHashTable(fuse_device_t dev, uint64_t ino)
 154{
 155    return (HNodeHashHead *)&gHashTable[((uint64_t)(u_long)dev + ino) & gHashTableMask];
 156}
 157
 158extern errno_t
 159HNodeInit(lck_grp_t   *lockGroup, 
 160          lck_attr_t  *lockAttr, 
 161          OSMallocTag  mallocTag, 
 162          uint32_t     magic, 
 163          size_t       fsNodeSize)
 164{
 165    errno_t     err;
 166    
 167    assert(lockGroup != NULL);
 168    // lockAttr may be NULL
 169    assert(mallocTag != NULL);
 170    assert(fsNodeSize != 0);
 171
 172    gMagic       = magic;
 173    gFSNodeSize  = fsNodeSize;
 174    gOSMallocTag = mallocTag;
 175    gLockGroup   = lockGroup;
 176
 177    gHashMutex = lck_mtx_alloc_init(lockGroup, lockAttr);
 178    gHashTable = hashinit(desiredvnodes, M_TEMP, &gHashTableMask);
 179    err = 0;
 180    if ((gHashMutex == NULL) || (gHashTable == NULL)) {
 181        HNodeTerm(); /* Clean up any partial allocations */
 182        err = ENOMEM;
 183    }
 184    return err;
 185}
 186
 187extern void
 188HNodeTerm(void)
 189{
 190    /*
 191     * Free the hash table. Also, if there are any hash nodes left, we
 192     * shouldn't be terminating.
 193     */
 194    
 195    if (gHashTable != NULL) {
 196        assert(gHashNodeCount == 0);
 197        #if MACH_ASSERT
 198            {
 199                u_long i;
 200                
 201                for (i = 0; i < (gHashTableMask + 1); i++) {
 202                    assert(gHashTable[i].lh_first == NULL);
 203                }
 204            }
 205        #endif
 206        FREE(gHashTable, M_TEMP);
 207        gHashTable = NULL;
 208    }
 209    
 210    if (gHashMutex != NULL) {
 211        assert(gLockGroup != NULL);
 212        
 213        lck_mtx_free(gHashMutex, gLockGroup);
 214        gHashMutex = NULL;
 215    }
 216
 217    gLockGroup = NULL;
 218    gOSMallocTag = NULL;
 219    gFSNodeSize = 0;
 220    gMagic = 0;
 221}
 222
 223extern void *
 224FSNodeGenericFromHNode(HNodeRef hnode)
 225{
 226    assert(hnode != NULL);
 227    assert(hnode->magic == gMagic);
 228    return (void *) &hnode[1];
 229}
 230
 231extern HNodeRef
 232HNodeFromFSNodeGeneric(void *fsNode)
 233{
 234    assert(fsNode != NULL);
 235    return  &((HNodeRef) fsNode)[-1];
 236}
 237
 238extern HNodeRef
 239HNodeFromVNode(vnode_t vn)
 240{
 241    HNodeRef hnode;
 242    
 243    assert(vn != NULL);
 244    hnode = vnode_fsnode(vn);
 245    assert(hnode != NULL);
 246    assert(hnode->magic = gMagic);
 247    
 248    return hnode;
 249}
 250
 251extern void *
 252FSNodeGenericFromVNode(vnode_t vn)
 253{
 254    assert(vn != NULL);
 255    return FSNodeGenericFromHNode(HNodeFromVNode(vn));
 256}
 257
 258extern fuse_device_t 
 259HNodeGetDevice(HNodeRef hnode)
 260{
 261    assert(hnode != NULL);
 262    assert(hnode->magic == gMagic);
 263    return hnode->dev;
 264}
 265
 266extern uint64_t
 267HNodeGetInodeNumber(HNodeRef hnode)
 268{
 269    assert(hnode != NULL);
 270    assert(hnode->magic == gMagic);
 271    return hnode->ino;
 272}
 273
 274extern vnode_t
 275HNodeGetVNodeForForkAtIndex(HNodeRef hnode, __unused size_t forkIndex)
 276{
 277    vnode_t     vn;
 278    
 279    assert(hnode != NULL);
 280    assert(hnode->magic == gMagic);
 281    assert(forkIndex < hnode->forkVNodesSize);
 282    
 283    /*
 284     * Locking and unlocking gHashMutex /is/ needed, because another thread
 285     * might be swapping in an expanded forkVNodes array. Because of the
 286     * multi-threaded nature of the kernel, no amount of clever ordering of
 287     * this swap can prevent the possibility of us seeing inconsistent data.
 288     */
 289    
 290    lck_mtx_lock(gHashMutex);
 291
 292    vn = hnode->forkVNodes[forkIndex];
 293
 294    lck_mtx_unlock(gHashMutex);
 295    
 296    return vn;
 297}
 298
 299/*
 300 * The fact that the caller must hold some sort of reference on the vnode 
 301 * prevents the vnode from being reclaimed, which means that we're 
 302 * guaranteed to find the vnode in the fork array.
 303 */
 304extern size_t
 305HNodeGetForkIndexForVNode(vnode_t vn)
 306{
 307    HNodeRef    hnode;
 308    size_t      forkCount;
 309    size_t      forkIndex;
 310    
 311    assert(vn != NULL);
 312    
 313    /* HNodeFromVNode asserts the validity of its result */
 314    hnode = HNodeFromVNode(vn);
 315    
 316    /*
 317     * Locking and unlocking gHashMutex is needed, because another thread might 
 318     * be switching in an expanded forkVNodes array.
 319     */
 320    
 321    lck_mtx_lock(gHashMutex);
 322
 323    forkCount = hnode->forkVNodesSize;
 324    for (forkIndex = 0; forkIndex < forkCount; forkIndex++) {
 325        if (vn == hnode->forkVNodes[forkIndex]) {
 326            break;
 327        }
 328    }
 329    /* That is, that vn is in forkVNodes */
 330    assert(forkIndex != forkCount);
 331
 332    lck_mtx_unlock(gHashMutex);
 333    
 334    return forkIndex;
 335}
 336
 337extern void
 338HNodeExchangeFromFSNode(void *fsnode1, void *fsnode2)
 339{
 340    struct HNode tmpHNode;
 341
 342    lck_mtx_lock(gHashMutex);
 343
 344    HNodeRef hnode1 = HNodeFromFSNodeGeneric(fsnode1);
 345    HNodeRef hnode2 = HNodeFromFSNodeGeneric(fsnode2);
 346
 347    memcpy(&tmpHNode, hnode1, sizeof(struct HNode));
 348    memcpy(hnode1, hnode2, sizeof(struct HNode));
 349    memcpy(hnode2, &tmpHNode, sizeof(struct HNode));
 350
 351    LIST_REMOVE(hnode1, hashLink);
 352    LIST_REMOVE(hnode2, hashLink);
 353    LIST_INSERT_HEAD(HNodeGetFirstFromHashTable(hnode1->dev, hnode1->ino),
 354                     hnode1, hashLink);
 355    LIST_INSERT_HEAD(HNodeGetFirstFromHashTable(hnode2->dev, hnode2->ino),
 356                     hnode2, hashLink);
 357
 358    lck_mtx_unlock(gHashMutex);
 359}
 360
 361extern errno_t
 362HNodeLookupCreatingIfNecessary(fuse_device_t dev,
 363                               uint64_t      ino,
 364                               size_t        forkIndex,
 365                               HNodeRef *hnodePtr,
 366                               vnode_t  *vnPtr)
 367{
 368    errno_t    err;
 369    HNodeRef   thisNode;
 370    HNodeRef   newNode;
 371    vnode_t   *newForkBuffer;
 372    boolean_t  needsUnlock;
 373    vnode_t    resultVN;
 374    uint32_t   vid;
 375    
 376    assert( hnodePtr != NULL);
 377    assert(*hnodePtr == NULL);
 378    assert( vnPtr    != NULL);
 379    assert(*vnPtr    == NULL);
 380
 381    /*
 382     * If you forget to call HNodeInit, it's likely that the first call
 383     * you'll make is HNodeLookupCreatingIfNecessary (to set up your root
 384     * vnode), and this assert will fire (rather than you dying inside wit
 385     * a memory access exception inside lck_mtx_lock).
 386     */
 387    
 388    assert(gHashMutex != NULL);
 389
 390    newNode = NULL;
 391    newForkBuffer = NULL;
 392    needsUnlock = TRUE;
 393    resultVN = NULL;
 394    
 395    lck_mtx_lock(gHashMutex);
 396    
 397    do {
 398        LCK_MTX_ASSERT(gHashMutex, LCK_MTX_ASSERT_OWNED);
 399
 400        err = EAGAIN;
 401        
 402        /* First look it up in the hash table. */
 403        
 404        thisNode = LIST_FIRST(HNodeGetFirstFromHashTable(dev, ino));
 405        while (thisNode != NULL) {
 406            assert(thisNode->magic == gMagic);
 407            
 408            if ((thisNode->dev == dev) && (thisNode->ino == ino)) {
 409                break;
 410            }
 411
 412            thisNode = LIST_NEXT(thisNode, hashLink);
 413        }
 414
 415        /*
 416         * If we didn't find it, we're creating a new HNode. If we haven't
 417         * already allocated newNode, we must do so. This drops the mutex,
 418         * so the hash table might have been changed by someone else, so we
 419         * have to loop.
 420         */ 
 421
 422        /* If we do have a newNode at hand, use that. */
 423        
 424        if (thisNode == NULL) {
 425            if (newNode == NULL) {
 426                lck_mtx_unlock(gHashMutex);
 427
 428                /* Allocate a new node. */
 429                
 430                newNode = FUSE_OSMalloc(sizeof(*newNode) + gFSNodeSize,
 431                                        gOSMallocTag);
 432                if (newNode == NULL) {
 433                    err = ENOMEM;
 434                } else {
 435
 436                    /* Fill it in. */
 437                    
 438                    memset(newNode, 0, sizeof(*newNode) + gFSNodeSize);
 439                    
 440                    newNode->magic = gMagic;
 441                    newNode->dev   = dev;
 442                    newNode->ino   = ino;
 443                    
 444                    /*
 445                     * If we're dealing with the first fork, use the internal
 446                     * buffer. Otherwise allocate an external buffer.
 447                     */
 448                    
 449                    if (forkIndex == 0) {
 450                        newNode->forkVNodesSize = 1;
 451                        newNode->forkVNodes = &newNode->forkVNodesStorage.internal;
 452                        newNode->forkVNodesStorage.internal = NULL;
 453                    } else {
 454                        newNode->forkVNodesSize = forkIndex + 1;
 455                        newNode->forkVNodesStorage.external = FUSE_OSMalloc(sizeof(*newNode->forkVNodesStorage.external) * (forkIndex + 1), gOSMallocTag);
 456                        newNode->forkVNodes = newNode->forkVNodesStorage.external;
 457                        if (newNode->forkVNodesStorage.external == NULL) {
 458                            /*
 459                             * If this allocation fails, we don't have to clean
 460                             * up newNode, because we'll fall out of the loop
 461                             * and newNode will get cleaned  up at the end.
 462                             */
 463                            err = ENOMEM;
 464                        } else {
 465                            memset(newNode->forkVNodesStorage.external, 0, sizeof(*newNode->forkVNodesStorage.external) * (forkIndex + 1));
 466                        }
 467                    }
 468                }
 469
 470                lck_mtx_lock(gHashMutex);
 471            } else {
 472                LIST_INSERT_HEAD(HNodeGetFirstFromHashTable(dev, ino),
 473                                 newNode, hashLink);
 474                gHashNodeCount += 1;
 475
 476                /*
 477                 * Set thisNode to the node that we inserted, and clear
 478                 * newNode so it doesn't get freed.
 479                 */
 480                
 481                thisNode = newNode;
 482                newNode = NULL;
 483                
 484                /*
 485                 * IMPORTANT:
 486                 * There's a /really/ subtle point here. Once we've inserted
 487                 * the new node into the hash table, it can be discovered by
 488                 * other threads. This would be bad, because it's only
 489                 * partially constructed at this point. We prevent this
 490                 * problem by not dropping gHashMutex from this point to
 491                 * the point that we're done. This only works because we
 492                 * allocate the new node with a fork buffer that's adequate
 493                 * to meet our needs.
 494                 */
 495            }
 496        }
 497
 498        /*
 499         * If we found a hash node (including the case where we've used one
 500         * that we previously allocated), check its status.
 501         */
 502        
 503        if (thisNode != NULL) {
 504            if (thisNode->attachOutstanding) {
 505                /*
 506                 * If there are outstanding attaches, wait for them to
 507                 * complete. This means that there can be only one outstanding
 508                 * attach at a time, which is important because we don't want
 509                 * two threads trying to fill in the same fork's vnode entry.
 510                 * 
 511                 * In theory we might keep an array of outstanding attach
 512                 * flags, one for each fork, but that's difficult and probably
 513                 * overkill.
 514                 */
 515
 516                thisNode->waiting = TRUE;
 517                
 518                (void)fuse_msleep(thisNode, gHashMutex, PINOD,
 519                                  "HNodeLookupCreatingIfNecessary", NULL);
 520                
 521                /*
 522                 * msleep drops and reacquires the mutex; the hash table may
 523                 * have changed, so we loop.
 524                 */
 525            } else if (forkIndex >= thisNode->forkVNodesSize) {
 526                /*
 527                 * If the fork vnode array (a buffer described by
 528                 * thisNode->forkVNodes and thisNode->forkVNodesSize) is too
 529                 * small, install a new buffer, big enough to hold the vnode
 530                 * fork forkIndex'th fork.
 531                 */
 532                
 533                if (newForkBuffer == NULL) {
 534                    /*
 535                     * If we don't already have a new fork buffer, allocate
 536                     * one. Because this drops the mutex, we have to loop and
 537                     * start again from scratch.
 538                     */
 539
 540                    lck_mtx_unlock(gHashMutex);
 541
 542                    newForkBuffer = FUSE_OSMalloc(sizeof(*newForkBuffer) * (forkIndex + 1), gOSMallocTag);
 543                    if (newForkBuffer == NULL) {
 544                        err = ENOMEM;
 545                    } else {
 546                        memset(newForkBuffer, 0, sizeof(*newForkBuffer) * (forkIndex + 1));
 547                    }
 548                    
 549                    lck_mtx_lock(gHashMutex);
 550                } else {
 551
 552                    /*
 553                     * Insert the newForkBuffer into theNode. This only works
 554                     * because readers of the thisNode->forkVNodes array
 555                     * (notably this routine and HNodeGetVNodeForForkAtIndex)
 556                     * always take gHashMutex. If that wasn't the case, you
 557                     * could get into some subtle race conditions as thread A
 558                     * brings a copy of thisNode->forkVNodes into a register 
 559                     * and then gets blocked, then thread B runs and expands
 560                     * the array and frees the buffer that is being pointed
 561                     * to be thread A's register.
 562                     */
 563                    
 564                    vnode_t *oldForkBuffer;
 565                    size_t   oldForkBufferSize;
 566                    
 567                    oldForkBufferSize = 0; /* Quieten a false warning */
 568                    
 569                    /*
 570                     * We only free the old fork buffer if it was external,
 571                     * rather than the single vnode buffer embedded in the HNode
 572                     */
 573                    
 574                    oldForkBuffer = NULL;
 575                    if (thisNode->forkVNodesSize > 1) {
 576                        oldForkBuffer = thisNode->forkVNodesStorage.external;
 577                        oldForkBufferSize = thisNode->forkVNodesSize;
 578                    }
 579                    memcpy(newForkBuffer, thisNode->forkVNodes,
 580                      sizeof(*thisNode->forkVNodes) * thisNode->forkVNodesSize);
 581                    thisNode->forkVNodes = newForkBuffer;
 582                    thisNode->forkVNodesSize = (forkIndex + 1);
 583                    thisNode->forkVNodesStorage.external = newForkBuffer;
 584                    
 585                    /* So we don't free it at the end */
 586                    newForkBuffer = NULL;
 587
 588                    /*
 589                     * Drop the mutex around the free, and then start
 590                     * again from scratch.
 591                     */
 592
 593                    lck_mtx_unlock(gHashMutex);
 594                    
 595                    if (oldForkBuffer != NULL) {
 596                        FUSE_OSFree(oldForkBuffer,
 597                                    sizeof(*oldForkBuffer) * oldForkBufferSize,
 598                                    gOSMallocTag);
 599                    }
 600                    
 601                    lck_mtx_lock(gHashMutex);                       
 602                }
 603            } else if (thisNode->forkVNodes[forkIndex] == NULL) {
 604                /*
 605                 * If there's no existing vnode associated with this fork of
 606                 * the HNode, we're done. The caller is responsible for
 607                 * attaching a vnode for  this fork. Setting attachOutstanding
 608                 * will block any other threads from using the HNode until the
 609                 * caller is done attaching. Also, we artificially increment
 610                 * the reference count to prevent the HNode from being freed
 611                 * until the caller has finished with it.
 612                 */
 613                
 614                thisNode->attachOutstanding = TRUE;
 615                thisNode->forkVNodesCount += 1;
 616                
 617                /* Results for the caller. */
 618                
 619                assert(thisNode != NULL);
 620                assert(resultVN == NULL);
 621                
 622                err = 0;
 623            } else {
 624
 625                vnode_t candidateVN;
 626                
 627                /*
 628                 * If there is an existing vnode, get a reference on it and
 629                 * return that to the caller. This vnode reference prevents
 630                 * the vnode from being reclaimed, which prevents the HNode
 631                 * from being freed.
 632                 */
 633                
 634                candidateVN = thisNode->forkVNodes[forkIndex];
 635                assert(candidateVN != NULL);
 636                
 637                /*
 638                 * Check that our vnode hasn't been recycled. If this succeeds,
 639                 * it acquires a reference on the vnode, which is the one we
 640                 * return to our caller. We do this with gHashMutex unlocked
 641                 * to avoid any deadlock concerns.
 642                 */
 643                
 644                vid = vnode_vid(candidateVN);
 645                
 646                lck_mtx_unlock(gHashMutex);
 647                
 648                err = vnode_getwithvid(candidateVN, vid);
 649
 650                if (err == 0) {
 651                    /* All ok; return the HNode/vnode to the caller. */
 652                    assert(thisNode != NULL);
 653                    assert(resultVN == NULL);
 654                    resultVN = candidateVN;
 655                    needsUnlock = FALSE;
 656                } else {
 657                    /* We're going to loop and retry, so relock the mutex. */
 658                    
 659                    lck_mtx_lock(gHashMutex);
 660
 661                    err = EAGAIN;
 662                }
 663            }
 664        }
 665    } while (err == EAGAIN);
 666
 667    /* On success, pass results back to the caller. */
 668    
 669    if (err == 0) {
 670        *hnodePtr = thisNode;
 671        *vnPtr    = resultVN;
 672    }
 673    
 674    /* Clean up. */
 675    
 676    if (needsUnlock) {
 677        lck_mtx_unlock(gHashMutex);
 678    }
 679
 680    /* Free newForkBuffer if we allocated it but didn't use it. */
 681    
 682    if (newForkBuffer != NULL) {
 683        FUSE_OSFree(newForkBuffer, sizeof(*newForkBuffer) * (forkIndex + 1),
 684                    gOSMallocTag);
 685    }
 686
 687    /* Free newNode if we allocated it but didn't put it into the table. */
 688    
 689    if (newNode != NULL) {
 690        FUSE_OSFree(newNode, sizeof(*newNode) + gFSNodeSize, gOSMallocTag); 
 691    }
 692    
 693    assert( (err == 0) == (*hnodePtr != NULL) );
 694    
 695    return err;
 696}
 697
 698/*
 699 * An attach operate has completed. If there is someone waiting for 
 700 * the HNode, wake them up.
 701 */
 702static void
 703HNodeAttachComplete(HNodeRef hnode)
 704{
 705    assert(hnode != NULL);
 706    assert(hnode->magic == gMagic);
 707    
 708    LCK_MTX_ASSERT(gHashMutex, LCK_MTX_ASSERT_OWNED);
 709
 710    assert(hnode->attachOutstanding);
 711    hnode->attachOutstanding = FALSE;
 712
 713    if (hnode->waiting) {
 714        fuse_wakeup(hnode);
 715        hnode->waiting = FALSE;
 716    }
 717}
 718
 719/*
 720 * Decrement the number of fork vnodes for this HNode. If it hits zero, 
 721 * the HNode is gone and we remove it from the hash table and return 
 722 * true indicating to our caller that they need to clean it up.
 723 */
 724static boolean_t
 725HNodeForkVNodeDecrement(HNodeRef hnode)
 726{
 727    boolean_t scrubIt;
 728
 729    assert(hnode != NULL);
 730    assert(hnode->magic == gMagic);
 731
 732    LCK_MTX_ASSERT(gHashMutex, LCK_MTX_ASSERT_OWNED);
 733
 734    scrubIt = FALSE;
 735
 736    hnode->forkVNodesCount -= 1;
 737    assert(hnode->forkVNodesCount >= 0);
 738    if (hnode->forkVNodesCount == 0) {
 739        LIST_REMOVE(hnode, hashLink);
 740
 741        /* We test for this case before decrementing it because it's unsigned */
 742        assert(gHashNodeCount > 0);
 743
 744        gHashNodeCount -= 1;
 745
 746        scrubIt = TRUE;
 747    }
 748
 749    return scrubIt;
 750}
 751
 752extern void
 753HNodeAttachVNodeSucceeded(HNodeRef hnode, size_t forkIndex, vnode_t vn)
 754{
 755    errno_t junk;
 756    
 757    lck_mtx_lock(gHashMutex);
 758
 759    assert(hnode != NULL);
 760    assert(hnode->magic == gMagic);
 761    assert(forkIndex < hnode->forkVNodesSize);
 762    assert(vn != NULL);
 763    assert(vnode_fsnode(vn) == hnode);
 764    
 765    /*
 766     * If someone is waiting for the HNode, wake them up. They won't actually 
 767     * start running until we drop gHashMutex.
 768     */
 769    
 770    HNodeAttachComplete(hnode);
 771
 772    /* Record the vnode's association with this HNode. */
 773    
 774    assert(hnode->forkVNodes[forkIndex] == NULL);
 775    hnode->forkVNodes[forkIndex] = vn;
 776    junk = vnode_addfsref(vn);
 777    assert(junk == 0);
 778    
 779    lck_mtx_unlock(gHashMutex);
 780}
 781
 782extern boolean_t
 783HNodeAttachVNodeFailed(HNodeRef hnode, __unused size_t forkIndex)
 784{
 785    boolean_t   scrubIt;
 786    
 787    lck_mtx_lock(gHashMutex);
 788
 789    assert(hnode != NULL);
 790    assert(hnode->magic == gMagic);
 791    assert(forkIndex < hnode->forkVNodesSize);
 792    
 793    /*
 794     * If someone is waiting for the HNode, wake them up. They won't actually 
 795     * start running until we drop gHashMutex.
 796     */
 797    
 798    HNodeAttachComplete(hnode);
 799
 800    /*
 801     * Decrement the number of fork vnodes referencing the HNode, freeing 
 802     * the HNode if it hits zero.
 803     */
 804
 805    scrubIt = HNodeForkVNodeDecrement(hnode);
 806
 807    lck_mtx_unlock(gHashMutex);
 808
 809    return scrubIt;
 810}
 811
 812extern boolean_t
 813HNodeDetachVNode(HNodeRef hnode, vnode_t vn)
 814{
 815    errno_t   junk;
 816    size_t    forkIndex;
 817    boolean_t scrubIt;
 818    
 819    lck_mtx_lock(gHashMutex);
 820
 821    assert(hnode != NULL);
 822    assert(hnode->magic == gMagic);
 823    assert(vn != NULL);
 824
 825    /* Find the fork index for vn. */
 826    
 827    for (forkIndex = 0; forkIndex < hnode->forkVNodesSize; forkIndex++) {
 828        if (hnode->forkVNodes[forkIndex] == vn) {
 829            break;
 830        }
 831    }
 832
 833    /* If this trips, vn isn't in the forkVNodes array */
 834    assert(forkIndex < hnode->forkVNodesSize);
 835
 836    /* Disassociate the vnode with this fork of the HNode. */
 837    
 838    hnode->forkVNodes[forkIndex] = NULL;
 839    junk = vnode_removefsref(vn);
 840    assert(junk == 0);
 841    vnode_clearfsnode(vn);
 842    
 843    /*
 844     * Decrement the number of fork vnodes referencing the HNode, 
 845     * freeing the HNode if it hits zero.
 846     */
 847
 848    scrubIt = HNodeForkVNodeDecrement(hnode);
 849
 850    lck_mtx_unlock(gHashMutex);
 851
 852    return scrubIt;
 853}
 854
 855extern void
 856HNodeScrubDone(HNodeRef hnode)
 857{
 858    assert(hnode != NULL);
 859    assert(hnode->magic == gMagic);
 860    
 861    if (hnode->forkVNodesSize > 1) {
 862        FUSE_OSFree(hnode->forkVNodesStorage.external,
 863            sizeof(*hnode->forkVNodesStorage.external) * hnode->forkVNodesSize,
 864            gOSMallocTag);
 865    }
 866
 867    /*
 868     * If anyone is waiting on this HNode, that would be bad.
 869     * It would be easy to fix this (we could wake them up at this 
 870     * point) but, as I don't think it can actually happen, I'd rather 
 871     * have this assert tell me whether the code is necessary than 
 872     * just add it blindly.
 873     */
 874
 875    assert(!hnode->waiting );
 876    FUSE_OSFree(hnode, sizeof(*hnode) + gFSNodeSize, gOSMallocTag);
 877}
 878
 879/*
 880 * There's a significant thread safety bug here. Specifically, the forkVNodes 
 881 * array is not included as part of the snapshot. If we switch the HNode's 
 882 * forkVNodes array between the point where we take the snapshot at the point 
 883 * where we print it, bad things will happen.
 884 */
 885extern void
 886HNodePrintState(void)
 887{
 888    int     err;
 889    size_t  nodeCount;
 890    size_t  nodeIndex;
 891    HNode  *nodes;
 892    u_long  hashBucketIndex;
 893    
 894    /* Take a snapshot. */
 895    
 896    do {
 897        err = 0;
 898        
 899        nodeCount = gHashNodeCount;
 900        
 901        nodes = FUSE_OSMalloc(sizeof(*nodes) * nodeCount, gOSMallocTag);
 902        if (nodes == NULL) {
 903            err = ENOMEM;
 904        }
 905        
 906        if (err == 0) {
 907
 908            lck_mtx_lock(gHashMutex);
 909            
 910            if (gHashNodeCount > nodeCount) {
 911                /* Whoops, it changed size, let's try again. */
 912                FUSE_OSFree(nodes, sizeof(*nodes) * nodeCount, gOSMallocTag);
 913                err = EAGAIN;
 914            } else {
 915
 916                nodeIndex = 0;
 917
 918                for (hashBucketIndex = 0; hashBucketIndex < gHashTableMask;
 919                     hashBucketIndex++) {
 920
 921                    HNode *thisNode;
 922                    
 923                    LIST_FOREACH(thisNode, &gHashTable[hashBucketIndex],
 924                                 hashLink) {
 925                        assert(nodeIndex < nodeCount);
 926                        nodes[nodeIndex] = *thisNode;
 927                        nodeIndex += 1;
 928                    }
 929                }
 930                assert(nodeIndex == nodeCount);
 931            }
 932            
 933            lck_mtx_unlock(gHashMutex);
 934        }
 935    } while (err == EAGAIN);
 936
 937    assert( (err == 0) == (nodes != NULL) );
 938    
 939    /* Print the snapshot. */
 940    
 941    if (err == 0) {
 942        printf("HNodePrintState\n");
 943        for (nodeIndex = 0; nodeIndex < nodeCount; nodeIndex++) {
 944            HNode  *thisNode;
 945            size_t  forkIndex;
 946            
 947            thisNode = &nodes[nodeIndex];
 948            
 949            printf("{%p.%lld %c%c ", thisNode->dev, thisNode->ino,
 950                   " A"[thisNode->attachOutstanding], " W"[thisNode->waiting]);
 951
 952            for (forkIndex = 0; forkIndex < thisNode->forkVNodesSize;
 953                 forkIndex++) {
 954                if (forkIndex > 0) {
 955                    printf(" ");
 956                }
 957                printf("%p", thisNode->forkVNodes[forkIndex]);
 958            }
 959            printf("}");
 960
 961            if (nodes[nodeIndex].hashLink.le_next == NULL) {
 962                printf("\n");
 963            }
 964        }
 965    }
 966
 967    if (nodes != NULL) {
 968        FUSE_OSFree(nodes, sizeof(*nodes) * nodeCount, gOSMallocTag);
 969    }
 970}
 971
 972/*
 973 * Device Kill
 974 */
 975
 976extern errno_t
 977HNodeLookupRealQuickIfExists(fuse_device_t dev,
 978                             uint64_t      ino,
 979                             size_t        forkIndex,
 980                             HNodeRef     *hnodePtr,
 981                             vnode_t      *vnPtr)
 982{
 983    errno_t   err = EAGAIN;
 984    HNodeRef  thisNode;
 985    boolean_t needsUnlock;
 986    vnode_t   resultVN;
 987    uint32_t  vid;
 988    
 989    assert(hnodePtr != NULL);
 990    assert(*hnodePtr == NULL);
 991    assert(vnPtr != NULL);
 992    assert(*vnPtr == NULL);
 993    assert(gHashMutex != NULL);
 994
 995    needsUnlock = TRUE;
 996    resultVN = NULL;
 997    
 998    lck_mtx_lock(gHashMutex);
 999    
1000    LCK_MTX_ASSERT(gHashMutex, LCK_MTX_ASSERT_OWNED);
1001
1002    thisNode = LIST_FIRST(HNodeGetFirstFromHashTable(dev, ino));
1003
1004    while (thisNode != NULL) {
1005        assert(thisNode->magic == gMagic);
1006        if ((thisNode->dev == dev) && (thisNode->ino == ino)) {
1007            break;
1008        }
1009        thisNode = LIST_NEXT(thisNode, hashLink);
1010    }
1011        
1012    if (thisNode == NULL) {
1013        err = ENOENT;
1014    } else {
1015        if (thisNode->attachOutstanding) {
1016            err = EAGAIN;
1017        } else if (forkIndex >= thisNode->forkVNodesSize) {
1018            err = ENOENT;
1019        } else if (thisNode->forkVNodes[forkIndex] == NULL) {
1020            err = ENOENT;
1021        } else {
1022            vnode_t candidateVN = thisNode->forkVNodes[forkIndex];
1023            assert(candidateVN != NULL);
1024            vid = vnode_vid(candidateVN);
1025            lck_mtx_unlock(gHashMutex);
1026            err = vnode_getwithvid(candidateVN, vid);
1027            needsUnlock = FALSE;
1028            if (err == 0) {
1029                assert(thisNode != NULL);
1030                assert(resultVN == NULL);
1031                resultVN = candidateVN;
1032            } else {
1033                err = EAGAIN;
1034            }
1035        }
1036    }
1037
1038    if (err == 0) {
1039        *hnodePtr = thisNode;
1040        *vnPtr    = resultVN;
1041    }
1042    
1043    if (needsUnlock) {
1044        lck_mtx_unlock(gHashMutex);
1045    }
1046
1047    assert((err == 0) == (*hnodePtr != NULL));
1048    
1049    return err;
1050}