]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/hfs/hfscommon/BTree/BTree.c
xnu-517.tar.gz
[apple/xnu.git] / bsd / hfs / hfscommon / BTree / BTree.c
index 48749f50d9870f05a4047f21fa0527055f28ed08..d61c73936cb373c2c87ab40b9b943159074a493d 100644 (file)
@@ -1,21 +1,24 @@
 /*
- * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2000-2003 Apple Computer, Inc. All rights reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
  * 
- * The contents of this file constitute Original Code as defined in and
- * are subject to the Apple Public Source License Version 1.1 (the
- * "License").  You may not use this file except in compliance with the
- * License.  Please obtain a copy of the License at
- * http://www.apple.com/publicsource and read it before using this file.
+ * Copyright (c) 1999-2003 Apple Computer, Inc.  All Rights Reserved.
  * 
- * This Original Code and all software distributed under the License are
- * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
- * License for the specific language governing rights and limitations
- * under the License.
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
  * 
  * @APPLE_LICENSE_HEADER_END@
  */
 
 #include "../headers/BTreesPrivate.h"
 
-#include "../headers/HFSInstrumentation.h"
-
 /* 
  * The amount that the BTree header leaf count can be wrong before we assume
  * it is in an infinite loop.
  */
 #define        kNumLeafRecSlack 10             
 
+/* BTree accessor routines */
+extern OSStatus GetBTreeBlock(FileReference vp, UInt32 blockNum, GetBlockOptions options, BlockDescriptor *block);
+extern OSStatus SetBTreeBlockSize(FileReference vp, ByteCount blockSize, ItemCount minBlockCount);
+extern OSStatus ExtendBTreeFile(FileReference vp, FSSize minEOF, FSSize maxEOF);
+extern OSStatus ReleaseBTreeBlock(FileReference vp, BlockDescPtr blockPtr, ReleaseBlockOptions options);
+
 //////////////////////////////////// Globals ////////////////////////////////////
 
 
@@ -170,9 +177,6 @@ Function:   Create BTree control block for a file, if necessary. Validates the
 
 Input:         filePtr                         - pointer to file to open as a B-tree
                        keyCompareProc          - pointer to client's KeyCompare function
-                       getBlockProc            - pointer to client's GetBlock function
-                       releaseBlockProc        - pointer to client's ReleaseBlock function
-                       setEndOfForkProc        - pointer to client's SetEOF function
 
 Result:                noErr                           - success
                        paramErr                        - required ptr was nil
@@ -181,37 +185,31 @@ Result:           noErr                           - success
                        != noErr                        - failure
 -------------------------------------------------------------------------------*/
 
