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: Miscellaneous operations for the BTree Module.
27 Version: xxx put the technology version here xxx
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):
49 <MOSXS> 6/1/99 djb Sync up with Mac OS 8.6.
50 <CS2> 9/4/97 djb Optimize TrySimpleReplace for the case where record size is not
52 <CS1> 4/23/97 djb first checked in
54 <HFS7> 3/31/97 djb Move ClearMemory to Utilities.c.
55 <HFS6> 3/17/97 DSH Casting for DFA
56 <HFS5> 2/27/97 msd Remove temporary fix from last revision. BTree EOF's should be
57 correct now, so check for strict equality.
58 <HFS4> 2/26/97 msd Fix a casting problem in ClearMemory. TEMPORARY FIX: Made
59 VerifyHeader more lenient, allowing the EOF to be greater than
60 the amount actually used by nodes; this should really be fixed
61 in the formatting code (which needs to compute the real BTree
62 sizes before writing the volume header).
63 <HFS3> 2/19/97 djb Added ClearMemory. Changed CalcKeyLength to KeyLength.
64 <HFS2> 1/3/97 djb Added support for large keys.
65 <HFS1> 12/19/96 djb first checked in
67 History applicable to original Scarecrow Design:
69 <9> 10/25/96 ser Changing for new VFPI
70 <8> 10/18/96 ser Converting over VFPI changes
71 <7> 9/17/96 dkh More BTree statistics. Change IsItAHint to not always check to
72 see if the hint node is allocated.
73 <6> 9/16/96 dkh Revised BTree statistics.
74 <5> 6/20/96 dkh Radar #1358740. Change from using Pools to debug MemAllocators.
75 <4> 1/22/96 dkh Change Pools.i inclusion to PoolsPriv.i
76 <3> 1/10/96 msd Change 64-bit math to use real function names from Math64.i.
77 <2> 12/7/95 dkh D10E2 build. Changed usage of Ref data type to LogicalAddress.
78 <1> 10/18/95 rst Moved from Scarecrow project.
80 <19> 4/26/95 prp In UpdateHeader, clear the dirty flag after the BTree is updated.
81 <18> 1/12/95 wjk Adopt Model FileSystem changes in D5.
82 <17> 11/16/94 prp Add IsItAHint routine and use it whenever hint's node number was
84 <16> 10/5/94 bk add pools.h include file
85 <15> 9/30/94 prp Get in sync with D2 interface changes.
86 <14> 7/22/94 wjk Convert to the new set of header files.
87 <13> 12/2/93 wjk Move from Makefiles to BuildFiles. Fit into the ModernOS and
89 <12> 11/30/93 wjk Move from Makefiles to BuildFiles. Fit into the ModernOS and
91 <11> 11/23/93 wjk Changes required to compile on the RS6000.
92 <10> 8/31/93 prp Use U64SetU instead of S64Set.
93 <9> 6/2/93 gs Update for changes to FSErrors.h and add some comments.
94 <8> 5/21/93 gs Modify UpdateHeader to write out attributes. Remove
95 Get/UpdateNode from TrySimpleReplace.
96 <7> 5/10/93 gs Add TrySimpleReplace routine.
97 <6> 3/23/93 gs Change MoveData to take void * instead of Ptr. Add UpdateHeader
98 and ClearBytes routines.
99 <5> 2/8/93 gs Add FindIteratorPosition.
100 <4> 12/10/92 gs Implement CheckKeyDescriptor and the KeyDescriptor interpreter.
101 <3> 12/8/92 gs Add GetKeyDescriptor, VerifyHeader, and Alloc/Dealloc memory
103 <2> 12/2/92 gs Add CompareKeys routine.
104 <1> 11/15/92 gs first checked in
108 #include "../headers/BTreesPrivate.h"
111 ////////////////////////////// Routine Definitions //////////////////////////////
113 /*-------------------------------------------------------------------------------
114 Routine: CalcKeyRecordSize - Return size of combined key/record structure.
116 Function: Rounds keySize and recSize so they will end on word boundaries.
117 Does NOT add size of offset.
119 Input: keySize - length of key (including length field)
120 recSize - length of record data
124 Result: UInt16 - size of combined key/record that will be inserted in btree
125 -------------------------------------------------------------------------------*/
127 UInt16
CalcKeyRecordSize (UInt16 keySize
,
130 if ( M_IsOdd (keySize
) ) keySize
+= 1; // pad byte
132 if (M_IsOdd (recSize
) ) recSize
+= 1; // pad byte
134 return (keySize
+ recSize
);
139 /*-------------------------------------------------------------------------------
140 Routine: VerifyHeader - Validate fields of the BTree header record.
142 Function: Examines the fields of the BTree header record to determine if the
143 fork appears to contain a valid BTree.
145 Input: forkPtr - pointer to fork control block
146 header - pointer to BTree header
149 Result: noErr - success
151 -------------------------------------------------------------------------------*/
153 OSStatus
VerifyHeader (FCB
*filePtr
,
154 BTHeaderRec
*header
)
160 switch (header
->nodeSize
) // node size == 512*2^n
169 default: return fsBTInvalidHeaderErr
; //\80\80 E_BadNodeType
172 totalNodes
= header
->totalNodes
;
174 forkSize
= (UInt64
)totalNodes
* (UInt64
)header
->nodeSize
;
176 if ( forkSize
!= filePtr
->fcbEOF
)
177 return fsBTInvalidHeaderErr
;
179 if ( header
->freeNodes
>= totalNodes
)
180 return fsBTInvalidHeaderErr
;
182 if ( header
->rootNode
>= totalNodes
)
183 return fsBTInvalidHeaderErr
;
185 if ( header
->firstLeafNode
>= totalNodes
)
186 return fsBTInvalidHeaderErr
;
188 if ( header
->lastLeafNode
>= totalNodes
)
189 return fsBTInvalidHeaderErr
;
191 if ( header
->treeDepth
> kMaxTreeDepth
)
192 return fsBTInvalidHeaderErr
;
195 /////////////////////////// Check BTree Type ////////////////////////////////
197 switch (header
->btreeType
)
199 case 0: // HFS Type - no Key Descriptor
200 case kUserBTreeType
: // with Key Descriptors etc.
201 case kReservedBTreeType
: // Desktop Mgr BTree ?
204 default: return fsBTUnknownVersionErr
;
212 /*-------------------------------------------------------------------------------
213 Routine: UpdateHeader - Write BTreeInfoRec fields to Header node.
215 Function: Checks the kBTHeaderDirty flag in the BTreeInfoRec and updates the
216 header node if necessary.
218 Input: btreePtr - pointer to BTreeInfoRec
221 Result: noErr - success
223 -------------------------------------------------------------------------------*/
225 OSStatus
UpdateHeader(BTreeControlBlockPtr btreePtr
, Boolean forceWrite
)
228 BlockDescriptor node
;
233 if ((btreePtr
->flags
& kBTHeaderDirty
) == 0) // btree info already flushed
237 err
= GetNode (btreePtr
, kHeaderNodeNum
, &node
);
241 header
= (BTHeaderRec
*) ((char *)node
.buffer
+ sizeof(BTNodeDescriptor
));
243 header
->treeDepth
= btreePtr
->treeDepth
;
244 header
->rootNode
= btreePtr
->rootNode
;
245 header
->leafRecords
= btreePtr
->leafRecords
;
246 header
->firstLeafNode
= btreePtr
->firstLeafNode
;
247 header
->lastLeafNode
= btreePtr
->lastLeafNode
;
248 header
->nodeSize
= btreePtr
->nodeSize
; //\80\80 this shouldn't change
249 header
->maxKeyLength
= btreePtr
->maxKeyLength
; //\80\80 neither should this
250 header
->totalNodes
= btreePtr
->totalNodes
;
251 header
->freeNodes
= btreePtr
->freeNodes
;
252 header
->btreeType
= btreePtr
->btreeType
;
254 // ignore header->clumpSize; //\80\80 rename this field?
257 options
= kForceWriteBlock
;
259 options
= kLockTransaction
;
261 err
= UpdateNode (btreePtr
, &node
, 0, options
);
263 btreePtr
->flags
&= (~kBTHeaderDirty
);
270 /*-------------------------------------------------------------------------------
271 Routine: FindIteratorPosition - One_line_description.
273 Function: Brief_description_of_the_function_and_any_side_effects
275 Algorithm: see FSC.BT.BTIterateRecord.PICT
277 Note: //\80\80 document side-effects of bad node hints
279 Input: btreePtr - description
280 iterator - description
283 Output: iterator - description
287 nodeNum - description
288 returnIndex - description
289 foundRecord - description
292 Result: noErr - success
294 -------------------------------------------------------------------------------*/
296 OSStatus
FindIteratorPosition (BTreeControlBlockPtr btreePtr
,
297 BTreeIteratorPtr iterator
,
298 BlockDescriptor
*left
,
299 BlockDescriptor
*middle
,
300 BlockDescriptor
*right
,
301 UInt32
*returnNodeNum
,
303 Boolean
*foundRecord
)
308 UInt16 leftIndex
, index
, rightIndex
;
311 // assume btreePtr valid
312 // assume left, middle, right point to BlockDescriptors
313 // assume nodeNum points to UInt32
314 // assume index points to UInt16
315 // assume foundRecord points to Boolean
318 middle
->buffer
= nil
;
323 if (iterator
== nil
) // do we have an iterator?
325 err
= fsBTInvalidIteratorErr
;
329 err
= IsItAHint (btreePtr
, iterator
, &validHint
);
332 nodeNum
= iterator
->hint
.nodeNum
;
333 if (! validHint
) // does the hint appear to be valid?
338 err
= GetNode (btreePtr
, nodeNum
, middle
);
339 if( err
== fsBTInvalidNodeErr
) // returned if nodeNum is out of range
344 if ( ((NodeDescPtr
) middle
->buffer
)->kind
!= kBTLeafNode
||
345 ((NodeDescPtr
) middle
->buffer
)->numRecords
<= 0 )
350 ++btreePtr
->numValidHints
;
352 foundIt
= SearchNode (btreePtr
, middle
->buffer
, &iterator
->key
, &index
);
360 if (((NodeDescPtr
) middle
->buffer
)->bLink
== 0) // before 1st btree record
365 nodeNum
= ((NodeDescPtr
) middle
->buffer
)->bLink
;
367 err
= GetLeftSiblingNode (btreePtr
, middle
->buffer
, left
);
370 if ( ((NodeDescPtr
) left
->buffer
)->kind
!= kBTLeafNode
||
371 ((NodeDescPtr
) left
->buffer
)->numRecords
<= 0 )
376 foundIt
= SearchNode (btreePtr
, left
->buffer
, &iterator
->key
, &leftIndex
);
387 if (leftIndex
== 0) // we're lost!
391 else if (leftIndex
>= ((NodeDescPtr
) left
->buffer
)->numRecords
)
393 nodeNum
= ((NodeDescPtr
) left
->buffer
)->fLink
;
395 PanicIf (index
!= 0, "\pFindIteratorPosition: index != 0"); //\80\80 just checking...
408 else if (index
>= ((NodeDescPtr
) middle
->buffer
)->numRecords
)
410 if (((NodeDescPtr
) middle
->buffer
)->fLink
== 0) // beyond last record
415 nodeNum
= ((NodeDescPtr
) middle
->buffer
)->fLink
;
417 err
= GetRightSiblingNode (btreePtr
, middle
->buffer
, right
);
420 if ( ((NodeDescPtr
) right
->buffer
)->kind
!= kBTLeafNode
||
421 ((NodeDescPtr
) right
->buffer
)->numRecords
<= 0 )
426 foundIt
= SearchNode (btreePtr
, right
->buffer
, &iterator
->key
, &rightIndex
);
427 if (rightIndex
>= ((NodeDescPtr
) right
->buffer
)->numRecords
) // we're lost
431 else // we found it, or rightIndex==0, or rightIndex<numRecs
443 //////////////////////////// Search The Tree ////////////////////////////////
447 TreePathTable treePathTable
; // so we only use stack space if we need to
449 err
= ReleaseNode (btreePtr
, left
); M_ExitOnError (err
);
450 err
= ReleaseNode (btreePtr
, middle
); M_ExitOnError (err
);
451 err
= ReleaseNode (btreePtr
, right
); M_ExitOnError (err
);
453 err
= SearchTree ( btreePtr
, &iterator
->key
, treePathTable
, &nodeNum
, middle
, &index
);
454 switch (err
) //\80\80 separate find condition from exceptions
456 case noErr
: foundIt
= true; break;
457 case fsBTRecordNotFoundErr
: break;
458 default: goto ErrorExit
;
462 /////////////////////////////// Success! ////////////////////////////////////
466 *returnNodeNum
= nodeNum
;
467 *returnIndex
= index
;
468 *foundRecord
= foundIt
;
473 ////////////////////////////// Error Exit ///////////////////////////////////
477 (void) ReleaseNode (btreePtr
, left
);
478 (void) ReleaseNode (btreePtr
, middle
);
479 (void) ReleaseNode (btreePtr
, right
);
483 *foundRecord
= false;
490 /////////////////////////////// CheckInsertParams ///////////////////////////////
492 OSStatus
CheckInsertParams (FCB
*filePtr
,
493 BTreeIterator
*iterator
,
494 FSBufferDescriptor
*record
,
497 BTreeControlBlockPtr btreePtr
;
499 if (filePtr
== nil
) return paramErr
;
501 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
502 if (btreePtr
== nil
) return fsBTInvalidFileErr
;
503 if (iterator
== nil
) return paramErr
;
504 if (record
== nil
) return paramErr
;
506 // check total key/record size limit
507 if ( CalcKeyRecordSize (CalcKeySize(btreePtr
, &iterator
->key
), recordLen
) > (btreePtr
->nodeSize
>> 1))
508 return fsBTRecordTooLargeErr
;
515 /*-------------------------------------------------------------------------------
516 Routine: TrySimpleReplace - Attempts a simple insert, set, or replace.
518 Function: If a hint exitst for the iterator, attempt to find the key in the hint
519 node. If the key is found, an insert operation fails. If the is not
520 found, a replace operation fails. If the key was not found, and the
521 insert position is greater than 0 and less than numRecords, the record
522 is inserted, provided there is enough freeSpace. If the key was found,
523 and there is more freeSpace than the difference between the new record
524 and the old record, the old record is deleted and the new record is
527 Assumptions: iterator key has already been checked by CheckKey
530 Input: btreePtr - description
531 iterator - description
533 recordLen - description
534 operation - description
537 Output: recordInserted - description
540 Result: noErr - success
541 E_RecordExits - insert operation failure
542 != noErr - GetNode, ReleaseNode, UpdateNode returned an error
543 -------------------------------------------------------------------------------*/
545 OSStatus
TrySimpleReplace (BTreeControlBlockPtr btreePtr
,
547 BTreeIterator
*iterator
,
548 FSBufferDescriptor
*record
,
550 Boolean
*recordInserted
)
560 *recordInserted
= false; // we'll assume this won't work...
562 if ( nodePtr
->kind
!= kBTLeafNode
)
563 return noErr
; // we're in the weeds!
565 foundIt
= SearchNode (btreePtr
, nodePtr
, &iterator
->key
, &index
);
567 if ( foundIt
== false )
568 return noErr
; // we might be lost...
570 keySize
= CalcKeySize(btreePtr
, &iterator
->key
); // includes length field
572 spaceNeeded
= CalcKeyRecordSize (keySize
, recordLen
);
574 oldSpace
= GetRecordSize (btreePtr
, nodePtr
, index
);
576 if ( spaceNeeded
== oldSpace
)
580 dst
= GetRecordAddress (btreePtr
, nodePtr
, index
);
582 if ( M_IsOdd (keySize
) )
583 ++keySize
; // add pad byte
585 dst
+= keySize
; // skip over key to point at record
587 BlockMoveData(record
->bufferAddress
, dst
, recordLen
); // blast away...
589 *recordInserted
= true;
591 else if ( (GetNodeFreeSize(btreePtr
, nodePtr
) + oldSpace
) >= spaceNeeded
)
593 DeleteRecord (btreePtr
, nodePtr
, index
);
595 didItFit
= InsertKeyRecord (btreePtr
, nodePtr
, index
,
596 &iterator
->key
, KeyLength(btreePtr
, &iterator
->key
),
597 record
->bufferAddress
, recordLen
);
598 PanicIf (didItFit
== false, "\pTrySimpleInsert: InsertKeyRecord returned false!");
600 *recordInserted
= true;
602 // else not enough space...
608 /*-------------------------------------------------------------------------------
609 Routine: IsItAHint - checks the hint within a BTreeInterator.
611 Function: checks the hint within a BTreeInterator. If it is non-zero, it may
614 Input: btreePtr - pointer to control block for BTree file
615 iterator - pointer to BTreeIterator
617 Output: answer - true if the hint looks reasonable
618 - false if the hint is 0
620 Result: noErr - success
621 -------------------------------------------------------------------------------*/
624 OSStatus
IsItAHint (BTreeControlBlockPtr btreePtr
, BTreeIterator
*iterator
, Boolean
*answer
)
626 ++btreePtr
->numHintChecks
;
629 if (iterator
->hint
.nodeNum
>= btreePtr
->totalNodes
)
635 if (iterator
->hint
.nodeNum
== 0)
642 ++btreePtr
->numPossibleHints
;