2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_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. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
26 Contains: Implementation of public interface routines for B-tree manager.
30 Written by: Gordon Sheridan and Bill Bruffey
32 Copyright: © 1992-1999 by Apple Computer, Inc., all rights reserved.
38 Other Contact: Mark Day
40 Technology: File Systems
48 Change History (most recent first):
49 <MOSXS> 9/22/99 ser Added routines BTGetLastSync and BTSetLastSync
50 <MOSXS> 6/1/99 djb Sync up with Mac OS 8.6.
51 <MOSXS> 6/30/98 djb In BTOpenPath make sure nodes are contiguous on disk (radar #2249539).
52 <MOSXS> 4/15/98 djb In BTOpenPath need to clear nodeRec.buffer if GetBlockProc fails.
53 <MOSXS> 4/11/98 djb Add RequireFileLock checking to all external entry points.
55 <MOSXS> 03/23/98 djb In BTOpenPath use kTrashBlock option when releasing the header so
56 that we get a full node when we call GetNode.
58 <CS9> 12/12/97 djb Radar #2202682, BTIterateRecord with kBTreeCurrentRecord was not
59 checking if we had a record and could call BlockMove with an
60 uninitialize source pointer (causing a bus error).
61 <CS8> 10/24/97 msd In BTIterateRecord, when moving to the previous or next record
62 and we have to move to another node, see if we need to release
63 the node about to be "shifted out" (opposite sibling of the
64 direction we need to move).
65 <CS7> 7/25/97 DSH BTSearchRecord now takes a heuristicHint, nodeNum, and tries it
66 before calling SearchBTree
67 <CS6> 7/24/97 djb GetBlockProc now take a file refnum instead of an FCB ptr.
68 <CS5> 7/22/97 djb Move trace points from BTreeWrapper.c to here.
69 <CS4> 7/21/97 djb LogEndTime now takes an error code.
70 <CS3> 7/16/97 DSH FilesInternal.i renamed FileMgrInternal.i to avoid name
72 <CS2> 5/19/97 djb Add summary traces to BTIterateRecord.
73 <CS1> 4/23/97 djb first checked in
75 <HFS7> 2/19/97 djb Enable variable sized index keys for HFS+ volumes. Added node
76 cache to support nodes larger than 512 bytes.
77 <HFS6> 1/27/97 djb Calls to InsertTree and DeleteTree are now recursive (to support
78 variable sized index keys).
79 <HFS5> 1/13/97 djb Added support for getting current record to BTIterateRecord.
80 <HFS4> 1/6/97 djb Initialize "BigKeys" attribute in BTOpen.
81 <HFS3> 1/3/97 djb Added support for large keys.
82 <HFS2> 12/23/96 djb On exit map fsBTEmptyErr and fsBTEndOfIterationErr to
83 fsBTRecordNotFoundErr.
84 <HFS1> 12/19/96 djb first checked in
86 History applicable to original Scarecrow Design:
88 <13> 10/25/96 ser Changing for new VFPI
89 <12> 10/18/96 ser Converting over VFPI changes
90 <11> 9/17/96 dkh More BTree statistics. Modified hint checks to not bail out when
91 an error is returned from GetNode.
92 <10> 9/16/96 dkh Revised BTree statistics.
93 <9> 8/23/96 dkh Remove checks for multiple paths to BTree file. Need to add
94 equivalent mechanism later.
95 <8> 6/20/96 dkh Radar #1358740. Switch from using Pools to debug MemAllocators.
96 <7> 3/14/96 jev Fix BTreeSetRecord, recordFound was not set for the case of a
97 simple replace causing the leafRecords count to get bumped even
98 though we didn't have to add a record.
99 <6> 3/1/96 prp Fix lint problems. Bug in BTSetRecord that does not initialize
101 <5> 1/22/96 dkh Add #include Memory.h
102 <4> 1/10/96 msd Use the real function names from Math64.i.
103 <3> 1/4/96 jev Fix BTItererateRecord for the condition when the iterator
104 position routine does not find the record and we are looking for
105 the next record. In such a case, if the node's forrward link is
106 non-zero, we have to keep iterating next and not return
107 fsBTEndOfIterationErr error.
108 <2> 12/7/95 dkh D10E2 build. Changed usage of Ref data type to LogicalAddress.
109 <1> 10/18/95 rst Moved from Scarecrow project.
111 <24> 7/18/95 mbb Change MoveData & ClearBytes to BlockMoveData & BlockZero.
112 <23> 1/31/95 prp GetBlockProc interface uses a 64 bit node number.
113 <22> 1/12/95 wjk Adopt Model FileSystem changes in D5.
114 <21> 11/16/94 prp Add IsItAHint routine and use it whenever hint's node number was
116 <20> 11/10/94 prp BTGetInfo name collides with the same name in FileManagerPriv.i.
117 Change it to BTGetInformation.
118 <19> 9/30/94 prp Get in sync with D2 interface changes.
119 <18> 7/22/94 wjk Convert to the new set of header files.
120 <17> 12/9/93 wjk Cleanup usage of char, Byte, int8, UInt8, etc.
121 <16> 12/2/93 wjk Move from Makefiles to BuildFiles. Fit into the ModernOS and
123 <15> 11/30/93 wjk Move from Makefiles to BuildFiles. Fit into the ModernOS and
125 <14> 9/30/93 gs Rename E_NoGetNodeProc and E_NoReleaseNodeProc to
127 <13> 8/31/93 prp Use Set64U instead of Set64.
128 <12> 8/16/93 prp In BTSearchRecord, if the input hint found the node and record,
129 set the local nodeNum variable correctly so that the resultant
130 iterator gets set correctly.
131 <11> 7/1/93 gs Fix bug in BTIterateRecord related to kBTreePrevRecord
133 <10> 6/2/93 gs Update for changes to FSErrors.h and add some comments.
134 <9> 5/24/93 gs Fix bug in BTInsert/Set/ReplaceRecord which didn't set node hint
135 properly in some cases.
136 <8> 5/24/93 gs Do NOT map fsBTEmptyErr to fsBTRecordNotFoundErr in BTSearchRecord.
137 <7> 5/24/93 gs Rename BTFlush to BTFlushPath.
138 <6> 5/21/93 gs Add hint optimization to Set/Replace routines.
139 <5> 5/10/93 gs Remove Panic from BTInitialize for small logicalEOF. Implement
140 Insert, Set, Replace, and Delete.
141 <4> 3/23/93 gs Finish BTInitialize.
142 <3> 2/8/93 gs Implement BTSearchRecord and BTIterateRecord.
143 <2> 12/8/92 gs Implement Open and Close routines.
144 <1> 11/15/92 gs first checked in
148 #include "../headers/BTreesPrivate.h"
151 * The amount that the BTree header leaf count can be wrong before we assume
152 * it is in an infinite loop.
154 #define kNumLeafRecSlack 10
156 /* BTree accessor routines */
157 extern OSStatus
GetBTreeBlock(FileReference vp
, UInt32 blockNum
, GetBlockOptions options
, BlockDescriptor
*block
);
158 extern OSStatus
SetBTreeBlockSize(FileReference vp
, ByteCount blockSize
, ItemCount minBlockCount
);
159 extern OSStatus
ExtendBTreeFile(FileReference vp
, FSSize minEOF
, FSSize maxEOF
);
160 extern OSStatus
ReleaseBTreeBlock(FileReference vp
, BlockDescPtr blockPtr
, ReleaseBlockOptions options
);
162 //////////////////////////////////// Globals ////////////////////////////////////
165 /////////////////////////// BTree Module Entry Points ///////////////////////////
169 /*-------------------------------------------------------------------------------
170 Routine: BTOpenPath - Open a file for access as a B*Tree.
172 Function: Create BTree control block for a file, if necessary. Validates the
173 file to be sure it looks like a BTree file.
176 Input: filePtr - pointer to file to open as a B-tree
177 keyCompareProc - pointer to client's KeyCompare function
179 Result: noErr - success
180 paramErr - required ptr was nil
184 -------------------------------------------------------------------------------*/
186 OSStatus
BTOpenPath(FCB
*filePtr
, KeyCompareProcPtr keyCompareProc
)
189 BTreeControlBlockPtr btreePtr
;
193 ////////////////////// Preliminary Error Checking ///////////////////////////
195 if ( filePtr
== nil
)
201 * Subsequent opens allow key compare proc to be changed.
203 if ( filePtr
->fcbBTCBPtr
!= nil
&& keyCompareProc
!= nil
) {
204 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
205 btreePtr
->keyCompareProc
= keyCompareProc
;
209 if ( filePtr
->fcbEOF
< kMinNodeSize
)
210 return fsBTInvalidFileErr
;
213 //////////////////////// Allocate Control Block /////////////////////////////
215 btreePtr
= (BTreeControlBlock
*) NewPtrSysClear( sizeof( BTreeControlBlock
) );
218 Panic ("\pBTOpen: no memory for btreePtr.");
222 btreePtr
->getBlockProc
= GetBTreeBlock
;
223 btreePtr
->releaseBlockProc
= ReleaseBTreeBlock
;
224 btreePtr
->setEndOfForkProc
= ExtendBTreeFile
;
225 btreePtr
->keyCompareProc
= keyCompareProc
;
227 /////////////////////////// Read Header Node ////////////////////////////////
229 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
230 btreePtr
->fileRefNum
= GetFileRefNumFromFCB(filePtr
);
231 filePtr
->fcbBTCBPtr
= (Ptr
) btreePtr
; // attach btree cb to file
233 /* The minimum node size is the physical block size */
234 nodeRec
.blockSize
= VTOHFS(btreePtr
->fileRefNum
)->hfs_phys_block_size
;
236 /* Start with the allocation block size for regular files. */
237 if (FTOC(filePtr
)->c_fileid
>= kHFSFirstUserCatalogNodeID
)
239 nodeRec
.blockSize
= FCBTOVCB(filePtr
)->blockSize
;
241 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
243 // it is now safe to call M_ExitOnError (err)
245 err
= SetBTreeBlockSize (btreePtr
->fileRefNum
, nodeRec
.blockSize
, 1);
249 err
= GetBTreeBlock(btreePtr
->fileRefNum
,
255 nodeRec
.buffer
= nil
;
256 nodeRec
.blockHeader
= nil
;
257 Panic("\pBTOpen: getNodeProc returned error getting header node.");
260 ++btreePtr
->numGetNodes
;
261 header
= (BTHeaderRec
*) ((u_long
)nodeRec
.buffer
+ sizeof(BTNodeDescriptor
));
264 ///////////////////////////// verify header /////////////////////////////////
266 err
= VerifyHeader (filePtr
, header
);
270 ///////////////////// Initalize fields from header //////////////////////////
272 PanicIf ( (FCBTOVCB(filePtr
)->vcbSigWord
!= 0x4244) && (header
->nodeSize
== 512), "\p BTOpenPath: wrong node size for HFS+ volume!"); // 0x4244 = 'BD'
274 btreePtr
->treeDepth
= header
->treeDepth
;
275 btreePtr
->rootNode
= header
->rootNode
;
276 btreePtr
->leafRecords
= header
->leafRecords
;
277 btreePtr
->firstLeafNode
= header
->firstLeafNode
;
278 btreePtr
->lastLeafNode
= header
->lastLeafNode
;
279 btreePtr
->nodeSize
= header
->nodeSize
;
280 btreePtr
->maxKeyLength
= header
->maxKeyLength
;
281 btreePtr
->totalNodes
= header
->totalNodes
;
282 btreePtr
->freeNodes
= header
->freeNodes
;
283 if (FTOC(filePtr
)->c_fileid
>= kHFSFirstUserCatalogNodeID
)
284 filePtr
->ff_clumpsize
= header
->clumpSize
;
285 btreePtr
->btreeType
= header
->btreeType
;
287 btreePtr
->keyCompareType
= header
->keyCompareType
;
289 btreePtr
->attributes
= header
->attributes
;
291 if ( btreePtr
->maxKeyLength
> 40 )
292 btreePtr
->attributes
|= (kBTBigKeysMask
+ kBTVariableIndexKeysMask
); //\80\80 we need a way to save these attributes
294 /////////////////////// Initialize dynamic fields ///////////////////////////
296 btreePtr
->version
= kBTreeVersion
;
298 btreePtr
->writeCount
= 1;
300 /////////////////////////// Check Header Node ///////////////////////////////
302 // set kBadClose attribute bit, and UpdateNode
304 /* b-tree node size must be at least as big as the physical block size */
305 if (btreePtr
->nodeSize
< nodeRec
.blockSize
)
308 * If this tree has any records or the media is writeable then
309 * we cannot mount using the current physical block size.
311 if (btreePtr
->leafRecords
> 0 ||
312 VTOHFS(btreePtr
->fileRefNum
)->hfs_flags
& HFS_WRITEABLE_MEDIA
)
314 err
= fsBTBadNodeSize
;
320 * If the actual node size is different than the amount we read,
321 * then release and trash this block, and re-read with the correct
324 if ( btreePtr
->nodeSize
!= nodeRec
.blockSize
)
326 err
= SetBTreeBlockSize (btreePtr
->fileRefNum
, btreePtr
->nodeSize
, 32);
330 * Need to use kTrashBlock option to force the
331 * buffer cache to read the entire node
333 err
= ReleaseBTreeBlock(btreePtr
->fileRefNum
, &nodeRec
, kTrashBlock
);
334 ++btreePtr
->numReleaseNodes
;
337 err
= GetNode (btreePtr
, kHeaderNodeNum
, &nodeRec
);
341 //\80\80 total nodes * node size <= LEOF?
344 err
= ReleaseNode (btreePtr
, &nodeRec
);
348 * Under Mac OS, b-tree nodes can be non-contiguous on disk when the
349 * allocation block size is smaller than the b-tree node size.
351 * If journaling is turned on for this volume we can't deal with this
352 * situation and so we bail out. If journaling isn't on it's ok as
353 * hfs_strategy_fragmented() deals with it. Journaling can't support
354 * this because it assumes that if you give it a block that it's
355 * contiguous on disk.
357 if ( FCBTOHFS(filePtr
)->jnl
&& !NodesAreContiguous(FCBTOVCB(filePtr
), filePtr
, btreePtr
->nodeSize
) ) {
358 return fsBTInvalidNodeErr
;
361 //////////////////////////////// Success ////////////////////////////////////
363 //\80\80 align LEOF to multiple of node size? - just on close
368 /////////////////////// Error - Clean up and Exit ///////////////////////////
372 filePtr
->fcbBTCBPtr
= nil
;
373 (void) ReleaseNode (btreePtr
, &nodeRec
);
374 DisposePtr( (Ptr
) btreePtr
);
381 /*-------------------------------------------------------------------------------
382 Routine: BTClosePath - Flush BTree Header and Deallocate Memory for BTree.
384 Function: Flush the BTreeControlBlock fields to header node, and delete BTree control
385 block and key descriptor associated with the file if filePtr is last
386 path of type kBTreeType ('btre').
389 Input: filePtr - pointer to file to delete BTree control block for.
391 Result: noErr - success
394 -------------------------------------------------------------------------------*/
396 OSStatus
BTClosePath (FCB
*filePtr
)
399 BTreeControlBlockPtr btreePtr
;
401 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
404 return fsBTInvalidFileErr
;
406 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
408 ////////////////////// Check for other BTree Paths //////////////////////////
410 btreePtr
->attributes
&= ~kBTBadCloseMask
; // clear "bad close" attribute bit
411 err
= UpdateHeader (btreePtr
, true);
414 DisposePtr( (Ptr
) btreePtr
);
415 filePtr
->fcbBTCBPtr
= nil
;
419 /////////////////////// Error - Clean Up and Exit ///////////////////////////
428 /*-------------------------------------------------------------------------------
429 Routine: BTSearchRecord - Search BTree for a record with a matching key.
431 Function: Search for position in B*Tree indicated by searchKey. If a valid node hint
432 is provided, it will be searched first, then SearchTree will be called.
433 If a BTreeIterator is provided, it will be set to the position found as
434 a result of the search. If a record exists at that position, and a BufferDescriptor
435 is supplied, the record will be copied to the buffer (as much as will fit),
436 and recordLen will be set to the length of the record.
438 If an error other than fsBTRecordNotFoundErr occurs, the BTreeIterator, if any,
439 is invalidated, and recordLen is set to 0.
442 Input: pathPtr - pointer to path for BTree file.
443 searchKey - pointer to search key to match.
444 hintPtr - pointer to hint (may be nil)
446 Output: record - pointer to BufferDescriptor containing record
447 recordLen - length of data at recordPtr
448 iterator - pointer to BTreeIterator indicating position result of search
450 Result: noErr - success, record contains copy of record found
451 fsBTRecordNotFoundErr - record was not found, no data copied
452 fsBTInvalidFileErr - no BTreeControlBlock is allocated for the fork
453 fsBTInvalidKeyLengthErr -
455 -------------------------------------------------------------------------------*/
457 OSStatus
BTSearchRecord (FCB
*filePtr
,
458 BTreeIterator
*searchIterator
,
459 FSBufferDescriptor
*record
,
461 BTreeIterator
*resultIterator
)
464 BTreeControlBlockPtr btreePtr
;
465 TreePathTable treePathTable
;
467 BlockDescriptor node
;
475 if (filePtr
== nil
) return paramErr
;
476 if (searchIterator
== nil
) return paramErr
;
479 node
.blockHeader
= nil
;
481 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
482 if (btreePtr
== nil
) return fsBTInvalidFileErr
;
484 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
488 ////////////////////////////// Take A Hint //////////////////////////////////
490 err
= IsItAHint (btreePtr
, searchIterator
, &validHint
);
495 nodeNum
= searchIterator
->hint
.nodeNum
;
497 err
= GetNode (btreePtr
, nodeNum
, &node
);
500 if ( ((BTNodeDescriptor
*) node
.buffer
)->kind
== kBTLeafNode
&&
501 ((BTNodeDescriptor
*) node
.buffer
)->numRecords
> 0 )
503 foundRecord
= SearchNode (btreePtr
, node
.buffer
, &searchIterator
->key
, &index
);
505 //\80\80 if !foundRecord, we could still skip tree search if ( 0 < index < numRecords )
508 if (foundRecord
== false)
510 err
= ReleaseNode (btreePtr
, &node
);
515 ++btreePtr
->numValidHints
;
519 if( foundRecord
== false )
520 (void) BTInvalidateHint( searchIterator
);
524 //////////////////////////// Search The Tree ////////////////////////////////
526 if (foundRecord
== false)
528 err
= SearchTree ( btreePtr
, &searchIterator
->key
, treePathTable
, &nodeNum
, &node
, &index
);
531 case noErr
: foundRecord
= true; break;
532 case fsBTRecordNotFoundErr
: break;
533 default: goto ErrorExit
;
538 //////////////////////////// Get the Record /////////////////////////////////
540 if (foundRecord
== true)
542 //XXX Should check for errors! Or BlockMove could choke on recordPtr!!!
543 GetRecordByIndex (btreePtr
, node
.buffer
, index
, &keyPtr
, &recordPtr
, &len
);
545 if (recordLen
!= nil
) *recordLen
= len
;
549 ByteCount recordSize
;
551 recordSize
= record
->itemCount
* record
->itemSize
;
553 if (len
> recordSize
) len
= recordSize
;
555 BlockMoveData (recordPtr
, record
->bufferAddress
, len
);
560 /////////////////////// Success - Update Iterator ///////////////////////////
562 if (resultIterator
!= nil
)
564 resultIterator
->hint
.writeCount
= btreePtr
->writeCount
;
565 resultIterator
->hint
.nodeNum
= nodeNum
;
566 resultIterator
->hint
.index
= index
;
568 resultIterator
->hint
.reserved1
= 0;
569 resultIterator
->hint
.reserved2
= 0;
570 resultIterator
->version
= 0;
571 resultIterator
->reserved
= 0;
573 // copy the key in the BTree when found rather than searchIterator->key to get proper case/diacriticals
574 if (foundRecord
== true)
575 BlockMoveData ((Ptr
)keyPtr
, (Ptr
)&resultIterator
->key
, CalcKeySize(btreePtr
, keyPtr
));
577 BlockMoveData ((Ptr
)&searchIterator
->key
, (Ptr
)&resultIterator
->key
, CalcKeySize(btreePtr
, &searchIterator
->key
));
580 err
= ReleaseNode (btreePtr
, &node
);
583 if (foundRecord
== false) return fsBTRecordNotFoundErr
;
587 /////////////////////// Error - Clean Up and Exit ///////////////////////////
591 if (recordLen
!= nil
)
594 if (resultIterator
!= nil
)
596 resultIterator
->hint
.writeCount
= 0;
597 resultIterator
->hint
.nodeNum
= 0;
598 resultIterator
->hint
.index
= 0;
599 resultIterator
->hint
.reserved1
= 0;
600 resultIterator
->hint
.reserved2
= 0;
602 resultIterator
->version
= 0;
603 resultIterator
->reserved
= 0;
604 resultIterator
->key
.length16
= 0; // zero out two bytes to cover both types of keys
607 if ( err
== fsBTEmptyErr
)
608 err
= fsBTRecordNotFoundErr
;
615 /*-------------------------------------------------------------------------------
616 Routine: BTIterateRecord - Find the first, next, previous, or last record.
618 Function: Find the first, next, previous, or last record in the BTree
620 Input: pathPtr - pointer to path iterate records for.
621 operation - iteration operation (first,next,prev,last)
622 iterator - pointer to iterator indicating start position
624 Output: iterator - iterator is updated to indicate new position
625 newKeyPtr - pointer to buffer to copy key found by iteration
626 record - pointer to buffer to copy record found by iteration
627 recordLen - length of record
629 Result: noErr - success
631 -------------------------------------------------------------------------------*/
633 OSStatus
BTIterateRecord (FCB
*filePtr
,
634 BTreeIterationOperation operation
,
635 BTreeIterator
*iterator
,
636 FSBufferDescriptor
*record
,
640 BTreeControlBlockPtr btreePtr
;
648 BlockDescriptor left
, node
, right
;
652 ////////////////////////// Priliminary Checks ///////////////////////////////
655 left
.blockHeader
= nil
;
657 right
.blockHeader
= nil
;
659 node
.blockHeader
= nil
;
667 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
670 return fsBTInvalidFileErr
; //\80\80 handle properly
673 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
675 if ((operation
!= kBTreeFirstRecord
) &&
676 (operation
!= kBTreeNextRecord
) &&
677 (operation
!= kBTreeCurrentRecord
) &&
678 (operation
!= kBTreePrevRecord
) &&
679 (operation
!= kBTreeLastRecord
))
681 err
= fsInvalidIterationMovmentErr
;
685 /////////////////////// Find First or Last Record ///////////////////////////
687 if ((operation
== kBTreeFirstRecord
) || (operation
== kBTreeLastRecord
))
689 if (operation
== kBTreeFirstRecord
) nodeNum
= btreePtr
->firstLeafNode
;
690 else nodeNum
= btreePtr
->lastLeafNode
;
698 err
= GetNode (btreePtr
, nodeNum
, &node
);
701 if ( ((NodeDescPtr
) node
.buffer
)->kind
!= kBTLeafNode
||
702 ((NodeDescPtr
) node
.buffer
)->numRecords
<= 0 )
704 err
= ReleaseNode (btreePtr
, &node
);
707 err
= fsBTInvalidNodeErr
;
708 MARK_VOLUMEDAMAGED(filePtr
);
712 if (operation
== kBTreeFirstRecord
) index
= 0;
713 else index
= ((BTNodeDescriptor
*) node
.buffer
)->numRecords
- 1;
715 goto CopyData
; //\80\80 is there a cleaner way?
719 //////////////////////// Find Iterator Position /////////////////////////////
721 err
= FindIteratorPosition (btreePtr
, iterator
,
722 &left
, &node
, &right
, &nodeNum
, &index
, &foundRecord
);
726 ///////////////////// Find Next Or Previous Record //////////////////////////
728 if (operation
== kBTreePrevRecord
)
736 if (left
.buffer
== nil
)
738 nodeNum
= ((NodeDescPtr
) node
.buffer
)->bLink
;
741 err
= GetNode (btreePtr
, nodeNum
, &left
);
744 err
= fsBTStartOfIterationErr
;
748 // Before we stomp on "right", we'd better release it if needed
749 if (right
.buffer
!= nil
) {
750 err
= ReleaseNode(btreePtr
, &right
);
756 index
= ((NodeDescPtr
) node
.buffer
)->numRecords
-1;
759 else if (operation
== kBTreeNextRecord
)
761 if ((foundRecord
!= true) &&
762 (((NodeDescPtr
) node
.buffer
)->fLink
== 0) &&
763 (index
== ((NodeDescPtr
) node
.buffer
)->numRecords
))
765 err
= fsBTEndOfIterationErr
;
769 // we did not find the record but the index is already positioned correctly
770 if ((foundRecord
== false) && (index
!= ((NodeDescPtr
) node
.buffer
)->numRecords
))
773 // we found the record OR we have to look in the next node
774 if (index
< ((NodeDescPtr
) node
.buffer
)->numRecords
-1)
780 if (right
.buffer
== nil
)
782 nodeNum
= ((NodeDescPtr
) node
.buffer
)->fLink
;
785 err
= GetNode (btreePtr
, nodeNum
, &right
);
788 err
= fsBTEndOfIterationErr
;
792 // Before we stomp on "left", we'd better release it if needed
793 if (left
.buffer
!= nil
) {
794 err
= ReleaseNode(btreePtr
, &left
);
803 else // operation == kBTreeCurrentRecord
805 // make sure we have something... <CS9>
806 if ((foundRecord
!= true) &&
807 (index
>= ((NodeDescPtr
) node
.buffer
)->numRecords
))
809 err
= fsBTEndOfIterationErr
;
814 //////////////////// Copy Record And Update Iterator ////////////////////////
818 // added check for errors <CS9>
819 err
= GetRecordByIndex (btreePtr
, node
.buffer
, index
, &keyPtr
, &recordPtr
, &len
);
822 if (recordLen
!= nil
)
827 ByteCount recordSize
;
829 recordSize
= record
->itemCount
* record
->itemSize
;
831 if (len
> recordSize
) len
= recordSize
;
833 BlockMoveData (recordPtr
, record
->bufferAddress
, len
);
836 if (iterator
!= nil
) // first & last do not require iterator
838 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
839 iterator
->hint
.nodeNum
= nodeNum
;
840 iterator
->hint
.index
= index
;
841 iterator
->hint
.reserved1
= 0;
842 iterator
->hint
.reserved2
= 0;
844 iterator
->version
= 0;
845 iterator
->reserved
= 0;
848 * Check for infinite loops by making sure we do not
849 * process more leaf records, than can possibly be (or the BTree header
850 * is seriously damaged)....a brute force method.
852 if ((operation
== kBTreeFirstRecord
) || (operation
== kBTreeLastRecord
))
853 iterator
->hitCount
= 1;
854 else if (operation
!= kBTreeCurrentRecord
)
855 iterator
->hitCount
+= 1;
856 /* Always use the highest max, in case the grows while iterating */
857 iterator
->maxLeafRecs
= max(btreePtr
->leafRecords
, iterator
->maxLeafRecs
);
860 if (iterator
->hitCount
> iterator
->maxLeafRecs
+ kNumLeafRecSlack
)
862 err
= fsBTInvalidNodeErr
;
863 MARK_VOLUMEDAMAGED(filePtr
);
868 BlockMoveData ((Ptr
)keyPtr
, (Ptr
)&iterator
->key
, CalcKeySize(btreePtr
, keyPtr
));
872 ///////////////////////////// Release Nodes /////////////////////////////////
874 err
= ReleaseNode (btreePtr
, &node
);
877 if (left
.buffer
!= nil
)
879 err
= ReleaseNode (btreePtr
, &left
);
883 if (right
.buffer
!= nil
)
885 err
= ReleaseNode (btreePtr
, &right
);
891 /////////////////////// Error - Clean Up and Exit ///////////////////////////
895 (void) ReleaseNode (btreePtr
, &left
);
896 (void) ReleaseNode (btreePtr
, &node
);
897 (void) ReleaseNode (btreePtr
, &right
);
899 if (recordLen
!= nil
)
904 iterator
->hint
.writeCount
= 0;
905 iterator
->hint
.nodeNum
= 0;
906 iterator
->hint
.index
= 0;
907 iterator
->hint
.reserved1
= 0;
908 iterator
->hint
.reserved2
= 0;
910 iterator
->version
= 0;
911 iterator
->reserved
= 0;
912 iterator
->key
.length16
= 0;
915 if ( err
== fsBTEmptyErr
|| err
== fsBTEndOfIterationErr
)
916 err
= fsBTRecordNotFoundErr
;
922 /*-------------------------------------------------------------------------------
923 Routine: BTIterateRecords
925 Function: Find a series of records
927 Input: filePtr - b-tree file
928 operation - iteration operation (first,next,prev,last)
929 iterator - pointer to iterator indicating start position
930 callBackProc - pointer to routince to process a record
931 callBackState - pointer to state data (used by callBackProc)
933 Output: iterator - iterator is updated to indicate new position
935 Result: noErr - success
937 -------------------------------------------------------------------------------*/
940 BTIterateRecords(FCB
*filePtr
, BTreeIterationOperation operation
, BTreeIterator
*iterator
,
941 IterateCallBackProcPtr callBackProc
, void * callBackState
)
944 BTreeControlBlockPtr btreePtr
;
950 BlockDescriptor left
, node
, right
;
954 ////////////////////////// Priliminary Checks ///////////////////////////////
957 left
.blockHeader
= nil
;
959 right
.blockHeader
= nil
;
961 node
.blockHeader
= nil
;
963 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
965 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
967 if ((operation
!= kBTreeFirstRecord
) &&
968 (operation
!= kBTreeNextRecord
) &&
969 (operation
!= kBTreeCurrentRecord
) &&
970 (operation
!= kBTreePrevRecord
) &&
971 (operation
!= kBTreeLastRecord
))
973 err
= fsInvalidIterationMovmentErr
;
977 /////////////////////// Find First or Last Record ///////////////////////////
979 if ((operation
== kBTreeFirstRecord
) || (operation
== kBTreeLastRecord
))
981 if (operation
== kBTreeFirstRecord
)
982 nodeNum
= btreePtr
->firstLeafNode
;
984 nodeNum
= btreePtr
->lastLeafNode
;
992 err
= GetNode(btreePtr
, nodeNum
, &node
);
995 if ( ((NodeDescPtr
)node
.buffer
)->kind
!= kBTLeafNode
||
996 ((NodeDescPtr
)node
.buffer
)->numRecords
<= 0 )
998 err
= ReleaseNode(btreePtr
, &node
);
1001 err
= fsBTInvalidNodeErr
;
1002 MARK_VOLUMEDAMAGED(filePtr
);
1006 if (operation
== kBTreeFirstRecord
)
1009 index
= ((BTNodeDescriptor
*) node
.buffer
)->numRecords
- 1;
1014 //////////////////////// Find Iterator Position /////////////////////////////
1016 err
= FindIteratorPosition(btreePtr
, iterator
, &left
, &node
, &right
,
1017 &nodeNum
, &index
, &foundRecord
);
1018 if (err
== fsBTRecordNotFoundErr
)
1023 ///////////////////// Find Next Or Previous Record //////////////////////////
1025 if (operation
== kBTreePrevRecord
)
1033 if (left
.buffer
== nil
)
1035 nodeNum
= ((NodeDescPtr
) node
.buffer
)->bLink
;
1038 err
= GetNode(btreePtr
, nodeNum
, &left
);
1041 err
= fsBTStartOfIterationErr
;
1045 // Before we stomp on "right", we'd better release it if needed
1046 if (right
.buffer
!= nil
) {
1047 err
= ReleaseNode(btreePtr
, &right
);
1053 index
= ((NodeDescPtr
) node
.buffer
)->numRecords
-1;
1056 else if (operation
== kBTreeNextRecord
)
1058 if ((foundRecord
!= true) &&
1059 (((NodeDescPtr
)node
.buffer
)->fLink
== 0) &&
1060 (index
== ((NodeDescPtr
)node
.buffer
)->numRecords
))
1062 err
= fsBTEndOfIterationErr
;
1066 // we did not find the record but the index is already positioned correctly
1067 if ((foundRecord
== false) && (index
!= ((NodeDescPtr
)node
.buffer
)->numRecords
))
1070 // we found the record OR we have to look in the next node
1071 if (index
< ((NodeDescPtr
)node
.buffer
)->numRecords
-1)
1077 if (right
.buffer
== nil
)
1079 nodeNum
= ((NodeDescPtr
)node
.buffer
)->fLink
;
1082 err
= GetNode(btreePtr
, nodeNum
, &right
);
1085 err
= fsBTEndOfIterationErr
;
1089 // Before we stomp on "left", we'd better release it if needed
1090 if (left
.buffer
!= nil
) {
1091 err
= ReleaseNode(btreePtr
, &left
);
1100 else // operation == kBTreeCurrentRecord
1102 // make sure we have something... <CS9>
1103 if ((foundRecord
!= true) &&
1104 (index
>= ((NodeDescPtr
)node
.buffer
)->numRecords
))
1106 err
= fsBTEndOfIterationErr
;
1111 //////////////////// Process Records Using Callback ////////////////////////
1114 err
= GetRecordByIndex(btreePtr
, node
.buffer
, index
, &keyPtr
, &recordPtr
, &len
);
1121 if (callBackProc(keyPtr
, recordPtr
, callBackState
) == 0)
1124 if ((index
+1) < ((NodeDescPtr
)node
.buffer
)->numRecords
) {
1127 if (right
.buffer
== nil
)
1129 nodeNum
= ((NodeDescPtr
)node
.buffer
)->fLink
;
1132 err
= GetNode(btreePtr
, nodeNum
, &right
);
1135 err
= fsBTEndOfIterationErr
;
1139 // Before we stomp on "left", we'd better release it if needed
1140 if (left
.buffer
!= nil
) {
1141 err
= ReleaseNode(btreePtr
, &left
);
1149 err
= GetRecordByIndex(btreePtr
, node
.buffer
, index
,
1150 &keyPtr
, &recordPtr
, &len
);
1158 ///////////////// Update Iterator to Last Item Processed /////////////////////
1161 if (iterator
!= nil
) // first & last have optional iterator
1163 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
1164 iterator
->hint
.nodeNum
= nodeNum
;
1165 iterator
->hint
.index
= index
;
1166 iterator
->version
= 0;
1168 BlockMoveData((Ptr
)keyPtr
, (Ptr
)&iterator
->key
, CalcKeySize(btreePtr
, keyPtr
));
1173 ///////////////////////////// Release Nodes /////////////////////////////////
1175 err
= ReleaseNode(btreePtr
, &node
);
1178 if (left
.buffer
!= nil
)
1180 err
= ReleaseNode(btreePtr
, &left
);
1184 if (right
.buffer
!= nil
)
1186 err
= ReleaseNode(btreePtr
, &right
);
1192 /////////////////////// Error - Clean Up and Exit ///////////////////////////
1196 (void) ReleaseNode(btreePtr
, &left
);
1197 (void) ReleaseNode(btreePtr
, &node
);
1198 (void) ReleaseNode(btreePtr
, &right
);
1200 if (iterator
!= nil
)
1202 iterator
->hint
.writeCount
= 0;
1203 iterator
->hint
.nodeNum
= 0;
1204 iterator
->hint
.index
= 0;
1205 iterator
->version
= 0;
1206 iterator
->key
.length16
= 0;
1209 if ( err
== fsBTEmptyErr
|| err
== fsBTEndOfIterationErr
)
1210 err
= fsBTRecordNotFoundErr
;
1216 //////////////////////////////// BTInsertRecord /////////////////////////////////
1218 OSStatus
BTInsertRecord (FCB
*filePtr
,
1219 BTreeIterator
*iterator
,
1220 FSBufferDescriptor
*record
,
1224 BTreeControlBlockPtr btreePtr
;
1225 TreePathTable treePathTable
;
1227 BlockDescriptor nodeRec
;
1228 UInt32 insertNodeNum
;
1232 ////////////////////////// Priliminary Checks ///////////////////////////////
1234 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
1235 nodeRec
.blockHeader
= nil
;
1237 err
= CheckInsertParams (filePtr
, iterator
, record
, recordLen
);
1241 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1243 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1246 ///////////////////////// Find Insert Position //////////////////////////////
1248 // always call SearchTree for Insert
1249 err
= SearchTree (btreePtr
, &iterator
->key
, treePathTable
, &insertNodeNum
, &nodeRec
, &index
);
1251 switch (err
) // set/replace/insert decision point
1253 case noErr
: err
= fsBTDuplicateRecordErr
;
1256 case fsBTRecordNotFoundErr
: break;
1258 case fsBTEmptyErr
: // if tree empty add 1st leaf node
1260 if (BTAvailableNodes(btreePtr
) == 0)
1262 err
= ExtendBTree (btreePtr
, btreePtr
->totalNodes
+ 1);
1263 M_ExitOnError (err
);
1266 err
= AllocateNode (btreePtr
, &insertNodeNum
);
1267 M_ExitOnError (err
);
1269 err
= GetNewNode (btreePtr
, insertNodeNum
, &nodeRec
);
1270 M_ExitOnError (err
);
1273 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1275 ((NodeDescPtr
)nodeRec
.buffer
)->kind
= kBTLeafNode
;
1276 ((NodeDescPtr
)nodeRec
.buffer
)->height
= 1;
1278 recordFit
= InsertKeyRecord (btreePtr
, nodeRec
.buffer
, 0,
1279 &iterator
->key
, KeyLength(btreePtr
, &iterator
->key
),
1280 record
->bufferAddress
, recordLen
);
1281 if (recordFit
!= true)
1283 err
= fsBTRecordTooLargeErr
;
1288 * Update the B-tree control block. Do this before
1289 * calling UpdateNode since it will compare the node's
1290 * height with treeDepth.
1292 btreePtr
->treeDepth
= 1;
1293 btreePtr
->rootNode
= insertNodeNum
;
1294 btreePtr
->firstLeafNode
= insertNodeNum
;
1295 btreePtr
->lastLeafNode
= insertNodeNum
;
1297 err
= UpdateNode (btreePtr
, &nodeRec
, 0, kLockTransaction
);
1298 M_ExitOnError (err
);
1300 M_BTreeHeaderDirty (btreePtr
);
1304 default: goto ErrorExit
;
1310 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1312 recordFit
= InsertKeyRecord (btreePtr
, nodeRec
.buffer
, index
,
1313 &iterator
->key
, KeyLength(btreePtr
, &iterator
->key
),
1314 record
->bufferAddress
, recordLen
);
1315 if (recordFit
== true)
1317 err
= UpdateNode (btreePtr
, &nodeRec
, 0, kLockTransaction
);
1318 M_ExitOnError (err
);
1324 /////////////////////// Extend File If Necessary ////////////////////////////
1326 nodesNeeded
= (SInt32
)btreePtr
->treeDepth
+ 1 - BTAvailableNodes(btreePtr
);
1327 if (nodesNeeded
> 0)
1329 nodesNeeded
+= (SInt32
)btreePtr
->totalNodes
;
1330 if (nodesNeeded
> CalcMapBits (btreePtr
)) // we'll need to add a map node too!
1333 err
= ExtendBTree (btreePtr
, nodesNeeded
);
1334 M_ExitOnError (err
);
1337 // no need to delete existing record
1339 err
= InsertTree (btreePtr
, treePathTable
, &iterator
->key
, record
->bufferAddress
,
1340 recordLen
, &nodeRec
, index
, 1, kInsertRecord
, &insertNodeNum
);
1341 M_ExitOnError (err
);
1344 //////////////////////////////// Success ////////////////////////////////////
1347 ++btreePtr
->writeCount
;
1348 ++btreePtr
->leafRecords
;
1349 M_BTreeHeaderDirty (btreePtr
);
1352 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
1353 iterator
->hint
.nodeNum
= insertNodeNum
;
1354 iterator
->hint
.index
= 0; // unused
1355 iterator
->hint
.reserved1
= 0;
1356 iterator
->hint
.reserved2
= 0;
1361 ////////////////////////////// Error Exit ///////////////////////////////////
1365 (void) ReleaseNode (btreePtr
, &nodeRec
);
1367 iterator
->hint
.writeCount
= 0;
1368 iterator
->hint
.nodeNum
= 0;
1369 iterator
->hint
.index
= 0;
1370 iterator
->hint
.reserved1
= 0;
1371 iterator
->hint
.reserved2
= 0;
1373 if (err
== fsBTEmptyErr
)
1374 err
= fsBTRecordNotFoundErr
;
1380 //////////////////////////////// BTReplaceRecord ////////////////////////////////
1382 OSStatus
BTReplaceRecord (FCB
*filePtr
,
1383 BTreeIterator
*iterator
,
1384 FSBufferDescriptor
*record
,
1388 BTreeControlBlockPtr btreePtr
;
1389 TreePathTable treePathTable
;
1391 BlockDescriptor nodeRec
;
1392 UInt32 insertNodeNum
;
1398 ////////////////////////// Priliminary Checks ///////////////////////////////
1400 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
1401 nodeRec
.blockHeader
= nil
;
1403 err
= CheckInsertParams (filePtr
, iterator
, record
, recordLen
);
1407 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1409 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1411 ////////////////////////////// Take A Hint //////////////////////////////////
1413 err
= IsItAHint (btreePtr
, iterator
, &validHint
);
1414 M_ExitOnError (err
);
1418 insertNodeNum
= iterator
->hint
.nodeNum
;
1420 err
= GetNode (btreePtr
, insertNodeNum
, &nodeRec
);
1424 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1426 err
= TrySimpleReplace (btreePtr
, nodeRec
.buffer
, iterator
, record
, recordLen
, &recordFit
);
1427 M_ExitOnError (err
);
1431 err
= UpdateNode (btreePtr
, &nodeRec
, 0, 0);
1432 M_ExitOnError (err
);
1434 ++btreePtr
->numValidHints
;
1440 (void) BTInvalidateHint( iterator
);
1443 err
= ReleaseNode (btreePtr
, &nodeRec
);
1444 M_ExitOnError (err
);
1448 (void) BTInvalidateHint( iterator
);
1453 ////////////////////////////// Get A Clue ///////////////////////////////////
1455 err
= SearchTree (btreePtr
, &iterator
->key
, treePathTable
, &insertNodeNum
, &nodeRec
, &index
);
1456 M_ExitOnError (err
); // record must exit for Replace
1458 // optimization - if simple replace will work then don't extend btree
1459 // \80\80 if we tried this before, and failed because it wouldn't fit then we shouldn't try this again...
1462 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1464 err
= TrySimpleReplace (btreePtr
, nodeRec
.buffer
, iterator
, record
, recordLen
, &recordFit
);
1465 M_ExitOnError (err
);
1469 err
= UpdateNode (btreePtr
, &nodeRec
, 0, 0);
1470 M_ExitOnError (err
);
1476 //////////////////////////// Make Some Room /////////////////////////////////
1478 nodesNeeded
= (SInt32
)btreePtr
->treeDepth
+ 1 - BTAvailableNodes(btreePtr
);
1479 if (nodesNeeded
> 0)
1481 nodesNeeded
+= (SInt32
)btreePtr
->totalNodes
;
1482 if (nodesNeeded
> CalcMapBits (btreePtr
)) // we'll need to add a map node too!
1485 err
= ExtendBTree (btreePtr
, nodesNeeded
);
1486 M_ExitOnError (err
);
1490 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1492 DeleteRecord (btreePtr
, nodeRec
.buffer
, index
); // delete existing key/record
1494 err
= InsertTree (btreePtr
, treePathTable
, &iterator
->key
, record
->bufferAddress
,
1495 recordLen
, &nodeRec
, index
, 1, kReplaceRecord
, &insertNodeNum
);
1496 M_ExitOnError (err
);
1498 ++btreePtr
->writeCount
; /* writeCount changes only if the tree structure changed */
1502 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
1503 iterator
->hint
.nodeNum
= insertNodeNum
;
1504 iterator
->hint
.index
= 0; // unused
1505 iterator
->hint
.reserved1
= 0;
1506 iterator
->hint
.reserved2
= 0;
1511 ////////////////////////////// Error Exit ///////////////////////////////////
1515 (void) ReleaseNode (btreePtr
, &nodeRec
);
1517 iterator
->hint
.writeCount
= 0;
1518 iterator
->hint
.nodeNum
= 0;
1519 iterator
->hint
.index
= 0;
1520 iterator
->hint
.reserved1
= 0;
1521 iterator
->hint
.reserved2
= 0;
1528 //////////////////////////////// BTUpdateRecord ////////////////////////////////
1531 BTUpdateRecord(FCB
*filePtr
, BTreeIterator
*iterator
,
1532 IterateCallBackProcPtr callBackProc
, void * callBackState
)
1535 BTreeControlBlockPtr btreePtr
;
1536 TreePathTable treePathTable
;
1537 BlockDescriptor nodeRec
;
1538 RecordPtr recordPtr
;
1540 UInt32 insertNodeNum
;
1546 ////////////////////////// Priliminary Checks ///////////////////////////////
1548 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
1549 nodeRec
.blockHeader
= nil
;
1551 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1553 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
1555 ////////////////////////////// Take A Hint //////////////////////////////////
1557 err
= IsItAHint (btreePtr
, iterator
, &validHint
);
1558 M_ExitOnError (err
);
1562 insertNodeNum
= iterator
->hint
.nodeNum
;
1564 err
= GetNode (btreePtr
, insertNodeNum
, &nodeRec
);
1567 if (((NodeDescPtr
)nodeRec
.buffer
)->kind
== kBTLeafNode
&&
1568 SearchNode (btreePtr
, nodeRec
.buffer
, &iterator
->key
, &index
))
1570 err
= GetRecordByIndex(btreePtr
, nodeRec
.buffer
, index
, &keyPtr
, &recordPtr
, &recordLen
);
1571 M_ExitOnError (err
);
1574 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1576 err
= callBackProc(keyPtr
, recordPtr
, callBackState
);
1577 M_ExitOnError (err
);
1579 err
= UpdateNode (btreePtr
, &nodeRec
, 0, 0);
1580 M_ExitOnError (err
);
1582 ++btreePtr
->numValidHints
;
1588 (void) BTInvalidateHint( iterator
);
1591 err
= ReleaseNode (btreePtr
, &nodeRec
);
1592 M_ExitOnError (err
);
1596 (void) BTInvalidateHint( iterator
);
1600 ////////////////////////////// Get A Clue ///////////////////////////////////
1602 err
= SearchTree (btreePtr
, &iterator
->key
, treePathTable
, &insertNodeNum
, &nodeRec
, &index
);
1603 M_ExitOnError (err
);
1605 err
= GetRecordByIndex(btreePtr
, nodeRec
.buffer
, index
, &keyPtr
, &recordPtr
, &recordLen
);
1606 M_ExitOnError (err
);
1609 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1611 err
= callBackProc(keyPtr
, recordPtr
, callBackState
);
1612 M_ExitOnError (err
);
1614 err
= UpdateNode (btreePtr
, &nodeRec
, 0, 0);
1615 M_ExitOnError (err
);
1619 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
1620 iterator
->hint
.nodeNum
= insertNodeNum
;
1621 iterator
->hint
.index
= 0;
1622 iterator
->hint
.reserved1
= 0;
1623 iterator
->hint
.reserved2
= 0;
1626 ////////////////////////////// Error Exit ///////////////////////////////////
1630 (void) ReleaseNode (btreePtr
, &nodeRec
);
1632 iterator
->hint
.writeCount
= 0;
1633 iterator
->hint
.nodeNum
= 0;
1634 iterator
->hint
.index
= 0;
1635 iterator
->hint
.reserved1
= 0;
1636 iterator
->hint
.reserved2
= 0;
1642 //////////////////////////////// BTDeleteRecord /////////////////////////////////
1644 OSStatus
BTDeleteRecord (FCB
*filePtr
,
1645 BTreeIterator
*iterator
)
1648 BTreeControlBlockPtr btreePtr
;
1649 TreePathTable treePathTable
;
1650 BlockDescriptor nodeRec
;
1656 ////////////////////////// Priliminary Checks ///////////////////////////////
1658 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
1659 nodeRec
.blockHeader
= nil
;
1661 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1662 M_ReturnErrorIf (iterator
== nil
, paramErr
);
1664 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1665 if (btreePtr
== nil
)
1667 err
= fsBTInvalidFileErr
;
1671 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1674 /////////////////////////////// Find Key ////////////////////////////////////
1676 //\80\80 check hint for simple delete case (index > 0, numRecords > 2)
1678 err
= SearchTree (btreePtr
, &iterator
->key
, treePathTable
, &nodeNum
, &nodeRec
, &index
);
1679 M_ExitOnError (err
); // record must exit for Delete
1682 /////////////////////// Extend File If Necessary ////////////////////////////
1684 nodesNeeded
= (SInt32
)btreePtr
->treeDepth
+ 1 - BTAvailableNodes(btreePtr
);
1685 if ((btreePtr
->attributes
& kBTVariableIndexKeysMask
) && (nodesNeeded
> 0))
1687 nodesNeeded
+= (SInt32
)btreePtr
->totalNodes
;
1688 if (nodesNeeded
> CalcMapBits (btreePtr
))
1691 err
= ExtendBTree (btreePtr
, nodesNeeded
);
1692 M_ExitOnError (err
);
1695 ///////////////////////////// Delete Record /////////////////////////////////
1697 err
= DeleteTree (btreePtr
, treePathTable
, &nodeRec
, index
, 1);
1698 M_ExitOnError (err
);
1700 ++btreePtr
->writeCount
;
1701 --btreePtr
->leafRecords
;
1702 M_BTreeHeaderDirty (btreePtr
);
1704 iterator
->hint
.nodeNum
= 0;
1708 ////////////////////////////// Error Exit ///////////////////////////////////
1711 (void) ReleaseNode (btreePtr
, &nodeRec
);
1718 OSStatus
BTGetInformation (FCB
*filePtr
,
1720 BTreeInfoRec
*info
)
1722 #pragma unused (version)
1724 BTreeControlBlockPtr btreePtr
;
1727 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1729 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1733 * This should not require the whole tree to be locked, just maybe the BTreeControlBlockPtr
1735 * REQUIRE_FILE_LOCK(btreePtr->fileRefNum, true);
1738 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1739 M_ReturnErrorIf (info
== nil
, paramErr
);
1741 //\80\80 check version?
1743 info
->nodeSize
= btreePtr
->nodeSize
;
1744 info
->maxKeyLength
= btreePtr
->maxKeyLength
;
1745 info
->treeDepth
= btreePtr
->treeDepth
;
1746 info
->numRecords
= btreePtr
->leafRecords
;
1747 info
->numNodes
= btreePtr
->totalNodes
;
1748 info
->numFreeNodes
= btreePtr
->freeNodes
;
1749 info
->lastfsync
= btreePtr
->lastfsync
;
1750 info
->keyCompareType
= btreePtr
->keyCompareType
;
1757 BTIsDirty(FCB
*filePtr
)
1759 BTreeControlBlockPtr btreePtr
;
1761 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1762 return TreeIsDirty(btreePtr
);
1765 /*-------------------------------------------------------------------------------
1766 Routine: BTFlushPath - Flush BTreeControlBlock to Header Node.
1768 Function: Brief_description_of_the_function_and_any_side_effects
1771 Input: pathPtr - pointer to path control block for B*Tree file to flush
1775 Result: noErr - success
1777 -------------------------------------------------------------------------------*/
1779 OSStatus
BTFlushPath (FCB
*filePtr
)
1782 BTreeControlBlockPtr btreePtr
;
1785 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1787 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1789 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1791 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
1793 err
= UpdateHeader (btreePtr
, false);
1799 /*-------------------------------------------------------------------------------
1800 Routine: BTReload - Reload B-tree Header Data.
1802 Function: Reload B-tree header data from disk. This is called after fsck
1803 has made repairs to the root filesystem. The filesystem is
1804 mounted read-only when BTReload is caled.
1807 Input: filePtr - the B*Tree file that needs its header updated
1811 Result: noErr - success
1813 -------------------------------------------------------------------------------*/
1816 BTReloadData(FCB
*filePtr
)
1819 BTreeControlBlockPtr btreePtr
;
1820 BlockDescriptor node
;
1821 BTHeaderRec
*header
;
1825 node
.blockHeader
= nil
;
1827 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1828 if (btreePtr
== nil
)
1829 return (fsBTInvalidFileErr
);
1831 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1833 err
= GetNode(btreePtr
, kHeaderNodeNum
, &node
);
1837 header
= (BTHeaderRec
*)((char *)node
.buffer
+ sizeof(BTNodeDescriptor
));
1838 if ((err
= VerifyHeader (filePtr
, header
)) == 0) {
1839 btreePtr
->treeDepth
= header
->treeDepth
;
1840 btreePtr
->rootNode
= header
->rootNode
;
1841 btreePtr
->leafRecords
= header
->leafRecords
;
1842 btreePtr
->firstLeafNode
= header
->firstLeafNode
;
1843 btreePtr
->lastLeafNode
= header
->lastLeafNode
;
1844 btreePtr
->maxKeyLength
= header
->maxKeyLength
;
1845 btreePtr
->totalNodes
= header
->totalNodes
;
1846 btreePtr
->freeNodes
= header
->freeNodes
;
1847 btreePtr
->btreeType
= header
->btreeType
;
1849 btreePtr
->flags
&= (~kBTHeaderDirty
);
1852 (void) ReleaseNode(btreePtr
, &node
);
1858 /*-------------------------------------------------------------------------------
1859 Routine: BTInvalidateHint - Invalidates the hint within a BTreeInterator.
1861 Function: Invalidates the hint within a BTreeInterator.
1864 Input: iterator - pointer to BTreeIterator
1866 Output: iterator - iterator with the hint.nodeNum cleared
1868 Result: noErr - success
1869 paramErr - iterator == nil
1870 -------------------------------------------------------------------------------*/
1873 OSStatus
BTInvalidateHint (BTreeIterator
*iterator
)
1875 if (iterator
== nil
)
1878 iterator
->hint
.nodeNum
= 0;
1886 /*-------------------------------------------------------------------------------
1887 Routine: BTGetLastSync
1889 Function: Returns the last time that this btree was flushed, does not include header.
1891 Input: filePtr - pointer file control block
1893 Output: lastfsync - time in seconds of last update
1895 Result: noErr - success
1896 paramErr - iterator == nil
1897 -------------------------------------------------------------------------------*/
1900 OSStatus
BTGetLastSync (FCB
*filePtr
,
1903 BTreeControlBlockPtr btreePtr
;
1906 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1908 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1910 /* Maybe instead of requiring a lock..an atomic set might be more appropriate */
1911 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
1913 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1914 M_ReturnErrorIf (lastsync
== nil
, paramErr
);
1916 *lastsync
= btreePtr
->lastfsync
;
1924 /*-------------------------------------------------------------------------------
1925 Routine: BTSetLastSync
1927 Function: Sets the last time that this btree was flushed, does not include header.
1930 Input: fcb - pointer file control block
1932 Output: lastfsync - time in seconds of last update
1934 Result: noErr - success
1935 paramErr - iterator == nil
1936 -------------------------------------------------------------------------------*/
1939 OSStatus
BTSetLastSync (FCB
*filePtr
,
1942 BTreeControlBlockPtr btreePtr
;
1945 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1947 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1949 /* Maybe instead of requiring a lock..an atomic set might be more appropriate */
1950 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
1952 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1953 M_ReturnErrorIf (lastsync
== nil
, paramErr
);
1955 btreePtr
->lastfsync
= lastsync
;
1962 OSStatus
BTHasContiguousNodes (FCB
*filePtr
)
1964 BTreeControlBlockPtr btreePtr
;
1967 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1969 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1971 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
1973 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1975 return NodesAreContiguous(FCBTOVCB(filePtr
), filePtr
, btreePtr
->nodeSize
);
1979 /*-------------------------------------------------------------------------------
1980 Routine: BTGetUserData
1982 Function: Read the user data area of the b-tree header node.
1984 -------------------------------------------------------------------------------*/
1987 BTGetUserData(FCB
*filePtr
, void * dataPtr
, int dataSize
)
1989 BTreeControlBlockPtr btreePtr
;
1990 BlockDescriptor node
;
1994 if (dataSize
> kBTreeHeaderUserBytes
)
1997 node
.blockHeader
= nil
;
1999 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
2000 if (btreePtr
== nil
)
2001 return (fsBTInvalidFileErr
);
2003 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
2005 err
= GetNode(btreePtr
, kHeaderNodeNum
, &node
);
2009 offset
= (char *)node
.buffer
+ sizeof(BTNodeDescriptor
) + sizeof(BTHeaderRec
);
2010 bcopy(offset
, dataPtr
, dataSize
);
2012 (void) ReleaseNode(btreePtr
, &node
);
2018 /*-------------------------------------------------------------------------------
2019 Routine: BTSetUserData
2021 Function: Write the user data area of the b-tree header node.
2022 -------------------------------------------------------------------------------*/
2025 BTSetUserData(FCB
*filePtr
, void * dataPtr
, int dataSize
)
2027 BTreeControlBlockPtr btreePtr
;
2028 BlockDescriptor node
;
2032 if (dataSize
> kBTreeHeaderUserBytes
)
2035 node
.blockHeader
= nil
;
2037 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
2038 if (btreePtr
== nil
)
2039 return (fsBTInvalidFileErr
);
2041 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
2043 err
= GetNode(btreePtr
, kHeaderNodeNum
, &node
);
2047 ModifyBlockStart(btreePtr
->fileRefNum
, &node
);
2049 offset
= (char *)node
.buffer
+ sizeof(BTNodeDescriptor
) + sizeof(BTHeaderRec
);
2050 bcopy(dataPtr
, offset
, dataSize
);
2052 err
= UpdateNode (btreePtr
, &node
, 0, 0);