2 * Copyright (c) 2000 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 //////////////////////////////////// Globals ////////////////////////////////////
158 /////////////////////////// BTree Module Entry Points ///////////////////////////
162 /*-------------------------------------------------------------------------------
163 Routine: BTOpenPath - Open a file for access as a B*Tree.
165 Function: Create BTree control block for a file, if necessary. Validates the
166 file to be sure it looks like a BTree file.
169 Input: filePtr - pointer to file to open as a B-tree
170 keyCompareProc - pointer to client's KeyCompare function
171 getBlockProc - pointer to client's GetBlock function
172 releaseBlockProc - pointer to client's ReleaseBlock function
173 setEndOfForkProc - pointer to client's SetEOF function
175 Result: noErr - success
176 paramErr - required ptr was nil
180 -------------------------------------------------------------------------------*/
182 OSStatus
BTOpenPath (FCB
*filePtr
,
183 KeyCompareProcPtr keyCompareProc
,
184 GetBlockProcPtr getBlockProc
,
185 ReleaseBlockProcPtr releaseBlockProc
,
186 SetEndOfForkProcPtr setEndOfForkProc
,
187 SetBlockSizeProcPtr setBlockSizeProc
)
190 BTreeControlBlockPtr btreePtr
;
194 ////////////////////// Preliminary Error Checking ///////////////////////////
196 if ( filePtr
== nil
||
197 getBlockProc
== nil
||
198 releaseBlockProc
== nil
||
199 setEndOfForkProc
== nil
||
200 setBlockSizeProc
== nil
)
205 if ( filePtr
->fcbBTCBPtr
!= nil
) // already has a BTreeCB
208 // is file large enough to contain header node?
209 if ( filePtr
->fcbEOF
< kMinNodeSize
)
210 return fsBTInvalidFileErr
; //\80\80 or E_BadHeader?
213 //////////////////////// Allocate Control Block /////////////////////////////
215 btreePtr
= (BTreeControlBlock
*) NewPtrSysClear( sizeof( BTreeControlBlock
) );
218 Panic ("\pBTOpen: no memory for btreePtr.");
222 btreePtr
->getBlockProc
= getBlockProc
;
223 btreePtr
->releaseBlockProc
= releaseBlockProc
;
224 btreePtr
->setEndOfForkProc
= setEndOfForkProc
;
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 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
238 // it is now safe to call M_ExitOnError (err)
240 err
= setBlockSizeProc (btreePtr
->fileRefNum
, nodeRec
.blockSize
, 1);
244 err
= getBlockProc (btreePtr
->fileRefNum
,
250 nodeRec
.buffer
= nil
;
251 nodeRec
.blockHeader
= nil
;
252 Panic("\pBTOpen: getNodeProc returned error getting header node.");
255 ++btreePtr
->numGetNodes
;
256 header
= (BTHeaderRec
*) ((u_long
)nodeRec
.buffer
+ sizeof(BTNodeDescriptor
));
259 ///////////////////////////// verify header /////////////////////////////////
261 err
= VerifyHeader (filePtr
, header
);
265 ///////////////////// Initalize fields from header //////////////////////////
267 PanicIf ( (FCBTOVCB(filePtr
)->vcbSigWord
!= 0x4244) && (header
->nodeSize
== 512), "\p BTOpenPath: wrong node size for HFS+ volume!"); // 0x4244 = 'BD'
269 btreePtr
->treeDepth
= header
->treeDepth
;
270 btreePtr
->rootNode
= header
->rootNode
;
271 btreePtr
->leafRecords
= header
->leafRecords
;
272 btreePtr
->firstLeafNode
= header
->firstLeafNode
;
273 btreePtr
->lastLeafNode
= header
->lastLeafNode
;
274 btreePtr
->nodeSize
= header
->nodeSize
;
275 btreePtr
->maxKeyLength
= header
->maxKeyLength
;
276 btreePtr
->totalNodes
= header
->totalNodes
;
277 btreePtr
->freeNodes
= header
->freeNodes
;
278 // ignore header->clumpSize; //\80\80 rename this field?
279 btreePtr
->btreeType
= header
->btreeType
;
281 btreePtr
->attributes
= header
->attributes
;
283 if ( btreePtr
->maxKeyLength
> 40 )
284 btreePtr
->attributes
|= (kBTBigKeysMask
+ kBTVariableIndexKeysMask
); //\80\80 we need a way to save these attributes
286 /////////////////////// Initialize dynamic fields ///////////////////////////
288 btreePtr
->version
= kBTreeVersion
;
290 btreePtr
->writeCount
= 1;
292 /////////////////////////// Check Header Node ///////////////////////////////
294 // set kBadClose attribute bit, and UpdateNode
296 /* b-tree node size must be at least as big as the physical block size */
297 if (btreePtr
->nodeSize
< nodeRec
.blockSize
)
300 * If this tree has any records or the media is writeable then
301 * we cannot mount using the current physical block size.
303 if (btreePtr
->leafRecords
> 0 ||
304 VTOHFS(btreePtr
->fileRefNum
)->hfs_media_writeable
)
306 err
= fsBTBadNodeSize
;
311 // if nodeSize Matches then we don't need to release, just CheckNode
312 if ( btreePtr
->nodeSize
== nodeRec
.blockSize
)
314 err
= CheckNode (btreePtr
, nodeRec
.buffer
);
316 VTOVCB(btreePtr
->fileRefNum
)->vcbFlags
|= kHFS_DamagedVolume
;
321 err
= setBlockSizeProc (btreePtr
->fileRefNum
, btreePtr
->nodeSize
, 32); //\80\80 we should try and get this down to 8
325 * Need to use kTrashBlock option to force the
326 * buffer cache to read the entire node
328 err
= releaseBlockProc(btreePtr
->fileRefNum
, &nodeRec
, kTrashBlock
);
329 ++btreePtr
->numReleaseNodes
;
332 err
= GetNode (btreePtr
, kHeaderNodeNum
, &nodeRec
); // calls CheckNode...
336 //\80\80 total nodes * node size <= LEOF?
339 err
= ReleaseNode (btreePtr
, &nodeRec
);
343 * Under Mac OS, b-tree nodes can be non-contiguous on disk when the
344 * allocation block size is smaller than the b-tree node size.
346 * If journaling is turned on for this volume we can't deal with this
347 * situation and so we bail out. If journaling isn't on it's ok as
348 * hfs_strategy_fragmented() deals with it. Journaling can't support
349 * this because it assumes that if you give it a block that it's
350 * contiguous on disk.
352 if ( FCBTOHFS(filePtr
)->jnl
&& !NodesAreContiguous(FCBTOVCB(filePtr
), filePtr
, btreePtr
->nodeSize
) ) {
353 return fsBTInvalidNodeErr
;
356 //////////////////////////////// Success ////////////////////////////////////
358 //\80\80 align LEOF to multiple of node size? - just on close
363 /////////////////////// Error - Clean up and Exit ///////////////////////////
367 filePtr
->fcbBTCBPtr
= nil
;
368 (void) ReleaseNode (btreePtr
, &nodeRec
);
369 DisposePtr( (Ptr
) btreePtr
);
376 /*-------------------------------------------------------------------------------
377 Routine: BTClosePath - Flush BTree Header and Deallocate Memory for BTree.
379 Function: Flush the BTreeControlBlock fields to header node, and delete BTree control
380 block and key descriptor associated with the file if filePtr is last
381 path of type kBTreeType ('btre').
384 Input: filePtr - pointer to file to delete BTree control block for.
386 Result: noErr - success
389 -------------------------------------------------------------------------------*/
391 OSStatus
BTClosePath (FCB
*filePtr
)
394 BTreeControlBlockPtr btreePtr
;
396 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
399 return fsBTInvalidFileErr
;
401 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
403 ////////////////////// Check for other BTree Paths //////////////////////////
405 btreePtr
->attributes
&= ~kBTBadCloseMask
; // clear "bad close" attribute bit
406 err
= UpdateHeader (btreePtr
, true);
409 DisposePtr( (Ptr
) btreePtr
);
410 filePtr
->fcbBTCBPtr
= nil
;
414 /////////////////////// Error - Clean Up and Exit ///////////////////////////
423 /*-------------------------------------------------------------------------------
424 Routine: BTSearchRecord - Search BTree for a record with a matching key.
426 Function: Search for position in B*Tree indicated by searchKey. If a valid node hint
427 is provided, it will be searched first, then SearchTree will be called.
428 If a BTreeIterator is provided, it will be set to the position found as
429 a result of the search. If a record exists at that position, and a BufferDescriptor
430 is supplied, the record will be copied to the buffer (as much as will fit),
431 and recordLen will be set to the length of the record.
433 If an error other than fsBTRecordNotFoundErr occurs, the BTreeIterator, if any,
434 is invalidated, and recordLen is set to 0.
437 Input: pathPtr - pointer to path for BTree file.
438 searchKey - pointer to search key to match.
439 hintPtr - pointer to hint (may be nil)
441 Output: record - pointer to BufferDescriptor containing record
442 recordLen - length of data at recordPtr
443 iterator - pointer to BTreeIterator indicating position result of search
445 Result: noErr - success, record contains copy of record found
446 fsBTRecordNotFoundErr - record was not found, no data copied
447 fsBTInvalidFileErr - no BTreeControlBlock is allocated for the fork
448 fsBTInvalidKeyLengthErr -
450 -------------------------------------------------------------------------------*/
452 OSStatus
BTSearchRecord (FCB
*filePtr
,
453 BTreeIterator
*searchIterator
,
454 FSBufferDescriptor
*record
,
456 BTreeIterator
*resultIterator
)
459 BTreeControlBlockPtr btreePtr
;
460 TreePathTable treePathTable
;
462 BlockDescriptor node
;
470 if (filePtr
== nil
) return paramErr
;
471 if (searchIterator
== nil
) return paramErr
;
474 node
.blockHeader
= nil
;
476 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
477 if (btreePtr
== nil
) return fsBTInvalidFileErr
;
479 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
483 ////////////////////////////// Take A Hint //////////////////////////////////
485 err
= IsItAHint (btreePtr
, searchIterator
, &validHint
);
490 nodeNum
= searchIterator
->hint
.nodeNum
;
492 err
= GetNode (btreePtr
, nodeNum
, &node
);
495 if ( ((BTNodeDescriptor
*) node
.buffer
)->kind
== kBTLeafNode
&&
496 ((BTNodeDescriptor
*) node
.buffer
)->numRecords
> 0 )
498 foundRecord
= SearchNode (btreePtr
, node
.buffer
, &searchIterator
->key
, &index
);
500 //\80\80 if !foundRecord, we could still skip tree search if ( 0 < index < numRecords )
503 if (foundRecord
== false)
505 err
= ReleaseNode (btreePtr
, &node
);
510 ++btreePtr
->numValidHints
;
514 if( foundRecord
== false )
515 (void) BTInvalidateHint( searchIterator
);
519 //////////////////////////// Search The Tree ////////////////////////////////
521 if (foundRecord
== false)
523 err
= SearchTree ( btreePtr
, &searchIterator
->key
, treePathTable
, &nodeNum
, &node
, &index
);
526 case noErr
: foundRecord
= true; break;
527 case fsBTRecordNotFoundErr
: break;
528 default: goto ErrorExit
;
533 //////////////////////////// Get the Record /////////////////////////////////
535 if (foundRecord
== true)
537 //XXX Should check for errors! Or BlockMove could choke on recordPtr!!!
538 GetRecordByIndex (btreePtr
, node
.buffer
, index
, &keyPtr
, &recordPtr
, &len
);
540 if (recordLen
!= nil
) *recordLen
= len
;
544 ByteCount recordSize
;
546 recordSize
= record
->itemCount
* record
->itemSize
;
548 if (len
> recordSize
) len
= recordSize
;
550 BlockMoveData (recordPtr
, record
->bufferAddress
, len
);
555 /////////////////////// Success - Update Iterator ///////////////////////////
557 if (resultIterator
!= nil
)
559 resultIterator
->hint
.writeCount
= btreePtr
->writeCount
;
560 resultIterator
->hint
.nodeNum
= nodeNum
;
561 resultIterator
->hint
.index
= index
;
563 resultIterator
->hint
.reserved1
= 0;
564 resultIterator
->hint
.reserved2
= 0;
565 resultIterator
->version
= 0;
566 resultIterator
->reserved
= 0;
568 // copy the key in the BTree when found rather than searchIterator->key to get proper case/diacriticals
569 if (foundRecord
== true)
570 BlockMoveData ((Ptr
)keyPtr
, (Ptr
)&resultIterator
->key
, CalcKeySize(btreePtr
, keyPtr
));
572 BlockMoveData ((Ptr
)&searchIterator
->key
, (Ptr
)&resultIterator
->key
, CalcKeySize(btreePtr
, &searchIterator
->key
));
575 err
= ReleaseNode (btreePtr
, &node
);
578 if (foundRecord
== false) return fsBTRecordNotFoundErr
;
582 /////////////////////// Error - Clean Up and Exit ///////////////////////////
586 if (recordLen
!= nil
)
589 if (resultIterator
!= nil
)
591 resultIterator
->hint
.writeCount
= 0;
592 resultIterator
->hint
.nodeNum
= 0;
593 resultIterator
->hint
.index
= 0;
594 resultIterator
->hint
.reserved1
= 0;
595 resultIterator
->hint
.reserved2
= 0;
597 resultIterator
->version
= 0;
598 resultIterator
->reserved
= 0;
599 resultIterator
->key
.length16
= 0; // zero out two bytes to cover both types of keys
602 if ( err
== fsBTEmptyErr
)
603 err
= fsBTRecordNotFoundErr
;
610 /*-------------------------------------------------------------------------------
611 Routine: BTIterateRecord - Find the first, next, previous, or last record.
613 Function: Find the first, next, previous, or last record in the BTree
615 Input: pathPtr - pointer to path iterate records for.
616 operation - iteration operation (first,next,prev,last)
617 iterator - pointer to iterator indicating start position
619 Output: iterator - iterator is updated to indicate new position
620 newKeyPtr - pointer to buffer to copy key found by iteration
621 record - pointer to buffer to copy record found by iteration
622 recordLen - length of record
624 Result: noErr - success
626 -------------------------------------------------------------------------------*/
628 OSStatus
BTIterateRecord (FCB
*filePtr
,
629 BTreeIterationOperation operation
,
630 BTreeIterator
*iterator
,
631 FSBufferDescriptor
*record
,
635 BTreeControlBlockPtr btreePtr
;
643 BlockDescriptor left
, node
, right
;
647 ////////////////////////// Priliminary Checks ///////////////////////////////
650 left
.blockHeader
= nil
;
652 right
.blockHeader
= nil
;
654 node
.blockHeader
= nil
;
662 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
665 return fsBTInvalidFileErr
; //\80\80 handle properly
668 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
670 if ((operation
!= kBTreeFirstRecord
) &&
671 (operation
!= kBTreeNextRecord
) &&
672 (operation
!= kBTreeCurrentRecord
) &&
673 (operation
!= kBTreePrevRecord
) &&
674 (operation
!= kBTreeLastRecord
))
676 err
= fsInvalidIterationMovmentErr
;
680 /////////////////////// Find First or Last Record ///////////////////////////
682 if ((operation
== kBTreeFirstRecord
) || (operation
== kBTreeLastRecord
))
684 if (operation
== kBTreeFirstRecord
) nodeNum
= btreePtr
->firstLeafNode
;
685 else nodeNum
= btreePtr
->lastLeafNode
;
693 err
= GetNode (btreePtr
, nodeNum
, &node
);
696 if ( ((NodeDescPtr
) node
.buffer
)->kind
!= kBTLeafNode
||
697 ((NodeDescPtr
) node
.buffer
)->numRecords
<= 0 )
699 err
= ReleaseNode (btreePtr
, &node
);
702 err
= fsBTInvalidNodeErr
;
703 MARK_VOLUMEDAMAGED(filePtr
);
707 if (operation
== kBTreeFirstRecord
) index
= 0;
708 else index
= ((BTNodeDescriptor
*) node
.buffer
)->numRecords
- 1;
710 goto CopyData
; //\80\80 is there a cleaner way?
714 //////////////////////// Find Iterator Position /////////////////////////////
716 err
= FindIteratorPosition (btreePtr
, iterator
,
717 &left
, &node
, &right
, &nodeNum
, &index
, &foundRecord
);
721 ///////////////////// Find Next Or Previous Record //////////////////////////
723 if (operation
== kBTreePrevRecord
)
731 if (left
.buffer
== nil
)
733 nodeNum
= ((NodeDescPtr
) node
.buffer
)->bLink
;
736 err
= GetNode (btreePtr
, nodeNum
, &left
);
739 err
= fsBTStartOfIterationErr
;
743 // Before we stomp on "right", we'd better release it if needed
744 if (right
.buffer
!= nil
) {
745 err
= ReleaseNode(btreePtr
, &right
);
751 index
= ((NodeDescPtr
) node
.buffer
)->numRecords
-1;
754 else if (operation
== kBTreeNextRecord
)
756 if ((foundRecord
!= true) &&
757 (((NodeDescPtr
) node
.buffer
)->fLink
== 0) &&
758 (index
== ((NodeDescPtr
) node
.buffer
)->numRecords
))
760 err
= fsBTEndOfIterationErr
;
764 // we did not find the record but the index is already positioned correctly
765 if ((foundRecord
== false) && (index
!= ((NodeDescPtr
) node
.buffer
)->numRecords
))
768 // we found the record OR we have to look in the next node
769 if (index
< ((NodeDescPtr
) node
.buffer
)->numRecords
-1)
775 if (right
.buffer
== nil
)
777 nodeNum
= ((NodeDescPtr
) node
.buffer
)->fLink
;
780 err
= GetNode (btreePtr
, nodeNum
, &right
);
783 err
= fsBTEndOfIterationErr
;
787 // Before we stomp on "left", we'd better release it if needed
788 if (left
.buffer
!= nil
) {
789 err
= ReleaseNode(btreePtr
, &left
);
798 else // operation == kBTreeCurrentRecord
800 // make sure we have something... <CS9>
801 if ((foundRecord
!= true) &&
802 (index
>= ((NodeDescPtr
) node
.buffer
)->numRecords
))
804 err
= fsBTEndOfIterationErr
;
809 //////////////////// Copy Record And Update Iterator ////////////////////////
813 // added check for errors <CS9>
814 err
= GetRecordByIndex (btreePtr
, node
.buffer
, index
, &keyPtr
, &recordPtr
, &len
);
817 if (recordLen
!= nil
)
822 ByteCount recordSize
;
824 recordSize
= record
->itemCount
* record
->itemSize
;
826 if (len
> recordSize
) len
= recordSize
;
828 BlockMoveData (recordPtr
, record
->bufferAddress
, len
);
831 if (iterator
!= nil
) // first & last do not require iterator
833 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
834 iterator
->hint
.nodeNum
= nodeNum
;
835 iterator
->hint
.index
= index
;
836 iterator
->hint
.reserved1
= 0;
837 iterator
->hint
.reserved2
= 0;
839 iterator
->version
= 0;
840 iterator
->reserved
= 0;
843 * Check for infinite loops by making sure we do not
844 * process more leaf records, than can possibly be (or the BTree header
845 * is seriously damaged)....a brute force method.
847 if ((operation
== kBTreeFirstRecord
) || (operation
== kBTreeLastRecord
))
848 iterator
->hitCount
= 1;
849 else if (operation
!= kBTreeCurrentRecord
)
850 iterator
->hitCount
+= 1;
851 /* Always use the highest max, in case the grows while iterating */
852 iterator
->maxLeafRecs
= max(btreePtr
->leafRecords
, iterator
->maxLeafRecs
);
855 if (iterator
->hitCount
> iterator
->maxLeafRecs
+ kNumLeafRecSlack
)
857 err
= fsBTInvalidNodeErr
;
858 MARK_VOLUMEDAMAGED(filePtr
);
863 BlockMoveData ((Ptr
)keyPtr
, (Ptr
)&iterator
->key
, CalcKeySize(btreePtr
, keyPtr
));
867 ///////////////////////////// Release Nodes /////////////////////////////////
869 err
= ReleaseNode (btreePtr
, &node
);
872 if (left
.buffer
!= nil
)
874 err
= ReleaseNode (btreePtr
, &left
);
878 if (right
.buffer
!= nil
)
880 err
= ReleaseNode (btreePtr
, &right
);
886 /////////////////////// Error - Clean Up and Exit ///////////////////////////
890 (void) ReleaseNode (btreePtr
, &left
);
891 (void) ReleaseNode (btreePtr
, &node
);
892 (void) ReleaseNode (btreePtr
, &right
);
894 if (recordLen
!= nil
)
899 iterator
->hint
.writeCount
= 0;
900 iterator
->hint
.nodeNum
= 0;
901 iterator
->hint
.index
= 0;
902 iterator
->hint
.reserved1
= 0;
903 iterator
->hint
.reserved2
= 0;
905 iterator
->version
= 0;
906 iterator
->reserved
= 0;
907 iterator
->key
.length16
= 0;
910 if ( err
== fsBTEmptyErr
|| err
== fsBTEndOfIterationErr
)
911 err
= fsBTRecordNotFoundErr
;
917 /*-------------------------------------------------------------------------------
918 Routine: BTIterateRecords
920 Function: Find a series of records
922 Input: filePtr - b-tree file
923 operation - iteration operation (first,next,prev,last)
924 iterator - pointer to iterator indicating start position
925 callBackProc - pointer to routince to process a record
926 callBackState - pointer to state data (used by callBackProc)
928 Output: iterator - iterator is updated to indicate new position
930 Result: noErr - success
932 -------------------------------------------------------------------------------*/
935 BTIterateRecords(FCB
*filePtr
, BTreeIterationOperation operation
, BTreeIterator
*iterator
,
936 IterateCallBackProcPtr callBackProc
, void * callBackState
)
939 BTreeControlBlockPtr btreePtr
;
945 BlockDescriptor left
, node
, right
;
949 ////////////////////////// Priliminary Checks ///////////////////////////////
952 left
.blockHeader
= nil
;
954 right
.blockHeader
= nil
;
956 node
.blockHeader
= nil
;
958 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
960 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
962 if ((operation
!= kBTreeFirstRecord
) &&
963 (operation
!= kBTreeNextRecord
) &&
964 (operation
!= kBTreeCurrentRecord
) &&
965 (operation
!= kBTreePrevRecord
) &&
966 (operation
!= kBTreeLastRecord
))
968 err
= fsInvalidIterationMovmentErr
;
972 /////////////////////// Find First or Last Record ///////////////////////////
974 if ((operation
== kBTreeFirstRecord
) || (operation
== kBTreeLastRecord
))
976 if (operation
== kBTreeFirstRecord
)
977 nodeNum
= btreePtr
->firstLeafNode
;
979 nodeNum
= btreePtr
->lastLeafNode
;
987 err
= GetNode(btreePtr
, nodeNum
, &node
);
990 if ( ((NodeDescPtr
)node
.buffer
)->kind
!= kBTLeafNode
||
991 ((NodeDescPtr
)node
.buffer
)->numRecords
<= 0 )
993 err
= ReleaseNode(btreePtr
, &node
);
996 err
= fsBTInvalidNodeErr
;
997 MARK_VOLUMEDAMAGED(filePtr
);
1001 if (operation
== kBTreeFirstRecord
)
1004 index
= ((BTNodeDescriptor
*) node
.buffer
)->numRecords
- 1;
1009 //////////////////////// Find Iterator Position /////////////////////////////
1011 err
= FindIteratorPosition(btreePtr
, iterator
, &left
, &node
, &right
,
1012 &nodeNum
, &index
, &foundRecord
);
1013 if (err
== fsBTRecordNotFoundErr
)
1018 ///////////////////// Find Next Or Previous Record //////////////////////////
1020 if (operation
== kBTreePrevRecord
)
1028 if (left
.buffer
== nil
)
1030 nodeNum
= ((NodeDescPtr
) node
.buffer
)->bLink
;
1033 err
= GetNode(btreePtr
, nodeNum
, &left
);
1036 err
= fsBTStartOfIterationErr
;
1040 // Before we stomp on "right", we'd better release it if needed
1041 if (right
.buffer
!= nil
) {
1042 err
= ReleaseNode(btreePtr
, &right
);
1048 index
= ((NodeDescPtr
) node
.buffer
)->numRecords
-1;
1051 else if (operation
== kBTreeNextRecord
)
1053 if ((foundRecord
!= true) &&
1054 (((NodeDescPtr
)node
.buffer
)->fLink
== 0) &&
1055 (index
== ((NodeDescPtr
)node
.buffer
)->numRecords
))
1057 err
= fsBTEndOfIterationErr
;
1061 // we did not find the record but the index is already positioned correctly
1062 if ((foundRecord
== false) && (index
!= ((NodeDescPtr
)node
.buffer
)->numRecords
))
1065 // we found the record OR we have to look in the next node
1066 if (index
< ((NodeDescPtr
)node
.buffer
)->numRecords
-1)
1072 if (right
.buffer
== nil
)
1074 nodeNum
= ((NodeDescPtr
)node
.buffer
)->fLink
;
1077 err
= GetNode(btreePtr
, nodeNum
, &right
);
1080 err
= fsBTEndOfIterationErr
;
1084 // Before we stomp on "left", we'd better release it if needed
1085 if (left
.buffer
!= nil
) {
1086 err
= ReleaseNode(btreePtr
, &left
);
1095 else // operation == kBTreeCurrentRecord
1097 // make sure we have something... <CS9>
1098 if ((foundRecord
!= true) &&
1099 (index
>= ((NodeDescPtr
)node
.buffer
)->numRecords
))
1101 err
= fsBTEndOfIterationErr
;
1106 //////////////////// Process Records Using Callback ////////////////////////
1109 err
= GetRecordByIndex(btreePtr
, node
.buffer
, index
, &keyPtr
, &recordPtr
, &len
);
1116 if (callBackProc(keyPtr
, recordPtr
, len
, callBackState
) == 0)
1119 if ((index
+1) < ((NodeDescPtr
)node
.buffer
)->numRecords
) {
1122 if (right
.buffer
== nil
)
1124 nodeNum
= ((NodeDescPtr
)node
.buffer
)->fLink
;
1127 err
= GetNode(btreePtr
, nodeNum
, &right
);
1130 err
= fsBTEndOfIterationErr
;
1134 // Before we stomp on "left", we'd better release it if needed
1135 if (left
.buffer
!= nil
) {
1136 err
= ReleaseNode(btreePtr
, &left
);
1144 err
= GetRecordByIndex(btreePtr
, node
.buffer
, index
,
1145 &keyPtr
, &recordPtr
, &len
);
1153 ///////////////// Update Iterator to Last Item Processed /////////////////////
1156 if (iterator
!= nil
) // first & last have optional iterator
1158 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
1159 iterator
->hint
.nodeNum
= nodeNum
;
1160 iterator
->hint
.index
= index
;
1161 iterator
->version
= 0;
1163 BlockMoveData((Ptr
)keyPtr
, (Ptr
)&iterator
->key
, CalcKeySize(btreePtr
, keyPtr
));
1168 ///////////////////////////// Release Nodes /////////////////////////////////
1170 err
= ReleaseNode(btreePtr
, &node
);
1173 if (left
.buffer
!= nil
)
1175 err
= ReleaseNode(btreePtr
, &left
);
1179 if (right
.buffer
!= nil
)
1181 err
= ReleaseNode(btreePtr
, &right
);
1187 /////////////////////// Error - Clean Up and Exit ///////////////////////////
1191 (void) ReleaseNode(btreePtr
, &left
);
1192 (void) ReleaseNode(btreePtr
, &node
);
1193 (void) ReleaseNode(btreePtr
, &right
);
1195 if (iterator
!= nil
)
1197 iterator
->hint
.writeCount
= 0;
1198 iterator
->hint
.nodeNum
= 0;
1199 iterator
->hint
.index
= 0;
1200 iterator
->version
= 0;
1201 iterator
->key
.length16
= 0;
1204 if ( err
== fsBTEmptyErr
|| err
== fsBTEndOfIterationErr
)
1205 err
= fsBTRecordNotFoundErr
;
1211 //////////////////////////////// BTInsertRecord /////////////////////////////////
1213 OSStatus
BTInsertRecord (FCB
*filePtr
,
1214 BTreeIterator
*iterator
,
1215 FSBufferDescriptor
*record
,
1219 BTreeControlBlockPtr btreePtr
;
1220 TreePathTable treePathTable
;
1222 BlockDescriptor nodeRec
;
1223 UInt32 insertNodeNum
;
1227 ////////////////////////// Priliminary Checks ///////////////////////////////
1229 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
1230 nodeRec
.blockHeader
= nil
;
1232 err
= CheckInsertParams (filePtr
, iterator
, record
, recordLen
);
1236 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1238 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1241 ///////////////////////// Find Insert Position //////////////////////////////
1243 // always call SearchTree for Insert
1244 err
= SearchTree (btreePtr
, &iterator
->key
, treePathTable
, &insertNodeNum
, &nodeRec
, &index
);
1246 switch (err
) // set/replace/insert decision point
1248 case noErr
: err
= fsBTDuplicateRecordErr
;
1251 case fsBTRecordNotFoundErr
: break;
1253 case fsBTEmptyErr
: // if tree empty add 1st leaf node
1255 if (btreePtr
->freeNodes
== 0)
1257 err
= ExtendBTree (btreePtr
, btreePtr
->totalNodes
+ 1);
1258 M_ExitOnError (err
);
1261 err
= AllocateNode (btreePtr
, &insertNodeNum
);
1262 M_ExitOnError (err
);
1264 err
= GetNewNode (btreePtr
, insertNodeNum
, &nodeRec
);
1265 M_ExitOnError (err
);
1268 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1270 ((NodeDescPtr
)nodeRec
.buffer
)->kind
= kBTLeafNode
;
1271 ((NodeDescPtr
)nodeRec
.buffer
)->height
= 1;
1273 recordFit
= InsertKeyRecord (btreePtr
, nodeRec
.buffer
, 0,
1274 &iterator
->key
, KeyLength(btreePtr
, &iterator
->key
),
1275 record
->bufferAddress
, recordLen
);
1276 if (recordFit
!= true)
1278 err
= fsBTRecordTooLargeErr
;
1282 err
= UpdateNode (btreePtr
, &nodeRec
, 0, kLockTransaction
);
1283 M_ExitOnError (err
);
1285 // update BTreeControlBlock
1286 btreePtr
->treeDepth
= 1;
1287 btreePtr
->rootNode
= insertNodeNum
;
1288 btreePtr
->firstLeafNode
= insertNodeNum
;
1289 btreePtr
->lastLeafNode
= insertNodeNum
;
1291 M_BTreeHeaderDirty (btreePtr
);
1295 default: goto ErrorExit
;
1301 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1303 recordFit
= InsertKeyRecord (btreePtr
, nodeRec
.buffer
, index
,
1304 &iterator
->key
, KeyLength(btreePtr
, &iterator
->key
),
1305 record
->bufferAddress
, recordLen
);
1306 if (recordFit
== true)
1308 err
= UpdateNode (btreePtr
, &nodeRec
, 0, kLockTransaction
);
1309 M_ExitOnError (err
);
1315 /////////////////////// Extend File If Necessary ////////////////////////////
1317 nodesNeeded
= btreePtr
->treeDepth
+ 1 - btreePtr
->freeNodes
; //\80\80 math limit
1318 if (nodesNeeded
> 0)
1320 nodesNeeded
+= btreePtr
->totalNodes
;
1321 if (nodesNeeded
> CalcMapBits (btreePtr
)) // we'll need to add a map node too!
1324 err
= ExtendBTree (btreePtr
, nodesNeeded
);
1325 M_ExitOnError (err
);
1328 // no need to delete existing record
1330 err
= InsertTree (btreePtr
, treePathTable
, &iterator
->key
, record
->bufferAddress
,
1331 recordLen
, &nodeRec
, index
, 1, kInsertRecord
, &insertNodeNum
);
1332 M_ExitOnError (err
);
1335 //////////////////////////////// Success ////////////////////////////////////
1338 ++btreePtr
->writeCount
;
1339 ++btreePtr
->leafRecords
;
1340 M_BTreeHeaderDirty (btreePtr
);
1343 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
1344 iterator
->hint
.nodeNum
= insertNodeNum
;
1345 iterator
->hint
.index
= 0; // unused
1346 iterator
->hint
.reserved1
= 0;
1347 iterator
->hint
.reserved2
= 0;
1352 ////////////////////////////// Error Exit ///////////////////////////////////
1356 (void) ReleaseNode (btreePtr
, &nodeRec
);
1358 iterator
->hint
.writeCount
= 0;
1359 iterator
->hint
.nodeNum
= 0;
1360 iterator
->hint
.index
= 0;
1361 iterator
->hint
.reserved1
= 0;
1362 iterator
->hint
.reserved2
= 0;
1364 if (err
== fsBTEmptyErr
)
1365 err
= fsBTRecordNotFoundErr
;
1371 //////////////////////////////// BTReplaceRecord ////////////////////////////////
1373 OSStatus
BTReplaceRecord (FCB
*filePtr
,
1374 BTreeIterator
*iterator
,
1375 FSBufferDescriptor
*record
,
1379 BTreeControlBlockPtr btreePtr
;
1380 TreePathTable treePathTable
;
1382 BlockDescriptor nodeRec
;
1383 UInt32 insertNodeNum
;
1389 ////////////////////////// Priliminary Checks ///////////////////////////////
1391 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
1392 nodeRec
.blockHeader
= nil
;
1394 err
= CheckInsertParams (filePtr
, iterator
, record
, recordLen
);
1398 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1400 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1402 ////////////////////////////// Take A Hint //////////////////////////////////
1404 err
= IsItAHint (btreePtr
, iterator
, &validHint
);
1405 M_ExitOnError (err
);
1409 insertNodeNum
= iterator
->hint
.nodeNum
;
1411 err
= GetNode (btreePtr
, insertNodeNum
, &nodeRec
);
1415 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1417 err
= TrySimpleReplace (btreePtr
, nodeRec
.buffer
, iterator
, record
, recordLen
, &recordFit
);
1418 M_ExitOnError (err
);
1422 err
= UpdateNode (btreePtr
, &nodeRec
, 0, 0);
1423 M_ExitOnError (err
);
1425 ++btreePtr
->numValidHints
;
1431 (void) BTInvalidateHint( iterator
);
1434 err
= ReleaseNode (btreePtr
, &nodeRec
);
1435 M_ExitOnError (err
);
1439 (void) BTInvalidateHint( iterator
);
1444 ////////////////////////////// Get A Clue ///////////////////////////////////
1446 err
= SearchTree (btreePtr
, &iterator
->key
, treePathTable
, &insertNodeNum
, &nodeRec
, &index
);
1447 M_ExitOnError (err
); // record must exit for Replace
1449 // optimization - if simple replace will work then don't extend btree
1450 // \80\80 if we tried this before, and failed because it wouldn't fit then we shouldn't try this again...
1453 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1455 err
= TrySimpleReplace (btreePtr
, nodeRec
.buffer
, iterator
, record
, recordLen
, &recordFit
);
1456 M_ExitOnError (err
);
1460 err
= UpdateNode (btreePtr
, &nodeRec
, 0, 0);
1461 M_ExitOnError (err
);
1467 //////////////////////////// Make Some Room /////////////////////////////////
1469 nodesNeeded
= btreePtr
->treeDepth
+ 1 - btreePtr
->freeNodes
; //\80\80 math limit
1470 if (nodesNeeded
> 0)
1472 nodesNeeded
+= btreePtr
->totalNodes
;
1473 if (nodesNeeded
> CalcMapBits (btreePtr
)) // we'll need to add a map node too!
1476 err
= ExtendBTree (btreePtr
, nodesNeeded
);
1477 M_ExitOnError (err
);
1482 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1484 DeleteRecord (btreePtr
, nodeRec
.buffer
, index
); // delete existing key/record
1486 err
= InsertTree (btreePtr
, treePathTable
, &iterator
->key
, record
->bufferAddress
,
1487 recordLen
, &nodeRec
, index
, 1, kReplaceRecord
, &insertNodeNum
);
1488 M_ExitOnError (err
);
1490 ++btreePtr
->writeCount
; /* writeCount changes only if the tree structure changed */
1494 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
1495 iterator
->hint
.nodeNum
= insertNodeNum
;
1496 iterator
->hint
.index
= 0; // unused
1497 iterator
->hint
.reserved1
= 0;
1498 iterator
->hint
.reserved2
= 0;
1503 ////////////////////////////// Error Exit ///////////////////////////////////
1507 (void) ReleaseNode (btreePtr
, &nodeRec
);
1509 iterator
->hint
.writeCount
= 0;
1510 iterator
->hint
.nodeNum
= 0;
1511 iterator
->hint
.index
= 0;
1512 iterator
->hint
.reserved1
= 0;
1513 iterator
->hint
.reserved2
= 0;
1520 //////////////////////////////// BTUpdateRecord ////////////////////////////////
1523 BTUpdateRecord(FCB
*filePtr
, BTreeIterator
*iterator
,
1524 IterateCallBackProcPtr callBackProc
, void * callBackState
)
1527 BTreeControlBlockPtr btreePtr
;
1528 TreePathTable treePathTable
;
1529 BlockDescriptor nodeRec
;
1530 RecordPtr recordPtr
;
1532 UInt32 insertNodeNum
;
1538 ////////////////////////// Priliminary Checks ///////////////////////////////
1540 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
1541 nodeRec
.blockHeader
= nil
;
1543 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1545 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1547 ////////////////////////////// Take A Hint //////////////////////////////////
1549 err
= IsItAHint (btreePtr
, iterator
, &validHint
);
1550 M_ExitOnError (err
);
1554 insertNodeNum
= iterator
->hint
.nodeNum
;
1556 err
= GetNode (btreePtr
, insertNodeNum
, &nodeRec
);
1559 if (((NodeDescPtr
)nodeRec
.buffer
)->kind
== kBTLeafNode
&&
1560 SearchNode (btreePtr
, nodeRec
.buffer
, &iterator
->key
, &index
))
1562 err
= GetRecordByIndex(btreePtr
, nodeRec
.buffer
, index
, &keyPtr
, &recordPtr
, &recordLen
);
1563 M_ExitOnError (err
);
1566 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1568 err
= callBackProc(keyPtr
, recordPtr
, recordLen
, callBackState
);
1569 M_ExitOnError (err
);
1571 err
= UpdateNode (btreePtr
, &nodeRec
, 0, 0);
1572 M_ExitOnError (err
);
1574 ++btreePtr
->numValidHints
;
1580 (void) BTInvalidateHint( iterator
);
1583 err
= ReleaseNode (btreePtr
, &nodeRec
);
1584 M_ExitOnError (err
);
1588 (void) BTInvalidateHint( iterator
);
1592 ////////////////////////////// Get A Clue ///////////////////////////////////
1594 err
= SearchTree (btreePtr
, &iterator
->key
, treePathTable
, &insertNodeNum
, &nodeRec
, &index
);
1595 M_ExitOnError (err
);
1597 err
= GetRecordByIndex(btreePtr
, nodeRec
.buffer
, index
, &keyPtr
, &recordPtr
, &recordLen
);
1598 M_ExitOnError (err
);
1601 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1603 err
= callBackProc(keyPtr
, recordPtr
, recordLen
, callBackState
);
1604 M_ExitOnError (err
);
1606 err
= UpdateNode (btreePtr
, &nodeRec
, 0, 0);
1607 M_ExitOnError (err
);
1611 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
1612 iterator
->hint
.nodeNum
= insertNodeNum
;
1613 iterator
->hint
.index
= 0;
1614 iterator
->hint
.reserved1
= 0;
1615 iterator
->hint
.reserved2
= 0;
1618 ////////////////////////////// Error Exit ///////////////////////////////////
1622 (void) ReleaseNode (btreePtr
, &nodeRec
);
1624 iterator
->hint
.writeCount
= 0;
1625 iterator
->hint
.nodeNum
= 0;
1626 iterator
->hint
.index
= 0;
1627 iterator
->hint
.reserved1
= 0;
1628 iterator
->hint
.reserved2
= 0;
1634 //////////////////////////////// BTDeleteRecord /////////////////////////////////
1636 OSStatus
BTDeleteRecord (FCB
*filePtr
,
1637 BTreeIterator
*iterator
)
1640 BTreeControlBlockPtr btreePtr
;
1641 TreePathTable treePathTable
;
1642 BlockDescriptor nodeRec
;
1647 ////////////////////////// Priliminary Checks ///////////////////////////////
1649 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
1650 nodeRec
.blockHeader
= nil
;
1652 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1653 M_ReturnErrorIf (iterator
== nil
, paramErr
);
1655 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1656 if (btreePtr
== nil
)
1658 err
= fsBTInvalidFileErr
;
1662 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1665 /////////////////////////////// Find Key ////////////////////////////////////
1667 //\80\80 check hint for simple delete case (index > 0, numRecords > 2)
1669 err
= SearchTree (btreePtr
, &iterator
->key
, treePathTable
, &nodeNum
, &nodeRec
, &index
);
1670 M_ExitOnError (err
); // record must exit for Delete
1673 ///////////////////////////// Delete Record /////////////////////////////////
1675 err
= DeleteTree (btreePtr
, treePathTable
, &nodeRec
, index
, 1);
1676 M_ExitOnError (err
);
1678 ++btreePtr
->writeCount
;
1679 --btreePtr
->leafRecords
;
1680 M_BTreeHeaderDirty (btreePtr
);
1682 iterator
->hint
.nodeNum
= 0;
1686 ////////////////////////////// Error Exit ///////////////////////////////////
1689 (void) ReleaseNode (btreePtr
, &nodeRec
);
1696 OSStatus
BTGetInformation (FCB
*filePtr
,
1698 BTreeInfoRec
*info
)
1700 #pragma unused (version)
1702 BTreeControlBlockPtr btreePtr
;
1705 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1707 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1711 * This should not require the whole tree to be locked, just maybe the BTreeControlBlockPtr
1713 * REQUIRE_FILE_LOCK(btreePtr->fileRefNum, true);
1716 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1717 M_ReturnErrorIf (info
== nil
, paramErr
);
1719 //\80\80 check version?
1721 info
->nodeSize
= btreePtr
->nodeSize
;
1722 info
->maxKeyLength
= btreePtr
->maxKeyLength
;
1723 info
->treeDepth
= btreePtr
->treeDepth
;
1724 info
->numRecords
= btreePtr
->leafRecords
;
1725 info
->numNodes
= btreePtr
->totalNodes
;
1726 info
->numFreeNodes
= btreePtr
->freeNodes
;
1727 info
->lastfsync
= btreePtr
->lastfsync
;
1736 BTIsDirty(FCB
*filePtr
)
1738 BTreeControlBlockPtr btreePtr
;
1740 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1741 return TreeIsDirty(btreePtr
);
1744 /*-------------------------------------------------------------------------------
1745 Routine: BTFlushPath - Flush BTreeControlBlock to Header Node.
1747 Function: Brief_description_of_the_function_and_any_side_effects
1750 Input: pathPtr - pointer to path control block for B*Tree file to flush
1754 Result: noErr - success
1756 -------------------------------------------------------------------------------*/
1758 OSStatus
BTFlushPath (FCB
*filePtr
)
1761 BTreeControlBlockPtr btreePtr
;
1764 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1766 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1768 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1770 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1772 err
= UpdateHeader (btreePtr
, false);
1778 /*-------------------------------------------------------------------------------
1779 Routine: BTReload - Reload B-tree Header Data.
1781 Function: Reload B-tree header data from disk. This is called after fsck
1782 has made repairs to the root filesystem. The filesystem is
1783 mounted read-only when BTReload is caled.
1786 Input: filePtr - the B*Tree file that needs its header updated
1790 Result: noErr - success
1792 -------------------------------------------------------------------------------*/
1795 BTReloadData(FCB
*filePtr
)
1798 BTreeControlBlockPtr btreePtr
;
1799 BlockDescriptor node
;
1800 BTHeaderRec
*header
;
1804 node
.blockHeader
= nil
;
1806 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1807 if (btreePtr
== nil
)
1808 return (fsBTInvalidFileErr
);
1810 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1812 err
= GetNode(btreePtr
, kHeaderNodeNum
, &node
);
1816 header
= (BTHeaderRec
*)((char *)node
.buffer
+ sizeof(BTNodeDescriptor
));
1817 if ((err
= VerifyHeader (filePtr
, header
)) == 0) {
1818 btreePtr
->treeDepth
= header
->treeDepth
;
1819 btreePtr
->rootNode
= header
->rootNode
;
1820 btreePtr
->leafRecords
= header
->leafRecords
;
1821 btreePtr
->firstLeafNode
= header
->firstLeafNode
;
1822 btreePtr
->lastLeafNode
= header
->lastLeafNode
;
1823 btreePtr
->maxKeyLength
= header
->maxKeyLength
;
1824 btreePtr
->totalNodes
= header
->totalNodes
;
1825 btreePtr
->freeNodes
= header
->freeNodes
;
1826 btreePtr
->btreeType
= header
->btreeType
;
1828 btreePtr
->flags
&= (~kBTHeaderDirty
);
1831 (void) ReleaseNode(btreePtr
, &node
);
1837 /*-------------------------------------------------------------------------------
1838 Routine: BTInvalidateHint - Invalidates the hint within a BTreeInterator.
1840 Function: Invalidates the hint within a BTreeInterator.
1843 Input: iterator - pointer to BTreeIterator
1845 Output: iterator - iterator with the hint.nodeNum cleared
1847 Result: noErr - success
1848 paramErr - iterator == nil
1849 -------------------------------------------------------------------------------*/
1852 OSStatus
BTInvalidateHint (BTreeIterator
*iterator
)
1854 if (iterator
== nil
)
1857 iterator
->hint
.nodeNum
= 0;
1865 /*-------------------------------------------------------------------------------
1866 Routine: BTGetLastSync
1868 Function: Returns the last time that this btree was flushed, does not include header.
1870 Input: filePtr - pointer file control block
1872 Output: lastfsync - time in seconds of last update
1874 Result: noErr - success
1875 paramErr - iterator == nil
1876 -------------------------------------------------------------------------------*/
1879 OSStatus
BTGetLastSync (FCB
*filePtr
,
1882 BTreeControlBlockPtr btreePtr
;
1885 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1887 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1889 /* Maybe instead of requiring a lock..an atomic set might be more appropriate */
1890 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
1892 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1893 M_ReturnErrorIf (lastsync
== nil
, paramErr
);
1895 *lastsync
= btreePtr
->lastfsync
;
1903 /*-------------------------------------------------------------------------------
1904 Routine: BTSetLastSync
1906 Function: Sets the last time that this btree was flushed, does not include header.
1909 Input: fcb - pointer file control block
1911 Output: lastfsync - time in seconds of last update
1913 Result: noErr - success
1914 paramErr - iterator == nil
1915 -------------------------------------------------------------------------------*/
1918 OSStatus
BTSetLastSync (FCB
*filePtr
,
1921 BTreeControlBlockPtr btreePtr
;
1924 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1926 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1928 /* Maybe instead of requiring a lock..an atomic set might be more appropriate */
1929 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
1931 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1932 M_ReturnErrorIf (lastsync
== nil
, paramErr
);
1934 btreePtr
->lastfsync
= lastsync
;
1940 /*-------------------------------------------------------------------------------
1941 Routine: BTCheckFreeSpace
1943 Function: Makes sure there is enough free space so that a tree operation
1946 Input: fcb - pointer file control block
1950 Result: noErr - success
1952 -------------------------------------------------------------------------------*/
1955 OSStatus
BTCheckFreeSpace (FCB
*filePtr
)
1957 BTreeControlBlockPtr btreePtr
;
1958 int nodesNeeded
, err
= noErr
;
1961 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1963 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1965 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
1967 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1969 // XXXdbg this is highly conservative but so much better than
1970 // winding up with turds on your disk.
1972 nodesNeeded
= (btreePtr
->treeDepth
+ 1) * 10;
1974 if (btreePtr
->freeNodes
< nodesNeeded
) {
1975 err
= ExtendBTree(btreePtr
, nodesNeeded
+ btreePtr
->totalNodes
- btreePtr
->freeNodes
);
1983 OSStatus
BTHasContiguousNodes (FCB
*filePtr
)
1985 BTreeControlBlockPtr btreePtr
;
1986 int nodesNeeded
, err
= noErr
;
1989 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1991 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1993 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
1995 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1997 return NodesAreContiguous(FCBTOVCB(filePtr
), filePtr
, btreePtr
->nodeSize
);