2 * Copyright (c) 2000-2003 Apple Computer, 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: Miscellaneous operations for the BTree Module.
33 Version: xxx put the technology version here xxx
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):
55 <MOSXS> 6/1/99 djb Sync up with Mac OS 8.6.
56 <CS2> 9/4/97 djb Optimize TrySimpleReplace for the case where record size is not
58 <CS1> 4/23/97 djb first checked in
60 <HFS7> 3/31/97 djb Move ClearMemory to Utilities.c.
61 <HFS6> 3/17/97 DSH Casting for DFA
62 <HFS5> 2/27/97 msd Remove temporary fix from last revision. BTree EOF's should be
63 correct now, so check for strict equality.
64 <HFS4> 2/26/97 msd Fix a casting problem in ClearMemory. TEMPORARY FIX: Made
65 VerifyHeader more lenient, allowing the EOF to be greater than
66 the amount actually used by nodes; this should really be fixed
67 in the formatting code (which needs to compute the real BTree
68 sizes before writing the volume header).
69 <HFS3> 2/19/97 djb Added ClearMemory. Changed CalcKeyLength to KeyLength.
70 <HFS2> 1/3/97 djb Added support for large keys.
71 <HFS1> 12/19/96 djb first checked in
73 History applicable to original Scarecrow Design:
75 <9> 10/25/96 ser Changing for new VFPI
76 <8> 10/18/96 ser Converting over VFPI changes
77 <7> 9/17/96 dkh More BTree statistics. Change IsItAHint to not always check to
78 see if the hint node is allocated.
79 <6> 9/16/96 dkh Revised BTree statistics.
80 <5> 6/20/96 dkh Radar #1358740. Change from using Pools to debug MemAllocators.
81 <4> 1/22/96 dkh Change Pools.i inclusion to PoolsPriv.i
82 <3> 1/10/96 msd Change 64-bit math to use real function names from Math64.i.
83 <2> 12/7/95 dkh D10E2 build. Changed usage of Ref data type to LogicalAddress.
84 <1> 10/18/95 rst Moved from Scarecrow project.
86 <19> 4/26/95 prp In UpdateHeader, clear the dirty flag after the BTree is updated.
87 <18> 1/12/95 wjk Adopt Model FileSystem changes in D5.
88 <17> 11/16/94 prp Add IsItAHint routine and use it whenever hint's node number was
90 <16> 10/5/94 bk add pools.h include file
91 <15> 9/30/94 prp Get in sync with D2 interface changes.
92 <14> 7/22/94 wjk Convert to the new set of header files.
93 <13> 12/2/93 wjk Move from Makefiles to BuildFiles. Fit into the ModernOS and
95 <12> 11/30/93 wjk Move from Makefiles to BuildFiles. Fit into the ModernOS and
97 <11> 11/23/93 wjk Changes required to compile on the RS6000.
98 <10> 8/31/93 prp Use U64SetU instead of S64Set.
99 <9> 6/2/93 gs Update for changes to FSErrors.h and add some comments.
100 <8> 5/21/93 gs Modify UpdateHeader to write out attributes. Remove
101 Get/UpdateNode from TrySimpleReplace.
102 <7> 5/10/93 gs Add TrySimpleReplace routine.
103 <6> 3/23/93 gs Change MoveData to take void * instead of Ptr. Add UpdateHeader
104 and ClearBytes routines.
105 <5> 2/8/93 gs Add FindIteratorPosition.
106 <4> 12/10/92 gs Implement CheckKeyDescriptor and the KeyDescriptor interpreter.
107 <3> 12/8/92 gs Add GetKeyDescriptor, VerifyHeader, and Alloc/Dealloc memory
109 <2> 12/2/92 gs Add CompareKeys routine.
110 <1> 11/15/92 gs first checked in
114 #include "../headers/BTreesPrivate.h"
117 ////////////////////////////// Routine Definitions //////////////////////////////
119 /*-------------------------------------------------------------------------------
120 Routine: CalcKeyRecordSize - Return size of combined key/record structure.
122 Function: Rounds keySize and recSize so they will end on word boundaries.
123 Does NOT add size of offset.
125 Input: keySize - length of key (including length field)
126 recSize - length of record data
130 Result: UInt16 - size of combined key/record that will be inserted in btree
131 -------------------------------------------------------------------------------*/
133 UInt16
CalcKeyRecordSize (UInt16 keySize
,
136 if ( M_IsOdd (keySize
) ) keySize
+= 1; // pad byte
138 if (M_IsOdd (recSize
) ) recSize
+= 1; // pad byte
140 return (keySize
+ recSize
);
145 /*-------------------------------------------------------------------------------
146 Routine: VerifyHeader - Validate fields of the BTree header record.
148 Function: Examines the fields of the BTree header record to determine if the
149 fork appears to contain a valid BTree.
151 Input: forkPtr - pointer to fork control block
152 header - pointer to BTree header
155 Result: noErr - success
157 -------------------------------------------------------------------------------*/
159 OSStatus
VerifyHeader (FCB
*filePtr
,
160 BTHeaderRec
*header
)
166 switch (header
->nodeSize
) // node size == 512*2^n
175 default: return fsBTInvalidHeaderErr
; //\80\80 E_BadNodeType
178 totalNodes
= header
->totalNodes
;
180 forkSize
= (UInt64
)totalNodes
* (UInt64
)header
->nodeSize
;
182 if ( forkSize
> filePtr
->fcbEOF
)
183 return fsBTInvalidHeaderErr
;
185 if ( header
->freeNodes
>= totalNodes
)
186 return fsBTInvalidHeaderErr
;
188 if ( header
->rootNode
>= totalNodes
)
189 return fsBTInvalidHeaderErr
;
191 if ( header
->firstLeafNode
>= totalNodes
)
192 return fsBTInvalidHeaderErr
;
194 if ( header
->lastLeafNode
>= totalNodes
)
195 return fsBTInvalidHeaderErr
;
197 if ( header
->treeDepth
> kMaxTreeDepth
)
198 return fsBTInvalidHeaderErr
;
201 /////////////////////////// Check BTree Type ////////////////////////////////
203 switch (header
->btreeType
)
205 case 0: // HFS Type - no Key Descriptor
206 case kUserBTreeType
: // with Key Descriptors etc.
207 case kReservedBTreeType
: // Desktop Mgr BTree ?
210 default: return fsBTUnknownVersionErr
;
219 OSStatus
TreeIsDirty(BTreeControlBlockPtr btreePtr
)
221 return (btreePtr
->flags
& kBTHeaderDirty
);
226 /*-------------------------------------------------------------------------------
227 Routine: UpdateHeader - Write BTreeInfoRec fields to Header node.
229 Function: Checks the kBTHeaderDirty flag in the BTreeInfoRec and updates the
230 header node if necessary.
232 Input: btreePtr - pointer to BTreeInfoRec
235 Result: noErr - success
237 -------------------------------------------------------------------------------*/
239 OSStatus
UpdateHeader(BTreeControlBlockPtr btreePtr
, Boolean forceWrite
)
242 BlockDescriptor node
;
246 if ((btreePtr
->flags
& kBTHeaderDirty
) == 0) // btree info already flushed
250 err
= GetNode (btreePtr
, kHeaderNodeNum
, &node
);
256 ModifyBlockStart(btreePtr
->fileRefNum
, &node
);
258 header
= (BTHeaderRec
*) ((char *)node
.buffer
+ sizeof(BTNodeDescriptor
));
260 header
->treeDepth
= btreePtr
->treeDepth
;
261 header
->rootNode
= btreePtr
->rootNode
;
262 header
->leafRecords
= btreePtr
->leafRecords
;
263 header
->firstLeafNode
= btreePtr
->firstLeafNode
;
264 header
->lastLeafNode
= btreePtr
->lastLeafNode
;
265 header
->nodeSize
= btreePtr
->nodeSize
; //\80\80 this shouldn't change
266 header
->maxKeyLength
= btreePtr
->maxKeyLength
; //\80\80 neither should this
267 header
->totalNodes
= btreePtr
->totalNodes
;
268 header
->freeNodes
= btreePtr
->freeNodes
;
269 header
->btreeType
= btreePtr
->btreeType
;
271 // ignore header->clumpSize; //\80\80 rename this field?
274 options
= kForceWriteBlock
;
276 options
= kLockTransaction
;
278 err
= UpdateNode (btreePtr
, &node
, 0, options
);
280 btreePtr
->flags
&= (~kBTHeaderDirty
);
287 /*-------------------------------------------------------------------------------
288 Routine: FindIteratorPosition - One_line_description.
290 Function: Brief_description_of_the_function_and_any_side_effects
292 Algorithm: see FSC.BT.BTIterateRecord.PICT
294 Note: //\80\80 document side-effects of bad node hints
296 Input: btreePtr - description
297 iterator - description
300 Output: iterator - description
304 nodeNum - description
305 returnIndex - description
306 foundRecord - description
309 Result: noErr - success
311 -------------------------------------------------------------------------------*/
313 OSStatus
FindIteratorPosition (BTreeControlBlockPtr btreePtr
,
314 BTreeIteratorPtr iterator
,
315 BlockDescriptor
*left
,
316 BlockDescriptor
*middle
,
317 BlockDescriptor
*right
,
318 UInt32
*returnNodeNum
,
320 Boolean
*foundRecord
)
325 UInt16 leftIndex
, index
, rightIndex
;
328 // assume btreePtr valid
329 // assume left, middle, right point to BlockDescriptors
330 // assume nodeNum points to UInt32
331 // assume index points to UInt16
332 // assume foundRecord points to Boolean
335 left
->blockHeader
= nil
;
336 middle
->buffer
= nil
;
337 middle
->blockHeader
= nil
;
339 right
->blockHeader
= nil
;
343 if (iterator
== nil
) // do we have an iterator?
345 err
= fsBTInvalidIteratorErr
;
349 err
= IsItAHint (btreePtr
, iterator
, &validHint
);
352 nodeNum
= iterator
->hint
.nodeNum
;
353 if (! validHint
) // does the hint appear to be valid?
358 err
= GetNode (btreePtr
, nodeNum
, middle
);
359 if( err
== fsBTInvalidNodeErr
) // returned if nodeNum is out of range
364 if ( ((NodeDescPtr
) middle
->buffer
)->kind
!= kBTLeafNode
||
365 ((NodeDescPtr
) middle
->buffer
)->numRecords
<= 0 )
370 ++btreePtr
->numValidHints
;
372 foundIt
= SearchNode (btreePtr
, middle
->buffer
, &iterator
->key
, &index
);
380 if (((NodeDescPtr
) middle
->buffer
)->bLink
== 0) // before 1st btree record
385 nodeNum
= ((NodeDescPtr
) middle
->buffer
)->bLink
;
387 err
= GetLeftSiblingNode (btreePtr
, middle
->buffer
, left
);
390 if ( ((NodeDescPtr
) left
->buffer
)->kind
!= kBTLeafNode
||
391 ((NodeDescPtr
) left
->buffer
)->numRecords
<= 0 )
396 foundIt
= SearchNode (btreePtr
, left
->buffer
, &iterator
->key
, &leftIndex
);
407 if (leftIndex
== 0) // we're lost!
411 else if (leftIndex
>= ((NodeDescPtr
) left
->buffer
)->numRecords
)
413 nodeNum
= ((NodeDescPtr
) left
->buffer
)->fLink
;
415 PanicIf (index
!= 0, "\pFindIteratorPosition: index != 0"); //\80\80 just checking...
428 else if (index
>= ((NodeDescPtr
) middle
->buffer
)->numRecords
)
430 if (((NodeDescPtr
) middle
->buffer
)->fLink
== 0) // beyond last record
435 nodeNum
= ((NodeDescPtr
) middle
->buffer
)->fLink
;
437 err
= GetRightSiblingNode (btreePtr
, middle
->buffer
, right
);
440 if ( ((NodeDescPtr
) right
->buffer
)->kind
!= kBTLeafNode
||
441 ((NodeDescPtr
) right
->buffer
)->numRecords
<= 0 )
446 foundIt
= SearchNode (btreePtr
, right
->buffer
, &iterator
->key
, &rightIndex
);
447 if (rightIndex
>= ((NodeDescPtr
) right
->buffer
)->numRecords
) // we're lost
451 else // we found it, or rightIndex==0, or rightIndex<numRecs
463 //////////////////////////// Search The Tree ////////////////////////////////
467 TreePathTable treePathTable
; // so we only use stack space if we need to
469 err
= ReleaseNode (btreePtr
, left
); M_ExitOnError (err
);
470 err
= ReleaseNode (btreePtr
, middle
); M_ExitOnError (err
);
471 err
= ReleaseNode (btreePtr
, right
); M_ExitOnError (err
);
473 err
= SearchTree ( btreePtr
, &iterator
->key
, treePathTable
, &nodeNum
, middle
, &index
);
474 switch (err
) //\80\80 separate find condition from exceptions
476 case noErr
: foundIt
= true; break;
477 case fsBTRecordNotFoundErr
: break;
478 default: goto ErrorExit
;
482 /////////////////////////////// Success! ////////////////////////////////////
486 *returnNodeNum
= nodeNum
;
487 *returnIndex
= index
;
488 *foundRecord
= foundIt
;
493 ////////////////////////////// Error Exit ///////////////////////////////////
497 (void) ReleaseNode (btreePtr
, left
);
498 (void) ReleaseNode (btreePtr
, middle
);
499 (void) ReleaseNode (btreePtr
, right
);
503 *foundRecord
= false;
510 /////////////////////////////// CheckInsertParams ///////////////////////////////
512 OSStatus
CheckInsertParams (FCB
*filePtr
,
513 BTreeIterator
*iterator
,
514 FSBufferDescriptor
*record
,
517 BTreeControlBlockPtr btreePtr
;
519 if (filePtr
== nil
) return paramErr
;
521 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
522 if (btreePtr
== nil
) return fsBTInvalidFileErr
;
523 if (iterator
== nil
) return paramErr
;
524 if (record
== nil
) return paramErr
;
526 // check total key/record size limit
527 if ( CalcKeyRecordSize (CalcKeySize(btreePtr
, &iterator
->key
), recordLen
) > (btreePtr
->nodeSize
>> 1))
528 return fsBTRecordTooLargeErr
;
535 /*-------------------------------------------------------------------------------
536 Routine: TrySimpleReplace - Attempts a simple insert, set, or replace.
538 Function: If a hint exitst for the iterator, attempt to find the key in the hint
539 node. If the key is found, an insert operation fails. If the is not
540 found, a replace operation fails. If the key was not found, and the
541 insert position is greater than 0 and less than numRecords, the record
542 is inserted, provided there is enough freeSpace. If the key was found,
543 and there is more freeSpace than the difference between the new record
544 and the old record, the old record is deleted and the new record is
547 Assumptions: iterator key has already been checked by CheckKey
550 Input: btreePtr - description
551 iterator - description
553 recordLen - description
554 operation - description
557 Output: recordInserted - description
560 Result: noErr - success
561 E_RecordExits - insert operation failure
562 != noErr - GetNode, ReleaseNode, UpdateNode returned an error
563 -------------------------------------------------------------------------------*/
565 OSStatus
TrySimpleReplace (BTreeControlBlockPtr btreePtr
,
567 BTreeIterator
*iterator
,
568 FSBufferDescriptor
*record
,
570 Boolean
*recordInserted
)
580 *recordInserted
= false; // we'll assume this won't work...
582 if ( nodePtr
->kind
!= kBTLeafNode
)
583 return noErr
; // we're in the weeds!
585 foundIt
= SearchNode (btreePtr
, nodePtr
, &iterator
->key
, &index
);
587 if ( foundIt
== false )
588 return noErr
; // we might be lost...
590 keySize
= CalcKeySize(btreePtr
, &iterator
->key
); // includes length field
592 spaceNeeded
= CalcKeyRecordSize (keySize
, recordLen
);
594 oldSpace
= GetRecordSize (btreePtr
, nodePtr
, index
);
596 if ( spaceNeeded
== oldSpace
)
600 dst
= GetRecordAddress (btreePtr
, nodePtr
, index
);
602 if ( M_IsOdd (keySize
) )
603 ++keySize
; // add pad byte
605 dst
+= keySize
; // skip over key to point at record
607 BlockMoveData(record
->bufferAddress
, dst
, recordLen
); // blast away...
609 *recordInserted
= true;
611 else if ( (GetNodeFreeSize(btreePtr
, nodePtr
) + oldSpace
) >= spaceNeeded
)
613 DeleteRecord (btreePtr
, nodePtr
, index
);
615 didItFit
= InsertKeyRecord (btreePtr
, nodePtr
, index
,
616 &iterator
->key
, KeyLength(btreePtr
, &iterator
->key
),
617 record
->bufferAddress
, recordLen
);
618 PanicIf (didItFit
== false, "\pTrySimpleInsert: InsertKeyRecord returned false!");
620 *recordInserted
= true;
622 // else not enough space...
628 /*-------------------------------------------------------------------------------
629 Routine: IsItAHint - checks the hint within a BTreeInterator.
631 Function: checks the hint within a BTreeInterator. If it is non-zero, it may
634 Input: btreePtr - pointer to control block for BTree file
635 iterator - pointer to BTreeIterator
637 Output: answer - true if the hint looks reasonable
638 - false if the hint is 0
640 Result: noErr - success
641 -------------------------------------------------------------------------------*/
644 OSStatus
IsItAHint (BTreeControlBlockPtr btreePtr
, BTreeIterator
*iterator
, Boolean
*answer
)
646 ++btreePtr
->numHintChecks
;
649 if (iterator
->hint
.nodeNum
>= btreePtr
->totalNodes
)
655 if (iterator
->hint
.nodeNum
== 0)
662 ++btreePtr
->numPossibleHints
;