2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
25 Contains: Implementation of public interface routines for B-tree manager.
29 Written by: Gordon Sheridan and Bill Bruffey
31 Copyright: © 1992-1999 by Apple Computer, Inc., all rights reserved.
37 Other Contact: Mark Day
39 Technology: File Systems
47 Change History (most recent first):
48 <MOSXS> 9/22/99 ser Added routines BTGetLastSync and BTSetLastSync
49 <MOSXS> 6/1/99 djb Sync up with Mac OS 8.6.
50 <MOSXS> 6/30/98 djb In BTOpenPath make sure nodes are contiguous on disk (radar #2249539).
51 <MOSXS> 4/15/98 djb In BTOpenPath need to clear nodeRec.buffer if GetBlockProc fails.
52 <MOSXS> 4/11/98 djb Add RequireFileLock checking to all external entry points.
54 <MOSXS> 03/23/98 djb In BTOpenPath use kTrashBlock option when releasing the header so
55 that we get a full node when we call GetNode.
57 <CS9> 12/12/97 djb Radar #2202682, BTIterateRecord with kBTreeCurrentRecord was not
58 checking if we had a record and could call BlockMove with an
59 uninitialize source pointer (causing a bus error).
60 <CS8> 10/24/97 msd In BTIterateRecord, when moving to the previous or next record
61 and we have to move to another node, see if we need to release
62 the node about to be "shifted out" (opposite sibling of the
63 direction we need to move).
64 <CS7> 7/25/97 DSH BTSearchRecord now takes a heuristicHint, nodeNum, and tries it
65 before calling SearchBTree
66 <CS6> 7/24/97 djb GetBlockProc now take a file refnum instead of an FCB ptr.
67 <CS5> 7/22/97 djb Move trace points from BTreeWrapper.c to here.
68 <CS4> 7/21/97 djb LogEndTime now takes an error code.
69 <CS3> 7/16/97 DSH FilesInternal.i renamed FileMgrInternal.i to avoid name
71 <CS2> 5/19/97 djb Add summary traces to BTIterateRecord.
72 <CS1> 4/23/97 djb first checked in
74 <HFS7> 2/19/97 djb Enable variable sized index keys for HFS+ volumes. Added node
75 cache to support nodes larger than 512 bytes.
76 <HFS6> 1/27/97 djb Calls to InsertTree and DeleteTree are now recursive (to support
77 variable sized index keys).
78 <HFS5> 1/13/97 djb Added support for getting current record to BTIterateRecord.
79 <HFS4> 1/6/97 djb Initialize "BigKeys" attribute in BTOpen.
80 <HFS3> 1/3/97 djb Added support for large keys.
81 <HFS2> 12/23/96 djb On exit map fsBTEmptyErr and fsBTEndOfIterationErr to
82 fsBTRecordNotFoundErr.
83 <HFS1> 12/19/96 djb first checked in
85 History applicable to original Scarecrow Design:
87 <13> 10/25/96 ser Changing for new VFPI
88 <12> 10/18/96 ser Converting over VFPI changes
89 <11> 9/17/96 dkh More BTree statistics. Modified hint checks to not bail out when
90 an error is returned from GetNode.
91 <10> 9/16/96 dkh Revised BTree statistics.
92 <9> 8/23/96 dkh Remove checks for multiple paths to BTree file. Need to add
93 equivalent mechanism later.
94 <8> 6/20/96 dkh Radar #1358740. Switch from using Pools to debug MemAllocators.
95 <7> 3/14/96 jev Fix BTreeSetRecord, recordFound was not set for the case of a
96 simple replace causing the leafRecords count to get bumped even
97 though we didn't have to add a record.
98 <6> 3/1/96 prp Fix lint problems. Bug in BTSetRecord that does not initialize
100 <5> 1/22/96 dkh Add #include Memory.h
101 <4> 1/10/96 msd Use the real function names from Math64.i.
102 <3> 1/4/96 jev Fix BTItererateRecord for the condition when the iterator
103 position routine does not find the record and we are looking for
104 the next record. In such a case, if the node's forrward link is
105 non-zero, we have to keep iterating next and not return
106 fsBTEndOfIterationErr error.
107 <2> 12/7/95 dkh D10E2 build. Changed usage of Ref data type to LogicalAddress.
108 <1> 10/18/95 rst Moved from Scarecrow project.
110 <24> 7/18/95 mbb Change MoveData & ClearBytes to BlockMoveData & BlockZero.
111 <23> 1/31/95 prp GetBlockProc interface uses a 64 bit node number.
112 <22> 1/12/95 wjk Adopt Model FileSystem changes in D5.
113 <21> 11/16/94 prp Add IsItAHint routine and use it whenever hint's node number was
115 <20> 11/10/94 prp BTGetInfo name collides with the same name in FileManagerPriv.i.
116 Change it to BTGetInformation.
117 <19> 9/30/94 prp Get in sync with D2 interface changes.
118 <18> 7/22/94 wjk Convert to the new set of header files.
119 <17> 12/9/93 wjk Cleanup usage of char, Byte, int8, UInt8, etc.
120 <16> 12/2/93 wjk Move from Makefiles to BuildFiles. Fit into the ModernOS and
122 <15> 11/30/93 wjk Move from Makefiles to BuildFiles. Fit into the ModernOS and
124 <14> 9/30/93 gs Rename E_NoGetNodeProc and E_NoReleaseNodeProc to
126 <13> 8/31/93 prp Use Set64U instead of Set64.
127 <12> 8/16/93 prp In BTSearchRecord, if the input hint found the node and record,
128 set the local nodeNum variable correctly so that the resultant
129 iterator gets set correctly.
130 <11> 7/1/93 gs Fix bug in BTIterateRecord related to kBTreePrevRecord
132 <10> 6/2/93 gs Update for changes to FSErrors.h and add some comments.
133 <9> 5/24/93 gs Fix bug in BTInsert/Set/ReplaceRecord which didn't set node hint
134 properly in some cases.
135 <8> 5/24/93 gs Do NOT map fsBTEmptyErr to fsBTRecordNotFoundErr in BTSearchRecord.
136 <7> 5/24/93 gs Rename BTFlush to BTFlushPath.
137 <6> 5/21/93 gs Add hint optimization to Set/Replace routines.
138 <5> 5/10/93 gs Remove Panic from BTInitialize for small logicalEOF. Implement
139 Insert, Set, Replace, and Delete.
140 <4> 3/23/93 gs Finish BTInitialize.
141 <3> 2/8/93 gs Implement BTSearchRecord and BTIterateRecord.
142 <2> 12/8/92 gs Implement Open and Close routines.
143 <1> 11/15/92 gs first checked in
147 #include "../headers/BTreesPrivate.h"
150 * The amount that the BTree header leaf count can be wrong before we assume
151 * it is in an infinite loop.
153 #define kNumLeafRecSlack 10
155 //////////////////////////////////// Globals ////////////////////////////////////
158 /////////////////////////// BTree Module Entry Points ///////////////////////////
162 /*-------------------------------------------------------------------------------
163 Routine: BTOpenPath - Open a file for access as a B*Tree.
165 Function: Create BTree control block for a file, if necessary. Validates the
166 file to be sure it looks like a BTree file.
169 Input: filePtr - pointer to file to open as a B-tree
170 keyCompareProc - pointer to client's KeyCompare function
171 getBlockProc - pointer to client's GetBlock function
172 releaseBlockProc - pointer to client's ReleaseBlock function
173 setEndOfForkProc - pointer to client's SetEOF function
175 Result: noErr - success
176 paramErr - required ptr was nil
180 -------------------------------------------------------------------------------*/
182 OSStatus
BTOpenPath (FCB
*filePtr
,
183 KeyCompareProcPtr keyCompareProc
,
184 GetBlockProcPtr getBlockProc
,
185 ReleaseBlockProcPtr releaseBlockProc
,
186 SetEndOfForkProcPtr setEndOfForkProc
,
187 SetBlockSizeProcPtr setBlockSizeProc
)
190 BTreeControlBlockPtr btreePtr
;
194 ////////////////////// Preliminary Error Checking ///////////////////////////
196 if ( filePtr
== nil
||
197 getBlockProc
== nil
||
198 releaseBlockProc
== nil
||
199 setEndOfForkProc
== nil
||
200 setBlockSizeProc
== nil
)
205 if ( filePtr
->fcbBTCBPtr
!= nil
) // already has a BTreeCB
208 // is file large enough to contain header node?
209 if ( filePtr
->fcbEOF
< kMinNodeSize
)
210 return fsBTInvalidFileErr
; //\80\80 or E_BadHeader?
213 //////////////////////// Allocate Control Block /////////////////////////////
215 btreePtr
= (BTreeControlBlock
*) NewPtrSysClear( sizeof( BTreeControlBlock
) );
218 Panic ("\pBTOpen: no memory for btreePtr.");
222 btreePtr
->getBlockProc
= getBlockProc
;
223 btreePtr
->releaseBlockProc
= releaseBlockProc
;
224 btreePtr
->setEndOfForkProc
= setEndOfForkProc
;
225 btreePtr
->keyCompareProc
= keyCompareProc
;
227 /////////////////////////// Read Header Node ////////////////////////////////
229 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
230 btreePtr
->fileRefNum
= GetFileRefNumFromFCB(filePtr
);
231 filePtr
->fcbBTCBPtr
= (Ptr
) btreePtr
; // attach btree cb to file
233 /* The minimum node size is the physical block size */
234 nodeRec
.blockSize
= VTOHFS(btreePtr
->fileRefNum
)->hfs_phys_block_size
;
236 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
238 // it is now safe to call M_ExitOnError (err)
240 err
= setBlockSizeProc (btreePtr
->fileRefNum
, nodeRec
.blockSize
, 1);
244 err
= getBlockProc (btreePtr
->fileRefNum
,
250 nodeRec
.buffer
= nil
;
251 nodeRec
.blockHeader
= nil
;
252 Panic("\pBTOpen: getNodeProc returned error getting header node.");
255 ++btreePtr
->numGetNodes
;
256 header
= (BTHeaderRec
*) ((u_long
)nodeRec
.buffer
+ sizeof(BTNodeDescriptor
));
259 ///////////////////////////// verify header /////////////////////////////////
261 err
= VerifyHeader (filePtr
, header
);
265 ///////////////////// Initalize fields from header //////////////////////////
267 PanicIf ( (FCBTOVCB(filePtr
)->vcbSigWord
!= 0x4244) && (header
->nodeSize
== 512), "\p BTOpenPath: wrong node size for HFS+ volume!"); // 0x4244 = 'BD'
269 btreePtr
->treeDepth
= header
->treeDepth
;
270 btreePtr
->rootNode
= header
->rootNode
;
271 btreePtr
->leafRecords
= header
->leafRecords
;
272 btreePtr
->firstLeafNode
= header
->firstLeafNode
;
273 btreePtr
->lastLeafNode
= header
->lastLeafNode
;
274 btreePtr
->nodeSize
= header
->nodeSize
;
275 btreePtr
->maxKeyLength
= header
->maxKeyLength
;
276 btreePtr
->totalNodes
= header
->totalNodes
;
277 btreePtr
->freeNodes
= header
->freeNodes
;
278 // ignore header->clumpSize; //\80\80 rename this field?
279 btreePtr
->btreeType
= header
->btreeType
;
281 btreePtr
->attributes
= header
->attributes
;
283 if ( btreePtr
->maxKeyLength
> 40 )
284 btreePtr
->attributes
|= (kBTBigKeysMask
+ kBTVariableIndexKeysMask
); //\80\80 we need a way to save these attributes
286 /////////////////////// Initialize dynamic fields ///////////////////////////
288 btreePtr
->version
= kBTreeVersion
;
290 btreePtr
->writeCount
= 1;
292 /////////////////////////// Check Header Node ///////////////////////////////
294 // set kBadClose attribute bit, and UpdateNode
296 /* b-tree node size must be at least as big as the physical block size */
297 if (btreePtr
->nodeSize
< nodeRec
.blockSize
)
300 * If this tree has any records or the media is writeable then
301 * we cannot mount using the current physical block size.
303 if (btreePtr
->leafRecords
> 0 ||
304 VTOHFS(btreePtr
->fileRefNum
)->hfs_media_writeable
)
306 err
= fsBTBadNodeSize
;
311 // if nodeSize Matches then we don't need to release, just CheckNode
312 if ( btreePtr
->nodeSize
== nodeRec
.blockSize
)
314 err
= CheckNode (btreePtr
, nodeRec
.buffer
);
316 VTOVCB(btreePtr
->fileRefNum
)->vcbFlags
|= kHFS_DamagedVolume
;
321 err
= setBlockSizeProc (btreePtr
->fileRefNum
, btreePtr
->nodeSize
, 32); //\80\80 we should try and get this down to 8
325 * Need to use kTrashBlock option to force the
326 * buffer cache to read the entire node
328 err
= releaseBlockProc(btreePtr
->fileRefNum
, &nodeRec
, kTrashBlock
);
329 ++btreePtr
->numReleaseNodes
;
332 err
= GetNode (btreePtr
, kHeaderNodeNum
, &nodeRec
); // calls CheckNode...
336 //\80\80 total nodes * node size <= LEOF?
339 err
= ReleaseNode (btreePtr
, &nodeRec
);
342 //////////////////////////////// Success ////////////////////////////////////
344 //\80\80 align LEOF to multiple of node size? - just on close
349 /////////////////////// Error - Clean up and Exit ///////////////////////////
353 filePtr
->fcbBTCBPtr
= nil
;
354 (void) ReleaseNode (btreePtr
, &nodeRec
);
355 DisposePtr( (Ptr
) btreePtr
);
362 /*-------------------------------------------------------------------------------
363 Routine: BTClosePath - Flush BTree Header and Deallocate Memory for BTree.
365 Function: Flush the BTreeControlBlock fields to header node, and delete BTree control
366 block and key descriptor associated with the file if filePtr is last
367 path of type kBTreeType ('btre').
370 Input: filePtr - pointer to file to delete BTree control block for.
372 Result: noErr - success
375 -------------------------------------------------------------------------------*/
377 OSStatus
BTClosePath (FCB
*filePtr
)
380 BTreeControlBlockPtr btreePtr
;
382 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
385 return fsBTInvalidFileErr
;
387 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
389 ////////////////////// Check for other BTree Paths //////////////////////////
391 btreePtr
->attributes
&= ~kBTBadCloseMask
; // clear "bad close" attribute bit
392 err
= UpdateHeader (btreePtr
, true);
395 DisposePtr( (Ptr
) btreePtr
);
396 filePtr
->fcbBTCBPtr
= nil
;
400 /////////////////////// Error - Clean Up and Exit ///////////////////////////
409 /*-------------------------------------------------------------------------------
410 Routine: BTSearchRecord - Search BTree for a record with a matching key.
412 Function: Search for position in B*Tree indicated by searchKey. If a valid node hint
413 is provided, it will be searched first, then SearchTree will be called.
414 If a BTreeIterator is provided, it will be set to the position found as
415 a result of the search. If a record exists at that position, and a BufferDescriptor
416 is supplied, the record will be copied to the buffer (as much as will fit),
417 and recordLen will be set to the length of the record.
419 If an error other than fsBTRecordNotFoundErr occurs, the BTreeIterator, if any,
420 is invalidated, and recordLen is set to 0.
423 Input: pathPtr - pointer to path for BTree file.
424 searchKey - pointer to search key to match.
425 hintPtr - pointer to hint (may be nil)
427 Output: record - pointer to BufferDescriptor containing record
428 recordLen - length of data at recordPtr
429 iterator - pointer to BTreeIterator indicating position result of search
431 Result: noErr - success, record contains copy of record found
432 fsBTRecordNotFoundErr - record was not found, no data copied
433 fsBTInvalidFileErr - no BTreeControlBlock is allocated for the fork
434 fsBTInvalidKeyLengthErr -
436 -------------------------------------------------------------------------------*/
438 OSStatus
BTSearchRecord (FCB
*filePtr
,
439 BTreeIterator
*searchIterator
,
440 FSBufferDescriptor
*record
,
442 BTreeIterator
*resultIterator
)
445 BTreeControlBlockPtr btreePtr
;
446 TreePathTable treePathTable
;
448 BlockDescriptor node
;
456 if (filePtr
== nil
) return paramErr
;
457 if (searchIterator
== nil
) return paramErr
;
459 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
460 if (btreePtr
== nil
) return fsBTInvalidFileErr
;
462 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
466 ////////////////////////////// Take A Hint //////////////////////////////////
468 err
= IsItAHint (btreePtr
, searchIterator
, &validHint
);
473 nodeNum
= searchIterator
->hint
.nodeNum
;
475 err
= GetNode (btreePtr
, nodeNum
, &node
);
478 if ( ((BTNodeDescriptor
*) node
.buffer
)->kind
== kBTLeafNode
&&
479 ((BTNodeDescriptor
*) node
.buffer
)->numRecords
> 0 )
481 foundRecord
= SearchNode (btreePtr
, node
.buffer
, &searchIterator
->key
, &index
);
483 //\80\80 if !foundRecord, we could still skip tree search if ( 0 < index < numRecords )
486 if (foundRecord
== false)
488 err
= ReleaseNode (btreePtr
, &node
);
493 ++btreePtr
->numValidHints
;
497 if( foundRecord
== false )
498 (void) BTInvalidateHint( searchIterator
);
502 //////////////////////////// Search The Tree ////////////////////////////////
504 if (foundRecord
== false)
506 err
= SearchTree ( btreePtr
, &searchIterator
->key
, treePathTable
, &nodeNum
, &node
, &index
);
509 case noErr
: foundRecord
= true; break;
510 case fsBTRecordNotFoundErr
: break;
511 default: goto ErrorExit
;
516 //////////////////////////// Get the Record /////////////////////////////////
518 if (foundRecord
== true)
520 //XXX Should check for errors! Or BlockMove could choke on recordPtr!!!
521 GetRecordByIndex (btreePtr
, node
.buffer
, index
, &keyPtr
, &recordPtr
, &len
);
523 if (recordLen
!= nil
) *recordLen
= len
;
527 ByteCount recordSize
;
529 recordSize
= record
->itemCount
* record
->itemSize
;
531 if (len
> recordSize
) len
= recordSize
;
533 BlockMoveData (recordPtr
, record
->bufferAddress
, len
);
538 /////////////////////// Success - Update Iterator ///////////////////////////
540 if (resultIterator
!= nil
)
542 resultIterator
->hint
.writeCount
= btreePtr
->writeCount
;
543 resultIterator
->hint
.nodeNum
= nodeNum
;
544 resultIterator
->hint
.index
= index
;
546 resultIterator
->hint
.reserved1
= 0;
547 resultIterator
->hint
.reserved2
= 0;
548 resultIterator
->version
= 0;
549 resultIterator
->reserved
= 0;
551 // copy the key in the BTree when found rather than searchIterator->key to get proper case/diacriticals
552 if (foundRecord
== true)
553 BlockMoveData ((Ptr
)keyPtr
, (Ptr
)&resultIterator
->key
, CalcKeySize(btreePtr
, keyPtr
));
555 BlockMoveData ((Ptr
)&searchIterator
->key
, (Ptr
)&resultIterator
->key
, CalcKeySize(btreePtr
, &searchIterator
->key
));
558 err
= ReleaseNode (btreePtr
, &node
);
561 if (foundRecord
== false) return fsBTRecordNotFoundErr
;
565 /////////////////////// Error - Clean Up and Exit ///////////////////////////
569 if (recordLen
!= nil
)
572 if (resultIterator
!= nil
)
574 resultIterator
->hint
.writeCount
= 0;
575 resultIterator
->hint
.nodeNum
= 0;
576 resultIterator
->hint
.index
= 0;
577 resultIterator
->hint
.reserved1
= 0;
578 resultIterator
->hint
.reserved2
= 0;
580 resultIterator
->version
= 0;
581 resultIterator
->reserved
= 0;
582 resultIterator
->key
.length16
= 0; // zero out two bytes to cover both types of keys
585 if ( err
== fsBTEmptyErr
)
586 err
= fsBTRecordNotFoundErr
;
593 /*-------------------------------------------------------------------------------
594 Routine: BTIterateRecord - Find the first, next, previous, or last record.
596 Function: Find the first, next, previous, or last record in the BTree
598 Input: pathPtr - pointer to path iterate records for.
599 operation - iteration operation (first,next,prev,last)
600 iterator - pointer to iterator indicating start position
602 Output: iterator - iterator is updated to indicate new position
603 newKeyPtr - pointer to buffer to copy key found by iteration
604 record - pointer to buffer to copy record found by iteration
605 recordLen - length of record
607 Result: noErr - success
609 -------------------------------------------------------------------------------*/
611 OSStatus
BTIterateRecord (FCB
*filePtr
,
612 BTreeIterationOperation operation
,
613 BTreeIterator
*iterator
,
614 FSBufferDescriptor
*record
,
618 BTreeControlBlockPtr btreePtr
;
626 BlockDescriptor left
, node
, right
;
630 ////////////////////////// Priliminary Checks ///////////////////////////////
642 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
645 return fsBTInvalidFileErr
; //\80\80 handle properly
648 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
650 if ((operation
!= kBTreeFirstRecord
) &&
651 (operation
!= kBTreeNextRecord
) &&
652 (operation
!= kBTreeCurrentRecord
) &&
653 (operation
!= kBTreePrevRecord
) &&
654 (operation
!= kBTreeLastRecord
))
656 err
= fsInvalidIterationMovmentErr
;
660 /////////////////////// Find First or Last Record ///////////////////////////
662 if ((operation
== kBTreeFirstRecord
) || (operation
== kBTreeLastRecord
))
664 if (operation
== kBTreeFirstRecord
) nodeNum
= btreePtr
->firstLeafNode
;
665 else nodeNum
= btreePtr
->lastLeafNode
;
673 err
= GetNode (btreePtr
, nodeNum
, &node
);
676 if ( ((NodeDescPtr
) node
.buffer
)->kind
!= kBTLeafNode
||
677 ((NodeDescPtr
) node
.buffer
)->numRecords
<= 0 )
679 err
= ReleaseNode (btreePtr
, &node
);
682 err
= fsBTInvalidNodeErr
;
683 MARK_VOLUMEDAMAGED(filePtr
);
687 if (operation
== kBTreeFirstRecord
) index
= 0;
688 else index
= ((BTNodeDescriptor
*) node
.buffer
)->numRecords
- 1;
690 goto CopyData
; //\80\80 is there a cleaner way?
694 //////////////////////// Find Iterator Position /////////////////////////////
696 err
= FindIteratorPosition (btreePtr
, iterator
,
697 &left
, &node
, &right
, &nodeNum
, &index
, &foundRecord
);
701 ///////////////////// Find Next Or Previous Record //////////////////////////
703 if (operation
== kBTreePrevRecord
)
711 if (left
.buffer
== nil
)
713 nodeNum
= ((NodeDescPtr
) node
.buffer
)->bLink
;
716 err
= GetNode (btreePtr
, nodeNum
, &left
);
719 err
= fsBTStartOfIterationErr
;
723 // Before we stomp on "right", we'd better release it if needed
724 if (right
.buffer
!= nil
) {
725 err
= ReleaseNode(btreePtr
, &right
);
731 index
= ((NodeDescPtr
) node
.buffer
)->numRecords
-1;
734 else if (operation
== kBTreeNextRecord
)
736 if ((foundRecord
!= true) &&
737 (((NodeDescPtr
) node
.buffer
)->fLink
== 0) &&
738 (index
== ((NodeDescPtr
) node
.buffer
)->numRecords
))
740 err
= fsBTEndOfIterationErr
;
744 // we did not find the record but the index is already positioned correctly
745 if ((foundRecord
== false) && (index
!= ((NodeDescPtr
) node
.buffer
)->numRecords
))
748 // we found the record OR we have to look in the next node
749 if (index
< ((NodeDescPtr
) node
.buffer
)->numRecords
-1)
755 if (right
.buffer
== nil
)
757 nodeNum
= ((NodeDescPtr
) node
.buffer
)->fLink
;
760 err
= GetNode (btreePtr
, nodeNum
, &right
);
763 err
= fsBTEndOfIterationErr
;
767 // Before we stomp on "left", we'd better release it if needed
768 if (left
.buffer
!= nil
) {
769 err
= ReleaseNode(btreePtr
, &left
);
778 else // operation == kBTreeCurrentRecord
780 // make sure we have something... <CS9>
781 if ((foundRecord
!= true) &&
782 (index
>= ((NodeDescPtr
) node
.buffer
)->numRecords
))
784 err
= fsBTEndOfIterationErr
;
789 //////////////////// Copy Record And Update Iterator ////////////////////////
793 // added check for errors <CS9>
794 err
= GetRecordByIndex (btreePtr
, node
.buffer
, index
, &keyPtr
, &recordPtr
, &len
);
797 if (recordLen
!= nil
)
802 ByteCount recordSize
;
804 recordSize
= record
->itemCount
* record
->itemSize
;
806 if (len
> recordSize
) len
= recordSize
;
808 BlockMoveData (recordPtr
, record
->bufferAddress
, len
);
811 if (iterator
!= nil
) // first & last do not require iterator
813 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
814 iterator
->hint
.nodeNum
= nodeNum
;
815 iterator
->hint
.index
= index
;
816 iterator
->hint
.reserved1
= 0;
817 iterator
->hint
.reserved2
= 0;
819 iterator
->version
= 0;
820 iterator
->reserved
= 0;
823 * Check for infinite loops by making sure we do not
824 * process more leaf records, than can possibly be (or the BTree header
825 * is seriously damaged)....a brute force method.
827 if ((operation
== kBTreeFirstRecord
) || (operation
== kBTreeLastRecord
))
828 iterator
->hitCount
= 1;
829 else if (operation
!= kBTreeCurrentRecord
)
830 iterator
->hitCount
+= 1;
831 /* Always use the highest max, in case the grows while iterating */
832 iterator
->maxLeafRecs
= max(btreePtr
->leafRecords
, iterator
->maxLeafRecs
);
835 if (iterator
->hitCount
> iterator
->maxLeafRecs
+ kNumLeafRecSlack
)
837 err
= fsBTInvalidNodeErr
;
838 MARK_VOLUMEDAMAGED(filePtr
);
843 BlockMoveData ((Ptr
)keyPtr
, (Ptr
)&iterator
->key
, CalcKeySize(btreePtr
, keyPtr
));
847 ///////////////////////////// Release Nodes /////////////////////////////////
849 err
= ReleaseNode (btreePtr
, &node
);
852 if (left
.buffer
!= nil
)
854 err
= ReleaseNode (btreePtr
, &left
);
858 if (right
.buffer
!= nil
)
860 err
= ReleaseNode (btreePtr
, &right
);
866 /////////////////////// Error - Clean Up and Exit ///////////////////////////
870 (void) ReleaseNode (btreePtr
, &left
);
871 (void) ReleaseNode (btreePtr
, &node
);
872 (void) ReleaseNode (btreePtr
, &right
);
874 if (recordLen
!= nil
)
879 iterator
->hint
.writeCount
= 0;
880 iterator
->hint
.nodeNum
= 0;
881 iterator
->hint
.index
= 0;
882 iterator
->hint
.reserved1
= 0;
883 iterator
->hint
.reserved2
= 0;
885 iterator
->version
= 0;
886 iterator
->reserved
= 0;
887 iterator
->key
.length16
= 0;
890 if ( err
== fsBTEmptyErr
|| err
== fsBTEndOfIterationErr
)
891 err
= fsBTRecordNotFoundErr
;
897 /*-------------------------------------------------------------------------------
898 Routine: BTIterateRecords
900 Function: Find a series of records
902 Input: filePtr - b-tree file
903 operation - iteration operation (first,next,prev,last)
904 iterator - pointer to iterator indicating start position
905 callBackProc - pointer to routince to process a record
906 callBackState - pointer to state data (used by callBackProc)
908 Output: iterator - iterator is updated to indicate new position
910 Result: noErr - success
912 -------------------------------------------------------------------------------*/
915 BTIterateRecords(FCB
*filePtr
, BTreeIterationOperation operation
, BTreeIterator
*iterator
,
916 IterateCallBackProcPtr callBackProc
, void * callBackState
)
919 BTreeControlBlockPtr btreePtr
;
925 BlockDescriptor left
, node
, right
;
929 ////////////////////////// Priliminary Checks ///////////////////////////////
935 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
937 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
939 if ((operation
!= kBTreeFirstRecord
) &&
940 (operation
!= kBTreeNextRecord
) &&
941 (operation
!= kBTreeCurrentRecord
) &&
942 (operation
!= kBTreePrevRecord
) &&
943 (operation
!= kBTreeLastRecord
))
945 err
= fsInvalidIterationMovmentErr
;
949 /////////////////////// Find First or Last Record ///////////////////////////
951 if ((operation
== kBTreeFirstRecord
) || (operation
== kBTreeLastRecord
))
953 if (operation
== kBTreeFirstRecord
)
954 nodeNum
= btreePtr
->firstLeafNode
;
956 nodeNum
= btreePtr
->lastLeafNode
;
964 err
= GetNode(btreePtr
, nodeNum
, &node
);
967 if ( ((NodeDescPtr
)node
.buffer
)->kind
!= kBTLeafNode
||
968 ((NodeDescPtr
)node
.buffer
)->numRecords
<= 0 )
970 err
= ReleaseNode(btreePtr
, &node
);
973 err
= fsBTInvalidNodeErr
;
974 MARK_VOLUMEDAMAGED(filePtr
);
978 if (operation
== kBTreeFirstRecord
)
981 index
= ((BTNodeDescriptor
*) node
.buffer
)->numRecords
- 1;
986 //////////////////////// Find Iterator Position /////////////////////////////
988 err
= FindIteratorPosition(btreePtr
, iterator
, &left
, &node
, &right
,
989 &nodeNum
, &index
, &foundRecord
);
990 if (err
== fsBTRecordNotFoundErr
)
995 ///////////////////// Find Next Or Previous Record //////////////////////////
997 if (operation
== kBTreePrevRecord
)
1005 if (left
.buffer
== nil
)
1007 nodeNum
= ((NodeDescPtr
) node
.buffer
)->bLink
;
1010 err
= GetNode(btreePtr
, nodeNum
, &left
);
1013 err
= fsBTStartOfIterationErr
;
1017 // Before we stomp on "right", we'd better release it if needed
1018 if (right
.buffer
!= nil
) {
1019 err
= ReleaseNode(btreePtr
, &right
);
1025 index
= ((NodeDescPtr
) node
.buffer
)->numRecords
-1;
1028 else if (operation
== kBTreeNextRecord
)
1030 if ((foundRecord
!= true) &&
1031 (((NodeDescPtr
)node
.buffer
)->fLink
== 0) &&
1032 (index
== ((NodeDescPtr
)node
.buffer
)->numRecords
))
1034 err
= fsBTEndOfIterationErr
;
1038 // we did not find the record but the index is already positioned correctly
1039 if ((foundRecord
== false) && (index
!= ((NodeDescPtr
)node
.buffer
)->numRecords
))
1042 // we found the record OR we have to look in the next node
1043 if (index
< ((NodeDescPtr
)node
.buffer
)->numRecords
-1)
1049 if (right
.buffer
== nil
)
1051 nodeNum
= ((NodeDescPtr
)node
.buffer
)->fLink
;
1054 err
= GetNode(btreePtr
, nodeNum
, &right
);
1057 err
= fsBTEndOfIterationErr
;
1061 // Before we stomp on "left", we'd better release it if needed
1062 if (left
.buffer
!= nil
) {
1063 err
= ReleaseNode(btreePtr
, &left
);
1072 else // operation == kBTreeCurrentRecord
1074 // make sure we have something... <CS9>
1075 if ((foundRecord
!= true) &&
1076 (index
>= ((NodeDescPtr
)node
.buffer
)->numRecords
))
1078 err
= fsBTEndOfIterationErr
;
1083 //////////////////// Process Records Using Callback ////////////////////////
1086 err
= GetRecordByIndex(btreePtr
, node
.buffer
, index
, &keyPtr
, &recordPtr
, &len
);
1093 if (callBackProc(keyPtr
, recordPtr
, len
, callBackState
) == 0)
1096 if ((index
+1) < ((NodeDescPtr
)node
.buffer
)->numRecords
) {
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
);
1121 err
= GetRecordByIndex(btreePtr
, node
.buffer
, index
,
1122 &keyPtr
, &recordPtr
, &len
);
1130 ///////////////// Update Iterator to Last Item Processed /////////////////////
1133 if (iterator
!= nil
) // first & last have optional iterator
1135 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
1136 iterator
->hint
.nodeNum
= nodeNum
;
1137 iterator
->hint
.index
= index
;
1138 iterator
->version
= 0;
1140 BlockMoveData((Ptr
)keyPtr
, (Ptr
)&iterator
->key
, CalcKeySize(btreePtr
, keyPtr
));
1145 ///////////////////////////// Release Nodes /////////////////////////////////
1147 err
= ReleaseNode(btreePtr
, &node
);
1150 if (left
.buffer
!= nil
)
1152 err
= ReleaseNode(btreePtr
, &left
);
1156 if (right
.buffer
!= nil
)
1158 err
= ReleaseNode(btreePtr
, &right
);
1164 /////////////////////// Error - Clean Up and Exit ///////////////////////////
1168 (void) ReleaseNode(btreePtr
, &left
);
1169 (void) ReleaseNode(btreePtr
, &node
);
1170 (void) ReleaseNode(btreePtr
, &right
);
1172 if (iterator
!= nil
)
1174 iterator
->hint
.writeCount
= 0;
1175 iterator
->hint
.nodeNum
= 0;
1176 iterator
->hint
.index
= 0;
1177 iterator
->version
= 0;
1178 iterator
->key
.length16
= 0;
1181 if ( err
== fsBTEmptyErr
|| err
== fsBTEndOfIterationErr
)
1182 err
= fsBTRecordNotFoundErr
;
1188 //////////////////////////////// BTInsertRecord /////////////////////////////////
1190 OSStatus
BTInsertRecord (FCB
*filePtr
,
1191 BTreeIterator
*iterator
,
1192 FSBufferDescriptor
*record
,
1196 BTreeControlBlockPtr btreePtr
;
1197 TreePathTable treePathTable
;
1199 BlockDescriptor nodeRec
;
1200 UInt32 insertNodeNum
;
1205 ////////////////////////// Priliminary Checks ///////////////////////////////
1207 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
1209 err
= CheckInsertParams (filePtr
, iterator
, record
, recordLen
);
1213 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1215 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1218 ///////////////////////// Find Insert Position //////////////////////////////
1220 // always call SearchTree for Insert
1221 err
= SearchTree (btreePtr
, &iterator
->key
, treePathTable
, &insertNodeNum
, &nodeRec
, &index
);
1223 switch (err
) // set/replace/insert decision point
1225 case noErr
: err
= fsBTDuplicateRecordErr
;
1228 case fsBTRecordNotFoundErr
: break;
1230 case fsBTEmptyErr
: // if tree empty add 1st leaf node
1232 if (btreePtr
->freeNodes
== 0)
1234 err
= ExtendBTree (btreePtr
, btreePtr
->totalNodes
+ 1);
1235 M_ExitOnError (err
);
1238 err
= AllocateNode (btreePtr
, &insertNodeNum
);
1239 M_ExitOnError (err
);
1241 err
= GetNewNode (btreePtr
, insertNodeNum
, &nodeRec
);
1242 M_ExitOnError (err
);
1244 ((NodeDescPtr
)nodeRec
.buffer
)->kind
= kBTLeafNode
;
1245 ((NodeDescPtr
)nodeRec
.buffer
)->height
= 1;
1247 recordFit
= InsertKeyRecord (btreePtr
, nodeRec
.buffer
, 0,
1248 &iterator
->key
, KeyLength(btreePtr
, &iterator
->key
),
1249 record
->bufferAddress
, recordLen
);
1250 if (recordFit
!= true)
1252 err
= fsBTRecordTooLargeErr
;
1256 err
= UpdateNode (btreePtr
, &nodeRec
, 0, kLockTransaction
);
1257 M_ExitOnError (err
);
1259 // update BTreeControlBlock
1260 btreePtr
->treeDepth
= 1;
1261 btreePtr
->rootNode
= insertNodeNum
;
1262 btreePtr
->firstLeafNode
= insertNodeNum
;
1263 btreePtr
->lastLeafNode
= insertNodeNum
;
1264 M_BTreeHeaderDirty (btreePtr
);
1268 default: goto ErrorExit
;
1273 recordFit
= InsertKeyRecord (btreePtr
, nodeRec
.buffer
, index
,
1274 &iterator
->key
, KeyLength(btreePtr
, &iterator
->key
),
1275 record
->bufferAddress
, recordLen
);
1276 if (recordFit
== true)
1278 err
= UpdateNode (btreePtr
, &nodeRec
, 0, kLockTransaction
);
1279 M_ExitOnError (err
);
1285 /////////////////////// Extend File If Necessary ////////////////////////////
1287 nodesNeeded
= btreePtr
->treeDepth
+ 1 - btreePtr
->freeNodes
; //\80\80 math limit
1288 if (nodesNeeded
> 0)
1290 nodesNeeded
+= btreePtr
->totalNodes
;
1291 if (nodesNeeded
> CalcMapBits (btreePtr
)) // we'll need to add a map node too!
1294 err
= ExtendBTree (btreePtr
, nodesNeeded
);
1295 M_ExitOnError (err
);
1298 // no need to delete existing record
1300 err
= InsertTree (btreePtr
, treePathTable
, &iterator
->key
, record
->bufferAddress
,
1301 recordLen
, &nodeRec
, index
, 1, kInsertRecord
, &insertNodeNum
);
1302 M_ExitOnError (err
);
1305 //////////////////////////////// Success ////////////////////////////////////
1308 ++btreePtr
->writeCount
;
1309 ++btreePtr
->leafRecords
;
1310 M_BTreeHeaderDirty (btreePtr
);
1313 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
1314 iterator
->hint
.nodeNum
= insertNodeNum
;
1315 iterator
->hint
.index
= 0; // unused
1316 iterator
->hint
.reserved1
= 0;
1317 iterator
->hint
.reserved2
= 0;
1322 ////////////////////////////// Error Exit ///////////////////////////////////
1326 (void) ReleaseNode (btreePtr
, &nodeRec
);
1328 iterator
->hint
.writeCount
= 0;
1329 iterator
->hint
.nodeNum
= 0;
1330 iterator
->hint
.index
= 0;
1331 iterator
->hint
.reserved1
= 0;
1332 iterator
->hint
.reserved2
= 0;
1334 if (err
== fsBTEmptyErr
)
1335 err
= fsBTRecordNotFoundErr
;
1341 //////////////////////////////// BTReplaceRecord ////////////////////////////////
1343 OSStatus
BTReplaceRecord (FCB
*filePtr
,
1344 BTreeIterator
*iterator
,
1345 FSBufferDescriptor
*record
,
1349 BTreeControlBlockPtr btreePtr
;
1350 TreePathTable treePathTable
;
1352 BlockDescriptor nodeRec
;
1353 UInt32 insertNodeNum
;
1359 ////////////////////////// Priliminary Checks ///////////////////////////////
1361 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
1363 err
= CheckInsertParams (filePtr
, iterator
, record
, recordLen
);
1367 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1369 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1371 ////////////////////////////// Take A Hint //////////////////////////////////
1373 err
= IsItAHint (btreePtr
, iterator
, &validHint
);
1374 M_ExitOnError (err
);
1378 insertNodeNum
= iterator
->hint
.nodeNum
;
1380 err
= GetNode (btreePtr
, insertNodeNum
, &nodeRec
);
1383 err
= TrySimpleReplace (btreePtr
, nodeRec
.buffer
, iterator
, record
, recordLen
, &recordFit
);
1384 M_ExitOnError (err
);
1388 err
= UpdateNode (btreePtr
, &nodeRec
, 0, 0);
1389 M_ExitOnError (err
);
1391 ++btreePtr
->numValidHints
;
1397 (void) BTInvalidateHint( iterator
);
1400 err
= ReleaseNode (btreePtr
, &nodeRec
);
1401 M_ExitOnError (err
);
1405 (void) BTInvalidateHint( iterator
);
1410 ////////////////////////////// Get A Clue ///////////////////////////////////
1412 err
= SearchTree (btreePtr
, &iterator
->key
, treePathTable
, &insertNodeNum
, &nodeRec
, &index
);
1413 M_ExitOnError (err
); // record must exit for Replace
1415 // optimization - if simple replace will work then don't extend btree
1416 // \80\80 if we tried this before, and failed because it wouldn't fit then we shouldn't try this again...
1418 err
= TrySimpleReplace (btreePtr
, nodeRec
.buffer
, iterator
, record
, recordLen
, &recordFit
);
1419 M_ExitOnError (err
);
1423 err
= UpdateNode (btreePtr
, &nodeRec
, 0, 0);
1424 M_ExitOnError (err
);
1430 //////////////////////////// Make Some Room /////////////////////////////////
1432 nodesNeeded
= btreePtr
->treeDepth
+ 1 - btreePtr
->freeNodes
; //\80\80 math limit
1433 if (nodesNeeded
> 0)
1435 nodesNeeded
+= btreePtr
->totalNodes
;
1436 if (nodesNeeded
> CalcMapBits (btreePtr
)) // we'll need to add a map node too!
1439 err
= ExtendBTree (btreePtr
, nodesNeeded
);
1440 M_ExitOnError (err
);
1444 DeleteRecord (btreePtr
, nodeRec
.buffer
, index
); // delete existing key/record
1446 err
= InsertTree (btreePtr
, treePathTable
, &iterator
->key
, record
->bufferAddress
,
1447 recordLen
, &nodeRec
, index
, 1, kReplaceRecord
, &insertNodeNum
);
1448 M_ExitOnError (err
);
1450 ++btreePtr
->writeCount
; /* writeCount changes only if the tree structure changed */
1454 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
1455 iterator
->hint
.nodeNum
= insertNodeNum
;
1456 iterator
->hint
.index
= 0; // unused
1457 iterator
->hint
.reserved1
= 0;
1458 iterator
->hint
.reserved2
= 0;
1463 ////////////////////////////// Error Exit ///////////////////////////////////
1467 (void) ReleaseNode (btreePtr
, &nodeRec
);
1469 iterator
->hint
.writeCount
= 0;
1470 iterator
->hint
.nodeNum
= 0;
1471 iterator
->hint
.index
= 0;
1472 iterator
->hint
.reserved1
= 0;
1473 iterator
->hint
.reserved2
= 0;
1480 //////////////////////////////// BTUpdateRecord ////////////////////////////////
1483 BTUpdateRecord(FCB
*filePtr
, BTreeIterator
*iterator
,
1484 IterateCallBackProcPtr callBackProc
, void * callBackState
)
1487 BTreeControlBlockPtr btreePtr
;
1488 TreePathTable treePathTable
;
1489 BlockDescriptor nodeRec
;
1490 RecordPtr recordPtr
;
1492 UInt32 insertNodeNum
;
1498 ////////////////////////// Priliminary Checks ///////////////////////////////
1500 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
1502 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1504 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1506 ////////////////////////////// Take A Hint //////////////////////////////////
1508 err
= IsItAHint (btreePtr
, iterator
, &validHint
);
1509 M_ExitOnError (err
);
1513 insertNodeNum
= iterator
->hint
.nodeNum
;
1515 err
= GetNode (btreePtr
, insertNodeNum
, &nodeRec
);
1518 if (((NodeDescPtr
)nodeRec
.buffer
)->kind
== kBTLeafNode
&&
1519 SearchNode (btreePtr
, nodeRec
.buffer
, &iterator
->key
, &index
))
1521 err
= GetRecordByIndex(btreePtr
, nodeRec
.buffer
, index
, &keyPtr
, &recordPtr
, &recordLen
);
1522 M_ExitOnError (err
);
1524 err
= callBackProc(keyPtr
, recordPtr
, recordLen
, callBackState
);
1525 M_ExitOnError (err
);
1527 err
= UpdateNode (btreePtr
, &nodeRec
, 0, 0);
1528 M_ExitOnError (err
);
1530 ++btreePtr
->numValidHints
;
1536 (void) BTInvalidateHint( iterator
);
1539 err
= ReleaseNode (btreePtr
, &nodeRec
);
1540 M_ExitOnError (err
);
1544 (void) BTInvalidateHint( iterator
);
1548 ////////////////////////////// Get A Clue ///////////////////////////////////
1550 err
= SearchTree (btreePtr
, &iterator
->key
, treePathTable
, &insertNodeNum
, &nodeRec
, &index
);
1551 M_ExitOnError (err
);
1553 err
= GetRecordByIndex(btreePtr
, nodeRec
.buffer
, index
, &keyPtr
, &recordPtr
, &recordLen
);
1554 M_ExitOnError (err
);
1556 err
= callBackProc(keyPtr
, recordPtr
, recordLen
, callBackState
);
1557 M_ExitOnError (err
);
1559 err
= UpdateNode (btreePtr
, &nodeRec
, 0, 0);
1560 M_ExitOnError (err
);
1564 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
1565 iterator
->hint
.nodeNum
= insertNodeNum
;
1566 iterator
->hint
.index
= 0;
1567 iterator
->hint
.reserved1
= 0;
1568 iterator
->hint
.reserved2
= 0;
1571 ////////////////////////////// Error Exit ///////////////////////////////////
1575 (void) ReleaseNode (btreePtr
, &nodeRec
);
1577 iterator
->hint
.writeCount
= 0;
1578 iterator
->hint
.nodeNum
= 0;
1579 iterator
->hint
.index
= 0;
1580 iterator
->hint
.reserved1
= 0;
1581 iterator
->hint
.reserved2
= 0;
1587 //////////////////////////////// BTDeleteRecord /////////////////////////////////
1589 OSStatus
BTDeleteRecord (FCB
*filePtr
,
1590 BTreeIterator
*iterator
)
1593 BTreeControlBlockPtr btreePtr
;
1594 TreePathTable treePathTable
;
1595 BlockDescriptor nodeRec
;
1600 ////////////////////////// Priliminary Checks ///////////////////////////////
1602 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
1604 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1605 M_ReturnErrorIf (iterator
== nil
, paramErr
);
1607 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1608 if (btreePtr
== nil
)
1610 err
= fsBTInvalidFileErr
;
1614 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1617 /////////////////////////////// Find Key ////////////////////////////////////
1619 //\80\80 check hint for simple delete case (index > 0, numRecords > 2)
1621 err
= SearchTree (btreePtr
, &iterator
->key
, treePathTable
, &nodeNum
, &nodeRec
, &index
);
1622 M_ExitOnError (err
); // record must exit for Delete
1625 ///////////////////////////// Delete Record /////////////////////////////////
1627 err
= DeleteTree (btreePtr
, treePathTable
, &nodeRec
, index
, 1);
1628 M_ExitOnError (err
);
1630 ++btreePtr
->writeCount
;
1631 --btreePtr
->leafRecords
;
1632 M_BTreeHeaderDirty (btreePtr
);
1634 iterator
->hint
.nodeNum
= 0;
1638 ////////////////////////////// Error Exit ///////////////////////////////////
1641 (void) ReleaseNode (btreePtr
, &nodeRec
);
1648 OSStatus
BTGetInformation (FCB
*filePtr
,
1650 BTreeInfoRec
*info
)
1652 #pragma unused (version)
1654 BTreeControlBlockPtr btreePtr
;
1657 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1659 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1663 * This should not require the whole tree to be locked, just maybe the BTreeControlBlockPtr
1665 * REQUIRE_FILE_LOCK(btreePtr->fileRefNum, true);
1668 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1669 M_ReturnErrorIf (info
== nil
, paramErr
);
1671 //\80\80 check version?
1673 info
->nodeSize
= btreePtr
->nodeSize
;
1674 info
->maxKeyLength
= btreePtr
->maxKeyLength
;
1675 info
->treeDepth
= btreePtr
->treeDepth
;
1676 info
->numRecords
= btreePtr
->leafRecords
;
1677 info
->numNodes
= btreePtr
->totalNodes
;
1678 info
->numFreeNodes
= btreePtr
->freeNodes
;
1679 info
->lastfsync
= btreePtr
->lastfsync
;
1687 /*-------------------------------------------------------------------------------
1688 Routine: BTFlushPath - Flush BTreeControlBlock to Header Node.
1690 Function: Brief_description_of_the_function_and_any_side_effects
1693 Input: pathPtr - pointer to path control block for B*Tree file to flush
1697 Result: noErr - success
1699 -------------------------------------------------------------------------------*/
1701 OSStatus
BTFlushPath (FCB
*filePtr
)
1704 BTreeControlBlockPtr btreePtr
;
1707 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1709 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1711 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1713 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1715 err
= UpdateHeader (btreePtr
, false);
1721 /*-------------------------------------------------------------------------------
1722 Routine: BTReload - Reload B-tree Header Data.
1724 Function: Reload B-tree header data from disk. This is called after fsck
1725 has made repairs to the root filesystem. The filesystem is
1726 mounted read-only when BTReload is caled.
1729 Input: filePtr - the B*Tree file that needs its header updated
1733 Result: noErr - success
1735 -------------------------------------------------------------------------------*/
1738 BTReloadData(FCB
*filePtr
)
1741 BTreeControlBlockPtr btreePtr
;
1742 BlockDescriptor node
;
1743 BTHeaderRec
*header
;
1746 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1747 if (btreePtr
== nil
)
1748 return (fsBTInvalidFileErr
);
1750 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1752 err
= GetNode(btreePtr
, kHeaderNodeNum
, &node
);
1756 header
= (BTHeaderRec
*)((char *)node
.buffer
+ sizeof(BTNodeDescriptor
));
1757 if ((err
= VerifyHeader (filePtr
, header
)) == 0) {
1758 btreePtr
->treeDepth
= header
->treeDepth
;
1759 btreePtr
->rootNode
= header
->rootNode
;
1760 btreePtr
->leafRecords
= header
->leafRecords
;
1761 btreePtr
->firstLeafNode
= header
->firstLeafNode
;
1762 btreePtr
->lastLeafNode
= header
->lastLeafNode
;
1763 btreePtr
->maxKeyLength
= header
->maxKeyLength
;
1764 btreePtr
->totalNodes
= header
->totalNodes
;
1765 btreePtr
->freeNodes
= header
->freeNodes
;
1766 btreePtr
->btreeType
= header
->btreeType
;
1768 btreePtr
->flags
&= (~kBTHeaderDirty
);
1771 (void) ReleaseNode(btreePtr
, &node
);
1777 /*-------------------------------------------------------------------------------
1778 Routine: BTInvalidateHint - Invalidates the hint within a BTreeInterator.
1780 Function: Invalidates the hint within a BTreeInterator.
1783 Input: iterator - pointer to BTreeIterator
1785 Output: iterator - iterator with the hint.nodeNum cleared
1787 Result: noErr - success
1788 paramErr - iterator == nil
1789 -------------------------------------------------------------------------------*/
1792 OSStatus
BTInvalidateHint (BTreeIterator
*iterator
)
1794 if (iterator
== nil
)
1797 iterator
->hint
.nodeNum
= 0;
1805 /*-------------------------------------------------------------------------------
1806 Routine: BTGetLastSync
1808 Function: Returns the last time that this btree was flushed, does not include header.
1810 Input: filePtr - pointer file control block
1812 Output: lastfsync - time in seconds of last update
1814 Result: noErr - success
1815 paramErr - iterator == nil
1816 -------------------------------------------------------------------------------*/
1819 OSStatus
BTGetLastSync (FCB
*filePtr
,
1822 BTreeControlBlockPtr btreePtr
;
1825 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1827 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1829 /* Maybe instead of requiring a lock..an atomic set might be more appropriate */
1830 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
1832 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1833 M_ReturnErrorIf (lastsync
== nil
, paramErr
);
1835 *lastsync
= btreePtr
->lastfsync
;
1843 /*-------------------------------------------------------------------------------
1844 Routine: BTSetLastSync
1846 Function: Sets the last time that this btree was flushed, does not include header.
1849 Input: fcb - pointer file control block
1851 Output: lastfsync - time in seconds of last update
1853 Result: noErr - success
1854 paramErr - iterator == nil
1855 -------------------------------------------------------------------------------*/
1858 OSStatus
BTSetLastSync (FCB
*filePtr
,
1861 BTreeControlBlockPtr btreePtr
;
1864 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1866 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1868 /* Maybe instead of requiring a lock..an atomic set might be more appropriate */
1869 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
1871 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1872 M_ReturnErrorIf (lastsync
== nil
, paramErr
);
1874 btreePtr
->lastfsync
= lastsync
;