4 #include "lf_hfs_defs.h"
5 #include "lf_hfs_file_mgr_internal.h"
6 #include "lf_hfs_btrees_private.h"
7 #include "lf_hfs_btrees_internal.h"
8 #include "lf_hfs_btrees_io.h"
9 #include "lf_hfs_vfsutils.h"
10 #include "lf_hfs_vfsops.h"
11 #include "lf_hfs_file_extent_mapping.h"
12 #include "lf_hfs_utils.h"
14 //////////////////////////////////// Globals ////////////////////////////////////
17 /////////////////////////// BTree Module Entry Points ///////////////////////////
21 /*-------------------------------------------------------------------------------
22 Routine: BTOpenPath - Open a file for access as a B*Tree.
24 Function: Create BTree control block for a file, if necessary. Validates the
25 file to be sure it looks like a BTree file.
28 Input: filePtr - pointer to file to open as a B-tree
29 keyCompareProc - pointer to client's KeyCompare function
31 Result: noErr - success
32 paramErr - required ptr was nil
36 -------------------------------------------------------------------------------*/
38 OSStatus
BTOpenPath(FCB
*filePtr
, KeyCompareProcPtr keyCompareProc
)
41 BTreeControlBlockPtr btreePtr
;
45 ////////////////////// Preliminary Error Checking ///////////////////////////
53 * Subsequent opens allow key compare proc to be changed.
55 if ( filePtr
->fcbBTCBPtr
!= nil
&& keyCompareProc
!= nil
) {
56 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
57 btreePtr
->keyCompareProc
= keyCompareProc
;
61 if ( filePtr
->fcbEOF
< kMinNodeSize
)
62 return fsBTInvalidFileErr
;
65 //////////////////////// Allocate Control Block /////////////////////////////
67 btreePtr
= hfs_mallocz(sizeof(BTreeControlBlock
));
69 btreePtr
->getBlockProc
= GetBTreeBlock
;
70 btreePtr
->releaseBlockProc
= ReleaseBTreeBlock
;
71 btreePtr
->setEndOfForkProc
= ExtendBTreeFile
;
72 btreePtr
->keyCompareProc
= keyCompareProc
;
74 /////////////////////////// Read Header Node ////////////////////////////////
76 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
77 btreePtr
->fileRefNum
= GetFileRefNumFromFCB(filePtr
);
78 filePtr
->fcbBTCBPtr
= (Ptr
) btreePtr
; // attach btree cb to file
80 /* Prefer doing I/O a physical block at a time */
81 nodeRec
.blockSize
= VTOHFS(btreePtr
->fileRefNum
)->hfs_physical_block_size
;
83 /* Start with the allocation block size for regular files. */
84 if (FTOC(filePtr
)->c_fileid
>= kHFSFirstUserCatalogNodeID
)
86 nodeRec
.blockSize
= FCBTOVCB(filePtr
)->blockSize
;
88 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
90 // it is now safe to call M_ExitOnError (err)
92 err
= SetBTreeBlockSize (btreePtr
->fileRefNum
, nodeRec
.blockSize
, 1);
96 err
= GetBTreeBlock(btreePtr
->fileRefNum
,
102 nodeRec
.buffer
= nil
;
103 nodeRec
.blockHeader
= nil
;
105 LFHFS_LOG(LEVEL_ERROR
, "BTOpen: getNodeProc returned error getting header node.");
108 ++btreePtr
->numGetNodes
;
109 header
= (BTHeaderRec
*) ((uintptr_t)nodeRec
.buffer
+ sizeof(BTNodeDescriptor
));
112 ///////////////////////////// verify header /////////////////////////////////
114 err
= VerifyHeader (filePtr
, header
);
118 ///////////////////// Initalize fields from header //////////////////////////
120 if ( (FCBTOVCB(filePtr
)->vcbSigWord
!= 0x4244) && (header
->nodeSize
== 512))
122 LFHFS_LOG(LEVEL_ERROR
, "BTOpenPath: wrong node size for HFS+ volume!");
126 btreePtr
->treeDepth
= header
->treeDepth
;
127 btreePtr
->rootNode
= header
->rootNode
;
128 btreePtr
->leafRecords
= header
->leafRecords
;
129 btreePtr
->firstLeafNode
= header
->firstLeafNode
;
130 btreePtr
->lastLeafNode
= header
->lastLeafNode
;
131 btreePtr
->nodeSize
= header
->nodeSize
;
132 btreePtr
->maxKeyLength
= header
->maxKeyLength
;
133 btreePtr
->totalNodes
= header
->totalNodes
;
134 btreePtr
->freeNodes
= header
->freeNodes
;
135 if (FTOC(filePtr
)->c_fileid
>= kHFSFirstUserCatalogNodeID
)
136 filePtr
->ff_clumpsize
= header
->clumpSize
;
137 btreePtr
->btreeType
= header
->btreeType
;
139 btreePtr
->keyCompareType
= header
->keyCompareType
;
141 btreePtr
->attributes
= header
->attributes
;
143 if ( btreePtr
->maxKeyLength
> 40 )
144 btreePtr
->attributes
|= (kBTBigKeysMask
+ kBTVariableIndexKeysMask
); //we need a way to save these attributes
146 /////////////////////// Initialize dynamic fields ///////////////////////////
148 btreePtr
->version
= kBTreeVersion
;
150 btreePtr
->writeCount
= 1;
152 /////////////////////////// Check Header Node ///////////////////////////////
154 // set kBadClose attribute bit, and UpdateNode
156 /* b-tree node size must be at least as big as the logical block size */
157 if (btreePtr
->nodeSize
< VTOHFS(btreePtr
->fileRefNum
)->hfs_logical_block_size
)
160 * If this tree has any records or the media is writeable then
161 * we cannot mount using the current physical block size.
163 if (btreePtr
->leafRecords
> 0 ||
164 VTOHFS(btreePtr
->fileRefNum
)->hfs_flags
& HFS_WRITEABLE_MEDIA
)
166 err
= fsBTBadNodeSize
;
172 * If the actual node size is different than the amount we read,
173 * then release and trash this block, and re-read with the correct
176 if ( btreePtr
->nodeSize
!= nodeRec
.blockSize
)
178 err
= SetBTreeBlockSize (btreePtr
->fileRefNum
, btreePtr
->nodeSize
, 32);
182 * Need to use kTrashBlock option to force the
183 * buffer cache to read the entire node
185 err
= ReleaseBTreeBlock(btreePtr
->fileRefNum
, &nodeRec
, kTrashBlock
);
186 ++btreePtr
->numReleaseNodes
;
189 err
= GetNode (btreePtr
, kHeaderNodeNum
, 0, &nodeRec
);
193 //total nodes * node size <= LEOF?
196 err
= ReleaseNode (btreePtr
, &nodeRec
);
200 * Under Mac OS, b-tree nodes can be non-contiguous on disk when the
201 * allocation block size is smaller than the b-tree node size.
203 * If journaling is turned on for this volume we can't deal with this
204 * situation and so we bail out. If journaling isn't on it's ok as
205 * hfs_strategy_fragmented() deals with it. Journaling can't support
206 * this because it assumes that if you give it a block that it's
207 * contiguous on disk.
209 if ( FCBTOHFS(filePtr
)->jnl
&& !NodesAreContiguous(FCBTOVCB(filePtr
), filePtr
, btreePtr
->nodeSize
) ) {
210 return fsBTInvalidNodeErr
;
213 //////////////////////////////// Success ////////////////////////////////////
215 //align LEOF to multiple of node size? - just on close
220 /////////////////////// Error - Clean up and Exit ///////////////////////////
224 filePtr
->fcbBTCBPtr
= nil
;
225 (void) ReleaseNode (btreePtr
, &nodeRec
);
233 /*-------------------------------------------------------------------------------
234 Routine: BTClosePath - Flush BTree Header and Deallocate Memory for BTree.
236 Function: Flush the BTreeControlBlock fields to header node, and delete BTree control
237 block and key descriptor associated with the file if filePtr is last
238 path of type kBTreeType ('btre').
241 Input: filePtr - pointer to file to delete BTree control block for.
243 Result: noErr - success
246 -------------------------------------------------------------------------------*/
248 OSStatus
BTClosePath (FCB
*filePtr
)
251 BTreeControlBlockPtr btreePtr
;
253 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
256 return fsBTInvalidFileErr
;
258 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
260 ////////////////////// Check for other BTree Paths //////////////////////////
262 btreePtr
->attributes
&= ~kBTBadCloseMask
; // clear "bad close" attribute bit
263 err
= UpdateHeader (btreePtr
, true);
267 filePtr
->fcbBTCBPtr
= nil
;
271 /////////////////////// Error - Clean Up and Exit ///////////////////////////
280 /*-------------------------------------------------------------------------------
281 Routine: BTSearchRecord - Search BTree for a record with a matching key.
283 Function: Search for position in B*Tree indicated by searchKey. If a valid node hint
284 is provided, it will be searched first, then SearchTree will be called.
285 If a BTreeIterator is provided, it will be set to the position found as
286 a result of the search. If a record exists at that position, and a BufferDescriptor
287 is supplied, the record will be copied to the buffer (as much as will fit),
288 and recordLen will be set to the length of the record.
290 If an error other than fsBTRecordNotFoundErr occurs, the BTreeIterator, if any,
291 is invalidated, and recordLen is set to 0.
294 Input: pathPtr - pointer to path for BTree file.
295 searchKey - pointer to search key to match.
296 hintPtr - pointer to hint (may be nil)
298 Output: record - pointer to BufferDescriptor containing record
299 recordLen - length of data at recordPtr
300 iterator - pointer to BTreeIterator indicating position result of search
302 Result: noErr - success, record contains copy of record found
303 fsBTRecordNotFoundErr - record was not found, no data copied
304 fsBTInvalidFileErr - no BTreeControlBlock is allocated for the fork
305 fsBTInvalidKeyLengthErr -
307 -------------------------------------------------------------------------------*/
309 OSStatus
BTSearchRecord (FCB
*filePtr
,
310 BTreeIterator
*searchIterator
,
311 FSBufferDescriptor
*record
,
312 u_int16_t
*recordLen
,
313 BTreeIterator
*resultIterator
)
316 BTreeControlBlockPtr btreePtr
;
317 TreePathTable treePathTable
;
318 u_int32_t nodeNum
= 0;
319 BlockDescriptor node
;
321 BTreeKeyPtr keyPtr
= NULL
;
332 if (searchIterator
== nil
)
338 node
.blockHeader
= nil
;
340 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
343 return fsBTInvalidFileErr
;
346 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
350 ////////////////////////////// Take A Hint //////////////////////////////////
352 err
= IsItAHint (btreePtr
, searchIterator
, &validHint
);
357 nodeNum
= searchIterator
->hint
.nodeNum
;
359 err
= GetNode (btreePtr
, nodeNum
, kGetNodeHint
, &node
);
362 if ( ((BTNodeDescriptor
*) node
.buffer
)->kind
== kBTLeafNode
&&
363 ((BTNodeDescriptor
*) node
.buffer
)->numRecords
> 0 )
365 foundRecord
= SearchNode (btreePtr
, node
.buffer
, &searchIterator
->key
, &index
);
367 //if !foundRecord, we could still skip tree search if ( 0 < index < numRecords )
370 if (foundRecord
== false)
372 err
= ReleaseNode (btreePtr
, &node
);
377 ++btreePtr
->numValidHints
;
381 if( foundRecord
== false )
382 (void) BTInvalidateHint( searchIterator
);
386 //////////////////////////// Search The Tree ////////////////////////////////
388 if (foundRecord
== false)
390 err
= SearchTree ( btreePtr
, &searchIterator
->key
, treePathTable
, &nodeNum
, &node
, &index
);
396 case fsBTRecordNotFoundErr
:
404 //////////////////////////// Get the Record /////////////////////////////////
406 if (foundRecord
== true)
408 //XXX Should check for errors! Or BlockMove could choke on recordPtr!!!
409 GetRecordByIndex (btreePtr
, node
.buffer
, index
, &keyPtr
, &recordPtr
, &len
);
411 if (recordLen
!= nil
) *recordLen
= len
;
415 ByteCount recordSize
;
417 recordSize
= record
->itemCount
* record
->itemSize
;
419 if (len
> recordSize
) len
= recordSize
;
421 BlockMoveData (recordPtr
, record
->bufferAddress
, len
);
426 /////////////////////// Success - Update Iterator ///////////////////////////
428 if (resultIterator
!= nil
)
431 resultIterator
->hint
.writeCount
= btreePtr
->writeCount
;
432 resultIterator
->hint
.nodeNum
= nodeNum
;
433 resultIterator
->hint
.index
= index
;
436 resultIterator
->hint
.reserved1
= 0;
437 resultIterator
->hint
.reserved2
= 0;
438 resultIterator
->version
= 0;
439 resultIterator
->reserved
= 0;
441 // copy the key in the BTree when found rather than searchIterator->key to get proper case/diacriticals
442 if (foundRecord
== true) {
443 BlockMoveData ((Ptr
)keyPtr
, (Ptr
)&resultIterator
->key
, CalcKeySize(btreePtr
, keyPtr
));
445 BlockMoveData ((Ptr
)&searchIterator
->key
, (Ptr
)&resultIterator
->key
, CalcKeySize(btreePtr
, &searchIterator
->key
));
449 err
= ReleaseNode (btreePtr
, &node
);
452 if (foundRecord
== false) return fsBTRecordNotFoundErr
;
456 /////////////////////// Error - Clean Up and Exit ///////////////////////////
460 if (recordLen
!= nil
)
463 if (resultIterator
!= nil
)
465 resultIterator
->hint
.writeCount
= 0;
466 resultIterator
->hint
.nodeNum
= 0;
467 resultIterator
->hint
.index
= 0;
468 resultIterator
->hint
.reserved1
= 0;
469 resultIterator
->hint
.reserved2
= 0;
471 resultIterator
->version
= 0;
472 resultIterator
->reserved
= 0;
473 resultIterator
->key
.length16
= 0; // zero out two bytes to cover both types of keys
476 if ( err
== fsBTEmptyErr
)
477 err
= fsBTRecordNotFoundErr
;
484 /*-------------------------------------------------------------------------------
485 Routine: BTIterateRecord - Find the first, next, previous, or last record.
487 Function: Find the first, next, previous, or last record in the BTree
489 Input: pathPtr - pointer to path iterate records for.
490 operation - iteration operation (first,next,prev,last)
491 iterator - pointer to iterator indicating start position
493 Output: iterator - iterator is updated to indicate new position
494 newKeyPtr - pointer to buffer to copy key found by iteration
495 record - pointer to buffer to copy record found by iteration
496 recordLen - length of record
498 Result: noErr - success
500 -------------------------------------------------------------------------------*/
502 OSStatus
BTIterateRecord (FCB
*filePtr
,
503 BTreeIterationOperation operation
,
504 BTreeIterator
*iterator
,
505 FSBufferDescriptor
*record
,
506 u_int16_t
*recordLen
)
509 BTreeControlBlockPtr btreePtr
;
517 BlockDescriptor left
, node
, right
;
521 ////////////////////////// Priliminary Checks ///////////////////////////////
524 left
.blockHeader
= nil
;
526 right
.blockHeader
= nil
;
528 node
.blockHeader
= nil
;
536 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
539 return fsBTInvalidFileErr
; //handle properly
542 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
544 if ((operation
!= kBTreeFirstRecord
) &&
545 (operation
!= kBTreeNextRecord
) &&
546 (operation
!= kBTreeCurrentRecord
) &&
547 (operation
!= kBTreePrevRecord
) &&
548 (operation
!= kBTreeLastRecord
))
550 err
= fsInvalidIterationMovmentErr
;
554 /////////////////////// Find First or Last Record ///////////////////////////
556 if ((operation
== kBTreeFirstRecord
) || (operation
== kBTreeLastRecord
))
558 if (operation
== kBTreeFirstRecord
) nodeNum
= btreePtr
->firstLeafNode
;
559 else nodeNum
= btreePtr
->lastLeafNode
;
567 err
= GetNode (btreePtr
, nodeNum
, 0, &node
);
570 if ( ((NodeDescPtr
) node
.buffer
)->kind
!= kBTLeafNode
||
571 ((NodeDescPtr
) node
.buffer
)->numRecords
<= 0 )
573 err
= ReleaseNode (btreePtr
, &node
);
576 err
= fsBTInvalidNodeErr
;
577 LFHFS_LOG(LEVEL_ERROR
, "BTIterateRecord() found invalid btree node on volume %s\n", FCBTOVCB(filePtr
)->vcbVN
);
578 hfs_mark_inconsistent(FCBTOVCB(filePtr
), HFS_INCONSISTENCY_DETECTED
);
582 if (operation
== kBTreeFirstRecord
) index
= 0;
583 else index
= ((BTNodeDescriptor
*) node
.buffer
)->numRecords
- 1;
585 goto CopyData
; //is there a cleaner way?
589 //////////////////////// Find Iterator Position /////////////////////////////
591 // Not called for (operation == kBTreeFirstRecord || operation == kBTreeLastRecord)
592 err
= FindIteratorPosition (btreePtr
, iterator
,
593 &left
, &node
, &right
, &nodeNum
, &index
, &foundRecord
);
597 ///////////////////// Find Next Or Previous Record //////////////////////////
599 if (operation
== kBTreePrevRecord
)
607 if (left
.buffer
== nil
)
609 nodeNum
= ((NodeDescPtr
) node
.buffer
)->bLink
;
612 // BTree nodes are always grabbed in left to right order.
613 // Therefore release the current node before looking up the
615 err
= ReleaseNode(btreePtr
, &node
);
618 // Look up the left node
619 err
= GetNode (btreePtr
, nodeNum
, 0, &left
);
622 // Look up the current node again
623 err
= GetRightSiblingNode (btreePtr
, left
.buffer
, &node
);
626 err
= fsBTStartOfIterationErr
;
630 // Before we stomp on "right", we'd better release it if needed
631 if (right
.buffer
!= nil
) {
632 err
= ReleaseNode(btreePtr
, &right
);
638 index
= ((NodeDescPtr
) node
.buffer
)->numRecords
-1;
641 else if (operation
== kBTreeNextRecord
)
643 if ((foundRecord
!= true) &&
644 (((NodeDescPtr
) node
.buffer
)->fLink
== 0) &&
645 (index
== ((NodeDescPtr
) node
.buffer
)->numRecords
))
647 err
= fsBTEndOfIterationErr
;
651 // we did not find the record but the index is already positioned correctly
652 if ((foundRecord
== false) && (index
!= ((NodeDescPtr
) node
.buffer
)->numRecords
))
655 // we found the record OR we have to look in the next node
656 if (index
< ((NodeDescPtr
) node
.buffer
)->numRecords
-1)
662 if (right
.buffer
== nil
)
664 nodeNum
= ((NodeDescPtr
) node
.buffer
)->fLink
;
667 err
= GetNode (btreePtr
, nodeNum
, 0, &right
);
670 err
= fsBTEndOfIterationErr
;
674 // Before we stomp on "left", we'd better release it if needed
675 if (left
.buffer
!= nil
) {
676 err
= ReleaseNode(btreePtr
, &left
);
685 else // operation == kBTreeCurrentRecord
687 // make sure we have something... <CS9>
688 if ((foundRecord
!= true) &&
689 (index
>= ((NodeDescPtr
) node
.buffer
)->numRecords
))
691 err
= fsBTEndOfIterationErr
;
696 //////////////////// Copy Record And Update Iterator ////////////////////////
700 // added check for errors <CS9>
701 err
= GetRecordByIndex (btreePtr
, node
.buffer
, index
, &keyPtr
, &recordPtr
, &len
);
704 if (recordLen
!= nil
)
709 ByteCount recordSize
;
711 recordSize
= record
->itemCount
* record
->itemSize
;
713 if (len
> recordSize
) len
= recordSize
;
715 BlockMoveData (recordPtr
, record
->bufferAddress
, len
);
718 if (iterator
!= nil
) // first & last do not require iterator
720 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
721 iterator
->hint
.nodeNum
= nodeNum
;
722 iterator
->hint
.index
= index
;
723 iterator
->hint
.reserved1
= 0;
724 iterator
->hint
.reserved2
= 0;
726 iterator
->version
= 0;
727 iterator
->reserved
= 0;
730 * Check for infinite loops by making sure we do not
731 * process more leaf records, than can possibly be (or the BTree header
732 * is seriously damaged)....a brute force method.
734 if ((operation
== kBTreeFirstRecord
) || (operation
== kBTreeLastRecord
))
735 iterator
->hitCount
= 1;
736 else if (operation
!= kBTreeCurrentRecord
)
737 iterator
->hitCount
+= 1;
738 /* Always use the highest max, in case the grows while iterating */
739 iterator
->maxLeafRecs
= max(btreePtr
->leafRecords
, iterator
->maxLeafRecs
);
742 if (iterator
->hitCount
> iterator
->maxLeafRecs
+ kNumLeafRecSlack
)
744 err
= fsBTInvalidNodeErr
;
745 LFHFS_LOG(LEVEL_ERROR
, "BTIterateRecord() found invalid btree node on volume %s\n", FCBTOVCB(filePtr
)->vcbVN
);
746 hfs_mark_inconsistent(FCBTOVCB(filePtr
), HFS_INCONSISTENCY_DETECTED
);
751 BlockMoveData ((Ptr
)keyPtr
, (Ptr
)&iterator
->key
, CalcKeySize(btreePtr
, keyPtr
));
755 ///////////////////////////// Release Nodes /////////////////////////////////
757 err
= ReleaseNode (btreePtr
, &node
);
760 if (left
.buffer
!= nil
)
762 err
= ReleaseNode (btreePtr
, &left
);
766 if (right
.buffer
!= nil
)
768 err
= ReleaseNode (btreePtr
, &right
);
774 /////////////////////// Error - Clean Up and Exit ///////////////////////////
778 (void) ReleaseNode (btreePtr
, &left
);
779 (void) ReleaseNode (btreePtr
, &node
);
780 (void) ReleaseNode (btreePtr
, &right
);
782 if (recordLen
!= nil
)
787 iterator
->hint
.writeCount
= 0;
788 iterator
->hint
.nodeNum
= 0;
789 iterator
->hint
.index
= 0;
790 iterator
->hint
.reserved1
= 0;
791 iterator
->hint
.reserved2
= 0;
793 iterator
->version
= 0;
794 iterator
->reserved
= 0;
795 iterator
->key
.length16
= 0;
798 if ( err
== fsBTEmptyErr
|| err
== fsBTEndOfIterationErr
)
799 err
= fsBTRecordNotFoundErr
;
805 /*-------------------------------------------------------------------------------
806 Routine: BTIterateRecords
808 Function: Find a series of records
810 Input: filePtr - b-tree file
811 operation - iteration operation (first,next,prev,last)
812 iterator - pointer to iterator indicating start position
813 callBackProc - pointer to routince to process a record
814 callBackState - pointer to state data (used by callBackProc)
816 Output: iterator - iterator is updated to indicate new position
818 Result: noErr - success
820 -------------------------------------------------------------------------------*/
823 BTIterateRecords(FCB
*filePtr
, BTreeIterationOperation operation
, BTreeIterator
*iterator
,
824 IterateCallBackProcPtr callBackProc
, void * callBackState
)
827 BTreeControlBlockPtr btreePtr
;
833 BlockDescriptor left
, node
, right
;
837 ////////////////////////// Priliminary Checks ///////////////////////////////
840 left
.blockHeader
= nil
;
842 right
.blockHeader
= nil
;
844 node
.blockHeader
= nil
;
846 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
848 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
850 if ((operation
!= kBTreeFirstRecord
) &&
851 (operation
!= kBTreeNextRecord
) &&
852 (operation
!= kBTreeCurrentRecord
) &&
853 (operation
!= kBTreePrevRecord
) &&
854 (operation
!= kBTreeLastRecord
))
856 err
= fsInvalidIterationMovmentErr
;
860 /////////////////////// Find First or Last Record ///////////////////////////
862 if ((operation
== kBTreeFirstRecord
) || (operation
== kBTreeLastRecord
))
864 if (operation
== kBTreeFirstRecord
)
865 nodeNum
= btreePtr
->firstLeafNode
;
867 nodeNum
= btreePtr
->lastLeafNode
;
875 err
= GetNode(btreePtr
, nodeNum
, 0, &node
);
878 if ( ((NodeDescPtr
)node
.buffer
)->kind
!= kBTLeafNode
||
879 ((NodeDescPtr
)node
.buffer
)->numRecords
<= 0 )
881 err
= ReleaseNode(btreePtr
, &node
);
884 err
= fsBTInvalidNodeErr
;
885 LFHFS_LOG(LEVEL_ERROR
, "BTIterateRecords() found invalid btree node on volume %s\n", FCBTOVCB(filePtr
)->vcbVN
);
886 hfs_mark_inconsistent(FCBTOVCB(filePtr
), HFS_INCONSISTENCY_DETECTED
);
890 if (operation
== kBTreeFirstRecord
)
893 index
= ((BTNodeDescriptor
*) node
.buffer
)->numRecords
- 1;
898 //////////////////////// Find Iterator Position /////////////////////////////
900 // Not called for (operation == kBTreeFirstRecord || operation == kBTreeLastRecord)
901 err
= FindIteratorPosition(btreePtr
, iterator
, &left
, &node
, &right
,
902 &nodeNum
, &index
, &foundRecord
);
903 if (err
== fsBTRecordNotFoundErr
)
908 ///////////////////// Find Next Or Previous Record //////////////////////////
910 if (operation
== kBTreePrevRecord
)
918 if (left
.buffer
== nil
)
920 nodeNum
= ((NodeDescPtr
) node
.buffer
)->bLink
;
923 // BTree nodes are always grabbed in left to right order.
924 // Therefore release the current node before looking up the
926 err
= ReleaseNode(btreePtr
, &node
);
929 // Look up the left node
930 err
= GetNode (btreePtr
, nodeNum
, 0, &left
);
933 // Look up the current node again
934 err
= GetRightSiblingNode (btreePtr
, left
.buffer
, &node
);
937 err
= fsBTStartOfIterationErr
;
941 // Before we stomp on "right", we'd better release it if needed
942 if (right
.buffer
!= nil
) {
943 err
= ReleaseNode(btreePtr
, &right
);
949 index
= ((NodeDescPtr
) node
.buffer
)->numRecords
-1;
952 else if (operation
== kBTreeNextRecord
)
954 if ((foundRecord
!= true) &&
955 (((NodeDescPtr
)node
.buffer
)->fLink
== 0) &&
956 (index
== ((NodeDescPtr
)node
.buffer
)->numRecords
))
958 err
= fsBTEndOfIterationErr
;
962 // we did not find the record but the index is already positioned correctly
963 if ((foundRecord
== false) && (index
!= ((NodeDescPtr
)node
.buffer
)->numRecords
))
966 // we found the record OR we have to look in the next node
967 if (index
< ((NodeDescPtr
)node
.buffer
)->numRecords
-1)
973 if (right
.buffer
== nil
)
975 nodeNum
= ((NodeDescPtr
)node
.buffer
)->fLink
;
978 err
= GetNode(btreePtr
, nodeNum
, 0, &right
);
981 err
= fsBTEndOfIterationErr
;
985 // Before we stomp on "left", we'd better release it if needed
986 if (left
.buffer
!= nil
) {
987 err
= ReleaseNode(btreePtr
, &left
);
996 else // operation == kBTreeCurrentRecord
998 // make sure we have something... <CS9>
999 if ((foundRecord
!= true) &&
1000 (index
>= ((NodeDescPtr
)node
.buffer
)->numRecords
))
1002 err
= fsBTEndOfIterationErr
;
1007 //////////////////// Process Records Using Callback ////////////////////////
1010 err
= GetRecordByIndex(btreePtr
, node
.buffer
, index
, &keyPtr
, &recordPtr
, &len
);
1017 if (callBackProc(keyPtr
, recordPtr
, callBackState
) == 0)
1020 if ((index
+1) < ((NodeDescPtr
)node
.buffer
)->numRecords
) {
1023 if (right
.buffer
== nil
)
1025 nodeNum
= ((NodeDescPtr
)node
.buffer
)->fLink
;
1028 err
= GetNode(btreePtr
, nodeNum
, 0, &right
);
1031 err
= fsBTEndOfIterationErr
;
1035 // Before we stomp on "left", we'd better release it if needed
1036 if (left
.buffer
!= nil
) {
1037 err
= ReleaseNode(btreePtr
, &left
);
1045 err
= GetRecordByIndex(btreePtr
, node
.buffer
, index
,
1046 &keyPtr
, &recordPtr
, &len
);
1054 ///////////////// Update Iterator to Last Item Processed /////////////////////
1057 if (iterator
!= nil
) // first & last have optional iterator
1059 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
1060 iterator
->hint
.nodeNum
= nodeNum
;
1061 iterator
->hint
.index
= index
;
1062 iterator
->version
= 0;
1064 BlockMoveData((Ptr
)keyPtr
, (Ptr
)&iterator
->key
, CalcKeySize(btreePtr
, keyPtr
));
1069 ///////////////////////////// Release Nodes /////////////////////////////////
1071 err
= ReleaseNode(btreePtr
, &node
);
1074 if (left
.buffer
!= nil
)
1076 err
= ReleaseNode(btreePtr
, &left
);
1080 if (right
.buffer
!= nil
)
1082 err
= ReleaseNode(btreePtr
, &right
);
1088 /////////////////////// Error - Clean Up and Exit ///////////////////////////
1092 (void) ReleaseNode(btreePtr
, &left
);
1093 (void) ReleaseNode(btreePtr
, &node
);
1094 (void) ReleaseNode(btreePtr
, &right
);
1096 if (iterator
!= nil
)
1098 iterator
->hint
.writeCount
= 0;
1099 iterator
->hint
.nodeNum
= 0;
1100 iterator
->hint
.index
= 0;
1101 iterator
->version
= 0;
1102 iterator
->key
.length16
= 0;
1105 if ( err
== fsBTEmptyErr
|| err
== fsBTEndOfIterationErr
)
1106 err
= fsBTRecordNotFoundErr
;
1112 //////////////////////////////// BTInsertRecord /////////////////////////////////
1114 OSStatus
BTInsertRecord (FCB
*filePtr
,
1115 BTreeIterator
*iterator
,
1116 FSBufferDescriptor
*record
,
1117 u_int16_t recordLen
)
1120 BTreeControlBlockPtr btreePtr
;
1121 TreePathTable treePathTable
;
1122 u_int32_t nodesNeeded
;
1123 BlockDescriptor nodeRec
;
1124 u_int32_t insertNodeNum
;
1128 ////////////////////////// Priliminary Checks ///////////////////////////////
1130 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
1131 nodeRec
.blockHeader
= nil
;
1133 err
= CheckInsertParams (filePtr
, iterator
, record
, recordLen
);
1137 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1139 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1142 ///////////////////////// Find Insert Position //////////////////////////////
1144 // always call SearchTree for Insert
1145 err
= SearchTree (btreePtr
, &iterator
->key
, treePathTable
, &insertNodeNum
, &nodeRec
, &index
);
1147 switch (err
) // set/replace/insert decision point
1149 case noErr
: err
= fsBTDuplicateRecordErr
;
1152 case fsBTRecordNotFoundErr
: break;
1154 case fsBTEmptyErr
: // if tree empty add 1st leaf node
1156 if (btreePtr
->freeNodes
== 0)
1158 err
= ExtendBTree (btreePtr
, btreePtr
->totalNodes
+ 1);
1159 M_ExitOnError (err
);
1162 err
= AllocateNode (btreePtr
, &insertNodeNum
);
1163 M_ExitOnError (err
);
1165 err
= GetNewNode (btreePtr
, insertNodeNum
, &nodeRec
);
1166 M_ExitOnError (err
);
1169 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1171 ((NodeDescPtr
)nodeRec
.buffer
)->kind
= kBTLeafNode
;
1172 ((NodeDescPtr
)nodeRec
.buffer
)->height
= 1;
1174 recordFit
= InsertKeyRecord (btreePtr
, nodeRec
.buffer
, 0,
1175 &iterator
->key
, KeyLength(btreePtr
, &iterator
->key
),
1176 record
->bufferAddress
, recordLen
);
1177 if (recordFit
!= true)
1179 err
= fsBTRecordTooLargeErr
;
1184 * Update the B-tree control block. Do this before
1185 * calling UpdateNode since it will compare the node's
1186 * height with treeDepth.
1188 btreePtr
->treeDepth
= 1;
1189 btreePtr
->rootNode
= insertNodeNum
;
1190 btreePtr
->firstLeafNode
= insertNodeNum
;
1191 btreePtr
->lastLeafNode
= insertNodeNum
;
1193 err
= UpdateNode (btreePtr
, &nodeRec
, 0, kLockTransaction
);
1194 M_ExitOnError (err
);
1196 M_BTreeHeaderDirty (btreePtr
);
1200 default: goto ErrorExit
;
1206 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1208 recordFit
= InsertKeyRecord (btreePtr
, nodeRec
.buffer
, index
,
1209 &iterator
->key
, KeyLength(btreePtr
, &iterator
->key
),
1210 record
->bufferAddress
, recordLen
);
1211 if (recordFit
== true)
1213 err
= UpdateNode (btreePtr
, &nodeRec
, 0, kLockTransaction
);
1214 M_ExitOnError (err
);
1220 /////////////////////// Extend File If Necessary ////////////////////////////
1222 if ((btreePtr
->treeDepth
+ 1UL) > btreePtr
->freeNodes
)
1224 nodesNeeded
= btreePtr
->treeDepth
+ 1 + btreePtr
->totalNodes
- btreePtr
->freeNodes
;
1225 if (nodesNeeded
> CalcMapBits (btreePtr
)) // we'll need to add a map node too!
1228 err
= ExtendBTree (btreePtr
, nodesNeeded
);
1229 M_ExitOnError (err
);
1232 // no need to delete existing record
1234 err
= InsertTree (btreePtr
, treePathTable
, &iterator
->key
, record
->bufferAddress
,
1235 recordLen
, &nodeRec
, index
, 1, kInsertRecord
, &insertNodeNum
);
1236 M_ExitOnError (err
);
1239 //////////////////////////////// Success ////////////////////////////////////
1242 ++btreePtr
->writeCount
;
1243 ++btreePtr
->leafRecords
;
1244 M_BTreeHeaderDirty (btreePtr
);
1247 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
1248 iterator
->hint
.nodeNum
= insertNodeNum
;
1249 iterator
->hint
.index
= 0; // unused
1250 iterator
->hint
.reserved1
= 0;
1251 iterator
->hint
.reserved2
= 0;
1256 ////////////////////////////// Error Exit ///////////////////////////////////
1260 (void) ReleaseNode (btreePtr
, &nodeRec
);
1262 iterator
->hint
.writeCount
= 0;
1263 iterator
->hint
.nodeNum
= 0;
1264 iterator
->hint
.index
= 0;
1265 iterator
->hint
.reserved1
= 0;
1266 iterator
->hint
.reserved2
= 0;
1268 if (err
== fsBTEmptyErr
)
1269 err
= fsBTRecordNotFoundErr
;
1275 //////////////////////////////// BTReplaceRecord ////////////////////////////////
1277 OSStatus
BTReplaceRecord (FCB
*filePtr
,
1278 BTreeIterator
*iterator
,
1279 FSBufferDescriptor
*record
,
1280 u_int16_t recordLen
)
1283 BTreeControlBlockPtr btreePtr
;
1284 TreePathTable treePathTable
;
1285 u_int32_t nodesNeeded
;
1286 BlockDescriptor nodeRec
;
1287 u_int32_t insertNodeNum
;
1293 ////////////////////////// Priliminary Checks ///////////////////////////////
1295 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
1296 nodeRec
.blockHeader
= nil
;
1298 err
= CheckInsertParams (filePtr
, iterator
, record
, recordLen
);
1302 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1304 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1306 ////////////////////////////// Take A Hint //////////////////////////////////
1308 err
= IsItAHint (btreePtr
, iterator
, &validHint
);
1309 M_ExitOnError (err
);
1313 insertNodeNum
= iterator
->hint
.nodeNum
;
1315 err
= GetNode (btreePtr
, insertNodeNum
, kGetNodeHint
, &nodeRec
);
1319 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1321 err
= TrySimpleReplace (btreePtr
, nodeRec
.buffer
, iterator
, record
, recordLen
, &recordFit
);
1322 M_ExitOnError (err
);
1326 err
= UpdateNode (btreePtr
, &nodeRec
, 0, 0);
1327 M_ExitOnError (err
);
1329 ++btreePtr
->numValidHints
;
1335 (void) BTInvalidateHint( iterator
);
1338 err
= ReleaseNode (btreePtr
, &nodeRec
);
1339 M_ExitOnError (err
);
1343 (void) BTInvalidateHint( iterator
);
1348 ////////////////////////////// Get A Clue ///////////////////////////////////
1350 err
= SearchTree (btreePtr
, &iterator
->key
, treePathTable
, &insertNodeNum
, &nodeRec
, &index
);
1351 M_ExitOnError (err
); // record must exit for Replace
1353 // optimization - if simple replace will work then don't extend btree
1354 // if we tried this before, and failed because it wouldn't fit then we shouldn't try this again...
1357 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1359 err
= TrySimpleReplace (btreePtr
, nodeRec
.buffer
, iterator
, record
, recordLen
, &recordFit
);
1360 M_ExitOnError (err
);
1364 err
= UpdateNode (btreePtr
, &nodeRec
, 0, 0);
1365 M_ExitOnError (err
);
1371 //////////////////////////// Make Some Room /////////////////////////////////
1373 if ((btreePtr
->treeDepth
+ 1UL) > btreePtr
->freeNodes
)
1375 nodesNeeded
= btreePtr
->treeDepth
+ 1 + btreePtr
->totalNodes
- btreePtr
->freeNodes
;
1376 if (nodesNeeded
> CalcMapBits (btreePtr
)) // we'll need to add a map node too!
1379 err
= ExtendBTree (btreePtr
, nodesNeeded
);
1380 M_ExitOnError (err
);
1384 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1386 DeleteRecord (btreePtr
, nodeRec
.buffer
, index
); // delete existing key/record
1388 err
= InsertTree (btreePtr
, treePathTable
, &iterator
->key
, record
->bufferAddress
,
1389 recordLen
, &nodeRec
, index
, 1, kReplaceRecord
, &insertNodeNum
);
1390 M_ExitOnError (err
);
1392 ++btreePtr
->writeCount
; /* writeCount changes only if the tree structure changed */
1396 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
1397 iterator
->hint
.nodeNum
= insertNodeNum
;
1398 iterator
->hint
.index
= 0; // unused
1399 iterator
->hint
.reserved1
= 0;
1400 iterator
->hint
.reserved2
= 0;
1405 ////////////////////////////// Error Exit ///////////////////////////////////
1409 (void) ReleaseNode (btreePtr
, &nodeRec
);
1411 iterator
->hint
.writeCount
= 0;
1412 iterator
->hint
.nodeNum
= 0;
1413 iterator
->hint
.index
= 0;
1414 iterator
->hint
.reserved1
= 0;
1415 iterator
->hint
.reserved2
= 0;
1422 //////////////////////////////// BTUpdateRecord ////////////////////////////////
1425 BTUpdateRecord(FCB
*filePtr
, BTreeIterator
*iterator
,
1426 IterateCallBackProcPtr callBackProc
, void * callBackState
)
1429 BTreeControlBlockPtr btreePtr
;
1430 TreePathTable treePathTable
;
1431 BlockDescriptor nodeRec
;
1432 RecordPtr recordPtr
;
1434 u_int32_t insertNodeNum
;
1435 u_int16_t recordLen
;
1440 ////////////////////////// Priliminary Checks ///////////////////////////////
1442 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
1443 nodeRec
.blockHeader
= nil
;
1445 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1447 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
1449 ////////////////////////////// Take A Hint //////////////////////////////////
1451 err
= IsItAHint (btreePtr
, iterator
, &validHint
);
1452 M_ExitOnError (err
);
1456 insertNodeNum
= iterator
->hint
.nodeNum
;
1458 err
= GetNode (btreePtr
, insertNodeNum
, kGetNodeHint
, &nodeRec
);
1461 if (((NodeDescPtr
)nodeRec
.buffer
)->kind
== kBTLeafNode
&&
1462 SearchNode (btreePtr
, nodeRec
.buffer
, &iterator
->key
, &index
))
1464 err
= GetRecordByIndex(btreePtr
, nodeRec
.buffer
, index
, &keyPtr
, &recordPtr
, &recordLen
);
1465 M_ExitOnError (err
);
1468 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1470 err
= callBackProc(keyPtr
, recordPtr
, callBackState
);
1471 M_ExitOnError (err
);
1473 err
= UpdateNode (btreePtr
, &nodeRec
, 0, 0);
1474 M_ExitOnError (err
);
1476 ++btreePtr
->numValidHints
;
1482 (void) BTInvalidateHint( iterator
);
1485 err
= ReleaseNode (btreePtr
, &nodeRec
);
1486 M_ExitOnError (err
);
1490 (void) BTInvalidateHint( iterator
);
1494 ////////////////////////////// Get A Clue ///////////////////////////////////
1496 err
= SearchTree (btreePtr
, &iterator
->key
, treePathTable
, &insertNodeNum
, &nodeRec
, &index
);
1497 M_ExitOnError (err
);
1499 err
= GetRecordByIndex(btreePtr
, nodeRec
.buffer
, index
, &keyPtr
, &recordPtr
, &recordLen
);
1500 M_ExitOnError (err
);
1503 ModifyBlockStart(btreePtr
->fileRefNum
, &nodeRec
);
1505 err
= callBackProc(keyPtr
, recordPtr
, callBackState
);
1506 M_ExitOnError (err
);
1508 err
= UpdateNode (btreePtr
, &nodeRec
, 0, 0);
1509 M_ExitOnError (err
);
1513 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
1514 iterator
->hint
.nodeNum
= insertNodeNum
;
1515 iterator
->hint
.index
= 0;
1516 iterator
->hint
.reserved1
= 0;
1517 iterator
->hint
.reserved2
= 0;
1520 ////////////////////////////// Error Exit ///////////////////////////////////
1524 (void) ReleaseNode (btreePtr
, &nodeRec
);
1526 iterator
->hint
.writeCount
= 0;
1527 iterator
->hint
.nodeNum
= 0;
1528 iterator
->hint
.index
= 0;
1529 iterator
->hint
.reserved1
= 0;
1530 iterator
->hint
.reserved2
= 0;
1536 //////////////////////////////// BTDeleteRecord /////////////////////////////////
1538 OSStatus
BTDeleteRecord (FCB
*filePtr
,
1539 BTreeIterator
*iterator
)
1542 BTreeControlBlockPtr btreePtr
;
1543 TreePathTable treePathTable
;
1544 BlockDescriptor nodeRec
;
1545 u_int32_t nodesNeeded
;
1550 ////////////////////////// Priliminary Checks ///////////////////////////////
1552 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
1553 nodeRec
.blockHeader
= nil
;
1555 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1556 M_ReturnErrorIf (iterator
== nil
, paramErr
);
1558 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1559 if (btreePtr
== nil
)
1561 err
= fsBTInvalidFileErr
;
1565 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1568 /////////////////////////////// Find Key ////////////////////////////////////
1570 // check hint for simple delete case (index > 0, numRecords > 2)
1572 err
= SearchTree (btreePtr
, &iterator
->key
, treePathTable
, &nodeNum
, &nodeRec
, &index
);
1573 M_ExitOnError (err
); // record must exit for Delete
1576 /////////////////////// Extend File If Necessary ////////////////////////////
1579 * Worst case: we delete the first record in the tree and
1580 * following key is sufficiently larger to cause all parents to
1581 * require splitting and we need a new root node and a new map
1584 if (index
== 0 && btreePtr
->treeDepth
+ 1 > btreePtr
->freeNodes
)
1586 nodesNeeded
= btreePtr
->treeDepth
+ btreePtr
->totalNodes
;
1587 if (nodesNeeded
> CalcMapBits (btreePtr
))
1590 if (nodesNeeded
- btreePtr
->totalNodes
> btreePtr
->freeNodes
) {
1591 err
= ExtendBTree (btreePtr
, nodesNeeded
);
1592 M_ExitOnError (err
);
1596 ///////////////////////////// Delete Record /////////////////////////////////
1598 err
= DeleteTree (btreePtr
, treePathTable
, &nodeRec
, index
, 1);
1599 M_ExitOnError (err
);
1601 ++btreePtr
->writeCount
;
1602 --btreePtr
->leafRecords
;
1603 M_BTreeHeaderDirty (btreePtr
);
1605 iterator
->hint
.nodeNum
= 0;
1609 ////////////////////////////// Error Exit ///////////////////////////////////
1612 (void) ReleaseNode (btreePtr
, &nodeRec
);
1619 OSStatus
BTGetInformation (FCB
*filePtr
,
1620 u_int16_t file_version
,
1621 BTreeInfoRec
*info
)
1623 #pragma unused (file_version)
1625 BTreeControlBlockPtr btreePtr
;
1628 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1630 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1634 * This should not require the whole tree to be locked, just maybe the BTreeControlBlockPtr
1636 * REQUIRE_FILE_LOCK(btreePtr->fileRefNum, true);
1639 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1640 M_ReturnErrorIf (info
== nil
, paramErr
);
1644 info
->nodeSize
= btreePtr
->nodeSize
;
1645 info
->maxKeyLength
= btreePtr
->maxKeyLength
;
1646 info
->treeDepth
= btreePtr
->treeDepth
;
1647 info
->numRecords
= btreePtr
->leafRecords
;
1648 info
->numNodes
= btreePtr
->totalNodes
;
1649 info
->numFreeNodes
= btreePtr
->freeNodes
;
1650 info
->lastfsync
= btreePtr
->lastfsync
;
1651 info
->keyCompareType
= btreePtr
->keyCompareType
;
1657 BTIsDirty(FCB
*filePtr
)
1659 BTreeControlBlockPtr btreePtr
;
1661 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1662 return TreeIsDirty(btreePtr
);
1665 /*-------------------------------------------------------------------------------
1666 Routine: BTFlushPath - Flush BTreeControlBlock to Header Node.
1668 Function: Brief_description_of_the_function_and_any_side_effects
1671 Input: pathPtr - pointer to path control block for B*Tree file to flush
1675 Result: noErr - success
1677 -------------------------------------------------------------------------------*/
1679 OSStatus
BTFlushPath (FCB
*filePtr
)
1682 BTreeControlBlockPtr btreePtr
;
1685 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1687 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1689 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1691 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
1693 err
= UpdateHeader (btreePtr
, false);
1699 /*-------------------------------------------------------------------------------
1700 Routine: BTReload - Reload B-tree Header Data.
1702 Function: Reload B-tree header data from disk. This is called after fsck
1703 has made repairs to the root filesystem. The filesystem is
1704 mounted read-only when BTReload is caled.
1707 Input: filePtr - the B*Tree file that needs its header updated
1711 Result: noErr - success
1713 -------------------------------------------------------------------------------*/
1716 BTReloadData(FCB
*filePtr
)
1719 BTreeControlBlockPtr btreePtr
;
1720 BlockDescriptor node
;
1721 BTHeaderRec
*header
;
1725 node
.blockHeader
= nil
;
1727 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1728 if (btreePtr
== nil
)
1729 return (fsBTInvalidFileErr
);
1731 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1733 err
= GetNode(btreePtr
, kHeaderNodeNum
, 0, &node
);
1737 header
= (BTHeaderRec
*)((char *)node
.buffer
+ sizeof(BTNodeDescriptor
));
1738 if ((err
= VerifyHeader (filePtr
, header
)) == 0) {
1739 btreePtr
->treeDepth
= header
->treeDepth
;
1740 btreePtr
->rootNode
= header
->rootNode
;
1741 btreePtr
->leafRecords
= header
->leafRecords
;
1742 btreePtr
->firstLeafNode
= header
->firstLeafNode
;
1743 btreePtr
->lastLeafNode
= header
->lastLeafNode
;
1744 btreePtr
->maxKeyLength
= header
->maxKeyLength
;
1745 btreePtr
->totalNodes
= header
->totalNodes
;
1746 btreePtr
->freeNodes
= header
->freeNodes
;
1747 btreePtr
->btreeType
= header
->btreeType
;
1749 btreePtr
->flags
&= (~kBTHeaderDirty
);
1752 (void) ReleaseNode(btreePtr
, &node
);
1758 /*-------------------------------------------------------------------------------
1759 Routine: BTInvalidateHint - Invalidates the hint within a BTreeInterator.
1761 Function: Invalidates the hint within a BTreeInterator.
1764 Input: iterator - pointer to BTreeIterator
1766 Output: iterator - iterator with the hint.nodeNum cleared
1768 Result: noErr - success
1769 paramErr - iterator == nil
1770 -------------------------------------------------------------------------------*/
1773 OSStatus
BTInvalidateHint (BTreeIterator
*iterator
)
1775 if (iterator
== nil
)
1778 iterator
->hint
.nodeNum
= 0;
1786 /*-------------------------------------------------------------------------------
1787 Routine: BTGetLastSync
1789 Function: Returns the last time that this btree was flushed, does not include header.
1791 Input: filePtr - pointer file control block
1793 Output: lastfsync - time in seconds of last update
1795 Result: noErr - success
1796 paramErr - iterator == nil
1797 -------------------------------------------------------------------------------*/
1800 OSStatus
BTGetLastSync (FCB
*filePtr
,
1801 u_int32_t
*lastsync
)
1803 BTreeControlBlockPtr btreePtr
;
1806 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1808 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1810 /* Maybe instead of requiring a lock..an atomic set might be more appropriate */
1811 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
1813 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1814 M_ReturnErrorIf (lastsync
== nil
, paramErr
);
1816 *lastsync
= btreePtr
->lastfsync
;
1824 /*-------------------------------------------------------------------------------
1825 Routine: BTSetLastSync
1827 Function: Sets the last time that this btree was flushed, does not include header.
1830 Input: fcb - pointer file control block
1832 Output: lastfsync - time in seconds of last update
1834 Result: noErr - success
1835 paramErr - iterator == nil
1836 -------------------------------------------------------------------------------*/
1839 OSStatus
BTSetLastSync (FCB
*filePtr
,
1842 BTreeControlBlockPtr btreePtr
;
1845 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1847 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1849 /* Maybe instead of requiring a lock..an atomic set might be more appropriate */
1850 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
1852 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1853 M_ReturnErrorIf (lastsync
== 0, paramErr
);
1855 btreePtr
->lastfsync
= lastsync
;
1860 OSStatus
BTHasContiguousNodes (FCB
*filePtr
)
1862 BTreeControlBlockPtr btreePtr
;
1865 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1867 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1869 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, true);
1871 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1873 return NodesAreContiguous(FCBTOVCB(filePtr
), filePtr
, btreePtr
->nodeSize
);
1877 /*-------------------------------------------------------------------------------
1878 Routine: BTGetUserData
1880 Function: Read the user data area of the b-tree header node.
1882 -------------------------------------------------------------------------------*/
1884 BTGetUserData(FCB
*filePtr
, void * dataPtr
, int dataSize
)
1886 BTreeControlBlockPtr btreePtr
;
1887 BlockDescriptor node
;
1891 if (dataSize
> kBTreeHeaderUserBytes
)
1894 node
.blockHeader
= nil
;
1896 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1897 if (btreePtr
== nil
)
1898 return (fsBTInvalidFileErr
);
1900 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1902 err
= GetNode(btreePtr
, kHeaderNodeNum
, 0, &node
);
1906 offset
= (char *)node
.buffer
+ sizeof(BTNodeDescriptor
) + sizeof(BTHeaderRec
);
1907 bcopy(offset
, dataPtr
, dataSize
);
1909 (void) ReleaseNode(btreePtr
, &node
);
1915 /*-------------------------------------------------------------------------------
1916 Routine: BTSetUserData
1918 Function: Write the user data area of the b-tree header node.
1919 -------------------------------------------------------------------------------*/
1921 BTSetUserData(FCB
*filePtr
, void * dataPtr
, int dataSize
)
1923 BTreeControlBlockPtr btreePtr
;
1924 BlockDescriptor node
;
1928 if (dataSize
> kBTreeHeaderUserBytes
)
1931 node
.blockHeader
= nil
;
1933 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
1934 if (btreePtr
== nil
)
1935 return (fsBTInvalidFileErr
);
1937 REQUIRE_FILE_LOCK(btreePtr
->fileRefNum
, false);
1939 err
= GetNode(btreePtr
, kHeaderNodeNum
, 0, &node
);
1943 ModifyBlockStart(btreePtr
->fileRefNum
, &node
);
1945 offset
= (char *)node
.buffer
+ sizeof(BTNodeDescriptor
) + sizeof(BTHeaderRec
);
1946 bcopy(dataPtr
, offset
, dataSize
);
1948 err
= UpdateNode (btreePtr
, &node
, 0, 0);