2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
25 Contains: Implementation of public interface routines for B-tree manager.
29 Written by: Gordon Sheridan and Bill Bruffey
31 Copyright: © 1992-1999 by Apple Computer, Inc., all rights reserved.
37 Other Contact: Mark Day
39 Technology: File Systems
47 Change History (most recent first):
48 <MOSXS> 9/22/99 ser Added routines BTGetLastSync and BTSetLastSync
49 <MOSXS> 6/1/99 djb Sync up with Mac OS 8.6.
50 <MOSXS> 6/30/98 djb In BTOpenPath make sure nodes are contiguous on disk (radar #2249539).
51 <MOSXS> 4/15/98 djb In BTOpenPath need to clear nodeRec.buffer if GetBlockProc fails.
52 <MOSXS> 4/11/98 djb Add RequireFileLock checking to all external entry points.
54 <MOSXS> 03/23/98 djb In BTOpenPath use kTrashBlock option when releasing the header so
55 that we get a full node when we call GetNode.
57 <CS9> 12/12/97 djb Radar #2202682, BTIterateRecord with kBTreeCurrentRecord was not
58 checking if we had a record and could call BlockMove with an
59 uninitialize source pointer (causing a bus error).
60 <CS8> 10/24/97 msd In BTIterateRecord, when moving to the previous or next record
61 and we have to move to another node, see if we need to release
62 the node about to be "shifted out" (opposite sibling of the
63 direction we need to move).
64 <CS7> 7/25/97 DSH BTSearchRecord now takes a heuristicHint, nodeNum, and tries it
65 before calling SearchBTree
66 <CS6> 7/24/97 djb GetBlockProc now take a file refnum instead of an FCB ptr.
67 <CS5> 7/22/97 djb Move trace points from BTreeWrapper.c to here.
68 <CS4> 7/21/97 djb LogEndTime now takes an error code.
69 <CS3> 7/16/97 DSH FilesInternal.i renamed FileMgrInternal.i to avoid name
71 <CS2> 5/19/97 djb Add summary traces to BTIterateRecord.
72 <CS1> 4/23/97 djb first checked in
74 <HFS7> 2/19/97 djb Enable variable sized index keys for HFS+ volumes. Added node
75 cache to support nodes larger than 512 bytes.
76 <HFS6> 1/27/97 djb Calls to InsertTree and DeleteTree are now recursive (to support
77 variable sized index keys).
78 <HFS5> 1/13/97 djb Added support for getting current record to BTIterateRecord.
79 <HFS4> 1/6/97 djb Initialize "BigKeys" attribute in BTOpen.
80 <HFS3> 1/3/97 djb Added support for large keys.
81 <HFS2> 12/23/96 djb On exit map fsBTEmptyErr and fsBTEndOfIterationErr to
82 fsBTRecordNotFoundErr.
83 <HFS1> 12/19/96 djb first checked in
85 History applicable to original Scarecrow Design:
87 <13> 10/25/96 ser Changing for new VFPI
88 <12> 10/18/96 ser Converting over VFPI changes
89 <11> 9/17/96 dkh More BTree statistics. Modified hint checks to not bail out when
90 an error is returned from GetNode.
91 <10> 9/16/96 dkh Revised BTree statistics.
92 <9> 8/23/96 dkh Remove checks for multiple paths to BTree file. Need to add
93 equivalent mechanism later.
94 <8> 6/20/96 dkh Radar #1358740. Switch from using Pools to debug MemAllocators.
95 <7> 3/14/96 jev Fix BTreeSetRecord, recordFound was not set for the case of a
96 simple replace causing the leafRecords count to get bumped even
97 though we didn't have to add a record.
98 <6> 3/1/96 prp Fix lint problems. Bug in BTSetRecord that does not initialize
100 <5> 1/22/96 dkh Add #include Memory.h
101 <4> 1/10/96 msd Use the real function names from Math64.i.
102 <3> 1/4/96 jev Fix BTItererateRecord for the condition when the iterator
103 position routine does not find the record and we are looking for
104 the next record. In such a case, if the node's forrward link is
105 non-zero, we have to keep iterating next and not return
106 fsBTEndOfIterationErr error.
107 <2> 12/7/95 dkh D10E2 build. Changed usage of Ref data type to LogicalAddress.
108 <1> 10/18/95 rst Moved from Scarecrow project.
110 <24> 7/18/95 mbb Change MoveData & ClearBytes to BlockMoveData & BlockZero.
111 <23> 1/31/95 prp GetBlockProc interface uses a 64 bit node number.
112 <22> 1/12/95 wjk Adopt Model FileSystem changes in D5.
113 <21> 11/16/94 prp Add IsItAHint routine and use it whenever hint's node number was
115 <20> 11/10/94 prp BTGetInfo name collides with the same name in FileManagerPriv.i.
116 Change it to BTGetInformation.
117 <19> 9/30/94 prp Get in sync with D2 interface changes.
118 <18> 7/22/94 wjk Convert to the new set of header files.
119 <17> 12/9/93 wjk Cleanup usage of char, Byte, int8, UInt8, etc.
120 <16> 12/2/93 wjk Move from Makefiles to BuildFiles. Fit into the ModernOS and
122 <15> 11/30/93 wjk Move from Makefiles to BuildFiles. Fit into the ModernOS and
124 <14> 9/30/93 gs Rename E_NoGetNodeProc and E_NoReleaseNodeProc to
126 <13> 8/31/93 prp Use Set64U instead of Set64.
127 <12> 8/16/93 prp In BTSearchRecord, if the input hint found the node and record,
128 set the local nodeNum variable correctly so that the resultant
129 iterator gets set correctly.
130 <11> 7/1/93 gs Fix bug in BTIterateRecord related to kBTreePrevRecord
132 <10> 6/2/93 gs Update for changes to FSErrors.h and add some comments.
133 <9> 5/24/93 gs Fix bug in BTInsert/Set/ReplaceRecord which didn't set node hint
134 properly in some cases.
135 <8> 5/24/93 gs Do NOT map fsBTEmptyErr to fsBTRecordNotFoundErr in BTSearchRecord.
136 <7> 5/24/93 gs Rename BTFlush to BTFlushPath.
137 <6> 5/21/93 gs Add hint optimization to Set/Replace routines.
138 <5> 5/10/93 gs Remove Panic from BTInitialize for small logicalEOF. Implement
139 Insert, Set, Replace, and Delete.
140 <4> 3/23/93 gs Finish BTInitialize.
141 <3> 2/8/93 gs Implement BTSearchRecord and BTIterateRecord.
142 <2> 12/8/92 gs Implement Open and Close routines.
143 <1> 11/15/92 gs first checked in
147 #include "../headers/BTreesPrivate.h"
150 * The amount that the BTree header leaf count can be wrong before we assume
151 * it is in an infinite loop.
153 #define kNumLeafRecSlack 10
155 /* BTree accessor routines */
156 extern OSStatus
GetBTreeBlock(FileReference vp
, UInt32 blockNum
, GetBlockOptions options
, BlockDescriptor
*block
);
157 extern OSStatus
SetBTreeBlockSize(FileReference vp
, ByteCount blockSize
, ItemCount minBlockCount
);
158 extern OSStatus
ExtendBTreeFile(FileReference vp
, FSSize minEOF
, FSSize maxEOF
);
159 extern OSStatus
ReleaseBTreeBlock(FileReference vp
, BlockDescPtr blockPtr
, ReleaseBlockOptions options
);
161 //////////////////////////////////// Globals ////////////////////////////////////
164 /////////////////////////// BTree Module Entry Points ///////////////////////////
168 /*-------------------------------------------------------------------------------
169 Routine: BTOpenPath - Open a file for access as a B*Tree.
171 Function: Create BTree control block for a file, if necessary. Validates the
172 file to be sure it looks like a BTree file.
175 Input: filePtr - pointer to file to open as a B-tree
176 keyCompareProc - pointer to client's KeyCompare function
178 Result: noErr - success
179 paramErr - required ptr was nil
183 -------------------------------------------------------------------------------*/
185 OSStatus
BTOpenPath(FCB
*filePtr
, KeyCompareProcPtr keyCompareProc
)
188 BTreeControlBlockPtr btreePtr
;
192 ////////////////////// Preliminary Error Checking ///////////////////////////
194 if ( filePtr
== nil
)
200 * Subsequent opens allow key compare proc to be changed.
202 if ( filePtr
->fcbBTCBPtr
!= nil
&& keyCompareProc
!= nil
) {
203 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
204 btreePtr
->keyCompareProc
= keyCompareProc
;
208 if ( filePtr
->fcbEOF
< kMinNodeSize
)
209 return fsBTInvalidFileErr
;
212 //////////////////////// Allocate Control Block /////////////////////////////
214 btreePtr
= (BTreeControlBlock
*) NewPtrSysClear( sizeof( BTreeControlBlock
) );
217 Panic ("\pBTOpen: no memory for btreePtr.");
221 btreePtr
->getBlockProc
= GetBTreeBlock
;
222 btreePtr
->releaseBlockProc
= ReleaseBTreeBlock
;
223 btreePtr
->setEndOfForkProc
= ExtendBTreeFile
;
224 btreePtr
->keyCompareProc
= keyCompareProc
;
226 /////////////////////////// Read Header Node ////////////////////////////////
228 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
229 btreePtr
->fileRefNum
= GetFileRefNumFromFCB(filePtr
);
230 filePtr
->fcbBTCBPtr
= (Ptr
) btreePtr
; // attach btree cb to file
232 /* The minimum node size is the physical block size */
233 nodeRec
.blockSize
= VTOHFS(btreePtr
->fileRefNum
)->hfs_phys_block_size
;
235 /* Start with the allocation block size for regular files. */
236 if (FTOC(filePtr
)->c_fileid
>= kHFSFirstUserCatalogNodeID
)
238 nodeRec
.blockSize
= FCBTOVCB(filePtr
)->blockSize
;
240 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
242 // it is now safe to call M_ExitOnError (err)
244 err
= SetBTreeBlockSize (btreePtr
->fileRefNum
, nodeRec
.blockSize
, 1);
248 err
= GetBTreeBlock(btreePtr
->fileRefNum
,
254 nodeRec
.buffer
= nil
;
255 nodeRec
.blockHeader
= nil
;
256 Panic("\pBTOpen: getNodeProc returned error getting header node.");
259 ++btreePtr
->numGetNodes
;
260 header
= (BTHeaderRec
*) ((u_long
)nodeRec
.buffer
+ sizeof(BTNodeDescriptor
));
263 ///////////////////////////// verify header /////////////////////////////////
265 err
= VerifyHeader (filePtr
, header
);
269 ///////////////////// Initalize fields from header //////////////////////////
271 PanicIf ( (FCBTOVCB(filePtr
)->vcbSigWord
!= 0x4244) && (header
->nodeSize
== 512), "\p BTOpenPath: wrong node size for HFS+ volume!"); // 0x4244 = 'BD'
273 btreePtr
->treeDepth
= header
->treeDepth
;
274 btreePtr
->rootNode
= header
->rootNode
;
275 btreePtr
->leafRecords
= header
->leafRecords
;
276 btreePtr
->firstLeafNode
= header
->firstLeafNode
;
277 btreePtr
->lastLeafNode
= header
->lastLeafNode
;
278 btreePtr
->nodeSize
= header
->nodeSize
;
279 btreePtr
->maxKeyLength
= header
->maxKeyLength
;
280 btreePtr
->totalNodes
= header
->totalNodes
;
281 btreePtr
->freeNodes
= header
->freeNodes
;
282 if (FTOC(filePtr
)->c_fileid
>= kHFSFirstUserCatalogNodeID
)
283 filePtr
->ff_clumpsize
= header
->clumpSize
;
284 btreePtr
->btreeType
= header
->btreeType
;
286 btreePtr
->keyCompareType
= header
->keyCompareType
;
288 btreePtr
->attributes
= header
->attributes
;
290 if ( btreePtr
->maxKeyLength
> 40 )
291 btreePtr
->attributes
|= (kBTBigKeysMask
+ kBTVariableIndexKeysMask
); //\80\80 we need a way to save these attributes
293 /////////////////////// Initialize dynamic fields ///////////////////////////
295 btreePtr
->version
= kBTreeVersion
;
297 btreePtr
->writeCount
= 1;
299 /////////////////////////// Check Header Node ///////////////////////////////
301 // set kBadClose attribute bit, and UpdateNode
303 /* b-tree node size must be at least as big as the physical block size */
304 if (btreePtr
->nodeSize
< nodeRec
.blockSize
)
307 * If this tree has any records or the media is writeable then
308 * we cannot mount using the current physical block size.
310 if (btreePtr
->leafRecords
> 0 ||
311 VTOHFS(btreePtr
->fileRefNum
)->hfs_flags
& HFS_WRITEABLE_MEDIA
)
313 err
= fsBTBadNodeSize
;
319 * If the actual node size is different than the amount we read,
320 * then release and trash this block, and re-read with the correct
323 if ( btreePtr
->nodeSize
!= nodeRec
.blockSize
)
325 err
= SetBTreeBlockSize (btreePtr
->fileRefNum
, btreePtr
->nodeSize
, 32);
329 * Need to use kTrashBlock option to force the
330 * buffer cache to read the entire node
332 err
= ReleaseBTreeBlock(btreePtr
->fileRefNum
, &nodeRec
, kTrashBlock
);
333 ++btreePtr
->numReleaseNodes
;
336 err
= GetNode (btreePtr
, kHeaderNodeNum
, &nodeRec
);
340 //\80\80 total nodes * node size <= LEOF?
343 err
= ReleaseNode (btreePtr
, &nodeRec
);
347 * Under Mac OS, b-tree nodes can be non-contiguous on disk when the
348 * allocation block size is smaller than the b-tree node size.
350 * If journaling is turned on for this volume we can't deal with this
351 * situation and so we bail out. If journaling isn't on it's ok as
352 * hfs_strategy_fragmented() deals with it. Journaling can't support
353 * this because it assumes that if you give it a block that it's
354 * contiguous on disk.
356 if ( FCBTOHFS(filePtr
)->jnl
&& !NodesAreContiguous(FCBTOVCB(filePtr
), filePtr
, btreePtr
->nodeSize
) ) {
357 return fsBTInvalidNodeErr
;
360 //////////////////////////////// Success ////////////////////////////////////
362 //\80\80 align LEOF to multiple of node size? - just on close
367 /////////////////////// Error - Clean up and Exit ///////////////////////////
371 filePtr
->fcbBTCBPtr
= nil
;
372 (void) ReleaseNode (btreePtr
, &nodeRec
);
373 DisposePtr( (Ptr
) btreePtr
);
380 /*-------------------------------------------------------------------------------
381 Routine: BTClosePath - Flush BTree Header and Deallocate Memory for BTree.
383 Function: Flush the BTreeControlBlock fields to header node, and delete BTree control
384 block and key descriptor associated with the file if filePtr is last
385 path of type kBTreeType ('btre').
388 Input: filePtr - pointer to file to delete BTree control block for.
390 Result: noErr - success
393 -------------------------------------------------------------------------------*/
395 OSStatus
BTClosePath (FCB
*filePtr
)
398 BTreeControlBlockPtr btreePtr
;
400 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
403 return fsBTInvalidFileErr
;
405 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
407 ////////////////////// Check for other BTree Paths //////////////////////////
409 btreePtr
->attributes
&= ~kBTBadCloseMask
; // clear "bad close" attribute bit
410 err
= UpdateHeader (btreePtr
, true);
413 DisposePtr( (Ptr
) btreePtr
);
414 filePtr
->fcbBTCBPtr
= nil
;
418 /////////////////////// Error - Clean Up and Exit ///////////////////////////
427 /*-------------------------------------------------------------------------------
428 Routine: BTSearchRecord - Search BTree for a record with a matching key.
430 Function: Search for position in B*Tree indicated by searchKey. If a valid node hint
431 is provided, it will be searched first, then SearchTree will be called.
432 If a BTreeIterator is provided, it will be set to the position found as
433 a result of the search. If a record exists at that position, and a BufferDescriptor
434 is supplied, the record will be copied to the buffer (as much as will fit),
435 and recordLen will be set to the length of the record.
437 If an error other than fsBTRecordNotFoundErr occurs, the BTreeIterator, if any,
438 is invalidated, and recordLen is set to 0.
441 Input: pathPtr - pointer to path for BTree file.
442 searchKey - pointer to search key to match.
443 hintPtr - pointer to hint (may be nil)
445 Output: record - pointer to BufferDescriptor containing record
446 recordLen - length of data at recordPtr
447 iterator - pointer to BTreeIterator indicating position result of search
449 Result: noErr - success, record contains copy of record found
450 fsBTRecordNotFoundErr - record was not found, no data copied
451 fsBTInvalidFileErr - no BTreeControlBlock is allocated for the fork
452 fsBTInvalidKeyLengthErr -
454 -------------------------------------------------------------------------------*/
456 OSStatus
BTSearchRecord (FCB
*filePtr
,
457 BTreeIterator
*searchIterator
,
458 FSBufferDescriptor
*record
,
460 BTreeIterator
*resultIterator
)
463 BTreeControlBlockPtr btreePtr
;
464 TreePathTable treePathTable
;
466 BlockDescriptor node
;
474 if (filePtr
== nil
) return paramErr
;
475 if (searchIterator
== nil
) return paramErr
;
478 node
.blockHeader
= nil
;
480 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
481 if (btreePtr
== nil
) return fsBTInvalidFileErr
;
483 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
487 ////////////////////////////// Take A Hint //////////////////////////////////
489 err
= IsItAHint (btreePtr
, searchIterator
, &validHint
);
494 nodeNum
= searchIterator
->hint
.nodeNum
;
496 err
= GetNode (btreePtr
, nodeNum
, &node
);
499 if ( ((BTNodeDescriptor
*) node
.buffer
)->kind
== kBTLeafNode
&&
500 ((BTNodeDescriptor
*) node
.buffer
)->numRecords
> 0 )
502 foundRecord
= SearchNode (btreePtr
, node
.buffer
, &searchIterator
->key
, &index
);
504 //\80\80 if !foundRecord, we could still skip tree search if ( 0 < index < numRecords )
507 if (foundRecord
== false)
509 err
= ReleaseNode (btreePtr
, &node
);
514 ++btreePtr
->numValidHints
;
518 if( foundRecord
== false )
519 (void) BTInvalidateHint( searchIterator
);
523 //////////////////////////// Search The Tree ////////////////////////////////
525 if (foundRecord
== false)
527 err
= SearchTree ( btreePtr
, &searchIterator
->key
, treePathTable
, &nodeNum
, &node
, &index
);
530 case noErr
: foundRecord
= true; break;
531 case fsBTRecordNotFoundErr
: break;
532 default: goto ErrorExit
;
537 //////////////////////////// Get the Record /////////////////////////////////
539 if (foundRecord
== true)
541 //XXX Should check for errors! Or BlockMove could choke on recordPtr!!!
542 GetRecordByIndex (btreePtr
, node
.buffer
, index
, &keyPtr
, &recordPtr
, &len
);
544 if (recordLen
!= nil
) *recordLen
= len
;
548 ByteCount recordSize
;
550 recordSize
= record
->itemCount
* record
->itemSize
;
552 if (len
> recordSize
) len
= recordSize
;
554 BlockMoveData (recordPtr
, record
->bufferAddress
, len
);
559 /////////////////////// Success - Update Iterator ///////////////////////////
561 if (resultIterator
!= nil
)
563 resultIterator
->hint
.writeCount
= btreePtr
->writeCount
;
564 resultIterator
->hint
.nodeNum
= nodeNum
;
565 resultIterator
->hint
.index
= index
;
567 resultIterator
->hint
.reserved1
= 0;
568 resultIterator
->hint
.reserved2
= 0;
569 resultIterator
->version
= 0;
570 resultIterator
->reserved
= 0;
572 // copy the key in the BTree when found rather than searchIterator->key to get proper case/diacriticals
573 if (foundRecord
== true)
574 BlockMoveData ((Ptr
)keyPtr
, (Ptr
)&resultIterator
->key
, CalcKeySize(btreePtr
, keyPtr
));
576 BlockMoveData ((Ptr
)&searchIterator
->key
, (Ptr
)&resultIterator
->key
, CalcKeySize(btreePtr
, &searchIterator
->key
));
579 err
= ReleaseNode (btreePtr
, &node
);
582 if (foundRecord
== false) return fsBTRecordNotFoundErr
;
586 /////////////////////// Error - Clean Up and Exit ///////////////////////////
590 if (recordLen
!= nil
)
593 if (resultIterator
!= nil
)
595 resultIterator
->hint
.writeCount
= 0;
596 resultIterator
->hint
.nodeNum
= 0;
597 resultIterator
->hint
.index
= 0;
598 resultIterator
->hint
.reserved1
= 0;
599 resultIterator
->hint
.reserved2
= 0;
601 resultIterator
->version
= 0;
602 resultIterator
->reserved
= 0;
603 resultIterator
->key
.length16
= 0; // zero out two bytes to cover both types of keys
606 if ( err
== fsBTEmptyErr
)
607 err
= fsBTRecordNotFoundErr
;
614 /*-------------------------------------------------------------------------------
615 Routine: BTIterateRecord - Find the first, next, previous, or last record.
617 Function: Find the first, next, previous, or last record in the BTree
619 Input: pathPtr - pointer to path iterate records for.
620 operation - iteration operation (first,next,prev,last)
621 iterator - pointer to iterator indicating start position
623 Output: iterator - iterator is updated to indicate new position
624 newKeyPtr - pointer to buffer to copy key found by iteration
625 record - pointer to buffer to copy record found by iteration
626 recordLen - length of record
628 Result: noErr - success
630 -------------------------------------------------------------------------------*/
632 OSStatus
BTIterateRecord (FCB
*filePtr
,
633 BTreeIterationOperation operation
,
634 BTreeIterator
*iterator
,
635 FSBufferDescriptor
*record
,
639 BTreeControlBlockPtr btreePtr
;
647 BlockDescriptor left
, node
, right
;
651 ////////////////////////// Priliminary Checks ///////////////////////////////
654 left
.blockHeader
= nil
;
656 right
.blockHeader
= nil
;
658 node
.blockHeader
= nil
;
666 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
669 return fsBTInvalidFileErr
; //\80\80 handle properly
672 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
674 if ((operation
!= kBTreeFirstRecord
) &&
675 (operation
!= kBTreeNextRecord
) &&
676 (operation
!= kBTreeCurrentRecord
) &&
677 (operation
!= kBTreePrevRecord
) &&
678 (operation
!= kBTreeLastRecord
))
680 err
= fsInvalidIterationMovmentErr
;
684 /////////////////////// Find First or Last Record ///////////////////////////
686 if ((operation
== kBTreeFirstRecord
) || (operation
== kBTreeLastRecord
))
688 if (operation
== kBTreeFirstRecord
) nodeNum
= btreePtr
->firstLeafNode
;
689 else nodeNum
= btreePtr
->lastLeafNode
;
697 err
= GetNode (btreePtr
, nodeNum
, &node
);
700 if ( ((NodeDescPtr
) node
.buffer
)->kind
!= kBTLeafNode
||
701 ((NodeDescPtr
) node
.buffer
)->numRecords
<= 0 )
703 err
= ReleaseNode (btreePtr
, &node
);
706 err
= fsBTInvalidNodeErr
;
707 MARK_VOLUMEDAMAGED(filePtr
);
711 if (operation
== kBTreeFirstRecord
) index
= 0;
712 else index
= ((BTNodeDescriptor
*) node
.buffer
)->numRecords
- 1;
714 goto CopyData
; //\80\80 is there a cleaner way?
718 //////////////////////// Find Iterator Position /////////////////////////////
720 err
= FindIteratorPosition (btreePtr
, iterator
,
721 &left
, &node
, &right
, &nodeNum
, &index
, &foundRecord
);
725 ///////////////////// Find Next Or Previous Record //////////////////////////
727 if (operation
== kBTreePrevRecord
)
735 if (left
.buffer
== nil
)
737 nodeNum
= ((NodeDescPtr
) node
.buffer
)->bLink
;
740 err
= GetNode (btreePtr
, nodeNum
, &left
);
743 err
= fsBTStartOfIterationErr
;
747 // Before we stomp on "right", we'd better release it if needed
748 if (right
.buffer
!= nil
) {
749 err
= ReleaseNode(btreePtr
, &right
);
755 index
= ((NodeDescPtr
) node
.buffer
)->numRecords
-1;
758 else if (operation
== kBTreeNextRecord
)
760 if ((foundRecord
!= true) &&
761 (((NodeDescPtr
) node
.buffer
)->fLink
== 0) &&
762 (index
== ((NodeDescPtr
) node
.buffer
)->numRecords
))
764 err
= fsBTEndOfIterationErr
;
768 // we did not find the record but the index is already positioned correctly
769 if ((foundRecord
== false) && (index
!= ((NodeDescPtr
) node
.buffer
)->numRecords
))
772 // we found the record OR we have to look in the next node
773 if (index
< ((NodeDescPtr
) node
.buffer
)->numRecords
-1)
779 if (right
.buffer
== nil
)
781 nodeNum
= ((NodeDescPtr
) node
.buffer
)->fLink
;
784 err
= GetNode (btreePtr
, nodeNum
, &right
);
787 err
= fsBTEndOfIterationErr
;
791 // Before we stomp on "left", we'd better release it if needed
792 if (left
.buffer
!= nil
) {
793 err
= ReleaseNode(btreePtr
, &left
);
802 else // operation == kBTreeCurrentRecord
804 // make sure we have something... <CS9>
805 if ((foundRecord
!= true) &&
806 (index
>= ((NodeDescPtr
) node
.buffer
)->numRecords
))
808 err
= fsBTEndOfIterationErr
;
813 //////////////////// Copy Record And Update Iterator ////////////////////////
817 // added check for errors <CS9>
818 err
= GetRecordByIndex (btreePtr
, node
.buffer
, index
, &keyPtr
, &recordPtr
, &len
);
821 if (recordLen
!= nil
)
826 ByteCount recordSize
;
828 recordSize
= record
->itemCount
* record
->itemSize
;
830 if (len
> recordSize
) len
= recordSize
;
832 BlockMoveData (recordPtr
, record
->bufferAddress
, len
);
835 if (iterator
!= nil
) // first & last do not require iterator
837 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
838 iterator
->hint
.nodeNum
= nodeNum
;
839 iterator
->hint
.index
= index
;
840 iterator
->hint
.reserved1
= 0;
841 iterator
->hint
.reserved2
= 0;
843 iterator
->version
= 0;
844 iterator
->reserved
= 0;
847 * Check for infinite loops by making sure we do not
848 * process more leaf records, than can possibly be (or the BTree header
849 * is seriously damaged)....a brute force method.
851 if ((operation
== kBTreeFirstRecord
) || (operation
== kBTreeLastRecord
))
852 iterator
->hitCount
= 1;
853 else if (operation
!= kBTreeCurrentRecord
)
854 iterator
->hitCount
+= 1;
855 /* Always use the highest max, in case the grows while iterating */
856 iterator
->maxLeafRecs
= max(btreePtr
->leafRecords
, iterator
->maxLeafRecs
);
859 if (iterator
->hitCount
> iterator
->maxLeafRecs
+ kNumLeafRecSlack
)
861 err
= fsBTInvalidNodeErr
;
862 MARK_VOLUMEDAMAGED(filePtr
);
867 BlockMoveData ((Ptr
)keyPtr
, (Ptr
)&iterator
->key
, CalcKeySize(btreePtr
, keyPtr
));
871 ///////////////////////////// Release Nodes /////////////////////////////////
873 err
= ReleaseNode (btreePtr
, &node
);
876 if (left
.buffer
!= nil
)
878 err
= ReleaseNode (btreePtr
, &left
);
882 if (right
.buffer
!= nil
)
884 err
= ReleaseNode (btreePtr
, &right
);
890 /////////////////////// Error - Clean Up and Exit ///////////////////////////
894 (void) ReleaseNode (btreePtr
, &left
);
895 (void) ReleaseNode (btreePtr
, &node
);
896 (void) ReleaseNode (btreePtr
, &right
);
898 if (recordLen
!= nil
)
903 iterator
->hint
.writeCount
= 0;
904 iterator
->hint
.nodeNum
= 0;
905 iterator
->hint
.index
= 0;
906 iterator
->hint
.reserved1
= 0;
907 iterator
->hint
.reserved2
= 0;
909 iterator
->version
= 0;
910 iterator
->reserved
= 0;
911 iterator
->key
.length16
= 0;
914 if ( err
== fsBTEmptyErr
|| err
== fsBTEndOfIterationErr
)
915 err
= fsBTRecordNotFoundErr
;
921 /*-------------------------------------------------------------------------------
922 Routine: BTIterateRecords
924 Function: Find a series of records
926 Input: filePtr - b-tree file
927 operation - iteration operation (first,next,prev,last)
928 iterator - pointer to iterator indicating start position
929 callBackProc - pointer to routince to process a record
930 callBackState - pointer to state data (used by callBackProc)
932 Output: iterator - iterator is updated to indicate new position
934 Result: noErr - success
936 -------------------------------------------------------------------------------*/
939 BTIterateRecords(FCB
*filePtr
, BTreeIterationOperation operation
, BTreeIterator
*iterator
,
940 IterateCallBackProcPtr callBackProc
, void * callBackState
)
943 BTreeControlBlockPtr btreePtr
;
949 BlockDescriptor left
, node
, right
;
953 ////////////////////////// Priliminary Checks ///////////////////////////////
956 left
.blockHeader
= nil
;
958 right
.blockHeader
= nil
;
960 node
.blockHeader
= nil
;
962 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
964 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
966 if ((operation
!= kBTreeFirstRecord
) &&
967 (operation
!= kBTreeNextRecord
) &&
968 (operation
!= kBTreeCurrentRecord
) &&
969 (operation
!= kBTreePrevRecord
) &&
970 (operation
!= kBTreeLastRecord
))
972 err
= fsInvalidIterationMovmentErr
;
976 /////////////////////// Find First or Last Record ///////////////////////////
978 if ((operation
== kBTreeFirstRecord
) || (operation
== kBTreeLastRecord
))
980 if (operation
== kBTreeFirstRecord
)
981 nodeNum
= btreePtr
->firstLeafNode
;
983 nodeNum
= btreePtr
->lastLeafNode
;
991 err
= GetNode(btreePtr
, nodeNum
, &node
);
994 if ( ((NodeDescPtr
)node
.buffer
)->kind
!= kBTLeafNode
||
995 ((NodeDescPtr
)node
.buffer
)->numRecords
<= 0 )
997 err
= ReleaseNode(btreePtr
, &node
);
1000 err
= fsBTInvalidNodeErr
;
1001 MARK_VOLUMEDAMAGED(filePtr
);
1005 if (operation
== kBTreeFirstRecord
)
1008 index
= ((BTNodeDescriptor
*) node
.buffer
)->numRecords
- 1;
1013 //////////////////////// Find Iterator Position /////////////////////////////
1015 err
= FindIteratorPosition(btreePtr
, iterator
, &left
, &node
, &right
,
1016 &nodeNum
, &index
, &foundRecord
);
1017 if (err
== fsBTRecordNotFoundErr
)
1022 ///////////////////// Find Next Or Previous Record //////////////////////////
1024 if (operation
== kBTreePrevRecord
)
1032 if (left
.buffer
== nil
)
1034 nodeNum
= ((NodeDescPtr
) node
.buffer
)->bLink
;
1037 err
= GetNode(btreePtr
, nodeNum
, &left
);
1040 err
= fsBTStartOfIterationErr
;
1044 // Before we stomp on "right", we'd better release it if needed
1045 if (right
.buffer
!= nil
) {
1046 err
= ReleaseNode(btreePtr
, &right
);
1052 index
= ((NodeDescPtr
) node
.buffer
)->numRecords
-1;
1055 else if (operation
== kBTreeNextRecord
)
1057 if ((foundRecord
!= true) &&
1058 (((NodeDescPtr
)node
.buffer
)->fLink
== 0) &&
1059 (index
== ((NodeDescPtr
)node
.buffer
)->numRecords
))
1061 err
= fsBTEndOfIterationErr
;
1065 // we did not find the record but the index is already positioned correctly
1066 if ((foundRecord
== false) && (index
!= ((NodeDescPtr
)node
.buffer
)->numRecords
))
1069 // we found the record OR we have to look in the next node
1070 if (index
< ((NodeDescPtr
)node
.buffer
)->numRecords
-1)
1076 if (right
.buffer
== nil
)
1078 nodeNum
= ((NodeDescPtr
)node
.buffer
)->fLink
;
1081 err
= GetNode(btreePtr
, nodeNum
, &right
);
1084 err
= fsBTEndOfIterationErr
;
1088 // Before we stomp on "left", we'd better release it if needed
1089 if (left
.buffer
!= nil
) {
1090 err
= ReleaseNode(btreePtr
, &left
);
1099 else // operation == kBTreeCurrentRecord
1101 // make sure we have something... <CS9>
1102 if ((foundRecord
!= true) &&
1103 (index
>= ((NodeDescPtr
)node
.buffer
)->numRecords
))
1105 err
= fsBTEndOfIterationErr
;
1110 //////////////////// Process Records Using Callback ////////////////////////
1113 err
= GetRecordByIndex(btreePtr
, node
.buffer
, index
, &keyPtr
, &recordPtr
, &len
);
1120 if (callBackProc(keyPtr
, recordPtr
, callBackState
) == 0)
1123 if ((index
+1) < ((NodeDescPtr
)node
.buffer
)->numRecords
) {
1126 if (right
.buffer
== nil
)
1128 nodeNum
= ((NodeDescPtr
)node
.buffer
)->fLink
;
1131 err
= GetNode(btreePtr
, nodeNum
, &right
);
1134 err
= fsBTEndOfIterationErr
;
1138 // Before we stomp on "left", we'd better release it if needed
1139 if (left
.buffer
!= nil
) {
1140 err
= ReleaseNode(btreePtr
, &left
);
1148 err
= GetRecordByIndex(btreePtr
, node
.buffer
, index
,
1149 &keyPtr
, &recordPtr
, &len
);
1157 ///////////////// Update Iterator to Last Item Processed /////////////////////
1160 if (iterator
!= nil
) // first & last have optional iterator
1162 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
1163 iterator
->hint
.nodeNum
= nodeNum
;
1164 iterator
->hint
.index
= index
;
1165 iterator
->version
= 0;
1167 BlockMoveData((Ptr
)keyPtr
, (Ptr
)&iterator
->key
, CalcKeySize(btreePtr
, keyPtr
));
1172 ///////////////////////////// Release Nodes /////////////////////////////////
1174 err
= ReleaseNode(btreePtr
, &node
);
1177 if (left
.buffer
!= nil
)
1179 err
= ReleaseNode(btreePtr
, &left
);
1183 if (right
.buffer
!= nil
)
1185 err
= ReleaseNode(btreePtr
, &right
);
1191 /////////////////////// Error - Clean Up and Exit ///////////////////////////
1195 (void) ReleaseNode(btreePtr
, &left
);
1196 (void) ReleaseNode(btreePtr
, &node
);
1197 (void) ReleaseNode(btreePtr
, &right
);
1199 if (iterator
!= nil
)
1201 iterator
->hint
.writeCount
= 0;
1202 iterator
->hint
.nodeNum
= 0;
1203 iterator
->hint
.index
= 0;
1204 iterator
->version
= 0;
1205 iterator
->key
.length16
= 0;
1208 if ( err
== fsBTEmptyErr
|| err
== fsBTEndOfIterationErr
)
1209 err
= fsBTRecordNotFoundErr
;
1215 //////////////////////////////// BTInsertRecord /////////////////////////////////
1217 OSStatus
BTInsertRecord (FCB
*filePtr
,
1218 BTreeIterator
*iterator
,
1219 FSBufferDescriptor
*record
,
1223 BTreeControlBlockPtr btreePtr
;
1224 TreePathTable treePathTable
;
1226 BlockDescriptor nodeRec
;
1227 UInt32 insertNodeNum
;
1231 ////////////////////////// Priliminary Checks ///////////////////////////////
1233 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
1234 nodeRec
.blockHeader
= nil
;
1236 err
= CheckInsertParams (filePtr
, iterator
, record
, recordLen
);
1240 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1242 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1245 ///////////////////////// Find Insert Position //////////////////////////////
1247 // always call SearchTree for Insert
1248 err
= SearchTree (btreePtr
, &iterator
->key
, treePathTable
, &insertNodeNum
, &nodeRec
, &index
);
1250 switch (err
) // set/replace/insert decision point
1252 case noErr
: err
= fsBTDuplicateRecordErr
;
1255 case fsBTRecordNotFoundErr
: break;
1257 case fsBTEmptyErr
: // if tree empty add 1st leaf node
1259 if (BTAvailableNodes(btreePtr
) == 0)
1261 err
= ExtendBTree (btreePtr
, btreePtr
->totalNodes
+ 1);
1262 M_ExitOnError (err
);
1265 err
= AllocateNode (btreePtr
, &insertNodeNum
);
1266 M_ExitOnError (err
);
1268 err
= GetNewNode (btreePtr
, insertNodeNum
, &nodeRec
);
1269 M_ExitOnError (err
);
1272 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1274 ((NodeDescPtr
)nodeRec
.buffer
)->kind
= kBTLeafNode
;
1275 ((NodeDescPtr
)nodeRec
.buffer
)->height
= 1;
1277 recordFit
= InsertKeyRecord (btreePtr
, nodeRec
.buffer
, 0,
1278 &iterator
->key
, KeyLength(btreePtr
, &iterator
->key
),
1279 record
->bufferAddress
, recordLen
);
1280 if (recordFit
!= true)
1282 err
= fsBTRecordTooLargeErr
;
1287 * Update the B-tree control block. Do this before
1288 * calling UpdateNode since it will compare the node's
1289 * height with treeDepth.
1291 btreePtr
->treeDepth
= 1;
1292 btreePtr
->rootNode
= insertNodeNum
;
1293 btreePtr
->firstLeafNode
= insertNodeNum
;
1294 btreePtr
->lastLeafNode
= insertNodeNum
;
1296 err
= UpdateNode (btreePtr
, &nodeRec
, 0, kLockTransaction
);
1297 M_ExitOnError (err
);
1299 M_BTreeHeaderDirty (btreePtr
);
1303 default: goto ErrorExit
;
1309 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1311 recordFit
= InsertKeyRecord (btreePtr
, nodeRec
.buffer
, index
,
1312 &iterator
->key
, KeyLength(btreePtr
, &iterator
->key
),
1313 record
->bufferAddress
, recordLen
);
1314 if (recordFit
== true)
1316 err
= UpdateNode (btreePtr
, &nodeRec
, 0, kLockTransaction
);
1317 M_ExitOnError (err
);
1323 /////////////////////// Extend File If Necessary ////////////////////////////
1325 nodesNeeded
= (SInt32
)btreePtr
->treeDepth
+ 1 - BTAvailableNodes(btreePtr
);
1326 if (nodesNeeded
> 0)
1328 nodesNeeded
+= (SInt32
)btreePtr
->totalNodes
;
1329 if (nodesNeeded
> CalcMapBits (btreePtr
)) // we'll need to add a map node too!
1332 err
= ExtendBTree (btreePtr
, nodesNeeded
);
1333 M_ExitOnError (err
);
1336 // no need to delete existing record
1338 err
= InsertTree (btreePtr
, treePathTable
, &iterator
->key
, record
->bufferAddress
,
1339 recordLen
, &nodeRec
, index
, 1, kInsertRecord
, &insertNodeNum
);
1340 M_ExitOnError (err
);
1343 //////////////////////////////// Success ////////////////////////////////////
1346 ++btreePtr
->writeCount
;
1347 ++btreePtr
->leafRecords
;
1348 M_BTreeHeaderDirty (btreePtr
);
1351 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
1352 iterator
->hint
.nodeNum
= insertNodeNum
;
1353 iterator
->hint
.index
= 0; // unused
1354 iterator
->hint
.reserved1
= 0;
1355 iterator
->hint
.reserved2
= 0;
1360 ////////////////////////////// Error Exit ///////////////////////////////////
1364 (void) ReleaseNode (btreePtr
, &nodeRec
);
1366 iterator
->hint
.writeCount
= 0;
1367 iterator
->hint
.nodeNum
= 0;
1368 iterator
->hint
.index
= 0;
1369 iterator
->hint
.reserved1
= 0;
1370 iterator
->hint
.reserved2
= 0;
1372 if (err
== fsBTEmptyErr
)
1373 err
= fsBTRecordNotFoundErr
;
1379 //////////////////////////////// BTReplaceRecord ////////////////////////////////
1381 OSStatus
BTReplaceRecord (FCB
*filePtr
,
1382 BTreeIterator
*iterator
,
1383 FSBufferDescriptor
*record
,
1387 BTreeControlBlockPtr btreePtr
;
1388 TreePathTable treePathTable
;
1390 BlockDescriptor nodeRec
;
1391 UInt32 insertNodeNum
;
1397 ////////////////////////// Priliminary Checks ///////////////////////////////
1399 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
1400 nodeRec
.blockHeader
= nil
;
1402 err
= CheckInsertParams (filePtr
, iterator
, record
, recordLen
);
1406 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1408 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1410 ////////////////////////////// Take A Hint //////////////////////////////////
1412 err
= IsItAHint (btreePtr
, iterator
, &validHint
);
1413 M_ExitOnError (err
);
1417 insertNodeNum
= iterator
->hint
.nodeNum
;
1419 err
= GetNode (btreePtr
, insertNodeNum
, &nodeRec
);
1423 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1425 err
= TrySimpleReplace (btreePtr
, nodeRec
.buffer
, iterator
, record
, recordLen
, &recordFit
);
1426 M_ExitOnError (err
);
1430 err
= UpdateNode (btreePtr
, &nodeRec
, 0, 0);
1431 M_ExitOnError (err
);
1433 ++btreePtr
->numValidHints
;
1439 (void) BTInvalidateHint( iterator
);
1442 err
= ReleaseNode (btreePtr
, &nodeRec
);
1443 M_ExitOnError (err
);
1447 (void) BTInvalidateHint( iterator
);
1452 ////////////////////////////// Get A Clue ///////////////////////////////////
1454 err
= SearchTree (btreePtr
, &iterator
->key
, treePathTable
, &insertNodeNum
, &nodeRec
, &index
);
1455 M_ExitOnError (err
); // record must exit for Replace
1457 // optimization - if simple replace will work then don't extend btree
1458 // \80\80 if we tried this before, and failed because it wouldn't fit then we shouldn't try this again...
1461 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1463 err
= TrySimpleReplace (btreePtr
, nodeRec
.buffer
, iterator
, record
, recordLen
, &recordFit
);
1464 M_ExitOnError (err
);
1468 err
= UpdateNode (btreePtr
, &nodeRec
, 0, 0);
1469 M_ExitOnError (err
);
1475 //////////////////////////// Make Some Room /////////////////////////////////
1477 nodesNeeded
= (SInt32
)btreePtr
->treeDepth
+ 1 - BTAvailableNodes(btreePtr
);
1478 if (nodesNeeded
> 0)
1480 nodesNeeded
+= (SInt32
)btreePtr
->totalNodes
;
1481 if (nodesNeeded
> CalcMapBits (btreePtr
)) // we'll need to add a map node too!
1484 err
= ExtendBTree (btreePtr
, nodesNeeded
);
1485 M_ExitOnError (err
);
1489 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1491 DeleteRecord (btreePtr
, nodeRec
.buffer
, index
); // delete existing key/record
1493 err
= InsertTree (btreePtr
, treePathTable
, &iterator
->key
, record
->bufferAddress
,
1494 recordLen
, &nodeRec
, index
, 1, kReplaceRecord
, &insertNodeNum
);
1495 M_ExitOnError (err
);
1497 ++btreePtr
->writeCount
; /* writeCount changes only if the tree structure changed */
1501 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
1502 iterator
->hint
.nodeNum
= insertNodeNum
;
1503 iterator
->hint
.index
= 0; // unused
1504 iterator
->hint
.reserved1
= 0;
1505 iterator
->hint
.reserved2
= 0;
1510 ////////////////////////////// Error Exit ///////////////////////////////////
1514 (void) ReleaseNode (btreePtr
, &nodeRec
);
1516 iterator
->hint
.writeCount
= 0;
1517 iterator
->hint
.nodeNum
= 0;
1518 iterator
->hint
.index
= 0;
1519 iterator
->hint
.reserved1
= 0;
1520 iterator
->hint
.reserved2
= 0;
1527 //////////////////////////////// BTUpdateRecord ////////////////////////////////
1530 BTUpdateRecord(FCB
*filePtr
, BTreeIterator
*iterator
,
1531 IterateCallBackProcPtr callBackProc
, void * callBackState
)
1534 BTreeControlBlockPtr btreePtr
;
1535 TreePathTable treePathTable
;
1536 BlockDescriptor nodeRec
;
1537 RecordPtr recordPtr
;
1539 UInt32 insertNodeNum
;
1545 ////////////////////////// Priliminary Checks ///////////////////////////////
1547 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
1548 nodeRec
.blockHeader
= nil
;
1550 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1552 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
1554 ////////////////////////////// Take A Hint //////////////////////////////////
1556 err
= IsItAHint (btreePtr
, iterator
, &validHint
);
1557 M_ExitOnError (err
);
1561 insertNodeNum
= iterator
->hint
.nodeNum
;
1563 err
= GetNode (btreePtr
, insertNodeNum
, &nodeRec
);
1566 if (((NodeDescPtr
)nodeRec
.buffer
)->kind
== kBTLeafNode
&&
1567 SearchNode (btreePtr
, nodeRec
.buffer
, &iterator
->key
, &index
))
1569 err
= GetRecordByIndex(btreePtr
, nodeRec
.buffer
, index
, &keyPtr
, &recordPtr
, &recordLen
);
1570 M_ExitOnError (err
);
1573 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1575 err
= callBackProc(keyPtr
, recordPtr
, callBackState
);
1576 M_ExitOnError (err
);
1578 err
= UpdateNode (btreePtr
, &nodeRec
, 0, 0);
1579 M_ExitOnError (err
);
1581 ++btreePtr
->numValidHints
;
1587 (void) BTInvalidateHint( iterator
);
1590 err
= ReleaseNode (btreePtr
, &nodeRec
);
1591 M_ExitOnError (err
);
1595 (void) BTInvalidateHint( iterator
);
1599 ////////////////////////////// Get A Clue ///////////////////////////////////
1601 err
= SearchTree (btreePtr
, &iterator
->key
, treePathTable
, &insertNodeNum
, &nodeRec
, &index
);
1602 M_ExitOnError (err
);
1604 err
= GetRecordByIndex(btreePtr
, nodeRec
.buffer
, index
, &keyPtr
, &recordPtr
, &recordLen
);
1605 M_ExitOnError (err
);
1608 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1610 err
= callBackProc(keyPtr
, recordPtr
, callBackState
);
1611 M_ExitOnError (err
);
1613 err
= UpdateNode (btreePtr
, &nodeRec
, 0, 0);
1614 M_ExitOnError (err
);
1618 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
1619 iterator
->hint
.nodeNum
= insertNodeNum
;
1620 iterator
->hint
.index
= 0;
1621 iterator
->hint
.reserved1
= 0;
1622 iterator
->hint
.reserved2
= 0;
1625 ////////////////////////////// Error Exit ///////////////////////////////////
1629 (void) ReleaseNode (btreePtr
, &nodeRec
);
1631 iterator
->hint
.writeCount
= 0;
1632 iterator
->hint
.nodeNum
= 0;
1633 iterator
->hint
.index
= 0;
1634 iterator
->hint
.reserved1
= 0;
1635 iterator
->hint
.reserved2
= 0;
1641 //////////////////////////////// BTDeleteRecord /////////////////////////////////
1643 OSStatus
BTDeleteRecord (FCB
*filePtr
,
1644 BTreeIterator
*iterator
)
1647 BTreeControlBlockPtr btreePtr
;
1648 TreePathTable treePathTable
;
1649 BlockDescriptor nodeRec
;
1655 ////////////////////////// Priliminary Checks ///////////////////////////////
1657 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
1658 nodeRec
.blockHeader
= nil
;
1660 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1661 M_ReturnErrorIf (iterator
== nil
, paramErr
);
1663 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1664 if (btreePtr
== nil
)
1666 err
= fsBTInvalidFileErr
;
1670 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1673 /////////////////////////////// Find Key ////////////////////////////////////
1675 //\80\80 check hint for simple delete case (index > 0, numRecords > 2)
1677 err
= SearchTree (btreePtr
, &iterator
->key
, treePathTable
, &nodeNum
, &nodeRec
, &index
);
1678 M_ExitOnError (err
); // record must exit for Delete
1681 /////////////////////// Extend File If Necessary ////////////////////////////
1683 nodesNeeded
= (SInt32
)btreePtr
->treeDepth
+ 1 - BTAvailableNodes(btreePtr
);
1684 if ((btreePtr
->attributes
& kBTVariableIndexKeysMask
) && (nodesNeeded
> 0))
1686 nodesNeeded
+= (SInt32
)btreePtr
->totalNodes
;
1687 if (nodesNeeded
> CalcMapBits (btreePtr
))
1690 err
= ExtendBTree (btreePtr
, nodesNeeded
);
1691 M_ExitOnError (err
);
1694 ///////////////////////////// Delete Record /////////////////////////////////
1696 err
= DeleteTree (btreePtr
, treePathTable
, &nodeRec
, index
, 1);
1697 M_ExitOnError (err
);
1699 ++btreePtr
->writeCount
;
1700 --btreePtr
->leafRecords
;
1701 M_BTreeHeaderDirty (btreePtr
);
1703 iterator
->hint
.nodeNum
= 0;
1707 ////////////////////////////// Error Exit ///////////////////////////////////
1710 (void) ReleaseNode (btreePtr
, &nodeRec
);
1717 OSStatus
BTGetInformation (FCB
*filePtr
,
1719 BTreeInfoRec
*info
)
1721 #pragma unused (version)
1723 BTreeControlBlockPtr btreePtr
;
1726 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1728 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1732 * This should not require the whole tree to be locked, just maybe the BTreeControlBlockPtr
1734 * REQUIRE_FILE_LOCK(btreePtr->fileRefNum, true);
1737 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1738 M_ReturnErrorIf (info
== nil
, paramErr
);
1740 //\80\80 check version?
1742 info
->nodeSize
= btreePtr
->nodeSize
;
1743 info
->maxKeyLength
= btreePtr
->maxKeyLength
;
1744 info
->treeDepth
= btreePtr
->treeDepth
;
1745 info
->numRecords
= btreePtr
->leafRecords
;
1746 info
->numNodes
= btreePtr
->totalNodes
;
1747 info
->numFreeNodes
= btreePtr
->freeNodes
;
1748 info
->lastfsync
= btreePtr
->lastfsync
;
1749 info
->keyCompareType
= btreePtr
->keyCompareType
;
1756 BTIsDirty(FCB
*filePtr
)
1758 BTreeControlBlockPtr btreePtr
;
1760 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1761 return TreeIsDirty(btreePtr
);
1764 /*-------------------------------------------------------------------------------
1765 Routine: BTFlushPath - Flush BTreeControlBlock to Header Node.
1767 Function: Brief_description_of_the_function_and_any_side_effects
1770 Input: pathPtr - pointer to path control block for B*Tree file to flush
1774 Result: noErr - success
1776 -------------------------------------------------------------------------------*/
1778 OSStatus
BTFlushPath (FCB
*filePtr
)
1781 BTreeControlBlockPtr btreePtr
;
1784 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1786 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1788 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1790 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
1792 err
= UpdateHeader (btreePtr
, false);
1798 /*-------------------------------------------------------------------------------
1799 Routine: BTReload - Reload B-tree Header Data.
1801 Function: Reload B-tree header data from disk. This is called after fsck
1802 has made repairs to the root filesystem. The filesystem is
1803 mounted read-only when BTReload is caled.
1806 Input: filePtr - the B*Tree file that needs its header updated
1810 Result: noErr - success
1812 -------------------------------------------------------------------------------*/
1815 BTReloadData(FCB
*filePtr
)
1818 BTreeControlBlockPtr btreePtr
;
1819 BlockDescriptor node
;
1820 BTHeaderRec
*header
;
1824 node
.blockHeader
= nil
;
1826 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1827 if (btreePtr
== nil
)
1828 return (fsBTInvalidFileErr
);
1830 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1832 err
= GetNode(btreePtr
, kHeaderNodeNum
, &node
);
1836 header
= (BTHeaderRec
*)((char *)node
.buffer
+ sizeof(BTNodeDescriptor
));
1837 if ((err
= VerifyHeader (filePtr
, header
)) == 0) {
1838 btreePtr
->treeDepth
= header
->treeDepth
;
1839 btreePtr
->rootNode
= header
->rootNode
;
1840 btreePtr
->leafRecords
= header
->leafRecords
;
1841 btreePtr
->firstLeafNode
= header
->firstLeafNode
;
1842 btreePtr
->lastLeafNode
= header
->lastLeafNode
;
1843 btreePtr
->maxKeyLength
= header
->maxKeyLength
;
1844 btreePtr
->totalNodes
= header
->totalNodes
;
1845 btreePtr
->freeNodes
= header
->freeNodes
;
1846 btreePtr
->btreeType
= header
->btreeType
;
1848 btreePtr
->flags
&= (~kBTHeaderDirty
);
1851 (void) ReleaseNode(btreePtr
, &node
);
1857 /*-------------------------------------------------------------------------------
1858 Routine: BTInvalidateHint - Invalidates the hint within a BTreeInterator.
1860 Function: Invalidates the hint within a BTreeInterator.
1863 Input: iterator - pointer to BTreeIterator
1865 Output: iterator - iterator with the hint.nodeNum cleared
1867 Result: noErr - success
1868 paramErr - iterator == nil
1869 -------------------------------------------------------------------------------*/
1872 OSStatus
BTInvalidateHint (BTreeIterator
*iterator
)
1874 if (iterator
== nil
)
1877 iterator
->hint
.nodeNum
= 0;
1885 /*-------------------------------------------------------------------------------
1886 Routine: BTGetLastSync
1888 Function: Returns the last time that this btree was flushed, does not include header.
1890 Input: filePtr - pointer file control block
1892 Output: lastfsync - time in seconds of last update
1894 Result: noErr - success
1895 paramErr - iterator == nil
1896 -------------------------------------------------------------------------------*/
1899 OSStatus
BTGetLastSync (FCB
*filePtr
,
1902 BTreeControlBlockPtr btreePtr
;
1905 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1907 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1909 /* Maybe instead of requiring a lock..an atomic set might be more appropriate */
1910 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
1912 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1913 M_ReturnErrorIf (lastsync
== nil
, paramErr
);
1915 *lastsync
= btreePtr
->lastfsync
;
1923 /*-------------------------------------------------------------------------------
1924 Routine: BTSetLastSync
1926 Function: Sets the last time that this btree was flushed, does not include header.
1929 Input: fcb - pointer file control block
1931 Output: lastfsync - time in seconds of last update
1933 Result: noErr - success
1934 paramErr - iterator == nil
1935 -------------------------------------------------------------------------------*/
1938 OSStatus
BTSetLastSync (FCB
*filePtr
,
1941 BTreeControlBlockPtr btreePtr
;
1944 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1946 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1948 /* Maybe instead of requiring a lock..an atomic set might be more appropriate */
1949 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
1951 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1952 M_ReturnErrorIf (lastsync
== nil
, paramErr
);
1954 btreePtr
->lastfsync
= lastsync
;
1961 OSStatus
BTHasContiguousNodes (FCB
*filePtr
)
1963 BTreeControlBlockPtr btreePtr
;
1966 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1968 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1970 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
1972 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1974 return NodesAreContiguous(FCBTOVCB(filePtr
), filePtr
, btreePtr
->nodeSize
);
1978 /*-------------------------------------------------------------------------------
1979 Routine: BTGetUserData
1981 Function: Read the user data area of the b-tree header node.
1983 -------------------------------------------------------------------------------*/
1986 BTGetUserData(FCB
*filePtr
, void * dataPtr
, int dataSize
)
1988 BTreeControlBlockPtr btreePtr
;
1989 BlockDescriptor node
;
1993 if (dataSize
> kBTreeHeaderUserBytes
)
1996 node
.blockHeader
= nil
;
1998 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1999 if (btreePtr
== nil
)
2000 return (fsBTInvalidFileErr
);
2002 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
2004 err
= GetNode(btreePtr
, kHeaderNodeNum
, &node
);
2008 offset
= (char *)node
.buffer
+ sizeof(BTNodeDescriptor
) + sizeof(BTHeaderRec
);
2009 bcopy(offset
, dataPtr
, dataSize
);
2011 (void) ReleaseNode(btreePtr
, &node
);
2017 /*-------------------------------------------------------------------------------
2018 Routine: BTSetUserData
2020 Function: Write the user data area of the b-tree header node.
2021 -------------------------------------------------------------------------------*/
2024 BTSetUserData(FCB
*filePtr
, void * dataPtr
, int dataSize
)
2026 BTreeControlBlockPtr btreePtr
;
2027 BlockDescriptor node
;
2031 if (dataSize
> kBTreeHeaderUserBytes
)
2034 node
.blockHeader
= nil
;
2036 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
2037 if (btreePtr
== nil
)
2038 return (fsBTInvalidFileErr
);
2040 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
2042 err
= GetNode(btreePtr
, kHeaderNodeNum
, &node
);
2046 ModifyBlockStart(btreePtr
->fileRefNum
, &node
);
2048 offset
= (char *)node
.buffer
+ sizeof(BTNodeDescriptor
) + sizeof(BTHeaderRec
);
2049 bcopy(dataPtr
, offset
, dataSize
);
2051 err
= UpdateNode (btreePtr
, &node
, 0, 0);