2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
25 Contains: Implementation of public interface routines for B-tree manager.
29 Written by: Gordon Sheridan and Bill Bruffey
31 Copyright: © 1992-1999 by Apple Computer, Inc., all rights reserved.
37 Other Contact: Mark Day
39 Technology: File Systems
47 Change History (most recent first):
48 <MOSXS> 9/22/99 ser Added routines BTGetLastSync and BTSetLastSync
49 <MOSXS> 6/1/99 djb Sync up with Mac OS 8.6.
50 <MOSXS> 6/30/98 djb In BTOpenPath make sure nodes are contiguous on disk (radar #2249539).
51 <MOSXS> 4/15/98 djb In BTOpenPath need to clear nodeRec.buffer if GetBlockProc fails.
52 <MOSXS> 4/11/98 djb Add RequireFileLock checking to all external entry points.
54 <MOSXS> 03/23/98 djb In BTOpenPath use kTrashBlock option when releasing the header so
55 that we get a full node when we call GetNode.
57 <CS9> 12/12/97 djb Radar #2202682, BTIterateRecord with kBTreeCurrentRecord was not
58 checking if we had a record and could call BlockMove with an
59 uninitialize source pointer (causing a bus error).
60 <CS8> 10/24/97 msd In BTIterateRecord, when moving to the previous or next record
61 and we have to move to another node, see if we need to release
62 the node about to be "shifted out" (opposite sibling of the
63 direction we need to move).
64 <CS7> 7/25/97 DSH BTSearchRecord now takes a heuristicHint, nodeNum, and tries it
65 before calling SearchBTree
66 <CS6> 7/24/97 djb GetBlockProc now take a file refnum instead of an FCB ptr.
67 <CS5> 7/22/97 djb Move trace points from BTreeWrapper.c to here.
68 <CS4> 7/21/97 djb LogEndTime now takes an error code.
69 <CS3> 7/16/97 DSH FilesInternal.i renamed FileMgrInternal.i to avoid name
71 <CS2> 5/19/97 djb Add summary traces to BTIterateRecord.
72 <CS1> 4/23/97 djb first checked in
74 <HFS7> 2/19/97 djb Enable variable sized index keys for HFS+ volumes. Added node
75 cache to support nodes larger than 512 bytes.
76 <HFS6> 1/27/97 djb Calls to InsertTree and DeleteTree are now recursive (to support
77 variable sized index keys).
78 <HFS5> 1/13/97 djb Added support for getting current record to BTIterateRecord.
79 <HFS4> 1/6/97 djb Initialize "BigKeys" attribute in BTOpen.
80 <HFS3> 1/3/97 djb Added support for large keys.
81 <HFS2> 12/23/96 djb On exit map fsBTEmptyErr and fsBTEndOfIterationErr to
82 fsBTRecordNotFoundErr.
83 <HFS1> 12/19/96 djb first checked in
85 History applicable to original Scarecrow Design:
87 <13> 10/25/96 ser Changing for new VFPI
88 <12> 10/18/96 ser Converting over VFPI changes
89 <11> 9/17/96 dkh More BTree statistics. Modified hint checks to not bail out when
90 an error is returned from GetNode.
91 <10> 9/16/96 dkh Revised BTree statistics.
92 <9> 8/23/96 dkh Remove checks for multiple paths to BTree file. Need to add
93 equivalent mechanism later.
94 <8> 6/20/96 dkh Radar #1358740. Switch from using Pools to debug MemAllocators.
95 <7> 3/14/96 jev Fix BTreeSetRecord, recordFound was not set for the case of a
96 simple replace causing the leafRecords count to get bumped even
97 though we didn't have to add a record.
98 <6> 3/1/96 prp Fix lint problems. Bug in BTSetRecord that does not initialize
100 <5> 1/22/96 dkh Add #include Memory.h
101 <4> 1/10/96 msd Use the real function names from Math64.i.
102 <3> 1/4/96 jev Fix BTItererateRecord for the condition when the iterator
103 position routine does not find the record and we are looking for
104 the next record. In such a case, if the node's forrward link is
105 non-zero, we have to keep iterating next and not return
106 fsBTEndOfIterationErr error.
107 <2> 12/7/95 dkh D10E2 build. Changed usage of Ref data type to LogicalAddress.
108 <1> 10/18/95 rst Moved from Scarecrow project.
110 <24> 7/18/95 mbb Change MoveData & ClearBytes to BlockMoveData & BlockZero.
111 <23> 1/31/95 prp GetBlockProc interface uses a 64 bit node number.
112 <22> 1/12/95 wjk Adopt Model FileSystem changes in D5.
113 <21> 11/16/94 prp Add IsItAHint routine and use it whenever hint's node number was
115 <20> 11/10/94 prp BTGetInfo name collides with the same name in FileManagerPriv.i.
116 Change it to BTGetInformation.
117 <19> 9/30/94 prp Get in sync with D2 interface changes.
118 <18> 7/22/94 wjk Convert to the new set of header files.
119 <17> 12/9/93 wjk Cleanup usage of char, Byte, int8, UInt8, etc.
120 <16> 12/2/93 wjk Move from Makefiles to BuildFiles. Fit into the ModernOS and
122 <15> 11/30/93 wjk Move from Makefiles to BuildFiles. Fit into the ModernOS and
124 <14> 9/30/93 gs Rename E_NoGetNodeProc and E_NoReleaseNodeProc to
126 <13> 8/31/93 prp Use Set64U instead of Set64.
127 <12> 8/16/93 prp In BTSearchRecord, if the input hint found the node and record,
128 set the local nodeNum variable correctly so that the resultant
129 iterator gets set correctly.
130 <11> 7/1/93 gs Fix bug in BTIterateRecord related to kBTreePrevRecord
132 <10> 6/2/93 gs Update for changes to FSErrors.h and add some comments.
133 <9> 5/24/93 gs Fix bug in BTInsert/Set/ReplaceRecord which didn't set node hint
134 properly in some cases.
135 <8> 5/24/93 gs Do NOT map fsBTEmptyErr to fsBTRecordNotFoundErr in BTSearchRecord.
136 <7> 5/24/93 gs Rename BTFlush to BTFlushPath.
137 <6> 5/21/93 gs Add hint optimization to Set/Replace routines.
138 <5> 5/10/93 gs Remove Panic from BTInitialize for small logicalEOF. Implement
139 Insert, Set, Replace, and Delete.
140 <4> 3/23/93 gs Finish BTInitialize.
141 <3> 2/8/93 gs Implement BTSearchRecord and BTIterateRecord.
142 <2> 12/8/92 gs Implement Open and Close routines.
143 <1> 11/15/92 gs first checked in
147 #include "../headers/BTreesPrivate.h"
150 * The amount that the BTree header leaf count can be wrong before we assume
151 * it is in an infinite loop.
153 #define kNumLeafRecSlack 10
155 /* BTree accessor routines */
156 extern OSStatus
GetBTreeBlock(FileReference vp
, UInt32 blockNum
, GetBlockOptions options
, BlockDescriptor
*block
);
157 extern OSStatus
SetBTreeBlockSize(FileReference vp
, ByteCount blockSize
, ItemCount minBlockCount
);
158 extern OSStatus
ExtendBTreeFile(FileReference vp
, FSSize minEOF
, FSSize maxEOF
);
159 extern OSStatus
ReleaseBTreeBlock(FileReference vp
, BlockDescPtr blockPtr
, ReleaseBlockOptions options
);
161 //////////////////////////////////// Globals ////////////////////////////////////
164 /////////////////////////// BTree Module Entry Points ///////////////////////////
168 /*-------------------------------------------------------------------------------
169 Routine: BTOpenPath - Open a file for access as a B*Tree.
171 Function: Create BTree control block for a file, if necessary. Validates the
172 file to be sure it looks like a BTree file.
175 Input: filePtr - pointer to file to open as a B-tree
176 keyCompareProc - pointer to client's KeyCompare function
178 Result: noErr - success
179 paramErr - required ptr was nil
183 -------------------------------------------------------------------------------*/
185 OSStatus
BTOpenPath(FCB
*filePtr
, KeyCompareProcPtr keyCompareProc
)
188 BTreeControlBlockPtr btreePtr
;
192 ////////////////////// Preliminary Error Checking ///////////////////////////
194 if ( filePtr
== nil
)
200 * Subsequent opens allow key compare proc to be changed.
202 if ( filePtr
->fcbBTCBPtr
!= nil
&& keyCompareProc
!= nil
) {
203 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
204 btreePtr
->keyCompareProc
= keyCompareProc
;
208 if ( filePtr
->fcbEOF
< kMinNodeSize
)
209 return fsBTInvalidFileErr
;
212 //////////////////////// Allocate Control Block /////////////////////////////
214 btreePtr
= (BTreeControlBlock
*) NewPtrSysClear( sizeof( BTreeControlBlock
) );
217 Panic ("\pBTOpen: no memory for btreePtr.");
221 btreePtr
->getBlockProc
= GetBTreeBlock
;
222 btreePtr
->releaseBlockProc
= ReleaseBTreeBlock
;
223 btreePtr
->setEndOfForkProc
= ExtendBTreeFile
;
224 btreePtr
->keyCompareProc
= keyCompareProc
;
226 /////////////////////////// Read Header Node ////////////////////////////////
228 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
229 btreePtr
->fileRefNum
= GetFileRefNumFromFCB(filePtr
);
230 filePtr
->fcbBTCBPtr
= (Ptr
) btreePtr
; // attach btree cb to file
232 /* The minimum node size is the physical block size */
233 nodeRec
.blockSize
= VTOHFS(btreePtr
->fileRefNum
)->hfs_phys_block_size
;
235 /* Start with the allocation block size for regular files. */
236 if (FTOC(filePtr
)->c_fileid
>= kHFSFirstUserCatalogNodeID
)
238 nodeRec
.blockSize
= FCBTOVCB(filePtr
)->blockSize
;
240 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
242 // it is now safe to call M_ExitOnError (err)
244 err
= SetBTreeBlockSize (btreePtr
->fileRefNum
, nodeRec
.blockSize
, 1);
248 err
= GetBTreeBlock(btreePtr
->fileRefNum
,
254 nodeRec
.buffer
= nil
;
255 nodeRec
.blockHeader
= nil
;
256 Panic("\pBTOpen: getNodeProc returned error getting header node.");
259 ++btreePtr
->numGetNodes
;
260 header
= (BTHeaderRec
*) ((u_long
)nodeRec
.buffer
+ sizeof(BTNodeDescriptor
));
263 ///////////////////////////// verify header /////////////////////////////////
265 err
= VerifyHeader (filePtr
, header
);
269 ///////////////////// Initalize fields from header //////////////////////////
271 PanicIf ( (FCBTOVCB(filePtr
)->vcbSigWord
!= 0x4244) && (header
->nodeSize
== 512), "\p BTOpenPath: wrong node size for HFS+ volume!"); // 0x4244 = 'BD'
273 btreePtr
->treeDepth
= header
->treeDepth
;
274 btreePtr
->rootNode
= header
->rootNode
;
275 btreePtr
->leafRecords
= header
->leafRecords
;
276 btreePtr
->firstLeafNode
= header
->firstLeafNode
;
277 btreePtr
->lastLeafNode
= header
->lastLeafNode
;
278 btreePtr
->nodeSize
= header
->nodeSize
;
279 btreePtr
->maxKeyLength
= header
->maxKeyLength
;
280 btreePtr
->totalNodes
= header
->totalNodes
;
281 btreePtr
->freeNodes
= header
->freeNodes
;
282 if (FTOC(filePtr
)->c_fileid
>= kHFSFirstUserCatalogNodeID
)
283 filePtr
->ff_clumpsize
= header
->clumpSize
;
284 btreePtr
->btreeType
= header
->btreeType
;
286 btreePtr
->keyCompareType
= header
->keyCompareType
;
288 btreePtr
->attributes
= header
->attributes
;
290 if ( btreePtr
->maxKeyLength
> 40 )
291 btreePtr
->attributes
|= (kBTBigKeysMask
+ kBTVariableIndexKeysMask
); //\80\80 we need a way to save these attributes
293 /////////////////////// Initialize dynamic fields ///////////////////////////
295 btreePtr
->version
= kBTreeVersion
;
297 btreePtr
->writeCount
= 1;
299 /////////////////////////// Check Header Node ///////////////////////////////
301 // set kBadClose attribute bit, and UpdateNode
303 /* b-tree node size must be at least as big as the physical block size */
304 if (btreePtr
->nodeSize
< nodeRec
.blockSize
)
307 * If this tree has any records or the media is writeable then
308 * we cannot mount using the current physical block size.
310 if (btreePtr
->leafRecords
> 0 ||
311 VTOHFS(btreePtr
->fileRefNum
)->hfs_flags
& HFS_WRITEABLE_MEDIA
)
313 err
= fsBTBadNodeSize
;
318 // if nodeSize Matches then we don't need to release, just CheckNode
319 if ( btreePtr
->nodeSize
== nodeRec
.blockSize
)
321 err
= CheckNode (btreePtr
, nodeRec
.buffer
);
323 VTOVCB(btreePtr
->fileRefNum
)->vcbFlags
|= kHFS_DamagedVolume
;
328 err
= SetBTreeBlockSize (btreePtr
->fileRefNum
, btreePtr
->nodeSize
, 32);
332 * Need to use kTrashBlock option to force the
333 * buffer cache to read the entire node
335 err
= ReleaseBTreeBlock(btreePtr
->fileRefNum
, &nodeRec
, kTrashBlock
);
336 ++btreePtr
->numReleaseNodes
;
339 err
= GetNode (btreePtr
, kHeaderNodeNum
, &nodeRec
); // calls CheckNode...
343 //\80\80 total nodes * node size <= LEOF?
346 err
= ReleaseNode (btreePtr
, &nodeRec
);
350 * Under Mac OS, b-tree nodes can be non-contiguous on disk when the
351 * allocation block size is smaller than the b-tree node size.
353 * If journaling is turned on for this volume we can't deal with this
354 * situation and so we bail out. If journaling isn't on it's ok as
355 * hfs_strategy_fragmented() deals with it. Journaling can't support
356 * this because it assumes that if you give it a block that it's
357 * contiguous on disk.
359 if ( FCBTOHFS(filePtr
)->jnl
&& !NodesAreContiguous(FCBTOVCB(filePtr
), filePtr
, btreePtr
->nodeSize
) ) {
360 return fsBTInvalidNodeErr
;
363 //////////////////////////////// Success ////////////////////////////////////
365 //\80\80 align LEOF to multiple of node size? - just on close
370 /////////////////////// Error - Clean up and Exit ///////////////////////////
374 filePtr
->fcbBTCBPtr
= nil
;
375 (void) ReleaseNode (btreePtr
, &nodeRec
);
376 DisposePtr( (Ptr
) btreePtr
);
383 /*-------------------------------------------------------------------------------
384 Routine: BTClosePath - Flush BTree Header and Deallocate Memory for BTree.
386 Function: Flush the BTreeControlBlock fields to header node, and delete BTree control
387 block and key descriptor associated with the file if filePtr is last
388 path of type kBTreeType ('btre').
391 Input: filePtr - pointer to file to delete BTree control block for.
393 Result: noErr - success
396 -------------------------------------------------------------------------------*/
398 OSStatus
BTClosePath (FCB
*filePtr
)
401 BTreeControlBlockPtr btreePtr
;
403 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
406 return fsBTInvalidFileErr
;
408 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
410 ////////////////////// Check for other BTree Paths //////////////////////////
412 btreePtr
->attributes
&= ~kBTBadCloseMask
; // clear "bad close" attribute bit
413 err
= UpdateHeader (btreePtr
, true);
416 DisposePtr( (Ptr
) btreePtr
);
417 filePtr
->fcbBTCBPtr
= nil
;
421 /////////////////////// Error - Clean Up and Exit ///////////////////////////
430 /*-------------------------------------------------------------------------------
431 Routine: BTSearchRecord - Search BTree for a record with a matching key.
433 Function: Search for position in B*Tree indicated by searchKey. If a valid node hint
434 is provided, it will be searched first, then SearchTree will be called.
435 If a BTreeIterator is provided, it will be set to the position found as
436 a result of the search. If a record exists at that position, and a BufferDescriptor
437 is supplied, the record will be copied to the buffer (as much as will fit),
438 and recordLen will be set to the length of the record.
440 If an error other than fsBTRecordNotFoundErr occurs, the BTreeIterator, if any,
441 is invalidated, and recordLen is set to 0.
444 Input: pathPtr - pointer to path for BTree file.
445 searchKey - pointer to search key to match.
446 hintPtr - pointer to hint (may be nil)
448 Output: record - pointer to BufferDescriptor containing record
449 recordLen - length of data at recordPtr
450 iterator - pointer to BTreeIterator indicating position result of search
452 Result: noErr - success, record contains copy of record found
453 fsBTRecordNotFoundErr - record was not found, no data copied
454 fsBTInvalidFileErr - no BTreeControlBlock is allocated for the fork
455 fsBTInvalidKeyLengthErr -
457 -------------------------------------------------------------------------------*/
459 OSStatus
BTSearchRecord (FCB
*filePtr
,
460 BTreeIterator
*searchIterator
,
461 FSBufferDescriptor
*record
,
463 BTreeIterator
*resultIterator
)
466 BTreeControlBlockPtr btreePtr
;
467 TreePathTable treePathTable
;
469 BlockDescriptor node
;
477 if (filePtr
== nil
) return paramErr
;
478 if (searchIterator
== nil
) return paramErr
;
481 node
.blockHeader
= nil
;
483 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
484 if (btreePtr
== nil
) return fsBTInvalidFileErr
;
486 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
490 ////////////////////////////// Take A Hint //////////////////////////////////
492 err
= IsItAHint (btreePtr
, searchIterator
, &validHint
);
497 nodeNum
= searchIterator
->hint
.nodeNum
;
499 err
= GetNode (btreePtr
, nodeNum
, &node
);
502 if ( ((BTNodeDescriptor
*) node
.buffer
)->kind
== kBTLeafNode
&&
503 ((BTNodeDescriptor
*) node
.buffer
)->numRecords
> 0 )
505 foundRecord
= SearchNode (btreePtr
, node
.buffer
, &searchIterator
->key
, &index
);
507 //\80\80 if !foundRecord, we could still skip tree search if ( 0 < index < numRecords )
510 if (foundRecord
== false)
512 err
= ReleaseNode (btreePtr
, &node
);
517 ++btreePtr
->numValidHints
;
521 if( foundRecord
== false )
522 (void) BTInvalidateHint( searchIterator
);
526 //////////////////////////// Search The Tree ////////////////////////////////
528 if (foundRecord
== false)
530 err
= SearchTree ( btreePtr
, &searchIterator
->key
, treePathTable
, &nodeNum
, &node
, &index
);
533 case noErr
: foundRecord
= true; break;
534 case fsBTRecordNotFoundErr
: break;
535 default: goto ErrorExit
;
540 //////////////////////////// Get the Record /////////////////////////////////
542 if (foundRecord
== true)
544 //XXX Should check for errors! Or BlockMove could choke on recordPtr!!!
545 GetRecordByIndex (btreePtr
, node
.buffer
, index
, &keyPtr
, &recordPtr
, &len
);
547 if (recordLen
!= nil
) *recordLen
= len
;
551 ByteCount recordSize
;
553 recordSize
= record
->itemCount
* record
->itemSize
;
555 if (len
> recordSize
) len
= recordSize
;
557 BlockMoveData (recordPtr
, record
->bufferAddress
, len
);
562 /////////////////////// Success - Update Iterator ///////////////////////////
564 if (resultIterator
!= nil
)
566 resultIterator
->hint
.writeCount
= btreePtr
->writeCount
;
567 resultIterator
->hint
.nodeNum
= nodeNum
;
568 resultIterator
->hint
.index
= index
;
570 resultIterator
->hint
.reserved1
= 0;
571 resultIterator
->hint
.reserved2
= 0;
572 resultIterator
->version
= 0;
573 resultIterator
->reserved
= 0;
575 // copy the key in the BTree when found rather than searchIterator->key to get proper case/diacriticals
576 if (foundRecord
== true)
577 BlockMoveData ((Ptr
)keyPtr
, (Ptr
)&resultIterator
->key
, CalcKeySize(btreePtr
, keyPtr
));
579 BlockMoveData ((Ptr
)&searchIterator
->key
, (Ptr
)&resultIterator
->key
, CalcKeySize(btreePtr
, &searchIterator
->key
));
582 err
= ReleaseNode (btreePtr
, &node
);
585 if (foundRecord
== false) return fsBTRecordNotFoundErr
;
589 /////////////////////// Error - Clean Up and Exit ///////////////////////////
593 if (recordLen
!= nil
)
596 if (resultIterator
!= nil
)
598 resultIterator
->hint
.writeCount
= 0;
599 resultIterator
->hint
.nodeNum
= 0;
600 resultIterator
->hint
.index
= 0;
601 resultIterator
->hint
.reserved1
= 0;
602 resultIterator
->hint
.reserved2
= 0;
604 resultIterator
->version
= 0;
605 resultIterator
->reserved
= 0;
606 resultIterator
->key
.length16
= 0; // zero out two bytes to cover both types of keys
609 if ( err
== fsBTEmptyErr
)
610 err
= fsBTRecordNotFoundErr
;
617 /*-------------------------------------------------------------------------------
618 Routine: BTIterateRecord - Find the first, next, previous, or last record.
620 Function: Find the first, next, previous, or last record in the BTree
622 Input: pathPtr - pointer to path iterate records for.
623 operation - iteration operation (first,next,prev,last)
624 iterator - pointer to iterator indicating start position
626 Output: iterator - iterator is updated to indicate new position
627 newKeyPtr - pointer to buffer to copy key found by iteration
628 record - pointer to buffer to copy record found by iteration
629 recordLen - length of record
631 Result: noErr - success
633 -------------------------------------------------------------------------------*/
635 OSStatus
BTIterateRecord (FCB
*filePtr
,
636 BTreeIterationOperation operation
,
637 BTreeIterator
*iterator
,
638 FSBufferDescriptor
*record
,
642 BTreeControlBlockPtr btreePtr
;
650 BlockDescriptor left
, node
, right
;
654 ////////////////////////// Priliminary Checks ///////////////////////////////
657 left
.blockHeader
= nil
;
659 right
.blockHeader
= nil
;
661 node
.blockHeader
= nil
;
669 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
672 return fsBTInvalidFileErr
; //\80\80 handle properly
675 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
677 if ((operation
!= kBTreeFirstRecord
) &&
678 (operation
!= kBTreeNextRecord
) &&
679 (operation
!= kBTreeCurrentRecord
) &&
680 (operation
!= kBTreePrevRecord
) &&
681 (operation
!= kBTreeLastRecord
))
683 err
= fsInvalidIterationMovmentErr
;
687 /////////////////////// Find First or Last Record ///////////////////////////
689 if ((operation
== kBTreeFirstRecord
) || (operation
== kBTreeLastRecord
))
691 if (operation
== kBTreeFirstRecord
) nodeNum
= btreePtr
->firstLeafNode
;
692 else nodeNum
= btreePtr
->lastLeafNode
;
700 err
= GetNode (btreePtr
, nodeNum
, &node
);
703 if ( ((NodeDescPtr
) node
.buffer
)->kind
!= kBTLeafNode
||
704 ((NodeDescPtr
) node
.buffer
)->numRecords
<= 0 )
706 err
= ReleaseNode (btreePtr
, &node
);
709 err
= fsBTInvalidNodeErr
;
710 MARK_VOLUMEDAMAGED(filePtr
);
714 if (operation
== kBTreeFirstRecord
) index
= 0;
715 else index
= ((BTNodeDescriptor
*) node
.buffer
)->numRecords
- 1;
717 goto CopyData
; //\80\80 is there a cleaner way?
721 //////////////////////// Find Iterator Position /////////////////////////////
723 err
= FindIteratorPosition (btreePtr
, iterator
,
724 &left
, &node
, &right
, &nodeNum
, &index
, &foundRecord
);
728 ///////////////////// Find Next Or Previous Record //////////////////////////
730 if (operation
== kBTreePrevRecord
)
738 if (left
.buffer
== nil
)
740 nodeNum
= ((NodeDescPtr
) node
.buffer
)->bLink
;
743 err
= GetNode (btreePtr
, nodeNum
, &left
);
746 err
= fsBTStartOfIterationErr
;
750 // Before we stomp on "right", we'd better release it if needed
751 if (right
.buffer
!= nil
) {
752 err
= ReleaseNode(btreePtr
, &right
);
758 index
= ((NodeDescPtr
) node
.buffer
)->numRecords
-1;
761 else if (operation
== kBTreeNextRecord
)
763 if ((foundRecord
!= true) &&
764 (((NodeDescPtr
) node
.buffer
)->fLink
== 0) &&
765 (index
== ((NodeDescPtr
) node
.buffer
)->numRecords
))
767 err
= fsBTEndOfIterationErr
;
771 // we did not find the record but the index is already positioned correctly
772 if ((foundRecord
== false) && (index
!= ((NodeDescPtr
) node
.buffer
)->numRecords
))
775 // we found the record OR we have to look in the next node
776 if (index
< ((NodeDescPtr
) node
.buffer
)->numRecords
-1)
782 if (right
.buffer
== nil
)
784 nodeNum
= ((NodeDescPtr
) node
.buffer
)->fLink
;
787 err
= GetNode (btreePtr
, nodeNum
, &right
);
790 err
= fsBTEndOfIterationErr
;
794 // Before we stomp on "left", we'd better release it if needed
795 if (left
.buffer
!= nil
) {
796 err
= ReleaseNode(btreePtr
, &left
);
805 else // operation == kBTreeCurrentRecord
807 // make sure we have something... <CS9>
808 if ((foundRecord
!= true) &&
809 (index
>= ((NodeDescPtr
) node
.buffer
)->numRecords
))
811 err
= fsBTEndOfIterationErr
;
816 //////////////////// Copy Record And Update Iterator ////////////////////////
820 // added check for errors <CS9>
821 err
= GetRecordByIndex (btreePtr
, node
.buffer
, index
, &keyPtr
, &recordPtr
, &len
);
824 if (recordLen
!= nil
)
829 ByteCount recordSize
;
831 recordSize
= record
->itemCount
* record
->itemSize
;
833 if (len
> recordSize
) len
= recordSize
;
835 BlockMoveData (recordPtr
, record
->bufferAddress
, len
);
838 if (iterator
!= nil
) // first & last do not require iterator
840 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
841 iterator
->hint
.nodeNum
= nodeNum
;
842 iterator
->hint
.index
= index
;
843 iterator
->hint
.reserved1
= 0;
844 iterator
->hint
.reserved2
= 0;
846 iterator
->version
= 0;
847 iterator
->reserved
= 0;
850 * Check for infinite loops by making sure we do not
851 * process more leaf records, than can possibly be (or the BTree header
852 * is seriously damaged)....a brute force method.
854 if ((operation
== kBTreeFirstRecord
) || (operation
== kBTreeLastRecord
))
855 iterator
->hitCount
= 1;
856 else if (operation
!= kBTreeCurrentRecord
)
857 iterator
->hitCount
+= 1;
858 /* Always use the highest max, in case the grows while iterating */
859 iterator
->maxLeafRecs
= max(btreePtr
->leafRecords
, iterator
->maxLeafRecs
);
862 if (iterator
->hitCount
> iterator
->maxLeafRecs
+ kNumLeafRecSlack
)
864 err
= fsBTInvalidNodeErr
;
865 MARK_VOLUMEDAMAGED(filePtr
);
870 BlockMoveData ((Ptr
)keyPtr
, (Ptr
)&iterator
->key
, CalcKeySize(btreePtr
, keyPtr
));
874 ///////////////////////////// Release Nodes /////////////////////////////////
876 err
= ReleaseNode (btreePtr
, &node
);
879 if (left
.buffer
!= nil
)
881 err
= ReleaseNode (btreePtr
, &left
);
885 if (right
.buffer
!= nil
)
887 err
= ReleaseNode (btreePtr
, &right
);
893 /////////////////////// Error - Clean Up and Exit ///////////////////////////
897 (void) ReleaseNode (btreePtr
, &left
);
898 (void) ReleaseNode (btreePtr
, &node
);
899 (void) ReleaseNode (btreePtr
, &right
);
901 if (recordLen
!= nil
)
906 iterator
->hint
.writeCount
= 0;
907 iterator
->hint
.nodeNum
= 0;
908 iterator
->hint
.index
= 0;
909 iterator
->hint
.reserved1
= 0;
910 iterator
->hint
.reserved2
= 0;
912 iterator
->version
= 0;
913 iterator
->reserved
= 0;
914 iterator
->key
.length16
= 0;
917 if ( err
== fsBTEmptyErr
|| err
== fsBTEndOfIterationErr
)
918 err
= fsBTRecordNotFoundErr
;
924 /*-------------------------------------------------------------------------------
925 Routine: BTIterateRecords
927 Function: Find a series of records
929 Input: filePtr - b-tree file
930 operation - iteration operation (first,next,prev,last)
931 iterator - pointer to iterator indicating start position
932 callBackProc - pointer to routince to process a record
933 callBackState - pointer to state data (used by callBackProc)
935 Output: iterator - iterator is updated to indicate new position
937 Result: noErr - success
939 -------------------------------------------------------------------------------*/
942 BTIterateRecords(FCB
*filePtr
, BTreeIterationOperation operation
, BTreeIterator
*iterator
,
943 IterateCallBackProcPtr callBackProc
, void * callBackState
)
946 BTreeControlBlockPtr btreePtr
;
952 BlockDescriptor left
, node
, right
;
956 ////////////////////////// Priliminary Checks ///////////////////////////////
959 left
.blockHeader
= nil
;
961 right
.blockHeader
= nil
;
963 node
.blockHeader
= nil
;
965 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
967 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
969 if ((operation
!= kBTreeFirstRecord
) &&
970 (operation
!= kBTreeNextRecord
) &&
971 (operation
!= kBTreeCurrentRecord
) &&
972 (operation
!= kBTreePrevRecord
) &&
973 (operation
!= kBTreeLastRecord
))
975 err
= fsInvalidIterationMovmentErr
;
979 /////////////////////// Find First or Last Record ///////////////////////////
981 if ((operation
== kBTreeFirstRecord
) || (operation
== kBTreeLastRecord
))
983 if (operation
== kBTreeFirstRecord
)
984 nodeNum
= btreePtr
->firstLeafNode
;
986 nodeNum
= btreePtr
->lastLeafNode
;
994 err
= GetNode(btreePtr
, nodeNum
, &node
);
997 if ( ((NodeDescPtr
)node
.buffer
)->kind
!= kBTLeafNode
||
998 ((NodeDescPtr
)node
.buffer
)->numRecords
<= 0 )
1000 err
= ReleaseNode(btreePtr
, &node
);
1003 err
= fsBTInvalidNodeErr
;
1004 MARK_VOLUMEDAMAGED(filePtr
);
1008 if (operation
== kBTreeFirstRecord
)
1011 index
= ((BTNodeDescriptor
*) node
.buffer
)->numRecords
- 1;
1016 //////////////////////// Find Iterator Position /////////////////////////////
1018 err
= FindIteratorPosition(btreePtr
, iterator
, &left
, &node
, &right
,
1019 &nodeNum
, &index
, &foundRecord
);
1020 if (err
== fsBTRecordNotFoundErr
)
1025 ///////////////////// Find Next Or Previous Record //////////////////////////
1027 if (operation
== kBTreePrevRecord
)
1035 if (left
.buffer
== nil
)
1037 nodeNum
= ((NodeDescPtr
) node
.buffer
)->bLink
;
1040 err
= GetNode(btreePtr
, nodeNum
, &left
);
1043 err
= fsBTStartOfIterationErr
;
1047 // Before we stomp on "right", we'd better release it if needed
1048 if (right
.buffer
!= nil
) {
1049 err
= ReleaseNode(btreePtr
, &right
);
1055 index
= ((NodeDescPtr
) node
.buffer
)->numRecords
-1;
1058 else if (operation
== kBTreeNextRecord
)
1060 if ((foundRecord
!= true) &&
1061 (((NodeDescPtr
)node
.buffer
)->fLink
== 0) &&
1062 (index
== ((NodeDescPtr
)node
.buffer
)->numRecords
))
1064 err
= fsBTEndOfIterationErr
;
1068 // we did not find the record but the index is already positioned correctly
1069 if ((foundRecord
== false) && (index
!= ((NodeDescPtr
)node
.buffer
)->numRecords
))
1072 // we found the record OR we have to look in the next node
1073 if (index
< ((NodeDescPtr
)node
.buffer
)->numRecords
-1)
1079 if (right
.buffer
== nil
)
1081 nodeNum
= ((NodeDescPtr
)node
.buffer
)->fLink
;
1084 err
= GetNode(btreePtr
, nodeNum
, &right
);
1087 err
= fsBTEndOfIterationErr
;
1091 // Before we stomp on "left", we'd better release it if needed
1092 if (left
.buffer
!= nil
) {
1093 err
= ReleaseNode(btreePtr
, &left
);
1102 else // operation == kBTreeCurrentRecord
1104 // make sure we have something... <CS9>
1105 if ((foundRecord
!= true) &&
1106 (index
>= ((NodeDescPtr
)node
.buffer
)->numRecords
))
1108 err
= fsBTEndOfIterationErr
;
1113 //////////////////// Process Records Using Callback ////////////////////////
1116 err
= GetRecordByIndex(btreePtr
, node
.buffer
, index
, &keyPtr
, &recordPtr
, &len
);
1123 if (callBackProc(keyPtr
, recordPtr
, callBackState
) == 0)
1126 if ((index
+1) < ((NodeDescPtr
)node
.buffer
)->numRecords
) {
1129 if (right
.buffer
== nil
)
1131 nodeNum
= ((NodeDescPtr
)node
.buffer
)->fLink
;
1134 err
= GetNode(btreePtr
, nodeNum
, &right
);
1137 err
= fsBTEndOfIterationErr
;
1141 // Before we stomp on "left", we'd better release it if needed
1142 if (left
.buffer
!= nil
) {
1143 err
= ReleaseNode(btreePtr
, &left
);
1151 err
= GetRecordByIndex(btreePtr
, node
.buffer
, index
,
1152 &keyPtr
, &recordPtr
, &len
);
1160 ///////////////// Update Iterator to Last Item Processed /////////////////////
1163 if (iterator
!= nil
) // first & last have optional iterator
1165 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
1166 iterator
->hint
.nodeNum
= nodeNum
;
1167 iterator
->hint
.index
= index
;
1168 iterator
->version
= 0;
1170 BlockMoveData((Ptr
)keyPtr
, (Ptr
)&iterator
->key
, CalcKeySize(btreePtr
, keyPtr
));
1175 ///////////////////////////// Release Nodes /////////////////////////////////
1177 err
= ReleaseNode(btreePtr
, &node
);
1180 if (left
.buffer
!= nil
)
1182 err
= ReleaseNode(btreePtr
, &left
);
1186 if (right
.buffer
!= nil
)
1188 err
= ReleaseNode(btreePtr
, &right
);
1194 /////////////////////// Error - Clean Up and Exit ///////////////////////////
1198 (void) ReleaseNode(btreePtr
, &left
);
1199 (void) ReleaseNode(btreePtr
, &node
);
1200 (void) ReleaseNode(btreePtr
, &right
);
1202 if (iterator
!= nil
)
1204 iterator
->hint
.writeCount
= 0;
1205 iterator
->hint
.nodeNum
= 0;
1206 iterator
->hint
.index
= 0;
1207 iterator
->version
= 0;
1208 iterator
->key
.length16
= 0;
1211 if ( err
== fsBTEmptyErr
|| err
== fsBTEndOfIterationErr
)
1212 err
= fsBTRecordNotFoundErr
;
1218 //////////////////////////////// BTInsertRecord /////////////////////////////////
1220 OSStatus
BTInsertRecord (FCB
*filePtr
,
1221 BTreeIterator
*iterator
,
1222 FSBufferDescriptor
*record
,
1226 BTreeControlBlockPtr btreePtr
;
1227 TreePathTable treePathTable
;
1229 BlockDescriptor nodeRec
;
1230 UInt32 insertNodeNum
;
1234 ////////////////////////// Priliminary Checks ///////////////////////////////
1236 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
1237 nodeRec
.blockHeader
= nil
;
1239 err
= CheckInsertParams (filePtr
, iterator
, record
, recordLen
);
1243 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1245 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1248 ///////////////////////// Find Insert Position //////////////////////////////
1250 // always call SearchTree for Insert
1251 err
= SearchTree (btreePtr
, &iterator
->key
, treePathTable
, &insertNodeNum
, &nodeRec
, &index
);
1253 switch (err
) // set/replace/insert decision point
1255 case noErr
: err
= fsBTDuplicateRecordErr
;
1258 case fsBTRecordNotFoundErr
: break;
1260 case fsBTEmptyErr
: // if tree empty add 1st leaf node
1262 if (BTAvailableNodes(btreePtr
) == 0)
1264 err
= ExtendBTree (btreePtr
, btreePtr
->totalNodes
+ 1);
1265 M_ExitOnError (err
);
1268 err
= AllocateNode (btreePtr
, &insertNodeNum
);
1269 M_ExitOnError (err
);
1271 err
= GetNewNode (btreePtr
, insertNodeNum
, &nodeRec
);
1272 M_ExitOnError (err
);
1275 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1277 ((NodeDescPtr
)nodeRec
.buffer
)->kind
= kBTLeafNode
;
1278 ((NodeDescPtr
)nodeRec
.buffer
)->height
= 1;
1280 recordFit
= InsertKeyRecord (btreePtr
, nodeRec
.buffer
, 0,
1281 &iterator
->key
, KeyLength(btreePtr
, &iterator
->key
),
1282 record
->bufferAddress
, recordLen
);
1283 if (recordFit
!= true)
1285 err
= fsBTRecordTooLargeErr
;
1289 err
= UpdateNode (btreePtr
, &nodeRec
, 0, kLockTransaction
);
1290 M_ExitOnError (err
);
1292 // update BTreeControlBlock
1293 btreePtr
->treeDepth
= 1;
1294 btreePtr
->rootNode
= insertNodeNum
;
1295 btreePtr
->firstLeafNode
= insertNodeNum
;
1296 btreePtr
->lastLeafNode
= insertNodeNum
;
1298 M_BTreeHeaderDirty (btreePtr
);
1302 default: goto ErrorExit
;
1308 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1310 recordFit
= InsertKeyRecord (btreePtr
, nodeRec
.buffer
, index
,
1311 &iterator
->key
, KeyLength(btreePtr
, &iterator
->key
),
1312 record
->bufferAddress
, recordLen
);
1313 if (recordFit
== true)
1315 err
= UpdateNode (btreePtr
, &nodeRec
, 0, kLockTransaction
);
1316 M_ExitOnError (err
);
1322 /////////////////////// Extend File If Necessary ////////////////////////////
1324 nodesNeeded
= (SInt32
)btreePtr
->treeDepth
+ 1 - BTAvailableNodes(btreePtr
);
1325 if (nodesNeeded
> 0)
1327 nodesNeeded
+= (SInt32
)btreePtr
->totalNodes
;
1328 if (nodesNeeded
> CalcMapBits (btreePtr
)) // we'll need to add a map node too!
1331 err
= ExtendBTree (btreePtr
, nodesNeeded
);
1332 M_ExitOnError (err
);
1335 // no need to delete existing record
1337 err
= InsertTree (btreePtr
, treePathTable
, &iterator
->key
, record
->bufferAddress
,
1338 recordLen
, &nodeRec
, index
, 1, kInsertRecord
, &insertNodeNum
);
1339 M_ExitOnError (err
);
1342 //////////////////////////////// Success ////////////////////////////////////
1345 ++btreePtr
->writeCount
;
1346 ++btreePtr
->leafRecords
;
1347 M_BTreeHeaderDirty (btreePtr
);
1350 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
1351 iterator
->hint
.nodeNum
= insertNodeNum
;
1352 iterator
->hint
.index
= 0; // unused
1353 iterator
->hint
.reserved1
= 0;
1354 iterator
->hint
.reserved2
= 0;
1359 ////////////////////////////// Error Exit ///////////////////////////////////
1363 (void) ReleaseNode (btreePtr
, &nodeRec
);
1365 iterator
->hint
.writeCount
= 0;
1366 iterator
->hint
.nodeNum
= 0;
1367 iterator
->hint
.index
= 0;
1368 iterator
->hint
.reserved1
= 0;
1369 iterator
->hint
.reserved2
= 0;
1371 if (err
== fsBTEmptyErr
)
1372 err
= fsBTRecordNotFoundErr
;
1378 //////////////////////////////// BTReplaceRecord ////////////////////////////////
1380 OSStatus
BTReplaceRecord (FCB
*filePtr
,
1381 BTreeIterator
*iterator
,
1382 FSBufferDescriptor
*record
,
1386 BTreeControlBlockPtr btreePtr
;
1387 TreePathTable treePathTable
;
1389 BlockDescriptor nodeRec
;
1390 UInt32 insertNodeNum
;
1396 ////////////////////////// Priliminary Checks ///////////////////////////////
1398 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
1399 nodeRec
.blockHeader
= nil
;
1401 err
= CheckInsertParams (filePtr
, iterator
, record
, recordLen
);
1405 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1407 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1409 ////////////////////////////// Take A Hint //////////////////////////////////
1411 err
= IsItAHint (btreePtr
, iterator
, &validHint
);
1412 M_ExitOnError (err
);
1416 insertNodeNum
= iterator
->hint
.nodeNum
;
1418 err
= GetNode (btreePtr
, insertNodeNum
, &nodeRec
);
1422 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1424 err
= TrySimpleReplace (btreePtr
, nodeRec
.buffer
, iterator
, record
, recordLen
, &recordFit
);
1425 M_ExitOnError (err
);
1429 err
= UpdateNode (btreePtr
, &nodeRec
, 0, 0);
1430 M_ExitOnError (err
);
1432 ++btreePtr
->numValidHints
;
1438 (void) BTInvalidateHint( iterator
);
1441 err
= ReleaseNode (btreePtr
, &nodeRec
);
1442 M_ExitOnError (err
);
1446 (void) BTInvalidateHint( iterator
);
1451 ////////////////////////////// Get A Clue ///////////////////////////////////
1453 err
= SearchTree (btreePtr
, &iterator
->key
, treePathTable
, &insertNodeNum
, &nodeRec
, &index
);
1454 M_ExitOnError (err
); // record must exit for Replace
1456 // optimization - if simple replace will work then don't extend btree
1457 // \80\80 if we tried this before, and failed because it wouldn't fit then we shouldn't try this again...
1460 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1462 err
= TrySimpleReplace (btreePtr
, nodeRec
.buffer
, iterator
, record
, recordLen
, &recordFit
);
1463 M_ExitOnError (err
);
1467 err
= UpdateNode (btreePtr
, &nodeRec
, 0, 0);
1468 M_ExitOnError (err
);
1474 //////////////////////////// Make Some Room /////////////////////////////////
1476 nodesNeeded
= (SInt32
)btreePtr
->treeDepth
+ 1 - BTAvailableNodes(btreePtr
);
1477 if (nodesNeeded
> 0)
1479 nodesNeeded
+= (SInt32
)btreePtr
->totalNodes
;
1480 if (nodesNeeded
> CalcMapBits (btreePtr
)) // we'll need to add a map node too!
1483 err
= ExtendBTree (btreePtr
, nodesNeeded
);
1484 M_ExitOnError (err
);
1488 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1490 DeleteRecord (btreePtr
, nodeRec
.buffer
, index
); // delete existing key/record
1492 err
= InsertTree (btreePtr
, treePathTable
, &iterator
->key
, record
->bufferAddress
,
1493 recordLen
, &nodeRec
, index
, 1, kReplaceRecord
, &insertNodeNum
);
1494 M_ExitOnError (err
);
1496 ++btreePtr
->writeCount
; /* writeCount changes only if the tree structure changed */
1500 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
1501 iterator
->hint
.nodeNum
= insertNodeNum
;
1502 iterator
->hint
.index
= 0; // unused
1503 iterator
->hint
.reserved1
= 0;
1504 iterator
->hint
.reserved2
= 0;
1509 ////////////////////////////// Error Exit ///////////////////////////////////
1513 (void) ReleaseNode (btreePtr
, &nodeRec
);
1515 iterator
->hint
.writeCount
= 0;
1516 iterator
->hint
.nodeNum
= 0;
1517 iterator
->hint
.index
= 0;
1518 iterator
->hint
.reserved1
= 0;
1519 iterator
->hint
.reserved2
= 0;
1526 //////////////////////////////// BTUpdateRecord ////////////////////////////////
1529 BTUpdateRecord(FCB
*filePtr
, BTreeIterator
*iterator
,
1530 IterateCallBackProcPtr callBackProc
, void * callBackState
)
1533 BTreeControlBlockPtr btreePtr
;
1534 TreePathTable treePathTable
;
1535 BlockDescriptor nodeRec
;
1536 RecordPtr recordPtr
;
1538 UInt32 insertNodeNum
;
1544 ////////////////////////// Priliminary Checks ///////////////////////////////
1546 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
1547 nodeRec
.blockHeader
= nil
;
1549 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1551 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
1553 ////////////////////////////// Take A Hint //////////////////////////////////
1555 err
= IsItAHint (btreePtr
, iterator
, &validHint
);
1556 M_ExitOnError (err
);
1560 insertNodeNum
= iterator
->hint
.nodeNum
;
1562 err
= GetNode (btreePtr
, insertNodeNum
, &nodeRec
);
1565 if (((NodeDescPtr
)nodeRec
.buffer
)->kind
== kBTLeafNode
&&
1566 SearchNode (btreePtr
, nodeRec
.buffer
, &iterator
->key
, &index
))
1568 err
= GetRecordByIndex(btreePtr
, nodeRec
.buffer
, index
, &keyPtr
, &recordPtr
, &recordLen
);
1569 M_ExitOnError (err
);
1572 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1574 err
= callBackProc(keyPtr
, recordPtr
, callBackState
);
1575 M_ExitOnError (err
);
1577 err
= UpdateNode (btreePtr
, &nodeRec
, 0, 0);
1578 M_ExitOnError (err
);
1580 ++btreePtr
->numValidHints
;
1586 (void) BTInvalidateHint( iterator
);
1589 err
= ReleaseNode (btreePtr
, &nodeRec
);
1590 M_ExitOnError (err
);
1594 (void) BTInvalidateHint( iterator
);
1598 ////////////////////////////// Get A Clue ///////////////////////////////////
1600 err
= SearchTree (btreePtr
, &iterator
->key
, treePathTable
, &insertNodeNum
, &nodeRec
, &index
);
1601 M_ExitOnError (err
);
1603 err
= GetRecordByIndex(btreePtr
, nodeRec
.buffer
, index
, &keyPtr
, &recordPtr
, &recordLen
);
1604 M_ExitOnError (err
);
1607 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1609 err
= callBackProc(keyPtr
, recordPtr
, callBackState
);
1610 M_ExitOnError (err
);
1612 err
= UpdateNode (btreePtr
, &nodeRec
, 0, 0);
1613 M_ExitOnError (err
);
1617 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
1618 iterator
->hint
.nodeNum
= insertNodeNum
;
1619 iterator
->hint
.index
= 0;
1620 iterator
->hint
.reserved1
= 0;
1621 iterator
->hint
.reserved2
= 0;
1624 ////////////////////////////// Error Exit ///////////////////////////////////
1628 (void) ReleaseNode (btreePtr
, &nodeRec
);
1630 iterator
->hint
.writeCount
= 0;
1631 iterator
->hint
.nodeNum
= 0;
1632 iterator
->hint
.index
= 0;
1633 iterator
->hint
.reserved1
= 0;
1634 iterator
->hint
.reserved2
= 0;
1640 //////////////////////////////// BTDeleteRecord /////////////////////////////////
1642 OSStatus
BTDeleteRecord (FCB
*filePtr
,
1643 BTreeIterator
*iterator
)
1646 BTreeControlBlockPtr btreePtr
;
1647 TreePathTable treePathTable
;
1648 BlockDescriptor nodeRec
;
1654 ////////////////////////// Priliminary Checks ///////////////////////////////
1656 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
1657 nodeRec
.blockHeader
= nil
;
1659 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1660 M_ReturnErrorIf (iterator
== nil
, paramErr
);
1662 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1663 if (btreePtr
== nil
)
1665 err
= fsBTInvalidFileErr
;
1669 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1672 /////////////////////////////// Find Key ////////////////////////////////////
1674 //\80\80 check hint for simple delete case (index > 0, numRecords > 2)
1676 err
= SearchTree (btreePtr
, &iterator
->key
, treePathTable
, &nodeNum
, &nodeRec
, &index
);
1677 M_ExitOnError (err
); // record must exit for Delete
1680 /////////////////////// Extend File If Necessary ////////////////////////////
1682 nodesNeeded
= (SInt32
)btreePtr
->treeDepth
+ 1 - BTAvailableNodes(btreePtr
);
1683 if ((btreePtr
->attributes
& kBTVariableIndexKeysMask
) && (nodesNeeded
> 0))
1685 nodesNeeded
+= (SInt32
)btreePtr
->totalNodes
;
1686 if (nodesNeeded
> CalcMapBits (btreePtr
))
1689 err
= ExtendBTree (btreePtr
, nodesNeeded
);
1690 M_ExitOnError (err
);
1693 ///////////////////////////// Delete Record /////////////////////////////////
1695 err
= DeleteTree (btreePtr
, treePathTable
, &nodeRec
, index
, 1);
1696 M_ExitOnError (err
);
1698 ++btreePtr
->writeCount
;
1699 --btreePtr
->leafRecords
;
1700 M_BTreeHeaderDirty (btreePtr
);
1702 iterator
->hint
.nodeNum
= 0;
1706 ////////////////////////////// Error Exit ///////////////////////////////////
1709 (void) ReleaseNode (btreePtr
, &nodeRec
);
1716 OSStatus
BTGetInformation (FCB
*filePtr
,
1718 BTreeInfoRec
*info
)
1720 #pragma unused (version)
1722 BTreeControlBlockPtr btreePtr
;
1725 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1727 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1731 * This should not require the whole tree to be locked, just maybe the BTreeControlBlockPtr
1733 * REQUIRE_FILE_LOCK(btreePtr->fileRefNum, true);
1736 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1737 M_ReturnErrorIf (info
== nil
, paramErr
);
1739 //\80\80 check version?
1741 info
->nodeSize
= btreePtr
->nodeSize
;
1742 info
->maxKeyLength
= btreePtr
->maxKeyLength
;
1743 info
->treeDepth
= btreePtr
->treeDepth
;
1744 info
->numRecords
= btreePtr
->leafRecords
;
1745 info
->numNodes
= btreePtr
->totalNodes
;
1746 info
->numFreeNodes
= btreePtr
->freeNodes
;
1747 info
->lastfsync
= btreePtr
->lastfsync
;
1748 info
->keyCompareType
= btreePtr
->keyCompareType
;
1755 BTIsDirty(FCB
*filePtr
)
1757 BTreeControlBlockPtr btreePtr
;
1759 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1760 return TreeIsDirty(btreePtr
);
1763 /*-------------------------------------------------------------------------------
1764 Routine: BTFlushPath - Flush BTreeControlBlock to Header Node.
1766 Function: Brief_description_of_the_function_and_any_side_effects
1769 Input: pathPtr - pointer to path control block for B*Tree file to flush
1773 Result: noErr - success
1775 -------------------------------------------------------------------------------*/
1777 OSStatus
BTFlushPath (FCB
*filePtr
)
1780 BTreeControlBlockPtr btreePtr
;
1783 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1785 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1787 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1789 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
1791 err
= UpdateHeader (btreePtr
, false);
1797 /*-------------------------------------------------------------------------------
1798 Routine: BTReload - Reload B-tree Header Data.
1800 Function: Reload B-tree header data from disk. This is called after fsck
1801 has made repairs to the root filesystem. The filesystem is
1802 mounted read-only when BTReload is caled.
1805 Input: filePtr - the B*Tree file that needs its header updated
1809 Result: noErr - success
1811 -------------------------------------------------------------------------------*/
1814 BTReloadData(FCB
*filePtr
)
1817 BTreeControlBlockPtr btreePtr
;
1818 BlockDescriptor node
;
1819 BTHeaderRec
*header
;
1823 node
.blockHeader
= nil
;
1825 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1826 if (btreePtr
== nil
)
1827 return (fsBTInvalidFileErr
);
1829 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1831 err
= GetNode(btreePtr
, kHeaderNodeNum
, &node
);
1835 header
= (BTHeaderRec
*)((char *)node
.buffer
+ sizeof(BTNodeDescriptor
));
1836 if ((err
= VerifyHeader (filePtr
, header
)) == 0) {
1837 btreePtr
->treeDepth
= header
->treeDepth
;
1838 btreePtr
->rootNode
= header
->rootNode
;
1839 btreePtr
->leafRecords
= header
->leafRecords
;
1840 btreePtr
->firstLeafNode
= header
->firstLeafNode
;
1841 btreePtr
->lastLeafNode
= header
->lastLeafNode
;
1842 btreePtr
->maxKeyLength
= header
->maxKeyLength
;
1843 btreePtr
->totalNodes
= header
->totalNodes
;
1844 btreePtr
->freeNodes
= header
->freeNodes
;
1845 btreePtr
->btreeType
= header
->btreeType
;
1847 btreePtr
->flags
&= (~kBTHeaderDirty
);
1850 (void) ReleaseNode(btreePtr
, &node
);
1856 /*-------------------------------------------------------------------------------
1857 Routine: BTInvalidateHint - Invalidates the hint within a BTreeInterator.
1859 Function: Invalidates the hint within a BTreeInterator.
1862 Input: iterator - pointer to BTreeIterator
1864 Output: iterator - iterator with the hint.nodeNum cleared
1866 Result: noErr - success
1867 paramErr - iterator == nil
1868 -------------------------------------------------------------------------------*/
1871 OSStatus
BTInvalidateHint (BTreeIterator
*iterator
)
1873 if (iterator
== nil
)
1876 iterator
->hint
.nodeNum
= 0;
1884 /*-------------------------------------------------------------------------------
1885 Routine: BTGetLastSync
1887 Function: Returns the last time that this btree was flushed, does not include header.
1889 Input: filePtr - pointer file control block
1891 Output: lastfsync - time in seconds of last update
1893 Result: noErr - success
1894 paramErr - iterator == nil
1895 -------------------------------------------------------------------------------*/
1898 OSStatus
BTGetLastSync (FCB
*filePtr
,
1901 BTreeControlBlockPtr btreePtr
;
1904 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1906 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1908 /* Maybe instead of requiring a lock..an atomic set might be more appropriate */
1909 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
1911 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1912 M_ReturnErrorIf (lastsync
== nil
, paramErr
);
1914 *lastsync
= btreePtr
->lastfsync
;
1922 /*-------------------------------------------------------------------------------
1923 Routine: BTSetLastSync
1925 Function: Sets the last time that this btree was flushed, does not include header.
1928 Input: fcb - pointer file control block
1930 Output: lastfsync - time in seconds of last update
1932 Result: noErr - success
1933 paramErr - iterator == nil
1934 -------------------------------------------------------------------------------*/
1937 OSStatus
BTSetLastSync (FCB
*filePtr
,
1940 BTreeControlBlockPtr btreePtr
;
1943 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1945 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1947 /* Maybe instead of requiring a lock..an atomic set might be more appropriate */
1948 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
1950 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1951 M_ReturnErrorIf (lastsync
== nil
, paramErr
);
1953 btreePtr
->lastfsync
= lastsync
;
1960 OSStatus
BTHasContiguousNodes (FCB
*filePtr
)
1962 BTreeControlBlockPtr btreePtr
;
1965 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1967 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1969 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
1971 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1973 return NodesAreContiguous(FCBTOVCB(filePtr
), filePtr
, btreePtr
->nodeSize
);
1977 /*-------------------------------------------------------------------------------
1978 Routine: BTGetUserData
1980 Function: Read the user data area of the b-tree header node.
1982 -------------------------------------------------------------------------------*/
1985 BTGetUserData(FCB
*filePtr
, void * dataPtr
, int dataSize
)
1987 BTreeControlBlockPtr btreePtr
;
1988 BlockDescriptor node
;
1992 if (dataSize
> kBTreeHeaderUserBytes
)
1995 node
.blockHeader
= nil
;
1997 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1998 if (btreePtr
== nil
)
1999 return (fsBTInvalidFileErr
);
2001 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
2003 err
= GetNode(btreePtr
, kHeaderNodeNum
, &node
);
2007 offset
= (char *)node
.buffer
+ sizeof(BTNodeDescriptor
) + sizeof(BTHeaderRec
);
2008 bcopy(offset
, dataPtr
, dataSize
);
2010 (void) ReleaseNode(btreePtr
, &node
);
2016 /*-------------------------------------------------------------------------------
2017 Routine: BTSetUserData
2019 Function: Write the user data area of the b-tree header node.
2020 -------------------------------------------------------------------------------*/
2023 BTSetUserData(FCB
*filePtr
, void * dataPtr
, int dataSize
)
2025 BTreeControlBlockPtr btreePtr
;
2026 BlockDescriptor node
;
2030 if (dataSize
> kBTreeHeaderUserBytes
)
2033 node
.blockHeader
= nil
;
2035 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
2036 if (btreePtr
== nil
)
2037 return (fsBTInvalidFileErr
);
2039 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
2041 err
= GetNode(btreePtr
, kHeaderNodeNum
, &node
);
2045 ModifyBlockStart(btreePtr
->fileRefNum
, &node
);
2047 offset
= (char *)node
.buffer
+ sizeof(BTNodeDescriptor
) + sizeof(BTHeaderRec
);
2048 bcopy(dataPtr
, offset
, dataSize
);
2050 err
= UpdateNode (btreePtr
, &node
, 0, 0);