|
|
1.1 ! root 1: /* ! 2: * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. ! 3: * ! 4: * @APPLE_LICENSE_HEADER_START@ ! 5: * ! 6: * The contents of this file constitute Original Code as defined in and ! 7: * are subject to the Apple Public Source License Version 1.1 (the ! 8: * "License"). You may not use this file except in compliance with the ! 9: * License. Please obtain a copy of the License at ! 10: * http://www.apple.com/publicsource and read it before using this file. ! 11: * ! 12: * This Original Code and all software distributed under the License are ! 13: * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER ! 14: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, ! 15: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, ! 16: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the ! 17: * License for the specific language governing rights and limitations ! 18: * under the License. ! 19: * ! 20: * @APPLE_LICENSE_HEADER_END@ ! 21: */ ! 22: /* ! 23: File: BTree.c ! 24: ! 25: Contains: Implementation of public interface routines for B-tree manager. ! 26: ! 27: Version: HFS Plus 1.0 ! 28: ! 29: Written by: Gordon Sheridan and Bill Bruffey ! 30: ! 31: Copyright: � 1992-1999 by Apple Computer, Inc., all rights reserved. ! 32: ! 33: File Ownership: ! 34: ! 35: DRI: Don Brady ! 36: ! 37: Other Contact: Mark Day ! 38: ! 39: Technology: File Systems ! 40: ! 41: Writers: ! 42: ! 43: (msd) Mark Day ! 44: (DSH) Deric Horn ! 45: (djb) Don Brady ! 46: ! 47: Change History (most recent first): ! 48: <MOSXS> 9/22/99 ser Added routines BTGetLastSync and BTSetLastSync ! 49: <MOSXS> 6/1/99 djb Sync up with Mac OS 8.6. ! 50: <MOSXS> 6/30/98 djb In BTOpenPath make sure nodes are contiguous on disk (radar #2249539). ! 51: <MOSXS> 4/15/98 djb In BTOpenPath need to clear nodeRec.buffer if GetBlockProc fails. ! 52: <MOSXS> 4/11/98 djb Add RequireFileLock checking to all external entry points. ! 53: ! 54: <MOSXS> 03/23/98 djb In BTOpenPath use kTrashBlock option when releasing the header so ! 55: that we get a full node when we call GetNode. ! 56: ! 57: <CS9> 12/12/97 djb Radar #2202682, BTIterateRecord with kBTreeCurrentRecord was not ! 58: checking if we had a record and could call BlockMove with an ! 59: uninitialize source pointer (causing a bus error). ! 60: <CS8> 10/24/97 msd In BTIterateRecord, when moving to the previous or next record ! 61: and we have to move to another node, see if we need to release ! 62: the node about to be "shifted out" (opposite sibling of the ! 63: direction we need to move). ! 64: <CS7> 7/25/97 DSH BTSearchRecord now takes a heuristicHint, nodeNum, and tries it ! 65: before calling SearchBTree ! 66: <CS6> 7/24/97 djb GetBlockProc now take a file refnum instead of an FCB ptr. ! 67: <CS5> 7/22/97 djb Move trace points from BTreeWrapper.c to here. ! 68: <CS4> 7/21/97 djb LogEndTime now takes an error code. ! 69: <CS3> 7/16/97 DSH FilesInternal.i renamed FileMgrInternal.i to avoid name ! 70: collision ! 71: <CS2> 5/19/97 djb Add summary traces to BTIterateRecord. ! 72: <CS1> 4/23/97 djb first checked in ! 73: ! 74: <HFS7> 2/19/97 djb Enable variable sized index keys for HFS+ volumes. Added node ! 75: cache to support nodes larger than 512 bytes. ! 76: <HFS6> 1/27/97 djb Calls to InsertTree and DeleteTree are now recursive (to support ! 77: variable sized index keys). ! 78: <HFS5> 1/13/97 djb Added support for getting current record to BTIterateRecord. ! 79: <HFS4> 1/6/97 djb Initialize "BigKeys" attribute in BTOpen. ! 80: <HFS3> 1/3/97 djb Added support for large keys. ! 81: <HFS2> 12/23/96 djb On exit map fsBTEmptyErr and fsBTEndOfIterationErr to ! 82: fsBTRecordNotFoundErr. ! 83: <HFS1> 12/19/96 djb first checked in ! 84: ! 85: History applicable to original Scarecrow Design: ! 86: ! 87: <13> 10/25/96 ser Changing for new VFPI ! 88: <12> 10/18/96 ser Converting over VFPI changes ! 89: <11> 9/17/96 dkh More BTree statistics. Modified hint checks to not bail out when ! 90: an error is returned from GetNode. ! 91: <10> 9/16/96 dkh Revised BTree statistics. ! 92: <9> 8/23/96 dkh Remove checks for multiple paths to BTree file. Need to add ! 93: equivalent mechanism later. ! 94: <8> 6/20/96 dkh Radar #1358740. Switch from using Pools to debug MemAllocators. ! 95: <7> 3/14/96 jev Fix BTreeSetRecord, recordFound was not set for the case of a ! 96: simple replace causing the leafRecords count to get bumped even ! 97: though we didn't have to add a record. ! 98: <6> 3/1/96 prp Fix lint problems. Bug in BTSetRecord that does not initialize ! 99: recordFound. ! 100: <5> 1/22/96 dkh Add #include Memory.h ! 101: <4> 1/10/96 msd Use the real function names from Math64.i. ! 102: <3> 1/4/96 jev Fix BTItererateRecord for the condition when the iterator ! 103: position routine does not find the record and we are looking for ! 104: the next record. In such a case, if the node's forrward link is ! 105: non-zero, we have to keep iterating next and not return ! 106: fsBTEndOfIterationErr error. ! 107: <2> 12/7/95 dkh D10E2 build. Changed usage of Ref data type to LogicalAddress. ! 108: <1> 10/18/95 rst Moved from Scarecrow project. ! 109: ! 110: <24> 7/18/95 mbb Change MoveData & ClearBytes to BlockMoveData & BlockZero. ! 111: <23> 1/31/95 prp GetBlockProc interface uses a 64 bit node number. ! 112: <22> 1/12/95 wjk Adopt Model FileSystem changes in D5. ! 113: <21> 11/16/94 prp Add IsItAHint routine and use it whenever hint's node number was ! 114: used for testing. ! 115: <20> 11/10/94 prp BTGetInfo name collides with the same name in FileManagerPriv.i. ! 116: Change it to BTGetInformation. ! 117: <19> 9/30/94 prp Get in sync with D2 interface changes. ! 118: <18> 7/22/94 wjk Convert to the new set of header files. ! 119: <17> 12/9/93 wjk Cleanup usage of char, Byte, int8, UInt8, etc. ! 120: <16> 12/2/93 wjk Move from Makefiles to BuildFiles. Fit into the ModernOS and ! 121: NRCmds environments. ! 122: <15> 11/30/93 wjk Move from Makefiles to BuildFiles. Fit into the ModernOS and ! 123: NRCmds environments. ! 124: <14> 9/30/93 gs Rename E_NoGetNodeProc and E_NoReleaseNodeProc to ! 125: E_NoXxxxBlockProc. ! 126: <13> 8/31/93 prp Use Set64U instead of Set64. ! 127: <12> 8/16/93 prp In BTSearchRecord, if the input hint found the node and record, ! 128: set the local nodeNum variable correctly so that the resultant ! 129: iterator gets set correctly. ! 130: <11> 7/1/93 gs Fix bug in BTIterateRecord related to kBTreePrevRecord ! 131: operation. ! 132: <10> 6/2/93 gs Update for changes to FSErrors.h and add some comments. ! 133: <9> 5/24/93 gs Fix bug in BTInsert/Set/ReplaceRecord which didn't set node hint ! 134: properly in some cases. ! 135: <8> 5/24/93 gs Do NOT map fsBTEmptyErr to fsBTRecordNotFoundErr in BTSearchRecord. ! 136: <7> 5/24/93 gs Rename BTFlush to BTFlushPath. ! 137: <6> 5/21/93 gs Add hint optimization to Set/Replace routines. ! 138: <5> 5/10/93 gs Remove Panic from BTInitialize for small logicalEOF. Implement ! 139: Insert, Set, Replace, and Delete. ! 140: <4> 3/23/93 gs Finish BTInitialize. ! 141: <3> 2/8/93 gs Implement BTSearchRecord and BTIterateRecord. ! 142: <2> 12/8/92 gs Implement Open and Close routines. ! 143: <1> 11/15/92 gs first checked in ! 144: ! 145: */ ! 146: ! 147: #include "../headers/BTreesPrivate.h" ! 148: ! 149: #include "../headers/HFSInstrumentation.h" ! 150: ! 151: ! 152: //////////////////////////////////// Globals //////////////////////////////////// ! 153: ! 154: ! 155: /////////////////////////// BTree Module Entry Points /////////////////////////// ! 156: ! 157: ! 158: ! 159: /*------------------------------------------------------------------------------- ! 160: Routine: BTOpenPath - Open a file for access as a B*Tree. ! 161: ! 162: Function: Create BTree control block for a file, if necessary. Validates the ! 163: file to be sure it looks like a BTree file. ! 164: ! 165: ! 166: Input: filePtr - pointer to file to open as a B-tree ! 167: keyCompareProc - pointer to client's KeyCompare function ! 168: getBlockProc - pointer to client's GetBlock function ! 169: releaseBlockProc - pointer to client's ReleaseBlock function ! 170: setEndOfForkProc - pointer to client's SetEOF function ! 171: ! 172: Result: noErr - success ! 173: paramErr - required ptr was nil ! 174: fsBTInvalidFileErr - ! 175: memFullErr - ! 176: != noErr - failure ! 177: -------------------------------------------------------------------------------*/ ! 178: ! 179: OSStatus BTOpenPath (FCB *filePtr, ! 180: KeyCompareProcPtr keyCompareProc, ! 181: GetBlockProcPtr getBlockProc, ! 182: ReleaseBlockProcPtr releaseBlockProc, ! 183: SetEndOfForkProcPtr setEndOfForkProc, ! 184: SetBlockSizeProcPtr setBlockSizeProc ) ! 185: { ! 186: OSStatus err; ! 187: BTreeControlBlockPtr btreePtr; ! 188: BTHeaderRec *header; ! 189: NodeRec nodeRec; ! 190: ! 191: LogStartTime(kTraceOpenBTree); ! 192: ! 193: ////////////////////// Preliminary Error Checking /////////////////////////// ! 194: ! 195: if ( filePtr == nil || ! 196: getBlockProc == nil || ! 197: releaseBlockProc == nil || ! 198: setEndOfForkProc == nil || ! 199: setBlockSizeProc == nil ) ! 200: { ! 201: return paramErr; ! 202: } ! 203: ! 204: if ( filePtr->fcbBTCBPtr != nil ) // already has a BTreeCB ! 205: return noErr; ! 206: ! 207: // is file large enough to contain header node? ! 208: if ( filePtr->fcbEOF < kMinNodeSize ) ! 209: return fsBTInvalidFileErr; //�� or E_BadHeader? ! 210: ! 211: ! 212: //////////////////////// Allocate Control Block ///////////////////////////// ! 213: ! 214: btreePtr = (BTreeControlBlock*) NewPtrSysClear( sizeof( BTreeControlBlock ) ); ! 215: if (btreePtr == nil) ! 216: { ! 217: Panic ("\pBTOpen: no memory for btreePtr."); ! 218: return memFullErr; ! 219: } ! 220: ! 221: btreePtr->getBlockProc = getBlockProc; ! 222: btreePtr->releaseBlockProc = releaseBlockProc; ! 223: btreePtr->setEndOfForkProc = setEndOfForkProc; ! 224: btreePtr->keyCompareProc = keyCompareProc; ! 225: ! 226: /////////////////////////// Read Header Node //////////////////////////////// ! 227: ! 228: nodeRec.buffer = nil; // so we can call ReleaseNode ! 229: nodeRec.blockSize = kMinNodeSize; ! 230: btreePtr->fileRefNum = GetFileRefNumFromFCB(filePtr); ! 231: filePtr->fcbBTCBPtr = (Ptr) btreePtr; // attach btree cb to file ! 232: ! 233: RequireFileLock(btreePtr->fileRefNum, false); ! 234: ! 235: // it is now safe to call M_ExitOnError (err) ! 236: ! 237: err = setBlockSizeProc (btreePtr->fileRefNum, kMinNodeSize, 1); ! 238: M_ExitOnError (err); ! 239: ! 240: ! 241: err = getBlockProc (btreePtr->fileRefNum, ! 242: kHeaderNodeNum, ! 243: kGetBlock, ! 244: &nodeRec ); ! 245: if (err != noErr) ! 246: { ! 247: nodeRec.buffer = nil; ! 248: nodeRec.blockHeader = nil; ! 249: Panic("\pBTOpen: getNodeProc returned error getting header node."); ! 250: goto ErrorExit; ! 251: } ! 252: ! 253: header = (BTHeaderRec*) ((u_long)nodeRec.buffer + sizeof(BTNodeDescriptor)); ! 254: ! 255: ! 256: ///////////////////////////// verify header ///////////////////////////////// ! 257: ! 258: err = VerifyHeader (filePtr, header); ! 259: M_ExitOnError (err); ! 260: ! 261: ! 262: ///////////////////// Initalize fields from header ////////////////////////// ! 263: ! 264: PanicIf ( (FCBTOVCB(filePtr)->vcbSigWord != 0x4244) && (header->nodeSize == 512), "\p BTOpenPath: wrong node size for HFS+ volume!"); // 0x4244 = 'BD' ! 265: ! 266: btreePtr->treeDepth = header->treeDepth; ! 267: btreePtr->rootNode = header->rootNode; ! 268: btreePtr->leafRecords = header->leafRecords; ! 269: btreePtr->firstLeafNode = header->firstLeafNode; ! 270: btreePtr->lastLeafNode = header->lastLeafNode; ! 271: btreePtr->nodeSize = header->nodeSize; ! 272: btreePtr->maxKeyLength = header->maxKeyLength; ! 273: btreePtr->totalNodes = header->totalNodes; ! 274: btreePtr->freeNodes = header->freeNodes; ! 275: // ignore header->clumpSize; //�� rename this field? ! 276: btreePtr->btreeType = header->btreeType; ! 277: ! 278: btreePtr->attributes = header->attributes; ! 279: ! 280: if ( btreePtr->maxKeyLength > 40 ) ! 281: btreePtr->attributes |= (kBTBigKeysMask + kBTVariableIndexKeysMask); //�� we need a way to save these attributes ! 282: ! 283: /////////////////////// Initialize dynamic fields /////////////////////////// ! 284: ! 285: btreePtr->version = kBTreeVersion; ! 286: btreePtr->flags = 0; ! 287: btreePtr->writeCount = 1; ! 288: ! 289: btreePtr->numGetNodes = 1; // for earlier call to getNodeProc ! 290: ! 291: /////////////////////////// Check Header Node /////////////////////////////// ! 292: ! 293: //�� set kBadClose attribute bit, and UpdateNode ! 294: ! 295: // if nodeSize is 512 then we don't need to release, just CheckNode ! 296: ! 297: if ( btreePtr->nodeSize == kMinNodeSize ) ! 298: { ! 299: err = CheckNode (btreePtr, nodeRec.buffer); ! 300: if (err) ! 301: VTOVCB(btreePtr->fileRefNum)->vcbFlags |= kHFS_DamagedVolume; ! 302: M_ExitOnError (err); ! 303: } ! 304: else ! 305: { ! 306: err = setBlockSizeProc (btreePtr->fileRefNum, btreePtr->nodeSize, 32); //���we should try and get this down to 8 ! 307: M_ExitOnError (err); ! 308: ! 309: /* ! 310: * Need to use kTrashBlock option to force the ! 311: * buffer cache to read the entire node ! 312: */ ! 313: err = releaseBlockProc(btreePtr->fileRefNum, &nodeRec, kTrashBlock); ! 314: M_ExitOnError (err); ! 315: ! 316: err = GetNode (btreePtr, kHeaderNodeNum, &nodeRec ); // calls CheckNode... ! 317: M_ExitOnError (err); ! 318: } ! 319: ! 320: //�� total nodes * node size <= LEOF? ! 321: ! 322: ! 323: err = ReleaseNode (btreePtr, &nodeRec); ! 324: M_ExitOnError (err); ! 325: ! 326: /* ! 327: * Under Mac OS, b-tree nodes can be non-contiguous on disk when the ! 328: * allocation block size is smaller than the b-tree node size. ! 329: */ ! 330: if ( !NodesAreContiguous(FCBTOVCB(filePtr), filePtr, btreePtr->nodeSize) ) ! 331: return fsBTInvalidNodeErr; ! 332: ! 333: //////////////////////////////// Success //////////////////////////////////// ! 334: ! 335: //�� align LEOF to multiple of node size? - just on close ! 336: ! 337: LogEndTime(kTraceOpenBTree, noErr); ! 338: ! 339: return noErr; ! 340: ! 341: ! 342: /////////////////////// Error - Clean up and Exit /////////////////////////// ! 343: ! 344: ErrorExit: ! 345: ! 346: filePtr->fcbBTCBPtr = nil; ! 347: (void) ReleaseNode (btreePtr, &nodeRec); ! 348: DisposePtr( (Ptr) btreePtr ); ! 349: ! 350: LogEndTime(kTraceOpenBTree, err); ! 351: ! 352: return err; ! 353: } ! 354: ! 355: ! 356: ! 357: /*------------------------------------------------------------------------------- ! 358: Routine: BTClosePath - Flush BTree Header and Deallocate Memory for BTree. ! 359: ! 360: Function: Flush the BTreeControlBlock fields to header node, and delete BTree control ! 361: block and key descriptor associated with the file if filePtr is last ! 362: path of type kBTreeType ('btre'). ! 363: ! 364: ! 365: Input: filePtr - pointer to file to delete BTree control block for. ! 366: ! 367: Result: noErr - success ! 368: fsBTInvalidFileErr - ! 369: != noErr - failure ! 370: -------------------------------------------------------------------------------*/ ! 371: ! 372: OSStatus BTClosePath (FCB *filePtr) ! 373: { ! 374: OSStatus err; ! 375: BTreeControlBlockPtr btreePtr; ! 376: ! 377: LogStartTime(kTraceCloseBTree); ! 378: ! 379: btreePtr = (BTreeControlBlockPtr) filePtr->fcbBTCBPtr; ! 380: ! 381: if (btreePtr == nil) ! 382: return fsBTInvalidFileErr; ! 383: ! 384: RequireFileLock(btreePtr->fileRefNum, false); ! 385: ! 386: ////////////////////// Check for other BTree Paths ////////////////////////// ! 387: ! 388: btreePtr->attributes &= ~kBTBadCloseMask; // clear "bad close" attribute bit ! 389: err = UpdateHeader (btreePtr); ! 390: M_ExitOnError (err); ! 391: ! 392: DisposePtr( (Ptr) btreePtr ); ! 393: filePtr->fcbBTCBPtr = nil; ! 394: ! 395: LogEndTime(kTraceCloseBTree, noErr); ! 396: ! 397: return noErr; ! 398: ! 399: /////////////////////// Error - Clean Up and Exit /////////////////////////// ! 400: ! 401: ErrorExit: ! 402: ! 403: LogEndTime(kTraceCloseBTree, err); ! 404: ! 405: return err; ! 406: } ! 407: ! 408: ! 409: ! 410: /*------------------------------------------------------------------------------- ! 411: Routine: BTSearchRecord - Search BTree for a record with a matching key. ! 412: ! 413: Function: Search for position in B*Tree indicated by searchKey. If a valid node hint ! 414: is provided, it will be searched first, then SearchTree will be called. ! 415: If a BTreeIterator is provided, it will be set to the position found as ! 416: a result of the search. If a record exists at that position, and a BufferDescriptor ! 417: is supplied, the record will be copied to the buffer (as much as will fit), ! 418: and recordLen will be set to the length of the record. ! 419: ! 420: If an error other than fsBTRecordNotFoundErr occurs, the BTreeIterator, if any, ! 421: is invalidated, and recordLen is set to 0. ! 422: ! 423: ! 424: Input: pathPtr - pointer to path for BTree file. ! 425: searchKey - pointer to search key to match. ! 426: hintPtr - pointer to hint (may be nil) ! 427: ! 428: Output: record - pointer to BufferDescriptor containing record ! 429: recordLen - length of data at recordPtr ! 430: iterator - pointer to BTreeIterator indicating position result of search ! 431: ! 432: Result: noErr - success, record contains copy of record found ! 433: fsBTRecordNotFoundErr - record was not found, no data copied ! 434: fsBTInvalidFileErr - no BTreeControlBlock is allocated for the fork ! 435: fsBTInvalidKeyLengthErr - ! 436: != noErr - failure ! 437: -------------------------------------------------------------------------------*/ ! 438: ! 439: OSStatus BTSearchRecord (FCB *filePtr, ! 440: BTreeIterator *searchIterator, ! 441: UInt32 heuristicHint, ! 442: FSBufferDescriptor *record, ! 443: UInt16 *recordLen, ! 444: BTreeIterator *resultIterator ) ! 445: { ! 446: OSStatus err; ! 447: BTreeControlBlockPtr btreePtr; ! 448: TreePathTable treePathTable; ! 449: UInt32 nodeNum; ! 450: BlockDescriptor node; ! 451: UInt16 index; ! 452: BTreeKeyPtr keyPtr; ! 453: RecordPtr recordPtr; ! 454: UInt16 len; ! 455: Boolean foundRecord; ! 456: Boolean validHint; ! 457: ! 458: ! 459: LogStartTime(kTraceSearchBTree); ! 460: ! 461: if (filePtr == nil) return paramErr; ! 462: if (searchIterator == nil) return paramErr; ! 463: ! 464: btreePtr = (BTreeControlBlockPtr) filePtr->fcbBTCBPtr; ! 465: if (btreePtr == nil) return fsBTInvalidFileErr; ! 466: ! 467: RequireFileLock(btreePtr->fileRefNum, true); ! 468: ! 469: foundRecord = false; ! 470: ! 471: ////////////////////////////// Take A Hint ////////////////////////////////// ! 472: ! 473: err = IsItAHint (btreePtr, searchIterator, &validHint); ! 474: M_ExitOnError (err); ! 475: ! 476: if (validHint) ! 477: { ! 478: nodeNum = searchIterator->hint.nodeNum; ! 479: ! 480: err = GetNode (btreePtr, nodeNum, &node); ! 481: if( err == noErr ) ! 482: { ! 483: if ( ((BTNodeDescriptor*) node.buffer)->kind == kBTLeafNode && ! 484: ((BTNodeDescriptor*) node.buffer)->numRecords > 0 ) ! 485: { ! 486: foundRecord = SearchNode (btreePtr, node.buffer, &searchIterator->key, &index); ! 487: ! 488: //�� if !foundRecord, we could still skip tree search if ( 0 < index < numRecords ) ! 489: } ! 490: ! 491: if (foundRecord == false) ! 492: { ! 493: err = ReleaseNode (btreePtr, &node); ! 494: M_ExitOnError (err); ! 495: } ! 496: else ! 497: { ! 498: ++btreePtr->numValidHints; ! 499: } ! 500: } ! 501: ! 502: if( foundRecord == false ) ! 503: (void) BTInvalidateHint( searchIterator ); ! 504: } ! 505: ! 506: ////////////////////////////// Try the heuristicHint ////////////////////////////////// ! 507: ! 508: if ( (foundRecord == false) && (heuristicHint != kInvalidMRUCacheKey) && (nodeNum != heuristicHint) ) ! 509: { ! 510: LogStartTime(kHeuristicHint); ! 511: nodeNum = heuristicHint; ! 512: ! 513: err = GetNode (btreePtr, nodeNum, &node); ! 514: if( err == noErr ) ! 515: { ! 516: if ( ((BTNodeDescriptor*) node.buffer)->kind == kBTLeafNode && ! 517: ((BTNodeDescriptor*) node.buffer)->numRecords > 0 ) ! 518: { ! 519: foundRecord = SearchNode (btreePtr, node.buffer, &searchIterator->key, &index); ! 520: } ! 521: ! 522: if (foundRecord == false) ! 523: { ! 524: err = ReleaseNode (btreePtr, &node); ! 525: M_ExitOnError (err); ! 526: } ! 527: } ! 528: LogEndTime(kHeuristicHint, (foundRecord == false)); ! 529: } ! 530: ! 531: //////////////////////////// Search The Tree //////////////////////////////// ! 532: ! 533: if (foundRecord == false) ! 534: { ! 535: err = SearchTree ( btreePtr, &searchIterator->key, treePathTable, &nodeNum, &node, &index); ! 536: switch (err) ! 537: { ! 538: case noErr: foundRecord = true; break; ! 539: case fsBTRecordNotFoundErr: break; ! 540: default: goto ErrorExit; ! 541: } ! 542: } ! 543: ! 544: ! 545: //////////////////////////// Get the Record ///////////////////////////////// ! 546: ! 547: if (foundRecord == true) ! 548: { ! 549: //XXX Should check for errors! Or BlockMove could choke on recordPtr!!! ! 550: GetRecordByIndex (btreePtr, node.buffer, index, &keyPtr, &recordPtr, &len); ! 551: ! 552: if (recordLen != nil) *recordLen = len; ! 553: ! 554: if (record != nil) ! 555: { ! 556: ByteCount recordSize; ! 557: ! 558: recordSize = record->itemCount * record->itemSize; ! 559: ! 560: if (len > recordSize) len = recordSize; ! 561: ! 562: BlockMoveData (recordPtr, record->bufferAddress, len); ! 563: } ! 564: } ! 565: ! 566: ! 567: /////////////////////// Success - Update Iterator /////////////////////////// ! 568: ! 569: if (resultIterator != nil) ! 570: { ! 571: resultIterator->hint.writeCount = btreePtr->writeCount; ! 572: resultIterator->hint.nodeNum = nodeNum; ! 573: resultIterator->hint.index = index; ! 574: #if DEBUG_BUILD ! 575: resultIterator->hint.reserved1 = 0; ! 576: resultIterator->hint.reserved2 = 0; ! 577: resultIterator->version = 0; ! 578: resultIterator->reserved = 0; ! 579: #endif ! 580: // copy the key in the BTree when found rather than searchIterator->key to get proper case/diacriticals ! 581: if (foundRecord == true) ! 582: BlockMoveData ((Ptr)keyPtr, (Ptr)&resultIterator->key, CalcKeySize(btreePtr, keyPtr)); ! 583: else ! 584: BlockMoveData ((Ptr)&searchIterator->key, (Ptr)&resultIterator->key, CalcKeySize(btreePtr, &searchIterator->key)); ! 585: } ! 586: ! 587: err = ReleaseNode (btreePtr, &node); ! 588: M_ExitOnError (err); ! 589: ! 590: LogEndTime(kTraceSearchBTree, (foundRecord == false)); ! 591: ! 592: if (foundRecord == false) return fsBTRecordNotFoundErr; ! 593: else return noErr; ! 594: ! 595: ! 596: /////////////////////// Error - Clean Up and Exit /////////////////////////// ! 597: ! 598: ErrorExit: ! 599: ! 600: if (recordLen != nil) ! 601: *recordLen = 0; ! 602: ! 603: if (resultIterator != nil) ! 604: { ! 605: resultIterator->hint.writeCount = 0; ! 606: resultIterator->hint.nodeNum = 0; ! 607: resultIterator->hint.index = 0; ! 608: resultIterator->hint.reserved1 = 0; ! 609: resultIterator->hint.reserved2 = 0; ! 610: ! 611: resultIterator->version = 0; ! 612: resultIterator->reserved = 0; ! 613: resultIterator->key.length16 = 0; // zero out two bytes to cover both types of keys ! 614: } ! 615: ! 616: if ( err == fsBTEmptyErr ) ! 617: err = fsBTRecordNotFoundErr; ! 618: ! 619: LogEndTime(kTraceSearchBTree, err); ! 620: ! 621: return err; ! 622: } ! 623: ! 624: ! 625: ! 626: /*------------------------------------------------------------------------------- ! 627: Routine: BTIterateRecord - Find the first, next, previous, or last record. ! 628: ! 629: Function: Find the first, next, previous, or last record in the BTree ! 630: ! 631: Input: pathPtr - pointer to path iterate records for. ! 632: operation - iteration operation (first,next,prev,last) ! 633: iterator - pointer to iterator indicating start position ! 634: ! 635: Output: iterator - iterator is updated to indicate new position ! 636: newKeyPtr - pointer to buffer to copy key found by iteration ! 637: record - pointer to buffer to copy record found by iteration ! 638: recordLen - length of record ! 639: ! 640: Result: noErr - success ! 641: != noErr - failure ! 642: -------------------------------------------------------------------------------*/ ! 643: ! 644: OSStatus BTIterateRecord (FCB *filePtr, ! 645: BTreeIterationOperation operation, ! 646: BTreeIterator *iterator, ! 647: FSBufferDescriptor *record, ! 648: UInt16 *recordLen ) ! 649: { ! 650: OSStatus err; ! 651: BTreeControlBlockPtr btreePtr; ! 652: BTreeKeyPtr keyPtr; ! 653: RecordPtr recordPtr; ! 654: UInt16 len; ! 655: ! 656: Boolean foundRecord; ! 657: UInt32 nodeNum; ! 658: ! 659: BlockDescriptor left, node, right; ! 660: UInt16 index; ! 661: ! 662: ! 663: LogStartTime(kTraceGetBTreeRecord); ! 664: ! 665: ////////////////////////// Priliminary Checks /////////////////////////////// ! 666: ! 667: left.buffer = nil; ! 668: right.buffer = nil; ! 669: node.buffer = nil; ! 670: ! 671: ! 672: if (filePtr == nil) ! 673: { ! 674: return paramErr; ! 675: } ! 676: ! 677: btreePtr = (BTreeControlBlockPtr) filePtr->fcbBTCBPtr; ! 678: if (btreePtr == nil) ! 679: { ! 680: return fsBTInvalidFileErr; //�� handle properly ! 681: } ! 682: ! 683: RequireFileLock(btreePtr->fileRefNum, true); ! 684: ! 685: if ((operation != kBTreeFirstRecord) && ! 686: (operation != kBTreeNextRecord) && ! 687: (operation != kBTreeCurrentRecord) && ! 688: (operation != kBTreePrevRecord) && ! 689: (operation != kBTreeLastRecord)) ! 690: { ! 691: err = fsInvalidIterationMovmentErr; ! 692: goto ErrorExit; ! 693: } ! 694: ! 695: /////////////////////// Find First or Last Record /////////////////////////// ! 696: ! 697: if ((operation == kBTreeFirstRecord) || (operation == kBTreeLastRecord)) ! 698: { ! 699: if (operation == kBTreeFirstRecord) nodeNum = btreePtr->firstLeafNode; ! 700: else nodeNum = btreePtr->lastLeafNode; ! 701: ! 702: if (nodeNum == 0) ! 703: { ! 704: err = fsBTEmptyErr; ! 705: goto ErrorExit; ! 706: } ! 707: ! 708: err = GetNode (btreePtr, nodeNum, &node); ! 709: M_ExitOnError (err); ! 710: ! 711: if ( ((NodeDescPtr) node.buffer)->kind != kBTLeafNode || ! 712: ((NodeDescPtr) node.buffer)->numRecords <= 0 ) ! 713: { ! 714: err = ReleaseNode (btreePtr, &node); ! 715: M_ExitOnError (err); ! 716: ! 717: err = fsBTInvalidNodeErr; ! 718: goto ErrorExit; ! 719: } ! 720: ! 721: if (operation == kBTreeFirstRecord) index = 0; ! 722: else index = ((BTNodeDescriptor*) node.buffer)->numRecords - 1; ! 723: ! 724: goto CopyData; //�� is there a cleaner way? ! 725: } ! 726: ! 727: ! 728: //////////////////////// Find Iterator Position ///////////////////////////// ! 729: ! 730: err = FindIteratorPosition (btreePtr, iterator, ! 731: &left, &node, &right, &nodeNum, &index, &foundRecord); ! 732: M_ExitOnError (err); ! 733: ! 734: ! 735: ///////////////////// Find Next Or Previous Record ////////////////////////// ! 736: ! 737: if (operation == kBTreePrevRecord) ! 738: { ! 739: if (index > 0) ! 740: { ! 741: --index; ! 742: } ! 743: else ! 744: { ! 745: if (left.buffer == nil) ! 746: { ! 747: nodeNum = ((NodeDescPtr) node.buffer)->bLink; ! 748: if ( nodeNum > 0) ! 749: { ! 750: err = GetNode (btreePtr, nodeNum, &left); ! 751: M_ExitOnError (err); ! 752: } else { ! 753: err = fsBTStartOfIterationErr; ! 754: goto ErrorExit; ! 755: } ! 756: } ! 757: // Before we stomp on "right", we'd better release it if needed ! 758: if (right.buffer != nil) { ! 759: err = ReleaseNode(btreePtr, &right); ! 760: M_ExitOnError(err); ! 761: } ! 762: right = node; ! 763: node = left; ! 764: left.buffer = nil; ! 765: index = ((NodeDescPtr) node.buffer)->numRecords -1; ! 766: } ! 767: } ! 768: else if (operation == kBTreeNextRecord) ! 769: { ! 770: if ((foundRecord != true) && ! 771: (((NodeDescPtr) node.buffer)->fLink == 0) && ! 772: (index == ((NodeDescPtr) node.buffer)->numRecords)) ! 773: { ! 774: err = fsBTEndOfIterationErr; ! 775: goto ErrorExit; ! 776: } ! 777: ! 778: // we did not find the record but the index is already positioned correctly ! 779: if ((foundRecord == false) && (index != ((NodeDescPtr) node.buffer)->numRecords)) ! 780: goto CopyData; ! 781: ! 782: // we found the record OR we have to look in the next node ! 783: if (index < ((NodeDescPtr) node.buffer)->numRecords -1) ! 784: { ! 785: ++index; ! 786: } ! 787: else ! 788: { ! 789: if (right.buffer == nil) ! 790: { ! 791: nodeNum = ((NodeDescPtr) node.buffer)->fLink; ! 792: if ( nodeNum > 0) ! 793: { ! 794: err = GetNode (btreePtr, nodeNum, &right); ! 795: M_ExitOnError (err); ! 796: } else { ! 797: err = fsBTEndOfIterationErr; ! 798: goto ErrorExit; ! 799: } ! 800: } ! 801: // Before we stomp on "left", we'd better release it if needed ! 802: if (left.buffer != nil) { ! 803: err = ReleaseNode(btreePtr, &left); ! 804: M_ExitOnError(err); ! 805: } ! 806: left = node; ! 807: node = right; ! 808: right.buffer = nil; ! 809: index = 0; ! 810: } ! 811: } ! 812: else // operation == kBTreeCurrentRecord ! 813: { ! 814: // make sure we have something... <CS9> ! 815: if ((foundRecord != true) && ! 816: (index >= ((NodeDescPtr) node.buffer)->numRecords)) ! 817: { ! 818: err = fsBTEndOfIterationErr; ! 819: goto ErrorExit; ! 820: } ! 821: } ! 822: ! 823: //////////////////// Copy Record And Update Iterator //////////////////////// ! 824: ! 825: CopyData: ! 826: ! 827: // added check for errors <CS9> ! 828: err = GetRecordByIndex (btreePtr, node.buffer, index, &keyPtr, &recordPtr, &len); ! 829: M_ExitOnError (err); ! 830: ! 831: if (recordLen != nil) ! 832: *recordLen = len; ! 833: ! 834: if (record != nil) ! 835: { ! 836: ByteCount recordSize; ! 837: ! 838: recordSize = record->itemCount * record->itemSize; ! 839: ! 840: if (len > recordSize) len = recordSize; ! 841: ! 842: BlockMoveData (recordPtr, record->bufferAddress, len); ! 843: } ! 844: ! 845: if (iterator != nil) // first & last do not require iterator ! 846: { ! 847: iterator->hint.writeCount = btreePtr->writeCount; ! 848: iterator->hint.nodeNum = nodeNum; ! 849: iterator->hint.index = index; ! 850: iterator->hint.reserved1 = 0; ! 851: iterator->hint.reserved2 = 0; ! 852: ! 853: iterator->version = 0; ! 854: iterator->reserved = 0; ! 855: ! 856: BlockMoveData ((Ptr)keyPtr, (Ptr)&iterator->key, CalcKeySize(btreePtr, keyPtr)); ! 857: } ! 858: ! 859: ! 860: ///////////////////////////// Release Nodes ///////////////////////////////// ! 861: ! 862: err = ReleaseNode (btreePtr, &node); ! 863: M_ExitOnError (err); ! 864: ! 865: if (left.buffer != nil) ! 866: { ! 867: err = ReleaseNode (btreePtr, &left); ! 868: M_ExitOnError (err); ! 869: } ! 870: ! 871: if (right.buffer != nil) ! 872: { ! 873: err = ReleaseNode (btreePtr, &right); ! 874: M_ExitOnError (err); ! 875: } ! 876: ! 877: LogEndTime(kTraceGetBTreeRecord, noErr); ! 878: ! 879: return noErr; ! 880: ! 881: /////////////////////// Error - Clean Up and Exit /////////////////////////// ! 882: ! 883: ErrorExit: ! 884: ! 885: (void) ReleaseNode (btreePtr, &left); ! 886: (void) ReleaseNode (btreePtr, &node); ! 887: (void) ReleaseNode (btreePtr, &right); ! 888: ! 889: if (recordLen != nil) ! 890: *recordLen = 0; ! 891: ! 892: if (iterator != nil) ! 893: { ! 894: iterator->hint.writeCount = 0; ! 895: iterator->hint.nodeNum = 0; ! 896: iterator->hint.index = 0; ! 897: iterator->hint.reserved1 = 0; ! 898: iterator->hint.reserved2 = 0; ! 899: ! 900: iterator->version = 0; ! 901: iterator->reserved = 0; ! 902: iterator->key.length16 = 0; ! 903: } ! 904: ! 905: if ( err == fsBTEmptyErr || err == fsBTEndOfIterationErr ) ! 906: err = fsBTRecordNotFoundErr; ! 907: ! 908: LogEndTime(kTraceGetBTreeRecord, err); ! 909: ! 910: return err; ! 911: } ! 912: ! 913: ! 914: /*------------------------------------------------------------------------------- ! 915: Routine: BTIterateRecords ! 916: ! 917: Function: Find a series of records ! 918: ! 919: Input: filePtr - b-tree file ! 920: operation - iteration operation (first,next,prev,last) ! 921: iterator - pointer to iterator indicating start position ! 922: callBackProc - pointer to routince to process a record ! 923: callBackState - pointer to state data (used by callBackProc) ! 924: ! 925: Output: iterator - iterator is updated to indicate new position ! 926: ! 927: Result: noErr - success ! 928: != noErr - failure ! 929: -------------------------------------------------------------------------------*/ ! 930: ! 931: OSStatus ! 932: BTIterateRecords(FCB *filePtr, BTreeIterationOperation operation, BTreeIterator *iterator, ! 933: IterateCallBackProcPtr callBackProc, void * callBackState) ! 934: { ! 935: OSStatus err; ! 936: BTreeControlBlockPtr btreePtr; ! 937: BTreeKeyPtr keyPtr; ! 938: RecordPtr recordPtr; ! 939: UInt16 len; ! 940: Boolean foundRecord; ! 941: UInt32 nodeNum; ! 942: BlockDescriptor left, node, right; ! 943: UInt16 index; ! 944: ! 945: ! 946: ////////////////////////// Priliminary Checks /////////////////////////////// ! 947: ! 948: left.buffer = nil; ! 949: right.buffer = nil; ! 950: node.buffer = nil; ! 951: ! 952: btreePtr = (BTreeControlBlockPtr) filePtr->fcbBTCBPtr; ! 953: ! 954: RequireFileLock(btreePtr->fileRefNum, true); ! 955: ! 956: if ((operation != kBTreeFirstRecord) && ! 957: (operation != kBTreeNextRecord) && ! 958: (operation != kBTreeCurrentRecord) && ! 959: (operation != kBTreePrevRecord) && ! 960: (operation != kBTreeLastRecord)) ! 961: { ! 962: err = fsInvalidIterationMovmentErr; ! 963: goto ErrorExit; ! 964: } ! 965: ! 966: /////////////////////// Find First or Last Record /////////////////////////// ! 967: ! 968: if ((operation == kBTreeFirstRecord) || (operation == kBTreeLastRecord)) ! 969: { ! 970: if (operation == kBTreeFirstRecord) ! 971: nodeNum = btreePtr->firstLeafNode; ! 972: else ! 973: nodeNum = btreePtr->lastLeafNode; ! 974: ! 975: if (nodeNum == 0) ! 976: { ! 977: err = fsBTEmptyErr; ! 978: goto ErrorExit; ! 979: } ! 980: ! 981: err = GetNode(btreePtr, nodeNum, &node); ! 982: M_ExitOnError(err); ! 983: ! 984: if ( ((NodeDescPtr)node.buffer)->kind != kBTLeafNode || ! 985: ((NodeDescPtr)node.buffer)->numRecords <= 0 ) ! 986: { ! 987: err = ReleaseNode(btreePtr, &node); ! 988: M_ExitOnError(err); ! 989: ! 990: err = fsBTInvalidNodeErr; ! 991: goto ErrorExit; ! 992: } ! 993: ! 994: if (operation == kBTreeFirstRecord) ! 995: index = 0; ! 996: else ! 997: index = ((BTNodeDescriptor*) node.buffer)->numRecords - 1; ! 998: ! 999: goto ProcessData; ! 1000: } ! 1001: ! 1002: //////////////////////// Find Iterator Position ///////////////////////////// ! 1003: ! 1004: err = FindIteratorPosition(btreePtr, iterator, &left, &node, &right, ! 1005: &nodeNum, &index, &foundRecord); ! 1006: M_ExitOnError(err); ! 1007: ! 1008: ! 1009: ///////////////////// Find Next Or Previous Record ////////////////////////// ! 1010: ! 1011: if (operation == kBTreePrevRecord) ! 1012: { ! 1013: if (index > 0) ! 1014: { ! 1015: --index; ! 1016: } ! 1017: else ! 1018: { ! 1019: if (left.buffer == nil) ! 1020: { ! 1021: nodeNum = ((NodeDescPtr) node.buffer)->bLink; ! 1022: if ( nodeNum > 0) ! 1023: { ! 1024: err = GetNode(btreePtr, nodeNum, &left); ! 1025: M_ExitOnError(err); ! 1026: } else { ! 1027: err = fsBTStartOfIterationErr; ! 1028: goto ErrorExit; ! 1029: } ! 1030: } ! 1031: // Before we stomp on "right", we'd better release it if needed ! 1032: if (right.buffer != nil) { ! 1033: err = ReleaseNode(btreePtr, &right); ! 1034: M_ExitOnError(err); ! 1035: } ! 1036: right = node; ! 1037: node = left; ! 1038: left.buffer = nil; ! 1039: index = ((NodeDescPtr) node.buffer)->numRecords -1; ! 1040: } ! 1041: } ! 1042: else if (operation == kBTreeNextRecord) ! 1043: { ! 1044: if ((foundRecord != true) && ! 1045: (((NodeDescPtr)node.buffer)->fLink == 0) && ! 1046: (index == ((NodeDescPtr)node.buffer)->numRecords)) ! 1047: { ! 1048: err = fsBTEndOfIterationErr; ! 1049: goto ErrorExit; ! 1050: } ! 1051: ! 1052: // we did not find the record but the index is already positioned correctly ! 1053: if ((foundRecord == false) && (index != ((NodeDescPtr)node.buffer)->numRecords)) ! 1054: goto ProcessData; ! 1055: ! 1056: // we found the record OR we have to look in the next node ! 1057: if (index < ((NodeDescPtr)node.buffer)->numRecords -1) ! 1058: { ! 1059: ++index; ! 1060: } ! 1061: else ! 1062: { ! 1063: if (right.buffer == nil) ! 1064: { ! 1065: nodeNum = ((NodeDescPtr)node.buffer)->fLink; ! 1066: if ( nodeNum > 0) ! 1067: { ! 1068: err = GetNode(btreePtr, nodeNum, &right); ! 1069: M_ExitOnError(err); ! 1070: } else { ! 1071: err = fsBTEndOfIterationErr; ! 1072: goto ErrorExit; ! 1073: } ! 1074: } ! 1075: // Before we stomp on "left", we'd better release it if needed ! 1076: if (left.buffer != nil) { ! 1077: err = ReleaseNode(btreePtr, &left); ! 1078: M_ExitOnError(err); ! 1079: } ! 1080: left = node; ! 1081: node = right; ! 1082: right.buffer = nil; ! 1083: index = 0; ! 1084: } ! 1085: } ! 1086: else // operation == kBTreeCurrentRecord ! 1087: { ! 1088: // make sure we have something... <CS9> ! 1089: if ((foundRecord != true) && ! 1090: (index >= ((NodeDescPtr)node.buffer)->numRecords)) ! 1091: { ! 1092: err = fsBTEndOfIterationErr; ! 1093: goto ErrorExit; ! 1094: } ! 1095: } ! 1096: ! 1097: //////////////////// Process Records Using Callback //////////////////////// ! 1098: ! 1099: ProcessData: ! 1100: err = GetRecordByIndex(btreePtr, node.buffer, index, &keyPtr, &recordPtr, &len); ! 1101: ! 1102: while (err == 0) { ! 1103: if (callBackProc(keyPtr, recordPtr, len, callBackState) == 0) ! 1104: break; ! 1105: ! 1106: if ((index+1) < ((NodeDescPtr)node.buffer)->numRecords) { ! 1107: ++index; ! 1108: } else { ! 1109: if (right.buffer == nil) ! 1110: { ! 1111: nodeNum = ((NodeDescPtr)node.buffer)->fLink; ! 1112: if ( nodeNum > 0) ! 1113: { ! 1114: err = GetNode(btreePtr, nodeNum, &right); ! 1115: M_ExitOnError(err); ! 1116: } else { ! 1117: err = fsBTEndOfIterationErr; ! 1118: break; ! 1119: } ! 1120: } ! 1121: // Before we stomp on "left", we'd better release it if needed ! 1122: if (left.buffer != nil) { ! 1123: err = ReleaseNode(btreePtr, &left); ! 1124: M_ExitOnError(err); ! 1125: } ! 1126: left = node; ! 1127: node = right; ! 1128: right.buffer = nil; ! 1129: index = 0; ! 1130: } ! 1131: err = GetRecordByIndex(btreePtr, node.buffer, index, ! 1132: &keyPtr, &recordPtr, &len); ! 1133: } ! 1134: ! 1135: ! 1136: ///////////////// Update Iterator to Last Item Processed ///////////////////// ! 1137: ! 1138: ! 1139: if (iterator != nil) // first & last have optional iterator ! 1140: { ! 1141: iterator->hint.writeCount = btreePtr->writeCount; ! 1142: iterator->hint.nodeNum = nodeNum; ! 1143: iterator->hint.index = index; ! 1144: iterator->version = 0; ! 1145: ! 1146: BlockMoveData((Ptr)keyPtr, (Ptr)&iterator->key, CalcKeySize(btreePtr, keyPtr)); ! 1147: } ! 1148: M_ExitOnError(err); ! 1149: ! 1150: ! 1151: ///////////////////////////// Release Nodes ///////////////////////////////// ! 1152: ! 1153: err = ReleaseNode(btreePtr, &node); ! 1154: M_ExitOnError(err); ! 1155: ! 1156: if (left.buffer != nil) ! 1157: { ! 1158: err = ReleaseNode(btreePtr, &left); ! 1159: M_ExitOnError(err); ! 1160: } ! 1161: ! 1162: if (right.buffer != nil) ! 1163: { ! 1164: err = ReleaseNode(btreePtr, &right); ! 1165: M_ExitOnError(err); ! 1166: } ! 1167: ! 1168: return noErr; ! 1169: ! 1170: /////////////////////// Error - Clean Up and Exit /////////////////////////// ! 1171: ! 1172: ErrorExit: ! 1173: ! 1174: (void) ReleaseNode(btreePtr, &left); ! 1175: (void) ReleaseNode(btreePtr, &node); ! 1176: (void) ReleaseNode(btreePtr, &right); ! 1177: ! 1178: if (iterator != nil) ! 1179: { ! 1180: iterator->hint.writeCount = 0; ! 1181: iterator->hint.nodeNum = 0; ! 1182: iterator->hint.index = 0; ! 1183: iterator->version = 0; ! 1184: iterator->key.length16 = 0; ! 1185: } ! 1186: ! 1187: if ( err == fsBTEmptyErr || err == fsBTEndOfIterationErr ) ! 1188: err = fsBTRecordNotFoundErr; ! 1189: ! 1190: return err; ! 1191: } ! 1192: ! 1193: ! 1194: //////////////////////////////// BTInsertRecord ///////////////////////////////// ! 1195: ! 1196: OSStatus BTInsertRecord (FCB *filePtr, ! 1197: BTreeIterator *iterator, ! 1198: FSBufferDescriptor *record, ! 1199: UInt16 recordLen ) ! 1200: { ! 1201: OSStatus err; ! 1202: BTreeControlBlockPtr btreePtr; ! 1203: TreePathTable treePathTable; ! 1204: SInt32 nodesNeeded; ! 1205: BlockDescriptor nodeRec; ! 1206: UInt32 insertNodeNum; ! 1207: UInt16 index; ! 1208: Boolean recordFit; ! 1209: ! 1210: ! 1211: ////////////////////////// Priliminary Checks /////////////////////////////// ! 1212: ! 1213: nodeRec.buffer = nil; // so we can call ReleaseNode ! 1214: ! 1215: err = CheckInsertParams (filePtr, iterator, record, recordLen); ! 1216: if (err != noErr) ! 1217: return err; ! 1218: ! 1219: LogStartTime(kTraceInsertBTreeRecord); ! 1220: ! 1221: btreePtr = (BTreeControlBlockPtr) filePtr->fcbBTCBPtr; ! 1222: ! 1223: RequireFileLock(btreePtr->fileRefNum, false); ! 1224: ! 1225: ! 1226: ///////////////////////// Find Insert Position ////////////////////////////// ! 1227: ! 1228: // always call SearchTree for Insert ! 1229: err = SearchTree (btreePtr, &iterator->key, treePathTable, &insertNodeNum, &nodeRec, &index); ! 1230: ! 1231: switch (err) // set/replace/insert decision point ! 1232: { ! 1233: case noErr: err = fsBTDuplicateRecordErr; ! 1234: goto ErrorExit; ! 1235: ! 1236: case fsBTRecordNotFoundErr: break; ! 1237: ! 1238: case fsBTEmptyErr: // if tree empty add 1st leaf node ! 1239: ! 1240: if (btreePtr->freeNodes == 0) ! 1241: { ! 1242: err = ExtendBTree (btreePtr, btreePtr->totalNodes + 1); ! 1243: M_ExitOnError (err); ! 1244: } ! 1245: ! 1246: err = AllocateNode (btreePtr, &insertNodeNum); ! 1247: M_ExitOnError (err); ! 1248: ! 1249: err = GetNewNode (btreePtr, insertNodeNum, &nodeRec); ! 1250: M_ExitOnError (err); ! 1251: ! 1252: ((NodeDescPtr)nodeRec.buffer)->kind = kBTLeafNode; ! 1253: ((NodeDescPtr)nodeRec.buffer)->height = 1; ! 1254: ! 1255: recordFit = InsertKeyRecord (btreePtr, nodeRec.buffer, 0, ! 1256: &iterator->key, KeyLength(btreePtr, &iterator->key), ! 1257: record->bufferAddress, recordLen ); ! 1258: if (recordFit != true) ! 1259: { ! 1260: err = fsBTRecordTooLargeErr; ! 1261: goto ErrorExit; ! 1262: } ! 1263: ! 1264: err = UpdateNode (btreePtr, &nodeRec, 0, kLockTransaction); ! 1265: M_ExitOnError (err); ! 1266: ! 1267: // update BTreeControlBlock ! 1268: btreePtr->treeDepth = 1; ! 1269: btreePtr->rootNode = insertNodeNum; ! 1270: btreePtr->firstLeafNode = insertNodeNum; ! 1271: btreePtr->lastLeafNode = insertNodeNum; ! 1272: M_BTreeHeaderDirty (btreePtr); ! 1273: ! 1274: goto Success; ! 1275: ! 1276: default: goto ErrorExit; ! 1277: } ! 1278: ! 1279: if (index > 0) ! 1280: { ! 1281: recordFit = InsertKeyRecord (btreePtr, nodeRec.buffer, index, ! 1282: &iterator->key, KeyLength(btreePtr, &iterator->key), ! 1283: record->bufferAddress, recordLen); ! 1284: if (recordFit == true) ! 1285: { ! 1286: err = UpdateNode (btreePtr, &nodeRec, 0, kLockTransaction); ! 1287: M_ExitOnError (err); ! 1288: ! 1289: goto Success; ! 1290: } ! 1291: } ! 1292: ! 1293: /////////////////////// Extend File If Necessary //////////////////////////// ! 1294: ! 1295: nodesNeeded = btreePtr->treeDepth + 1 - btreePtr->freeNodes; //�� math limit ! 1296: if (nodesNeeded > 0) ! 1297: { ! 1298: nodesNeeded += btreePtr->totalNodes; ! 1299: if (nodesNeeded > CalcMapBits (btreePtr)) // we'll need to add a map node too! ! 1300: ++nodesNeeded; ! 1301: ! 1302: err = ExtendBTree (btreePtr, nodesNeeded); ! 1303: M_ExitOnError (err); ! 1304: } ! 1305: ! 1306: // no need to delete existing record ! 1307: ! 1308: err = InsertTree (btreePtr, treePathTable, &iterator->key, record->bufferAddress, ! 1309: recordLen, &nodeRec, index, 1, kInsertRecord, &insertNodeNum); ! 1310: M_ExitOnError (err); ! 1311: ! 1312: ! 1313: //////////////////////////////// Success //////////////////////////////////// ! 1314: ! 1315: Success: ! 1316: ++btreePtr->writeCount; ! 1317: ++btreePtr->leafRecords; ! 1318: M_BTreeHeaderDirty (btreePtr); ! 1319: ! 1320: // create hint ! 1321: iterator->hint.writeCount = btreePtr->writeCount; ! 1322: iterator->hint.nodeNum = insertNodeNum; ! 1323: iterator->hint.index = 0; // unused ! 1324: iterator->hint.reserved1 = 0; ! 1325: iterator->hint.reserved2 = 0; ! 1326: ! 1327: LogEndTime(kTraceInsertBTreeRecord, noErr); ! 1328: ! 1329: return noErr; ! 1330: ! 1331: ! 1332: ////////////////////////////// Error Exit /////////////////////////////////// ! 1333: ! 1334: ErrorExit: ! 1335: ! 1336: (void) ReleaseNode (btreePtr, &nodeRec); ! 1337: ! 1338: iterator->hint.writeCount = 0; ! 1339: iterator->hint.nodeNum = 0; ! 1340: iterator->hint.index = 0; ! 1341: iterator->hint.reserved1 = 0; ! 1342: iterator->hint.reserved2 = 0; ! 1343: ! 1344: if (err == fsBTEmptyErr) ! 1345: err = fsBTRecordNotFoundErr; ! 1346: ! 1347: LogEndTime(kTraceInsertBTreeRecord, err); ! 1348: ! 1349: return err; ! 1350: } ! 1351: ! 1352: ! 1353: //////////////////////////////// BTReplaceRecord //////////////////////////////// ! 1354: ! 1355: OSStatus BTReplaceRecord (FCB *filePtr, ! 1356: BTreeIterator *iterator, ! 1357: FSBufferDescriptor *record, ! 1358: UInt16 recordLen ) ! 1359: { ! 1360: OSStatus err; ! 1361: BTreeControlBlockPtr btreePtr; ! 1362: TreePathTable treePathTable; ! 1363: SInt32 nodesNeeded; ! 1364: BlockDescriptor nodeRec; ! 1365: UInt32 insertNodeNum; ! 1366: UInt16 index; ! 1367: Boolean recordFit; ! 1368: Boolean validHint; ! 1369: ! 1370: ! 1371: ////////////////////////// Priliminary Checks /////////////////////////////// ! 1372: ! 1373: nodeRec.buffer = nil; // so we can call ReleaseNode ! 1374: ! 1375: err = CheckInsertParams (filePtr, iterator, record, recordLen); ! 1376: if (err != noErr) ! 1377: return err; ! 1378: ! 1379: LogStartTime(kTraceReplaceBTreeRecord); ! 1380: ! 1381: btreePtr = (BTreeControlBlockPtr) filePtr->fcbBTCBPtr; ! 1382: ! 1383: RequireFileLock(btreePtr->fileRefNum, false); ! 1384: ! 1385: ////////////////////////////// Take A Hint ////////////////////////////////// ! 1386: ! 1387: err = IsItAHint (btreePtr, iterator, &validHint); ! 1388: M_ExitOnError (err); ! 1389: ! 1390: if (validHint) ! 1391: { ! 1392: insertNodeNum = iterator->hint.nodeNum; ! 1393: ! 1394: err = GetNode (btreePtr, insertNodeNum, &nodeRec); ! 1395: if( err == noErr ) ! 1396: { ! 1397: err = TrySimpleReplace (btreePtr, nodeRec.buffer, iterator, record, recordLen, &recordFit); ! 1398: M_ExitOnError (err); ! 1399: ! 1400: if (recordFit) ! 1401: { ! 1402: err = UpdateNode (btreePtr, &nodeRec, 0, 0); ! 1403: M_ExitOnError (err); ! 1404: ! 1405: ++btreePtr->numValidHints; ! 1406: ! 1407: goto Success; ! 1408: } ! 1409: else ! 1410: { ! 1411: (void) BTInvalidateHint( iterator ); ! 1412: } ! 1413: ! 1414: err = ReleaseNode (btreePtr, &nodeRec); ! 1415: M_ExitOnError (err); ! 1416: } ! 1417: else ! 1418: { ! 1419: (void) BTInvalidateHint( iterator ); ! 1420: } ! 1421: } ! 1422: ! 1423: ! 1424: ////////////////////////////// Get A Clue /////////////////////////////////// ! 1425: ! 1426: err = SearchTree (btreePtr, &iterator->key, treePathTable, &insertNodeNum, &nodeRec, &index); ! 1427: M_ExitOnError (err); // record must exit for Replace ! 1428: ! 1429: // optimization - if simple replace will work then don't extend btree ! 1430: // �� if we tried this before, and failed because it wouldn't fit then we shouldn't try this again... ! 1431: ! 1432: err = TrySimpleReplace (btreePtr, nodeRec.buffer, iterator, record, recordLen, &recordFit); ! 1433: M_ExitOnError (err); ! 1434: ! 1435: if (recordFit) ! 1436: { ! 1437: err = UpdateNode (btreePtr, &nodeRec, 0, 0); ! 1438: M_ExitOnError (err); ! 1439: ! 1440: goto Success; ! 1441: } ! 1442: ! 1443: ! 1444: //////////////////////////// Make Some Room ///////////////////////////////// ! 1445: ! 1446: nodesNeeded = btreePtr->treeDepth + 1 - btreePtr->freeNodes; //�� math limit ! 1447: if (nodesNeeded > 0) ! 1448: { ! 1449: nodesNeeded += btreePtr->totalNodes; ! 1450: if (nodesNeeded > CalcMapBits (btreePtr)) // we'll need to add a map node too! ! 1451: ++nodesNeeded; ! 1452: ! 1453: err = ExtendBTree (btreePtr, nodesNeeded); ! 1454: M_ExitOnError (err); ! 1455: } ! 1456: ! 1457: ! 1458: DeleteRecord (btreePtr, nodeRec.buffer, index); // delete existing key/record ! 1459: ! 1460: err = InsertTree (btreePtr, treePathTable, &iterator->key, record->bufferAddress, ! 1461: recordLen, &nodeRec, index, 1, kReplaceRecord, &insertNodeNum); ! 1462: M_ExitOnError (err); ! 1463: ! 1464: ++btreePtr->writeCount; /* writeCount changes only if the tree structure changed */ ! 1465: ! 1466: Success: ! 1467: // create hint ! 1468: iterator->hint.writeCount = btreePtr->writeCount; ! 1469: iterator->hint.nodeNum = insertNodeNum; ! 1470: iterator->hint.index = 0; // unused ! 1471: iterator->hint.reserved1 = 0; ! 1472: iterator->hint.reserved2 = 0; ! 1473: ! 1474: LogEndTime(kTraceReplaceBTreeRecord, noErr); ! 1475: ! 1476: return noErr; ! 1477: ! 1478: ! 1479: ////////////////////////////// Error Exit /////////////////////////////////// ! 1480: ! 1481: ErrorExit: ! 1482: ! 1483: (void) ReleaseNode (btreePtr, &nodeRec); ! 1484: ! 1485: iterator->hint.writeCount = 0; ! 1486: iterator->hint.nodeNum = 0; ! 1487: iterator->hint.index = 0; ! 1488: iterator->hint.reserved1 = 0; ! 1489: iterator->hint.reserved2 = 0; ! 1490: ! 1491: ! 1492: LogEndTime(kTraceReplaceBTreeRecord, err); ! 1493: ! 1494: return err; ! 1495: } ! 1496: ! 1497: ! 1498: ! 1499: //////////////////////////////// BTDeleteRecord ///////////////////////////////// ! 1500: ! 1501: OSStatus BTDeleteRecord (FCB *filePtr, ! 1502: BTreeIterator *iterator ) ! 1503: { ! 1504: OSStatus err; ! 1505: BTreeControlBlockPtr btreePtr; ! 1506: TreePathTable treePathTable; ! 1507: BlockDescriptor nodeRec; ! 1508: UInt32 nodeNum; ! 1509: UInt16 index; ! 1510: ! 1511: LogStartTime(kTraceDeleteBTreeRecord); ! 1512: ! 1513: ////////////////////////// Priliminary Checks /////////////////////////////// ! 1514: ! 1515: nodeRec.buffer = nil; // so we can call ReleaseNode ! 1516: ! 1517: M_ReturnErrorIf (filePtr == nil, paramErr); ! 1518: M_ReturnErrorIf (iterator == nil, paramErr); ! 1519: ! 1520: btreePtr = (BTreeControlBlockPtr) filePtr->fcbBTCBPtr; ! 1521: if (btreePtr == nil) ! 1522: { ! 1523: err = fsBTInvalidFileErr; ! 1524: goto ErrorExit; ! 1525: } ! 1526: ! 1527: RequireFileLock(btreePtr->fileRefNum, false); ! 1528: ! 1529: ! 1530: /////////////////////////////// Find Key //////////////////////////////////// ! 1531: ! 1532: //�� check hint for simple delete case (index > 0, numRecords > 2) ! 1533: ! 1534: err = SearchTree (btreePtr, &iterator->key, treePathTable, &nodeNum, &nodeRec, &index); ! 1535: M_ExitOnError (err); // record must exit for Delete ! 1536: ! 1537: ! 1538: ///////////////////////////// Delete Record ///////////////////////////////// ! 1539: ! 1540: err = DeleteTree (btreePtr, treePathTable, &nodeRec, index, 1); ! 1541: M_ExitOnError (err); ! 1542: ! 1543: ++btreePtr->writeCount; ! 1544: --btreePtr->leafRecords; ! 1545: M_BTreeHeaderDirty (btreePtr); ! 1546: ! 1547: iterator->hint.nodeNum = 0; ! 1548: ! 1549: LogEndTime(kTraceDeleteBTreeRecord, noErr); ! 1550: ! 1551: return noErr; ! 1552: ! 1553: ////////////////////////////// Error Exit /////////////////////////////////// ! 1554: ! 1555: ErrorExit: ! 1556: (void) ReleaseNode (btreePtr, &nodeRec); ! 1557: ! 1558: LogEndTime(kTraceDeleteBTreeRecord, err); ! 1559: ! 1560: return err; ! 1561: } ! 1562: ! 1563: ! 1564: ! 1565: OSStatus BTGetInformation (FCB *filePtr, ! 1566: UInt16 version, ! 1567: BTreeInfoRec *info ) ! 1568: { ! 1569: #pragma unused (version) ! 1570: ! 1571: BTreeControlBlockPtr btreePtr; ! 1572: ! 1573: ! 1574: M_ReturnErrorIf (filePtr == nil, paramErr); ! 1575: ! 1576: btreePtr = (BTreeControlBlockPtr) filePtr->fcbBTCBPtr; ! 1577: ! 1578: RequireFileLock(btreePtr->fileRefNum, true); ! 1579: ! 1580: M_ReturnErrorIf (btreePtr == nil, fsBTInvalidFileErr); ! 1581: M_ReturnErrorIf (info == nil, paramErr); ! 1582: ! 1583: //�� check version? ! 1584: ! 1585: info->nodeSize = btreePtr->nodeSize; ! 1586: info->maxKeyLength = btreePtr->maxKeyLength; ! 1587: info->treeDepth = btreePtr->treeDepth; ! 1588: info->numRecords = btreePtr->leafRecords; ! 1589: info->numNodes = btreePtr->totalNodes; ! 1590: info->numFreeNodes = btreePtr->freeNodes; ! 1591: info->lastfsync = btreePtr->lastfsync; ! 1592: info->reserved = 0; ! 1593: ! 1594: return noErr; ! 1595: } ! 1596: ! 1597: ! 1598: ! 1599: /*------------------------------------------------------------------------------- ! 1600: Routine: BTFlushPath - Flush BTreeControlBlock to Header Node. ! 1601: ! 1602: Function: Brief_description_of_the_function_and_any_side_effects ! 1603: ! 1604: ! 1605: Input: pathPtr - pointer to path control block for B*Tree file to flush ! 1606: ! 1607: Output: none ! 1608: ! 1609: Result: noErr - success ! 1610: != noErr - failure ! 1611: -------------------------------------------------------------------------------*/ ! 1612: ! 1613: OSStatus BTFlushPath (FCB *filePtr) ! 1614: { ! 1615: OSStatus err; ! 1616: BTreeControlBlockPtr btreePtr; ! 1617: ! 1618: ! 1619: LogStartTime(kTraceFlushBTree); ! 1620: ! 1621: M_ReturnErrorIf (filePtr == nil, paramErr); ! 1622: ! 1623: btreePtr = (BTreeControlBlockPtr) filePtr->fcbBTCBPtr; ! 1624: ! 1625: M_ReturnErrorIf (btreePtr == nil, fsBTInvalidFileErr); ! 1626: ! 1627: RequireFileLock(btreePtr->fileRefNum, false); ! 1628: ! 1629: err = UpdateHeader (btreePtr); ! 1630: ! 1631: LogEndTime(kTraceFlushBTree, err); ! 1632: ! 1633: return err; ! 1634: } ! 1635: ! 1636: ! 1637: ! 1638: /*------------------------------------------------------------------------------- ! 1639: Routine: BTInvalidateHint - Invalidates the hint within a BTreeInterator. ! 1640: ! 1641: Function: Invalidates the hint within a BTreeInterator. ! 1642: ! 1643: ! 1644: Input: iterator - pointer to BTreeIterator ! 1645: ! 1646: Output: iterator - iterator with the hint.nodeNum cleared ! 1647: ! 1648: Result: noErr - success ! 1649: paramErr - iterator == nil ! 1650: -------------------------------------------------------------------------------*/ ! 1651: ! 1652: ! 1653: OSStatus BTInvalidateHint (BTreeIterator *iterator ) ! 1654: { ! 1655: if (iterator == nil) ! 1656: return paramErr; ! 1657: ! 1658: iterator->hint.nodeNum = 0; ! 1659: ! 1660: return noErr; ! 1661: } ! 1662: ! 1663: ! 1664: ! 1665: ! 1666: /*------------------------------------------------------------------------------- ! 1667: Routine: BTGetLastSync ! 1668: ! 1669: Function: Returns the last time that this btree was flushed, does not include header. ! 1670: ! 1671: Input: filePtr - pointer file control block ! 1672: ! 1673: Output: lastfsync - time in seconds of last update ! 1674: ! 1675: Result: noErr - success ! 1676: paramErr - iterator == nil ! 1677: -------------------------------------------------------------------------------*/ ! 1678: ! 1679: ! 1680: OSStatus BTGetLastSync (FCB *filePtr, ! 1681: UInt32 *lastsync) ! 1682: { ! 1683: BTreeControlBlockPtr btreePtr; ! 1684: ! 1685: ! 1686: M_ReturnErrorIf (filePtr == nil, paramErr); ! 1687: ! 1688: btreePtr = (BTreeControlBlockPtr) filePtr->fcbBTCBPtr; ! 1689: ! 1690: /* Maybe instead of requiring a lock..an atomic set might be more appropriate */ ! 1691: RequireFileLock(btreePtr->fileRefNum, true); ! 1692: ! 1693: M_ReturnErrorIf (btreePtr == nil, fsBTInvalidFileErr); ! 1694: M_ReturnErrorIf (lastsync == nil, paramErr); ! 1695: ! 1696: *lastsync = btreePtr->lastfsync; ! 1697: ! 1698: return noErr; ! 1699: } ! 1700: ! 1701: ! 1702: ! 1703: ! 1704: /*------------------------------------------------------------------------------- ! 1705: Routine: BTSetLastSync ! 1706: ! 1707: Function: Sets the last time that this btree was flushed, does not include header. ! 1708: ! 1709: ! 1710: Input: fcb - pointer file control block ! 1711: ! 1712: Output: lastfsync - time in seconds of last update ! 1713: ! 1714: Result: noErr - success ! 1715: paramErr - iterator == nil ! 1716: -------------------------------------------------------------------------------*/ ! 1717: ! 1718: ! 1719: OSStatus BTSetLastSync (FCB *filePtr, ! 1720: UInt32 lastsync) ! 1721: { ! 1722: BTreeControlBlockPtr btreePtr; ! 1723: ! 1724: ! 1725: M_ReturnErrorIf (filePtr == nil, paramErr); ! 1726: ! 1727: btreePtr = (BTreeControlBlockPtr) filePtr->fcbBTCBPtr; ! 1728: ! 1729: /* Maybe instead of requiring a lock..an atomic set might be more appropriate */ ! 1730: RequireFileLock(btreePtr->fileRefNum, true); ! 1731: ! 1732: M_ReturnErrorIf (btreePtr == nil, fsBTInvalidFileErr); ! 1733: M_ReturnErrorIf (lastsync == nil, paramErr); ! 1734: ! 1735: btreePtr->lastfsync = lastsync; ! 1736: ! 1737: return noErr; ! 1738: } ! 1739: ! 1740:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.