2 * Copyright (c) 2000-2008 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
31 Contains: Implementation of public interface routines for B-tree manager.
35 Written by: Gordon Sheridan and Bill Bruffey
37 Copyright: © 1992-1999 by Apple Computer, Inc., all rights reserved.
43 Other Contact: Mark Day
45 Technology: File Systems
53 Change History (most recent first):
54 <MOSXS> 9/22/99 ser Added routines BTGetLastSync and BTSetLastSync
55 <MOSXS> 6/1/99 djb Sync up with Mac OS 8.6.
56 <MOSXS> 6/30/98 djb In BTOpenPath make sure nodes are contiguous on disk (radar #2249539).
57 <MOSXS> 4/15/98 djb In BTOpenPath need to clear nodeRec.buffer if GetBlockProc fails.
58 <MOSXS> 4/11/98 djb Add RequireFileLock checking to all external entry points.
60 <MOSXS> 03/23/98 djb In BTOpenPath use kTrashBlock option when releasing the header so
61 that we get a full node when we call GetNode.
63 <CS9> 12/12/97 djb Radar #2202682, BTIterateRecord with kBTreeCurrentRecord was not
64 checking if we had a record and could call BlockMove with an
65 uninitialize source pointer (causing a bus error).
66 <CS8> 10/24/97 msd In BTIterateRecord, when moving to the previous or next record
67 and we have to move to another node, see if we need to release
68 the node about to be "shifted out" (opposite sibling of the
69 direction we need to move).
70 <CS7> 7/25/97 DSH BTSearchRecord now takes a heuristicHint, nodeNum, and tries it
71 before calling SearchBTree
72 <CS6> 7/24/97 djb GetBlockProc now take a file refnum instead of an FCB ptr.
73 <CS5> 7/22/97 djb Move trace points from BTreeWrapper.c to here.
74 <CS4> 7/21/97 djb LogEndTime now takes an error code.
75 <CS3> 7/16/97 DSH FilesInternal.i renamed FileMgrInternal.i to avoid name
77 <CS2> 5/19/97 djb Add summary traces to BTIterateRecord.
78 <CS1> 4/23/97 djb first checked in
80 <HFS7> 2/19/97 djb Enable variable sized index keys for HFS+ volumes. Added node
81 cache to support nodes larger than 512 bytes.
82 <HFS6> 1/27/97 djb Calls to InsertTree and DeleteTree are now recursive (to support
83 variable sized index keys).
84 <HFS5> 1/13/97 djb Added support for getting current record to BTIterateRecord.
85 <HFS4> 1/6/97 djb Initialize "BigKeys" attribute in BTOpen.
86 <HFS3> 1/3/97 djb Added support for large keys.
87 <HFS2> 12/23/96 djb On exit map fsBTEmptyErr and fsBTEndOfIterationErr to
88 fsBTRecordNotFoundErr.
89 <HFS1> 12/19/96 djb first checked in
91 History applicable to original Scarecrow Design:
93 <13> 10/25/96 ser Changing for new VFPI
94 <12> 10/18/96 ser Converting over VFPI changes
95 <11> 9/17/96 dkh More BTree statistics. Modified hint checks to not bail out when
96 an error is returned from GetNode.
97 <10> 9/16/96 dkh Revised BTree statistics.
98 <9> 8/23/96 dkh Remove checks for multiple paths to BTree file. Need to add
99 equivalent mechanism later.
100 <8> 6/20/96 dkh Radar #1358740. Switch from using Pools to debug MemAllocators.
101 <7> 3/14/96 jev Fix BTreeSetRecord, recordFound was not set for the case of a
102 simple replace causing the leafRecords count to get bumped even
103 though we didn't have to add a record.
104 <6> 3/1/96 prp Fix lint problems. Bug in BTSetRecord that does not initialize
106 <5> 1/22/96 dkh Add #include Memory.h
107 <4> 1/10/96 msd Use the real function names from Math64.i.
108 <3> 1/4/96 jev Fix BTItererateRecord for the condition when the iterator
109 position routine does not find the record and we are looking for
110 the next record. In such a case, if the node's forrward link is
111 non-zero, we have to keep iterating next and not return
112 fsBTEndOfIterationErr error.
113 <2> 12/7/95 dkh D10E2 build. Changed usage of Ref data type to LogicalAddress.
114 <1> 10/18/95 rst Moved from Scarecrow project.
116 <24> 7/18/95 mbb Change MoveData & ClearBytes to BlockMoveData & BlockZero.
117 <23> 1/31/95 prp GetBlockProc interface uses a 64 bit node number.
118 <22> 1/12/95 wjk Adopt Model FileSystem changes in D5.
119 <21> 11/16/94 prp Add IsItAHint routine and use it whenever hint's node number was
121 <20> 11/10/94 prp BTGetInfo name collides with the same name in FileManagerPriv.i.
122 Change it to BTGetInformation.
123 <19> 9/30/94 prp Get in sync with D2 interface changes.
124 <18> 7/22/94 wjk Convert to the new set of header files.
125 <17> 12/9/93 wjk Cleanup usage of char, Byte, int8, UInt8, etc.
126 <16> 12/2/93 wjk Move from Makefiles to BuildFiles. Fit into the ModernOS and
128 <15> 11/30/93 wjk Move from Makefiles to BuildFiles. Fit into the ModernOS and
130 <14> 9/30/93 gs Rename E_NoGetNodeProc and E_NoReleaseNodeProc to
132 <13> 8/31/93 prp Use Set64U instead of Set64.
133 <12> 8/16/93 prp In BTSearchRecord, if the input hint found the node and record,
134 set the local nodeNum variable correctly so that the resultant
135 iterator gets set correctly.
136 <11> 7/1/93 gs Fix bug in BTIterateRecord related to kBTreePrevRecord
138 <10> 6/2/93 gs Update for changes to FSErrors.h and add some comments.
139 <9> 5/24/93 gs Fix bug in BTInsert/Set/ReplaceRecord which didn't set node hint
140 properly in some cases.
141 <8> 5/24/93 gs Do NOT map fsBTEmptyErr to fsBTRecordNotFoundErr in BTSearchRecord.
142 <7> 5/24/93 gs Rename BTFlush to BTFlushPath.
143 <6> 5/21/93 gs Add hint optimization to Set/Replace routines.
144 <5> 5/10/93 gs Remove Panic from BTInitialize for small logicalEOF. Implement
145 Insert, Set, Replace, and Delete.
146 <4> 3/23/93 gs Finish BTInitialize.
147 <3> 2/8/93 gs Implement BTSearchRecord and BTIterateRecord.
148 <2> 12/8/92 gs Implement Open and Close routines.
149 <1> 11/15/92 gs first checked in
153 #include "../headers/BTreesPrivate.h"
154 #include "../../hfs_btreeio.h"
157 * The amount that the BTree header leaf count can be wrong before we assume
158 * it is in an infinite loop.
160 #define kNumLeafRecSlack 10
162 //////////////////////////////////// Globals ////////////////////////////////////
165 /////////////////////////// BTree Module Entry Points ///////////////////////////
169 /*-------------------------------------------------------------------------------
170 Routine: BTOpenPath - Open a file for access as a B*Tree.
172 Function: Create BTree control block for a file, if necessary. Validates the
173 file to be sure it looks like a BTree file.
176 Input: filePtr - pointer to file to open as a B-tree
177 keyCompareProc - pointer to client's KeyCompare function
179 Result: noErr - success
180 paramErr - required ptr was nil
184 -------------------------------------------------------------------------------*/
186 OSStatus
BTOpenPath(FCB
*filePtr
, KeyCompareProcPtr keyCompareProc
)
189 BTreeControlBlockPtr btreePtr
;
193 ////////////////////// Preliminary Error Checking ///////////////////////////
195 if ( filePtr
== nil
)
201 * Subsequent opens allow key compare proc to be changed.
203 if ( filePtr
->fcbBTCBPtr
!= nil
&& keyCompareProc
!= nil
) {
204 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
205 btreePtr
->keyCompareProc
= keyCompareProc
;
209 if ( filePtr
->fcbEOF
< kMinNodeSize
)
210 return fsBTInvalidFileErr
;
213 //////////////////////// Allocate Control Block /////////////////////////////
215 btreePtr
= (BTreeControlBlock
*) NewPtrSysClear( sizeof( BTreeControlBlock
) );
218 Panic ("BTOpen: no memory for btreePtr.");
222 btreePtr
->getBlockProc
= GetBTreeBlock
;
223 btreePtr
->releaseBlockProc
= ReleaseBTreeBlock
;
224 btreePtr
->setEndOfForkProc
= ExtendBTreeFile
;
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 /* Prefer doing I/O a physical block at a time */
234 nodeRec
.blockSize
= VTOHFS(btreePtr
->fileRefNum
)->hfs_physical_block_size
;
236 /* Start with the allocation block size for regular files. */
237 if (FTOC(filePtr
)->c_fileid
>= kHFSFirstUserCatalogNodeID
)
239 nodeRec
.blockSize
= FCBTOVCB(filePtr
)->blockSize
;
241 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
243 // it is now safe to call M_ExitOnError (err)
245 err
= SetBTreeBlockSize (btreePtr
->fileRefNum
, nodeRec
.blockSize
, 1);
249 err
= GetBTreeBlock(btreePtr
->fileRefNum
,
255 nodeRec
.buffer
= nil
;
256 nodeRec
.blockHeader
= nil
;
257 Panic("BTOpen: getNodeProc returned error getting header node.");
260 ++btreePtr
->numGetNodes
;
261 header
= (BTHeaderRec
*) ((uintptr_t)nodeRec
.buffer
+ sizeof(BTNodeDescriptor
));
264 ///////////////////////////// verify header /////////////////////////////////
266 err
= VerifyHeader (filePtr
, header
);
270 ///////////////////// Initalize fields from header //////////////////////////
272 PanicIf ( (FCBTOVCB(filePtr
)->vcbSigWord
!= 0x4244) && (header
->nodeSize
== 512), " BTOpenPath: wrong node size for HFS+ volume!"); // 0x4244 = 'BD'
274 btreePtr
->treeDepth
= header
->treeDepth
;
275 btreePtr
->rootNode
= header
->rootNode
;
276 btreePtr
->leafRecords
= header
->leafRecords
;
277 btreePtr
->firstLeafNode
= header
->firstLeafNode
;
278 btreePtr
->lastLeafNode
= header
->lastLeafNode
;
279 btreePtr
->nodeSize
= header
->nodeSize
;
280 btreePtr
->maxKeyLength
= header
->maxKeyLength
;
281 btreePtr
->totalNodes
= header
->totalNodes
;
282 btreePtr
->freeNodes
= header
->freeNodes
;
283 if (FTOC(filePtr
)->c_fileid
>= kHFSFirstUserCatalogNodeID
)
284 filePtr
->ff_clumpsize
= header
->clumpSize
;
285 btreePtr
->btreeType
= header
->btreeType
;
287 btreePtr
->keyCompareType
= header
->keyCompareType
;
289 btreePtr
->attributes
= header
->attributes
;
291 if ( btreePtr
->maxKeyLength
> 40 )
292 btreePtr
->attributes
|= (kBTBigKeysMask
+ kBTVariableIndexKeysMask
); //\80\80 we need a way to save these attributes
294 /////////////////////// Initialize dynamic fields ///////////////////////////
296 btreePtr
->version
= kBTreeVersion
;
298 btreePtr
->writeCount
= 1;
300 /////////////////////////// Check Header Node ///////////////////////////////
302 // set kBadClose attribute bit, and UpdateNode
304 /* b-tree node size must be at least as big as the logical block size */
305 if (btreePtr
->nodeSize
< VTOHFS(btreePtr
->fileRefNum
)->hfs_logical_block_size
)
308 * If this tree has any records or the media is writeable then
309 * we cannot mount using the current physical block size.
311 if (btreePtr
->leafRecords
> 0 ||
312 VTOHFS(btreePtr
->fileRefNum
)->hfs_flags
& HFS_WRITEABLE_MEDIA
)
314 err
= fsBTBadNodeSize
;
320 * If the actual node size is different than the amount we read,
321 * then release and trash this block, and re-read with the correct
324 if ( btreePtr
->nodeSize
!= nodeRec
.blockSize
)
326 err
= SetBTreeBlockSize (btreePtr
->fileRefNum
, btreePtr
->nodeSize
, 32);
330 * Need to use kTrashBlock option to force the
331 * buffer cache to read the entire node
333 err
= ReleaseBTreeBlock(btreePtr
->fileRefNum
, &nodeRec
, kTrashBlock
);
334 ++btreePtr
->numReleaseNodes
;
337 err
= GetNode (btreePtr
, kHeaderNodeNum
, 0, &nodeRec
);
341 //\80\80 total nodes * node size <= LEOF?
344 err
= ReleaseNode (btreePtr
, &nodeRec
);
348 * Under Mac OS, b-tree nodes can be non-contiguous on disk when the
349 * allocation block size is smaller than the b-tree node size.
351 * If journaling is turned on for this volume we can't deal with this
352 * situation and so we bail out. If journaling isn't on it's ok as
353 * hfs_strategy_fragmented() deals with it. Journaling can't support
354 * this because it assumes that if you give it a block that it's
355 * contiguous on disk.
357 if ( FCBTOHFS(filePtr
)->jnl
&& !NodesAreContiguous(FCBTOVCB(filePtr
), filePtr
, btreePtr
->nodeSize
) ) {
358 return fsBTInvalidNodeErr
;
361 //////////////////////////////// Success ////////////////////////////////////
363 //\80\80 align LEOF to multiple of node size? - just on close
368 /////////////////////// Error - Clean up and Exit ///////////////////////////
372 filePtr
->fcbBTCBPtr
= nil
;
373 (void) ReleaseNode (btreePtr
, &nodeRec
);
374 DisposePtr( (Ptr
) btreePtr
);
381 /*-------------------------------------------------------------------------------
382 Routine: BTClosePath - Flush BTree Header and Deallocate Memory for BTree.
384 Function: Flush the BTreeControlBlock fields to header node, and delete BTree control
385 block and key descriptor associated with the file if filePtr is last
386 path of type kBTreeType ('btre').
389 Input: filePtr - pointer to file to delete BTree control block for.
391 Result: noErr - success
394 -------------------------------------------------------------------------------*/
396 OSStatus
BTClosePath (FCB
*filePtr
)
399 BTreeControlBlockPtr btreePtr
;
401 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
404 return fsBTInvalidFileErr
;
406 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
408 ////////////////////// Check for other BTree Paths //////////////////////////
410 btreePtr
->attributes
&= ~kBTBadCloseMask
; // clear "bad close" attribute bit
411 err
= UpdateHeader (btreePtr
, true);
414 DisposePtr( (Ptr
) btreePtr
);
415 filePtr
->fcbBTCBPtr
= nil
;
419 /////////////////////// Error - Clean Up and Exit ///////////////////////////
428 /*-------------------------------------------------------------------------------
429 Routine: BTSearchRecord - Search BTree for a record with a matching key.
431 Function: Search for position in B*Tree indicated by searchKey. If a valid node hint
432 is provided, it will be searched first, then SearchTree will be called.
433 If a BTreeIterator is provided, it will be set to the position found as
434 a result of the search. If a record exists at that position, and a BufferDescriptor
435 is supplied, the record will be copied to the buffer (as much as will fit),
436 and recordLen will be set to the length of the record.
438 If an error other than fsBTRecordNotFoundErr occurs, the BTreeIterator, if any,
439 is invalidated, and recordLen is set to 0.
442 Input: pathPtr - pointer to path for BTree file.
443 searchKey - pointer to search key to match.
444 hintPtr - pointer to hint (may be nil)
446 Output: record - pointer to BufferDescriptor containing record
447 recordLen - length of data at recordPtr
448 iterator - pointer to BTreeIterator indicating position result of search
450 Result: noErr - success, record contains copy of record found
451 fsBTRecordNotFoundErr - record was not found, no data copied
452 fsBTInvalidFileErr - no BTreeControlBlock is allocated for the fork
453 fsBTInvalidKeyLengthErr -
455 -------------------------------------------------------------------------------*/
457 OSStatus
BTSearchRecord (FCB
*filePtr
,
458 BTreeIterator
*searchIterator
,
459 FSBufferDescriptor
*record
,
460 u_int16_t
*recordLen
,
461 BTreeIterator
*resultIterator
)
464 BTreeControlBlockPtr btreePtr
;
465 TreePathTable treePathTable
;
467 BlockDescriptor node
;
480 if (searchIterator
== nil
)
486 node
.blockHeader
= nil
;
488 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
491 return fsBTInvalidFileErr
;
494 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
498 ////////////////////////////// Take A Hint //////////////////////////////////
500 err
= IsItAHint (btreePtr
, searchIterator
, &validHint
);
505 nodeNum
= searchIterator
->hint
.nodeNum
;
507 err
= GetNode (btreePtr
, nodeNum
, kGetNodeHint
, &node
);
510 if ( ((BTNodeDescriptor
*) node
.buffer
)->kind
== kBTLeafNode
&&
511 ((BTNodeDescriptor
*) node
.buffer
)->numRecords
> 0 )
513 foundRecord
= SearchNode (btreePtr
, node
.buffer
, &searchIterator
->key
, &index
);
515 //\80\80 if !foundRecord, we could still skip tree search if ( 0 < index < numRecords )
518 if (foundRecord
== false)
520 err
= ReleaseNode (btreePtr
, &node
);
525 ++btreePtr
->numValidHints
;
529 if( foundRecord
== false )
530 (void) BTInvalidateHint( searchIterator
);
534 //////////////////////////// Search The Tree ////////////////////////////////
536 if (foundRecord
== false)
538 err
= SearchTree ( btreePtr
, &searchIterator
->key
, treePathTable
, &nodeNum
, &node
, &index
);
544 case fsBTRecordNotFoundErr
:
552 //////////////////////////// Get the Record /////////////////////////////////
554 if (foundRecord
== true)
556 //XXX Should check for errors! Or BlockMove could choke on recordPtr!!!
557 GetRecordByIndex (btreePtr
, node
.buffer
, index
, &keyPtr
, &recordPtr
, &len
);
559 if (recordLen
!= nil
) *recordLen
= len
;
563 ByteCount recordSize
;
565 recordSize
= record
->itemCount
* record
->itemSize
;
567 if (len
> recordSize
) len
= recordSize
;
569 BlockMoveData (recordPtr
, record
->bufferAddress
, len
);
574 /////////////////////// Success - Update Iterator ///////////////////////////
576 if (resultIterator
!= nil
)
579 resultIterator
->hint
.writeCount
= btreePtr
->writeCount
;
580 resultIterator
->hint
.nodeNum
= nodeNum
;
581 resultIterator
->hint
.index
= index
;
584 resultIterator
->hint
.reserved1
= 0;
585 resultIterator
->hint
.reserved2
= 0;
586 resultIterator
->version
= 0;
587 resultIterator
->reserved
= 0;
589 // copy the key in the BTree when found rather than searchIterator->key to get proper case/diacriticals
590 if (foundRecord
== true)
591 BlockMoveData ((Ptr
)keyPtr
, (Ptr
)&resultIterator
->key
, CalcKeySize(btreePtr
, keyPtr
));
593 BlockMoveData ((Ptr
)&searchIterator
->key
, (Ptr
)&resultIterator
->key
, CalcKeySize(btreePtr
, &searchIterator
->key
));
596 err
= ReleaseNode (btreePtr
, &node
);
599 if (foundRecord
== false) return fsBTRecordNotFoundErr
;
603 /////////////////////// Error - Clean Up and Exit ///////////////////////////
607 if (recordLen
!= nil
)
610 if (resultIterator
!= nil
)
612 resultIterator
->hint
.writeCount
= 0;
613 resultIterator
->hint
.nodeNum
= 0;
614 resultIterator
->hint
.index
= 0;
615 resultIterator
->hint
.reserved1
= 0;
616 resultIterator
->hint
.reserved2
= 0;
618 resultIterator
->version
= 0;
619 resultIterator
->reserved
= 0;
620 resultIterator
->key
.length16
= 0; // zero out two bytes to cover both types of keys
623 if ( err
== fsBTEmptyErr
)
624 err
= fsBTRecordNotFoundErr
;
631 /*-------------------------------------------------------------------------------
632 Routine: BTIterateRecord - Find the first, next, previous, or last record.
634 Function: Find the first, next, previous, or last record in the BTree
636 Input: pathPtr - pointer to path iterate records for.
637 operation - iteration operation (first,next,prev,last)
638 iterator - pointer to iterator indicating start position
640 Output: iterator - iterator is updated to indicate new position
641 newKeyPtr - pointer to buffer to copy key found by iteration
642 record - pointer to buffer to copy record found by iteration
643 recordLen - length of record
645 Result: noErr - success
647 -------------------------------------------------------------------------------*/
649 OSStatus
BTIterateRecord (FCB
*filePtr
,
650 BTreeIterationOperation operation
,
651 BTreeIterator
*iterator
,
652 FSBufferDescriptor
*record
,
653 u_int16_t
*recordLen
)
656 BTreeControlBlockPtr btreePtr
;
664 BlockDescriptor left
, node
, right
;
668 ////////////////////////// Priliminary Checks ///////////////////////////////
671 left
.blockHeader
= nil
;
673 right
.blockHeader
= nil
;
675 node
.blockHeader
= nil
;
683 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
686 return fsBTInvalidFileErr
; //\80\80 handle properly
689 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
691 if ((operation
!= kBTreeFirstRecord
) &&
692 (operation
!= kBTreeNextRecord
) &&
693 (operation
!= kBTreeCurrentRecord
) &&
694 (operation
!= kBTreePrevRecord
) &&
695 (operation
!= kBTreeLastRecord
))
697 err
= fsInvalidIterationMovmentErr
;
701 /////////////////////// Find First or Last Record ///////////////////////////
703 if ((operation
== kBTreeFirstRecord
) || (operation
== kBTreeLastRecord
))
705 if (operation
== kBTreeFirstRecord
) nodeNum
= btreePtr
->firstLeafNode
;
706 else nodeNum
= btreePtr
->lastLeafNode
;
714 err
= GetNode (btreePtr
, nodeNum
, 0, &node
);
717 if ( ((NodeDescPtr
) node
.buffer
)->kind
!= kBTLeafNode
||
718 ((NodeDescPtr
) node
.buffer
)->numRecords
<= 0 )
720 err
= ReleaseNode (btreePtr
, &node
);
723 err
= fsBTInvalidNodeErr
;
724 printf ("hfs: BTIterateRecord() found invalid btree node on volume %s\n", FCBTOVCB(filePtr
)->vcbVN
);
725 hfs_mark_volume_inconsistent(FCBTOVCB(filePtr
));
729 if (operation
== kBTreeFirstRecord
) index
= 0;
730 else index
= ((BTNodeDescriptor
*) node
.buffer
)->numRecords
- 1;
732 goto CopyData
; //\80\80 is there a cleaner way?
736 //////////////////////// Find Iterator Position /////////////////////////////
738 // Not called for (operation == kBTreeFirstRecord || operation == kBTreeLastRecord)
739 err
= FindIteratorPosition (btreePtr
, iterator
,
740 &left
, &node
, &right
, &nodeNum
, &index
, &foundRecord
);
744 ///////////////////// Find Next Or Previous Record //////////////////////////
746 if (operation
== kBTreePrevRecord
)
754 if (left
.buffer
== nil
)
756 nodeNum
= ((NodeDescPtr
) node
.buffer
)->bLink
;
759 // BTree nodes are always grabbed in left to right order.
760 // Therefore release the current node before looking up the
762 err
= ReleaseNode(btreePtr
, &node
);
765 // Look up the left node
766 err
= GetNode (btreePtr
, nodeNum
, 0, &left
);
769 // Look up the current node again
770 err
= GetRightSiblingNode (btreePtr
, left
.buffer
, &node
);
773 err
= fsBTStartOfIterationErr
;
777 // Before we stomp on "right", we'd better release it if needed
778 if (right
.buffer
!= nil
) {
779 err
= ReleaseNode(btreePtr
, &right
);
785 index
= ((NodeDescPtr
) node
.buffer
)->numRecords
-1;
788 else if (operation
== kBTreeNextRecord
)
790 if ((foundRecord
!= true) &&
791 (((NodeDescPtr
) node
.buffer
)->fLink
== 0) &&
792 (index
== ((NodeDescPtr
) node
.buffer
)->numRecords
))
794 err
= fsBTEndOfIterationErr
;
798 // we did not find the record but the index is already positioned correctly
799 if ((foundRecord
== false) && (index
!= ((NodeDescPtr
) node
.buffer
)->numRecords
))
802 // we found the record OR we have to look in the next node
803 if (index
< ((NodeDescPtr
) node
.buffer
)->numRecords
-1)
809 if (right
.buffer
== nil
)
811 nodeNum
= ((NodeDescPtr
) node
.buffer
)->fLink
;
814 err
= GetNode (btreePtr
, nodeNum
, 0, &right
);
817 err
= fsBTEndOfIterationErr
;
821 // Before we stomp on "left", we'd better release it if needed
822 if (left
.buffer
!= nil
) {
823 err
= ReleaseNode(btreePtr
, &left
);
832 else // operation == kBTreeCurrentRecord
834 // make sure we have something... <CS9>
835 if ((foundRecord
!= true) &&
836 (index
>= ((NodeDescPtr
) node
.buffer
)->numRecords
))
838 err
= fsBTEndOfIterationErr
;
843 //////////////////// Copy Record And Update Iterator ////////////////////////
847 // added check for errors <CS9>
848 err
= GetRecordByIndex (btreePtr
, node
.buffer
, index
, &keyPtr
, &recordPtr
, &len
);
851 if (recordLen
!= nil
)
856 ByteCount recordSize
;
858 recordSize
= record
->itemCount
* record
->itemSize
;
860 if (len
> recordSize
) len
= recordSize
;
862 BlockMoveData (recordPtr
, record
->bufferAddress
, len
);
865 if (iterator
!= nil
) // first & last do not require iterator
867 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
868 iterator
->hint
.nodeNum
= nodeNum
;
869 iterator
->hint
.index
= index
;
870 iterator
->hint
.reserved1
= 0;
871 iterator
->hint
.reserved2
= 0;
873 iterator
->version
= 0;
874 iterator
->reserved
= 0;
877 * Check for infinite loops by making sure we do not
878 * process more leaf records, than can possibly be (or the BTree header
879 * is seriously damaged)....a brute force method.
881 if ((operation
== kBTreeFirstRecord
) || (operation
== kBTreeLastRecord
))
882 iterator
->hitCount
= 1;
883 else if (operation
!= kBTreeCurrentRecord
)
884 iterator
->hitCount
+= 1;
885 /* Always use the highest max, in case the grows while iterating */
886 iterator
->maxLeafRecs
= max(btreePtr
->leafRecords
, iterator
->maxLeafRecs
);
889 if (iterator
->hitCount
> iterator
->maxLeafRecs
+ kNumLeafRecSlack
)
891 err
= fsBTInvalidNodeErr
;
892 printf ("hfs: BTIterateRecord() found invalid btree node on volume %s\n", FCBTOVCB(filePtr
)->vcbVN
);
893 hfs_mark_volume_inconsistent(FCBTOVCB(filePtr
));
898 BlockMoveData ((Ptr
)keyPtr
, (Ptr
)&iterator
->key
, CalcKeySize(btreePtr
, keyPtr
));
902 ///////////////////////////// Release Nodes /////////////////////////////////
904 err
= ReleaseNode (btreePtr
, &node
);
907 if (left
.buffer
!= nil
)
909 err
= ReleaseNode (btreePtr
, &left
);
913 if (right
.buffer
!= nil
)
915 err
= ReleaseNode (btreePtr
, &right
);
921 /////////////////////// Error - Clean Up and Exit ///////////////////////////
925 (void) ReleaseNode (btreePtr
, &left
);
926 (void) ReleaseNode (btreePtr
, &node
);
927 (void) ReleaseNode (btreePtr
, &right
);
929 if (recordLen
!= nil
)
934 iterator
->hint
.writeCount
= 0;
935 iterator
->hint
.nodeNum
= 0;
936 iterator
->hint
.index
= 0;
937 iterator
->hint
.reserved1
= 0;
938 iterator
->hint
.reserved2
= 0;
940 iterator
->version
= 0;
941 iterator
->reserved
= 0;
942 iterator
->key
.length16
= 0;
945 if ( err
== fsBTEmptyErr
|| err
== fsBTEndOfIterationErr
)
946 err
= fsBTRecordNotFoundErr
;
952 /*-------------------------------------------------------------------------------
953 Routine: BTIterateRecords
955 Function: Find a series of records
957 Input: filePtr - b-tree file
958 operation - iteration operation (first,next,prev,last)
959 iterator - pointer to iterator indicating start position
960 callBackProc - pointer to routince to process a record
961 callBackState - pointer to state data (used by callBackProc)
963 Output: iterator - iterator is updated to indicate new position
965 Result: noErr - success
967 -------------------------------------------------------------------------------*/
970 BTIterateRecords(FCB
*filePtr
, BTreeIterationOperation operation
, BTreeIterator
*iterator
,
971 IterateCallBackProcPtr callBackProc
, void * callBackState
)
974 BTreeControlBlockPtr btreePtr
;
980 BlockDescriptor left
, node
, right
;
984 ////////////////////////// Priliminary Checks ///////////////////////////////
987 left
.blockHeader
= nil
;
989 right
.blockHeader
= nil
;
991 node
.blockHeader
= nil
;
993 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
995 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
997 if ((operation
!= kBTreeFirstRecord
) &&
998 (operation
!= kBTreeNextRecord
) &&
999 (operation
!= kBTreeCurrentRecord
) &&
1000 (operation
!= kBTreePrevRecord
) &&
1001 (operation
!= kBTreeLastRecord
))
1003 err
= fsInvalidIterationMovmentErr
;
1007 /////////////////////// Find First or Last Record ///////////////////////////
1009 if ((operation
== kBTreeFirstRecord
) || (operation
== kBTreeLastRecord
))
1011 if (operation
== kBTreeFirstRecord
)
1012 nodeNum
= btreePtr
->firstLeafNode
;
1014 nodeNum
= btreePtr
->lastLeafNode
;
1022 err
= GetNode(btreePtr
, nodeNum
, 0, &node
);
1025 if ( ((NodeDescPtr
)node
.buffer
)->kind
!= kBTLeafNode
||
1026 ((NodeDescPtr
)node
.buffer
)->numRecords
<= 0 )
1028 err
= ReleaseNode(btreePtr
, &node
);
1031 err
= fsBTInvalidNodeErr
;
1032 printf ("hfs: BTIterateRecords() found invalid btree node on volume %s\n", FCBTOVCB(filePtr
)->vcbVN
);
1033 hfs_mark_volume_inconsistent(FCBTOVCB(filePtr
));
1037 if (operation
== kBTreeFirstRecord
)
1040 index
= ((BTNodeDescriptor
*) node
.buffer
)->numRecords
- 1;
1045 //////////////////////// Find Iterator Position /////////////////////////////
1047 // Not called for (operation == kBTreeFirstRecord || operation == kBTreeLastRecord)
1048 err
= FindIteratorPosition(btreePtr
, iterator
, &left
, &node
, &right
,
1049 &nodeNum
, &index
, &foundRecord
);
1050 if (err
== fsBTRecordNotFoundErr
)
1055 ///////////////////// Find Next Or Previous Record //////////////////////////
1057 if (operation
== kBTreePrevRecord
)
1065 if (left
.buffer
== nil
)
1067 nodeNum
= ((NodeDescPtr
) node
.buffer
)->bLink
;
1070 // BTree nodes are always grabbed in left to right order.
1071 // Therefore release the current node before looking up the
1073 err
= ReleaseNode(btreePtr
, &node
);
1076 // Look up the left node
1077 err
= GetNode (btreePtr
, nodeNum
, 0, &left
);
1078 M_ExitOnError (err
);
1080 // Look up the current node again
1081 err
= GetRightSiblingNode (btreePtr
, left
.buffer
, &node
);
1082 M_ExitOnError (err
);
1084 err
= fsBTStartOfIterationErr
;
1088 // Before we stomp on "right", we'd better release it if needed
1089 if (right
.buffer
!= nil
) {
1090 err
= ReleaseNode(btreePtr
, &right
);
1096 index
= ((NodeDescPtr
) node
.buffer
)->numRecords
-1;
1099 else if (operation
== kBTreeNextRecord
)
1101 if ((foundRecord
!= true) &&
1102 (((NodeDescPtr
)node
.buffer
)->fLink
== 0) &&
1103 (index
== ((NodeDescPtr
)node
.buffer
)->numRecords
))
1105 err
= fsBTEndOfIterationErr
;
1109 // we did not find the record but the index is already positioned correctly
1110 if ((foundRecord
== false) && (index
!= ((NodeDescPtr
)node
.buffer
)->numRecords
))
1113 // we found the record OR we have to look in the next node
1114 if (index
< ((NodeDescPtr
)node
.buffer
)->numRecords
-1)
1120 if (right
.buffer
== nil
)
1122 nodeNum
= ((NodeDescPtr
)node
.buffer
)->fLink
;
1125 err
= GetNode(btreePtr
, nodeNum
, 0, &right
);
1128 err
= fsBTEndOfIterationErr
;
1132 // Before we stomp on "left", we'd better release it if needed
1133 if (left
.buffer
!= nil
) {
1134 err
= ReleaseNode(btreePtr
, &left
);
1143 else // operation == kBTreeCurrentRecord
1145 // make sure we have something... <CS9>
1146 if ((foundRecord
!= true) &&
1147 (index
>= ((NodeDescPtr
)node
.buffer
)->numRecords
))
1149 err
= fsBTEndOfIterationErr
;
1154 //////////////////// Process Records Using Callback ////////////////////////
1157 err
= GetRecordByIndex(btreePtr
, node
.buffer
, index
, &keyPtr
, &recordPtr
, &len
);
1164 if (callBackProc(keyPtr
, recordPtr
, callBackState
) == 0)
1167 if ((index
+1) < ((NodeDescPtr
)node
.buffer
)->numRecords
) {
1170 if (right
.buffer
== nil
)
1172 nodeNum
= ((NodeDescPtr
)node
.buffer
)->fLink
;
1175 err
= GetNode(btreePtr
, nodeNum
, 0, &right
);
1178 err
= fsBTEndOfIterationErr
;
1182 // Before we stomp on "left", we'd better release it if needed
1183 if (left
.buffer
!= nil
) {
1184 err
= ReleaseNode(btreePtr
, &left
);
1192 err
= GetRecordByIndex(btreePtr
, node
.buffer
, index
,
1193 &keyPtr
, &recordPtr
, &len
);
1201 ///////////////// Update Iterator to Last Item Processed /////////////////////
1204 if (iterator
!= nil
) // first & last have optional iterator
1206 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
1207 iterator
->hint
.nodeNum
= nodeNum
;
1208 iterator
->hint
.index
= index
;
1209 iterator
->version
= 0;
1211 BlockMoveData((Ptr
)keyPtr
, (Ptr
)&iterator
->key
, CalcKeySize(btreePtr
, keyPtr
));
1216 ///////////////////////////// Release Nodes /////////////////////////////////
1218 err
= ReleaseNode(btreePtr
, &node
);
1221 if (left
.buffer
!= nil
)
1223 err
= ReleaseNode(btreePtr
, &left
);
1227 if (right
.buffer
!= nil
)
1229 err
= ReleaseNode(btreePtr
, &right
);
1235 /////////////////////// Error - Clean Up and Exit ///////////////////////////
1239 (void) ReleaseNode(btreePtr
, &left
);
1240 (void) ReleaseNode(btreePtr
, &node
);
1241 (void) ReleaseNode(btreePtr
, &right
);
1243 if (iterator
!= nil
)
1245 iterator
->hint
.writeCount
= 0;
1246 iterator
->hint
.nodeNum
= 0;
1247 iterator
->hint
.index
= 0;
1248 iterator
->version
= 0;
1249 iterator
->key
.length16
= 0;
1252 if ( err
== fsBTEmptyErr
|| err
== fsBTEndOfIterationErr
)
1253 err
= fsBTRecordNotFoundErr
;
1259 //////////////////////////////// BTInsertRecord /////////////////////////////////
1261 OSStatus
BTInsertRecord (FCB
*filePtr
,
1262 BTreeIterator
*iterator
,
1263 FSBufferDescriptor
*record
,
1264 u_int16_t recordLen
)
1267 BTreeControlBlockPtr btreePtr
;
1268 TreePathTable treePathTable
;
1269 u_int32_t nodesNeeded
;
1270 BlockDescriptor nodeRec
;
1271 u_int32_t insertNodeNum
;
1275 ////////////////////////// Priliminary Checks ///////////////////////////////
1277 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
1278 nodeRec
.blockHeader
= nil
;
1280 err
= CheckInsertParams (filePtr
, iterator
, record
, recordLen
);
1284 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1286 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1289 ///////////////////////// Find Insert Position //////////////////////////////
1291 // always call SearchTree for Insert
1292 err
= SearchTree (btreePtr
, &iterator
->key
, treePathTable
, &insertNodeNum
, &nodeRec
, &index
);
1294 switch (err
) // set/replace/insert decision point
1296 case noErr
: err
= fsBTDuplicateRecordErr
;
1299 case fsBTRecordNotFoundErr
: break;
1301 case fsBTEmptyErr
: // if tree empty add 1st leaf node
1303 if (btreePtr
->freeNodes
== 0)
1305 err
= ExtendBTree (btreePtr
, btreePtr
->totalNodes
+ 1);
1306 M_ExitOnError (err
);
1309 err
= AllocateNode (btreePtr
, &insertNodeNum
);
1310 M_ExitOnError (err
);
1312 err
= GetNewNode (btreePtr
, insertNodeNum
, &nodeRec
);
1313 M_ExitOnError (err
);
1316 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1318 ((NodeDescPtr
)nodeRec
.buffer
)->kind
= kBTLeafNode
;
1319 ((NodeDescPtr
)nodeRec
.buffer
)->height
= 1;
1321 recordFit
= InsertKeyRecord (btreePtr
, nodeRec
.buffer
, 0,
1322 &iterator
->key
, KeyLength(btreePtr
, &iterator
->key
),
1323 record
->bufferAddress
, recordLen
);
1324 if (recordFit
!= true)
1326 err
= fsBTRecordTooLargeErr
;
1331 * Update the B-tree control block. Do this before
1332 * calling UpdateNode since it will compare the node's
1333 * height with treeDepth.
1335 btreePtr
->treeDepth
= 1;
1336 btreePtr
->rootNode
= insertNodeNum
;
1337 btreePtr
->firstLeafNode
= insertNodeNum
;
1338 btreePtr
->lastLeafNode
= insertNodeNum
;
1340 err
= UpdateNode (btreePtr
, &nodeRec
, 0, kLockTransaction
);
1341 M_ExitOnError (err
);
1343 M_BTreeHeaderDirty (btreePtr
);
1347 default: goto ErrorExit
;
1353 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1355 recordFit
= InsertKeyRecord (btreePtr
, nodeRec
.buffer
, index
,
1356 &iterator
->key
, KeyLength(btreePtr
, &iterator
->key
),
1357 record
->bufferAddress
, recordLen
);
1358 if (recordFit
== true)
1360 err
= UpdateNode (btreePtr
, &nodeRec
, 0, kLockTransaction
);
1361 M_ExitOnError (err
);
1367 /////////////////////// Extend File If Necessary ////////////////////////////
1369 if ((btreePtr
->treeDepth
+ 1UL) > btreePtr
->freeNodes
)
1371 nodesNeeded
= btreePtr
->treeDepth
+ 1 + btreePtr
->totalNodes
- btreePtr
->freeNodes
;
1372 if (nodesNeeded
> CalcMapBits (btreePtr
)) // we'll need to add a map node too!
1375 err
= ExtendBTree (btreePtr
, nodesNeeded
);
1376 M_ExitOnError (err
);
1379 // no need to delete existing record
1381 err
= InsertTree (btreePtr
, treePathTable
, &iterator
->key
, record
->bufferAddress
,
1382 recordLen
, &nodeRec
, index
, 1, kInsertRecord
, &insertNodeNum
);
1383 M_ExitOnError (err
);
1386 //////////////////////////////// Success ////////////////////////////////////
1389 ++btreePtr
->writeCount
;
1390 ++btreePtr
->leafRecords
;
1391 M_BTreeHeaderDirty (btreePtr
);
1394 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
1395 iterator
->hint
.nodeNum
= insertNodeNum
;
1396 iterator
->hint
.index
= 0; // unused
1397 iterator
->hint
.reserved1
= 0;
1398 iterator
->hint
.reserved2
= 0;
1403 ////////////////////////////// Error Exit ///////////////////////////////////
1407 (void) ReleaseNode (btreePtr
, &nodeRec
);
1409 iterator
->hint
.writeCount
= 0;
1410 iterator
->hint
.nodeNum
= 0;
1411 iterator
->hint
.index
= 0;
1412 iterator
->hint
.reserved1
= 0;
1413 iterator
->hint
.reserved2
= 0;
1415 if (err
== fsBTEmptyErr
)
1416 err
= fsBTRecordNotFoundErr
;
1422 //////////////////////////////// BTReplaceRecord ////////////////////////////////
1424 OSStatus
BTReplaceRecord (FCB
*filePtr
,
1425 BTreeIterator
*iterator
,
1426 FSBufferDescriptor
*record
,
1427 u_int16_t recordLen
)
1430 BTreeControlBlockPtr btreePtr
;
1431 TreePathTable treePathTable
;
1432 u_int32_t nodesNeeded
;
1433 BlockDescriptor nodeRec
;
1434 u_int32_t insertNodeNum
;
1440 ////////////////////////// Priliminary Checks ///////////////////////////////
1442 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
1443 nodeRec
.blockHeader
= nil
;
1445 err
= CheckInsertParams (filePtr
, iterator
, record
, recordLen
);
1449 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1451 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1453 ////////////////////////////// Take A Hint //////////////////////////////////
1455 err
= IsItAHint (btreePtr
, iterator
, &validHint
);
1456 M_ExitOnError (err
);
1460 insertNodeNum
= iterator
->hint
.nodeNum
;
1462 err
= GetNode (btreePtr
, insertNodeNum
, kGetNodeHint
, &nodeRec
);
1466 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
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
);
1476 ++btreePtr
->numValidHints
;
1482 (void) BTInvalidateHint( iterator
);
1485 err
= ReleaseNode (btreePtr
, &nodeRec
);
1486 M_ExitOnError (err
);
1490 (void) BTInvalidateHint( iterator
);
1495 ////////////////////////////// Get A Clue ///////////////////////////////////
1497 err
= SearchTree (btreePtr
, &iterator
->key
, treePathTable
, &insertNodeNum
, &nodeRec
, &index
);
1498 M_ExitOnError (err
); // record must exit for Replace
1500 // optimization - if simple replace will work then don't extend btree
1501 // \80\80 if we tried this before, and failed because it wouldn't fit then we shouldn't try this again...
1504 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1506 err
= TrySimpleReplace (btreePtr
, nodeRec
.buffer
, iterator
, record
, recordLen
, &recordFit
);
1507 M_ExitOnError (err
);
1511 err
= UpdateNode (btreePtr
, &nodeRec
, 0, 0);
1512 M_ExitOnError (err
);
1518 //////////////////////////// Make Some Room /////////////////////////////////
1520 if ((btreePtr
->treeDepth
+ 1UL) > btreePtr
->freeNodes
)
1522 nodesNeeded
= btreePtr
->treeDepth
+ 1 + btreePtr
->totalNodes
- btreePtr
->freeNodes
;
1523 if (nodesNeeded
> CalcMapBits (btreePtr
)) // we'll need to add a map node too!
1526 err
= ExtendBTree (btreePtr
, nodesNeeded
);
1527 M_ExitOnError (err
);
1531 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1533 DeleteRecord (btreePtr
, nodeRec
.buffer
, index
); // delete existing key/record
1535 err
= InsertTree (btreePtr
, treePathTable
, &iterator
->key
, record
->bufferAddress
,
1536 recordLen
, &nodeRec
, index
, 1, kReplaceRecord
, &insertNodeNum
);
1537 M_ExitOnError (err
);
1539 ++btreePtr
->writeCount
; /* writeCount changes only if the tree structure changed */
1543 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
1544 iterator
->hint
.nodeNum
= insertNodeNum
;
1545 iterator
->hint
.index
= 0; // unused
1546 iterator
->hint
.reserved1
= 0;
1547 iterator
->hint
.reserved2
= 0;
1552 ////////////////////////////// Error Exit ///////////////////////////////////
1556 (void) ReleaseNode (btreePtr
, &nodeRec
);
1558 iterator
->hint
.writeCount
= 0;
1559 iterator
->hint
.nodeNum
= 0;
1560 iterator
->hint
.index
= 0;
1561 iterator
->hint
.reserved1
= 0;
1562 iterator
->hint
.reserved2
= 0;
1569 //////////////////////////////// BTUpdateRecord ////////////////////////////////
1572 BTUpdateRecord(FCB
*filePtr
, BTreeIterator
*iterator
,
1573 IterateCallBackProcPtr callBackProc
, void * callBackState
)
1576 BTreeControlBlockPtr btreePtr
;
1577 TreePathTable treePathTable
;
1578 BlockDescriptor nodeRec
;
1579 RecordPtr recordPtr
;
1581 u_int32_t insertNodeNum
;
1582 u_int16_t recordLen
;
1587 ////////////////////////// Priliminary Checks ///////////////////////////////
1589 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
1590 nodeRec
.blockHeader
= nil
;
1592 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1594 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
1596 ////////////////////////////// Take A Hint //////////////////////////////////
1598 err
= IsItAHint (btreePtr
, iterator
, &validHint
);
1599 M_ExitOnError (err
);
1603 insertNodeNum
= iterator
->hint
.nodeNum
;
1605 err
= GetNode (btreePtr
, insertNodeNum
, kGetNodeHint
, &nodeRec
);
1608 if (((NodeDescPtr
)nodeRec
.buffer
)->kind
== kBTLeafNode
&&
1609 SearchNode (btreePtr
, nodeRec
.buffer
, &iterator
->key
, &index
))
1611 err
= GetRecordByIndex(btreePtr
, nodeRec
.buffer
, index
, &keyPtr
, &recordPtr
, &recordLen
);
1612 M_ExitOnError (err
);
1615 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1617 err
= callBackProc(keyPtr
, recordPtr
, callBackState
);
1618 M_ExitOnError (err
);
1620 err
= UpdateNode (btreePtr
, &nodeRec
, 0, 0);
1621 M_ExitOnError (err
);
1623 ++btreePtr
->numValidHints
;
1629 (void) BTInvalidateHint( iterator
);
1632 err
= ReleaseNode (btreePtr
, &nodeRec
);
1633 M_ExitOnError (err
);
1637 (void) BTInvalidateHint( iterator
);
1641 ////////////////////////////// Get A Clue ///////////////////////////////////
1643 err
= SearchTree (btreePtr
, &iterator
->key
, treePathTable
, &insertNodeNum
, &nodeRec
, &index
);
1644 M_ExitOnError (err
);
1646 err
= GetRecordByIndex(btreePtr
, nodeRec
.buffer
, index
, &keyPtr
, &recordPtr
, &recordLen
);
1647 M_ExitOnError (err
);
1650 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1652 err
= callBackProc(keyPtr
, recordPtr
, callBackState
);
1653 M_ExitOnError (err
);
1655 err
= UpdateNode (btreePtr
, &nodeRec
, 0, 0);
1656 M_ExitOnError (err
);
1660 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
1661 iterator
->hint
.nodeNum
= insertNodeNum
;
1662 iterator
->hint
.index
= 0;
1663 iterator
->hint
.reserved1
= 0;
1664 iterator
->hint
.reserved2
= 0;
1667 ////////////////////////////// Error Exit ///////////////////////////////////
1671 (void) ReleaseNode (btreePtr
, &nodeRec
);
1673 iterator
->hint
.writeCount
= 0;
1674 iterator
->hint
.nodeNum
= 0;
1675 iterator
->hint
.index
= 0;
1676 iterator
->hint
.reserved1
= 0;
1677 iterator
->hint
.reserved2
= 0;
1683 //////////////////////////////// BTDeleteRecord /////////////////////////////////
1685 OSStatus
BTDeleteRecord (FCB
*filePtr
,
1686 BTreeIterator
*iterator
)
1689 BTreeControlBlockPtr btreePtr
;
1690 TreePathTable treePathTable
;
1691 BlockDescriptor nodeRec
;
1692 u_int32_t nodesNeeded
;
1697 ////////////////////////// Priliminary Checks ///////////////////////////////
1699 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
1700 nodeRec
.blockHeader
= nil
;
1702 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1703 M_ReturnErrorIf (iterator
== nil
, paramErr
);
1705 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1706 if (btreePtr
== nil
)
1708 err
= fsBTInvalidFileErr
;
1712 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1715 /////////////////////////////// Find Key ////////////////////////////////////
1717 // check hint for simple delete case (index > 0, numRecords > 2)
1719 err
= SearchTree (btreePtr
, &iterator
->key
, treePathTable
, &nodeNum
, &nodeRec
, &index
);
1720 M_ExitOnError (err
); // record must exit for Delete
1723 /////////////////////// Extend File If Necessary ////////////////////////////
1725 if ((btreePtr
->treeDepth
+ 1UL) > btreePtr
->totalNodes
)
1727 nodesNeeded
= btreePtr
->treeDepth
+ 1 + btreePtr
->totalNodes
;
1728 if (nodesNeeded
> CalcMapBits (btreePtr
))
1731 err
= ExtendBTree (btreePtr
, nodesNeeded
);
1732 M_ExitOnError (err
);
1735 ///////////////////////////// Delete Record /////////////////////////////////
1737 err
= DeleteTree (btreePtr
, treePathTable
, &nodeRec
, index
, 1);
1738 M_ExitOnError (err
);
1740 ++btreePtr
->writeCount
;
1741 --btreePtr
->leafRecords
;
1742 M_BTreeHeaderDirty (btreePtr
);
1744 iterator
->hint
.nodeNum
= 0;
1748 ////////////////////////////// Error Exit ///////////////////////////////////
1751 (void) ReleaseNode (btreePtr
, &nodeRec
);
1758 OSStatus
BTGetInformation (FCB
*filePtr
,
1759 u_int16_t file_version
,
1760 BTreeInfoRec
*info
)
1762 #pragma unused (file_version)
1764 BTreeControlBlockPtr btreePtr
;
1767 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1769 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1773 * This should not require the whole tree to be locked, just maybe the BTreeControlBlockPtr
1775 * REQUIRE_FILE_LOCK(btreePtr->fileRefNum, true);
1778 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1779 M_ReturnErrorIf (info
== nil
, paramErr
);
1781 //\80\80 check version?
1783 info
->nodeSize
= btreePtr
->nodeSize
;
1784 info
->maxKeyLength
= btreePtr
->maxKeyLength
;
1785 info
->treeDepth
= btreePtr
->treeDepth
;
1786 info
->numRecords
= btreePtr
->leafRecords
;
1787 info
->numNodes
= btreePtr
->totalNodes
;
1788 info
->numFreeNodes
= btreePtr
->freeNodes
;
1789 info
->lastfsync
= btreePtr
->lastfsync
;
1790 info
->keyCompareType
= btreePtr
->keyCompareType
;
1797 BTIsDirty(FCB
*filePtr
)
1799 BTreeControlBlockPtr btreePtr
;
1801 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1802 return TreeIsDirty(btreePtr
);
1805 /*-------------------------------------------------------------------------------
1806 Routine: BTFlushPath - Flush BTreeControlBlock to Header Node.
1808 Function: Brief_description_of_the_function_and_any_side_effects
1811 Input: pathPtr - pointer to path control block for B*Tree file to flush
1815 Result: noErr - success
1817 -------------------------------------------------------------------------------*/
1819 OSStatus
BTFlushPath (FCB
*filePtr
)
1822 BTreeControlBlockPtr btreePtr
;
1825 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1827 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1829 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1831 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
1833 err
= UpdateHeader (btreePtr
, false);
1839 /*-------------------------------------------------------------------------------
1840 Routine: BTReload - Reload B-tree Header Data.
1842 Function: Reload B-tree header data from disk. This is called after fsck
1843 has made repairs to the root filesystem. The filesystem is
1844 mounted read-only when BTReload is caled.
1847 Input: filePtr - the B*Tree file that needs its header updated
1851 Result: noErr - success
1853 -------------------------------------------------------------------------------*/
1856 BTReloadData(FCB
*filePtr
)
1859 BTreeControlBlockPtr btreePtr
;
1860 BlockDescriptor node
;
1861 BTHeaderRec
*header
;
1865 node
.blockHeader
= nil
;
1867 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1868 if (btreePtr
== nil
)
1869 return (fsBTInvalidFileErr
);
1871 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1873 err
= GetNode(btreePtr
, kHeaderNodeNum
, 0, &node
);
1877 header
= (BTHeaderRec
*)((char *)node
.buffer
+ sizeof(BTNodeDescriptor
));
1878 if ((err
= VerifyHeader (filePtr
, header
)) == 0) {
1879 btreePtr
->treeDepth
= header
->treeDepth
;
1880 btreePtr
->rootNode
= header
->rootNode
;
1881 btreePtr
->leafRecords
= header
->leafRecords
;
1882 btreePtr
->firstLeafNode
= header
->firstLeafNode
;
1883 btreePtr
->lastLeafNode
= header
->lastLeafNode
;
1884 btreePtr
->maxKeyLength
= header
->maxKeyLength
;
1885 btreePtr
->totalNodes
= header
->totalNodes
;
1886 btreePtr
->freeNodes
= header
->freeNodes
;
1887 btreePtr
->btreeType
= header
->btreeType
;
1889 btreePtr
->flags
&= (~kBTHeaderDirty
);
1892 (void) ReleaseNode(btreePtr
, &node
);
1898 /*-------------------------------------------------------------------------------
1899 Routine: BTInvalidateHint - Invalidates the hint within a BTreeInterator.
1901 Function: Invalidates the hint within a BTreeInterator.
1904 Input: iterator - pointer to BTreeIterator
1906 Output: iterator - iterator with the hint.nodeNum cleared
1908 Result: noErr - success
1909 paramErr - iterator == nil
1910 -------------------------------------------------------------------------------*/
1913 OSStatus
BTInvalidateHint (BTreeIterator
*iterator
)
1915 if (iterator
== nil
)
1918 iterator
->hint
.nodeNum
= 0;
1926 /*-------------------------------------------------------------------------------
1927 Routine: BTGetLastSync
1929 Function: Returns the last time that this btree was flushed, does not include header.
1931 Input: filePtr - pointer file control block
1933 Output: lastfsync - time in seconds of last update
1935 Result: noErr - success
1936 paramErr - iterator == nil
1937 -------------------------------------------------------------------------------*/
1940 OSStatus
BTGetLastSync (FCB
*filePtr
,
1941 u_int32_t
*lastsync
)
1943 BTreeControlBlockPtr btreePtr
;
1946 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1948 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1950 /* Maybe instead of requiring a lock..an atomic set might be more appropriate */
1951 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
1953 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1954 M_ReturnErrorIf (lastsync
== nil
, paramErr
);
1956 *lastsync
= btreePtr
->lastfsync
;
1964 /*-------------------------------------------------------------------------------
1965 Routine: BTSetLastSync
1967 Function: Sets the last time that this btree was flushed, does not include header.
1970 Input: fcb - pointer file control block
1972 Output: lastfsync - time in seconds of last update
1974 Result: noErr - success
1975 paramErr - iterator == nil
1976 -------------------------------------------------------------------------------*/
1979 OSStatus
BTSetLastSync (FCB
*filePtr
,
1982 BTreeControlBlockPtr btreePtr
;
1985 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1987 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1989 /* Maybe instead of requiring a lock..an atomic set might be more appropriate */
1990 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
1992 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1993 M_ReturnErrorIf (lastsync
== 0, paramErr
);
1995 btreePtr
->lastfsync
= lastsync
;
2001 OSStatus
BTHasContiguousNodes (FCB
*filePtr
)
2003 BTreeControlBlockPtr btreePtr
;
2006 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
2008 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
2010 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
2012 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
2014 return NodesAreContiguous(FCBTOVCB(filePtr
), filePtr
, btreePtr
->nodeSize
);
2018 /*-------------------------------------------------------------------------------
2019 Routine: BTGetUserData
2021 Function: Read the user data area of the b-tree header node.
2023 -------------------------------------------------------------------------------*/
2025 BTGetUserData(FCB
*filePtr
, void * dataPtr
, int dataSize
)
2027 BTreeControlBlockPtr btreePtr
;
2028 BlockDescriptor node
;
2032 if (dataSize
> kBTreeHeaderUserBytes
)
2035 node
.blockHeader
= nil
;
2037 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
2038 if (btreePtr
== nil
)
2039 return (fsBTInvalidFileErr
);
2041 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
2043 err
= GetNode(btreePtr
, kHeaderNodeNum
, 0, &node
);
2047 offset
= (char *)node
.buffer
+ sizeof(BTNodeDescriptor
) + sizeof(BTHeaderRec
);
2048 bcopy(offset
, dataPtr
, dataSize
);
2050 (void) ReleaseNode(btreePtr
, &node
);
2056 /*-------------------------------------------------------------------------------
2057 Routine: BTSetUserData
2059 Function: Write the user data area of the b-tree header node.
2060 -------------------------------------------------------------------------------*/
2062 BTSetUserData(FCB
*filePtr
, void * dataPtr
, int dataSize
)
2064 BTreeControlBlockPtr btreePtr
;
2065 BlockDescriptor node
;
2069 if (dataSize
> kBTreeHeaderUserBytes
)
2072 node
.blockHeader
= nil
;
2074 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
2075 if (btreePtr
== nil
)
2076 return (fsBTInvalidFileErr
);
2078 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
2080 err
= GetNode(btreePtr
, kHeaderNodeNum
, 0, &node
);
2084 ModifyBlockStart(btreePtr
->fileRefNum
, &node
);
2086 offset
= (char *)node
.buffer
+ sizeof(BTNodeDescriptor
) + sizeof(BTHeaderRec
);
2087 bcopy(dataPtr
, offset
, dataSize
);
2089 err
= UpdateNode (btreePtr
, &node
, 0, 0);