2 // lf_hfs_btree_misc_ops.c
5 // Created by Yakov Ben Zaken on 22/03/2018.
8 #include "lf_hfs_btrees_private.h"
9 #include "lf_hfs_btrees_io.h"
10 #include "lf_hfs_utils.h"
12 ////////////////////////////// Routine Definitions //////////////////////////////
14 /*-------------------------------------------------------------------------------
15 Routine: CalcKeyRecordSize - Return size of combined key/record structure.
17 Function: Rounds keySize and recSize so they will end on word boundaries.
18 Does NOT add size of offset.
20 Input: keySize - length of key (including length field)
21 recSize - length of record data
25 Result: u_int16_t - size of combined key/record that will be inserted in btree
26 -------------------------------------------------------------------------------*/
28 u_int16_t
CalcKeyRecordSize (u_int16_t keySize
,
31 if ( M_IsOdd (keySize
) ) keySize
+= 1; // pad byte
33 if (M_IsOdd (recSize
) ) recSize
+= 1; // pad byte
35 return (keySize
+ recSize
);
40 /*-------------------------------------------------------------------------------
41 Routine: VerifyHeader - Validate fields of the BTree header record.
43 Function: Examines the fields of the BTree header record to determine if the
44 fork appears to contain a valid BTree.
46 Input: forkPtr - pointer to fork control block
47 header - pointer to BTree header
50 Result: noErr - success
52 -------------------------------------------------------------------------------*/
54 OSStatus
VerifyHeader (FCB
*filePtr
,
61 switch (header
->nodeSize
) // node size == 512*2^n
70 default: return fsBTInvalidHeaderErr
; //E_BadNodeType
73 totalNodes
= header
->totalNodes
;
75 forkSize
= (u_int64_t
)totalNodes
* (u_int64_t
)header
->nodeSize
;
77 if ( forkSize
> (u_int64_t
)filePtr
->fcbEOF
)
78 return fsBTInvalidHeaderErr
;
80 if ( header
->freeNodes
>= totalNodes
)
81 return fsBTInvalidHeaderErr
;
83 if ( header
->rootNode
>= totalNodes
)
84 return fsBTInvalidHeaderErr
;
86 if ( header
->firstLeafNode
>= totalNodes
)
87 return fsBTInvalidHeaderErr
;
89 if ( header
->lastLeafNode
>= totalNodes
)
90 return fsBTInvalidHeaderErr
;
92 if ( header
->treeDepth
> kMaxTreeDepth
)
93 return fsBTInvalidHeaderErr
;
96 /////////////////////////// Check BTree Type ////////////////////////////////
98 switch (header
->btreeType
)
100 case 0: // HFS Type - no Key Descriptor
101 case kUserBTreeType
: // with Key Descriptors etc.
102 case kReservedBTreeType
: // Desktop Mgr BTree ?
105 default: return fsBTUnknownVersionErr
;
113 OSStatus
TreeIsDirty(BTreeControlBlockPtr btreePtr
)
115 return (btreePtr
->flags
& kBTHeaderDirty
);
120 /*-------------------------------------------------------------------------------
121 Routine: UpdateHeader - Write BTreeInfoRec fields to Header node.
123 Function: Checks the kBTHeaderDirty flag in the BTreeInfoRec and updates the
124 header node if necessary.
126 Input: btreePtr - pointer to BTreeInfoRec
129 Result: noErr - success
131 -------------------------------------------------------------------------------*/
133 OSStatus
UpdateHeader(BTreeControlBlockPtr btreePtr
, Boolean forceWrite
)
136 BlockDescriptor node
;
140 if ((btreePtr
->flags
& kBTHeaderDirty
) == 0) // btree info already flushed
143 err
= GetNode (btreePtr
, kHeaderNodeNum
, 0, &node
);
149 ModifyBlockStart(btreePtr
->fileRefNum
, &node
);
151 header
= (BTHeaderRec
*) ((char *)node
.buffer
+ sizeof(BTNodeDescriptor
));
153 header
->treeDepth
= btreePtr
->treeDepth
;
154 header
->rootNode
= btreePtr
->rootNode
;
155 header
->leafRecords
= btreePtr
->leafRecords
;
156 header
->firstLeafNode
= btreePtr
->firstLeafNode
;
157 header
->lastLeafNode
= btreePtr
->lastLeafNode
;
158 header
->nodeSize
= btreePtr
->nodeSize
; // this shouldn't change
159 header
->maxKeyLength
= btreePtr
->maxKeyLength
; // neither should this
160 header
->totalNodes
= btreePtr
->totalNodes
;
161 header
->freeNodes
= btreePtr
->freeNodes
;
162 header
->btreeType
= btreePtr
->btreeType
;
164 // ignore header->clumpSize; // rename this field?
167 options
= kForceWriteBlock
;
169 options
= kLockTransaction
;
171 err
= UpdateNode (btreePtr
, &node
, 0, options
);
173 btreePtr
->flags
&= (~kBTHeaderDirty
);
180 /*-------------------------------------------------------------------------------
181 Routine: FindIteratorPosition - One_line_description.
183 Function: Brief_description_of_the_function_and_any_side_effects
185 Algorithm: see FSC.BT.BTIterateRecord.PICT
187 Note: // document side-effects of bad node hints
189 Input: btreePtr - description
190 iterator - description
193 Output: iterator - description
197 nodeNum - description
198 returnIndex - description
199 foundRecord - description
202 Result: noErr - success
204 -------------------------------------------------------------------------------*/
206 OSStatus
FindIteratorPosition (BTreeControlBlockPtr btreePtr
,
207 BTreeIteratorPtr iterator
,
208 BlockDescriptor
*left
,
209 BlockDescriptor
*middle
,
210 BlockDescriptor
*right
,
211 u_int32_t
*returnNodeNum
,
212 u_int16_t
*returnIndex
,
213 Boolean
*foundRecord
)
218 u_int16_t leftIndex
, index
, rightIndex
;
221 // assume btreePtr valid
222 // assume left, middle, right point to BlockDescriptors
223 // assume nodeNum points to u_int32_t
224 // assume index points to u_int16_t
225 // assume foundRecord points to Boolean
228 left
->blockHeader
= nil
;
229 middle
->buffer
= nil
;
230 middle
->blockHeader
= nil
;
232 right
->blockHeader
= nil
;
236 if (iterator
== nil
) // do we have an iterator?
238 err
= fsBTInvalidIteratorErr
;
242 err
= IsItAHint (btreePtr
, iterator
, &validHint
);
245 nodeNum
= iterator
->hint
.nodeNum
;
246 if (! validHint
) // does the hint appear to be valid?
251 err
= GetNode (btreePtr
, nodeNum
, kGetNodeHint
, middle
);
252 if( err
== fsBTInvalidNodeErr
) // returned if nodeNum is out of range
257 if ( ((NodeDescPtr
) middle
->buffer
)->kind
!= kBTLeafNode
||
258 ((NodeDescPtr
) middle
->buffer
)->numRecords
<= 0 )
263 foundIt
= SearchNode (btreePtr
, middle
->buffer
, &iterator
->key
, &index
);
266 ++btreePtr
->numValidHints
;
269 iterator
->hint
.nodeNum
= 0;
273 if (((NodeDescPtr
) middle
->buffer
)->bLink
== 0) // before 1st btree record
278 nodeNum
= ((NodeDescPtr
) middle
->buffer
)->bLink
;
280 // BTree nodes are always grabbed in left to right order.
281 // Therefore release the current node before looking up the
283 err
= ReleaseNode(btreePtr
, middle
);
286 // Look up the left node
287 err
= GetNode (btreePtr
, nodeNum
, 0, left
);
290 // Look up the current node again
291 err
= GetRightSiblingNode (btreePtr
, left
->buffer
, middle
);
294 if ( ((NodeDescPtr
) left
->buffer
)->kind
!= kBTLeafNode
||
295 ((NodeDescPtr
) left
->buffer
)->numRecords
<= 0 )
300 foundIt
= SearchNode (btreePtr
, left
->buffer
, &iterator
->key
, &leftIndex
);
311 if (leftIndex
== 0) // we're lost!
315 else if (leftIndex
>= ((NodeDescPtr
) left
->buffer
)->numRecords
)
317 nodeNum
= ((NodeDescPtr
) left
->buffer
)->fLink
;
320 LFHFS_LOG(LEVEL_ERROR
, "FindIteratorPosition: index != 0\n");
335 else if (index
>= ((NodeDescPtr
) middle
->buffer
)->numRecords
)
337 if (((NodeDescPtr
) middle
->buffer
)->fLink
== 0) // beyond last record
342 nodeNum
= ((NodeDescPtr
) middle
->buffer
)->fLink
;
344 err
= GetRightSiblingNode (btreePtr
, middle
->buffer
, right
);
347 if ( ((NodeDescPtr
) right
->buffer
)->kind
!= kBTLeafNode
||
348 ((NodeDescPtr
) right
->buffer
)->numRecords
<= 0 )
353 foundIt
= SearchNode (btreePtr
, right
->buffer
, &iterator
->key
, &rightIndex
);
354 if (rightIndex
>= ((NodeDescPtr
) right
->buffer
)->numRecords
) // we're lost
358 else // we found it, or rightIndex==0, or rightIndex<numRecs
370 //////////////////////////// Search The Tree ////////////////////////////////
374 TreePathTable treePathTable
; // so we only use stack space if we need to
376 err
= ReleaseNode (btreePtr
, left
); M_ExitOnError (err
);
377 err
= ReleaseNode (btreePtr
, middle
); M_ExitOnError (err
);
378 err
= ReleaseNode (btreePtr
, right
); M_ExitOnError (err
);
380 err
= SearchTree ( btreePtr
, &iterator
->key
, treePathTable
, &nodeNum
, middle
, &index
);
381 switch (err
) // separate find condition from exceptions
383 case noErr
: foundIt
= true; break;
384 case fsBTRecordNotFoundErr
: break;
385 default: goto ErrorExit
;
389 /////////////////////////////// Success! ////////////////////////////////////
393 *returnNodeNum
= nodeNum
;
394 *returnIndex
= index
;
395 *foundRecord
= foundIt
;
400 ////////////////////////////// Error Exit ///////////////////////////////////
404 (void) ReleaseNode (btreePtr
, left
);
405 (void) ReleaseNode (btreePtr
, middle
);
406 (void) ReleaseNode (btreePtr
, right
);
410 *foundRecord
= false;
417 /////////////////////////////// CheckInsertParams ///////////////////////////////
419 OSStatus
CheckInsertParams (FCB
*filePtr
,
420 BTreeIterator
*iterator
,
421 FSBufferDescriptor
*record
,
422 u_int16_t recordLen
)
424 BTreeControlBlockPtr btreePtr
;
426 if (filePtr
== nil
) return paramErr
;
428 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
429 if (btreePtr
== nil
) return fsBTInvalidFileErr
;
430 if (iterator
== nil
) return paramErr
;
431 if (record
== nil
) return paramErr
;
433 // check total key/record size limit
434 if ( CalcKeyRecordSize (CalcKeySize(btreePtr
, &iterator
->key
), recordLen
) > (btreePtr
->nodeSize
>> 1))
435 return fsBTRecordTooLargeErr
;
442 /*-------------------------------------------------------------------------------
443 Routine: TrySimpleReplace - Attempts a simple insert, set, or replace.
445 Function: If a hint exitst for the iterator, attempt to find the key in the hint
446 node. If the key is found, an insert operation fails. If the is not
447 found, a replace operation fails. If the key was not found, and the
448 insert position is greater than 0 and less than numRecords, the record
449 is inserted, provided there is enough freeSpace. If the key was found,
450 and there is more freeSpace than the difference between the new record
451 and the old record, the old record is deleted and the new record is
454 Assumptions: iterator key has already been checked by CheckKey
457 Input: btreePtr - description
458 iterator - description
460 recordLen - description
461 operation - description
464 Output: recordInserted - description
467 Result: noErr - success
468 E_RecordExits - insert operation failure
469 != noErr - GetNode, ReleaseNode, UpdateNode returned an error
470 -------------------------------------------------------------------------------*/
472 OSStatus
TrySimpleReplace (BTreeControlBlockPtr btreePtr
,
474 BTreeIterator
*iterator
,
475 FSBufferDescriptor
*record
,
477 Boolean
*recordInserted
)
480 u_int32_t spaceNeeded
;
487 *recordInserted
= false; // we'll assume this won't work...
489 if ( nodePtr
->kind
!= kBTLeafNode
)
490 return noErr
; // we're in the weeds!
492 foundIt
= SearchNode (btreePtr
, nodePtr
, &iterator
->key
, &index
);
494 if ( foundIt
== false )
495 return noErr
; // we might be lost...
497 keySize
= CalcKeySize(btreePtr
, &iterator
->key
); // includes length field
499 spaceNeeded
= CalcKeyRecordSize (keySize
, recordLen
);
501 oldSpace
= GetRecordSize (btreePtr
, nodePtr
, index
);
503 if ( spaceNeeded
== oldSpace
)
507 dst
= GetRecordAddress (btreePtr
, nodePtr
, index
);
509 if ( M_IsOdd (keySize
) )
510 ++keySize
; // add pad byte
512 dst
+= keySize
; // skip over key to point at record
514 BlockMoveData(record
->bufferAddress
, dst
, recordLen
); // blast away...
516 *recordInserted
= true;
518 else if ( (GetNodeFreeSize(btreePtr
, nodePtr
) + oldSpace
) >= spaceNeeded
)
520 DeleteRecord (btreePtr
, nodePtr
, index
);
522 didItFit
= InsertKeyRecord (btreePtr
, nodePtr
, index
,
523 &iterator
->key
, KeyLength(btreePtr
, &iterator
->key
),
524 record
->bufferAddress
, recordLen
);
525 if (didItFit
== false)
527 LFHFS_LOG(LEVEL_ERROR
, "TrySimpleInsert: InsertKeyRecord returned false!");
530 *recordInserted
= true;
532 // else not enough space...
538 /*-------------------------------------------------------------------------------
539 Routine: IsItAHint - checks the hint within a BTreeInterator.
541 Function: checks the hint within a BTreeInterator. If it is non-zero, it may
544 Input: btreePtr - pointer to control block for BTree file
545 iterator - pointer to BTreeIterator
547 Output: answer - true if the hint looks reasonable
548 - false if the hint is 0
550 Result: noErr - success
551 -------------------------------------------------------------------------------*/
554 OSStatus
IsItAHint (BTreeControlBlockPtr btreePtr
, BTreeIterator
*iterator
, Boolean
*answer
)
556 ++btreePtr
->numHintChecks
;
557 if (iterator
->hint
.nodeNum
== 0)
564 ++btreePtr
->numPossibleHints
;