2 * Copyright (c) 2000-2003 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
28 Contains: Implementation of public interface routines for B-tree manager.
32 Written by: Gordon Sheridan and Bill Bruffey
34 Copyright: © 1992-1999 by Apple Computer, Inc., all rights reserved.
40 Other Contact: Mark Day
42 Technology: File Systems
50 Change History (most recent first):
51 <MOSXS> 9/22/99 ser Added routines BTGetLastSync and BTSetLastSync
52 <MOSXS> 6/1/99 djb Sync up with Mac OS 8.6.
53 <MOSXS> 6/30/98 djb In BTOpenPath make sure nodes are contiguous on disk (radar #2249539).
54 <MOSXS> 4/15/98 djb In BTOpenPath need to clear nodeRec.buffer if GetBlockProc fails.
55 <MOSXS> 4/11/98 djb Add RequireFileLock checking to all external entry points.
57 <MOSXS> 03/23/98 djb In BTOpenPath use kTrashBlock option when releasing the header so
58 that we get a full node when we call GetNode.
60 <CS9> 12/12/97 djb Radar #2202682, BTIterateRecord with kBTreeCurrentRecord was not
61 checking if we had a record and could call BlockMove with an
62 uninitialize source pointer (causing a bus error).
63 <CS8> 10/24/97 msd In BTIterateRecord, when moving to the previous or next record
64 and we have to move to another node, see if we need to release
65 the node about to be "shifted out" (opposite sibling of the
66 direction we need to move).
67 <CS7> 7/25/97 DSH BTSearchRecord now takes a heuristicHint, nodeNum, and tries it
68 before calling SearchBTree
69 <CS6> 7/24/97 djb GetBlockProc now take a file refnum instead of an FCB ptr.
70 <CS5> 7/22/97 djb Move trace points from BTreeWrapper.c to here.
71 <CS4> 7/21/97 djb LogEndTime now takes an error code.
72 <CS3> 7/16/97 DSH FilesInternal.i renamed FileMgrInternal.i to avoid name
74 <CS2> 5/19/97 djb Add summary traces to BTIterateRecord.
75 <CS1> 4/23/97 djb first checked in
77 <HFS7> 2/19/97 djb Enable variable sized index keys for HFS+ volumes. Added node
78 cache to support nodes larger than 512 bytes.
79 <HFS6> 1/27/97 djb Calls to InsertTree and DeleteTree are now recursive (to support
80 variable sized index keys).
81 <HFS5> 1/13/97 djb Added support for getting current record to BTIterateRecord.
82 <HFS4> 1/6/97 djb Initialize "BigKeys" attribute in BTOpen.
83 <HFS3> 1/3/97 djb Added support for large keys.
84 <HFS2> 12/23/96 djb On exit map fsBTEmptyErr and fsBTEndOfIterationErr to
85 fsBTRecordNotFoundErr.
86 <HFS1> 12/19/96 djb first checked in
88 History applicable to original Scarecrow Design:
90 <13> 10/25/96 ser Changing for new VFPI
91 <12> 10/18/96 ser Converting over VFPI changes
92 <11> 9/17/96 dkh More BTree statistics. Modified hint checks to not bail out when
93 an error is returned from GetNode.
94 <10> 9/16/96 dkh Revised BTree statistics.
95 <9> 8/23/96 dkh Remove checks for multiple paths to BTree file. Need to add
96 equivalent mechanism later.
97 <8> 6/20/96 dkh Radar #1358740. Switch from using Pools to debug MemAllocators.
98 <7> 3/14/96 jev Fix BTreeSetRecord, recordFound was not set for the case of a
99 simple replace causing the leafRecords count to get bumped even
100 though we didn't have to add a record.
101 <6> 3/1/96 prp Fix lint problems. Bug in BTSetRecord that does not initialize
103 <5> 1/22/96 dkh Add #include Memory.h
104 <4> 1/10/96 msd Use the real function names from Math64.i.
105 <3> 1/4/96 jev Fix BTItererateRecord for the condition when the iterator
106 position routine does not find the record and we are looking for
107 the next record. In such a case, if the node's forrward link is
108 non-zero, we have to keep iterating next and not return
109 fsBTEndOfIterationErr error.
110 <2> 12/7/95 dkh D10E2 build. Changed usage of Ref data type to LogicalAddress.
111 <1> 10/18/95 rst Moved from Scarecrow project.
113 <24> 7/18/95 mbb Change MoveData & ClearBytes to BlockMoveData & BlockZero.
114 <23> 1/31/95 prp GetBlockProc interface uses a 64 bit node number.
115 <22> 1/12/95 wjk Adopt Model FileSystem changes in D5.
116 <21> 11/16/94 prp Add IsItAHint routine and use it whenever hint's node number was
118 <20> 11/10/94 prp BTGetInfo name collides with the same name in FileManagerPriv.i.
119 Change it to BTGetInformation.
120 <19> 9/30/94 prp Get in sync with D2 interface changes.
121 <18> 7/22/94 wjk Convert to the new set of header files.
122 <17> 12/9/93 wjk Cleanup usage of char, Byte, int8, UInt8, etc.
123 <16> 12/2/93 wjk Move from Makefiles to BuildFiles. Fit into the ModernOS and
125 <15> 11/30/93 wjk Move from Makefiles to BuildFiles. Fit into the ModernOS and
127 <14> 9/30/93 gs Rename E_NoGetNodeProc and E_NoReleaseNodeProc to
129 <13> 8/31/93 prp Use Set64U instead of Set64.
130 <12> 8/16/93 prp In BTSearchRecord, if the input hint found the node and record,
131 set the local nodeNum variable correctly so that the resultant
132 iterator gets set correctly.
133 <11> 7/1/93 gs Fix bug in BTIterateRecord related to kBTreePrevRecord
135 <10> 6/2/93 gs Update for changes to FSErrors.h and add some comments.
136 <9> 5/24/93 gs Fix bug in BTInsert/Set/ReplaceRecord which didn't set node hint
137 properly in some cases.
138 <8> 5/24/93 gs Do NOT map fsBTEmptyErr to fsBTRecordNotFoundErr in BTSearchRecord.
139 <7> 5/24/93 gs Rename BTFlush to BTFlushPath.
140 <6> 5/21/93 gs Add hint optimization to Set/Replace routines.
141 <5> 5/10/93 gs Remove Panic from BTInitialize for small logicalEOF. Implement
142 Insert, Set, Replace, and Delete.
143 <4> 3/23/93 gs Finish BTInitialize.
144 <3> 2/8/93 gs Implement BTSearchRecord and BTIterateRecord.
145 <2> 12/8/92 gs Implement Open and Close routines.
146 <1> 11/15/92 gs first checked in
150 #include "../headers/BTreesPrivate.h"
153 * The amount that the BTree header leaf count can be wrong before we assume
154 * it is in an infinite loop.
156 #define kNumLeafRecSlack 10
158 /* BTree accessor routines */
159 extern OSStatus
GetBTreeBlock(FileReference vp
, UInt32 blockNum
, GetBlockOptions options
, BlockDescriptor
*block
);
160 extern OSStatus
SetBTreeBlockSize(FileReference vp
, ByteCount blockSize
, ItemCount minBlockCount
);
161 extern OSStatus
ExtendBTreeFile(FileReference vp
, FSSize minEOF
, FSSize maxEOF
);
162 extern OSStatus
ReleaseBTreeBlock(FileReference vp
, BlockDescPtr blockPtr
, ReleaseBlockOptions options
);
164 //////////////////////////////////// Globals ////////////////////////////////////
167 /////////////////////////// BTree Module Entry Points ///////////////////////////
171 /*-------------------------------------------------------------------------------
172 Routine: BTOpenPath - Open a file for access as a B*Tree.
174 Function: Create BTree control block for a file, if necessary. Validates the
175 file to be sure it looks like a BTree file.
178 Input: filePtr - pointer to file to open as a B-tree
179 keyCompareProc - pointer to client's KeyCompare function
181 Result: noErr - success
182 paramErr - required ptr was nil
186 -------------------------------------------------------------------------------*/
188 OSStatus
BTOpenPath(FCB
*filePtr
, KeyCompareProcPtr keyCompareProc
)
191 BTreeControlBlockPtr btreePtr
;
195 ////////////////////// Preliminary Error Checking ///////////////////////////
197 if ( filePtr
== nil
)
203 * Subsequent opens allow key compare proc to be changed.
205 if ( filePtr
->fcbBTCBPtr
!= nil
&& keyCompareProc
!= nil
) {
206 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
207 btreePtr
->keyCompareProc
= keyCompareProc
;
211 if ( filePtr
->fcbEOF
< kMinNodeSize
)
212 return fsBTInvalidFileErr
;
215 //////////////////////// Allocate Control Block /////////////////////////////
217 btreePtr
= (BTreeControlBlock
*) NewPtrSysClear( sizeof( BTreeControlBlock
) );
220 Panic ("\pBTOpen: no memory for btreePtr.");
224 btreePtr
->getBlockProc
= GetBTreeBlock
;
225 btreePtr
->releaseBlockProc
= ReleaseBTreeBlock
;
226 btreePtr
->setEndOfForkProc
= ExtendBTreeFile
;
227 btreePtr
->keyCompareProc
= keyCompareProc
;
229 /////////////////////////// Read Header Node ////////////////////////////////
231 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
232 btreePtr
->fileRefNum
= GetFileRefNumFromFCB(filePtr
);
233 filePtr
->fcbBTCBPtr
= (Ptr
) btreePtr
; // attach btree cb to file
235 /* The minimum node size is the physical block size */
236 nodeRec
.blockSize
= VTOHFS(btreePtr
->fileRefNum
)->hfs_phys_block_size
;
238 /* Start with the allocation block size for regular files. */
239 if (FTOC(filePtr
)->c_fileid
>= kHFSFirstUserCatalogNodeID
)
241 nodeRec
.blockSize
= FCBTOVCB(filePtr
)->blockSize
;
243 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
245 // it is now safe to call M_ExitOnError (err)
247 err
= SetBTreeBlockSize (btreePtr
->fileRefNum
, nodeRec
.blockSize
, 1);
251 err
= GetBTreeBlock(btreePtr
->fileRefNum
,
257 nodeRec
.buffer
= nil
;
258 nodeRec
.blockHeader
= nil
;
259 Panic("\pBTOpen: getNodeProc returned error getting header node.");
262 ++btreePtr
->numGetNodes
;
263 header
= (BTHeaderRec
*) ((u_long
)nodeRec
.buffer
+ sizeof(BTNodeDescriptor
));
266 ///////////////////////////// verify header /////////////////////////////////
268 err
= VerifyHeader (filePtr
, header
);
272 ///////////////////// Initalize fields from header //////////////////////////
274 PanicIf ( (FCBTOVCB(filePtr
)->vcbSigWord
!= 0x4244) && (header
->nodeSize
== 512), "\p BTOpenPath: wrong node size for HFS+ volume!"); // 0x4244 = 'BD'
276 btreePtr
->treeDepth
= header
->treeDepth
;
277 btreePtr
->rootNode
= header
->rootNode
;
278 btreePtr
->leafRecords
= header
->leafRecords
;
279 btreePtr
->firstLeafNode
= header
->firstLeafNode
;
280 btreePtr
->lastLeafNode
= header
->lastLeafNode
;
281 btreePtr
->nodeSize
= header
->nodeSize
;
282 btreePtr
->maxKeyLength
= header
->maxKeyLength
;
283 btreePtr
->totalNodes
= header
->totalNodes
;
284 btreePtr
->freeNodes
= header
->freeNodes
;
285 if (FTOC(filePtr
)->c_fileid
>= kHFSFirstUserCatalogNodeID
)
286 filePtr
->ff_clumpsize
= header
->clumpSize
;
287 btreePtr
->btreeType
= header
->btreeType
;
289 btreePtr
->keyCompareType
= header
->keyCompareType
;
291 btreePtr
->attributes
= header
->attributes
;
293 if ( btreePtr
->maxKeyLength
> 40 )
294 btreePtr
->attributes
|= (kBTBigKeysMask
+ kBTVariableIndexKeysMask
); //\80\80 we need a way to save these attributes
296 /////////////////////// Initialize dynamic fields ///////////////////////////
298 btreePtr
->version
= kBTreeVersion
;
300 btreePtr
->writeCount
= 1;
302 /////////////////////////// Check Header Node ///////////////////////////////
304 // set kBadClose attribute bit, and UpdateNode
306 /* b-tree node size must be at least as big as the physical block size */
307 if (btreePtr
->nodeSize
< nodeRec
.blockSize
)
310 * If this tree has any records or the media is writeable then
311 * we cannot mount using the current physical block size.
313 if (btreePtr
->leafRecords
> 0 ||
314 VTOHFS(btreePtr
->fileRefNum
)->hfs_flags
& HFS_WRITEABLE_MEDIA
)
316 err
= fsBTBadNodeSize
;
321 // if nodeSize Matches then we don't need to release, just CheckNode
322 if ( btreePtr
->nodeSize
== nodeRec
.blockSize
)
324 err
= CheckNode (btreePtr
, nodeRec
.buffer
);
326 VTOVCB(btreePtr
->fileRefNum
)->vcbFlags
|= kHFS_DamagedVolume
;
331 err
= SetBTreeBlockSize (btreePtr
->fileRefNum
, btreePtr
->nodeSize
, 32);
335 * Need to use kTrashBlock option to force the
336 * buffer cache to read the entire node
338 err
= ReleaseBTreeBlock(btreePtr
->fileRefNum
, &nodeRec
, kTrashBlock
);
339 ++btreePtr
->numReleaseNodes
;
342 err
= GetNode (btreePtr
, kHeaderNodeNum
, &nodeRec
); // calls CheckNode...
346 //\80\80 total nodes * node size <= LEOF?
349 err
= ReleaseNode (btreePtr
, &nodeRec
);
353 * Under Mac OS, b-tree nodes can be non-contiguous on disk when the
354 * allocation block size is smaller than the b-tree node size.
356 * If journaling is turned on for this volume we can't deal with this
357 * situation and so we bail out. If journaling isn't on it's ok as
358 * hfs_strategy_fragmented() deals with it. Journaling can't support
359 * this because it assumes that if you give it a block that it's
360 * contiguous on disk.
362 if ( FCBTOHFS(filePtr
)->jnl
&& !NodesAreContiguous(FCBTOVCB(filePtr
), filePtr
, btreePtr
->nodeSize
) ) {
363 return fsBTInvalidNodeErr
;
366 //////////////////////////////// Success ////////////////////////////////////
368 //\80\80 align LEOF to multiple of node size? - just on close
373 /////////////////////// Error - Clean up and Exit ///////////////////////////
377 filePtr
->fcbBTCBPtr
= nil
;
378 (void) ReleaseNode (btreePtr
, &nodeRec
);
379 DisposePtr( (Ptr
) btreePtr
);
386 /*-------------------------------------------------------------------------------
387 Routine: BTClosePath - Flush BTree Header and Deallocate Memory for BTree.
389 Function: Flush the BTreeControlBlock fields to header node, and delete BTree control
390 block and key descriptor associated with the file if filePtr is last
391 path of type kBTreeType ('btre').
394 Input: filePtr - pointer to file to delete BTree control block for.
396 Result: noErr - success
399 -------------------------------------------------------------------------------*/
401 OSStatus
BTClosePath (FCB
*filePtr
)
404 BTreeControlBlockPtr btreePtr
;
406 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
409 return fsBTInvalidFileErr
;
411 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
413 ////////////////////// Check for other BTree Paths //////////////////////////
415 btreePtr
->attributes
&= ~kBTBadCloseMask
; // clear "bad close" attribute bit
416 err
= UpdateHeader (btreePtr
, true);
419 DisposePtr( (Ptr
) btreePtr
);
420 filePtr
->fcbBTCBPtr
= nil
;
424 /////////////////////// Error - Clean Up and Exit ///////////////////////////
433 /*-------------------------------------------------------------------------------
434 Routine: BTSearchRecord - Search BTree for a record with a matching key.
436 Function: Search for position in B*Tree indicated by searchKey. If a valid node hint
437 is provided, it will be searched first, then SearchTree will be called.
438 If a BTreeIterator is provided, it will be set to the position found as
439 a result of the search. If a record exists at that position, and a BufferDescriptor
440 is supplied, the record will be copied to the buffer (as much as will fit),
441 and recordLen will be set to the length of the record.
443 If an error other than fsBTRecordNotFoundErr occurs, the BTreeIterator, if any,
444 is invalidated, and recordLen is set to 0.
447 Input: pathPtr - pointer to path for BTree file.
448 searchKey - pointer to search key to match.
449 hintPtr - pointer to hint (may be nil)
451 Output: record - pointer to BufferDescriptor containing record
452 recordLen - length of data at recordPtr
453 iterator - pointer to BTreeIterator indicating position result of search
455 Result: noErr - success, record contains copy of record found
456 fsBTRecordNotFoundErr - record was not found, no data copied
457 fsBTInvalidFileErr - no BTreeControlBlock is allocated for the fork
458 fsBTInvalidKeyLengthErr -
460 -------------------------------------------------------------------------------*/
462 OSStatus
BTSearchRecord (FCB
*filePtr
,
463 BTreeIterator
*searchIterator
,
464 FSBufferDescriptor
*record
,
466 BTreeIterator
*resultIterator
)
469 BTreeControlBlockPtr btreePtr
;
470 TreePathTable treePathTable
;
472 BlockDescriptor node
;
480 if (filePtr
== nil
) return paramErr
;
481 if (searchIterator
== nil
) return paramErr
;
484 node
.blockHeader
= nil
;
486 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
487 if (btreePtr
== nil
) return fsBTInvalidFileErr
;
489 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
493 ////////////////////////////// Take A Hint //////////////////////////////////
495 err
= IsItAHint (btreePtr
, searchIterator
, &validHint
);
500 nodeNum
= searchIterator
->hint
.nodeNum
;
502 err
= GetNode (btreePtr
, nodeNum
, &node
);
505 if ( ((BTNodeDescriptor
*) node
.buffer
)->kind
== kBTLeafNode
&&
506 ((BTNodeDescriptor
*) node
.buffer
)->numRecords
> 0 )
508 foundRecord
= SearchNode (btreePtr
, node
.buffer
, &searchIterator
->key
, &index
);
510 //\80\80 if !foundRecord, we could still skip tree search if ( 0 < index < numRecords )
513 if (foundRecord
== false)
515 err
= ReleaseNode (btreePtr
, &node
);
520 ++btreePtr
->numValidHints
;
524 if( foundRecord
== false )
525 (void) BTInvalidateHint( searchIterator
);
529 //////////////////////////// Search The Tree ////////////////////////////////
531 if (foundRecord
== false)
533 err
= SearchTree ( btreePtr
, &searchIterator
->key
, treePathTable
, &nodeNum
, &node
, &index
);
536 case noErr
: foundRecord
= true; break;
537 case fsBTRecordNotFoundErr
: break;
538 default: goto ErrorExit
;
543 //////////////////////////// Get the Record /////////////////////////////////
545 if (foundRecord
== true)
547 //XXX Should check for errors! Or BlockMove could choke on recordPtr!!!
548 GetRecordByIndex (btreePtr
, node
.buffer
, index
, &keyPtr
, &recordPtr
, &len
);
550 if (recordLen
!= nil
) *recordLen
= len
;
554 ByteCount recordSize
;
556 recordSize
= record
->itemCount
* record
->itemSize
;
558 if (len
> recordSize
) len
= recordSize
;
560 BlockMoveData (recordPtr
, record
->bufferAddress
, len
);
565 /////////////////////// Success - Update Iterator ///////////////////////////
567 if (resultIterator
!= nil
)
569 resultIterator
->hint
.writeCount
= btreePtr
->writeCount
;
570 resultIterator
->hint
.nodeNum
= nodeNum
;
571 resultIterator
->hint
.index
= index
;
573 resultIterator
->hint
.reserved1
= 0;
574 resultIterator
->hint
.reserved2
= 0;
575 resultIterator
->version
= 0;
576 resultIterator
->reserved
= 0;
578 // copy the key in the BTree when found rather than searchIterator->key to get proper case/diacriticals
579 if (foundRecord
== true)
580 BlockMoveData ((Ptr
)keyPtr
, (Ptr
)&resultIterator
->key
, CalcKeySize(btreePtr
, keyPtr
));
582 BlockMoveData ((Ptr
)&searchIterator
->key
, (Ptr
)&resultIterator
->key
, CalcKeySize(btreePtr
, &searchIterator
->key
));
585 err
= ReleaseNode (btreePtr
, &node
);
588 if (foundRecord
== false) return fsBTRecordNotFoundErr
;
592 /////////////////////// Error - Clean Up and Exit ///////////////////////////
596 if (recordLen
!= nil
)
599 if (resultIterator
!= nil
)
601 resultIterator
->hint
.writeCount
= 0;
602 resultIterator
->hint
.nodeNum
= 0;
603 resultIterator
->hint
.index
= 0;
604 resultIterator
->hint
.reserved1
= 0;
605 resultIterator
->hint
.reserved2
= 0;
607 resultIterator
->version
= 0;
608 resultIterator
->reserved
= 0;
609 resultIterator
->key
.length16
= 0; // zero out two bytes to cover both types of keys
612 if ( err
== fsBTEmptyErr
)
613 err
= fsBTRecordNotFoundErr
;
620 /*-------------------------------------------------------------------------------
621 Routine: BTIterateRecord - Find the first, next, previous, or last record.
623 Function: Find the first, next, previous, or last record in the BTree
625 Input: pathPtr - pointer to path iterate records for.
626 operation - iteration operation (first,next,prev,last)
627 iterator - pointer to iterator indicating start position
629 Output: iterator - iterator is updated to indicate new position
630 newKeyPtr - pointer to buffer to copy key found by iteration
631 record - pointer to buffer to copy record found by iteration
632 recordLen - length of record
634 Result: noErr - success
636 -------------------------------------------------------------------------------*/
638 OSStatus
BTIterateRecord (FCB
*filePtr
,
639 BTreeIterationOperation operation
,
640 BTreeIterator
*iterator
,
641 FSBufferDescriptor
*record
,
645 BTreeControlBlockPtr btreePtr
;
653 BlockDescriptor left
, node
, right
;
657 ////////////////////////// Priliminary Checks ///////////////////////////////
660 left
.blockHeader
= nil
;
662 right
.blockHeader
= nil
;
664 node
.blockHeader
= nil
;
672 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
675 return fsBTInvalidFileErr
; //\80\80 handle properly
678 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
680 if ((operation
!= kBTreeFirstRecord
) &&
681 (operation
!= kBTreeNextRecord
) &&
682 (operation
!= kBTreeCurrentRecord
) &&
683 (operation
!= kBTreePrevRecord
) &&
684 (operation
!= kBTreeLastRecord
))
686 err
= fsInvalidIterationMovmentErr
;
690 /////////////////////// Find First or Last Record ///////////////////////////
692 if ((operation
== kBTreeFirstRecord
) || (operation
== kBTreeLastRecord
))
694 if (operation
== kBTreeFirstRecord
) nodeNum
= btreePtr
->firstLeafNode
;
695 else nodeNum
= btreePtr
->lastLeafNode
;
703 err
= GetNode (btreePtr
, nodeNum
, &node
);
706 if ( ((NodeDescPtr
) node
.buffer
)->kind
!= kBTLeafNode
||
707 ((NodeDescPtr
) node
.buffer
)->numRecords
<= 0 )
709 err
= ReleaseNode (btreePtr
, &node
);
712 err
= fsBTInvalidNodeErr
;
713 MARK_VOLUMEDAMAGED(filePtr
);
717 if (operation
== kBTreeFirstRecord
) index
= 0;
718 else index
= ((BTNodeDescriptor
*) node
.buffer
)->numRecords
- 1;
720 goto CopyData
; //\80\80 is there a cleaner way?
724 //////////////////////// Find Iterator Position /////////////////////////////
726 err
= FindIteratorPosition (btreePtr
, iterator
,
727 &left
, &node
, &right
, &nodeNum
, &index
, &foundRecord
);
731 ///////////////////// Find Next Or Previous Record //////////////////////////
733 if (operation
== kBTreePrevRecord
)
741 if (left
.buffer
== nil
)
743 nodeNum
= ((NodeDescPtr
) node
.buffer
)->bLink
;
746 err
= GetNode (btreePtr
, nodeNum
, &left
);
749 err
= fsBTStartOfIterationErr
;
753 // Before we stomp on "right", we'd better release it if needed
754 if (right
.buffer
!= nil
) {
755 err
= ReleaseNode(btreePtr
, &right
);
761 index
= ((NodeDescPtr
) node
.buffer
)->numRecords
-1;
764 else if (operation
== kBTreeNextRecord
)
766 if ((foundRecord
!= true) &&
767 (((NodeDescPtr
) node
.buffer
)->fLink
== 0) &&
768 (index
== ((NodeDescPtr
) node
.buffer
)->numRecords
))
770 err
= fsBTEndOfIterationErr
;
774 // we did not find the record but the index is already positioned correctly
775 if ((foundRecord
== false) && (index
!= ((NodeDescPtr
) node
.buffer
)->numRecords
))
778 // we found the record OR we have to look in the next node
779 if (index
< ((NodeDescPtr
) node
.buffer
)->numRecords
-1)
785 if (right
.buffer
== nil
)
787 nodeNum
= ((NodeDescPtr
) node
.buffer
)->fLink
;
790 err
= GetNode (btreePtr
, nodeNum
, &right
);
793 err
= fsBTEndOfIterationErr
;
797 // Before we stomp on "left", we'd better release it if needed
798 if (left
.buffer
!= nil
) {
799 err
= ReleaseNode(btreePtr
, &left
);
808 else // operation == kBTreeCurrentRecord
810 // make sure we have something... <CS9>
811 if ((foundRecord
!= true) &&
812 (index
>= ((NodeDescPtr
) node
.buffer
)->numRecords
))
814 err
= fsBTEndOfIterationErr
;
819 //////////////////// Copy Record And Update Iterator ////////////////////////
823 // added check for errors <CS9>
824 err
= GetRecordByIndex (btreePtr
, node
.buffer
, index
, &keyPtr
, &recordPtr
, &len
);
827 if (recordLen
!= nil
)
832 ByteCount recordSize
;
834 recordSize
= record
->itemCount
* record
->itemSize
;
836 if (len
> recordSize
) len
= recordSize
;
838 BlockMoveData (recordPtr
, record
->bufferAddress
, len
);
841 if (iterator
!= nil
) // first & last do not require iterator
843 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
844 iterator
->hint
.nodeNum
= nodeNum
;
845 iterator
->hint
.index
= index
;
846 iterator
->hint
.reserved1
= 0;
847 iterator
->hint
.reserved2
= 0;
849 iterator
->version
= 0;
850 iterator
->reserved
= 0;
853 * Check for infinite loops by making sure we do not
854 * process more leaf records, than can possibly be (or the BTree header
855 * is seriously damaged)....a brute force method.
857 if ((operation
== kBTreeFirstRecord
) || (operation
== kBTreeLastRecord
))
858 iterator
->hitCount
= 1;
859 else if (operation
!= kBTreeCurrentRecord
)
860 iterator
->hitCount
+= 1;
861 /* Always use the highest max, in case the grows while iterating */
862 iterator
->maxLeafRecs
= max(btreePtr
->leafRecords
, iterator
->maxLeafRecs
);
865 if (iterator
->hitCount
> iterator
->maxLeafRecs
+ kNumLeafRecSlack
)
867 err
= fsBTInvalidNodeErr
;
868 MARK_VOLUMEDAMAGED(filePtr
);
873 BlockMoveData ((Ptr
)keyPtr
, (Ptr
)&iterator
->key
, CalcKeySize(btreePtr
, keyPtr
));
877 ///////////////////////////// Release Nodes /////////////////////////////////
879 err
= ReleaseNode (btreePtr
, &node
);
882 if (left
.buffer
!= nil
)
884 err
= ReleaseNode (btreePtr
, &left
);
888 if (right
.buffer
!= nil
)
890 err
= ReleaseNode (btreePtr
, &right
);
896 /////////////////////// Error - Clean Up and Exit ///////////////////////////
900 (void) ReleaseNode (btreePtr
, &left
);
901 (void) ReleaseNode (btreePtr
, &node
);
902 (void) ReleaseNode (btreePtr
, &right
);
904 if (recordLen
!= nil
)
909 iterator
->hint
.writeCount
= 0;
910 iterator
->hint
.nodeNum
= 0;
911 iterator
->hint
.index
= 0;
912 iterator
->hint
.reserved1
= 0;
913 iterator
->hint
.reserved2
= 0;
915 iterator
->version
= 0;
916 iterator
->reserved
= 0;
917 iterator
->key
.length16
= 0;
920 if ( err
== fsBTEmptyErr
|| err
== fsBTEndOfIterationErr
)
921 err
= fsBTRecordNotFoundErr
;
927 /*-------------------------------------------------------------------------------
928 Routine: BTIterateRecords
930 Function: Find a series of records
932 Input: filePtr - b-tree file
933 operation - iteration operation (first,next,prev,last)
934 iterator - pointer to iterator indicating start position
935 callBackProc - pointer to routince to process a record
936 callBackState - pointer to state data (used by callBackProc)
938 Output: iterator - iterator is updated to indicate new position
940 Result: noErr - success
942 -------------------------------------------------------------------------------*/
945 BTIterateRecords(FCB
*filePtr
, BTreeIterationOperation operation
, BTreeIterator
*iterator
,
946 IterateCallBackProcPtr callBackProc
, void * callBackState
)
949 BTreeControlBlockPtr btreePtr
;
955 BlockDescriptor left
, node
, right
;
959 ////////////////////////// Priliminary Checks ///////////////////////////////
962 left
.blockHeader
= nil
;
964 right
.blockHeader
= nil
;
966 node
.blockHeader
= nil
;
968 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
970 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
972 if ((operation
!= kBTreeFirstRecord
) &&
973 (operation
!= kBTreeNextRecord
) &&
974 (operation
!= kBTreeCurrentRecord
) &&
975 (operation
!= kBTreePrevRecord
) &&
976 (operation
!= kBTreeLastRecord
))
978 err
= fsInvalidIterationMovmentErr
;
982 /////////////////////// Find First or Last Record ///////////////////////////
984 if ((operation
== kBTreeFirstRecord
) || (operation
== kBTreeLastRecord
))
986 if (operation
== kBTreeFirstRecord
)
987 nodeNum
= btreePtr
->firstLeafNode
;
989 nodeNum
= btreePtr
->lastLeafNode
;
997 err
= GetNode(btreePtr
, nodeNum
, &node
);
1000 if ( ((NodeDescPtr
)node
.buffer
)->kind
!= kBTLeafNode
||
1001 ((NodeDescPtr
)node
.buffer
)->numRecords
<= 0 )
1003 err
= ReleaseNode(btreePtr
, &node
);
1006 err
= fsBTInvalidNodeErr
;
1007 MARK_VOLUMEDAMAGED(filePtr
);
1011 if (operation
== kBTreeFirstRecord
)
1014 index
= ((BTNodeDescriptor
*) node
.buffer
)->numRecords
- 1;
1019 //////////////////////// Find Iterator Position /////////////////////////////
1021 err
= FindIteratorPosition(btreePtr
, iterator
, &left
, &node
, &right
,
1022 &nodeNum
, &index
, &foundRecord
);
1023 if (err
== fsBTRecordNotFoundErr
)
1028 ///////////////////// Find Next Or Previous Record //////////////////////////
1030 if (operation
== kBTreePrevRecord
)
1038 if (left
.buffer
== nil
)
1040 nodeNum
= ((NodeDescPtr
) node
.buffer
)->bLink
;
1043 err
= GetNode(btreePtr
, nodeNum
, &left
);
1046 err
= fsBTStartOfIterationErr
;
1050 // Before we stomp on "right", we'd better release it if needed
1051 if (right
.buffer
!= nil
) {
1052 err
= ReleaseNode(btreePtr
, &right
);
1058 index
= ((NodeDescPtr
) node
.buffer
)->numRecords
-1;
1061 else if (operation
== kBTreeNextRecord
)
1063 if ((foundRecord
!= true) &&
1064 (((NodeDescPtr
)node
.buffer
)->fLink
== 0) &&
1065 (index
== ((NodeDescPtr
)node
.buffer
)->numRecords
))
1067 err
= fsBTEndOfIterationErr
;
1071 // we did not find the record but the index is already positioned correctly
1072 if ((foundRecord
== false) && (index
!= ((NodeDescPtr
)node
.buffer
)->numRecords
))
1075 // we found the record OR we have to look in the next node
1076 if (index
< ((NodeDescPtr
)node
.buffer
)->numRecords
-1)
1082 if (right
.buffer
== nil
)
1084 nodeNum
= ((NodeDescPtr
)node
.buffer
)->fLink
;
1087 err
= GetNode(btreePtr
, nodeNum
, &right
);
1090 err
= fsBTEndOfIterationErr
;
1094 // Before we stomp on "left", we'd better release it if needed
1095 if (left
.buffer
!= nil
) {
1096 err
= ReleaseNode(btreePtr
, &left
);
1105 else // operation == kBTreeCurrentRecord
1107 // make sure we have something... <CS9>
1108 if ((foundRecord
!= true) &&
1109 (index
>= ((NodeDescPtr
)node
.buffer
)->numRecords
))
1111 err
= fsBTEndOfIterationErr
;
1116 //////////////////// Process Records Using Callback ////////////////////////
1119 err
= GetRecordByIndex(btreePtr
, node
.buffer
, index
, &keyPtr
, &recordPtr
, &len
);
1126 if (callBackProc(keyPtr
, recordPtr
, len
, callBackState
) == 0)
1129 if ((index
+1) < ((NodeDescPtr
)node
.buffer
)->numRecords
) {
1132 if (right
.buffer
== nil
)
1134 nodeNum
= ((NodeDescPtr
)node
.buffer
)->fLink
;
1137 err
= GetNode(btreePtr
, nodeNum
, &right
);
1140 err
= fsBTEndOfIterationErr
;
1144 // Before we stomp on "left", we'd better release it if needed
1145 if (left
.buffer
!= nil
) {
1146 err
= ReleaseNode(btreePtr
, &left
);
1154 err
= GetRecordByIndex(btreePtr
, node
.buffer
, index
,
1155 &keyPtr
, &recordPtr
, &len
);
1163 ///////////////// Update Iterator to Last Item Processed /////////////////////
1166 if (iterator
!= nil
) // first & last have optional iterator
1168 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
1169 iterator
->hint
.nodeNum
= nodeNum
;
1170 iterator
->hint
.index
= index
;
1171 iterator
->version
= 0;
1173 BlockMoveData((Ptr
)keyPtr
, (Ptr
)&iterator
->key
, CalcKeySize(btreePtr
, keyPtr
));
1178 ///////////////////////////// Release Nodes /////////////////////////////////
1180 err
= ReleaseNode(btreePtr
, &node
);
1183 if (left
.buffer
!= nil
)
1185 err
= ReleaseNode(btreePtr
, &left
);
1189 if (right
.buffer
!= nil
)
1191 err
= ReleaseNode(btreePtr
, &right
);
1197 /////////////////////// Error - Clean Up and Exit ///////////////////////////
1201 (void) ReleaseNode(btreePtr
, &left
);
1202 (void) ReleaseNode(btreePtr
, &node
);
1203 (void) ReleaseNode(btreePtr
, &right
);
1205 if (iterator
!= nil
)
1207 iterator
->hint
.writeCount
= 0;
1208 iterator
->hint
.nodeNum
= 0;
1209 iterator
->hint
.index
= 0;
1210 iterator
->version
= 0;
1211 iterator
->key
.length16
= 0;
1214 if ( err
== fsBTEmptyErr
|| err
== fsBTEndOfIterationErr
)
1215 err
= fsBTRecordNotFoundErr
;
1221 //////////////////////////////// BTInsertRecord /////////////////////////////////
1223 OSStatus
BTInsertRecord (FCB
*filePtr
,
1224 BTreeIterator
*iterator
,
1225 FSBufferDescriptor
*record
,
1229 BTreeControlBlockPtr btreePtr
;
1230 TreePathTable treePathTable
;
1232 BlockDescriptor nodeRec
;
1233 UInt32 insertNodeNum
;
1237 ////////////////////////// Priliminary Checks ///////////////////////////////
1239 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
1240 nodeRec
.blockHeader
= nil
;
1242 err
= CheckInsertParams (filePtr
, iterator
, record
, recordLen
);
1246 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1248 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1251 ///////////////////////// Find Insert Position //////////////////////////////
1253 // always call SearchTree for Insert
1254 err
= SearchTree (btreePtr
, &iterator
->key
, treePathTable
, &insertNodeNum
, &nodeRec
, &index
);
1256 switch (err
) // set/replace/insert decision point
1258 case noErr
: err
= fsBTDuplicateRecordErr
;
1261 case fsBTRecordNotFoundErr
: break;
1263 case fsBTEmptyErr
: // if tree empty add 1st leaf node
1265 if (BTAvailableNodes(btreePtr
) == 0)
1267 err
= ExtendBTree (btreePtr
, btreePtr
->totalNodes
+ 1);
1268 M_ExitOnError (err
);
1271 err
= AllocateNode (btreePtr
, &insertNodeNum
);
1272 M_ExitOnError (err
);
1274 err
= GetNewNode (btreePtr
, insertNodeNum
, &nodeRec
);
1275 M_ExitOnError (err
);
1278 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1280 ((NodeDescPtr
)nodeRec
.buffer
)->kind
= kBTLeafNode
;
1281 ((NodeDescPtr
)nodeRec
.buffer
)->height
= 1;
1283 recordFit
= InsertKeyRecord (btreePtr
, nodeRec
.buffer
, 0,
1284 &iterator
->key
, KeyLength(btreePtr
, &iterator
->key
),
1285 record
->bufferAddress
, recordLen
);
1286 if (recordFit
!= true)
1288 err
= fsBTRecordTooLargeErr
;
1292 err
= UpdateNode (btreePtr
, &nodeRec
, 0, kLockTransaction
);
1293 M_ExitOnError (err
);
1295 // update BTreeControlBlock
1296 btreePtr
->treeDepth
= 1;
1297 btreePtr
->rootNode
= insertNodeNum
;
1298 btreePtr
->firstLeafNode
= insertNodeNum
;
1299 btreePtr
->lastLeafNode
= insertNodeNum
;
1301 M_BTreeHeaderDirty (btreePtr
);
1305 default: goto ErrorExit
;
1311 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1313 recordFit
= InsertKeyRecord (btreePtr
, nodeRec
.buffer
, index
,
1314 &iterator
->key
, KeyLength(btreePtr
, &iterator
->key
),
1315 record
->bufferAddress
, recordLen
);
1316 if (recordFit
== true)
1318 err
= UpdateNode (btreePtr
, &nodeRec
, 0, kLockTransaction
);
1319 M_ExitOnError (err
);
1325 /////////////////////// Extend File If Necessary ////////////////////////////
1327 nodesNeeded
= (SInt32
)btreePtr
->treeDepth
+ 1 - BTAvailableNodes(btreePtr
);
1328 if (nodesNeeded
> 0)
1330 nodesNeeded
+= (SInt32
)btreePtr
->totalNodes
;
1331 if (nodesNeeded
> CalcMapBits (btreePtr
)) // we'll need to add a map node too!
1334 err
= ExtendBTree (btreePtr
, nodesNeeded
);
1335 M_ExitOnError (err
);
1338 // no need to delete existing record
1340 err
= InsertTree (btreePtr
, treePathTable
, &iterator
->key
, record
->bufferAddress
,
1341 recordLen
, &nodeRec
, index
, 1, kInsertRecord
, &insertNodeNum
);
1342 M_ExitOnError (err
);
1345 //////////////////////////////// Success ////////////////////////////////////
1348 ++btreePtr
->writeCount
;
1349 ++btreePtr
->leafRecords
;
1350 M_BTreeHeaderDirty (btreePtr
);
1353 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
1354 iterator
->hint
.nodeNum
= insertNodeNum
;
1355 iterator
->hint
.index
= 0; // unused
1356 iterator
->hint
.reserved1
= 0;
1357 iterator
->hint
.reserved2
= 0;
1362 ////////////////////////////// Error Exit ///////////////////////////////////
1366 (void) ReleaseNode (btreePtr
, &nodeRec
);
1368 iterator
->hint
.writeCount
= 0;
1369 iterator
->hint
.nodeNum
= 0;
1370 iterator
->hint
.index
= 0;
1371 iterator
->hint
.reserved1
= 0;
1372 iterator
->hint
.reserved2
= 0;
1374 if (err
== fsBTEmptyErr
)
1375 err
= fsBTRecordNotFoundErr
;
1381 //////////////////////////////// BTReplaceRecord ////////////////////////////////
1383 OSStatus
BTReplaceRecord (FCB
*filePtr
,
1384 BTreeIterator
*iterator
,
1385 FSBufferDescriptor
*record
,
1389 BTreeControlBlockPtr btreePtr
;
1390 TreePathTable treePathTable
;
1392 BlockDescriptor nodeRec
;
1393 UInt32 insertNodeNum
;
1399 ////////////////////////// Priliminary Checks ///////////////////////////////
1401 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
1402 nodeRec
.blockHeader
= nil
;
1404 err
= CheckInsertParams (filePtr
, iterator
, record
, recordLen
);
1408 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1410 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1412 ////////////////////////////// Take A Hint //////////////////////////////////
1414 err
= IsItAHint (btreePtr
, iterator
, &validHint
);
1415 M_ExitOnError (err
);
1419 insertNodeNum
= iterator
->hint
.nodeNum
;
1421 err
= GetNode (btreePtr
, insertNodeNum
, &nodeRec
);
1425 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1427 err
= TrySimpleReplace (btreePtr
, nodeRec
.buffer
, iterator
, record
, recordLen
, &recordFit
);
1428 M_ExitOnError (err
);
1432 err
= UpdateNode (btreePtr
, &nodeRec
, 0, 0);
1433 M_ExitOnError (err
);
1435 ++btreePtr
->numValidHints
;
1441 (void) BTInvalidateHint( iterator
);
1444 err
= ReleaseNode (btreePtr
, &nodeRec
);
1445 M_ExitOnError (err
);
1449 (void) BTInvalidateHint( iterator
);
1454 ////////////////////////////// Get A Clue ///////////////////////////////////
1456 err
= SearchTree (btreePtr
, &iterator
->key
, treePathTable
, &insertNodeNum
, &nodeRec
, &index
);
1457 M_ExitOnError (err
); // record must exit for Replace
1459 // optimization - if simple replace will work then don't extend btree
1460 // \80\80 if we tried this before, and failed because it wouldn't fit then we shouldn't try this again...
1463 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1465 err
= TrySimpleReplace (btreePtr
, nodeRec
.buffer
, iterator
, record
, recordLen
, &recordFit
);
1466 M_ExitOnError (err
);
1470 err
= UpdateNode (btreePtr
, &nodeRec
, 0, 0);
1471 M_ExitOnError (err
);
1477 //////////////////////////// Make Some Room /////////////////////////////////
1479 nodesNeeded
= (SInt32
)btreePtr
->treeDepth
+ 1 - BTAvailableNodes(btreePtr
);
1480 if (nodesNeeded
> 0)
1482 nodesNeeded
+= (SInt32
)btreePtr
->totalNodes
;
1483 if (nodesNeeded
> CalcMapBits (btreePtr
)) // we'll need to add a map node too!
1486 err
= ExtendBTree (btreePtr
, nodesNeeded
);
1487 M_ExitOnError (err
);
1491 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1493 DeleteRecord (btreePtr
, nodeRec
.buffer
, index
); // delete existing key/record
1495 err
= InsertTree (btreePtr
, treePathTable
, &iterator
->key
, record
->bufferAddress
,
1496 recordLen
, &nodeRec
, index
, 1, kReplaceRecord
, &insertNodeNum
);
1497 M_ExitOnError (err
);
1499 ++btreePtr
->writeCount
; /* writeCount changes only if the tree structure changed */
1503 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
1504 iterator
->hint
.nodeNum
= insertNodeNum
;
1505 iterator
->hint
.index
= 0; // unused
1506 iterator
->hint
.reserved1
= 0;
1507 iterator
->hint
.reserved2
= 0;
1512 ////////////////////////////// Error Exit ///////////////////////////////////
1516 (void) ReleaseNode (btreePtr
, &nodeRec
);
1518 iterator
->hint
.writeCount
= 0;
1519 iterator
->hint
.nodeNum
= 0;
1520 iterator
->hint
.index
= 0;
1521 iterator
->hint
.reserved1
= 0;
1522 iterator
->hint
.reserved2
= 0;
1529 //////////////////////////////// BTUpdateRecord ////////////////////////////////
1532 BTUpdateRecord(FCB
*filePtr
, BTreeIterator
*iterator
,
1533 IterateCallBackProcPtr callBackProc
, void * callBackState
)
1536 BTreeControlBlockPtr btreePtr
;
1537 TreePathTable treePathTable
;
1538 BlockDescriptor nodeRec
;
1539 RecordPtr recordPtr
;
1541 UInt32 insertNodeNum
;
1547 ////////////////////////// Priliminary Checks ///////////////////////////////
1549 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
1550 nodeRec
.blockHeader
= nil
;
1552 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1554 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1556 ////////////////////////////// Take A Hint //////////////////////////////////
1558 err
= IsItAHint (btreePtr
, iterator
, &validHint
);
1559 M_ExitOnError (err
);
1563 insertNodeNum
= iterator
->hint
.nodeNum
;
1565 err
= GetNode (btreePtr
, insertNodeNum
, &nodeRec
);
1568 if (((NodeDescPtr
)nodeRec
.buffer
)->kind
== kBTLeafNode
&&
1569 SearchNode (btreePtr
, nodeRec
.buffer
, &iterator
->key
, &index
))
1571 err
= GetRecordByIndex(btreePtr
, nodeRec
.buffer
, index
, &keyPtr
, &recordPtr
, &recordLen
);
1572 M_ExitOnError (err
);
1575 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1577 err
= callBackProc(keyPtr
, recordPtr
, recordLen
, callBackState
);
1578 M_ExitOnError (err
);
1580 err
= UpdateNode (btreePtr
, &nodeRec
, 0, 0);
1581 M_ExitOnError (err
);
1583 ++btreePtr
->numValidHints
;
1589 (void) BTInvalidateHint( iterator
);
1592 err
= ReleaseNode (btreePtr
, &nodeRec
);
1593 M_ExitOnError (err
);
1597 (void) BTInvalidateHint( iterator
);
1601 ////////////////////////////// Get A Clue ///////////////////////////////////
1603 err
= SearchTree (btreePtr
, &iterator
->key
, treePathTable
, &insertNodeNum
, &nodeRec
, &index
);
1604 M_ExitOnError (err
);
1606 err
= GetRecordByIndex(btreePtr
, nodeRec
.buffer
, index
, &keyPtr
, &recordPtr
, &recordLen
);
1607 M_ExitOnError (err
);
1610 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1612 err
= callBackProc(keyPtr
, recordPtr
, recordLen
, callBackState
);
1613 M_ExitOnError (err
);
1615 err
= UpdateNode (btreePtr
, &nodeRec
, 0, 0);
1616 M_ExitOnError (err
);
1620 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
1621 iterator
->hint
.nodeNum
= insertNodeNum
;
1622 iterator
->hint
.index
= 0;
1623 iterator
->hint
.reserved1
= 0;
1624 iterator
->hint
.reserved2
= 0;
1627 ////////////////////////////// Error Exit ///////////////////////////////////
1631 (void) ReleaseNode (btreePtr
, &nodeRec
);
1633 iterator
->hint
.writeCount
= 0;
1634 iterator
->hint
.nodeNum
= 0;
1635 iterator
->hint
.index
= 0;
1636 iterator
->hint
.reserved1
= 0;
1637 iterator
->hint
.reserved2
= 0;
1643 //////////////////////////////// BTDeleteRecord /////////////////////////////////
1645 OSStatus
BTDeleteRecord (FCB
*filePtr
,
1646 BTreeIterator
*iterator
)
1649 BTreeControlBlockPtr btreePtr
;
1650 TreePathTable treePathTable
;
1651 BlockDescriptor nodeRec
;
1657 ////////////////////////// Priliminary Checks ///////////////////////////////
1659 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
1660 nodeRec
.blockHeader
= nil
;
1662 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1663 M_ReturnErrorIf (iterator
== nil
, paramErr
);
1665 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1666 if (btreePtr
== nil
)
1668 err
= fsBTInvalidFileErr
;
1672 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1675 /////////////////////////////// Find Key ////////////////////////////////////
1677 //\80\80 check hint for simple delete case (index > 0, numRecords > 2)
1679 err
= SearchTree (btreePtr
, &iterator
->key
, treePathTable
, &nodeNum
, &nodeRec
, &index
);
1680 M_ExitOnError (err
); // record must exit for Delete
1683 /////////////////////// Extend File If Necessary ////////////////////////////
1685 nodesNeeded
= (SInt32
)btreePtr
->treeDepth
+ 1 - BTAvailableNodes(btreePtr
);
1686 if ((btreePtr
->attributes
& kBTVariableIndexKeysMask
) && (nodesNeeded
> 0))
1688 nodesNeeded
+= (SInt32
)btreePtr
->totalNodes
;
1689 if (nodesNeeded
> CalcMapBits (btreePtr
))
1692 err
= ExtendBTree (btreePtr
, nodesNeeded
);
1693 M_ExitOnError (err
);
1696 ///////////////////////////// Delete Record /////////////////////////////////
1698 err
= DeleteTree (btreePtr
, treePathTable
, &nodeRec
, index
, 1);
1699 M_ExitOnError (err
);
1701 ++btreePtr
->writeCount
;
1702 --btreePtr
->leafRecords
;
1703 M_BTreeHeaderDirty (btreePtr
);
1705 iterator
->hint
.nodeNum
= 0;
1709 ////////////////////////////// Error Exit ///////////////////////////////////
1712 (void) ReleaseNode (btreePtr
, &nodeRec
);
1719 OSStatus
BTGetInformation (FCB
*filePtr
,
1721 BTreeInfoRec
*info
)
1723 #pragma unused (version)
1725 BTreeControlBlockPtr btreePtr
;
1728 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1730 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1734 * This should not require the whole tree to be locked, just maybe the BTreeControlBlockPtr
1736 * REQUIRE_FILE_LOCK(btreePtr->fileRefNum, true);
1739 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1740 M_ReturnErrorIf (info
== nil
, paramErr
);
1742 //\80\80 check version?
1744 info
->nodeSize
= btreePtr
->nodeSize
;
1745 info
->maxKeyLength
= btreePtr
->maxKeyLength
;
1746 info
->treeDepth
= btreePtr
->treeDepth
;
1747 info
->numRecords
= btreePtr
->leafRecords
;
1748 info
->numNodes
= btreePtr
->totalNodes
;
1749 info
->numFreeNodes
= btreePtr
->freeNodes
;
1750 info
->lastfsync
= btreePtr
->lastfsync
;
1751 info
->keyCompareType
= btreePtr
->keyCompareType
;
1758 BTIsDirty(FCB
*filePtr
)
1760 BTreeControlBlockPtr btreePtr
;
1762 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1763 return TreeIsDirty(btreePtr
);
1766 /*-------------------------------------------------------------------------------
1767 Routine: BTFlushPath - Flush BTreeControlBlock to Header Node.
1769 Function: Brief_description_of_the_function_and_any_side_effects
1772 Input: pathPtr - pointer to path control block for B*Tree file to flush
1776 Result: noErr - success
1778 -------------------------------------------------------------------------------*/
1780 OSStatus
BTFlushPath (FCB
*filePtr
)
1783 BTreeControlBlockPtr btreePtr
;
1786 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1788 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1790 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1792 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1794 err
= UpdateHeader (btreePtr
, false);
1800 /*-------------------------------------------------------------------------------
1801 Routine: BTReload - Reload B-tree Header Data.
1803 Function: Reload B-tree header data from disk. This is called after fsck
1804 has made repairs to the root filesystem. The filesystem is
1805 mounted read-only when BTReload is caled.
1808 Input: filePtr - the B*Tree file that needs its header updated
1812 Result: noErr - success
1814 -------------------------------------------------------------------------------*/
1817 BTReloadData(FCB
*filePtr
)
1820 BTreeControlBlockPtr btreePtr
;
1821 BlockDescriptor node
;
1822 BTHeaderRec
*header
;
1826 node
.blockHeader
= nil
;
1828 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1829 if (btreePtr
== nil
)
1830 return (fsBTInvalidFileErr
);
1832 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1834 err
= GetNode(btreePtr
, kHeaderNodeNum
, &node
);
1838 header
= (BTHeaderRec
*)((char *)node
.buffer
+ sizeof(BTNodeDescriptor
));
1839 if ((err
= VerifyHeader (filePtr
, header
)) == 0) {
1840 btreePtr
->treeDepth
= header
->treeDepth
;
1841 btreePtr
->rootNode
= header
->rootNode
;
1842 btreePtr
->leafRecords
= header
->leafRecords
;
1843 btreePtr
->firstLeafNode
= header
->firstLeafNode
;
1844 btreePtr
->lastLeafNode
= header
->lastLeafNode
;
1845 btreePtr
->maxKeyLength
= header
->maxKeyLength
;
1846 btreePtr
->totalNodes
= header
->totalNodes
;
1847 btreePtr
->freeNodes
= header
->freeNodes
;
1848 btreePtr
->btreeType
= header
->btreeType
;
1850 btreePtr
->flags
&= (~kBTHeaderDirty
);
1853 (void) ReleaseNode(btreePtr
, &node
);
1859 /*-------------------------------------------------------------------------------
1860 Routine: BTInvalidateHint - Invalidates the hint within a BTreeInterator.
1862 Function: Invalidates the hint within a BTreeInterator.
1865 Input: iterator - pointer to BTreeIterator
1867 Output: iterator - iterator with the hint.nodeNum cleared
1869 Result: noErr - success
1870 paramErr - iterator == nil
1871 -------------------------------------------------------------------------------*/
1874 OSStatus
BTInvalidateHint (BTreeIterator
*iterator
)
1876 if (iterator
== nil
)
1879 iterator
->hint
.nodeNum
= 0;
1887 /*-------------------------------------------------------------------------------
1888 Routine: BTGetLastSync
1890 Function: Returns the last time that this btree was flushed, does not include header.
1892 Input: filePtr - pointer file control block
1894 Output: lastfsync - time in seconds of last update
1896 Result: noErr - success
1897 paramErr - iterator == nil
1898 -------------------------------------------------------------------------------*/
1901 OSStatus
BTGetLastSync (FCB
*filePtr
,
1904 BTreeControlBlockPtr btreePtr
;
1907 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1909 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1911 /* Maybe instead of requiring a lock..an atomic set might be more appropriate */
1912 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
1914 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1915 M_ReturnErrorIf (lastsync
== nil
, paramErr
);
1917 *lastsync
= btreePtr
->lastfsync
;
1925 /*-------------------------------------------------------------------------------
1926 Routine: BTSetLastSync
1928 Function: Sets the last time that this btree was flushed, does not include header.
1931 Input: fcb - pointer file control block
1933 Output: lastfsync - time in seconds of last update
1935 Result: noErr - success
1936 paramErr - iterator == nil
1937 -------------------------------------------------------------------------------*/
1940 OSStatus
BTSetLastSync (FCB
*filePtr
,
1943 BTreeControlBlockPtr btreePtr
;
1946 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1948 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1950 /* Maybe instead of requiring a lock..an atomic set might be more appropriate */
1951 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
1953 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1954 M_ReturnErrorIf (lastsync
== nil
, paramErr
);
1956 btreePtr
->lastfsync
= lastsync
;
1963 OSStatus
BTHasContiguousNodes (FCB
*filePtr
)
1965 BTreeControlBlockPtr btreePtr
;
1968 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1970 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1972 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
1974 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1976 return NodesAreContiguous(FCBTOVCB(filePtr
), filePtr
, btreePtr
->nodeSize
);
1980 /*-------------------------------------------------------------------------------
1981 Routine: BTGetUserData
1983 Function: Read the user data area of the b-tree header node.
1985 -------------------------------------------------------------------------------*/
1988 BTGetUserData(FCB
*filePtr
, void * dataPtr
, int dataSize
)
1990 BTreeControlBlockPtr btreePtr
;
1991 BlockDescriptor node
;
1995 if (dataSize
> kBTreeHeaderUserBytes
)
1998 node
.blockHeader
= nil
;
2000 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
2001 if (btreePtr
== nil
)
2002 return (fsBTInvalidFileErr
);
2004 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
2006 err
= GetNode(btreePtr
, kHeaderNodeNum
, &node
);
2010 offset
= (char *)node
.buffer
+ sizeof(BTNodeDescriptor
) + sizeof(BTHeaderRec
);
2011 bcopy(offset
, dataPtr
, dataSize
);
2013 (void) ReleaseNode(btreePtr
, &node
);
2019 /*-------------------------------------------------------------------------------
2020 Routine: BTSetUserData
2022 Function: Write the user data area of the b-tree header node.
2023 -------------------------------------------------------------------------------*/
2026 BTSetUserData(FCB
*filePtr
, void * dataPtr
, int dataSize
)
2028 BTreeControlBlockPtr btreePtr
;
2029 BlockDescriptor node
;
2033 if (dataSize
> kBTreeHeaderUserBytes
)
2036 node
.blockHeader
= nil
;
2038 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
2039 if (btreePtr
== nil
)
2040 return (fsBTInvalidFileErr
);
2042 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
2044 err
= GetNode(btreePtr
, kHeaderNodeNum
, &node
);
2048 ModifyBlockStart(btreePtr
->fileRefNum
, &node
);
2050 offset
= (char *)node
.buffer
+ sizeof(BTNodeDescriptor
) + sizeof(BTHeaderRec
);
2051 bcopy(dataPtr
, offset
, dataSize
);
2053 err
= UpdateNode (btreePtr
, &node
, 0, 0);