2 * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved.
4 * @APPLE_LICENSE_OSREFERENCE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the
10 * License may not be used to create, or enable the creation or
11 * redistribution of, unlawful or unlicensed copies of an Apple operating
12 * system, or to circumvent, violate, or enable the circumvention or
13 * violation of, any terms of an Apple operating system software license
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
20 * The Original Code and all software distributed under the License are
21 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
22 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
23 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
25 * Please see the License for the specific language governing rights and
26 * limitations under the License.
28 * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
33 Contains: Implementation of public interface routines for B-tree manager.
37 Written by: Gordon Sheridan and Bill Bruffey
39 Copyright: © 1992-1999 by Apple Computer, Inc., all rights reserved.
45 Other Contact: Mark Day
47 Technology: File Systems
55 Change History (most recent first):
56 <MOSXS> 9/22/99 ser Added routines BTGetLastSync and BTSetLastSync
57 <MOSXS> 6/1/99 djb Sync up with Mac OS 8.6.
58 <MOSXS> 6/30/98 djb In BTOpenPath make sure nodes are contiguous on disk (radar #2249539).
59 <MOSXS> 4/15/98 djb In BTOpenPath need to clear nodeRec.buffer if GetBlockProc fails.
60 <MOSXS> 4/11/98 djb Add RequireFileLock checking to all external entry points.
62 <MOSXS> 03/23/98 djb In BTOpenPath use kTrashBlock option when releasing the header so
63 that we get a full node when we call GetNode.
65 <CS9> 12/12/97 djb Radar #2202682, BTIterateRecord with kBTreeCurrentRecord was not
66 checking if we had a record and could call BlockMove with an
67 uninitialize source pointer (causing a bus error).
68 <CS8> 10/24/97 msd In BTIterateRecord, when moving to the previous or next record
69 and we have to move to another node, see if we need to release
70 the node about to be "shifted out" (opposite sibling of the
71 direction we need to move).
72 <CS7> 7/25/97 DSH BTSearchRecord now takes a heuristicHint, nodeNum, and tries it
73 before calling SearchBTree
74 <CS6> 7/24/97 djb GetBlockProc now take a file refnum instead of an FCB ptr.
75 <CS5> 7/22/97 djb Move trace points from BTreeWrapper.c to here.
76 <CS4> 7/21/97 djb LogEndTime now takes an error code.
77 <CS3> 7/16/97 DSH FilesInternal.i renamed FileMgrInternal.i to avoid name
79 <CS2> 5/19/97 djb Add summary traces to BTIterateRecord.
80 <CS1> 4/23/97 djb first checked in
82 <HFS7> 2/19/97 djb Enable variable sized index keys for HFS+ volumes. Added node
83 cache to support nodes larger than 512 bytes.
84 <HFS6> 1/27/97 djb Calls to InsertTree and DeleteTree are now recursive (to support
85 variable sized index keys).
86 <HFS5> 1/13/97 djb Added support for getting current record to BTIterateRecord.
87 <HFS4> 1/6/97 djb Initialize "BigKeys" attribute in BTOpen.
88 <HFS3> 1/3/97 djb Added support for large keys.
89 <HFS2> 12/23/96 djb On exit map fsBTEmptyErr and fsBTEndOfIterationErr to
90 fsBTRecordNotFoundErr.
91 <HFS1> 12/19/96 djb first checked in
93 History applicable to original Scarecrow Design:
95 <13> 10/25/96 ser Changing for new VFPI
96 <12> 10/18/96 ser Converting over VFPI changes
97 <11> 9/17/96 dkh More BTree statistics. Modified hint checks to not bail out when
98 an error is returned from GetNode.
99 <10> 9/16/96 dkh Revised BTree statistics.
100 <9> 8/23/96 dkh Remove checks for multiple paths to BTree file. Need to add
101 equivalent mechanism later.
102 <8> 6/20/96 dkh Radar #1358740. Switch from using Pools to debug MemAllocators.
103 <7> 3/14/96 jev Fix BTreeSetRecord, recordFound was not set for the case of a
104 simple replace causing the leafRecords count to get bumped even
105 though we didn't have to add a record.
106 <6> 3/1/96 prp Fix lint problems. Bug in BTSetRecord that does not initialize
108 <5> 1/22/96 dkh Add #include Memory.h
109 <4> 1/10/96 msd Use the real function names from Math64.i.
110 <3> 1/4/96 jev Fix BTItererateRecord for the condition when the iterator
111 position routine does not find the record and we are looking for
112 the next record. In such a case, if the node's forrward link is
113 non-zero, we have to keep iterating next and not return
114 fsBTEndOfIterationErr error.
115 <2> 12/7/95 dkh D10E2 build. Changed usage of Ref data type to LogicalAddress.
116 <1> 10/18/95 rst Moved from Scarecrow project.
118 <24> 7/18/95 mbb Change MoveData & ClearBytes to BlockMoveData & BlockZero.
119 <23> 1/31/95 prp GetBlockProc interface uses a 64 bit node number.
120 <22> 1/12/95 wjk Adopt Model FileSystem changes in D5.
121 <21> 11/16/94 prp Add IsItAHint routine and use it whenever hint's node number was
123 <20> 11/10/94 prp BTGetInfo name collides with the same name in FileManagerPriv.i.
124 Change it to BTGetInformation.
125 <19> 9/30/94 prp Get in sync with D2 interface changes.
126 <18> 7/22/94 wjk Convert to the new set of header files.
127 <17> 12/9/93 wjk Cleanup usage of char, Byte, int8, UInt8, etc.
128 <16> 12/2/93 wjk Move from Makefiles to BuildFiles. Fit into the ModernOS and
130 <15> 11/30/93 wjk Move from Makefiles to BuildFiles. Fit into the ModernOS and
132 <14> 9/30/93 gs Rename E_NoGetNodeProc and E_NoReleaseNodeProc to
134 <13> 8/31/93 prp Use Set64U instead of Set64.
135 <12> 8/16/93 prp In BTSearchRecord, if the input hint found the node and record,
136 set the local nodeNum variable correctly so that the resultant
137 iterator gets set correctly.
138 <11> 7/1/93 gs Fix bug in BTIterateRecord related to kBTreePrevRecord
140 <10> 6/2/93 gs Update for changes to FSErrors.h and add some comments.
141 <9> 5/24/93 gs Fix bug in BTInsert/Set/ReplaceRecord which didn't set node hint
142 properly in some cases.
143 <8> 5/24/93 gs Do NOT map fsBTEmptyErr to fsBTRecordNotFoundErr in BTSearchRecord.
144 <7> 5/24/93 gs Rename BTFlush to BTFlushPath.
145 <6> 5/21/93 gs Add hint optimization to Set/Replace routines.
146 <5> 5/10/93 gs Remove Panic from BTInitialize for small logicalEOF. Implement
147 Insert, Set, Replace, and Delete.
148 <4> 3/23/93 gs Finish BTInitialize.
149 <3> 2/8/93 gs Implement BTSearchRecord and BTIterateRecord.
150 <2> 12/8/92 gs Implement Open and Close routines.
151 <1> 11/15/92 gs first checked in
155 #include "../headers/BTreesPrivate.h"
158 * The amount that the BTree header leaf count can be wrong before we assume
159 * it is in an infinite loop.
161 #define kNumLeafRecSlack 10
163 /* BTree accessor routines */
164 extern OSStatus
GetBTreeBlock(FileReference vp
, UInt32 blockNum
, GetBlockOptions options
, BlockDescriptor
*block
);
165 extern OSStatus
SetBTreeBlockSize(FileReference vp
, ByteCount blockSize
, ItemCount minBlockCount
);
166 extern OSStatus
ExtendBTreeFile(FileReference vp
, FSSize minEOF
, FSSize maxEOF
);
167 extern OSStatus
ReleaseBTreeBlock(FileReference vp
, BlockDescPtr blockPtr
, ReleaseBlockOptions options
);
169 //////////////////////////////////// Globals ////////////////////////////////////
172 /////////////////////////// BTree Module Entry Points ///////////////////////////
176 /*-------------------------------------------------------------------------------
177 Routine: BTOpenPath - Open a file for access as a B*Tree.
179 Function: Create BTree control block for a file, if necessary. Validates the
180 file to be sure it looks like a BTree file.
183 Input: filePtr - pointer to file to open as a B-tree
184 keyCompareProc - pointer to client's KeyCompare function
186 Result: noErr - success
187 paramErr - required ptr was nil
191 -------------------------------------------------------------------------------*/
193 OSStatus
BTOpenPath(FCB
*filePtr
, KeyCompareProcPtr keyCompareProc
)
196 BTreeControlBlockPtr btreePtr
;
200 ////////////////////// Preliminary Error Checking ///////////////////////////
202 if ( filePtr
== nil
)
208 * Subsequent opens allow key compare proc to be changed.
210 if ( filePtr
->fcbBTCBPtr
!= nil
&& keyCompareProc
!= nil
) {
211 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
212 btreePtr
->keyCompareProc
= keyCompareProc
;
216 if ( filePtr
->fcbEOF
< kMinNodeSize
)
217 return fsBTInvalidFileErr
;
220 //////////////////////// Allocate Control Block /////////////////////////////
222 btreePtr
= (BTreeControlBlock
*) NewPtrSysClear( sizeof( BTreeControlBlock
) );
225 Panic ("\pBTOpen: no memory for btreePtr.");
229 btreePtr
->getBlockProc
= GetBTreeBlock
;
230 btreePtr
->releaseBlockProc
= ReleaseBTreeBlock
;
231 btreePtr
->setEndOfForkProc
= ExtendBTreeFile
;
232 btreePtr
->keyCompareProc
= keyCompareProc
;
234 /////////////////////////// Read Header Node ////////////////////////////////
236 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
237 btreePtr
->fileRefNum
= GetFileRefNumFromFCB(filePtr
);
238 filePtr
->fcbBTCBPtr
= (Ptr
) btreePtr
; // attach btree cb to file
240 /* The minimum node size is the physical block size */
241 nodeRec
.blockSize
= VTOHFS(btreePtr
->fileRefNum
)->hfs_phys_block_size
;
243 /* Start with the allocation block size for regular files. */
244 if (FTOC(filePtr
)->c_fileid
>= kHFSFirstUserCatalogNodeID
)
246 nodeRec
.blockSize
= FCBTOVCB(filePtr
)->blockSize
;
248 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
250 // it is now safe to call M_ExitOnError (err)
252 err
= SetBTreeBlockSize (btreePtr
->fileRefNum
, nodeRec
.blockSize
, 1);
256 err
= GetBTreeBlock(btreePtr
->fileRefNum
,
262 nodeRec
.buffer
= nil
;
263 nodeRec
.blockHeader
= nil
;
264 Panic("\pBTOpen: getNodeProc returned error getting header node.");
267 ++btreePtr
->numGetNodes
;
268 header
= (BTHeaderRec
*) ((u_long
)nodeRec
.buffer
+ sizeof(BTNodeDescriptor
));
271 ///////////////////////////// verify header /////////////////////////////////
273 err
= VerifyHeader (filePtr
, header
);
277 ///////////////////// Initalize fields from header //////////////////////////
279 PanicIf ( (FCBTOVCB(filePtr
)->vcbSigWord
!= 0x4244) && (header
->nodeSize
== 512), "\p BTOpenPath: wrong node size for HFS+ volume!"); // 0x4244 = 'BD'
281 btreePtr
->treeDepth
= header
->treeDepth
;
282 btreePtr
->rootNode
= header
->rootNode
;
283 btreePtr
->leafRecords
= header
->leafRecords
;
284 btreePtr
->firstLeafNode
= header
->firstLeafNode
;
285 btreePtr
->lastLeafNode
= header
->lastLeafNode
;
286 btreePtr
->nodeSize
= header
->nodeSize
;
287 btreePtr
->maxKeyLength
= header
->maxKeyLength
;
288 btreePtr
->totalNodes
= header
->totalNodes
;
289 btreePtr
->freeNodes
= header
->freeNodes
;
290 if (FTOC(filePtr
)->c_fileid
>= kHFSFirstUserCatalogNodeID
)
291 filePtr
->ff_clumpsize
= header
->clumpSize
;
292 btreePtr
->btreeType
= header
->btreeType
;
294 btreePtr
->keyCompareType
= header
->keyCompareType
;
296 btreePtr
->attributes
= header
->attributes
;
298 if ( btreePtr
->maxKeyLength
> 40 )
299 btreePtr
->attributes
|= (kBTBigKeysMask
+ kBTVariableIndexKeysMask
); //\80\80 we need a way to save these attributes
301 /////////////////////// Initialize dynamic fields ///////////////////////////
303 btreePtr
->version
= kBTreeVersion
;
305 btreePtr
->writeCount
= 1;
307 /////////////////////////// Check Header Node ///////////////////////////////
309 // set kBadClose attribute bit, and UpdateNode
311 /* b-tree node size must be at least as big as the physical block size */
312 if (btreePtr
->nodeSize
< nodeRec
.blockSize
)
315 * If this tree has any records or the media is writeable then
316 * we cannot mount using the current physical block size.
318 if (btreePtr
->leafRecords
> 0 ||
319 VTOHFS(btreePtr
->fileRefNum
)->hfs_flags
& HFS_WRITEABLE_MEDIA
)
321 err
= fsBTBadNodeSize
;
327 * If the actual node size is different than the amount we read,
328 * then release and trash this block, and re-read with the correct
331 if ( btreePtr
->nodeSize
!= nodeRec
.blockSize
)
333 err
= SetBTreeBlockSize (btreePtr
->fileRefNum
, btreePtr
->nodeSize
, 32);
337 * Need to use kTrashBlock option to force the
338 * buffer cache to read the entire node
340 err
= ReleaseBTreeBlock(btreePtr
->fileRefNum
, &nodeRec
, kTrashBlock
);
341 ++btreePtr
->numReleaseNodes
;
344 err
= GetNode (btreePtr
, kHeaderNodeNum
, &nodeRec
);
348 //\80\80 total nodes * node size <= LEOF?
351 err
= ReleaseNode (btreePtr
, &nodeRec
);
355 * Under Mac OS, b-tree nodes can be non-contiguous on disk when the
356 * allocation block size is smaller than the b-tree node size.
358 * If journaling is turned on for this volume we can't deal with this
359 * situation and so we bail out. If journaling isn't on it's ok as
360 * hfs_strategy_fragmented() deals with it. Journaling can't support
361 * this because it assumes that if you give it a block that it's
362 * contiguous on disk.
364 if ( FCBTOHFS(filePtr
)->jnl
&& !NodesAreContiguous(FCBTOVCB(filePtr
), filePtr
, btreePtr
->nodeSize
) ) {
365 return fsBTInvalidNodeErr
;
368 //////////////////////////////// Success ////////////////////////////////////
370 //\80\80 align LEOF to multiple of node size? - just on close
375 /////////////////////// Error - Clean up and Exit ///////////////////////////
379 filePtr
->fcbBTCBPtr
= nil
;
380 (void) ReleaseNode (btreePtr
, &nodeRec
);
381 DisposePtr( (Ptr
) btreePtr
);
388 /*-------------------------------------------------------------------------------
389 Routine: BTClosePath - Flush BTree Header and Deallocate Memory for BTree.
391 Function: Flush the BTreeControlBlock fields to header node, and delete BTree control
392 block and key descriptor associated with the file if filePtr is last
393 path of type kBTreeType ('btre').
396 Input: filePtr - pointer to file to delete BTree control block for.
398 Result: noErr - success
401 -------------------------------------------------------------------------------*/
403 OSStatus
BTClosePath (FCB
*filePtr
)
406 BTreeControlBlockPtr btreePtr
;
408 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
411 return fsBTInvalidFileErr
;
413 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
415 ////////////////////// Check for other BTree Paths //////////////////////////
417 btreePtr
->attributes
&= ~kBTBadCloseMask
; // clear "bad close" attribute bit
418 err
= UpdateHeader (btreePtr
, true);
421 DisposePtr( (Ptr
) btreePtr
);
422 filePtr
->fcbBTCBPtr
= nil
;
426 /////////////////////// Error - Clean Up and Exit ///////////////////////////
435 /*-------------------------------------------------------------------------------
436 Routine: BTSearchRecord - Search BTree for a record with a matching key.
438 Function: Search for position in B*Tree indicated by searchKey. If a valid node hint
439 is provided, it will be searched first, then SearchTree will be called.
440 If a BTreeIterator is provided, it will be set to the position found as
441 a result of the search. If a record exists at that position, and a BufferDescriptor
442 is supplied, the record will be copied to the buffer (as much as will fit),
443 and recordLen will be set to the length of the record.
445 If an error other than fsBTRecordNotFoundErr occurs, the BTreeIterator, if any,
446 is invalidated, and recordLen is set to 0.
449 Input: pathPtr - pointer to path for BTree file.
450 searchKey - pointer to search key to match.
451 hintPtr - pointer to hint (may be nil)
453 Output: record - pointer to BufferDescriptor containing record
454 recordLen - length of data at recordPtr
455 iterator - pointer to BTreeIterator indicating position result of search
457 Result: noErr - success, record contains copy of record found
458 fsBTRecordNotFoundErr - record was not found, no data copied
459 fsBTInvalidFileErr - no BTreeControlBlock is allocated for the fork
460 fsBTInvalidKeyLengthErr -
462 -------------------------------------------------------------------------------*/
464 OSStatus
BTSearchRecord (FCB
*filePtr
,
465 BTreeIterator
*searchIterator
,
466 FSBufferDescriptor
*record
,
468 BTreeIterator
*resultIterator
)
471 BTreeControlBlockPtr btreePtr
;
472 TreePathTable treePathTable
;
474 BlockDescriptor node
;
482 if (filePtr
== nil
) return paramErr
;
483 if (searchIterator
== nil
) return paramErr
;
486 node
.blockHeader
= nil
;
488 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
489 if (btreePtr
== nil
) return fsBTInvalidFileErr
;
491 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
495 ////////////////////////////// Take A Hint //////////////////////////////////
497 err
= IsItAHint (btreePtr
, searchIterator
, &validHint
);
502 nodeNum
= searchIterator
->hint
.nodeNum
;
504 err
= GetNode (btreePtr
, nodeNum
, &node
);
507 if ( ((BTNodeDescriptor
*) node
.buffer
)->kind
== kBTLeafNode
&&
508 ((BTNodeDescriptor
*) node
.buffer
)->numRecords
> 0 )
510 foundRecord
= SearchNode (btreePtr
, node
.buffer
, &searchIterator
->key
, &index
);
512 //\80\80 if !foundRecord, we could still skip tree search if ( 0 < index < numRecords )
515 if (foundRecord
== false)
517 err
= ReleaseNode (btreePtr
, &node
);
522 ++btreePtr
->numValidHints
;
526 if( foundRecord
== false )
527 (void) BTInvalidateHint( searchIterator
);
531 //////////////////////////// Search The Tree ////////////////////////////////
533 if (foundRecord
== false)
535 err
= SearchTree ( btreePtr
, &searchIterator
->key
, treePathTable
, &nodeNum
, &node
, &index
);
538 case noErr
: foundRecord
= true; break;
539 case fsBTRecordNotFoundErr
: break;
540 default: goto ErrorExit
;
545 //////////////////////////// Get the Record /////////////////////////////////
547 if (foundRecord
== true)
549 //XXX Should check for errors! Or BlockMove could choke on recordPtr!!!
550 GetRecordByIndex (btreePtr
, node
.buffer
, index
, &keyPtr
, &recordPtr
, &len
);
552 if (recordLen
!= nil
) *recordLen
= len
;
556 ByteCount recordSize
;
558 recordSize
= record
->itemCount
* record
->itemSize
;
560 if (len
> recordSize
) len
= recordSize
;
562 BlockMoveData (recordPtr
, record
->bufferAddress
, len
);
567 /////////////////////// Success - Update Iterator ///////////////////////////
569 if (resultIterator
!= nil
)
571 resultIterator
->hint
.writeCount
= btreePtr
->writeCount
;
572 resultIterator
->hint
.nodeNum
= nodeNum
;
573 resultIterator
->hint
.index
= index
;
575 resultIterator
->hint
.reserved1
= 0;
576 resultIterator
->hint
.reserved2
= 0;
577 resultIterator
->version
= 0;
578 resultIterator
->reserved
= 0;
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
));
584 BlockMoveData ((Ptr
)&searchIterator
->key
, (Ptr
)&resultIterator
->key
, CalcKeySize(btreePtr
, &searchIterator
->key
));
587 err
= ReleaseNode (btreePtr
, &node
);
590 if (foundRecord
== false) return fsBTRecordNotFoundErr
;
594 /////////////////////// Error - Clean Up and Exit ///////////////////////////
598 if (recordLen
!= nil
)
601 if (resultIterator
!= nil
)
603 resultIterator
->hint
.writeCount
= 0;
604 resultIterator
->hint
.nodeNum
= 0;
605 resultIterator
->hint
.index
= 0;
606 resultIterator
->hint
.reserved1
= 0;
607 resultIterator
->hint
.reserved2
= 0;
609 resultIterator
->version
= 0;
610 resultIterator
->reserved
= 0;
611 resultIterator
->key
.length16
= 0; // zero out two bytes to cover both types of keys
614 if ( err
== fsBTEmptyErr
)
615 err
= fsBTRecordNotFoundErr
;
622 /*-------------------------------------------------------------------------------
623 Routine: BTIterateRecord - Find the first, next, previous, or last record.
625 Function: Find the first, next, previous, or last record in the BTree
627 Input: pathPtr - pointer to path iterate records for.
628 operation - iteration operation (first,next,prev,last)
629 iterator - pointer to iterator indicating start position
631 Output: iterator - iterator is updated to indicate new position
632 newKeyPtr - pointer to buffer to copy key found by iteration
633 record - pointer to buffer to copy record found by iteration
634 recordLen - length of record
636 Result: noErr - success
638 -------------------------------------------------------------------------------*/
640 OSStatus
BTIterateRecord (FCB
*filePtr
,
641 BTreeIterationOperation operation
,
642 BTreeIterator
*iterator
,
643 FSBufferDescriptor
*record
,
647 BTreeControlBlockPtr btreePtr
;
655 BlockDescriptor left
, node
, right
;
659 ////////////////////////// Priliminary Checks ///////////////////////////////
662 left
.blockHeader
= nil
;
664 right
.blockHeader
= nil
;
666 node
.blockHeader
= nil
;
674 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
677 return fsBTInvalidFileErr
; //\80\80 handle properly
680 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
682 if ((operation
!= kBTreeFirstRecord
) &&
683 (operation
!= kBTreeNextRecord
) &&
684 (operation
!= kBTreeCurrentRecord
) &&
685 (operation
!= kBTreePrevRecord
) &&
686 (operation
!= kBTreeLastRecord
))
688 err
= fsInvalidIterationMovmentErr
;
692 /////////////////////// Find First or Last Record ///////////////////////////
694 if ((operation
== kBTreeFirstRecord
) || (operation
== kBTreeLastRecord
))
696 if (operation
== kBTreeFirstRecord
) nodeNum
= btreePtr
->firstLeafNode
;
697 else nodeNum
= btreePtr
->lastLeafNode
;
705 err
= GetNode (btreePtr
, nodeNum
, &node
);
708 if ( ((NodeDescPtr
) node
.buffer
)->kind
!= kBTLeafNode
||
709 ((NodeDescPtr
) node
.buffer
)->numRecords
<= 0 )
711 err
= ReleaseNode (btreePtr
, &node
);
714 err
= fsBTInvalidNodeErr
;
715 MARK_VOLUMEDAMAGED(filePtr
);
719 if (operation
== kBTreeFirstRecord
) index
= 0;
720 else index
= ((BTNodeDescriptor
*) node
.buffer
)->numRecords
- 1;
722 goto CopyData
; //\80\80 is there a cleaner way?
726 //////////////////////// Find Iterator Position /////////////////////////////
728 err
= FindIteratorPosition (btreePtr
, iterator
,
729 &left
, &node
, &right
, &nodeNum
, &index
, &foundRecord
);
733 ///////////////////// Find Next Or Previous Record //////////////////////////
735 if (operation
== kBTreePrevRecord
)
743 if (left
.buffer
== nil
)
745 nodeNum
= ((NodeDescPtr
) node
.buffer
)->bLink
;
748 err
= GetNode (btreePtr
, nodeNum
, &left
);
751 err
= fsBTStartOfIterationErr
;
755 // Before we stomp on "right", we'd better release it if needed
756 if (right
.buffer
!= nil
) {
757 err
= ReleaseNode(btreePtr
, &right
);
763 index
= ((NodeDescPtr
) node
.buffer
)->numRecords
-1;
766 else if (operation
== kBTreeNextRecord
)
768 if ((foundRecord
!= true) &&
769 (((NodeDescPtr
) node
.buffer
)->fLink
== 0) &&
770 (index
== ((NodeDescPtr
) node
.buffer
)->numRecords
))
772 err
= fsBTEndOfIterationErr
;
776 // we did not find the record but the index is already positioned correctly
777 if ((foundRecord
== false) && (index
!= ((NodeDescPtr
) node
.buffer
)->numRecords
))
780 // we found the record OR we have to look in the next node
781 if (index
< ((NodeDescPtr
) node
.buffer
)->numRecords
-1)
787 if (right
.buffer
== nil
)
789 nodeNum
= ((NodeDescPtr
) node
.buffer
)->fLink
;
792 err
= GetNode (btreePtr
, nodeNum
, &right
);
795 err
= fsBTEndOfIterationErr
;
799 // Before we stomp on "left", we'd better release it if needed
800 if (left
.buffer
!= nil
) {
801 err
= ReleaseNode(btreePtr
, &left
);
810 else // operation == kBTreeCurrentRecord
812 // make sure we have something... <CS9>
813 if ((foundRecord
!= true) &&
814 (index
>= ((NodeDescPtr
) node
.buffer
)->numRecords
))
816 err
= fsBTEndOfIterationErr
;
821 //////////////////// Copy Record And Update Iterator ////////////////////////
825 // added check for errors <CS9>
826 err
= GetRecordByIndex (btreePtr
, node
.buffer
, index
, &keyPtr
, &recordPtr
, &len
);
829 if (recordLen
!= nil
)
834 ByteCount recordSize
;
836 recordSize
= record
->itemCount
* record
->itemSize
;
838 if (len
> recordSize
) len
= recordSize
;
840 BlockMoveData (recordPtr
, record
->bufferAddress
, len
);
843 if (iterator
!= nil
) // first & last do not require iterator
845 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
846 iterator
->hint
.nodeNum
= nodeNum
;
847 iterator
->hint
.index
= index
;
848 iterator
->hint
.reserved1
= 0;
849 iterator
->hint
.reserved2
= 0;
851 iterator
->version
= 0;
852 iterator
->reserved
= 0;
855 * Check for infinite loops by making sure we do not
856 * process more leaf records, than can possibly be (or the BTree header
857 * is seriously damaged)....a brute force method.
859 if ((operation
== kBTreeFirstRecord
) || (operation
== kBTreeLastRecord
))
860 iterator
->hitCount
= 1;
861 else if (operation
!= kBTreeCurrentRecord
)
862 iterator
->hitCount
+= 1;
863 /* Always use the highest max, in case the grows while iterating */
864 iterator
->maxLeafRecs
= max(btreePtr
->leafRecords
, iterator
->maxLeafRecs
);
867 if (iterator
->hitCount
> iterator
->maxLeafRecs
+ kNumLeafRecSlack
)
869 err
= fsBTInvalidNodeErr
;
870 MARK_VOLUMEDAMAGED(filePtr
);
875 BlockMoveData ((Ptr
)keyPtr
, (Ptr
)&iterator
->key
, CalcKeySize(btreePtr
, keyPtr
));
879 ///////////////////////////// Release Nodes /////////////////////////////////
881 err
= ReleaseNode (btreePtr
, &node
);
884 if (left
.buffer
!= nil
)
886 err
= ReleaseNode (btreePtr
, &left
);
890 if (right
.buffer
!= nil
)
892 err
= ReleaseNode (btreePtr
, &right
);
898 /////////////////////// Error - Clean Up and Exit ///////////////////////////
902 (void) ReleaseNode (btreePtr
, &left
);
903 (void) ReleaseNode (btreePtr
, &node
);
904 (void) ReleaseNode (btreePtr
, &right
);
906 if (recordLen
!= nil
)
911 iterator
->hint
.writeCount
= 0;
912 iterator
->hint
.nodeNum
= 0;
913 iterator
->hint
.index
= 0;
914 iterator
->hint
.reserved1
= 0;
915 iterator
->hint
.reserved2
= 0;
917 iterator
->version
= 0;
918 iterator
->reserved
= 0;
919 iterator
->key
.length16
= 0;
922 if ( err
== fsBTEmptyErr
|| err
== fsBTEndOfIterationErr
)
923 err
= fsBTRecordNotFoundErr
;
929 /*-------------------------------------------------------------------------------
930 Routine: BTIterateRecords
932 Function: Find a series of records
934 Input: filePtr - b-tree file
935 operation - iteration operation (first,next,prev,last)
936 iterator - pointer to iterator indicating start position
937 callBackProc - pointer to routince to process a record
938 callBackState - pointer to state data (used by callBackProc)
940 Output: iterator - iterator is updated to indicate new position
942 Result: noErr - success
944 -------------------------------------------------------------------------------*/
947 BTIterateRecords(FCB
*filePtr
, BTreeIterationOperation operation
, BTreeIterator
*iterator
,
948 IterateCallBackProcPtr callBackProc
, void * callBackState
)
951 BTreeControlBlockPtr btreePtr
;
957 BlockDescriptor left
, node
, right
;
961 ////////////////////////// Priliminary Checks ///////////////////////////////
964 left
.blockHeader
= nil
;
966 right
.blockHeader
= nil
;
968 node
.blockHeader
= nil
;
970 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
972 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
974 if ((operation
!= kBTreeFirstRecord
) &&
975 (operation
!= kBTreeNextRecord
) &&
976 (operation
!= kBTreeCurrentRecord
) &&
977 (operation
!= kBTreePrevRecord
) &&
978 (operation
!= kBTreeLastRecord
))
980 err
= fsInvalidIterationMovmentErr
;
984 /////////////////////// Find First or Last Record ///////////////////////////
986 if ((operation
== kBTreeFirstRecord
) || (operation
== kBTreeLastRecord
))
988 if (operation
== kBTreeFirstRecord
)
989 nodeNum
= btreePtr
->firstLeafNode
;
991 nodeNum
= btreePtr
->lastLeafNode
;
999 err
= GetNode(btreePtr
, nodeNum
, &node
);
1002 if ( ((NodeDescPtr
)node
.buffer
)->kind
!= kBTLeafNode
||
1003 ((NodeDescPtr
)node
.buffer
)->numRecords
<= 0 )
1005 err
= ReleaseNode(btreePtr
, &node
);
1008 err
= fsBTInvalidNodeErr
;
1009 MARK_VOLUMEDAMAGED(filePtr
);
1013 if (operation
== kBTreeFirstRecord
)
1016 index
= ((BTNodeDescriptor
*) node
.buffer
)->numRecords
- 1;
1021 //////////////////////// Find Iterator Position /////////////////////////////
1023 err
= FindIteratorPosition(btreePtr
, iterator
, &left
, &node
, &right
,
1024 &nodeNum
, &index
, &foundRecord
);
1025 if (err
== fsBTRecordNotFoundErr
)
1030 ///////////////////// Find Next Or Previous Record //////////////////////////
1032 if (operation
== kBTreePrevRecord
)
1040 if (left
.buffer
== nil
)
1042 nodeNum
= ((NodeDescPtr
) node
.buffer
)->bLink
;
1045 err
= GetNode(btreePtr
, nodeNum
, &left
);
1048 err
= fsBTStartOfIterationErr
;
1052 // Before we stomp on "right", we'd better release it if needed
1053 if (right
.buffer
!= nil
) {
1054 err
= ReleaseNode(btreePtr
, &right
);
1060 index
= ((NodeDescPtr
) node
.buffer
)->numRecords
-1;
1063 else if (operation
== kBTreeNextRecord
)
1065 if ((foundRecord
!= true) &&
1066 (((NodeDescPtr
)node
.buffer
)->fLink
== 0) &&
1067 (index
== ((NodeDescPtr
)node
.buffer
)->numRecords
))
1069 err
= fsBTEndOfIterationErr
;
1073 // we did not find the record but the index is already positioned correctly
1074 if ((foundRecord
== false) && (index
!= ((NodeDescPtr
)node
.buffer
)->numRecords
))
1077 // we found the record OR we have to look in the next node
1078 if (index
< ((NodeDescPtr
)node
.buffer
)->numRecords
-1)
1084 if (right
.buffer
== nil
)
1086 nodeNum
= ((NodeDescPtr
)node
.buffer
)->fLink
;
1089 err
= GetNode(btreePtr
, nodeNum
, &right
);
1092 err
= fsBTEndOfIterationErr
;
1096 // Before we stomp on "left", we'd better release it if needed
1097 if (left
.buffer
!= nil
) {
1098 err
= ReleaseNode(btreePtr
, &left
);
1107 else // operation == kBTreeCurrentRecord
1109 // make sure we have something... <CS9>
1110 if ((foundRecord
!= true) &&
1111 (index
>= ((NodeDescPtr
)node
.buffer
)->numRecords
))
1113 err
= fsBTEndOfIterationErr
;
1118 //////////////////// Process Records Using Callback ////////////////////////
1121 err
= GetRecordByIndex(btreePtr
, node
.buffer
, index
, &keyPtr
, &recordPtr
, &len
);
1128 if (callBackProc(keyPtr
, recordPtr
, callBackState
) == 0)
1131 if ((index
+1) < ((NodeDescPtr
)node
.buffer
)->numRecords
) {
1134 if (right
.buffer
== nil
)
1136 nodeNum
= ((NodeDescPtr
)node
.buffer
)->fLink
;
1139 err
= GetNode(btreePtr
, nodeNum
, &right
);
1142 err
= fsBTEndOfIterationErr
;
1146 // Before we stomp on "left", we'd better release it if needed
1147 if (left
.buffer
!= nil
) {
1148 err
= ReleaseNode(btreePtr
, &left
);
1156 err
= GetRecordByIndex(btreePtr
, node
.buffer
, index
,
1157 &keyPtr
, &recordPtr
, &len
);
1165 ///////////////// Update Iterator to Last Item Processed /////////////////////
1168 if (iterator
!= nil
) // first & last have optional iterator
1170 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
1171 iterator
->hint
.nodeNum
= nodeNum
;
1172 iterator
->hint
.index
= index
;
1173 iterator
->version
= 0;
1175 BlockMoveData((Ptr
)keyPtr
, (Ptr
)&iterator
->key
, CalcKeySize(btreePtr
, keyPtr
));
1180 ///////////////////////////// Release Nodes /////////////////////////////////
1182 err
= ReleaseNode(btreePtr
, &node
);
1185 if (left
.buffer
!= nil
)
1187 err
= ReleaseNode(btreePtr
, &left
);
1191 if (right
.buffer
!= nil
)
1193 err
= ReleaseNode(btreePtr
, &right
);
1199 /////////////////////// Error - Clean Up and Exit ///////////////////////////
1203 (void) ReleaseNode(btreePtr
, &left
);
1204 (void) ReleaseNode(btreePtr
, &node
);
1205 (void) ReleaseNode(btreePtr
, &right
);
1207 if (iterator
!= nil
)
1209 iterator
->hint
.writeCount
= 0;
1210 iterator
->hint
.nodeNum
= 0;
1211 iterator
->hint
.index
= 0;
1212 iterator
->version
= 0;
1213 iterator
->key
.length16
= 0;
1216 if ( err
== fsBTEmptyErr
|| err
== fsBTEndOfIterationErr
)
1217 err
= fsBTRecordNotFoundErr
;
1223 //////////////////////////////// BTInsertRecord /////////////////////////////////
1225 OSStatus
BTInsertRecord (FCB
*filePtr
,
1226 BTreeIterator
*iterator
,
1227 FSBufferDescriptor
*record
,
1231 BTreeControlBlockPtr btreePtr
;
1232 TreePathTable treePathTable
;
1234 BlockDescriptor nodeRec
;
1235 UInt32 insertNodeNum
;
1239 ////////////////////////// Priliminary Checks ///////////////////////////////
1241 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
1242 nodeRec
.blockHeader
= nil
;
1244 err
= CheckInsertParams (filePtr
, iterator
, record
, recordLen
);
1248 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1250 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1253 ///////////////////////// Find Insert Position //////////////////////////////
1255 // always call SearchTree for Insert
1256 err
= SearchTree (btreePtr
, &iterator
->key
, treePathTable
, &insertNodeNum
, &nodeRec
, &index
);
1258 switch (err
) // set/replace/insert decision point
1260 case noErr
: err
= fsBTDuplicateRecordErr
;
1263 case fsBTRecordNotFoundErr
: break;
1265 case fsBTEmptyErr
: // if tree empty add 1st leaf node
1267 if (BTAvailableNodes(btreePtr
) == 0)
1269 err
= ExtendBTree (btreePtr
, btreePtr
->totalNodes
+ 1);
1270 M_ExitOnError (err
);
1273 err
= AllocateNode (btreePtr
, &insertNodeNum
);
1274 M_ExitOnError (err
);
1276 err
= GetNewNode (btreePtr
, insertNodeNum
, &nodeRec
);
1277 M_ExitOnError (err
);
1280 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1282 ((NodeDescPtr
)nodeRec
.buffer
)->kind
= kBTLeafNode
;
1283 ((NodeDescPtr
)nodeRec
.buffer
)->height
= 1;
1285 recordFit
= InsertKeyRecord (btreePtr
, nodeRec
.buffer
, 0,
1286 &iterator
->key
, KeyLength(btreePtr
, &iterator
->key
),
1287 record
->bufferAddress
, recordLen
);
1288 if (recordFit
!= true)
1290 err
= fsBTRecordTooLargeErr
;
1295 * Update the B-tree control block. Do this before
1296 * calling UpdateNode since it will compare the node's
1297 * height with treeDepth.
1299 btreePtr
->treeDepth
= 1;
1300 btreePtr
->rootNode
= insertNodeNum
;
1301 btreePtr
->firstLeafNode
= insertNodeNum
;
1302 btreePtr
->lastLeafNode
= insertNodeNum
;
1304 err
= UpdateNode (btreePtr
, &nodeRec
, 0, kLockTransaction
);
1305 M_ExitOnError (err
);
1307 M_BTreeHeaderDirty (btreePtr
);
1311 default: goto ErrorExit
;
1317 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1319 recordFit
= InsertKeyRecord (btreePtr
, nodeRec
.buffer
, index
,
1320 &iterator
->key
, KeyLength(btreePtr
, &iterator
->key
),
1321 record
->bufferAddress
, recordLen
);
1322 if (recordFit
== true)
1324 err
= UpdateNode (btreePtr
, &nodeRec
, 0, kLockTransaction
);
1325 M_ExitOnError (err
);
1331 /////////////////////// Extend File If Necessary ////////////////////////////
1333 nodesNeeded
= (SInt32
)btreePtr
->treeDepth
+ 1 - BTAvailableNodes(btreePtr
);
1334 if (nodesNeeded
> 0)
1336 nodesNeeded
+= (SInt32
)btreePtr
->totalNodes
;
1337 if (nodesNeeded
> CalcMapBits (btreePtr
)) // we'll need to add a map node too!
1340 err
= ExtendBTree (btreePtr
, nodesNeeded
);
1341 M_ExitOnError (err
);
1344 // no need to delete existing record
1346 err
= InsertTree (btreePtr
, treePathTable
, &iterator
->key
, record
->bufferAddress
,
1347 recordLen
, &nodeRec
, index
, 1, kInsertRecord
, &insertNodeNum
);
1348 M_ExitOnError (err
);
1351 //////////////////////////////// Success ////////////////////////////////////
1354 ++btreePtr
->writeCount
;
1355 ++btreePtr
->leafRecords
;
1356 M_BTreeHeaderDirty (btreePtr
);
1359 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
1360 iterator
->hint
.nodeNum
= insertNodeNum
;
1361 iterator
->hint
.index
= 0; // unused
1362 iterator
->hint
.reserved1
= 0;
1363 iterator
->hint
.reserved2
= 0;
1368 ////////////////////////////// Error Exit ///////////////////////////////////
1372 (void) ReleaseNode (btreePtr
, &nodeRec
);
1374 iterator
->hint
.writeCount
= 0;
1375 iterator
->hint
.nodeNum
= 0;
1376 iterator
->hint
.index
= 0;
1377 iterator
->hint
.reserved1
= 0;
1378 iterator
->hint
.reserved2
= 0;
1380 if (err
== fsBTEmptyErr
)
1381 err
= fsBTRecordNotFoundErr
;
1387 //////////////////////////////// BTReplaceRecord ////////////////////////////////
1389 OSStatus
BTReplaceRecord (FCB
*filePtr
,
1390 BTreeIterator
*iterator
,
1391 FSBufferDescriptor
*record
,
1395 BTreeControlBlockPtr btreePtr
;
1396 TreePathTable treePathTable
;
1398 BlockDescriptor nodeRec
;
1399 UInt32 insertNodeNum
;
1405 ////////////////////////// Priliminary Checks ///////////////////////////////
1407 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
1408 nodeRec
.blockHeader
= nil
;
1410 err
= CheckInsertParams (filePtr
, iterator
, record
, recordLen
);
1414 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1416 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1418 ////////////////////////////// Take A Hint //////////////////////////////////
1420 err
= IsItAHint (btreePtr
, iterator
, &validHint
);
1421 M_ExitOnError (err
);
1425 insertNodeNum
= iterator
->hint
.nodeNum
;
1427 err
= GetNode (btreePtr
, insertNodeNum
, &nodeRec
);
1431 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1433 err
= TrySimpleReplace (btreePtr
, nodeRec
.buffer
, iterator
, record
, recordLen
, &recordFit
);
1434 M_ExitOnError (err
);
1438 err
= UpdateNode (btreePtr
, &nodeRec
, 0, 0);
1439 M_ExitOnError (err
);
1441 ++btreePtr
->numValidHints
;
1447 (void) BTInvalidateHint( iterator
);
1450 err
= ReleaseNode (btreePtr
, &nodeRec
);
1451 M_ExitOnError (err
);
1455 (void) BTInvalidateHint( iterator
);
1460 ////////////////////////////// Get A Clue ///////////////////////////////////
1462 err
= SearchTree (btreePtr
, &iterator
->key
, treePathTable
, &insertNodeNum
, &nodeRec
, &index
);
1463 M_ExitOnError (err
); // record must exit for Replace
1465 // optimization - if simple replace will work then don't extend btree
1466 // \80\80 if we tried this before, and failed because it wouldn't fit then we shouldn't try this again...
1469 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1471 err
= TrySimpleReplace (btreePtr
, nodeRec
.buffer
, iterator
, record
, recordLen
, &recordFit
);
1472 M_ExitOnError (err
);
1476 err
= UpdateNode (btreePtr
, &nodeRec
, 0, 0);
1477 M_ExitOnError (err
);
1483 //////////////////////////// Make Some Room /////////////////////////////////
1485 nodesNeeded
= (SInt32
)btreePtr
->treeDepth
+ 1 - BTAvailableNodes(btreePtr
);
1486 if (nodesNeeded
> 0)
1488 nodesNeeded
+= (SInt32
)btreePtr
->totalNodes
;
1489 if (nodesNeeded
> CalcMapBits (btreePtr
)) // we'll need to add a map node too!
1492 err
= ExtendBTree (btreePtr
, nodesNeeded
);
1493 M_ExitOnError (err
);
1497 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1499 DeleteRecord (btreePtr
, nodeRec
.buffer
, index
); // delete existing key/record
1501 err
= InsertTree (btreePtr
, treePathTable
, &iterator
->key
, record
->bufferAddress
,
1502 recordLen
, &nodeRec
, index
, 1, kReplaceRecord
, &insertNodeNum
);
1503 M_ExitOnError (err
);
1505 ++btreePtr
->writeCount
; /* writeCount changes only if the tree structure changed */
1509 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
1510 iterator
->hint
.nodeNum
= insertNodeNum
;
1511 iterator
->hint
.index
= 0; // unused
1512 iterator
->hint
.reserved1
= 0;
1513 iterator
->hint
.reserved2
= 0;
1518 ////////////////////////////// Error Exit ///////////////////////////////////
1522 (void) ReleaseNode (btreePtr
, &nodeRec
);
1524 iterator
->hint
.writeCount
= 0;
1525 iterator
->hint
.nodeNum
= 0;
1526 iterator
->hint
.index
= 0;
1527 iterator
->hint
.reserved1
= 0;
1528 iterator
->hint
.reserved2
= 0;
1535 //////////////////////////////// BTUpdateRecord ////////////////////////////////
1538 BTUpdateRecord(FCB
*filePtr
, BTreeIterator
*iterator
,
1539 IterateCallBackProcPtr callBackProc
, void * callBackState
)
1542 BTreeControlBlockPtr btreePtr
;
1543 TreePathTable treePathTable
;
1544 BlockDescriptor nodeRec
;
1545 RecordPtr recordPtr
;
1547 UInt32 insertNodeNum
;
1553 ////////////////////////// Priliminary Checks ///////////////////////////////
1555 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
1556 nodeRec
.blockHeader
= nil
;
1558 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1560 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
1562 ////////////////////////////// Take A Hint //////////////////////////////////
1564 err
= IsItAHint (btreePtr
, iterator
, &validHint
);
1565 M_ExitOnError (err
);
1569 insertNodeNum
= iterator
->hint
.nodeNum
;
1571 err
= GetNode (btreePtr
, insertNodeNum
, &nodeRec
);
1574 if (((NodeDescPtr
)nodeRec
.buffer
)->kind
== kBTLeafNode
&&
1575 SearchNode (btreePtr
, nodeRec
.buffer
, &iterator
->key
, &index
))
1577 err
= GetRecordByIndex(btreePtr
, nodeRec
.buffer
, index
, &keyPtr
, &recordPtr
, &recordLen
);
1578 M_ExitOnError (err
);
1581 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1583 err
= callBackProc(keyPtr
, recordPtr
, callBackState
);
1584 M_ExitOnError (err
);
1586 err
= UpdateNode (btreePtr
, &nodeRec
, 0, 0);
1587 M_ExitOnError (err
);
1589 ++btreePtr
->numValidHints
;
1595 (void) BTInvalidateHint( iterator
);
1598 err
= ReleaseNode (btreePtr
, &nodeRec
);
1599 M_ExitOnError (err
);
1603 (void) BTInvalidateHint( iterator
);
1607 ////////////////////////////// Get A Clue ///////////////////////////////////
1609 err
= SearchTree (btreePtr
, &iterator
->key
, treePathTable
, &insertNodeNum
, &nodeRec
, &index
);
1610 M_ExitOnError (err
);
1612 err
= GetRecordByIndex(btreePtr
, nodeRec
.buffer
, index
, &keyPtr
, &recordPtr
, &recordLen
);
1613 M_ExitOnError (err
);
1616 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1618 err
= callBackProc(keyPtr
, recordPtr
, callBackState
);
1619 M_ExitOnError (err
);
1621 err
= UpdateNode (btreePtr
, &nodeRec
, 0, 0);
1622 M_ExitOnError (err
);
1626 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
1627 iterator
->hint
.nodeNum
= insertNodeNum
;
1628 iterator
->hint
.index
= 0;
1629 iterator
->hint
.reserved1
= 0;
1630 iterator
->hint
.reserved2
= 0;
1633 ////////////////////////////// Error Exit ///////////////////////////////////
1637 (void) ReleaseNode (btreePtr
, &nodeRec
);
1639 iterator
->hint
.writeCount
= 0;
1640 iterator
->hint
.nodeNum
= 0;
1641 iterator
->hint
.index
= 0;
1642 iterator
->hint
.reserved1
= 0;
1643 iterator
->hint
.reserved2
= 0;
1649 //////////////////////////////// BTDeleteRecord /////////////////////////////////
1651 OSStatus
BTDeleteRecord (FCB
*filePtr
,
1652 BTreeIterator
*iterator
)
1655 BTreeControlBlockPtr btreePtr
;
1656 TreePathTable treePathTable
;
1657 BlockDescriptor nodeRec
;
1663 ////////////////////////// Priliminary Checks ///////////////////////////////
1665 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
1666 nodeRec
.blockHeader
= nil
;
1668 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1669 M_ReturnErrorIf (iterator
== nil
, paramErr
);
1671 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1672 if (btreePtr
== nil
)
1674 err
= fsBTInvalidFileErr
;
1678 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1681 /////////////////////////////// Find Key ////////////////////////////////////
1683 //\80\80 check hint for simple delete case (index > 0, numRecords > 2)
1685 err
= SearchTree (btreePtr
, &iterator
->key
, treePathTable
, &nodeNum
, &nodeRec
, &index
);
1686 M_ExitOnError (err
); // record must exit for Delete
1689 /////////////////////// Extend File If Necessary ////////////////////////////
1691 nodesNeeded
= (SInt32
)btreePtr
->treeDepth
+ 1 - BTAvailableNodes(btreePtr
);
1692 if ((btreePtr
->attributes
& kBTVariableIndexKeysMask
) && (nodesNeeded
> 0))
1694 nodesNeeded
+= (SInt32
)btreePtr
->totalNodes
;
1695 if (nodesNeeded
> CalcMapBits (btreePtr
))
1698 err
= ExtendBTree (btreePtr
, nodesNeeded
);
1699 M_ExitOnError (err
);
1702 ///////////////////////////// Delete Record /////////////////////////////////
1704 err
= DeleteTree (btreePtr
, treePathTable
, &nodeRec
, index
, 1);
1705 M_ExitOnError (err
);
1707 ++btreePtr
->writeCount
;
1708 --btreePtr
->leafRecords
;
1709 M_BTreeHeaderDirty (btreePtr
);
1711 iterator
->hint
.nodeNum
= 0;
1715 ////////////////////////////// Error Exit ///////////////////////////////////
1718 (void) ReleaseNode (btreePtr
, &nodeRec
);
1725 OSStatus
BTGetInformation (FCB
*filePtr
,
1727 BTreeInfoRec
*info
)
1729 #pragma unused (version)
1731 BTreeControlBlockPtr btreePtr
;
1734 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1736 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1740 * This should not require the whole tree to be locked, just maybe the BTreeControlBlockPtr
1742 * REQUIRE_FILE_LOCK(btreePtr->fileRefNum, true);
1745 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1746 M_ReturnErrorIf (info
== nil
, paramErr
);
1748 //\80\80 check version?
1750 info
->nodeSize
= btreePtr
->nodeSize
;
1751 info
->maxKeyLength
= btreePtr
->maxKeyLength
;
1752 info
->treeDepth
= btreePtr
->treeDepth
;
1753 info
->numRecords
= btreePtr
->leafRecords
;
1754 info
->numNodes
= btreePtr
->totalNodes
;
1755 info
->numFreeNodes
= btreePtr
->freeNodes
;
1756 info
->lastfsync
= btreePtr
->lastfsync
;
1757 info
->keyCompareType
= btreePtr
->keyCompareType
;
1764 BTIsDirty(FCB
*filePtr
)
1766 BTreeControlBlockPtr btreePtr
;
1768 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1769 return TreeIsDirty(btreePtr
);
1772 /*-------------------------------------------------------------------------------
1773 Routine: BTFlushPath - Flush BTreeControlBlock to Header Node.
1775 Function: Brief_description_of_the_function_and_any_side_effects
1778 Input: pathPtr - pointer to path control block for B*Tree file to flush
1782 Result: noErr - success
1784 -------------------------------------------------------------------------------*/
1786 OSStatus
BTFlushPath (FCB
*filePtr
)
1789 BTreeControlBlockPtr btreePtr
;
1792 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1794 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1796 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1798 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
1800 err
= UpdateHeader (btreePtr
, false);
1806 /*-------------------------------------------------------------------------------
1807 Routine: BTReload - Reload B-tree Header Data.
1809 Function: Reload B-tree header data from disk. This is called after fsck
1810 has made repairs to the root filesystem. The filesystem is
1811 mounted read-only when BTReload is caled.
1814 Input: filePtr - the B*Tree file that needs its header updated
1818 Result: noErr - success
1820 -------------------------------------------------------------------------------*/
1823 BTReloadData(FCB
*filePtr
)
1826 BTreeControlBlockPtr btreePtr
;
1827 BlockDescriptor node
;
1828 BTHeaderRec
*header
;
1832 node
.blockHeader
= nil
;
1834 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1835 if (btreePtr
== nil
)
1836 return (fsBTInvalidFileErr
);
1838 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1840 err
= GetNode(btreePtr
, kHeaderNodeNum
, &node
);
1844 header
= (BTHeaderRec
*)((char *)node
.buffer
+ sizeof(BTNodeDescriptor
));
1845 if ((err
= VerifyHeader (filePtr
, header
)) == 0) {
1846 btreePtr
->treeDepth
= header
->treeDepth
;
1847 btreePtr
->rootNode
= header
->rootNode
;
1848 btreePtr
->leafRecords
= header
->leafRecords
;
1849 btreePtr
->firstLeafNode
= header
->firstLeafNode
;
1850 btreePtr
->lastLeafNode
= header
->lastLeafNode
;
1851 btreePtr
->maxKeyLength
= header
->maxKeyLength
;
1852 btreePtr
->totalNodes
= header
->totalNodes
;
1853 btreePtr
->freeNodes
= header
->freeNodes
;
1854 btreePtr
->btreeType
= header
->btreeType
;
1856 btreePtr
->flags
&= (~kBTHeaderDirty
);
1859 (void) ReleaseNode(btreePtr
, &node
);
1865 /*-------------------------------------------------------------------------------
1866 Routine: BTInvalidateHint - Invalidates the hint within a BTreeInterator.
1868 Function: Invalidates the hint within a BTreeInterator.
1871 Input: iterator - pointer to BTreeIterator
1873 Output: iterator - iterator with the hint.nodeNum cleared
1875 Result: noErr - success
1876 paramErr - iterator == nil
1877 -------------------------------------------------------------------------------*/
1880 OSStatus
BTInvalidateHint (BTreeIterator
*iterator
)
1882 if (iterator
== nil
)
1885 iterator
->hint
.nodeNum
= 0;
1893 /*-------------------------------------------------------------------------------
1894 Routine: BTGetLastSync
1896 Function: Returns the last time that this btree was flushed, does not include header.
1898 Input: filePtr - pointer file control block
1900 Output: lastfsync - time in seconds of last update
1902 Result: noErr - success
1903 paramErr - iterator == nil
1904 -------------------------------------------------------------------------------*/
1907 OSStatus
BTGetLastSync (FCB
*filePtr
,
1910 BTreeControlBlockPtr btreePtr
;
1913 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1915 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1917 /* Maybe instead of requiring a lock..an atomic set might be more appropriate */
1918 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
1920 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1921 M_ReturnErrorIf (lastsync
== nil
, paramErr
);
1923 *lastsync
= btreePtr
->lastfsync
;
1931 /*-------------------------------------------------------------------------------
1932 Routine: BTSetLastSync
1934 Function: Sets the last time that this btree was flushed, does not include header.
1937 Input: fcb - pointer file control block
1939 Output: lastfsync - time in seconds of last update
1941 Result: noErr - success
1942 paramErr - iterator == nil
1943 -------------------------------------------------------------------------------*/
1946 OSStatus
BTSetLastSync (FCB
*filePtr
,
1949 BTreeControlBlockPtr btreePtr
;
1952 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1954 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1956 /* Maybe instead of requiring a lock..an atomic set might be more appropriate */
1957 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
1959 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1960 M_ReturnErrorIf (lastsync
== nil
, paramErr
);
1962 btreePtr
->lastfsync
= lastsync
;
1969 OSStatus
BTHasContiguousNodes (FCB
*filePtr
)
1971 BTreeControlBlockPtr btreePtr
;
1974 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1976 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1978 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
1980 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1982 return NodesAreContiguous(FCBTOVCB(filePtr
), filePtr
, btreePtr
->nodeSize
);
1986 /*-------------------------------------------------------------------------------
1987 Routine: BTGetUserData
1989 Function: Read the user data area of the b-tree header node.
1991 -------------------------------------------------------------------------------*/
1994 BTGetUserData(FCB
*filePtr
, void * dataPtr
, int dataSize
)
1996 BTreeControlBlockPtr btreePtr
;
1997 BlockDescriptor node
;
2001 if (dataSize
> kBTreeHeaderUserBytes
)
2004 node
.blockHeader
= nil
;
2006 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
2007 if (btreePtr
== nil
)
2008 return (fsBTInvalidFileErr
);
2010 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
2012 err
= GetNode(btreePtr
, kHeaderNodeNum
, &node
);
2016 offset
= (char *)node
.buffer
+ sizeof(BTNodeDescriptor
) + sizeof(BTHeaderRec
);
2017 bcopy(offset
, dataPtr
, dataSize
);
2019 (void) ReleaseNode(btreePtr
, &node
);
2025 /*-------------------------------------------------------------------------------
2026 Routine: BTSetUserData
2028 Function: Write the user data area of the b-tree header node.
2029 -------------------------------------------------------------------------------*/
2032 BTSetUserData(FCB
*filePtr
, void * dataPtr
, int dataSize
)
2034 BTreeControlBlockPtr btreePtr
;
2035 BlockDescriptor node
;
2039 if (dataSize
> kBTreeHeaderUserBytes
)
2042 node
.blockHeader
= nil
;
2044 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
2045 if (btreePtr
== nil
)
2046 return (fsBTInvalidFileErr
);
2048 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
2050 err
= GetNode(btreePtr
, kHeaderNodeNum
, &node
);
2054 ModifyBlockStart(btreePtr
->fileRefNum
, &node
);
2056 offset
= (char *)node
.buffer
+ sizeof(BTNodeDescriptor
) + sizeof(BTHeaderRec
);
2057 bcopy(dataPtr
, offset
, dataSize
);
2059 err
= UpdateNode (btreePtr
, &node
, 0, 0);