2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
25 Contains: Implementation of public interface routines for B-tree manager.
29 Written by: Gordon Sheridan and Bill Bruffey
31 Copyright: © 1992-1999 by Apple Computer, Inc., all rights reserved.
37 Other Contact: Mark Day
39 Technology: File Systems
47 Change History (most recent first):
48 <MOSXS> 9/22/99 ser Added routines BTGetLastSync and BTSetLastSync
49 <MOSXS> 6/1/99 djb Sync up with Mac OS 8.6.
50 <MOSXS> 6/30/98 djb In BTOpenPath make sure nodes are contiguous on disk (radar #2249539).
51 <MOSXS> 4/15/98 djb In BTOpenPath need to clear nodeRec.buffer if GetBlockProc fails.
52 <MOSXS> 4/11/98 djb Add RequireFileLock checking to all external entry points.
54 <MOSXS> 03/23/98 djb In BTOpenPath use kTrashBlock option when releasing the header so
55 that we get a full node when we call GetNode.
57 <CS9> 12/12/97 djb Radar #2202682, BTIterateRecord with kBTreeCurrentRecord was not
58 checking if we had a record and could call BlockMove with an
59 uninitialize source pointer (causing a bus error).
60 <CS8> 10/24/97 msd In BTIterateRecord, when moving to the previous or next record
61 and we have to move to another node, see if we need to release
62 the node about to be "shifted out" (opposite sibling of the
63 direction we need to move).
64 <CS7> 7/25/97 DSH BTSearchRecord now takes a heuristicHint, nodeNum, and tries it
65 before calling SearchBTree
66 <CS6> 7/24/97 djb GetBlockProc now take a file refnum instead of an FCB ptr.
67 <CS5> 7/22/97 djb Move trace points from BTreeWrapper.c to here.
68 <CS4> 7/21/97 djb LogEndTime now takes an error code.
69 <CS3> 7/16/97 DSH FilesInternal.i renamed FileMgrInternal.i to avoid name
71 <CS2> 5/19/97 djb Add summary traces to BTIterateRecord.
72 <CS1> 4/23/97 djb first checked in
74 <HFS7> 2/19/97 djb Enable variable sized index keys for HFS+ volumes. Added node
75 cache to support nodes larger than 512 bytes.
76 <HFS6> 1/27/97 djb Calls to InsertTree and DeleteTree are now recursive (to support
77 variable sized index keys).
78 <HFS5> 1/13/97 djb Added support for getting current record to BTIterateRecord.
79 <HFS4> 1/6/97 djb Initialize "BigKeys" attribute in BTOpen.
80 <HFS3> 1/3/97 djb Added support for large keys.
81 <HFS2> 12/23/96 djb On exit map fsBTEmptyErr and fsBTEndOfIterationErr to
82 fsBTRecordNotFoundErr.
83 <HFS1> 12/19/96 djb first checked in
85 History applicable to original Scarecrow Design:
87 <13> 10/25/96 ser Changing for new VFPI
88 <12> 10/18/96 ser Converting over VFPI changes
89 <11> 9/17/96 dkh More BTree statistics. Modified hint checks to not bail out when
90 an error is returned from GetNode.
91 <10> 9/16/96 dkh Revised BTree statistics.
92 <9> 8/23/96 dkh Remove checks for multiple paths to BTree file. Need to add
93 equivalent mechanism later.
94 <8> 6/20/96 dkh Radar #1358740. Switch from using Pools to debug MemAllocators.
95 <7> 3/14/96 jev Fix BTreeSetRecord, recordFound was not set for the case of a
96 simple replace causing the leafRecords count to get bumped even
97 though we didn't have to add a record.
98 <6> 3/1/96 prp Fix lint problems. Bug in BTSetRecord that does not initialize
100 <5> 1/22/96 dkh Add #include Memory.h
101 <4> 1/10/96 msd Use the real function names from Math64.i.
102 <3> 1/4/96 jev Fix BTItererateRecord for the condition when the iterator
103 position routine does not find the record and we are looking for
104 the next record. In such a case, if the node's forrward link is
105 non-zero, we have to keep iterating next and not return
106 fsBTEndOfIterationErr error.
107 <2> 12/7/95 dkh D10E2 build. Changed usage of Ref data type to LogicalAddress.
108 <1> 10/18/95 rst Moved from Scarecrow project.
110 <24> 7/18/95 mbb Change MoveData & ClearBytes to BlockMoveData & BlockZero.
111 <23> 1/31/95 prp GetBlockProc interface uses a 64 bit node number.
112 <22> 1/12/95 wjk Adopt Model FileSystem changes in D5.
113 <21> 11/16/94 prp Add IsItAHint routine and use it whenever hint's node number was
115 <20> 11/10/94 prp BTGetInfo name collides with the same name in FileManagerPriv.i.
116 Change it to BTGetInformation.
117 <19> 9/30/94 prp Get in sync with D2 interface changes.
118 <18> 7/22/94 wjk Convert to the new set of header files.
119 <17> 12/9/93 wjk Cleanup usage of char, Byte, int8, UInt8, etc.
120 <16> 12/2/93 wjk Move from Makefiles to BuildFiles. Fit into the ModernOS and
122 <15> 11/30/93 wjk Move from Makefiles to BuildFiles. Fit into the ModernOS and
124 <14> 9/30/93 gs Rename E_NoGetNodeProc and E_NoReleaseNodeProc to
126 <13> 8/31/93 prp Use Set64U instead of Set64.
127 <12> 8/16/93 prp In BTSearchRecord, if the input hint found the node and record,
128 set the local nodeNum variable correctly so that the resultant
129 iterator gets set correctly.
130 <11> 7/1/93 gs Fix bug in BTIterateRecord related to kBTreePrevRecord
132 <10> 6/2/93 gs Update for changes to FSErrors.h and add some comments.
133 <9> 5/24/93 gs Fix bug in BTInsert/Set/ReplaceRecord which didn't set node hint
134 properly in some cases.
135 <8> 5/24/93 gs Do NOT map fsBTEmptyErr to fsBTRecordNotFoundErr in BTSearchRecord.
136 <7> 5/24/93 gs Rename BTFlush to BTFlushPath.
137 <6> 5/21/93 gs Add hint optimization to Set/Replace routines.
138 <5> 5/10/93 gs Remove Panic from BTInitialize for small logicalEOF. Implement
139 Insert, Set, Replace, and Delete.
140 <4> 3/23/93 gs Finish BTInitialize.
141 <3> 2/8/93 gs Implement BTSearchRecord and BTIterateRecord.
142 <2> 12/8/92 gs Implement Open and Close routines.
143 <1> 11/15/92 gs first checked in
147 #include "../headers/BTreesPrivate.h"
149 #include "../headers/HFSInstrumentation.h"
152 * The amount that the BTree header leaf count can be wrong before we assume
153 * it is in an infinite loop.
155 #define kNumLeafRecSlack 10
157 //////////////////////////////////// Globals ////////////////////////////////////
160 /////////////////////////// BTree Module Entry Points ///////////////////////////
164 /*-------------------------------------------------------------------------------
165 Routine: BTOpenPath - Open a file for access as a B*Tree.
167 Function: Create BTree control block for a file, if necessary. Validates the
168 file to be sure it looks like a BTree file.
171 Input: filePtr - pointer to file to open as a B-tree
172 keyCompareProc - pointer to client's KeyCompare function
173 getBlockProc - pointer to client's GetBlock function
174 releaseBlockProc - pointer to client's ReleaseBlock function
175 setEndOfForkProc - pointer to client's SetEOF function
177 Result: noErr - success
178 paramErr - required ptr was nil
182 -------------------------------------------------------------------------------*/
184 OSStatus
BTOpenPath (FCB
*filePtr
,
185 KeyCompareProcPtr keyCompareProc
,
186 GetBlockProcPtr getBlockProc
,
187 ReleaseBlockProcPtr releaseBlockProc
,
188 SetEndOfForkProcPtr setEndOfForkProc
,
189 SetBlockSizeProcPtr setBlockSizeProc
)
192 BTreeControlBlockPtr btreePtr
;
196 LogStartTime(kTraceOpenBTree
);
198 ////////////////////// Preliminary Error Checking ///////////////////////////
200 if ( filePtr
== nil
||
201 getBlockProc
== nil
||
202 releaseBlockProc
== nil
||
203 setEndOfForkProc
== nil
||
204 setBlockSizeProc
== nil
)
209 if ( filePtr
->fcbBTCBPtr
!= nil
) // already has a BTreeCB
212 // is file large enough to contain header node?
213 if ( filePtr
->fcbEOF
< kMinNodeSize
)
214 return fsBTInvalidFileErr
; //\80\80 or E_BadHeader?
217 //////////////////////// Allocate Control Block /////////////////////////////
219 btreePtr
= (BTreeControlBlock
*) NewPtrSysClear( sizeof( BTreeControlBlock
) );
222 Panic ("\pBTOpen: no memory for btreePtr.");
226 btreePtr
->getBlockProc
= getBlockProc
;
227 btreePtr
->releaseBlockProc
= releaseBlockProc
;
228 btreePtr
->setEndOfForkProc
= setEndOfForkProc
;
229 btreePtr
->keyCompareProc
= keyCompareProc
;
231 /////////////////////////// Read Header Node ////////////////////////////////
233 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
234 btreePtr
->fileRefNum
= GetFileRefNumFromFCB(filePtr
);
235 filePtr
->fcbBTCBPtr
= (Ptr
) btreePtr
; // attach btree cb to file
237 /* The minimum node size is the physical block size */
238 nodeRec
.blockSize
= VTOHFS(btreePtr
->fileRefNum
)->hfs_phys_block_size
;
240 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
242 // it is now safe to call M_ExitOnError (err)
244 err
= setBlockSizeProc (btreePtr
->fileRefNum
, nodeRec
.blockSize
, 1);
248 err
= getBlockProc (btreePtr
->fileRefNum
,
254 nodeRec
.buffer
= nil
;
255 nodeRec
.blockHeader
= nil
;
256 Panic("\pBTOpen: getNodeProc returned error getting header node.");
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 // ignore header->clumpSize; //\80\80 rename this field?
283 btreePtr
->btreeType
= header
->btreeType
;
285 btreePtr
->attributes
= header
->attributes
;
287 if ( btreePtr
->maxKeyLength
> 40 )
288 btreePtr
->attributes
|= (kBTBigKeysMask
+ kBTVariableIndexKeysMask
); //\80\80 we need a way to save these attributes
290 /////////////////////// Initialize dynamic fields ///////////////////////////
292 btreePtr
->version
= kBTreeVersion
;
294 btreePtr
->writeCount
= 1;
296 btreePtr
->numGetNodes
= 1; // for earlier call to getNodeProc
298 /////////////////////////// Check Header Node ///////////////////////////////
300 //\80\80 set kBadClose attribute bit, and UpdateNode
302 // if nodeSize matches then we don't need to release, just CheckNode
304 /* b-tree node size must be at least as big as the physical block size */
305 if (btreePtr
->nodeSize
< nodeRec
.blockSize
) {
306 err
= fsBTBadNodeSize
;
310 if ( btreePtr
->nodeSize
== nodeRec
.blockSize
)
312 err
= CheckNode (btreePtr
, nodeRec
.buffer
);
314 VTOVCB(btreePtr
->fileRefNum
)->vcbFlags
|= kHFS_DamagedVolume
;
319 err
= setBlockSizeProc (btreePtr
->fileRefNum
, btreePtr
->nodeSize
, 32); //\80\80 we should try and get this down to 8
323 * Need to use kTrashBlock option to force the
324 * buffer cache to read the entire node
326 err
= releaseBlockProc(btreePtr
->fileRefNum
, &nodeRec
, kTrashBlock
);
329 err
= GetNode (btreePtr
, kHeaderNodeNum
, &nodeRec
); // calls CheckNode...
333 //\80\80 total nodes * node size <= LEOF?
336 err
= ReleaseNode (btreePtr
, &nodeRec
);
340 * Under Mac OS, b-tree nodes can be non-contiguous on disk when the
341 * allocation block size is smaller than the b-tree node size.
343 if ( !NodesAreContiguous(FCBTOVCB(filePtr
), filePtr
, btreePtr
->nodeSize
) )
344 return fsBTInvalidNodeErr
;
346 //////////////////////////////// Success ////////////////////////////////////
348 //\80\80 align LEOF to multiple of node size? - just on close
350 LogEndTime(kTraceOpenBTree
, noErr
);
355 /////////////////////// Error - Clean up and Exit ///////////////////////////
359 filePtr
->fcbBTCBPtr
= nil
;
360 (void) ReleaseNode (btreePtr
, &nodeRec
);
361 DisposePtr( (Ptr
) btreePtr
);
363 LogEndTime(kTraceOpenBTree
, err
);
370 /*-------------------------------------------------------------------------------
371 Routine: BTClosePath - Flush BTree Header and Deallocate Memory for BTree.
373 Function: Flush the BTreeControlBlock fields to header node, and delete BTree control
374 block and key descriptor associated with the file if filePtr is last
375 path of type kBTreeType ('btre').
378 Input: filePtr - pointer to file to delete BTree control block for.
380 Result: noErr - success
383 -------------------------------------------------------------------------------*/
385 OSStatus
BTClosePath (FCB
*filePtr
)
388 BTreeControlBlockPtr btreePtr
;
390 LogStartTime(kTraceCloseBTree
);
392 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
395 return fsBTInvalidFileErr
;
397 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
399 ////////////////////// Check for other BTree Paths //////////////////////////
401 btreePtr
->attributes
&= ~kBTBadCloseMask
; // clear "bad close" attribute bit
402 err
= UpdateHeader (btreePtr
, true);
405 DisposePtr( (Ptr
) btreePtr
);
406 filePtr
->fcbBTCBPtr
= nil
;
408 LogEndTime(kTraceCloseBTree
, noErr
);
412 /////////////////////// Error - Clean Up and Exit ///////////////////////////
416 LogEndTime(kTraceCloseBTree
, err
);
423 /*-------------------------------------------------------------------------------
424 Routine: BTSearchRecord - Search BTree for a record with a matching key.
426 Function: Search for position in B*Tree indicated by searchKey. If a valid node hint
427 is provided, it will be searched first, then SearchTree will be called.
428 If a BTreeIterator is provided, it will be set to the position found as
429 a result of the search. If a record exists at that position, and a BufferDescriptor
430 is supplied, the record will be copied to the buffer (as much as will fit),
431 and recordLen will be set to the length of the record.
433 If an error other than fsBTRecordNotFoundErr occurs, the BTreeIterator, if any,
434 is invalidated, and recordLen is set to 0.
437 Input: pathPtr - pointer to path for BTree file.
438 searchKey - pointer to search key to match.
439 hintPtr - pointer to hint (may be nil)
441 Output: record - pointer to BufferDescriptor containing record
442 recordLen - length of data at recordPtr
443 iterator - pointer to BTreeIterator indicating position result of search
445 Result: noErr - success, record contains copy of record found
446 fsBTRecordNotFoundErr - record was not found, no data copied
447 fsBTInvalidFileErr - no BTreeControlBlock is allocated for the fork
448 fsBTInvalidKeyLengthErr -
450 -------------------------------------------------------------------------------*/
452 OSStatus
BTSearchRecord (FCB
*filePtr
,
453 BTreeIterator
*searchIterator
,
454 UInt32 heuristicHint
,
455 FSBufferDescriptor
*record
,
457 BTreeIterator
*resultIterator
)
460 BTreeControlBlockPtr btreePtr
;
461 TreePathTable treePathTable
;
463 BlockDescriptor node
;
472 LogStartTime(kTraceSearchBTree
);
474 if (filePtr
== nil
) return paramErr
;
475 if (searchIterator
== nil
) return paramErr
;
477 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
478 if (btreePtr
== nil
) return fsBTInvalidFileErr
;
480 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
484 ////////////////////////////// Take A Hint //////////////////////////////////
486 err
= IsItAHint (btreePtr
, searchIterator
, &validHint
);
491 nodeNum
= searchIterator
->hint
.nodeNum
;
493 err
= GetNode (btreePtr
, nodeNum
, &node
);
496 if ( ((BTNodeDescriptor
*) node
.buffer
)->kind
== kBTLeafNode
&&
497 ((BTNodeDescriptor
*) node
.buffer
)->numRecords
> 0 )
499 foundRecord
= SearchNode (btreePtr
, node
.buffer
, &searchIterator
->key
, &index
);
501 //\80\80 if !foundRecord, we could still skip tree search if ( 0 < index < numRecords )
504 if (foundRecord
== false)
506 err
= ReleaseNode (btreePtr
, &node
);
511 ++btreePtr
->numValidHints
;
515 if( foundRecord
== false )
516 (void) BTInvalidateHint( searchIterator
);
519 ////////////////////////////// Try the heuristicHint //////////////////////////////////
521 if ( (foundRecord
== false) && (heuristicHint
!= kInvalidMRUCacheKey
) && (nodeNum
!= heuristicHint
) )
523 LogStartTime(kHeuristicHint
);
524 nodeNum
= heuristicHint
;
526 err
= GetNode (btreePtr
, nodeNum
, &node
);
529 if ( ((BTNodeDescriptor
*) node
.buffer
)->kind
== kBTLeafNode
&&
530 ((BTNodeDescriptor
*) node
.buffer
)->numRecords
> 0 )
532 foundRecord
= SearchNode (btreePtr
, node
.buffer
, &searchIterator
->key
, &index
);
535 if (foundRecord
== false)
537 err
= ReleaseNode (btreePtr
, &node
);
541 LogEndTime(kHeuristicHint
, (foundRecord
== false));
544 //////////////////////////// Search The Tree ////////////////////////////////
546 if (foundRecord
== false)
548 err
= SearchTree ( btreePtr
, &searchIterator
->key
, treePathTable
, &nodeNum
, &node
, &index
);
551 case noErr
: foundRecord
= true; break;
552 case fsBTRecordNotFoundErr
: break;
553 default: goto ErrorExit
;
558 //////////////////////////// Get the Record /////////////////////////////////
560 if (foundRecord
== true)
562 //XXX Should check for errors! Or BlockMove could choke on recordPtr!!!
563 GetRecordByIndex (btreePtr
, node
.buffer
, index
, &keyPtr
, &recordPtr
, &len
);
565 if (recordLen
!= nil
) *recordLen
= len
;
569 ByteCount recordSize
;
571 recordSize
= record
->itemCount
* record
->itemSize
;
573 if (len
> recordSize
) len
= recordSize
;
575 BlockMoveData (recordPtr
, record
->bufferAddress
, len
);
580 /////////////////////// Success - Update Iterator ///////////////////////////
582 if (resultIterator
!= nil
)
584 resultIterator
->hint
.writeCount
= btreePtr
->writeCount
;
585 resultIterator
->hint
.nodeNum
= nodeNum
;
586 resultIterator
->hint
.index
= index
;
588 resultIterator
->hint
.reserved1
= 0;
589 resultIterator
->hint
.reserved2
= 0;
590 resultIterator
->version
= 0;
591 resultIterator
->reserved
= 0;
593 // copy the key in the BTree when found rather than searchIterator->key to get proper case/diacriticals
594 if (foundRecord
== true)
595 BlockMoveData ((Ptr
)keyPtr
, (Ptr
)&resultIterator
->key
, CalcKeySize(btreePtr
, keyPtr
));
597 BlockMoveData ((Ptr
)&searchIterator
->key
, (Ptr
)&resultIterator
->key
, CalcKeySize(btreePtr
, &searchIterator
->key
));
600 err
= ReleaseNode (btreePtr
, &node
);
603 LogEndTime(kTraceSearchBTree
, (foundRecord
== false));
605 if (foundRecord
== false) return fsBTRecordNotFoundErr
;
609 /////////////////////// Error - Clean Up and Exit ///////////////////////////
613 if (recordLen
!= nil
)
616 if (resultIterator
!= nil
)
618 resultIterator
->hint
.writeCount
= 0;
619 resultIterator
->hint
.nodeNum
= 0;
620 resultIterator
->hint
.index
= 0;
621 resultIterator
->hint
.reserved1
= 0;
622 resultIterator
->hint
.reserved2
= 0;
624 resultIterator
->version
= 0;
625 resultIterator
->reserved
= 0;
626 resultIterator
->key
.length16
= 0; // zero out two bytes to cover both types of keys
629 if ( err
== fsBTEmptyErr
)
630 err
= fsBTRecordNotFoundErr
;
632 LogEndTime(kTraceSearchBTree
, err
);
639 /*-------------------------------------------------------------------------------
640 Routine: BTIterateRecord - Find the first, next, previous, or last record.
642 Function: Find the first, next, previous, or last record in the BTree
644 Input: pathPtr - pointer to path iterate records for.
645 operation - iteration operation (first,next,prev,last)
646 iterator - pointer to iterator indicating start position
648 Output: iterator - iterator is updated to indicate new position
649 newKeyPtr - pointer to buffer to copy key found by iteration
650 record - pointer to buffer to copy record found by iteration
651 recordLen - length of record
653 Result: noErr - success
655 -------------------------------------------------------------------------------*/
657 OSStatus
BTIterateRecord (FCB
*filePtr
,
658 BTreeIterationOperation operation
,
659 BTreeIterator
*iterator
,
660 FSBufferDescriptor
*record
,
664 BTreeControlBlockPtr btreePtr
;
672 BlockDescriptor left
, node
, right
;
676 LogStartTime(kTraceGetBTreeRecord
);
678 ////////////////////////// Priliminary Checks ///////////////////////////////
690 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
693 return fsBTInvalidFileErr
; //\80\80 handle properly
696 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
698 if ((operation
!= kBTreeFirstRecord
) &&
699 (operation
!= kBTreeNextRecord
) &&
700 (operation
!= kBTreeCurrentRecord
) &&
701 (operation
!= kBTreePrevRecord
) &&
702 (operation
!= kBTreeLastRecord
))
704 err
= fsInvalidIterationMovmentErr
;
708 /////////////////////// Find First or Last Record ///////////////////////////
710 if ((operation
== kBTreeFirstRecord
) || (operation
== kBTreeLastRecord
))
712 if (operation
== kBTreeFirstRecord
) nodeNum
= btreePtr
->firstLeafNode
;
713 else nodeNum
= btreePtr
->lastLeafNode
;
721 err
= GetNode (btreePtr
, nodeNum
, &node
);
724 if ( ((NodeDescPtr
) node
.buffer
)->kind
!= kBTLeafNode
||
725 ((NodeDescPtr
) node
.buffer
)->numRecords
<= 0 )
727 err
= ReleaseNode (btreePtr
, &node
);
730 err
= fsBTInvalidNodeErr
;
731 MARK_VOLUMEDAMAGED(filePtr
);
735 if (operation
== kBTreeFirstRecord
) index
= 0;
736 else index
= ((BTNodeDescriptor
*) node
.buffer
)->numRecords
- 1;
738 goto CopyData
; //\80\80 is there a cleaner way?
742 //////////////////////// Find Iterator Position /////////////////////////////
744 err
= FindIteratorPosition (btreePtr
, iterator
,
745 &left
, &node
, &right
, &nodeNum
, &index
, &foundRecord
);
749 ///////////////////// Find Next Or Previous Record //////////////////////////
751 if (operation
== kBTreePrevRecord
)
759 if (left
.buffer
== nil
)
761 nodeNum
= ((NodeDescPtr
) node
.buffer
)->bLink
;
764 err
= GetNode (btreePtr
, nodeNum
, &left
);
767 err
= fsBTStartOfIterationErr
;
771 // Before we stomp on "right", we'd better release it if needed
772 if (right
.buffer
!= nil
) {
773 err
= ReleaseNode(btreePtr
, &right
);
779 index
= ((NodeDescPtr
) node
.buffer
)->numRecords
-1;
782 else if (operation
== kBTreeNextRecord
)
784 if ((foundRecord
!= true) &&
785 (((NodeDescPtr
) node
.buffer
)->fLink
== 0) &&
786 (index
== ((NodeDescPtr
) node
.buffer
)->numRecords
))
788 err
= fsBTEndOfIterationErr
;
792 // we did not find the record but the index is already positioned correctly
793 if ((foundRecord
== false) && (index
!= ((NodeDescPtr
) node
.buffer
)->numRecords
))
796 // we found the record OR we have to look in the next node
797 if (index
< ((NodeDescPtr
) node
.buffer
)->numRecords
-1)
803 if (right
.buffer
== nil
)
805 nodeNum
= ((NodeDescPtr
) node
.buffer
)->fLink
;
808 err
= GetNode (btreePtr
, nodeNum
, &right
);
811 err
= fsBTEndOfIterationErr
;
815 // Before we stomp on "left", we'd better release it if needed
816 if (left
.buffer
!= nil
) {
817 err
= ReleaseNode(btreePtr
, &left
);
826 else // operation == kBTreeCurrentRecord
828 // make sure we have something... <CS9>
829 if ((foundRecord
!= true) &&
830 (index
>= ((NodeDescPtr
) node
.buffer
)->numRecords
))
832 err
= fsBTEndOfIterationErr
;
837 //////////////////// Copy Record And Update Iterator ////////////////////////
841 // added check for errors <CS9>
842 err
= GetRecordByIndex (btreePtr
, node
.buffer
, index
, &keyPtr
, &recordPtr
, &len
);
845 if (recordLen
!= nil
)
850 ByteCount recordSize
;
852 recordSize
= record
->itemCount
* record
->itemSize
;
854 if (len
> recordSize
) len
= recordSize
;
856 BlockMoveData (recordPtr
, record
->bufferAddress
, len
);
859 if (iterator
!= nil
) // first & last do not require iterator
861 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
862 iterator
->hint
.nodeNum
= nodeNum
;
863 iterator
->hint
.index
= index
;
864 iterator
->hint
.reserved1
= 0;
865 iterator
->hint
.reserved2
= 0;
867 iterator
->version
= 0;
868 iterator
->reserved
= 0;
871 * Check for infinite loops by making sure we do not
872 * process more leaf records, than can possibly be (or the BTree header
873 * is seriously damaged)....a brute force method.
875 if ((operation
== kBTreeFirstRecord
) || (operation
== kBTreeLastRecord
))
876 iterator
->hitCount
= 1;
877 else if (operation
!= kBTreeCurrentRecord
)
878 iterator
->hitCount
+= 1;
879 /* Always use the highest max, in case the grows while iterating */
880 iterator
->maxLeafRecs
= max(btreePtr
->leafRecords
, iterator
->maxLeafRecs
);
883 if (iterator
->hitCount
> iterator
->maxLeafRecs
+ kNumLeafRecSlack
)
885 err
= fsBTInvalidNodeErr
;
886 MARK_VOLUMEDAMAGED(filePtr
);
891 BlockMoveData ((Ptr
)keyPtr
, (Ptr
)&iterator
->key
, CalcKeySize(btreePtr
, keyPtr
));
895 ///////////////////////////// Release Nodes /////////////////////////////////
897 err
= ReleaseNode (btreePtr
, &node
);
900 if (left
.buffer
!= nil
)
902 err
= ReleaseNode (btreePtr
, &left
);
906 if (right
.buffer
!= nil
)
908 err
= ReleaseNode (btreePtr
, &right
);
912 LogEndTime(kTraceGetBTreeRecord
, noErr
);
916 /////////////////////// Error - Clean Up and Exit ///////////////////////////
920 (void) ReleaseNode (btreePtr
, &left
);
921 (void) ReleaseNode (btreePtr
, &node
);
922 (void) ReleaseNode (btreePtr
, &right
);
924 if (recordLen
!= nil
)
929 iterator
->hint
.writeCount
= 0;
930 iterator
->hint
.nodeNum
= 0;
931 iterator
->hint
.index
= 0;
932 iterator
->hint
.reserved1
= 0;
933 iterator
->hint
.reserved2
= 0;
935 iterator
->version
= 0;
936 iterator
->reserved
= 0;
937 iterator
->key
.length16
= 0;
940 if ( err
== fsBTEmptyErr
|| err
== fsBTEndOfIterationErr
)
941 err
= fsBTRecordNotFoundErr
;
943 LogEndTime(kTraceGetBTreeRecord
, err
);
949 /*-------------------------------------------------------------------------------
950 Routine: BTIterateRecords
952 Function: Find a series of records
954 Input: filePtr - b-tree file
955 operation - iteration operation (first,next,prev,last)
956 iterator - pointer to iterator indicating start position
957 callBackProc - pointer to routince to process a record
958 callBackState - pointer to state data (used by callBackProc)
960 Output: iterator - iterator is updated to indicate new position
962 Result: noErr - success
964 -------------------------------------------------------------------------------*/
967 BTIterateRecords(FCB
*filePtr
, BTreeIterationOperation operation
, BTreeIterator
*iterator
,
968 IterateCallBackProcPtr callBackProc
, void * callBackState
)
971 BTreeControlBlockPtr btreePtr
;
977 BlockDescriptor left
, node
, right
;
981 ////////////////////////// Priliminary Checks ///////////////////////////////
987 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
989 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
991 if ((operation
!= kBTreeFirstRecord
) &&
992 (operation
!= kBTreeNextRecord
) &&
993 (operation
!= kBTreeCurrentRecord
) &&
994 (operation
!= kBTreePrevRecord
) &&
995 (operation
!= kBTreeLastRecord
))
997 err
= fsInvalidIterationMovmentErr
;
1001 /////////////////////// Find First or Last Record ///////////////////////////
1003 if ((operation
== kBTreeFirstRecord
) || (operation
== kBTreeLastRecord
))
1005 if (operation
== kBTreeFirstRecord
)
1006 nodeNum
= btreePtr
->firstLeafNode
;
1008 nodeNum
= btreePtr
->lastLeafNode
;
1016 err
= GetNode(btreePtr
, nodeNum
, &node
);
1019 if ( ((NodeDescPtr
)node
.buffer
)->kind
!= kBTLeafNode
||
1020 ((NodeDescPtr
)node
.buffer
)->numRecords
<= 0 )
1022 err
= ReleaseNode(btreePtr
, &node
);
1025 err
= fsBTInvalidNodeErr
;
1026 MARK_VOLUMEDAMAGED(filePtr
);
1030 if (operation
== kBTreeFirstRecord
)
1033 index
= ((BTNodeDescriptor
*) node
.buffer
)->numRecords
- 1;
1038 //////////////////////// Find Iterator Position /////////////////////////////
1040 err
= FindIteratorPosition(btreePtr
, iterator
, &left
, &node
, &right
,
1041 &nodeNum
, &index
, &foundRecord
);
1045 ///////////////////// Find Next Or Previous Record //////////////////////////
1047 if (operation
== kBTreePrevRecord
)
1055 if (left
.buffer
== nil
)
1057 nodeNum
= ((NodeDescPtr
) node
.buffer
)->bLink
;
1060 err
= GetNode(btreePtr
, nodeNum
, &left
);
1063 err
= fsBTStartOfIterationErr
;
1067 // Before we stomp on "right", we'd better release it if needed
1068 if (right
.buffer
!= nil
) {
1069 err
= ReleaseNode(btreePtr
, &right
);
1075 index
= ((NodeDescPtr
) node
.buffer
)->numRecords
-1;
1078 else if (operation
== kBTreeNextRecord
)
1080 if ((foundRecord
!= true) &&
1081 (((NodeDescPtr
)node
.buffer
)->fLink
== 0) &&
1082 (index
== ((NodeDescPtr
)node
.buffer
)->numRecords
))
1084 err
= fsBTEndOfIterationErr
;
1088 // we did not find the record but the index is already positioned correctly
1089 if ((foundRecord
== false) && (index
!= ((NodeDescPtr
)node
.buffer
)->numRecords
))
1092 // we found the record OR we have to look in the next node
1093 if (index
< ((NodeDescPtr
)node
.buffer
)->numRecords
-1)
1099 if (right
.buffer
== nil
)
1101 nodeNum
= ((NodeDescPtr
)node
.buffer
)->fLink
;
1104 err
= GetNode(btreePtr
, nodeNum
, &right
);
1107 err
= fsBTEndOfIterationErr
;
1111 // Before we stomp on "left", we'd better release it if needed
1112 if (left
.buffer
!= nil
) {
1113 err
= ReleaseNode(btreePtr
, &left
);
1122 else // operation == kBTreeCurrentRecord
1124 // make sure we have something... <CS9>
1125 if ((foundRecord
!= true) &&
1126 (index
>= ((NodeDescPtr
)node
.buffer
)->numRecords
))
1128 err
= fsBTEndOfIterationErr
;
1133 //////////////////// Process Records Using Callback ////////////////////////
1136 err
= GetRecordByIndex(btreePtr
, node
.buffer
, index
, &keyPtr
, &recordPtr
, &len
);
1139 if (callBackProc(keyPtr
, recordPtr
, len
, callBackState
) == 0)
1142 if ((index
+1) < ((NodeDescPtr
)node
.buffer
)->numRecords
) {
1145 if (right
.buffer
== nil
)
1147 nodeNum
= ((NodeDescPtr
)node
.buffer
)->fLink
;
1150 err
= GetNode(btreePtr
, nodeNum
, &right
);
1153 err
= fsBTEndOfIterationErr
;
1157 // Before we stomp on "left", we'd better release it if needed
1158 if (left
.buffer
!= nil
) {
1159 err
= ReleaseNode(btreePtr
, &left
);
1167 err
= GetRecordByIndex(btreePtr
, node
.buffer
, index
,
1168 &keyPtr
, &recordPtr
, &len
);
1172 ///////////////// Update Iterator to Last Item Processed /////////////////////
1175 if (iterator
!= nil
) // first & last have optional iterator
1177 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
1178 iterator
->hint
.nodeNum
= nodeNum
;
1179 iterator
->hint
.index
= index
;
1180 iterator
->version
= 0;
1182 BlockMoveData((Ptr
)keyPtr
, (Ptr
)&iterator
->key
, CalcKeySize(btreePtr
, keyPtr
));
1187 ///////////////////////////// Release Nodes /////////////////////////////////
1189 err
= ReleaseNode(btreePtr
, &node
);
1192 if (left
.buffer
!= nil
)
1194 err
= ReleaseNode(btreePtr
, &left
);
1198 if (right
.buffer
!= nil
)
1200 err
= ReleaseNode(btreePtr
, &right
);
1206 /////////////////////// Error - Clean Up and Exit ///////////////////////////
1210 (void) ReleaseNode(btreePtr
, &left
);
1211 (void) ReleaseNode(btreePtr
, &node
);
1212 (void) ReleaseNode(btreePtr
, &right
);
1214 if (iterator
!= nil
)
1216 iterator
->hint
.writeCount
= 0;
1217 iterator
->hint
.nodeNum
= 0;
1218 iterator
->hint
.index
= 0;
1219 iterator
->version
= 0;
1220 iterator
->key
.length16
= 0;
1223 if ( err
== fsBTEmptyErr
|| err
== fsBTEndOfIterationErr
)
1224 err
= fsBTRecordNotFoundErr
;
1230 //////////////////////////////// BTInsertRecord /////////////////////////////////
1232 OSStatus
BTInsertRecord (FCB
*filePtr
,
1233 BTreeIterator
*iterator
,
1234 FSBufferDescriptor
*record
,
1238 BTreeControlBlockPtr btreePtr
;
1239 TreePathTable treePathTable
;
1241 BlockDescriptor nodeRec
;
1242 UInt32 insertNodeNum
;
1247 ////////////////////////// Priliminary Checks ///////////////////////////////
1249 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
1251 err
= CheckInsertParams (filePtr
, iterator
, record
, recordLen
);
1255 LogStartTime(kTraceInsertBTreeRecord
);
1257 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1259 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1262 ///////////////////////// Find Insert Position //////////////////////////////
1264 // always call SearchTree for Insert
1265 err
= SearchTree (btreePtr
, &iterator
->key
, treePathTable
, &insertNodeNum
, &nodeRec
, &index
);
1267 switch (err
) // set/replace/insert decision point
1269 case noErr
: err
= fsBTDuplicateRecordErr
;
1272 case fsBTRecordNotFoundErr
: break;
1274 case fsBTEmptyErr
: // if tree empty add 1st leaf node
1276 if (btreePtr
->freeNodes
== 0)
1278 err
= ExtendBTree (btreePtr
, btreePtr
->totalNodes
+ 1);
1279 M_ExitOnError (err
);
1282 err
= AllocateNode (btreePtr
, &insertNodeNum
);
1283 M_ExitOnError (err
);
1285 err
= GetNewNode (btreePtr
, insertNodeNum
, &nodeRec
);
1286 M_ExitOnError (err
);
1288 ((NodeDescPtr
)nodeRec
.buffer
)->kind
= kBTLeafNode
;
1289 ((NodeDescPtr
)nodeRec
.buffer
)->height
= 1;
1291 recordFit
= InsertKeyRecord (btreePtr
, nodeRec
.buffer
, 0,
1292 &iterator
->key
, KeyLength(btreePtr
, &iterator
->key
),
1293 record
->bufferAddress
, recordLen
);
1294 if (recordFit
!= true)
1296 err
= fsBTRecordTooLargeErr
;
1300 err
= UpdateNode (btreePtr
, &nodeRec
, 0, kLockTransaction
);
1301 M_ExitOnError (err
);
1303 // update BTreeControlBlock
1304 btreePtr
->treeDepth
= 1;
1305 btreePtr
->rootNode
= insertNodeNum
;
1306 btreePtr
->firstLeafNode
= insertNodeNum
;
1307 btreePtr
->lastLeafNode
= insertNodeNum
;
1308 M_BTreeHeaderDirty (btreePtr
);
1312 default: goto ErrorExit
;
1317 recordFit
= InsertKeyRecord (btreePtr
, nodeRec
.buffer
, index
,
1318 &iterator
->key
, KeyLength(btreePtr
, &iterator
->key
),
1319 record
->bufferAddress
, recordLen
);
1320 if (recordFit
== true)
1322 err
= UpdateNode (btreePtr
, &nodeRec
, 0, kLockTransaction
);
1323 M_ExitOnError (err
);
1329 /////////////////////// Extend File If Necessary ////////////////////////////
1331 nodesNeeded
= btreePtr
->treeDepth
+ 1 - btreePtr
->freeNodes
; //\80\80 math limit
1332 if (nodesNeeded
> 0)
1334 nodesNeeded
+= btreePtr
->totalNodes
;
1335 if (nodesNeeded
> CalcMapBits (btreePtr
)) // we'll need to add a map node too!
1338 err
= ExtendBTree (btreePtr
, nodesNeeded
);
1339 M_ExitOnError (err
);
1342 // no need to delete existing record
1344 err
= InsertTree (btreePtr
, treePathTable
, &iterator
->key
, record
->bufferAddress
,
1345 recordLen
, &nodeRec
, index
, 1, kInsertRecord
, &insertNodeNum
);
1346 M_ExitOnError (err
);
1349 //////////////////////////////// Success ////////////////////////////////////
1352 ++btreePtr
->writeCount
;
1353 ++btreePtr
->leafRecords
;
1354 M_BTreeHeaderDirty (btreePtr
);
1357 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
1358 iterator
->hint
.nodeNum
= insertNodeNum
;
1359 iterator
->hint
.index
= 0; // unused
1360 iterator
->hint
.reserved1
= 0;
1361 iterator
->hint
.reserved2
= 0;
1363 LogEndTime(kTraceInsertBTreeRecord
, noErr
);
1368 ////////////////////////////// Error Exit ///////////////////////////////////
1372 (void) ReleaseNode (btreePtr
, &nodeRec
);
1374 iterator
->hint
.writeCount
= 0;
1375 iterator
->hint
.nodeNum
= 0;
1376 iterator
->hint
.index
= 0;
1377 iterator
->hint
.reserved1
= 0;
1378 iterator
->hint
.reserved2
= 0;
1380 if (err
== fsBTEmptyErr
)
1381 err
= fsBTRecordNotFoundErr
;
1383 LogEndTime(kTraceInsertBTreeRecord
, err
);
1389 //////////////////////////////// BTReplaceRecord ////////////////////////////////
1391 OSStatus
BTReplaceRecord (FCB
*filePtr
,
1392 BTreeIterator
*iterator
,
1393 FSBufferDescriptor
*record
,
1397 BTreeControlBlockPtr btreePtr
;
1398 TreePathTable treePathTable
;
1400 BlockDescriptor nodeRec
;
1401 UInt32 insertNodeNum
;
1407 ////////////////////////// Priliminary Checks ///////////////////////////////
1409 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
1411 err
= CheckInsertParams (filePtr
, iterator
, record
, recordLen
);
1415 LogStartTime(kTraceReplaceBTreeRecord
);
1417 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1419 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1421 ////////////////////////////// Take A Hint //////////////////////////////////
1423 err
= IsItAHint (btreePtr
, iterator
, &validHint
);
1424 M_ExitOnError (err
);
1428 insertNodeNum
= iterator
->hint
.nodeNum
;
1430 err
= GetNode (btreePtr
, insertNodeNum
, &nodeRec
);
1433 err
= TrySimpleReplace (btreePtr
, nodeRec
.buffer
, iterator
, record
, recordLen
, &recordFit
);
1434 M_ExitOnError (err
);
1438 err
= UpdateNode (btreePtr
, &nodeRec
, 0, 0);
1439 M_ExitOnError (err
);
1441 ++btreePtr
->numValidHints
;
1447 (void) BTInvalidateHint( iterator
);
1450 err
= ReleaseNode (btreePtr
, &nodeRec
);
1451 M_ExitOnError (err
);
1455 (void) BTInvalidateHint( iterator
);
1460 ////////////////////////////// Get A Clue ///////////////////////////////////
1462 err
= SearchTree (btreePtr
, &iterator
->key
, treePathTable
, &insertNodeNum
, &nodeRec
, &index
);
1463 M_ExitOnError (err
); // record must exit for Replace
1465 // optimization - if simple replace will work then don't extend btree
1466 // \80\80 if we tried this before, and failed because it wouldn't fit then we shouldn't try this again...
1468 err
= TrySimpleReplace (btreePtr
, nodeRec
.buffer
, iterator
, record
, recordLen
, &recordFit
);
1469 M_ExitOnError (err
);
1473 err
= UpdateNode (btreePtr
, &nodeRec
, 0, 0);
1474 M_ExitOnError (err
);
1480 //////////////////////////// Make Some Room /////////////////////////////////
1482 nodesNeeded
= btreePtr
->treeDepth
+ 1 - btreePtr
->freeNodes
; //\80\80 math limit
1483 if (nodesNeeded
> 0)
1485 nodesNeeded
+= btreePtr
->totalNodes
;
1486 if (nodesNeeded
> CalcMapBits (btreePtr
)) // we'll need to add a map node too!
1489 err
= ExtendBTree (btreePtr
, nodesNeeded
);
1490 M_ExitOnError (err
);
1494 DeleteRecord (btreePtr
, nodeRec
.buffer
, index
); // delete existing key/record
1496 err
= InsertTree (btreePtr
, treePathTable
, &iterator
->key
, record
->bufferAddress
,
1497 recordLen
, &nodeRec
, index
, 1, kReplaceRecord
, &insertNodeNum
);
1498 M_ExitOnError (err
);
1500 ++btreePtr
->writeCount
; /* writeCount changes only if the tree structure changed */
1504 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
1505 iterator
->hint
.nodeNum
= insertNodeNum
;
1506 iterator
->hint
.index
= 0; // unused
1507 iterator
->hint
.reserved1
= 0;
1508 iterator
->hint
.reserved2
= 0;
1510 LogEndTime(kTraceReplaceBTreeRecord
, noErr
);
1515 ////////////////////////////// Error Exit ///////////////////////////////////
1519 (void) ReleaseNode (btreePtr
, &nodeRec
);
1521 iterator
->hint
.writeCount
= 0;
1522 iterator
->hint
.nodeNum
= 0;
1523 iterator
->hint
.index
= 0;
1524 iterator
->hint
.reserved1
= 0;
1525 iterator
->hint
.reserved2
= 0;
1528 LogEndTime(kTraceReplaceBTreeRecord
, err
);
1535 //////////////////////////////// BTDeleteRecord /////////////////////////////////
1537 OSStatus
BTDeleteRecord (FCB
*filePtr
,
1538 BTreeIterator
*iterator
)
1541 BTreeControlBlockPtr btreePtr
;
1542 TreePathTable treePathTable
;
1543 BlockDescriptor nodeRec
;
1547 LogStartTime(kTraceDeleteBTreeRecord
);
1549 ////////////////////////// Priliminary Checks ///////////////////////////////
1551 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
1553 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1554 M_ReturnErrorIf (iterator
== nil
, paramErr
);
1556 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1557 if (btreePtr
== nil
)
1559 err
= fsBTInvalidFileErr
;
1563 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1566 /////////////////////////////// Find Key ////////////////////////////////////
1568 //\80\80 check hint for simple delete case (index > 0, numRecords > 2)
1570 err
= SearchTree (btreePtr
, &iterator
->key
, treePathTable
, &nodeNum
, &nodeRec
, &index
);
1571 M_ExitOnError (err
); // record must exit for Delete
1574 ///////////////////////////// Delete Record /////////////////////////////////
1576 err
= DeleteTree (btreePtr
, treePathTable
, &nodeRec
, index
, 1);
1577 M_ExitOnError (err
);
1579 ++btreePtr
->writeCount
;
1580 --btreePtr
->leafRecords
;
1581 M_BTreeHeaderDirty (btreePtr
);
1583 iterator
->hint
.nodeNum
= 0;
1585 LogEndTime(kTraceDeleteBTreeRecord
, noErr
);
1589 ////////////////////////////// Error Exit ///////////////////////////////////
1592 (void) ReleaseNode (btreePtr
, &nodeRec
);
1594 LogEndTime(kTraceDeleteBTreeRecord
, err
);
1601 OSStatus
BTGetInformation (FCB
*filePtr
,
1603 BTreeInfoRec
*info
)
1605 #pragma unused (version)
1607 BTreeControlBlockPtr btreePtr
;
1610 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1612 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1616 * This should not require the whole tree to be locked, just maybe the BTreeControlBlockPtr
1618 * REQUIRE_FILE_LOCK(btreePtr->fileRefNum, true);
1621 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1622 M_ReturnErrorIf (info
== nil
, paramErr
);
1624 //\80\80 check version?
1626 info
->nodeSize
= btreePtr
->nodeSize
;
1627 info
->maxKeyLength
= btreePtr
->maxKeyLength
;
1628 info
->treeDepth
= btreePtr
->treeDepth
;
1629 info
->numRecords
= btreePtr
->leafRecords
;
1630 info
->numNodes
= btreePtr
->totalNodes
;
1631 info
->numFreeNodes
= btreePtr
->freeNodes
;
1632 info
->lastfsync
= btreePtr
->lastfsync
;
1640 /*-------------------------------------------------------------------------------
1641 Routine: BTFlushPath - Flush BTreeControlBlock to Header Node.
1643 Function: Brief_description_of_the_function_and_any_side_effects
1646 Input: pathPtr - pointer to path control block for B*Tree file to flush
1650 Result: noErr - success
1652 -------------------------------------------------------------------------------*/
1654 OSStatus
BTFlushPath (FCB
*filePtr
)
1657 BTreeControlBlockPtr btreePtr
;
1660 LogStartTime(kTraceFlushBTree
);
1662 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1664 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1666 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1668 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1670 err
= UpdateHeader (btreePtr
, false);
1672 LogEndTime(kTraceFlushBTree
, err
);
1678 /*-------------------------------------------------------------------------------
1679 Routine: BTReload - Reload B-tree Header Data.
1681 Function: Reload B-tree header data from disk. This is called after fsck
1682 has made repairs to the root filesystem. The filesystem is
1683 mounted read-only when BTReload is caled.
1686 Input: filePtr - the B*Tree file that needs its header updated
1690 Result: noErr - success
1692 -------------------------------------------------------------------------------*/
1695 BTReloadData(FCB
*filePtr
)
1698 BTreeControlBlockPtr btreePtr
;
1699 BlockDescriptor node
;
1700 BTHeaderRec
*header
;
1703 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1704 if (btreePtr
== nil
)
1705 return (fsBTInvalidFileErr
);
1707 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1709 err
= GetNode(btreePtr
, kHeaderNodeNum
, &node
);
1713 header
= (BTHeaderRec
*)((char *)node
.buffer
+ sizeof(BTNodeDescriptor
));
1714 if ((err
= VerifyHeader (filePtr
, header
)) == 0) {
1715 btreePtr
->treeDepth
= header
->treeDepth
;
1716 btreePtr
->rootNode
= header
->rootNode
;
1717 btreePtr
->leafRecords
= header
->leafRecords
;
1718 btreePtr
->firstLeafNode
= header
->firstLeafNode
;
1719 btreePtr
->lastLeafNode
= header
->lastLeafNode
;
1720 btreePtr
->maxKeyLength
= header
->maxKeyLength
;
1721 btreePtr
->totalNodes
= header
->totalNodes
;
1722 btreePtr
->freeNodes
= header
->freeNodes
;
1723 btreePtr
->btreeType
= header
->btreeType
;
1725 btreePtr
->flags
&= (~kBTHeaderDirty
);
1728 (void) ReleaseNode(btreePtr
, &node
);
1734 /*-------------------------------------------------------------------------------
1735 Routine: BTInvalidateHint - Invalidates the hint within a BTreeInterator.
1737 Function: Invalidates the hint within a BTreeInterator.
1740 Input: iterator - pointer to BTreeIterator
1742 Output: iterator - iterator with the hint.nodeNum cleared
1744 Result: noErr - success
1745 paramErr - iterator == nil
1746 -------------------------------------------------------------------------------*/
1749 OSStatus
BTInvalidateHint (BTreeIterator
*iterator
)
1751 if (iterator
== nil
)
1754 iterator
->hint
.nodeNum
= 0;
1762 /*-------------------------------------------------------------------------------
1763 Routine: BTGetLastSync
1765 Function: Returns the last time that this btree was flushed, does not include header.
1767 Input: filePtr - pointer file control block
1769 Output: lastfsync - time in seconds of last update
1771 Result: noErr - success
1772 paramErr - iterator == nil
1773 -------------------------------------------------------------------------------*/
1776 OSStatus
BTGetLastSync (FCB
*filePtr
,
1779 BTreeControlBlockPtr btreePtr
;
1782 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1784 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1786 /* Maybe instead of requiring a lock..an atomic set might be more appropriate */
1787 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
1789 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1790 M_ReturnErrorIf (lastsync
== nil
, paramErr
);
1792 *lastsync
= btreePtr
->lastfsync
;
1800 /*-------------------------------------------------------------------------------
1801 Routine: BTSetLastSync
1803 Function: Sets the last time that this btree was flushed, does not include header.
1806 Input: fcb - pointer file control block
1808 Output: lastfsync - time in seconds of last update
1810 Result: noErr - success
1811 paramErr - iterator == nil
1812 -------------------------------------------------------------------------------*/
1815 OSStatus
BTSetLastSync (FCB
*filePtr
,
1818 BTreeControlBlockPtr btreePtr
;
1821 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1823 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1825 /* Maybe instead of requiring a lock..an atomic set might be more appropriate */
1826 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
1828 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1829 M_ReturnErrorIf (lastsync
== nil
, paramErr
);
1831 btreePtr
->lastfsync
= lastsync
;