2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
28 Contains: Implementation of public interface routines for B-tree manager.
32 Written by: Gordon Sheridan and Bill Bruffey
34 Copyright: © 1992-1999 by Apple Computer, Inc., all rights reserved.
40 Other Contact: Mark Day
42 Technology: File Systems
50 Change History (most recent first):
51 <MOSXS> 9/22/99 ser Added routines BTGetLastSync and BTSetLastSync
52 <MOSXS> 6/1/99 djb Sync up with Mac OS 8.6.
53 <MOSXS> 6/30/98 djb In BTOpenPath make sure nodes are contiguous on disk (radar #2249539).
54 <MOSXS> 4/15/98 djb In BTOpenPath need to clear nodeRec.buffer if GetBlockProc fails.
55 <MOSXS> 4/11/98 djb Add RequireFileLock checking to all external entry points.
57 <MOSXS> 03/23/98 djb In BTOpenPath use kTrashBlock option when releasing the header so
58 that we get a full node when we call GetNode.
60 <CS9> 12/12/97 djb Radar #2202682, BTIterateRecord with kBTreeCurrentRecord was not
61 checking if we had a record and could call BlockMove with an
62 uninitialize source pointer (causing a bus error).
63 <CS8> 10/24/97 msd In BTIterateRecord, when moving to the previous or next record
64 and we have to move to another node, see if we need to release
65 the node about to be "shifted out" (opposite sibling of the
66 direction we need to move).
67 <CS7> 7/25/97 DSH BTSearchRecord now takes a heuristicHint, nodeNum, and tries it
68 before calling SearchBTree
69 <CS6> 7/24/97 djb GetBlockProc now take a file refnum instead of an FCB ptr.
70 <CS5> 7/22/97 djb Move trace points from BTreeWrapper.c to here.
71 <CS4> 7/21/97 djb LogEndTime now takes an error code.
72 <CS3> 7/16/97 DSH FilesInternal.i renamed FileMgrInternal.i to avoid name
74 <CS2> 5/19/97 djb Add summary traces to BTIterateRecord.
75 <CS1> 4/23/97 djb first checked in
77 <HFS7> 2/19/97 djb Enable variable sized index keys for HFS+ volumes. Added node
78 cache to support nodes larger than 512 bytes.
79 <HFS6> 1/27/97 djb Calls to InsertTree and DeleteTree are now recursive (to support
80 variable sized index keys).
81 <HFS5> 1/13/97 djb Added support for getting current record to BTIterateRecord.
82 <HFS4> 1/6/97 djb Initialize "BigKeys" attribute in BTOpen.
83 <HFS3> 1/3/97 djb Added support for large keys.
84 <HFS2> 12/23/96 djb On exit map fsBTEmptyErr and fsBTEndOfIterationErr to
85 fsBTRecordNotFoundErr.
86 <HFS1> 12/19/96 djb first checked in
88 History applicable to original Scarecrow Design:
90 <13> 10/25/96 ser Changing for new VFPI
91 <12> 10/18/96 ser Converting over VFPI changes
92 <11> 9/17/96 dkh More BTree statistics. Modified hint checks to not bail out when
93 an error is returned from GetNode.
94 <10> 9/16/96 dkh Revised BTree statistics.
95 <9> 8/23/96 dkh Remove checks for multiple paths to BTree file. Need to add
96 equivalent mechanism later.
97 <8> 6/20/96 dkh Radar #1358740. Switch from using Pools to debug MemAllocators.
98 <7> 3/14/96 jev Fix BTreeSetRecord, recordFound was not set for the case of a
99 simple replace causing the leafRecords count to get bumped even
100 though we didn't have to add a record.
101 <6> 3/1/96 prp Fix lint problems. Bug in BTSetRecord that does not initialize
103 <5> 1/22/96 dkh Add #include Memory.h
104 <4> 1/10/96 msd Use the real function names from Math64.i.
105 <3> 1/4/96 jev Fix BTItererateRecord for the condition when the iterator
106 position routine does not find the record and we are looking for
107 the next record. In such a case, if the node's forrward link is
108 non-zero, we have to keep iterating next and not return
109 fsBTEndOfIterationErr error.
110 <2> 12/7/95 dkh D10E2 build. Changed usage of Ref data type to LogicalAddress.
111 <1> 10/18/95 rst Moved from Scarecrow project.
113 <24> 7/18/95 mbb Change MoveData & ClearBytes to BlockMoveData & BlockZero.
114 <23> 1/31/95 prp GetBlockProc interface uses a 64 bit node number.
115 <22> 1/12/95 wjk Adopt Model FileSystem changes in D5.
116 <21> 11/16/94 prp Add IsItAHint routine and use it whenever hint's node number was
118 <20> 11/10/94 prp BTGetInfo name collides with the same name in FileManagerPriv.i.
119 Change it to BTGetInformation.
120 <19> 9/30/94 prp Get in sync with D2 interface changes.
121 <18> 7/22/94 wjk Convert to the new set of header files.
122 <17> 12/9/93 wjk Cleanup usage of char, Byte, int8, UInt8, etc.
123 <16> 12/2/93 wjk Move from Makefiles to BuildFiles. Fit into the ModernOS and
125 <15> 11/30/93 wjk Move from Makefiles to BuildFiles. Fit into the ModernOS and
127 <14> 9/30/93 gs Rename E_NoGetNodeProc and E_NoReleaseNodeProc to
129 <13> 8/31/93 prp Use Set64U instead of Set64.
130 <12> 8/16/93 prp In BTSearchRecord, if the input hint found the node and record,
131 set the local nodeNum variable correctly so that the resultant
132 iterator gets set correctly.
133 <11> 7/1/93 gs Fix bug in BTIterateRecord related to kBTreePrevRecord
135 <10> 6/2/93 gs Update for changes to FSErrors.h and add some comments.
136 <9> 5/24/93 gs Fix bug in BTInsert/Set/ReplaceRecord which didn't set node hint
137 properly in some cases.
138 <8> 5/24/93 gs Do NOT map fsBTEmptyErr to fsBTRecordNotFoundErr in BTSearchRecord.
139 <7> 5/24/93 gs Rename BTFlush to BTFlushPath.
140 <6> 5/21/93 gs Add hint optimization to Set/Replace routines.
141 <5> 5/10/93 gs Remove Panic from BTInitialize for small logicalEOF. Implement
142 Insert, Set, Replace, and Delete.
143 <4> 3/23/93 gs Finish BTInitialize.
144 <3> 2/8/93 gs Implement BTSearchRecord and BTIterateRecord.
145 <2> 12/8/92 gs Implement Open and Close routines.
146 <1> 11/15/92 gs first checked in
150 #include "../headers/BTreesPrivate.h"
153 * The amount that the BTree header leaf count can be wrong before we assume
154 * it is in an infinite loop.
156 #define kNumLeafRecSlack 10
158 //////////////////////////////////// Globals ////////////////////////////////////
161 /////////////////////////// BTree Module Entry Points ///////////////////////////
165 /*-------------------------------------------------------------------------------
166 Routine: BTOpenPath - Open a file for access as a B*Tree.
168 Function: Create BTree control block for a file, if necessary. Validates the
169 file to be sure it looks like a BTree file.
172 Input: filePtr - pointer to file to open as a B-tree
173 keyCompareProc - pointer to client's KeyCompare function
174 getBlockProc - pointer to client's GetBlock function
175 releaseBlockProc - pointer to client's ReleaseBlock function
176 setEndOfForkProc - pointer to client's SetEOF function
178 Result: noErr - success
179 paramErr - required ptr was nil
183 -------------------------------------------------------------------------------*/
185 OSStatus
BTOpenPath (FCB
*filePtr
,
186 KeyCompareProcPtr keyCompareProc
,
187 GetBlockProcPtr getBlockProc
,
188 ReleaseBlockProcPtr releaseBlockProc
,
189 SetEndOfForkProcPtr setEndOfForkProc
,
190 SetBlockSizeProcPtr setBlockSizeProc
)
193 BTreeControlBlockPtr btreePtr
;
197 ////////////////////// Preliminary Error Checking ///////////////////////////
199 if ( filePtr
== nil
||
200 getBlockProc
== nil
||
201 releaseBlockProc
== nil
||
202 setEndOfForkProc
== nil
||
203 setBlockSizeProc
== nil
)
208 if ( filePtr
->fcbBTCBPtr
!= nil
) // already has a BTreeCB
211 // is file large enough to contain header node?
212 if ( filePtr
->fcbEOF
< kMinNodeSize
)
213 return fsBTInvalidFileErr
; //\80\80 or E_BadHeader?
216 //////////////////////// Allocate Control Block /////////////////////////////
218 btreePtr
= (BTreeControlBlock
*) NewPtrSysClear( sizeof( BTreeControlBlock
) );
221 Panic ("\pBTOpen: no memory for btreePtr.");
225 btreePtr
->getBlockProc
= getBlockProc
;
226 btreePtr
->releaseBlockProc
= releaseBlockProc
;
227 btreePtr
->setEndOfForkProc
= setEndOfForkProc
;
228 btreePtr
->keyCompareProc
= keyCompareProc
;
230 /////////////////////////// Read Header Node ////////////////////////////////
232 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
233 btreePtr
->fileRefNum
= GetFileRefNumFromFCB(filePtr
);
234 filePtr
->fcbBTCBPtr
= (Ptr
) btreePtr
; // attach btree cb to file
236 /* The minimum node size is the physical block size */
237 nodeRec
.blockSize
= VTOHFS(btreePtr
->fileRefNum
)->hfs_phys_block_size
;
239 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
241 // it is now safe to call M_ExitOnError (err)
243 err
= setBlockSizeProc (btreePtr
->fileRefNum
, nodeRec
.blockSize
, 1);
247 err
= getBlockProc (btreePtr
->fileRefNum
,
253 nodeRec
.buffer
= nil
;
254 nodeRec
.blockHeader
= nil
;
255 Panic("\pBTOpen: getNodeProc returned error getting header node.");
258 ++btreePtr
->numGetNodes
;
259 header
= (BTHeaderRec
*) ((u_long
)nodeRec
.buffer
+ sizeof(BTNodeDescriptor
));
262 ///////////////////////////// verify header /////////////////////////////////
264 err
= VerifyHeader (filePtr
, header
);
268 ///////////////////// Initalize fields from header //////////////////////////
270 PanicIf ( (FCBTOVCB(filePtr
)->vcbSigWord
!= 0x4244) && (header
->nodeSize
== 512), "\p BTOpenPath: wrong node size for HFS+ volume!"); // 0x4244 = 'BD'
272 btreePtr
->treeDepth
= header
->treeDepth
;
273 btreePtr
->rootNode
= header
->rootNode
;
274 btreePtr
->leafRecords
= header
->leafRecords
;
275 btreePtr
->firstLeafNode
= header
->firstLeafNode
;
276 btreePtr
->lastLeafNode
= header
->lastLeafNode
;
277 btreePtr
->nodeSize
= header
->nodeSize
;
278 btreePtr
->maxKeyLength
= header
->maxKeyLength
;
279 btreePtr
->totalNodes
= header
->totalNodes
;
280 btreePtr
->freeNodes
= header
->freeNodes
;
281 // ignore header->clumpSize; //\80\80 rename this field?
282 btreePtr
->btreeType
= header
->btreeType
;
284 btreePtr
->attributes
= header
->attributes
;
286 if ( btreePtr
->maxKeyLength
> 40 )
287 btreePtr
->attributes
|= (kBTBigKeysMask
+ kBTVariableIndexKeysMask
); //\80\80 we need a way to save these attributes
289 /////////////////////// Initialize dynamic fields ///////////////////////////
291 btreePtr
->version
= kBTreeVersion
;
293 btreePtr
->writeCount
= 1;
295 /////////////////////////// Check Header Node ///////////////////////////////
297 // set kBadClose attribute bit, and UpdateNode
299 /* b-tree node size must be at least as big as the physical block size */
300 if (btreePtr
->nodeSize
< nodeRec
.blockSize
)
303 * If this tree has any records or the media is writeable then
304 * we cannot mount using the current physical block size.
306 if (btreePtr
->leafRecords
> 0 ||
307 VTOHFS(btreePtr
->fileRefNum
)->hfs_media_writeable
)
309 err
= fsBTBadNodeSize
;
314 // if nodeSize Matches then we don't need to release, just CheckNode
315 if ( btreePtr
->nodeSize
== nodeRec
.blockSize
)
317 err
= CheckNode (btreePtr
, nodeRec
.buffer
);
319 VTOVCB(btreePtr
->fileRefNum
)->vcbFlags
|= kHFS_DamagedVolume
;
324 err
= setBlockSizeProc (btreePtr
->fileRefNum
, btreePtr
->nodeSize
, 32); //\80\80 we should try and get this down to 8
328 * Need to use kTrashBlock option to force the
329 * buffer cache to read the entire node
331 err
= releaseBlockProc(btreePtr
->fileRefNum
, &nodeRec
, kTrashBlock
);
332 ++btreePtr
->numReleaseNodes
;
335 err
= GetNode (btreePtr
, kHeaderNodeNum
, &nodeRec
); // calls CheckNode...
339 //\80\80 total nodes * node size <= LEOF?
342 err
= ReleaseNode (btreePtr
, &nodeRec
);
346 * Under Mac OS, b-tree nodes can be non-contiguous on disk when the
347 * allocation block size is smaller than the b-tree node size.
349 * If journaling is turned on for this volume we can't deal with this
350 * situation and so we bail out. If journaling isn't on it's ok as
351 * hfs_strategy_fragmented() deals with it. Journaling can't support
352 * this because it assumes that if you give it a block that it's
353 * contiguous on disk.
355 if ( FCBTOHFS(filePtr
)->jnl
&& !NodesAreContiguous(FCBTOVCB(filePtr
), filePtr
, btreePtr
->nodeSize
) ) {
356 return fsBTInvalidNodeErr
;
359 //////////////////////////////// Success ////////////////////////////////////
361 //\80\80 align LEOF to multiple of node size? - just on close
366 /////////////////////// Error - Clean up and Exit ///////////////////////////
370 filePtr
->fcbBTCBPtr
= nil
;
371 (void) ReleaseNode (btreePtr
, &nodeRec
);
372 DisposePtr( (Ptr
) btreePtr
);
379 /*-------------------------------------------------------------------------------
380 Routine: BTClosePath - Flush BTree Header and Deallocate Memory for BTree.
382 Function: Flush the BTreeControlBlock fields to header node, and delete BTree control
383 block and key descriptor associated with the file if filePtr is last
384 path of type kBTreeType ('btre').
387 Input: filePtr - pointer to file to delete BTree control block for.
389 Result: noErr - success
392 -------------------------------------------------------------------------------*/
394 OSStatus
BTClosePath (FCB
*filePtr
)
397 BTreeControlBlockPtr btreePtr
;
399 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
402 return fsBTInvalidFileErr
;
404 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
406 ////////////////////// Check for other BTree Paths //////////////////////////
408 btreePtr
->attributes
&= ~kBTBadCloseMask
; // clear "bad close" attribute bit
409 err
= UpdateHeader (btreePtr
, true);
412 DisposePtr( (Ptr
) btreePtr
);
413 filePtr
->fcbBTCBPtr
= nil
;
417 /////////////////////// Error - Clean Up and Exit ///////////////////////////
426 /*-------------------------------------------------------------------------------
427 Routine: BTSearchRecord - Search BTree for a record with a matching key.
429 Function: Search for position in B*Tree indicated by searchKey. If a valid node hint
430 is provided, it will be searched first, then SearchTree will be called.
431 If a BTreeIterator is provided, it will be set to the position found as
432 a result of the search. If a record exists at that position, and a BufferDescriptor
433 is supplied, the record will be copied to the buffer (as much as will fit),
434 and recordLen will be set to the length of the record.
436 If an error other than fsBTRecordNotFoundErr occurs, the BTreeIterator, if any,
437 is invalidated, and recordLen is set to 0.
440 Input: pathPtr - pointer to path for BTree file.
441 searchKey - pointer to search key to match.
442 hintPtr - pointer to hint (may be nil)
444 Output: record - pointer to BufferDescriptor containing record
445 recordLen - length of data at recordPtr
446 iterator - pointer to BTreeIterator indicating position result of search
448 Result: noErr - success, record contains copy of record found
449 fsBTRecordNotFoundErr - record was not found, no data copied
450 fsBTInvalidFileErr - no BTreeControlBlock is allocated for the fork
451 fsBTInvalidKeyLengthErr -
453 -------------------------------------------------------------------------------*/
455 OSStatus
BTSearchRecord (FCB
*filePtr
,
456 BTreeIterator
*searchIterator
,
457 FSBufferDescriptor
*record
,
459 BTreeIterator
*resultIterator
)
462 BTreeControlBlockPtr btreePtr
;
463 TreePathTable treePathTable
;
465 BlockDescriptor node
;
473 if (filePtr
== nil
) return paramErr
;
474 if (searchIterator
== nil
) return paramErr
;
477 node
.blockHeader
= nil
;
479 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
480 if (btreePtr
== nil
) return fsBTInvalidFileErr
;
482 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
486 ////////////////////////////// Take A Hint //////////////////////////////////
488 err
= IsItAHint (btreePtr
, searchIterator
, &validHint
);
493 nodeNum
= searchIterator
->hint
.nodeNum
;
495 err
= GetNode (btreePtr
, nodeNum
, &node
);
498 if ( ((BTNodeDescriptor
*) node
.buffer
)->kind
== kBTLeafNode
&&
499 ((BTNodeDescriptor
*) node
.buffer
)->numRecords
> 0 )
501 foundRecord
= SearchNode (btreePtr
, node
.buffer
, &searchIterator
->key
, &index
);
503 //\80\80 if !foundRecord, we could still skip tree search if ( 0 < index < numRecords )
506 if (foundRecord
== false)
508 err
= ReleaseNode (btreePtr
, &node
);
513 ++btreePtr
->numValidHints
;
517 if( foundRecord
== false )
518 (void) BTInvalidateHint( searchIterator
);
522 //////////////////////////// Search The Tree ////////////////////////////////
524 if (foundRecord
== false)
526 err
= SearchTree ( btreePtr
, &searchIterator
->key
, treePathTable
, &nodeNum
, &node
, &index
);
529 case noErr
: foundRecord
= true; break;
530 case fsBTRecordNotFoundErr
: break;
531 default: goto ErrorExit
;
536 //////////////////////////// Get the Record /////////////////////////////////
538 if (foundRecord
== true)
540 //XXX Should check for errors! Or BlockMove could choke on recordPtr!!!
541 GetRecordByIndex (btreePtr
, node
.buffer
, index
, &keyPtr
, &recordPtr
, &len
);
543 if (recordLen
!= nil
) *recordLen
= len
;
547 ByteCount recordSize
;
549 recordSize
= record
->itemCount
* record
->itemSize
;
551 if (len
> recordSize
) len
= recordSize
;
553 BlockMoveData (recordPtr
, record
->bufferAddress
, len
);
558 /////////////////////// Success - Update Iterator ///////////////////////////
560 if (resultIterator
!= nil
)
562 resultIterator
->hint
.writeCount
= btreePtr
->writeCount
;
563 resultIterator
->hint
.nodeNum
= nodeNum
;
564 resultIterator
->hint
.index
= index
;
566 resultIterator
->hint
.reserved1
= 0;
567 resultIterator
->hint
.reserved2
= 0;
568 resultIterator
->version
= 0;
569 resultIterator
->reserved
= 0;
571 // copy the key in the BTree when found rather than searchIterator->key to get proper case/diacriticals
572 if (foundRecord
== true)
573 BlockMoveData ((Ptr
)keyPtr
, (Ptr
)&resultIterator
->key
, CalcKeySize(btreePtr
, keyPtr
));
575 BlockMoveData ((Ptr
)&searchIterator
->key
, (Ptr
)&resultIterator
->key
, CalcKeySize(btreePtr
, &searchIterator
->key
));
578 err
= ReleaseNode (btreePtr
, &node
);
581 if (foundRecord
== false) return fsBTRecordNotFoundErr
;
585 /////////////////////// Error - Clean Up and Exit ///////////////////////////
589 if (recordLen
!= nil
)
592 if (resultIterator
!= nil
)
594 resultIterator
->hint
.writeCount
= 0;
595 resultIterator
->hint
.nodeNum
= 0;
596 resultIterator
->hint
.index
= 0;
597 resultIterator
->hint
.reserved1
= 0;
598 resultIterator
->hint
.reserved2
= 0;
600 resultIterator
->version
= 0;
601 resultIterator
->reserved
= 0;
602 resultIterator
->key
.length16
= 0; // zero out two bytes to cover both types of keys
605 if ( err
== fsBTEmptyErr
)
606 err
= fsBTRecordNotFoundErr
;
613 /*-------------------------------------------------------------------------------
614 Routine: BTIterateRecord - Find the first, next, previous, or last record.
616 Function: Find the first, next, previous, or last record in the BTree
618 Input: pathPtr - pointer to path iterate records for.
619 operation - iteration operation (first,next,prev,last)
620 iterator - pointer to iterator indicating start position
622 Output: iterator - iterator is updated to indicate new position
623 newKeyPtr - pointer to buffer to copy key found by iteration
624 record - pointer to buffer to copy record found by iteration
625 recordLen - length of record
627 Result: noErr - success
629 -------------------------------------------------------------------------------*/
631 OSStatus
BTIterateRecord (FCB
*filePtr
,
632 BTreeIterationOperation operation
,
633 BTreeIterator
*iterator
,
634 FSBufferDescriptor
*record
,
638 BTreeControlBlockPtr btreePtr
;
646 BlockDescriptor left
, node
, right
;
650 ////////////////////////// Priliminary Checks ///////////////////////////////
653 left
.blockHeader
= nil
;
655 right
.blockHeader
= nil
;
657 node
.blockHeader
= nil
;
665 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
668 return fsBTInvalidFileErr
; //\80\80 handle properly
671 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
673 if ((operation
!= kBTreeFirstRecord
) &&
674 (operation
!= kBTreeNextRecord
) &&
675 (operation
!= kBTreeCurrentRecord
) &&
676 (operation
!= kBTreePrevRecord
) &&
677 (operation
!= kBTreeLastRecord
))
679 err
= fsInvalidIterationMovmentErr
;
683 /////////////////////// Find First or Last Record ///////////////////////////
685 if ((operation
== kBTreeFirstRecord
) || (operation
== kBTreeLastRecord
))
687 if (operation
== kBTreeFirstRecord
) nodeNum
= btreePtr
->firstLeafNode
;
688 else nodeNum
= btreePtr
->lastLeafNode
;
696 err
= GetNode (btreePtr
, nodeNum
, &node
);
699 if ( ((NodeDescPtr
) node
.buffer
)->kind
!= kBTLeafNode
||
700 ((NodeDescPtr
) node
.buffer
)->numRecords
<= 0 )
702 err
= ReleaseNode (btreePtr
, &node
);
705 err
= fsBTInvalidNodeErr
;
706 MARK_VOLUMEDAMAGED(filePtr
);
710 if (operation
== kBTreeFirstRecord
) index
= 0;
711 else index
= ((BTNodeDescriptor
*) node
.buffer
)->numRecords
- 1;
713 goto CopyData
; //\80\80 is there a cleaner way?
717 //////////////////////// Find Iterator Position /////////////////////////////
719 err
= FindIteratorPosition (btreePtr
, iterator
,
720 &left
, &node
, &right
, &nodeNum
, &index
, &foundRecord
);
724 ///////////////////// Find Next Or Previous Record //////////////////////////
726 if (operation
== kBTreePrevRecord
)
734 if (left
.buffer
== nil
)
736 nodeNum
= ((NodeDescPtr
) node
.buffer
)->bLink
;
739 err
= GetNode (btreePtr
, nodeNum
, &left
);
742 err
= fsBTStartOfIterationErr
;
746 // Before we stomp on "right", we'd better release it if needed
747 if (right
.buffer
!= nil
) {
748 err
= ReleaseNode(btreePtr
, &right
);
754 index
= ((NodeDescPtr
) node
.buffer
)->numRecords
-1;
757 else if (operation
== kBTreeNextRecord
)
759 if ((foundRecord
!= true) &&
760 (((NodeDescPtr
) node
.buffer
)->fLink
== 0) &&
761 (index
== ((NodeDescPtr
) node
.buffer
)->numRecords
))
763 err
= fsBTEndOfIterationErr
;
767 // we did not find the record but the index is already positioned correctly
768 if ((foundRecord
== false) && (index
!= ((NodeDescPtr
) node
.buffer
)->numRecords
))
771 // we found the record OR we have to look in the next node
772 if (index
< ((NodeDescPtr
) node
.buffer
)->numRecords
-1)
778 if (right
.buffer
== nil
)
780 nodeNum
= ((NodeDescPtr
) node
.buffer
)->fLink
;
783 err
= GetNode (btreePtr
, nodeNum
, &right
);
786 err
= fsBTEndOfIterationErr
;
790 // Before we stomp on "left", we'd better release it if needed
791 if (left
.buffer
!= nil
) {
792 err
= ReleaseNode(btreePtr
, &left
);
801 else // operation == kBTreeCurrentRecord
803 // make sure we have something... <CS9>
804 if ((foundRecord
!= true) &&
805 (index
>= ((NodeDescPtr
) node
.buffer
)->numRecords
))
807 err
= fsBTEndOfIterationErr
;
812 //////////////////// Copy Record And Update Iterator ////////////////////////
816 // added check for errors <CS9>
817 err
= GetRecordByIndex (btreePtr
, node
.buffer
, index
, &keyPtr
, &recordPtr
, &len
);
820 if (recordLen
!= nil
)
825 ByteCount recordSize
;
827 recordSize
= record
->itemCount
* record
->itemSize
;
829 if (len
> recordSize
) len
= recordSize
;
831 BlockMoveData (recordPtr
, record
->bufferAddress
, len
);
834 if (iterator
!= nil
) // first & last do not require iterator
836 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
837 iterator
->hint
.nodeNum
= nodeNum
;
838 iterator
->hint
.index
= index
;
839 iterator
->hint
.reserved1
= 0;
840 iterator
->hint
.reserved2
= 0;
842 iterator
->version
= 0;
843 iterator
->reserved
= 0;
846 * Check for infinite loops by making sure we do not
847 * process more leaf records, than can possibly be (or the BTree header
848 * is seriously damaged)....a brute force method.
850 if ((operation
== kBTreeFirstRecord
) || (operation
== kBTreeLastRecord
))
851 iterator
->hitCount
= 1;
852 else if (operation
!= kBTreeCurrentRecord
)
853 iterator
->hitCount
+= 1;
854 /* Always use the highest max, in case the grows while iterating */
855 iterator
->maxLeafRecs
= max(btreePtr
->leafRecords
, iterator
->maxLeafRecs
);
858 if (iterator
->hitCount
> iterator
->maxLeafRecs
+ kNumLeafRecSlack
)
860 err
= fsBTInvalidNodeErr
;
861 MARK_VOLUMEDAMAGED(filePtr
);
866 BlockMoveData ((Ptr
)keyPtr
, (Ptr
)&iterator
->key
, CalcKeySize(btreePtr
, keyPtr
));
870 ///////////////////////////// Release Nodes /////////////////////////////////
872 err
= ReleaseNode (btreePtr
, &node
);
875 if (left
.buffer
!= nil
)
877 err
= ReleaseNode (btreePtr
, &left
);
881 if (right
.buffer
!= nil
)
883 err
= ReleaseNode (btreePtr
, &right
);
889 /////////////////////// Error - Clean Up and Exit ///////////////////////////
893 (void) ReleaseNode (btreePtr
, &left
);
894 (void) ReleaseNode (btreePtr
, &node
);
895 (void) ReleaseNode (btreePtr
, &right
);
897 if (recordLen
!= nil
)
902 iterator
->hint
.writeCount
= 0;
903 iterator
->hint
.nodeNum
= 0;
904 iterator
->hint
.index
= 0;
905 iterator
->hint
.reserved1
= 0;
906 iterator
->hint
.reserved2
= 0;
908 iterator
->version
= 0;
909 iterator
->reserved
= 0;
910 iterator
->key
.length16
= 0;
913 if ( err
== fsBTEmptyErr
|| err
== fsBTEndOfIterationErr
)
914 err
= fsBTRecordNotFoundErr
;
920 /*-------------------------------------------------------------------------------
921 Routine: BTIterateRecords
923 Function: Find a series of records
925 Input: filePtr - b-tree file
926 operation - iteration operation (first,next,prev,last)
927 iterator - pointer to iterator indicating start position
928 callBackProc - pointer to routince to process a record
929 callBackState - pointer to state data (used by callBackProc)
931 Output: iterator - iterator is updated to indicate new position
933 Result: noErr - success
935 -------------------------------------------------------------------------------*/
938 BTIterateRecords(FCB
*filePtr
, BTreeIterationOperation operation
, BTreeIterator
*iterator
,
939 IterateCallBackProcPtr callBackProc
, void * callBackState
)
942 BTreeControlBlockPtr btreePtr
;
948 BlockDescriptor left
, node
, right
;
952 ////////////////////////// Priliminary Checks ///////////////////////////////
955 left
.blockHeader
= nil
;
957 right
.blockHeader
= nil
;
959 node
.blockHeader
= nil
;
961 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
963 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
965 if ((operation
!= kBTreeFirstRecord
) &&
966 (operation
!= kBTreeNextRecord
) &&
967 (operation
!= kBTreeCurrentRecord
) &&
968 (operation
!= kBTreePrevRecord
) &&
969 (operation
!= kBTreeLastRecord
))
971 err
= fsInvalidIterationMovmentErr
;
975 /////////////////////// Find First or Last Record ///////////////////////////
977 if ((operation
== kBTreeFirstRecord
) || (operation
== kBTreeLastRecord
))
979 if (operation
== kBTreeFirstRecord
)
980 nodeNum
= btreePtr
->firstLeafNode
;
982 nodeNum
= btreePtr
->lastLeafNode
;
990 err
= GetNode(btreePtr
, nodeNum
, &node
);
993 if ( ((NodeDescPtr
)node
.buffer
)->kind
!= kBTLeafNode
||
994 ((NodeDescPtr
)node
.buffer
)->numRecords
<= 0 )
996 err
= ReleaseNode(btreePtr
, &node
);
999 err
= fsBTInvalidNodeErr
;
1000 MARK_VOLUMEDAMAGED(filePtr
);
1004 if (operation
== kBTreeFirstRecord
)
1007 index
= ((BTNodeDescriptor
*) node
.buffer
)->numRecords
- 1;
1012 //////////////////////// Find Iterator Position /////////////////////////////
1014 err
= FindIteratorPosition(btreePtr
, iterator
, &left
, &node
, &right
,
1015 &nodeNum
, &index
, &foundRecord
);
1016 if (err
== fsBTRecordNotFoundErr
)
1021 ///////////////////// Find Next Or Previous Record //////////////////////////
1023 if (operation
== kBTreePrevRecord
)
1031 if (left
.buffer
== nil
)
1033 nodeNum
= ((NodeDescPtr
) node
.buffer
)->bLink
;
1036 err
= GetNode(btreePtr
, nodeNum
, &left
);
1039 err
= fsBTStartOfIterationErr
;
1043 // Before we stomp on "right", we'd better release it if needed
1044 if (right
.buffer
!= nil
) {
1045 err
= ReleaseNode(btreePtr
, &right
);
1051 index
= ((NodeDescPtr
) node
.buffer
)->numRecords
-1;
1054 else if (operation
== kBTreeNextRecord
)
1056 if ((foundRecord
!= true) &&
1057 (((NodeDescPtr
)node
.buffer
)->fLink
== 0) &&
1058 (index
== ((NodeDescPtr
)node
.buffer
)->numRecords
))
1060 err
= fsBTEndOfIterationErr
;
1064 // we did not find the record but the index is already positioned correctly
1065 if ((foundRecord
== false) && (index
!= ((NodeDescPtr
)node
.buffer
)->numRecords
))
1068 // we found the record OR we have to look in the next node
1069 if (index
< ((NodeDescPtr
)node
.buffer
)->numRecords
-1)
1075 if (right
.buffer
== nil
)
1077 nodeNum
= ((NodeDescPtr
)node
.buffer
)->fLink
;
1080 err
= GetNode(btreePtr
, nodeNum
, &right
);
1083 err
= fsBTEndOfIterationErr
;
1087 // Before we stomp on "left", we'd better release it if needed
1088 if (left
.buffer
!= nil
) {
1089 err
= ReleaseNode(btreePtr
, &left
);
1098 else // operation == kBTreeCurrentRecord
1100 // make sure we have something... <CS9>
1101 if ((foundRecord
!= true) &&
1102 (index
>= ((NodeDescPtr
)node
.buffer
)->numRecords
))
1104 err
= fsBTEndOfIterationErr
;
1109 //////////////////// Process Records Using Callback ////////////////////////
1112 err
= GetRecordByIndex(btreePtr
, node
.buffer
, index
, &keyPtr
, &recordPtr
, &len
);
1119 if (callBackProc(keyPtr
, recordPtr
, len
, callBackState
) == 0)
1122 if ((index
+1) < ((NodeDescPtr
)node
.buffer
)->numRecords
) {
1125 if (right
.buffer
== nil
)
1127 nodeNum
= ((NodeDescPtr
)node
.buffer
)->fLink
;
1130 err
= GetNode(btreePtr
, nodeNum
, &right
);
1133 err
= fsBTEndOfIterationErr
;
1137 // Before we stomp on "left", we'd better release it if needed
1138 if (left
.buffer
!= nil
) {
1139 err
= ReleaseNode(btreePtr
, &left
);
1147 err
= GetRecordByIndex(btreePtr
, node
.buffer
, index
,
1148 &keyPtr
, &recordPtr
, &len
);
1156 ///////////////// Update Iterator to Last Item Processed /////////////////////
1159 if (iterator
!= nil
) // first & last have optional iterator
1161 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
1162 iterator
->hint
.nodeNum
= nodeNum
;
1163 iterator
->hint
.index
= index
;
1164 iterator
->version
= 0;
1166 BlockMoveData((Ptr
)keyPtr
, (Ptr
)&iterator
->key
, CalcKeySize(btreePtr
, keyPtr
));
1171 ///////////////////////////// Release Nodes /////////////////////////////////
1173 err
= ReleaseNode(btreePtr
, &node
);
1176 if (left
.buffer
!= nil
)
1178 err
= ReleaseNode(btreePtr
, &left
);
1182 if (right
.buffer
!= nil
)
1184 err
= ReleaseNode(btreePtr
, &right
);
1190 /////////////////////// Error - Clean Up and Exit ///////////////////////////
1194 (void) ReleaseNode(btreePtr
, &left
);
1195 (void) ReleaseNode(btreePtr
, &node
);
1196 (void) ReleaseNode(btreePtr
, &right
);
1198 if (iterator
!= nil
)
1200 iterator
->hint
.writeCount
= 0;
1201 iterator
->hint
.nodeNum
= 0;
1202 iterator
->hint
.index
= 0;
1203 iterator
->version
= 0;
1204 iterator
->key
.length16
= 0;
1207 if ( err
== fsBTEmptyErr
|| err
== fsBTEndOfIterationErr
)
1208 err
= fsBTRecordNotFoundErr
;
1214 //////////////////////////////// BTInsertRecord /////////////////////////////////
1216 OSStatus
BTInsertRecord (FCB
*filePtr
,
1217 BTreeIterator
*iterator
,
1218 FSBufferDescriptor
*record
,
1222 BTreeControlBlockPtr btreePtr
;
1223 TreePathTable treePathTable
;
1225 BlockDescriptor nodeRec
;
1226 UInt32 insertNodeNum
;
1230 ////////////////////////// Priliminary Checks ///////////////////////////////
1232 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
1233 nodeRec
.blockHeader
= nil
;
1235 err
= CheckInsertParams (filePtr
, iterator
, record
, recordLen
);
1239 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1241 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1244 ///////////////////////// Find Insert Position //////////////////////////////
1246 // always call SearchTree for Insert
1247 err
= SearchTree (btreePtr
, &iterator
->key
, treePathTable
, &insertNodeNum
, &nodeRec
, &index
);
1249 switch (err
) // set/replace/insert decision point
1251 case noErr
: err
= fsBTDuplicateRecordErr
;
1254 case fsBTRecordNotFoundErr
: break;
1256 case fsBTEmptyErr
: // if tree empty add 1st leaf node
1258 if (btreePtr
->freeNodes
== 0)
1260 err
= ExtendBTree (btreePtr
, btreePtr
->totalNodes
+ 1);
1261 M_ExitOnError (err
);
1264 err
= AllocateNode (btreePtr
, &insertNodeNum
);
1265 M_ExitOnError (err
);
1267 err
= GetNewNode (btreePtr
, insertNodeNum
, &nodeRec
);
1268 M_ExitOnError (err
);
1271 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1273 ((NodeDescPtr
)nodeRec
.buffer
)->kind
= kBTLeafNode
;
1274 ((NodeDescPtr
)nodeRec
.buffer
)->height
= 1;
1276 recordFit
= InsertKeyRecord (btreePtr
, nodeRec
.buffer
, 0,
1277 &iterator
->key
, KeyLength(btreePtr
, &iterator
->key
),
1278 record
->bufferAddress
, recordLen
);
1279 if (recordFit
!= true)
1281 err
= fsBTRecordTooLargeErr
;
1285 err
= UpdateNode (btreePtr
, &nodeRec
, 0, kLockTransaction
);
1286 M_ExitOnError (err
);
1288 // update BTreeControlBlock
1289 btreePtr
->treeDepth
= 1;
1290 btreePtr
->rootNode
= insertNodeNum
;
1291 btreePtr
->firstLeafNode
= insertNodeNum
;
1292 btreePtr
->lastLeafNode
= insertNodeNum
;
1294 M_BTreeHeaderDirty (btreePtr
);
1298 default: goto ErrorExit
;
1304 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1306 recordFit
= InsertKeyRecord (btreePtr
, nodeRec
.buffer
, index
,
1307 &iterator
->key
, KeyLength(btreePtr
, &iterator
->key
),
1308 record
->bufferAddress
, recordLen
);
1309 if (recordFit
== true)
1311 err
= UpdateNode (btreePtr
, &nodeRec
, 0, kLockTransaction
);
1312 M_ExitOnError (err
);
1318 /////////////////////// Extend File If Necessary ////////////////////////////
1320 nodesNeeded
= btreePtr
->treeDepth
+ 1 - btreePtr
->freeNodes
; //\80\80 math limit
1321 if (nodesNeeded
> 0)
1323 nodesNeeded
+= btreePtr
->totalNodes
;
1324 if (nodesNeeded
> CalcMapBits (btreePtr
)) // we'll need to add a map node too!
1327 err
= ExtendBTree (btreePtr
, nodesNeeded
);
1328 M_ExitOnError (err
);
1331 // no need to delete existing record
1333 err
= InsertTree (btreePtr
, treePathTable
, &iterator
->key
, record
->bufferAddress
,
1334 recordLen
, &nodeRec
, index
, 1, kInsertRecord
, &insertNodeNum
);
1335 M_ExitOnError (err
);
1338 //////////////////////////////// Success ////////////////////////////////////
1341 ++btreePtr
->writeCount
;
1342 ++btreePtr
->leafRecords
;
1343 M_BTreeHeaderDirty (btreePtr
);
1346 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
1347 iterator
->hint
.nodeNum
= insertNodeNum
;
1348 iterator
->hint
.index
= 0; // unused
1349 iterator
->hint
.reserved1
= 0;
1350 iterator
->hint
.reserved2
= 0;
1355 ////////////////////////////// Error Exit ///////////////////////////////////
1359 (void) ReleaseNode (btreePtr
, &nodeRec
);
1361 iterator
->hint
.writeCount
= 0;
1362 iterator
->hint
.nodeNum
= 0;
1363 iterator
->hint
.index
= 0;
1364 iterator
->hint
.reserved1
= 0;
1365 iterator
->hint
.reserved2
= 0;
1367 if (err
== fsBTEmptyErr
)
1368 err
= fsBTRecordNotFoundErr
;
1374 //////////////////////////////// BTReplaceRecord ////////////////////////////////
1376 OSStatus
BTReplaceRecord (FCB
*filePtr
,
1377 BTreeIterator
*iterator
,
1378 FSBufferDescriptor
*record
,
1382 BTreeControlBlockPtr btreePtr
;
1383 TreePathTable treePathTable
;
1385 BlockDescriptor nodeRec
;
1386 UInt32 insertNodeNum
;
1392 ////////////////////////// Priliminary Checks ///////////////////////////////
1394 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
1395 nodeRec
.blockHeader
= nil
;
1397 err
= CheckInsertParams (filePtr
, iterator
, record
, recordLen
);
1401 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1403 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1405 ////////////////////////////// Take A Hint //////////////////////////////////
1407 err
= IsItAHint (btreePtr
, iterator
, &validHint
);
1408 M_ExitOnError (err
);
1412 insertNodeNum
= iterator
->hint
.nodeNum
;
1414 err
= GetNode (btreePtr
, insertNodeNum
, &nodeRec
);
1418 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1420 err
= TrySimpleReplace (btreePtr
, nodeRec
.buffer
, iterator
, record
, recordLen
, &recordFit
);
1421 M_ExitOnError (err
);
1425 err
= UpdateNode (btreePtr
, &nodeRec
, 0, 0);
1426 M_ExitOnError (err
);
1428 ++btreePtr
->numValidHints
;
1434 (void) BTInvalidateHint( iterator
);
1437 err
= ReleaseNode (btreePtr
, &nodeRec
);
1438 M_ExitOnError (err
);
1442 (void) BTInvalidateHint( iterator
);
1447 ////////////////////////////// Get A Clue ///////////////////////////////////
1449 err
= SearchTree (btreePtr
, &iterator
->key
, treePathTable
, &insertNodeNum
, &nodeRec
, &index
);
1450 M_ExitOnError (err
); // record must exit for Replace
1452 // optimization - if simple replace will work then don't extend btree
1453 // \80\80 if we tried this before, and failed because it wouldn't fit then we shouldn't try this again...
1456 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1458 err
= TrySimpleReplace (btreePtr
, nodeRec
.buffer
, iterator
, record
, recordLen
, &recordFit
);
1459 M_ExitOnError (err
);
1463 err
= UpdateNode (btreePtr
, &nodeRec
, 0, 0);
1464 M_ExitOnError (err
);
1470 //////////////////////////// Make Some Room /////////////////////////////////
1472 nodesNeeded
= btreePtr
->treeDepth
+ 1 - btreePtr
->freeNodes
; //\80\80 math limit
1473 if (nodesNeeded
> 0)
1475 nodesNeeded
+= btreePtr
->totalNodes
;
1476 if (nodesNeeded
> CalcMapBits (btreePtr
)) // we'll need to add a map node too!
1479 err
= ExtendBTree (btreePtr
, nodesNeeded
);
1480 M_ExitOnError (err
);
1485 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1487 DeleteRecord (btreePtr
, nodeRec
.buffer
, index
); // delete existing key/record
1489 err
= InsertTree (btreePtr
, treePathTable
, &iterator
->key
, record
->bufferAddress
,
1490 recordLen
, &nodeRec
, index
, 1, kReplaceRecord
, &insertNodeNum
);
1491 M_ExitOnError (err
);
1493 ++btreePtr
->writeCount
; /* writeCount changes only if the tree structure changed */
1497 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
1498 iterator
->hint
.nodeNum
= insertNodeNum
;
1499 iterator
->hint
.index
= 0; // unused
1500 iterator
->hint
.reserved1
= 0;
1501 iterator
->hint
.reserved2
= 0;
1506 ////////////////////////////// Error Exit ///////////////////////////////////
1510 (void) ReleaseNode (btreePtr
, &nodeRec
);
1512 iterator
->hint
.writeCount
= 0;
1513 iterator
->hint
.nodeNum
= 0;
1514 iterator
->hint
.index
= 0;
1515 iterator
->hint
.reserved1
= 0;
1516 iterator
->hint
.reserved2
= 0;
1523 //////////////////////////////// BTUpdateRecord ////////////////////////////////
1526 BTUpdateRecord(FCB
*filePtr
, BTreeIterator
*iterator
,
1527 IterateCallBackProcPtr callBackProc
, void * callBackState
)
1530 BTreeControlBlockPtr btreePtr
;
1531 TreePathTable treePathTable
;
1532 BlockDescriptor nodeRec
;
1533 RecordPtr recordPtr
;
1535 UInt32 insertNodeNum
;
1541 ////////////////////////// Priliminary Checks ///////////////////////////////
1543 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
1544 nodeRec
.blockHeader
= nil
;
1546 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1548 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1550 ////////////////////////////// Take A Hint //////////////////////////////////
1552 err
= IsItAHint (btreePtr
, iterator
, &validHint
);
1553 M_ExitOnError (err
);
1557 insertNodeNum
= iterator
->hint
.nodeNum
;
1559 err
= GetNode (btreePtr
, insertNodeNum
, &nodeRec
);
1562 if (((NodeDescPtr
)nodeRec
.buffer
)->kind
== kBTLeafNode
&&
1563 SearchNode (btreePtr
, nodeRec
.buffer
, &iterator
->key
, &index
))
1565 err
= GetRecordByIndex(btreePtr
, nodeRec
.buffer
, index
, &keyPtr
, &recordPtr
, &recordLen
);
1566 M_ExitOnError (err
);
1569 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1571 err
= callBackProc(keyPtr
, recordPtr
, recordLen
, callBackState
);
1572 M_ExitOnError (err
);
1574 err
= UpdateNode (btreePtr
, &nodeRec
, 0, 0);
1575 M_ExitOnError (err
);
1577 ++btreePtr
->numValidHints
;
1583 (void) BTInvalidateHint( iterator
);
1586 err
= ReleaseNode (btreePtr
, &nodeRec
);
1587 M_ExitOnError (err
);
1591 (void) BTInvalidateHint( iterator
);
1595 ////////////////////////////// Get A Clue ///////////////////////////////////
1597 err
= SearchTree (btreePtr
, &iterator
->key
, treePathTable
, &insertNodeNum
, &nodeRec
, &index
);
1598 M_ExitOnError (err
);
1600 err
= GetRecordByIndex(btreePtr
, nodeRec
.buffer
, index
, &keyPtr
, &recordPtr
, &recordLen
);
1601 M_ExitOnError (err
);
1604 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1606 err
= callBackProc(keyPtr
, recordPtr
, recordLen
, callBackState
);
1607 M_ExitOnError (err
);
1609 err
= UpdateNode (btreePtr
, &nodeRec
, 0, 0);
1610 M_ExitOnError (err
);
1614 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
1615 iterator
->hint
.nodeNum
= insertNodeNum
;
1616 iterator
->hint
.index
= 0;
1617 iterator
->hint
.reserved1
= 0;
1618 iterator
->hint
.reserved2
= 0;
1621 ////////////////////////////// Error Exit ///////////////////////////////////
1625 (void) ReleaseNode (btreePtr
, &nodeRec
);
1627 iterator
->hint
.writeCount
= 0;
1628 iterator
->hint
.nodeNum
= 0;
1629 iterator
->hint
.index
= 0;
1630 iterator
->hint
.reserved1
= 0;
1631 iterator
->hint
.reserved2
= 0;
1637 //////////////////////////////// BTDeleteRecord /////////////////////////////////
1639 OSStatus
BTDeleteRecord (FCB
*filePtr
,
1640 BTreeIterator
*iterator
)
1643 BTreeControlBlockPtr btreePtr
;
1644 TreePathTable treePathTable
;
1645 BlockDescriptor nodeRec
;
1650 ////////////////////////// Priliminary Checks ///////////////////////////////
1652 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
1653 nodeRec
.blockHeader
= nil
;
1655 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1656 M_ReturnErrorIf (iterator
== nil
, paramErr
);
1658 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1659 if (btreePtr
== nil
)
1661 err
= fsBTInvalidFileErr
;
1665 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1668 /////////////////////////////// Find Key ////////////////////////////////////
1670 //\80\80 check hint for simple delete case (index > 0, numRecords > 2)
1672 err
= SearchTree (btreePtr
, &iterator
->key
, treePathTable
, &nodeNum
, &nodeRec
, &index
);
1673 M_ExitOnError (err
); // record must exit for Delete
1676 ///////////////////////////// Delete Record /////////////////////////////////
1678 err
= DeleteTree (btreePtr
, treePathTable
, &nodeRec
, index
, 1);
1679 M_ExitOnError (err
);
1681 ++btreePtr
->writeCount
;
1682 --btreePtr
->leafRecords
;
1683 M_BTreeHeaderDirty (btreePtr
);
1685 iterator
->hint
.nodeNum
= 0;
1689 ////////////////////////////// Error Exit ///////////////////////////////////
1692 (void) ReleaseNode (btreePtr
, &nodeRec
);
1699 OSStatus
BTGetInformation (FCB
*filePtr
,
1701 BTreeInfoRec
*info
)
1703 #pragma unused (version)
1705 BTreeControlBlockPtr btreePtr
;
1708 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1710 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1714 * This should not require the whole tree to be locked, just maybe the BTreeControlBlockPtr
1716 * REQUIRE_FILE_LOCK(btreePtr->fileRefNum, true);
1719 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1720 M_ReturnErrorIf (info
== nil
, paramErr
);
1722 //\80\80 check version?
1724 info
->nodeSize
= btreePtr
->nodeSize
;
1725 info
->maxKeyLength
= btreePtr
->maxKeyLength
;
1726 info
->treeDepth
= btreePtr
->treeDepth
;
1727 info
->numRecords
= btreePtr
->leafRecords
;
1728 info
->numNodes
= btreePtr
->totalNodes
;
1729 info
->numFreeNodes
= btreePtr
->freeNodes
;
1730 info
->lastfsync
= btreePtr
->lastfsync
;
1739 BTIsDirty(FCB
*filePtr
)
1741 BTreeControlBlockPtr btreePtr
;
1743 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1744 return TreeIsDirty(btreePtr
);
1747 /*-------------------------------------------------------------------------------
1748 Routine: BTFlushPath - Flush BTreeControlBlock to Header Node.
1750 Function: Brief_description_of_the_function_and_any_side_effects
1753 Input: pathPtr - pointer to path control block for B*Tree file to flush
1757 Result: noErr - success
1759 -------------------------------------------------------------------------------*/
1761 OSStatus
BTFlushPath (FCB
*filePtr
)
1764 BTreeControlBlockPtr btreePtr
;
1767 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1769 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1771 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1773 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1775 err
= UpdateHeader (btreePtr
, false);
1781 /*-------------------------------------------------------------------------------
1782 Routine: BTReload - Reload B-tree Header Data.
1784 Function: Reload B-tree header data from disk. This is called after fsck
1785 has made repairs to the root filesystem. The filesystem is
1786 mounted read-only when BTReload is caled.
1789 Input: filePtr - the B*Tree file that needs its header updated
1793 Result: noErr - success
1795 -------------------------------------------------------------------------------*/
1798 BTReloadData(FCB
*filePtr
)
1801 BTreeControlBlockPtr btreePtr
;
1802 BlockDescriptor node
;
1803 BTHeaderRec
*header
;
1807 node
.blockHeader
= nil
;
1809 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1810 if (btreePtr
== nil
)
1811 return (fsBTInvalidFileErr
);
1813 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1815 err
= GetNode(btreePtr
, kHeaderNodeNum
, &node
);
1819 header
= (BTHeaderRec
*)((char *)node
.buffer
+ sizeof(BTNodeDescriptor
));
1820 if ((err
= VerifyHeader (filePtr
, header
)) == 0) {
1821 btreePtr
->treeDepth
= header
->treeDepth
;
1822 btreePtr
->rootNode
= header
->rootNode
;
1823 btreePtr
->leafRecords
= header
->leafRecords
;
1824 btreePtr
->firstLeafNode
= header
->firstLeafNode
;
1825 btreePtr
->lastLeafNode
= header
->lastLeafNode
;
1826 btreePtr
->maxKeyLength
= header
->maxKeyLength
;
1827 btreePtr
->totalNodes
= header
->totalNodes
;
1828 btreePtr
->freeNodes
= header
->freeNodes
;
1829 btreePtr
->btreeType
= header
->btreeType
;
1831 btreePtr
->flags
&= (~kBTHeaderDirty
);
1834 (void) ReleaseNode(btreePtr
, &node
);
1840 /*-------------------------------------------------------------------------------
1841 Routine: BTInvalidateHint - Invalidates the hint within a BTreeInterator.
1843 Function: Invalidates the hint within a BTreeInterator.
1846 Input: iterator - pointer to BTreeIterator
1848 Output: iterator - iterator with the hint.nodeNum cleared
1850 Result: noErr - success
1851 paramErr - iterator == nil
1852 -------------------------------------------------------------------------------*/
1855 OSStatus
BTInvalidateHint (BTreeIterator
*iterator
)
1857 if (iterator
== nil
)
1860 iterator
->hint
.nodeNum
= 0;
1868 /*-------------------------------------------------------------------------------
1869 Routine: BTGetLastSync
1871 Function: Returns the last time that this btree was flushed, does not include header.
1873 Input: filePtr - pointer file control block
1875 Output: lastfsync - time in seconds of last update
1877 Result: noErr - success
1878 paramErr - iterator == nil
1879 -------------------------------------------------------------------------------*/
1882 OSStatus
BTGetLastSync (FCB
*filePtr
,
1885 BTreeControlBlockPtr btreePtr
;
1888 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1890 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1892 /* Maybe instead of requiring a lock..an atomic set might be more appropriate */
1893 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
1895 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1896 M_ReturnErrorIf (lastsync
== nil
, paramErr
);
1898 *lastsync
= btreePtr
->lastfsync
;
1906 /*-------------------------------------------------------------------------------
1907 Routine: BTSetLastSync
1909 Function: Sets the last time that this btree was flushed, does not include header.
1912 Input: fcb - pointer file control block
1914 Output: lastfsync - time in seconds of last update
1916 Result: noErr - success
1917 paramErr - iterator == nil
1918 -------------------------------------------------------------------------------*/
1921 OSStatus
BTSetLastSync (FCB
*filePtr
,
1924 BTreeControlBlockPtr btreePtr
;
1927 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1929 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1931 /* Maybe instead of requiring a lock..an atomic set might be more appropriate */
1932 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
1934 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1935 M_ReturnErrorIf (lastsync
== nil
, paramErr
);
1937 btreePtr
->lastfsync
= lastsync
;
1943 /*-------------------------------------------------------------------------------
1944 Routine: BTCheckFreeSpace
1946 Function: Makes sure there is enough free space so that a tree operation
1949 Input: fcb - pointer file control block
1953 Result: noErr - success
1955 -------------------------------------------------------------------------------*/
1958 OSStatus
BTCheckFreeSpace (FCB
*filePtr
)
1960 BTreeControlBlockPtr btreePtr
;
1961 int nodesNeeded
, err
= noErr
;
1964 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1966 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1968 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
1970 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1972 // XXXdbg this is highly conservative but so much better than
1973 // winding up with turds on your disk.
1975 nodesNeeded
= (btreePtr
->treeDepth
+ 1) * 10;
1977 if (btreePtr
->freeNodes
< nodesNeeded
) {
1978 err
= ExtendBTree(btreePtr
, nodesNeeded
+ btreePtr
->totalNodes
- btreePtr
->freeNodes
);
1986 OSStatus
BTHasContiguousNodes (FCB
*filePtr
)
1988 BTreeControlBlockPtr btreePtr
;
1989 int nodesNeeded
, err
= noErr
;
1992 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1994 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1996 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
1998 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
2000 return NodesAreContiguous(FCBTOVCB(filePtr
), filePtr
, btreePtr
->nodeSize
);