2 * Copyright (c) 2000-2003 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_OSREFERENCE_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
10 * License may not be used to create, or enable the creation or
11 * redistribution of, unlawful or unlicensed copies of an Apple operating
12 * system, or to circumvent, violate, or enable the circumvention or
13 * violation of, any terms of an Apple operating system software license
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
20 * The Original Code and all software distributed under the License are
21 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
22 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
23 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
25 * Please see the License for the specific language governing rights and
26 * limitations under the License.
28 * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
33 Contains: Miscellaneous operations for the BTree Module.
35 Version: xxx put the technology version here xxx
37 Written by: Gordon Sheridan and Bill Bruffey
39 Copyright: © 1992-1999 by Apple Computer, Inc., all rights reserved.
45 Other Contact: Mark Day
47 Technology: File Systems
55 Change History (most recent first):
57 <MOSXS> 6/1/99 djb Sync up with Mac OS 8.6.
58 <CS2> 9/4/97 djb Optimize TrySimpleReplace for the case where record size is not
60 <CS1> 4/23/97 djb first checked in
62 <HFS7> 3/31/97 djb Move ClearMemory to Utilities.c.
63 <HFS6> 3/17/97 DSH Casting for DFA
64 <HFS5> 2/27/97 msd Remove temporary fix from last revision. BTree EOF's should be
65 correct now, so check for strict equality.
66 <HFS4> 2/26/97 msd Fix a casting problem in ClearMemory. TEMPORARY FIX: Made
67 VerifyHeader more lenient, allowing the EOF to be greater than
68 the amount actually used by nodes; this should really be fixed
69 in the formatting code (which needs to compute the real BTree
70 sizes before writing the volume header).
71 <HFS3> 2/19/97 djb Added ClearMemory. Changed CalcKeyLength to KeyLength.
72 <HFS2> 1/3/97 djb Added support for large keys.
73 <HFS1> 12/19/96 djb first checked in
75 History applicable to original Scarecrow Design:
77 <9> 10/25/96 ser Changing for new VFPI
78 <8> 10/18/96 ser Converting over VFPI changes
79 <7> 9/17/96 dkh More BTree statistics. Change IsItAHint to not always check to
80 see if the hint node is allocated.
81 <6> 9/16/96 dkh Revised BTree statistics.
82 <5> 6/20/96 dkh Radar #1358740. Change from using Pools to debug MemAllocators.
83 <4> 1/22/96 dkh Change Pools.i inclusion to PoolsPriv.i
84 <3> 1/10/96 msd Change 64-bit math to use real function names from Math64.i.
85 <2> 12/7/95 dkh D10E2 build. Changed usage of Ref data type to LogicalAddress.
86 <1> 10/18/95 rst Moved from Scarecrow project.
88 <19> 4/26/95 prp In UpdateHeader, clear the dirty flag after the BTree is updated.
89 <18> 1/12/95 wjk Adopt Model FileSystem changes in D5.
90 <17> 11/16/94 prp Add IsItAHint routine and use it whenever hint's node number was
92 <16> 10/5/94 bk add pools.h include file
93 <15> 9/30/94 prp Get in sync with D2 interface changes.
94 <14> 7/22/94 wjk Convert to the new set of header files.
95 <13> 12/2/93 wjk Move from Makefiles to BuildFiles. Fit into the ModernOS and
97 <12> 11/30/93 wjk Move from Makefiles to BuildFiles. Fit into the ModernOS and
99 <11> 11/23/93 wjk Changes required to compile on the RS6000.
100 <10> 8/31/93 prp Use U64SetU instead of S64Set.
101 <9> 6/2/93 gs Update for changes to FSErrors.h and add some comments.
102 <8> 5/21/93 gs Modify UpdateHeader to write out attributes. Remove
103 Get/UpdateNode from TrySimpleReplace.
104 <7> 5/10/93 gs Add TrySimpleReplace routine.
105 <6> 3/23/93 gs Change MoveData to take void * instead of Ptr. Add UpdateHeader
106 and ClearBytes routines.
107 <5> 2/8/93 gs Add FindIteratorPosition.
108 <4> 12/10/92 gs Implement CheckKeyDescriptor and the KeyDescriptor interpreter.
109 <3> 12/8/92 gs Add GetKeyDescriptor, VerifyHeader, and Alloc/Dealloc memory
111 <2> 12/2/92 gs Add CompareKeys routine.
112 <1> 11/15/92 gs first checked in
116 #include "../headers/BTreesPrivate.h"
119 ////////////////////////////// Routine Definitions //////////////////////////////
121 /*-------------------------------------------------------------------------------
122 Routine: CalcKeyRecordSize - Return size of combined key/record structure.
124 Function: Rounds keySize and recSize so they will end on word boundaries.
125 Does NOT add size of offset.
127 Input: keySize - length of key (including length field)
128 recSize - length of record data
132 Result: UInt16 - size of combined key/record that will be inserted in btree
133 -------------------------------------------------------------------------------*/
135 UInt16
CalcKeyRecordSize (UInt16 keySize
,
138 if ( M_IsOdd (keySize
) ) keySize
+= 1; // pad byte
140 if (M_IsOdd (recSize
) ) recSize
+= 1; // pad byte
142 return (keySize
+ recSize
);
147 /*-------------------------------------------------------------------------------
148 Routine: VerifyHeader - Validate fields of the BTree header record.
150 Function: Examines the fields of the BTree header record to determine if the
151 fork appears to contain a valid BTree.
153 Input: forkPtr - pointer to fork control block
154 header - pointer to BTree header
157 Result: noErr - success
159 -------------------------------------------------------------------------------*/
161 OSStatus
VerifyHeader (FCB
*filePtr
,
162 BTHeaderRec
*header
)
168 switch (header
->nodeSize
) // node size == 512*2^n
177 default: return fsBTInvalidHeaderErr
; //\80\80 E_BadNodeType
180 totalNodes
= header
->totalNodes
;
182 forkSize
= (UInt64
)totalNodes
* (UInt64
)header
->nodeSize
;
184 if ( forkSize
> filePtr
->fcbEOF
)
185 return fsBTInvalidHeaderErr
;
187 if ( header
->freeNodes
>= totalNodes
)
188 return fsBTInvalidHeaderErr
;
190 if ( header
->rootNode
>= totalNodes
)
191 return fsBTInvalidHeaderErr
;
193 if ( header
->firstLeafNode
>= totalNodes
)
194 return fsBTInvalidHeaderErr
;
196 if ( header
->lastLeafNode
>= totalNodes
)
197 return fsBTInvalidHeaderErr
;
199 if ( header
->treeDepth
> kMaxTreeDepth
)
200 return fsBTInvalidHeaderErr
;
203 /////////////////////////// Check BTree Type ////////////////////////////////
205 switch (header
->btreeType
)
207 case 0: // HFS Type - no Key Descriptor
208 case kUserBTreeType
: // with Key Descriptors etc.
209 case kReservedBTreeType
: // Desktop Mgr BTree ?
212 default: return fsBTUnknownVersionErr
;
221 OSStatus
TreeIsDirty(BTreeControlBlockPtr btreePtr
)
223 return (btreePtr
->flags
& kBTHeaderDirty
);
228 /*-------------------------------------------------------------------------------
229 Routine: UpdateHeader - Write BTreeInfoRec fields to Header node.
231 Function: Checks the kBTHeaderDirty flag in the BTreeInfoRec and updates the
232 header node if necessary.
234 Input: btreePtr - pointer to BTreeInfoRec
237 Result: noErr - success
239 -------------------------------------------------------------------------------*/
241 OSStatus
UpdateHeader(BTreeControlBlockPtr btreePtr
, Boolean forceWrite
)
244 BlockDescriptor node
;
248 if ((btreePtr
->flags
& kBTHeaderDirty
) == 0) // btree info already flushed
252 err
= GetNode (btreePtr
, kHeaderNodeNum
, &node
);
258 ModifyBlockStart(btreePtr
->fileRefNum
, &node
);
260 header
= (BTHeaderRec
*) ((char *)node
.buffer
+ sizeof(BTNodeDescriptor
));
262 header
->treeDepth
= btreePtr
->treeDepth
;
263 header
->rootNode
= btreePtr
->rootNode
;
264 header
->leafRecords
= btreePtr
->leafRecords
;
265 header
->firstLeafNode
= btreePtr
->firstLeafNode
;
266 header
->lastLeafNode
= btreePtr
->lastLeafNode
;
267 header
->nodeSize
= btreePtr
->nodeSize
; //\80\80 this shouldn't change
268 header
->maxKeyLength
= btreePtr
->maxKeyLength
; //\80\80 neither should this
269 header
->totalNodes
= btreePtr
->totalNodes
;
270 header
->freeNodes
= btreePtr
->freeNodes
;
271 header
->btreeType
= btreePtr
->btreeType
;
273 // ignore header->clumpSize; //\80\80 rename this field?
276 options
= kForceWriteBlock
;
278 options
= kLockTransaction
;
280 err
= UpdateNode (btreePtr
, &node
, 0, options
);
282 btreePtr
->flags
&= (~kBTHeaderDirty
);
289 /*-------------------------------------------------------------------------------
290 Routine: FindIteratorPosition - One_line_description.
292 Function: Brief_description_of_the_function_and_any_side_effects
294 Algorithm: see FSC.BT.BTIterateRecord.PICT
296 Note: //\80\80 document side-effects of bad node hints
298 Input: btreePtr - description
299 iterator - description
302 Output: iterator - description
306 nodeNum - description
307 returnIndex - description
308 foundRecord - description
311 Result: noErr - success
313 -------------------------------------------------------------------------------*/
315 OSStatus
FindIteratorPosition (BTreeControlBlockPtr btreePtr
,
316 BTreeIteratorPtr iterator
,
317 BlockDescriptor
*left
,
318 BlockDescriptor
*middle
,
319 BlockDescriptor
*right
,
320 UInt32
*returnNodeNum
,
322 Boolean
*foundRecord
)
327 UInt16 leftIndex
, index
, rightIndex
;
330 // assume btreePtr valid
331 // assume left, middle, right point to BlockDescriptors
332 // assume nodeNum points to UInt32
333 // assume index points to UInt16
334 // assume foundRecord points to Boolean
337 left
->blockHeader
= nil
;
338 middle
->buffer
= nil
;
339 middle
->blockHeader
= nil
;
341 right
->blockHeader
= nil
;
345 if (iterator
== nil
) // do we have an iterator?
347 err
= fsBTInvalidIteratorErr
;
351 err
= IsItAHint (btreePtr
, iterator
, &validHint
);
354 nodeNum
= iterator
->hint
.nodeNum
;
355 if (! validHint
) // does the hint appear to be valid?
360 err
= GetNode (btreePtr
, nodeNum
, middle
);
361 if( err
== fsBTInvalidNodeErr
) // returned if nodeNum is out of range
366 if ( ((NodeDescPtr
) middle
->buffer
)->kind
!= kBTLeafNode
||
367 ((NodeDescPtr
) middle
->buffer
)->numRecords
<= 0 )
372 ++btreePtr
->numValidHints
;
374 foundIt
= SearchNode (btreePtr
, middle
->buffer
, &iterator
->key
, &index
);
382 if (((NodeDescPtr
) middle
->buffer
)->bLink
== 0) // before 1st btree record
387 nodeNum
= ((NodeDescPtr
) middle
->buffer
)->bLink
;
389 err
= GetLeftSiblingNode (btreePtr
, middle
->buffer
, left
);
392 if ( ((NodeDescPtr
) left
->buffer
)->kind
!= kBTLeafNode
||
393 ((NodeDescPtr
) left
->buffer
)->numRecords
<= 0 )
398 foundIt
= SearchNode (btreePtr
, left
->buffer
, &iterator
->key
, &leftIndex
);
409 if (leftIndex
== 0) // we're lost!
413 else if (leftIndex
>= ((NodeDescPtr
) left
->buffer
)->numRecords
)
415 nodeNum
= ((NodeDescPtr
) left
->buffer
)->fLink
;
417 PanicIf (index
!= 0, "\pFindIteratorPosition: index != 0"); //\80\80 just checking...
430 else if (index
>= ((NodeDescPtr
) middle
->buffer
)->numRecords
)
432 if (((NodeDescPtr
) middle
->buffer
)->fLink
== 0) // beyond last record
437 nodeNum
= ((NodeDescPtr
) middle
->buffer
)->fLink
;
439 err
= GetRightSiblingNode (btreePtr
, middle
->buffer
, right
);
442 if ( ((NodeDescPtr
) right
->buffer
)->kind
!= kBTLeafNode
||
443 ((NodeDescPtr
) right
->buffer
)->numRecords
<= 0 )
448 foundIt
= SearchNode (btreePtr
, right
->buffer
, &iterator
->key
, &rightIndex
);
449 if (rightIndex
>= ((NodeDescPtr
) right
->buffer
)->numRecords
) // we're lost
453 else // we found it, or rightIndex==0, or rightIndex<numRecs
465 //////////////////////////// Search The Tree ////////////////////////////////
469 TreePathTable treePathTable
; // so we only use stack space if we need to
471 err
= ReleaseNode (btreePtr
, left
); M_ExitOnError (err
);
472 err
= ReleaseNode (btreePtr
, middle
); M_ExitOnError (err
);
473 err
= ReleaseNode (btreePtr
, right
); M_ExitOnError (err
);
475 err
= SearchTree ( btreePtr
, &iterator
->key
, treePathTable
, &nodeNum
, middle
, &index
);
476 switch (err
) //\80\80 separate find condition from exceptions
478 case noErr
: foundIt
= true; break;
479 case fsBTRecordNotFoundErr
: break;
480 default: goto ErrorExit
;
484 /////////////////////////////// Success! ////////////////////////////////////
488 *returnNodeNum
= nodeNum
;
489 *returnIndex
= index
;
490 *foundRecord
= foundIt
;
495 ////////////////////////////// Error Exit ///////////////////////////////////
499 (void) ReleaseNode (btreePtr
, left
);
500 (void) ReleaseNode (btreePtr
, middle
);
501 (void) ReleaseNode (btreePtr
, right
);
505 *foundRecord
= false;
512 /////////////////////////////// CheckInsertParams ///////////////////////////////
514 OSStatus
CheckInsertParams (FCB
*filePtr
,
515 BTreeIterator
*iterator
,
516 FSBufferDescriptor
*record
,
519 BTreeControlBlockPtr btreePtr
;
521 if (filePtr
== nil
) return paramErr
;
523 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
524 if (btreePtr
== nil
) return fsBTInvalidFileErr
;
525 if (iterator
== nil
) return paramErr
;
526 if (record
== nil
) return paramErr
;
528 // check total key/record size limit
529 if ( CalcKeyRecordSize (CalcKeySize(btreePtr
, &iterator
->key
), recordLen
) > (btreePtr
->nodeSize
>> 1))
530 return fsBTRecordTooLargeErr
;
537 /*-------------------------------------------------------------------------------
538 Routine: TrySimpleReplace - Attempts a simple insert, set, or replace.
540 Function: If a hint exitst for the iterator, attempt to find the key in the hint
541 node. If the key is found, an insert operation fails. If the is not
542 found, a replace operation fails. If the key was not found, and the
543 insert position is greater than 0 and less than numRecords, the record
544 is inserted, provided there is enough freeSpace. If the key was found,
545 and there is more freeSpace than the difference between the new record
546 and the old record, the old record is deleted and the new record is
549 Assumptions: iterator key has already been checked by CheckKey
552 Input: btreePtr - description
553 iterator - description
555 recordLen - description
556 operation - description
559 Output: recordInserted - description
562 Result: noErr - success
563 E_RecordExits - insert operation failure
564 != noErr - GetNode, ReleaseNode, UpdateNode returned an error
565 -------------------------------------------------------------------------------*/
567 OSStatus
TrySimpleReplace (BTreeControlBlockPtr btreePtr
,
569 BTreeIterator
*iterator
,
570 FSBufferDescriptor
*record
,
572 Boolean
*recordInserted
)
582 *recordInserted
= false; // we'll assume this won't work...
584 if ( nodePtr
->kind
!= kBTLeafNode
)
585 return noErr
; // we're in the weeds!
587 foundIt
= SearchNode (btreePtr
, nodePtr
, &iterator
->key
, &index
);
589 if ( foundIt
== false )
590 return noErr
; // we might be lost...
592 keySize
= CalcKeySize(btreePtr
, &iterator
->key
); // includes length field
594 spaceNeeded
= CalcKeyRecordSize (keySize
, recordLen
);
596 oldSpace
= GetRecordSize (btreePtr
, nodePtr
, index
);
598 if ( spaceNeeded
== oldSpace
)
602 dst
= GetRecordAddress (btreePtr
, nodePtr
, index
);
604 if ( M_IsOdd (keySize
) )
605 ++keySize
; // add pad byte
607 dst
+= keySize
; // skip over key to point at record
609 BlockMoveData(record
->bufferAddress
, dst
, recordLen
); // blast away...
611 *recordInserted
= true;
613 else if ( (GetNodeFreeSize(btreePtr
, nodePtr
) + oldSpace
) >= spaceNeeded
)
615 DeleteRecord (btreePtr
, nodePtr
, index
);
617 didItFit
= InsertKeyRecord (btreePtr
, nodePtr
, index
,
618 &iterator
->key
, KeyLength(btreePtr
, &iterator
->key
),
619 record
->bufferAddress
, recordLen
);
620 PanicIf (didItFit
== false, "\pTrySimpleInsert: InsertKeyRecord returned false!");
622 *recordInserted
= true;
624 // else not enough space...
630 /*-------------------------------------------------------------------------------
631 Routine: IsItAHint - checks the hint within a BTreeInterator.
633 Function: checks the hint within a BTreeInterator. If it is non-zero, it may
636 Input: btreePtr - pointer to control block for BTree file
637 iterator - pointer to BTreeIterator
639 Output: answer - true if the hint looks reasonable
640 - false if the hint is 0
642 Result: noErr - success
643 -------------------------------------------------------------------------------*/
646 OSStatus
IsItAHint (BTreeControlBlockPtr btreePtr
, BTreeIterator
*iterator
, Boolean
*answer
)
648 ++btreePtr
->numHintChecks
;
651 if (iterator
->hint
.nodeNum
>= btreePtr
->totalNodes
)
657 if (iterator
->hint
.nodeNum
== 0)
664 ++btreePtr
->numPossibleHints
;