-OSStatus       BTOpenPath                      (FCB                                    *filePtr,
-                                                                KeyCompareProcPtr               keyCompareProc,
-                                                                GetBlockProcPtr                 getBlockProc,
-                                                                ReleaseBlockProcPtr     releaseBlockProc,
-                                                                SetEndOfForkProcPtr     setEndOfForkProc,
-                                                                SetBlockSizeProcPtr     setBlockSizeProc )
+OSStatus BTOpenPath(FCB *filePtr, KeyCompareProcPtr keyCompareProc)
 {
        OSStatus                                err;
        BTreeControlBlockPtr    btreePtr;
        BTHeaderRec                             *header;
        NodeRec                                 nodeRec;
 
-       LogStartTime(kTraceOpenBTree);
-
        ////////////////////// Preliminary Error Checking ///////////////////////////
 
-       if ( filePtr == nil                             ||
-                getBlockProc == nil            ||
-                releaseBlockProc == nil        ||
-                setEndOfForkProc == nil        ||
-                setBlockSizeProc == nil )
+       if ( filePtr == nil )
        {
                return  paramErr;
        }
 
-       if ( filePtr->fcbBTCBPtr != nil )                       // already has a BTreeCB
+       /*
+        * Subsequent opens allow key compare proc to be changed.
+        */
+       if ( filePtr->fcbBTCBPtr != nil && keyCompareProc != nil) {
+               btreePtr = (BTreeControlBlockPtr) filePtr->fcbBTCBPtr;
+               btreePtr->keyCompareProc = keyCompareProc;
                return noErr;
+       }
 
-                                                                                               // is file large enough to contain header node?
        if ( filePtr->fcbEOF < kMinNodeSize )
-               return fsBTInvalidFileErr;                                                      //\80\80 or E_BadHeader?
+               return fsBTInvalidFileErr;
 
 
        //////////////////////// Allocate Control Block /////////////////////////////
@@ -223,27 +221,34 @@ OSStatus  BTOpenPath                      (FCB                                    *filePtr,
                return  memFullErr;
        }
 
-       btreePtr->getBlockProc          = getBlockProc;
-       btreePtr->releaseBlockProc      = releaseBlockProc;
-       btreePtr->setEndOfForkProc      = setEndOfForkProc;
+       btreePtr->getBlockProc          = GetBTreeBlock;
+       btreePtr->releaseBlockProc      = ReleaseBTreeBlock;
+       btreePtr->setEndOfForkProc      = ExtendBTreeFile;
        btreePtr->keyCompareProc        = keyCompareProc;
 
        /////////////////////////// Read Header Node ////////////////////////////////
 
        nodeRec.buffer                          = nil;                          // so we can call ReleaseNode
-       nodeRec.blockSize                       = kMinNodeSize;
        btreePtr->fileRefNum            = GetFileRefNumFromFCB(filePtr);
        filePtr->fcbBTCBPtr                     = (Ptr) btreePtr;       // attach btree cb to file
 
+       /* The minimum node size is the physical block size */
+       nodeRec.blockSize = VTOHFS(btreePtr->fileRefNum)->hfs_phys_block_size;
+
+       /* Start with the allocation block size for regular files. */
+       if (FTOC(filePtr)->c_fileid >= kHFSFirstUserCatalogNodeID)
+       {
+               nodeRec.blockSize = FCBTOVCB(filePtr)->blockSize;
+       }
        REQUIRE_FILE_LOCK(btreePtr->fileRefNum, false);
 
        // it is now safe to call M_ExitOnError (err)
 
-       err = setBlockSizeProc (btreePtr->fileRefNum, kMinNodeSize, 1);
+       err = SetBTreeBlockSize (btreePtr->fileRefNum, nodeRec.blockSize, 1);
        M_ExitOnError (err);
 
 
-       err = getBlockProc (btreePtr->fileRefNum,
+       err = GetBTreeBlock(btreePtr->fileRefNum,
                                                kHeaderNodeNum,
                                                kGetBlock,
                                                &nodeRec );
@@ -254,7 +259,7 @@ OSStatus    BTOpenPath                      (FCB                                    *filePtr,
                Panic("\pBTOpen: getNodeProc returned error getting header node.");
                goto ErrorExit;
        }
-
+       ++btreePtr->numGetNodes;
        header = (BTHeaderRec*) ((u_long)nodeRec.buffer + sizeof(BTNodeDescriptor));
 
 
@@ -277,9 +282,12 @@ OSStatus   BTOpenPath                      (FCB                                    *filePtr,
        btreePtr->maxKeyLength          = header->maxKeyLength;
        btreePtr->totalNodes            = header->totalNodes;
        btreePtr->freeNodes                     = header->freeNodes;
-       // ignore                                         header->clumpSize;    //\80\80 rename this field?
+       if (FTOC(filePtr)->c_fileid >= kHFSFirstUserCatalogNodeID)
+               filePtr->ff_clumpsize = header->clumpSize;
        btreePtr->btreeType                     = header->btreeType;
 
+       btreePtr->keyCompareType = header->keyCompareType;
+
        btreePtr->attributes            = header->attributes;
 
        if ( btreePtr->maxKeyLength > 40 )
@@ -291,15 +299,27 @@ OSStatus  BTOpenPath                      (FCB                                    *filePtr,
        btreePtr->flags                         = 0;
        btreePtr->writeCount            = 1;
 
-       btreePtr->numGetNodes           = 1;            // for earlier call to getNodeProc
-
        /////////////////////////// Check Header Node ///////////////////////////////
 
-       //\80\80 set kBadClose attribute bit, and UpdateNode
+       // set kBadClose attribute bit, and UpdateNode
 
-       // if nodeSize is 512 then we don't need to release, just CheckNode
+       /* b-tree node size must be at least as big as the physical block size */
+       if (btreePtr->nodeSize < nodeRec.blockSize)
+       {
+               /*
+                * If this tree has any records or the media is writeable then
+                * we cannot mount using the current physical block size.
+                */
+               if (btreePtr->leafRecords > 0 ||
+                   VTOHFS(btreePtr->fileRefNum)->hfs_flags & HFS_WRITEABLE_MEDIA)              
+               {
+                       err = fsBTBadNodeSize;
+                       goto ErrorExit;
+               }
+       }
 
-       if ( btreePtr->nodeSize == kMinNodeSize )
+       // if nodeSize Matches then we don't need to release, just CheckNode
+       if ( btreePtr->nodeSize == nodeRec.blockSize )
        {
                err = CheckNode (btreePtr, nodeRec.buffer);
                if (err)
@@ -308,14 +328,15 @@ OSStatus  BTOpenPath                      (FCB                                    *filePtr,
        }
        else
        {
-               err = setBlockSizeProc (btreePtr->fileRefNum, btreePtr->nodeSize, 32);  //\80\80 we should try and get this down to 8
+               err = SetBTreeBlockSize (btreePtr->fileRefNum, btreePtr->nodeSize, 32);
                M_ExitOnError (err);
 
                /*
                 * Need to use kTrashBlock option to force the
                 * buffer cache to read the entire node
                 */
-               err = releaseBlockProc(btreePtr->fileRefNum, &nodeRec, kTrashBlock);
+               err = ReleaseBTreeBlock(btreePtr->fileRefNum, &nodeRec, kTrashBlock);
+               ++btreePtr->numReleaseNodes;
                M_ExitOnError (err);
 
                err = GetNode (btreePtr, kHeaderNodeNum, &nodeRec );            // calls CheckNode...
@@ -331,16 +352,21 @@ OSStatus  BTOpenPath                      (FCB                                    *filePtr,
        /*
         * Under Mac OS, b-tree nodes can be non-contiguous on disk when the
         * allocation block size is smaller than the b-tree node size.
+        *
+        * If journaling is turned on for this volume we can't deal with this
+        * situation and so we bail out.  If journaling isn't on it's ok as
+        * hfs_strategy_fragmented() deals with it.  Journaling can't support
+        * this because it assumes that if you give it a block that it's
+        * contiguous on disk.
         */
-       if ( !NodesAreContiguous(FCBTOVCB(filePtr), filePtr, btreePtr->nodeSize) )
+       if ( FCBTOHFS(filePtr)->jnl && !NodesAreContiguous(FCBTOVCB(filePtr), filePtr, btreePtr->nodeSize) ) {
                return fsBTInvalidNodeErr;
+       }
 
        //////////////////////////////// Success ////////////////////////////////////
 
        //\80\80 align LEOF to multiple of node size?       - just on close
 
-       LogEndTime(kTraceOpenBTree, noErr);
-
        return noErr;
 
 
@@ -352,8 +378,6 @@ ErrorExit:
        (void) ReleaseNode (btreePtr, &nodeRec);
        DisposePtr( (Ptr) btreePtr );
 
-       LogEndTime(kTraceOpenBTree, err);
-
        return err;
 }
 
@@ -379,8 +403,6 @@ OSStatus    BTClosePath                     (FCB                                    *filePtr)
        OSStatus                                err;
        BTreeControlBlockPtr    btreePtr;
 
-       LogStartTime(kTraceCloseBTree);
-
        btreePtr = (BTreeControlBlockPtr) filePtr->fcbBTCBPtr;
 
        if (btreePtr == nil)
@@ -397,16 +419,12 @@ OSStatus  BTClosePath                     (FCB                                    *filePtr)
        DisposePtr( (Ptr) btreePtr );
        filePtr->fcbBTCBPtr = nil;
 
-       LogEndTime(kTraceCloseBTree, noErr);
-
        return  noErr;
 
        /////////////////////// Error - Clean Up and Exit ///////////////////////////
 
 ErrorExit:
 
-       LogEndTime(kTraceCloseBTree, err);
-
        return  err;
 }
 
@@ -443,7 +461,6 @@ Result:             noErr                   - success, record contains copy of record found
 
 OSStatus       BTSearchRecord          (FCB                                            *filePtr,
                                                                 BTreeIterator                          *searchIterator,
-                                                                UInt32                                         heuristicHint,
                                                                 FSBufferDescriptor                     *record,
                                                                 UInt16                                         *recordLen,
                                                                 BTreeIterator                          *resultIterator )
@@ -460,12 +477,12 @@ OSStatus  BTSearchRecord          (FCB                                            *filePtr,
        Boolean                                 foundRecord;
        Boolean                                 validHint;
 
-
-       LogStartTime(kTraceSearchBTree);
-
        if (filePtr == nil)                                                                     return  paramErr;
        if (searchIterator == nil)                                                      return  paramErr;
 
+       node.buffer = nil;
+       node.blockHeader = nil;
+
        btreePtr = (BTreeControlBlockPtr) filePtr->fcbBTCBPtr;
        if (btreePtr == nil)                                                            return  fsBTInvalidFileErr;
 
@@ -508,30 +525,6 @@ OSStatus   BTSearchRecord          (FCB                                            *filePtr,
                        (void) BTInvalidateHint( searchIterator );
        }
 
-       ////////////////////////////// Try the heuristicHint //////////////////////////////////
-
-       if ( (foundRecord == false) && (heuristicHint != kInvalidMRUCacheKey) && (nodeNum != heuristicHint) )
-       {
-               LogStartTime(kHeuristicHint);
-               nodeNum = heuristicHint;
-               
-               err = GetNode (btreePtr, nodeNum, &node);
-               if( err == noErr )
-               {
-                       if ( ((BTNodeDescriptor*) node.buffer)->kind == kBTLeafNode &&
-                                ((BTNodeDescriptor*) node.buffer)->numRecords  >  0 )
-                       {
-                               foundRecord = SearchNode (btreePtr, node.buffer, &searchIterator->key, &index);
-                       }
-
-                       if (foundRecord == false)
-                       {
-                               err = ReleaseNode (btreePtr, &node);
-                               M_ExitOnError (err);
-                       }
-               }
-               LogEndTime(kHeuristicHint, (foundRecord == false));
-       }
 
        //////////////////////////// Search The Tree ////////////////////////////////
 
@@ -592,8 +585,6 @@ OSStatus    BTSearchRecord          (FCB                                            *filePtr,
        err = ReleaseNode (btreePtr, &node);
        M_ExitOnError (err);
 
-       LogEndTime(kTraceSearchBTree, (foundRecord == false));
-
        if (foundRecord == false)       return  fsBTRecordNotFoundErr;
        else                                            return  noErr;
 
@@ -621,8 +612,6 @@ ErrorExit:
        if ( err == fsBTEmptyErr )
                err = fsBTRecordNotFoundErr;
 
-       LogEndTime(kTraceSearchBTree, err);
-
        return err;
 }
 
@@ -665,13 +654,14 @@ OSStatus  BTIterateRecord         (FCB                                            *filePtr,
        UInt16                                          index;
 
 
-       LogStartTime(kTraceGetBTreeRecord);
-
        ////////////////////////// Priliminary Checks ///////////////////////////////
 
-       left.buffer             = nil;
-       right.buffer    = nil;
-       node.buffer             = nil;
+       left.buffer               = nil;
+       left.blockHeader  = nil;
+       right.buffer      = nil;
+       right.blockHeader = nil;
+       node.buffer               = nil;
+       node.blockHeader  = nil;
 
 
        if (filePtr == nil)
@@ -901,8 +891,6 @@ CopyData:
                M_ExitOnError (err);
        }
 
-       LogEndTime(kTraceGetBTreeRecord, noErr);
-
        return noErr;
 
        /////////////////////// Error - Clean Up and Exit ///////////////////////////
@@ -932,8 +920,6 @@ ErrorExit:
        if ( err == fsBTEmptyErr || err == fsBTEndOfIterationErr )
                err = fsBTRecordNotFoundErr;
 
-       LogEndTime(kTraceGetBTreeRecord, err);
-
        return err;
 }
 
@@ -972,9 +958,12 @@ BTIterateRecords(FCB *filePtr, BTreeIterationOperation operation, BTreeIterator
 
        ////////////////////////// Priliminary Checks ///////////////////////////////
 
-       left.buffer  = nil;
-       right.buffer = nil;
-       node.buffer  = nil;
+       left.buffer       = nil;
+       left.blockHeader  = nil;
+       right.buffer      = nil;
+       right.blockHeader = nil;
+       node.buffer       = nil;
+       node.blockHeader  = nil;
 
        btreePtr = (BTreeControlBlockPtr) filePtr->fcbBTCBPtr;
 
@@ -1031,6 +1020,8 @@ BTIterateRecords(FCB *filePtr, BTreeIterationOperation operation, BTreeIterator
 
        err = FindIteratorPosition(btreePtr, iterator, &left, &node, &right,
                                   &nodeNum, &index, &foundRecord);
+       if (err == fsBTRecordNotFoundErr)
+               err = 0;
        M_ExitOnError(err);
 
 
@@ -1126,6 +1117,10 @@ BTIterateRecords(FCB *filePtr, BTreeIterationOperation operation, BTreeIterator
 
 ProcessData:
        err = GetRecordByIndex(btreePtr, node.buffer, index, &keyPtr, &recordPtr, &len);
+       if (err) {
+               err = btBadNode;
+               goto ErrorExit;
+       }
        
        while (err == 0) {
                if (callBackProc(keyPtr, recordPtr, len, callBackState) == 0)
@@ -1158,6 +1153,10 @@ ProcessData:
                }
                err = GetRecordByIndex(btreePtr, node.buffer, index,
                                                &keyPtr, &recordPtr, &len);
+               if (err) {
+                       err = btBadNode;
+                       goto ErrorExit;
+               }
        }
 
 
@@ -1235,17 +1234,15 @@ OSStatus        BTInsertRecord          (FCB                                            *filePtr,
        UInt16                                  index;
        Boolean                                 recordFit;
 
-
        ////////////////////////// Priliminary Checks ///////////////////////////////
 
        nodeRec.buffer = nil;                                   // so we can call ReleaseNode
+       nodeRec.blockHeader = nil;
 
        err = CheckInsertParams (filePtr, iterator, record, recordLen);
        if (err != noErr)
                return  err;
 
-       LogStartTime(kTraceInsertBTreeRecord);
-
        btreePtr = (BTreeControlBlockPtr) filePtr->fcbBTCBPtr;
 
        REQUIRE_FILE_LOCK(btreePtr->fileRefNum, false);
@@ -1265,7 +1262,7 @@ OSStatus  BTInsertRecord          (FCB                                            *filePtr,
 
                case fsBTEmptyErr:      // if tree empty add 1st leaf node
 
-                                                               if (btreePtr->freeNodes == 0)
+                                                               if (BTAvailableNodes(btreePtr) == 0)
                                                                {
                                                                        err = ExtendBTree (btreePtr, btreePtr->totalNodes + 1);
                                                                        M_ExitOnError (err);
@@ -1277,6 +1274,9 @@ OSStatus  BTInsertRecord          (FCB                                            *filePtr,
                                                                err = GetNewNode (btreePtr, insertNodeNum, &nodeRec);
                                                                M_ExitOnError (err);
 
+                                                               // XXXdbg
+                                                               ModifyBlockStart(btreePtr->fileRefNum, &nodeRec);
+                                                               
                                                                ((NodeDescPtr)nodeRec.buffer)->kind = kBTLeafNode;
                                                                ((NodeDescPtr)nodeRec.buffer)->height   = 1;
 
@@ -1297,6 +1297,7 @@ OSStatus  BTInsertRecord          (FCB                                            *filePtr,
                                                                btreePtr->rootNode                      = insertNodeNum;
                                                                btreePtr->firstLeafNode         = insertNodeNum;
                                                                btreePtr->lastLeafNode          = insertNodeNum;
+
                                                                M_BTreeHeaderDirty (btreePtr);
 
                                                                goto Success;
@@ -1306,6 +1307,9 @@ OSStatus  BTInsertRecord          (FCB                                            *filePtr,
 
        if (index > 0)
        {
+               // XXXdbg
+               ModifyBlockStart(btreePtr->fileRefNum, &nodeRec);
+                                                               
                recordFit = InsertKeyRecord (btreePtr, nodeRec.buffer, index,
                                                                                &iterator->key, KeyLength(btreePtr, &iterator->key),
                                                                                record->bufferAddress, recordLen);
@@ -1320,10 +1324,10 @@ OSStatus        BTInsertRecord          (FCB                                            *filePtr,
 
        /////////////////////// Extend File If Necessary ////////////////////////////
 
-       nodesNeeded =  btreePtr->treeDepth + 1 - btreePtr->freeNodes;   //\80\80 math limit
+       nodesNeeded = (SInt32)btreePtr->treeDepth + 1 - BTAvailableNodes(btreePtr);
        if (nodesNeeded > 0)
        {
-               nodesNeeded += btreePtr->totalNodes;
+               nodesNeeded += (SInt32)btreePtr->totalNodes;
                if (nodesNeeded > CalcMapBits (btreePtr))       // we'll need to add a map node too!
                        ++nodesNeeded;
 
@@ -1344,7 +1348,7 @@ Success:
        ++btreePtr->writeCount;
        ++btreePtr->leafRecords;
        M_BTreeHeaderDirty (btreePtr);
-
+               
        // create hint
        iterator->hint.writeCount       = btreePtr->writeCount;
        iterator->hint.nodeNum          = insertNodeNum;
@@ -1352,8 +1356,6 @@ Success:
        iterator->hint.reserved1        = 0;
        iterator->hint.reserved2        = 0;
 
-       LogEndTime(kTraceInsertBTreeRecord, noErr);
-
        return noErr;
 
 
@@ -1372,8 +1374,6 @@ ErrorExit:
        if (err == fsBTEmptyErr)
                err = fsBTRecordNotFoundErr;
 
-       LogEndTime(kTraceInsertBTreeRecord, err);
-
        return err;
 }
 
@@ -1399,13 +1399,12 @@ OSStatus        BTReplaceRecord         (FCB                                            *filePtr,
        ////////////////////////// Priliminary Checks ///////////////////////////////
 
        nodeRec.buffer = nil;                                   // so we can call ReleaseNode
+       nodeRec.blockHeader = nil;
 
        err = CheckInsertParams (filePtr, iterator, record, recordLen);
        if (err != noErr)
                return err;
 
-       LogStartTime(kTraceReplaceBTreeRecord);
-
        btreePtr = (BTreeControlBlockPtr) filePtr->fcbBTCBPtr;
 
        REQUIRE_FILE_LOCK(btreePtr->fileRefNum, false);
@@ -1422,6 +1421,9 @@ OSStatus  BTReplaceRecord         (FCB                                            *filePtr,
                err = GetNode (btreePtr, insertNodeNum, &nodeRec);
                if( err == noErr )
                {
+                       // XXXdbg
+                       ModifyBlockStart(btreePtr->fileRefNum, &nodeRec);
+                                                               
                        err = TrySimpleReplace (btreePtr, nodeRec.buffer, iterator, record, recordLen, &recordFit);
                        M_ExitOnError (err);
 
@@ -1457,6 +1459,9 @@ OSStatus  BTReplaceRecord         (FCB                                            *filePtr,
        // optimization - if simple replace will work then don't extend btree
        // \80\80 if we tried this before, and failed because it wouldn't fit then we shouldn't try this again...
 
+       // XXXdbg
+       ModifyBlockStart(btreePtr->fileRefNum, &nodeRec);
+
        err = TrySimpleReplace (btreePtr, nodeRec.buffer, iterator, record, recordLen, &recordFit);
        M_ExitOnError (err);
 
@@ -1471,10 +1476,10 @@ OSStatus        BTReplaceRecord         (FCB                                            *filePtr,
 
        //////////////////////////// Make Some Room /////////////////////////////////
 
-       nodesNeeded =  btreePtr->treeDepth + 1 - btreePtr->freeNodes;   //\80\80 math limit
+       nodesNeeded = (SInt32)btreePtr->treeDepth + 1 - BTAvailableNodes(btreePtr);
        if (nodesNeeded > 0)
        {
-               nodesNeeded += btreePtr->totalNodes;
+               nodesNeeded += (SInt32)btreePtr->totalNodes;
                if (nodesNeeded > CalcMapBits (btreePtr))       // we'll need to add a map node too!
                        ++nodesNeeded;
 
@@ -1482,7 +1487,9 @@ OSStatus  BTReplaceRecord         (FCB                                            *filePtr,
                M_ExitOnError (err);
        }
 
-
+       // XXXdbg
+       ModifyBlockStart(btreePtr->fileRefNum, &nodeRec);
+                                                               
        DeleteRecord (btreePtr, nodeRec.buffer, index); // delete existing key/record
 
        err = InsertTree (btreePtr, treePathTable, &iterator->key, record->bufferAddress,
@@ -1499,8 +1506,6 @@ Success:
        iterator->hint.reserved1        = 0;
        iterator->hint.reserved2        = 0;
 
-       LogEndTime(kTraceReplaceBTreeRecord, noErr);
-
        return noErr;
 
 
@@ -1516,9 +1521,120 @@ ErrorExit:
        iterator->hint.reserved1        = 0;
        iterator->hint.reserved2        = 0;
 
+       return err;
+}
+
+
+
+//////////////////////////////// BTUpdateRecord ////////////////////////////////
+
+OSStatus
+BTUpdateRecord(FCB *filePtr, BTreeIterator *iterator,
+               IterateCallBackProcPtr callBackProc, void * callBackState)
+{
+       OSStatus                                err;
+       BTreeControlBlockPtr    btreePtr;
+       TreePathTable                   treePathTable;
+       BlockDescriptor                 nodeRec;
+       RecordPtr                               recordPtr;
+       BTreeKeyPtr                             keyPtr;
+       UInt32                                  insertNodeNum;
+       UInt16                                  recordLen;
+       UInt16                                  index;
+       Boolean                                 validHint;
+
+
+       ////////////////////////// Priliminary Checks ///////////////////////////////
+
+       nodeRec.buffer = nil;                                   // so we can call ReleaseNode
+       nodeRec.blockHeader = nil;
+
+       btreePtr = (BTreeControlBlockPtr) filePtr->fcbBTCBPtr;
+
+       REQUIRE_FILE_LOCK(btreePtr->fileRefNum, false);
+
+       ////////////////////////////// Take A Hint //////////////////////////////////
+
+       err = IsItAHint (btreePtr, iterator, &validHint);
+       M_ExitOnError (err);
+
+       if (validHint)
+       {
+               insertNodeNum = iterator->hint.nodeNum;
+
+               err = GetNode (btreePtr, insertNodeNum, &nodeRec);
+               if (err == noErr)
+               {
+                       if (((NodeDescPtr)nodeRec.buffer)->kind == kBTLeafNode &&
+                           SearchNode (btreePtr, nodeRec.buffer, &iterator->key, &index))
+                       {
+                               err = GetRecordByIndex(btreePtr, nodeRec.buffer, index, &keyPtr, &recordPtr, &recordLen);
+                               M_ExitOnError (err);
 
-       LogEndTime(kTraceReplaceBTreeRecord, err);
+                               // XXXdbg
+                               ModifyBlockStart(btreePtr->fileRefNum, &nodeRec);
+                                                               
+                               err = callBackProc(keyPtr, recordPtr, recordLen, callBackState);
+                               M_ExitOnError (err);
 
+                               err = UpdateNode (btreePtr, &nodeRec, 0, 0);
+                               M_ExitOnError (err);
+
+                               ++btreePtr->numValidHints;
+
+                               goto Success;
+                       }
+                       else
+                       {
+                               (void) BTInvalidateHint( iterator );
+                       }
+                       
+                       err = ReleaseNode (btreePtr, &nodeRec);
+                       M_ExitOnError (err);
+               }
+               else
+               {
+                       (void) BTInvalidateHint( iterator );
+               }
+       }
+
+       ////////////////////////////// Get A Clue ///////////////////////////////////
+
+       err = SearchTree (btreePtr, &iterator->key, treePathTable, &insertNodeNum, &nodeRec, &index);
+       M_ExitOnError (err);
+
+       err = GetRecordByIndex(btreePtr, nodeRec.buffer, index, &keyPtr, &recordPtr, &recordLen);
+       M_ExitOnError (err);
+
+       // XXXdbg
+       ModifyBlockStart(btreePtr->fileRefNum, &nodeRec);
+                                                               
+       err = callBackProc(keyPtr, recordPtr, recordLen, callBackState);
+       M_ExitOnError (err);
+
+       err = UpdateNode (btreePtr, &nodeRec, 0, 0);
+       M_ExitOnError (err);
+
+Success:
+       // create hint
+       iterator->hint.writeCount       = btreePtr->writeCount;
+       iterator->hint.nodeNum          = insertNodeNum;
+       iterator->hint.index            = 0;
+       iterator->hint.reserved1        = 0;
+       iterator->hint.reserved2        = 0;
+       return noErr;
+
+       ////////////////////////////// Error Exit ///////////////////////////////////
+
+ErrorExit:
+
+       (void) ReleaseNode (btreePtr, &nodeRec);
+
+       iterator->hint.writeCount       = 0;
+       iterator->hint.nodeNum          = 0;
+       iterator->hint.index            = 0;
+       iterator->hint.reserved1        = 0;
+       iterator->hint.reserved2        = 0;
        return err;
 }
 
@@ -1533,14 +1649,15 @@ OSStatus        BTDeleteRecord          (FCB                                            *filePtr,
        BTreeControlBlockPtr    btreePtr;
        TreePathTable                   treePathTable;
        BlockDescriptor                 nodeRec;
+       SInt32 nodesNeeded;
        UInt32                                  nodeNum;
        UInt16                                  index;
 
-       LogStartTime(kTraceDeleteBTreeRecord);
 
        ////////////////////////// Priliminary Checks ///////////////////////////////
 
        nodeRec.buffer = nil;                                   // so we can call ReleaseNode
+       nodeRec.blockHeader = nil;
 
        M_ReturnErrorIf (filePtr == nil,        paramErr);
        M_ReturnErrorIf (iterator == nil,       paramErr);
@@ -1563,6 +1680,19 @@ OSStatus BTDeleteRecord          (FCB                                            *filePtr,
        M_ExitOnError (err);                                    // record must exit for Delete
 
 
+       /////////////////////// Extend File If Necessary ////////////////////////////
+
+       nodesNeeded = (SInt32)btreePtr->treeDepth + 1 - BTAvailableNodes(btreePtr);
+       if ((btreePtr->attributes & kBTVariableIndexKeysMask) && (nodesNeeded > 0))
+       {
+               nodesNeeded += (SInt32)btreePtr->totalNodes;
+               if (nodesNeeded > CalcMapBits (btreePtr))
+                       ++nodesNeeded;
+
+               err = ExtendBTree (btreePtr, nodesNeeded);
+               M_ExitOnError (err);
+       }
+
        ///////////////////////////// Delete Record /////////////////////////////////
 
        err = DeleteTree (btreePtr, treePathTable, &nodeRec, index, 1);
@@ -1571,11 +1701,9 @@ OSStatus BTDeleteRecord          (FCB                                            *filePtr,
        ++btreePtr->writeCount;
        --btreePtr->leafRecords;
        M_BTreeHeaderDirty (btreePtr);
-
+               
        iterator->hint.nodeNum  = 0;
 
-       LogEndTime(kTraceDeleteBTreeRecord, noErr);
-
        return noErr;
 
        ////////////////////////////// Error Exit ///////////////////////////////////
@@ -1583,8 +1711,6 @@ OSStatus  BTDeleteRecord          (FCB                                            *filePtr,
 ErrorExit:
        (void) ReleaseNode (btreePtr, &nodeRec);
 
-       LogEndTime(kTraceDeleteBTreeRecord, err);
-
        return  err;
 }
 
@@ -1622,12 +1748,20 @@ OSStatus        BTGetInformation        (FCB                                    *filePtr,
        info->numNodes          = btreePtr->totalNodes;
        info->numFreeNodes      = btreePtr->freeNodes;
        info->lastfsync         = btreePtr->lastfsync;
-       info->reserved          = 0;
-
+       info->keyCompareType    = btreePtr->keyCompareType;
        return noErr;
 }
 
+// XXXdbg
+__private_extern__
+OSStatus
+BTIsDirty(FCB *filePtr)
+{
+       BTreeControlBlockPtr    btreePtr;
 
+       btreePtr = (BTreeControlBlockPtr) filePtr->fcbBTCBPtr;
+       return TreeIsDirty(btreePtr);
+}
 
 /*-------------------------------------------------------------------------------
 Routine:       BTFlushPath     -       Flush BTreeControlBlock to Header Node.
@@ -1649,8 +1783,6 @@ OSStatus  BTFlushPath                             (FCB                                    *filePtr)
        BTreeControlBlockPtr    btreePtr;
 
 
-       LogStartTime(kTraceFlushBTree);
-
        M_ReturnErrorIf (filePtr == nil,        paramErr);
 
        btreePtr = (BTreeControlBlockPtr) filePtr->fcbBTCBPtr;
@@ -1661,8 +1793,6 @@ OSStatus  BTFlushPath                             (FCB                                    *filePtr)
 
        err = UpdateHeader (btreePtr, false);
 
-       LogEndTime(kTraceFlushBTree, err);
-
        return  err;
 }
 
@@ -1692,6 +1822,9 @@ BTReloadData(FCB *filePtr)
        BTHeaderRec *header;    
 
 
+       node.buffer = nil;
+       node.blockHeader = nil;
+
        btreePtr = (BTreeControlBlockPtr) filePtr->fcbBTCBPtr;
        if (btreePtr == nil)
                return (fsBTInvalidFileErr);
@@ -1826,3 +1959,99 @@ OSStatus BTSetLastSync           (FCB                                    *filePtr,
 }
 
 
+__private_extern__
+OSStatus       BTHasContiguousNodes    (FCB                                    *filePtr)
+{
+       BTreeControlBlockPtr    btreePtr;
+
+
+       M_ReturnErrorIf (filePtr == nil,        paramErr);
+
+       btreePtr = (BTreeControlBlockPtr) filePtr->fcbBTCBPtr;
+       
+       REQUIRE_FILE_LOCK(btreePtr->fileRefNum, true);
+
+       M_ReturnErrorIf (btreePtr == nil,       fsBTInvalidFileErr);
+
+       return NodesAreContiguous(FCBTOVCB(filePtr), filePtr, btreePtr->nodeSize);
+}
+
+
+/*-------------------------------------------------------------------------------
+Routine:       BTGetUserData
+
+Function:      Read the user data area of the b-tree header node.
+
+-------------------------------------------------------------------------------*/
+__private_extern__
+OSStatus
+BTGetUserData(FCB *filePtr, void * dataPtr, int dataSize)
+{
+       BTreeControlBlockPtr btreePtr;
+       BlockDescriptor node;
+       char * offset;
+       OSStatus err;
+
+       if (dataSize > kBTreeHeaderUserBytes)
+               return (EINVAL);
+       node.buffer = nil;
+       node.blockHeader = nil;
+
+       btreePtr = (BTreeControlBlockPtr) filePtr->fcbBTCBPtr;
+       if (btreePtr == nil)
+               return (fsBTInvalidFileErr);
+
+       REQUIRE_FILE_LOCK(btreePtr->fileRefNum, false);
+
+       err = GetNode(btreePtr, kHeaderNodeNum, &node);
+       if (err)
+               return (err);
+       
+       offset = (char *)node.buffer + sizeof(BTNodeDescriptor) + sizeof(BTHeaderRec);
+       bcopy(offset, dataPtr, dataSize);
+
+       (void) ReleaseNode(btreePtr, &node);
+
+       return  (0);
+}
+
+
+/*-------------------------------------------------------------------------------
+Routine:       BTSetUserData
+
+Function:      Write the user data area of the b-tree header node.
+-------------------------------------------------------------------------------*/
+__private_extern__
+OSStatus
+BTSetUserData(FCB *filePtr, void * dataPtr, int dataSize)
+{
+       BTreeControlBlockPtr btreePtr;
+       BlockDescriptor node;
+       char * offset;
+       OSStatus err;
+
+       if (dataSize > kBTreeHeaderUserBytes)
+               return (EINVAL);
+       node.buffer = nil;
+       node.blockHeader = nil;
+
+       btreePtr = (BTreeControlBlockPtr) filePtr->fcbBTCBPtr;
+       if (btreePtr == nil)
+               return (fsBTInvalidFileErr);
+
+       REQUIRE_FILE_LOCK(btreePtr->fileRefNum, false);
+
+       err = GetNode(btreePtr, kHeaderNodeNum, &node);
+       if (err)
+               return (err);
+       
+       ModifyBlockStart(btreePtr->fileRefNum, &node);
+
+       offset = (char *)node.buffer + sizeof(BTNodeDescriptor) + sizeof(BTHeaderRec);
+       bcopy(dataPtr, offset, dataSize);
+
+       err = UpdateNode (btreePtr, &node, 0, 0);
+
+       return  (err);
+}
+