2 * Copyright (c) 2000-2003, 2005-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: 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"
115 #include "../../hfs_btreeio.h"
118 ////////////////////////////// Routine Definitions //////////////////////////////
120 /*-------------------------------------------------------------------------------
121 Routine: CalcKeyRecordSize - Return size of combined key/record structure.
123 Function: Rounds keySize and recSize so they will end on word boundaries.
124 Does NOT add size of offset.
126 Input: keySize - length of key (including length field)
127 recSize - length of record data
131 Result: u_int16_t - size of combined key/record that will be inserted in btree
132 -------------------------------------------------------------------------------*/
134 u_int16_t
CalcKeyRecordSize (u_int16_t keySize
,
137 if ( M_IsOdd (keySize
) ) keySize
+= 1; // pad byte
139 if (M_IsOdd (recSize
) ) recSize
+= 1; // pad byte
141 return (keySize
+ recSize
);
146 /*-------------------------------------------------------------------------------
147 Routine: VerifyHeader - Validate fields of the BTree header record.
149 Function: Examines the fields of the BTree header record to determine if the
150 fork appears to contain a valid BTree.
152 Input: forkPtr - pointer to fork control block
153 header - pointer to BTree header
156 Result: noErr - success
158 -------------------------------------------------------------------------------*/
160 OSStatus
VerifyHeader (FCB
*filePtr
,
161 BTHeaderRec
*header
)
164 u_int32_t totalNodes
;
167 switch (header
->nodeSize
) // node size == 512*2^n
176 default: return fsBTInvalidHeaderErr
; //\80\80 E_BadNodeType
179 totalNodes
= header
->totalNodes
;
181 forkSize
= (u_int64_t
)totalNodes
* (u_int64_t
)header
->nodeSize
;
183 if ( forkSize
> (u_int64_t
)filePtr
->fcbEOF
)
184 return fsBTInvalidHeaderErr
;
186 if ( header
->freeNodes
>= totalNodes
)
187 return fsBTInvalidHeaderErr
;
189 if ( header
->rootNode
>= totalNodes
)
190 return fsBTInvalidHeaderErr
;
192 if ( header
->firstLeafNode
>= totalNodes
)
193 return fsBTInvalidHeaderErr
;
195 if ( header
->lastLeafNode
>= totalNodes
)
196 return fsBTInvalidHeaderErr
;
198 if ( header
->treeDepth
> kMaxTreeDepth
)
199 return fsBTInvalidHeaderErr
;
202 /////////////////////////// Check BTree Type ////////////////////////////////
204 switch (header
->btreeType
)
206 case 0: // HFS Type - no Key Descriptor
207 case kUserBTreeType
: // with Key Descriptors etc.
208 case kReservedBTreeType
: // Desktop Mgr BTree ?
211 default: return fsBTUnknownVersionErr
;
220 OSStatus
TreeIsDirty(BTreeControlBlockPtr btreePtr
)
222 return (btreePtr
->flags
& kBTHeaderDirty
);
227 /*-------------------------------------------------------------------------------
228 Routine: UpdateHeader - Write BTreeInfoRec fields to Header node.
230 Function: Checks the kBTHeaderDirty flag in the BTreeInfoRec and updates the
231 header node if necessary.
233 Input: btreePtr - pointer to BTreeInfoRec
236 Result: noErr - success
238 -------------------------------------------------------------------------------*/
240 OSStatus
UpdateHeader(BTreeControlBlockPtr btreePtr
, Boolean forceWrite
)
243 BlockDescriptor node
;
247 if ((btreePtr
->flags
& kBTHeaderDirty
) == 0) // btree info already flushed
251 err
= GetNode (btreePtr
, kHeaderNodeNum
, 0, &node
);
257 ModifyBlockStart(btreePtr
->fileRefNum
, &node
);
259 header
= (BTHeaderRec
*) ((char *)node
.buffer
+ sizeof(BTNodeDescriptor
));
261 header
->treeDepth
= btreePtr
->treeDepth
;
262 header
->rootNode
= btreePtr
->rootNode
;
263 header
->leafRecords
= btreePtr
->leafRecords
;
264 header
->firstLeafNode
= btreePtr
->firstLeafNode
;
265 header
->lastLeafNode
= btreePtr
->lastLeafNode
;
266 header
->nodeSize
= btreePtr
->nodeSize
; //\80\80 this shouldn't change
267 header
->maxKeyLength
= btreePtr
->maxKeyLength
; //\80\80 neither should this
268 header
->totalNodes
= btreePtr
->totalNodes
;
269 header
->freeNodes
= btreePtr
->freeNodes
;
270 header
->btreeType
= btreePtr
->btreeType
;
272 // ignore header->clumpSize; //\80\80 rename this field?
275 options
= kForceWriteBlock
;
277 options
= kLockTransaction
;
279 err
= UpdateNode (btreePtr
, &node
, 0, options
);
281 btreePtr
->flags
&= (~kBTHeaderDirty
);
288 /*-------------------------------------------------------------------------------
289 Routine: FindIteratorPosition - One_line_description.
291 Function: Brief_description_of_the_function_and_any_side_effects
293 Algorithm: see FSC.BT.BTIterateRecord.PICT
295 Note: //\80\80 document side-effects of bad node hints
297 Input: btreePtr - description
298 iterator - description
301 Output: iterator - description
305 nodeNum - description
306 returnIndex - description
307 foundRecord - description
310 Result: noErr - success
312 -------------------------------------------------------------------------------*/
314 OSStatus
FindIteratorPosition (BTreeControlBlockPtr btreePtr
,
315 BTreeIteratorPtr iterator
,
316 BlockDescriptor
*left
,
317 BlockDescriptor
*middle
,
318 BlockDescriptor
*right
,
319 u_int32_t
*returnNodeNum
,
320 u_int16_t
*returnIndex
,
321 Boolean
*foundRecord
)
326 u_int16_t leftIndex
, index
, rightIndex
;
329 // assume btreePtr valid
330 // assume left, middle, right point to BlockDescriptors
331 // assume nodeNum points to u_int32_t
332 // assume index points to u_int16_t
333 // assume foundRecord points to Boolean
336 left
->blockHeader
= nil
;
337 middle
->buffer
= nil
;
338 middle
->blockHeader
= nil
;
340 right
->blockHeader
= nil
;
344 if (iterator
== nil
) // do we have an iterator?
346 err
= fsBTInvalidIteratorErr
;
350 err
= IsItAHint (btreePtr
, iterator
, &validHint
);
353 nodeNum
= iterator
->hint
.nodeNum
;
354 if (! validHint
) // does the hint appear to be valid?
359 err
= GetNode (btreePtr
, nodeNum
, kGetNodeHint
, middle
);
360 if( err
== fsBTInvalidNodeErr
) // returned if nodeNum is out of range
365 if ( ((NodeDescPtr
) middle
->buffer
)->kind
!= kBTLeafNode
||
366 ((NodeDescPtr
) middle
->buffer
)->numRecords
<= 0 )
371 foundIt
= SearchNode (btreePtr
, middle
->buffer
, &iterator
->key
, &index
);
374 ++btreePtr
->numValidHints
;
377 iterator
->hint
.nodeNum
= 0;
381 if (((NodeDescPtr
) middle
->buffer
)->bLink
== 0) // before 1st btree record
386 nodeNum
= ((NodeDescPtr
) middle
->buffer
)->bLink
;
388 // BTree nodes are always grabbed in left to right order.
389 // Therefore release the current node before looking up the
391 err
= ReleaseNode(btreePtr
, middle
);
394 // Look up the left node
395 err
= GetNode (btreePtr
, nodeNum
, 0, left
);
398 // Look up the current node again
399 err
= GetRightSiblingNode (btreePtr
, left
->buffer
, middle
);
402 if ( ((NodeDescPtr
) left
->buffer
)->kind
!= kBTLeafNode
||
403 ((NodeDescPtr
) left
->buffer
)->numRecords
<= 0 )
408 foundIt
= SearchNode (btreePtr
, left
->buffer
, &iterator
->key
, &leftIndex
);
419 if (leftIndex
== 0) // we're lost!
423 else if (leftIndex
>= ((NodeDescPtr
) left
->buffer
)->numRecords
)
425 nodeNum
= ((NodeDescPtr
) left
->buffer
)->fLink
;
427 PanicIf (index
!= 0, "FindIteratorPosition: index != 0"); //\80\80 just checking...
440 else if (index
>= ((NodeDescPtr
) middle
->buffer
)->numRecords
)
442 if (((NodeDescPtr
) middle
->buffer
)->fLink
== 0) // beyond last record
447 nodeNum
= ((NodeDescPtr
) middle
->buffer
)->fLink
;
449 err
= GetRightSiblingNode (btreePtr
, middle
->buffer
, right
);
452 if ( ((NodeDescPtr
) right
->buffer
)->kind
!= kBTLeafNode
||
453 ((NodeDescPtr
) right
->buffer
)->numRecords
<= 0 )
458 foundIt
= SearchNode (btreePtr
, right
->buffer
, &iterator
->key
, &rightIndex
);
459 if (rightIndex
>= ((NodeDescPtr
) right
->buffer
)->numRecords
) // we're lost
463 else // we found it, or rightIndex==0, or rightIndex<numRecs
475 //////////////////////////// Search The Tree ////////////////////////////////
479 TreePathTable treePathTable
; // so we only use stack space if we need to
481 err
= ReleaseNode (btreePtr
, left
); M_ExitOnError (err
);
482 err
= ReleaseNode (btreePtr
, middle
); M_ExitOnError (err
);
483 err
= ReleaseNode (btreePtr
, right
); M_ExitOnError (err
);
485 err
= SearchTree ( btreePtr
, &iterator
->key
, treePathTable
, &nodeNum
, middle
, &index
);
486 switch (err
) //\80\80 separate find condition from exceptions
488 case noErr
: foundIt
= true; break;
489 case fsBTRecordNotFoundErr
: break;
490 default: goto ErrorExit
;
494 /////////////////////////////// Success! ////////////////////////////////////
498 *returnNodeNum
= nodeNum
;
499 *returnIndex
= index
;
500 *foundRecord
= foundIt
;
505 ////////////////////////////// Error Exit ///////////////////////////////////
509 (void) ReleaseNode (btreePtr
, left
);
510 (void) ReleaseNode (btreePtr
, middle
);
511 (void) ReleaseNode (btreePtr
, right
);
515 *foundRecord
= false;
522 /////////////////////////////// CheckInsertParams ///////////////////////////////
524 OSStatus
CheckInsertParams (FCB
*filePtr
,
525 BTreeIterator
*iterator
,
526 FSBufferDescriptor
*record
,
527 u_int16_t recordLen
)
529 BTreeControlBlockPtr btreePtr
;
531 if (filePtr
== nil
) return paramErr
;
533 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
534 if (btreePtr
== nil
) return fsBTInvalidFileErr
;
535 if (iterator
== nil
) return paramErr
;
536 if (record
== nil
) return paramErr
;
538 // check total key/record size limit
539 if ( CalcKeyRecordSize (CalcKeySize(btreePtr
, &iterator
->key
), recordLen
) > (btreePtr
->nodeSize
>> 1))
540 return fsBTRecordTooLargeErr
;
547 /*-------------------------------------------------------------------------------
548 Routine: TrySimpleReplace - Attempts a simple insert, set, or replace.
550 Function: If a hint exitst for the iterator, attempt to find the key in the hint
551 node. If the key is found, an insert operation fails. If the is not
552 found, a replace operation fails. If the key was not found, and the
553 insert position is greater than 0 and less than numRecords, the record
554 is inserted, provided there is enough freeSpace. If the key was found,
555 and there is more freeSpace than the difference between the new record
556 and the old record, the old record is deleted and the new record is
559 Assumptions: iterator key has already been checked by CheckKey
562 Input: btreePtr - description
563 iterator - description
565 recordLen - description
566 operation - description
569 Output: recordInserted - description
572 Result: noErr - success
573 E_RecordExits - insert operation failure
574 != noErr - GetNode, ReleaseNode, UpdateNode returned an error
575 -------------------------------------------------------------------------------*/
577 OSStatus
TrySimpleReplace (BTreeControlBlockPtr btreePtr
,
579 BTreeIterator
*iterator
,
580 FSBufferDescriptor
*record
,
582 Boolean
*recordInserted
)
585 u_int32_t spaceNeeded
;
592 *recordInserted
= false; // we'll assume this won't work...
594 if ( nodePtr
->kind
!= kBTLeafNode
)
595 return noErr
; // we're in the weeds!
597 foundIt
= SearchNode (btreePtr
, nodePtr
, &iterator
->key
, &index
);
599 if ( foundIt
== false )
600 return noErr
; // we might be lost...
602 keySize
= CalcKeySize(btreePtr
, &iterator
->key
); // includes length field
604 spaceNeeded
= CalcKeyRecordSize (keySize
, recordLen
);
606 oldSpace
= GetRecordSize (btreePtr
, nodePtr
, index
);
608 if ( spaceNeeded
== oldSpace
)
612 dst
= GetRecordAddress (btreePtr
, nodePtr
, index
);
614 if ( M_IsOdd (keySize
) )
615 ++keySize
; // add pad byte
617 dst
+= keySize
; // skip over key to point at record
619 BlockMoveData(record
->bufferAddress
, dst
, recordLen
); // blast away...
621 *recordInserted
= true;
623 else if ( (GetNodeFreeSize(btreePtr
, nodePtr
) + oldSpace
) >= spaceNeeded
)
625 DeleteRecord (btreePtr
, nodePtr
, index
);
627 didItFit
= InsertKeyRecord (btreePtr
, nodePtr
, index
,
628 &iterator
->key
, KeyLength(btreePtr
, &iterator
->key
),
629 record
->bufferAddress
, recordLen
);
630 PanicIf (didItFit
== false, "TrySimpleInsert: InsertKeyRecord returned false!");
632 *recordInserted
= true;
634 // else not enough space...
640 /*-------------------------------------------------------------------------------
641 Routine: IsItAHint - checks the hint within a BTreeInterator.
643 Function: checks the hint within a BTreeInterator. If it is non-zero, it may
646 Input: btreePtr - pointer to control block for BTree file
647 iterator - pointer to BTreeIterator
649 Output: answer - true if the hint looks reasonable
650 - false if the hint is 0
652 Result: noErr - success
653 -------------------------------------------------------------------------------*/
656 OSStatus
IsItAHint (BTreeControlBlockPtr btreePtr
, BTreeIterator
*iterator
, Boolean
*answer
)
658 ++btreePtr
->numHintChecks
;
661 if (iterator
->hint
.nodeNum
>= btreePtr
->totalNodes
)
667 if (iterator
->hint
.nodeNum
== 0)
674 ++btreePtr
->numPossibleHints
;