2 * Copyright (c) 2002-2005, 2007-2009 Apple Inc. All rights reserved.
4 * @APPLE_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. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
26 Contains: This file contains BTree rebuilding code.
28 Written by: Jerry Cottingham
30 Copyright: © 1986, 1990, 1992-2002 by Apple Computer, Inc., all rights reserved.
34 #define SHOW_ELAPSED_TIMES 0
35 #define DEBUG_REBUILD 1
37 extern void MyIndirectLog(const char *);
39 #if SHOW_ELAPSED_TIMES
43 #include "Scavenger.h"
46 /* internal routine prototypes */
48 /*static*/ OSErr
CreateNewBTree( SGlobPtr theSGlobPtr
, int FileID
);
49 static OSErr
DeleteBTree( SGlobPtr theSGlobPtr
, SFCB
* theFCBPtr
);
50 static OSErr
InitializeBTree( BTreeControlBlock
* theBTreeCBPtr
,
51 UInt32
* theBytesUsedPtr
,
52 UInt32
* theMapNodeCountPtr
);
53 static OSErr
ReleaseExtentsInExtentsBTree( SGlobPtr theSGlobPtr
,
55 static OSErr
ValidateCatalogRecordLength( SGlobPtr theSGlobPtr
,
56 CatalogRecord
* theRecPtr
,
58 static OSErr
ValidateAttributeRecordLength (SGlobPtr s
, HFSPlusAttrRecord
* theRecPtr
, UInt32 theRecSize
);
59 static OSErr
ValidateExtentRecordLength (SGlobPtr s
, ExtentRecord
* theRecPtr
, UInt32 theRecSize
);
60 static OSErr
WriteMapNodes( BTreeControlBlock
* theBTreeCBPtr
,
61 UInt32 theFirstMapNode
,
62 UInt32 theNodeCount
);
65 static void PrintBTHeaderRec( BTHeaderRec
* thePtr
);
66 static void PrintNodeDescriptor( NodeDescPtr thePtr
);
67 static void PrintBTreeKey( KeyPtr thePtr
, BTreeControlBlock
* theBTreeCBPtr
);
68 static void PrintBTreeData(void *data
, UInt32 length
);
69 static void PrintIndexNodeRec( UInt32 theNodeNum
);
70 static void PrintLeafNodeRec( HFSPlusCatalogFolder
* thePtr
);
73 void SETOFFSET ( void *buffer
, UInt16 btNodeSize
, SInt16 recOffset
, SInt16 vecOffset
);
74 #define SETOFFSET( buf,ndsiz,offset,rec ) \
75 ( *(SInt16 *)((UInt8 *)(buf) + (ndsiz) + (-2 * (rec))) = (offset) )
78 //_________________________________________________________________________________
80 // Routine: RebuildBTree
82 // Purpose: Attempt to rebuild a B-Tree file using an existing B-Tree
83 // file. When successful a new BT-ree file will exist and
84 // the old one will be deleted. The MDB an alternate MDB
85 // will be updated to point to the new file.
87 // The tree is rebuilt by walking through every record. We use
88 // BTScanNextRecord(), which iterates sequentially through the
89 // nodes in the tree (starting at the first node), and extracts
90 // each record from each leaf node. It does not use the node
91 // forward or backward links; this allows it to rebuild the tree
92 // when the index nodes are non-reliable, or the leaf node links
95 // The rebuild will be aborted (leaving the existing btree
96 // as it was found) if there are errors retreiving the nodes or
97 // records, or if there are errors inserting the records into
101 // SGlobPtr->calculatedCatalogBTCB or SGlobPtr->calculatedAttributesBTCB
102 // need this as a model and in order to extract leaf records.
103 // SGlobPtr->calculatedCatalogFCB or SGlobPtr->calculatedAttributesFCB
104 // need this as a model and in order to extract leaf records.
105 // SGlobPtr->calculatedRepairFCB
106 // pointer to our repair FCB.
107 // SGlobPtr->calculatedRepairBTCB
108 // pointer to our repair BTreeControlBlock.
111 // SGlobPtr->calculatedVCB
112 // this will get mostly filled in here. On input it is not fully
114 // SGlobPtr->calculatedCatalogFCB or SGlobPtr->calculatedAttributesFCB
115 // tis will refer to the new catalog file.
118 // various error codes when problem occur or noErr if rebuild completed
121 // have an option where we get back what we can.
124 // - requires input BTCB and FCBs to be valid!
125 //_________________________________________________________________________________
127 OSErr
RebuildBTree( SGlobPtr theSGlobPtr
, int FileID
)
129 BlockDescriptor myBlockDescriptor
;
130 BTreeKeyPtr myCurrentKeyPtr
;
131 void * myCurrentDataPtr
;
132 SFCB
* myFCBPtr
= NULL
;
133 SFCB
* oldFCBPtr
= NULL
;
139 UInt32 numRecords
= 0;
141 #if SHOW_ELAPSED_TIMES
142 struct timeval myStartTime
;
143 struct timeval myEndTime
;
144 struct timeval myElapsedTime
;
145 struct timezone zone
;
148 theSGlobPtr
->TarID
= FileID
;
149 theSGlobPtr
->TarBlock
= 0;
150 myBlockDescriptor
.buffer
= NULL
;
151 myVCBPtr
= theSGlobPtr
->calculatedVCB
;
152 if (kHFSCatalogFileID
== FileID
) {
153 oldFCBPtr
= theSGlobPtr
->calculatedCatalogFCB
;
154 } else if (kHFSAttributesFileID
== FileID
) {
155 oldFCBPtr
= theSGlobPtr
->calculatedAttributesFCB
;
158 * If we don't have an attributes btree, then we should just
159 * quit now -- nothing to rebuild.
161 if (oldFCBPtr
->fcbLogicalSize
== 0) {
163 plog("Requested attributes btree rebuild, but attributes file size is 0\n");
165 goto ExitThisRoutine
;
167 } else if (kHFSExtentsFileID
== FileID
) {
168 oldFCBPtr
= theSGlobPtr
->calculatedExtentsFCB
;
173 myErr
= BTScanInitialize( oldFCBPtr
, &theSGlobPtr
->scanState
);
174 if ( noErr
!= myErr
)
175 goto ExitThisRoutine
;
177 // some VCB fields that we need may not have been calculated so we get it from the MDB.
178 // this can happen because the fsck_hfs code path to fully set up the VCB may have been
179 // aborted if an error was found that would trigger a rebuild. For example,
180 // if a leaf record was found to have a keys out of order then the verify phase of the
181 // B-Tree check would be aborted and we would come directly (if allowable) to here.
182 isHFSPlus
= ( myVCBPtr
->vcbSignature
== kHFSPlusSigWord
);
186 goto ExitThisRoutine
;
189 myErr
= GetVolumeObjectVHBorMDB( &myBlockDescriptor
);
190 if ( noErr
!= myErr
)
191 goto ExitThisRoutine
;
195 HFSPlusVolumeHeader
* myVHBPtr
;
197 myVHBPtr
= (HFSPlusVolumeHeader
*) myBlockDescriptor
.buffer
;
198 myVCBPtr
->vcbFreeBlocks
= myVHBPtr
->freeBlocks
;
199 myVCBPtr
->vcbFileCount
= myVHBPtr
->fileCount
;
200 myVCBPtr
->vcbFolderCount
= myVHBPtr
->folderCount
;
201 myVCBPtr
->vcbEncodingsBitmap
= myVHBPtr
->encodingsBitmap
;
202 myVCBPtr
->vcbRsrcClumpSize
= myVHBPtr
->rsrcClumpSize
;
203 myVCBPtr
->vcbDataClumpSize
= myVHBPtr
->dataClumpSize
;
205 // check out creation and last mod dates
206 myVCBPtr
->vcbCreateDate
= myVHBPtr
->createDate
;
207 myVCBPtr
->vcbModifyDate
= myVHBPtr
->modifyDate
;
208 myVCBPtr
->vcbCheckedDate
= myVHBPtr
->checkedDate
;
209 myVCBPtr
->vcbBackupDate
= myVHBPtr
->backupDate
;
210 myVCBPtr
->vcbCatalogFile
->fcbClumpSize
= myVHBPtr
->catalogFile
.clumpSize
;
211 if (myVCBPtr
->vcbAttributesFile
!= NULL
) {
212 myVCBPtr
->vcbAttributesFile
->fcbClumpSize
= myVHBPtr
->attributesFile
.clumpSize
;
214 myVCBPtr
->vcbExtentsFile
->fcbClumpSize
= myVHBPtr
->extentsFile
.clumpSize
;
216 // 3882639: Removed check for volume attributes in HFS Plus
217 myVCBPtr
->vcbAttributes
= myVHBPtr
->attributes
;
219 CopyMemory( myVHBPtr
->finderInfo
, myVCBPtr
->vcbFinderInfo
, sizeof(myVCBPtr
->vcbFinderInfo
) );
223 HFSMasterDirectoryBlock
* myMDBPtr
;
224 myMDBPtr
= (HFSMasterDirectoryBlock
*) myBlockDescriptor
.buffer
;
225 myVCBPtr
->vcbFreeBlocks
= myMDBPtr
->drFreeBks
;
226 myVCBPtr
->vcbFileCount
= myMDBPtr
->drFilCnt
;
227 myVCBPtr
->vcbFolderCount
= myMDBPtr
->drDirCnt
;
228 myVCBPtr
->vcbDataClumpSize
= myMDBPtr
->drClpSiz
;
229 myVCBPtr
->vcbCatalogFile
->fcbClumpSize
= myMDBPtr
->drCTClpSiz
;
230 myVCBPtr
->vcbNmRtDirs
= myMDBPtr
->drNmRtDirs
;
232 // check out creation and last mod dates
233 myVCBPtr
->vcbCreateDate
= myMDBPtr
->drCrDate
;
234 myVCBPtr
->vcbModifyDate
= myMDBPtr
->drLsMod
;
236 // verify volume attribute flags
237 if ( (myMDBPtr
->drAtrb
& VAtrb_Msk
) == 0 )
238 myVCBPtr
->vcbAttributes
= myMDBPtr
->drAtrb
;
240 myVCBPtr
->vcbAttributes
= VAtrb_DFlt
;
241 myVCBPtr
->vcbBackupDate
= myMDBPtr
->drVolBkUp
;
242 myVCBPtr
->vcbVSeqNum
= myMDBPtr
->drVSeqNum
;
243 CopyMemory( myMDBPtr
->drFndrInfo
, myVCBPtr
->vcbFinderInfo
, sizeof(myMDBPtr
->drFndrInfo
) );
245 (void) ReleaseVolumeBlock( myVCBPtr
, &myBlockDescriptor
, kReleaseBlock
);
246 myBlockDescriptor
.buffer
= NULL
;
248 // create the new BTree file
249 if (FileID
== kHFSCatalogFileID
|| FileID
== kHFSAttributesFileID
|| FileID
== kHFSExtentsFileID
) {
250 myErr
= CreateNewBTree( theSGlobPtr
, FileID
);
254 if ( noErr
!= myErr
) {
256 plog("CreateNewBTree returned %d\n", myErr
);
258 if (myErr
== dskFulErr
) {
259 fsckPrint(theSGlobPtr
->context
, E_DiskFull
);
261 goto ExitThisRoutine
;
263 myFCBPtr
= theSGlobPtr
->calculatedRepairFCB
;
265 #if SHOW_ELAPSED_TIMES
266 gettimeofday( &myStartTime
, &zone
);
272 HFSPlusExtentDescriptor
*te
= (HFSPlusExtentDescriptor
*)&theSGlobPtr
->calculatedRepairFCB
->fcbExtents32
;
273 printf("Extent records for rebuilt file %u:\n", FileID
);
274 for (i
= 0; i
< kHFSPlusExtentDensity
; i
++) {
275 printf("\t[ %u, %u ]\n", te
[i
].startBlock
, te
[i
].blockCount
);
282 /* scan the btree for leaf records */
283 myErr
= BTScanNextRecord( &theSGlobPtr
->scanState
,
284 (void **) &myCurrentKeyPtr
,
285 (void **) &myCurrentDataPtr
,
287 if ( noErr
!= myErr
)
290 /* do some validation on the record */
291 theSGlobPtr
->TarBlock
= theSGlobPtr
->scanState
.nodeNum
;
292 if (FileID
== kHFSCatalogFileID
) {
293 myErr
= ValidateCatalogRecordLength( theSGlobPtr
, myCurrentDataPtr
, myDataSize
);
294 } else if (FileID
== kHFSAttributesFileID
) {
295 myErr
= ValidateAttributeRecordLength( theSGlobPtr
, myCurrentDataPtr
, myDataSize
);
296 } else if (FileID
== kHFSExtentsFileID
) {
297 myErr
= ValidateExtentRecordLength( theSGlobPtr
, myCurrentDataPtr
, myDataSize
);
299 if ( noErr
!= myErr
)
303 plog( "%s - Record length validation (file %d) failed! \n", __FUNCTION__
, FileID
);
304 plog( "%s - record %d in node %d is not recoverable. \n",
305 __FUNCTION__
, (theSGlobPtr
->scanState
.recordNum
- 1),
306 theSGlobPtr
->scanState
.nodeNum
);
310 break; // this implementation does not handle partial rebuilds (all or none)
313 /* insert this record into the new btree file */
314 myErr
= InsertBTreeRecord( myFCBPtr
, myCurrentKeyPtr
,
315 myCurrentDataPtr
, myDataSize
, &myHint
);
316 if ( noErr
!= myErr
)
320 plog( "%s - InsertBTreeRecord failed with err %d 0x%02X \n",
321 __FUNCTION__
, myErr
, myErr
);
322 plog( "%s - numRecords = %d\n", __FUNCTION__
, numRecords
);
323 plog( "%s - record %d in node %d is not recoverable. \n",
324 __FUNCTION__
, (theSGlobPtr
->scanState
.recordNum
- 1),
325 theSGlobPtr
->scanState
.nodeNum
);
326 PrintBTreeKey( myCurrentKeyPtr
, theSGlobPtr
->calculatedCatalogBTCB
);
327 PrintBTreeData( myCurrentDataPtr
, myDataSize
);
329 if (myErr
== btExists
)
332 if (dskFulErr
== myErr
)
334 fsckPrint(theSGlobPtr
->context
, E_DiskFull
);
337 break; // this implementation does not handle partial rebuilds (all or none)
341 if (debug
&& ((numRecords
% 1000) == 0))
342 plog("btree file %d: %u records\n", FileID
, numRecords
);
346 #if SHOW_ELAPSED_TIMES
347 gettimeofday( &myEndTime
, &zone
);
348 timersub( &myEndTime
, &myStartTime
, &myElapsedTime
);
349 plog( "\n%s - rebuild btree %u %u records elapsed time \n", __FUNCTION__
, FileID
, numRecords
);
350 plog( ">>>>>>>>>>>>> secs %d msecs %d \n\n", myElapsedTime
.tv_sec
, myElapsedTime
.tv_usec
);
353 if ( btNotFound
== myErr
)
355 if ( noErr
!= myErr
)
356 goto ExitThisRoutine
;
358 /* update our header node on disk from our BTreeControlBlock */
359 myErr
= BTFlushPath( myFCBPtr
);
360 if ( noErr
!= myErr
)
361 goto ExitThisRoutine
;
362 myErr
= CacheFlush( myVCBPtr
->vcbBlockCache
);
363 if ( noErr
!= myErr
)
364 goto ExitThisRoutine
;
366 /* switch old file with our new one */
367 if (FileID
== kHFSCatalogFileID
) {
368 theSGlobPtr
->calculatedRepairFCB
= theSGlobPtr
->calculatedCatalogFCB
;
369 theSGlobPtr
->calculatedCatalogFCB
= myFCBPtr
;
370 myVCBPtr
->vcbCatalogFile
= myFCBPtr
;
371 theSGlobPtr
->calculatedCatalogFCB
->fcbFileID
= kHFSCatalogFileID
;
372 theSGlobPtr
->calculatedRepairBTCB
= theSGlobPtr
->calculatedCatalogBTCB
;
373 } else if (FileID
== kHFSAttributesFileID
) {
374 theSGlobPtr
->calculatedRepairFCB
= theSGlobPtr
->calculatedAttributesFCB
;
375 theSGlobPtr
->calculatedAttributesFCB
= myFCBPtr
;
376 myVCBPtr
->vcbAttributesFile
= myFCBPtr
;
377 if (theSGlobPtr
->calculatedAttributesFCB
== NULL
) {
379 plog("Can't rebuilt attributes btree when there is no attributes file\n");
382 goto ExitThisRoutine
;
384 theSGlobPtr
->calculatedAttributesFCB
->fcbFileID
= kHFSAttributesFileID
;
385 theSGlobPtr
->calculatedRepairBTCB
= theSGlobPtr
->calculatedAttributesBTCB
;
386 } else if (FileID
== kHFSExtentsFileID
) {
387 theSGlobPtr
->calculatedRepairFCB
= theSGlobPtr
->calculatedExtentsFCB
;
388 theSGlobPtr
->calculatedExtentsFCB
= myFCBPtr
;
389 myVCBPtr
->vcbExtentsFile
= myFCBPtr
;
390 theSGlobPtr
->calculatedExtentsFCB
->fcbFileID
= kHFSExtentsFileID
;
391 theSGlobPtr
->calculatedRepairBTCB
= theSGlobPtr
->calculatedExtentsBTCB
;
394 // todo - add code to allow new btree file to be allocated in extents.
395 // Note when we do allow this the swap of btree files gets even more
396 // tricky since extent record key contains the file ID. The rebuilt
397 // file has file ID kHFSCatalogFileID/kHFSCatalogFileID when it is created.
399 MarkVCBDirty( myVCBPtr
);
400 myErr
= FlushAlternateVolumeControlBlock( myVCBPtr
, isHFSPlus
);
401 if ( noErr
!= myErr
)
403 // we may be totally screwed if we get here, try to recover
404 if (FileID
== kHFSCatalogFileID
) {
405 theSGlobPtr
->calculatedCatalogFCB
= theSGlobPtr
->calculatedRepairFCB
;
406 theSGlobPtr
->calculatedRepairFCB
= myFCBPtr
;
407 myVCBPtr
->vcbCatalogFile
= theSGlobPtr
->calculatedCatalogFCB
;
408 } else if (FileID
== kHFSAttributesFileID
) {
409 theSGlobPtr
->calculatedAttributesFCB
= theSGlobPtr
->calculatedRepairFCB
;
410 theSGlobPtr
->calculatedRepairFCB
= myFCBPtr
;
411 myVCBPtr
->vcbAttributesFile
= theSGlobPtr
->calculatedAttributesFCB
;
412 } else if (FileID
== kHFSExtentsFileID
) {
413 theSGlobPtr
->calculatedExtentsFCB
= theSGlobPtr
->calculatedRepairFCB
;
414 theSGlobPtr
->calculatedRepairFCB
= myFCBPtr
;
415 myVCBPtr
->vcbExtentsFile
= theSGlobPtr
->calculatedExtentsFCB
;
417 MarkVCBDirty( myVCBPtr
);
418 (void) FlushAlternateVolumeControlBlock( myVCBPtr
, isHFSPlus
);
419 goto ExitThisRoutine
;
422 /* release space occupied by old BTree file */
423 (void) DeleteBTree( theSGlobPtr
, theSGlobPtr
->calculatedRepairFCB
);
424 if (FileID
== kHFSExtentsFileID
)
425 (void)FlushExtentFile(myVCBPtr
);
428 if ( myBlockDescriptor
.buffer
!= NULL
)
429 (void) ReleaseVolumeBlock( myVCBPtr
, &myBlockDescriptor
, kReleaseBlock
);
431 if ( myErr
!= noErr
&& myFCBPtr
!= NULL
)
432 (void) DeleteBTree( theSGlobPtr
, myFCBPtr
);
433 BTScanTerminate( &theSGlobPtr
->scanState
);
439 //_________________________________________________________________________________
441 // Routine: CreateNewBTree
443 // Purpose: Create and Initialize a new B-Tree on the target volume
444 // using the physical size of the old (being rebuilt) file as an initial
447 // NOTES: we force this to be contiguous in order to get this into Jaguar.
448 // Allowing the new file to go into extents makes the swap
449 // of the old and new files complicated. The extent records
450 // are keyed by file ID and the new (rebuilt) btree file starts out as
451 // file Id kHFSCatalogFileID/kHFSCatalogFileID/kHFSCatalogFileID.
452 // If there were extents then we would have to fix up the extent records in the extent B-Tree.
454 // todo: Don't force new file to be contiguous
457 // SGlobPtr global state set up by fsck_hfs. We depend upon the
458 // manufactured and repair FCBs.
461 // calculatedRepairBTCB fill in the BTreeControlBlock for new B-Tree file.
462 // calculatedRepairFCB fill in the SFCB for the new B-Tree file
465 // various error codes when problems occur or noErr if all is well
467 //_________________________________________________________________________________
469 /*static*/ OSErr
CreateNewBTree( SGlobPtr theSGlobPtr
, int FileID
)
472 BTreeControlBlock
* myBTreeCBPtr
, * oldBCBPtr
;
474 SFCB
* myFCBPtr
, * oldFCBPtr
;
475 UInt32 myBytesUsed
= 0;
476 UInt32 myMapNodeCount
;
479 BTHeaderRec myHeaderRec
;
481 myBTreeCBPtr
= theSGlobPtr
->calculatedRepairBTCB
;
482 myFCBPtr
= theSGlobPtr
->calculatedRepairFCB
;
483 ClearMemory( (Ptr
) myFCBPtr
, sizeof( *myFCBPtr
) );
484 ClearMemory( (Ptr
) myBTreeCBPtr
, sizeof( *myBTreeCBPtr
) );
486 if (FileID
== kHFSCatalogFileID
) {
487 oldFCBPtr
= theSGlobPtr
->calculatedCatalogFCB
;
488 oldBCBPtr
= theSGlobPtr
->calculatedCatalogBTCB
;
489 } else if (FileID
== kHFSAttributesFileID
) {
490 oldFCBPtr
= theSGlobPtr
->calculatedAttributesFCB
;
491 oldBCBPtr
= theSGlobPtr
->calculatedAttributesBTCB
;
492 } else if (FileID
== kHFSExtentsFileID
) {
493 oldFCBPtr
= theSGlobPtr
->calculatedExtentsFCB
;
494 oldBCBPtr
= theSGlobPtr
->calculatedExtentsBTCB
;
499 myVCBPtr
= oldFCBPtr
->fcbVolume
;
500 if (FileID
== kHFSCatalogFileID
)
501 myFCBPtr
->fcbFileID
= kHFSCatalogFileID
;
502 else if (FileID
== kHFSAttributesFileID
)
503 myFCBPtr
->fcbFileID
= kHFSAttributesFileID
;
504 else if (FileID
== kHFSExtentsFileID
)
505 myFCBPtr
->fcbFileID
= kHFSExtentsFileID
;
507 myFCBPtr
->fcbVolume
= myVCBPtr
;
508 myFCBPtr
->fcbBtree
= myBTreeCBPtr
;
509 myFCBPtr
->fcbBlockSize
= oldBCBPtr
->nodeSize
;
511 // Create new BTree Control Block
512 myBTreeCBPtr
->fcbPtr
= myFCBPtr
;
513 myBTreeCBPtr
->btreeType
= kHFSBTreeType
;
514 myBTreeCBPtr
->keyCompareType
= oldBCBPtr
->keyCompareType
;
515 myBTreeCBPtr
->keyCompareProc
= oldBCBPtr
->keyCompareProc
;
516 myBTreeCBPtr
->nodeSize
= oldBCBPtr
->nodeSize
;
517 myBTreeCBPtr
->maxKeyLength
= oldBCBPtr
->maxKeyLength
;
518 if (myVCBPtr
->vcbSignature
== kHFSPlusSigWord
) {
519 if (FileID
== kHFSExtentsFileID
)
520 myBTreeCBPtr
->attributes
= kBTBigKeysMask
;
522 myBTreeCBPtr
->attributes
= ( kBTBigKeysMask
+ kBTVariableIndexKeysMask
);
525 myBTreeCBPtr
->getBlockProc
= GetFileBlock
;
526 myBTreeCBPtr
->releaseBlockProc
= ReleaseFileBlock
;
527 myBTreeCBPtr
->setEndOfForkProc
= SetEndOfForkProc
;
529 myNewEOF
= oldFCBPtr
->fcbPhysicalSize
;
531 myNumBlocks
= myNewEOF
/ myVCBPtr
->vcbBlockSize
;
532 myErr
= BlockFindAll( myBTreeCBPtr
->fcbPtr
, myNumBlocks
);
533 ReturnIfError( myErr
);
534 myBTreeCBPtr
->fcbPtr
->fcbPhysicalSize
= myNewEOF
;
535 myErr
= ZeroFileBlocks( myVCBPtr
, myBTreeCBPtr
->fcbPtr
, 0, myNewEOF
>> kSectorShift
);
536 ReturnIfError( myErr
);
538 /* now set real values in our BTree Control Block */
539 myFCBPtr
->fcbLogicalSize
= myFCBPtr
->fcbPhysicalSize
; // new B-tree looks at fcbLogicalSize
540 if (FileID
== kHFSCatalogFileID
)
541 myFCBPtr
->fcbClumpSize
= myVCBPtr
->vcbCatalogFile
->fcbClumpSize
;
542 else if (FileID
== kHFSAttributesFileID
)
543 myFCBPtr
->fcbClumpSize
= myVCBPtr
->vcbAttributesFile
->fcbClumpSize
;
544 else if (FileID
== kHFSExtentsFileID
)
545 myFCBPtr
->fcbClumpSize
= myVCBPtr
->vcbExtentsFile
->fcbClumpSize
;
547 myBTreeCBPtr
->totalNodes
= ( myFCBPtr
->fcbPhysicalSize
/ myBTreeCBPtr
->nodeSize
);
548 myBTreeCBPtr
->freeNodes
= myBTreeCBPtr
->totalNodes
;
550 // Initialize our new BTree (write out header node and an empty leaf node)
551 myErr
= InitializeBTree( myBTreeCBPtr
, &myBytesUsed
, &myMapNodeCount
);
552 ReturnIfError( myErr
);
554 // Update our BTreeControlBlock from BTHeaderRec we just wrote out
555 myErr
= GetBTreeHeader( theSGlobPtr
, myFCBPtr
, &myHeaderRec
);
556 ReturnIfError( myErr
);
558 myBTreeCBPtr
->treeDepth
= myHeaderRec
.treeDepth
;
559 myBTreeCBPtr
->rootNode
= myHeaderRec
.rootNode
;
560 myBTreeCBPtr
->leafRecords
= myHeaderRec
.leafRecords
;
561 myBTreeCBPtr
->firstLeafNode
= myHeaderRec
.firstLeafNode
;
562 myBTreeCBPtr
->lastLeafNode
= myHeaderRec
.lastLeafNode
;
563 myBTreeCBPtr
->totalNodes
= myHeaderRec
.totalNodes
;
564 myBTreeCBPtr
->freeNodes
= myHeaderRec
.freeNodes
;
565 myBTreeCBPtr
->maxKeyLength
= myHeaderRec
.maxKeyLength
;
567 if ( myMapNodeCount
> 0 )
569 myErr
= WriteMapNodes( myBTreeCBPtr
, (myBytesUsed
/ myBTreeCBPtr
->nodeSize
), myMapNodeCount
);
570 ReturnIfError( myErr
);
575 } /* CreateNewBTree */
581 * This routine manufactures and writes out a B-Tree header
582 * node and an empty leaf node.
584 * Note: Since large volumes can have bigger b-trees they
585 * might need to have map nodes setup.
587 * this routine originally came from newfs_hfs.tproj ( see
588 * WriteCatalogFile in file makehfs.c) and was modified for fsck_hfs.
590 static OSErr
InitializeBTree( BTreeControlBlock
* theBTreeCBPtr
,
591 UInt32
* theBytesUsedPtr
,
592 UInt32
* theMapNodeCountPtr
)
595 BlockDescriptor myNode
;
596 Boolean isHFSPlus
= false;
598 BTNodeDescriptor
* myNodeDescPtr
;
599 BTHeaderRec
* myHeaderRecPtr
;
602 UInt32 myNodeBitsInHeader
;
606 myVCBPtr
= theBTreeCBPtr
->fcbPtr
->fcbVolume
;
607 isHFSPlus
= ( myVCBPtr
->vcbSignature
== kHFSPlusSigWord
) ;
608 *theMapNodeCountPtr
= 0;
610 myErr
= GetNewNode( theBTreeCBPtr
, kHeaderNodeNum
, &myNode
);
611 ReturnIfError( myErr
);
613 myBufferPtr
= (UInt8
*) myNode
.buffer
;
615 /* FILL IN THE NODE DESCRIPTOR: */
616 myNodeDescPtr
= (BTNodeDescriptor
*) myBufferPtr
;
617 myNodeDescPtr
->kind
= kBTHeaderNode
;
618 myNodeDescPtr
->numRecords
= 3;
619 myOffset
= sizeof( BTNodeDescriptor
);
621 SETOFFSET( myBufferPtr
, theBTreeCBPtr
->nodeSize
, myOffset
, 1 );
623 /* FILL IN THE HEADER RECORD: */
624 myHeaderRecPtr
= (BTHeaderRec
*)((UInt8
*)myBufferPtr
+ myOffset
);
625 myHeaderRecPtr
->treeDepth
= 0;
626 myHeaderRecPtr
->rootNode
= 0;
627 myHeaderRecPtr
->firstLeafNode
= 0;
628 myHeaderRecPtr
->lastLeafNode
= 0;
629 myHeaderRecPtr
->nodeSize
= theBTreeCBPtr
->nodeSize
;
630 myHeaderRecPtr
->totalNodes
= theBTreeCBPtr
->totalNodes
;
631 myHeaderRecPtr
->freeNodes
= myHeaderRecPtr
->totalNodes
- 1; /* header node */
632 myHeaderRecPtr
->clumpSize
= theBTreeCBPtr
->fcbPtr
->fcbClumpSize
;
634 myHeaderRecPtr
->attributes
= theBTreeCBPtr
->attributes
;
635 myHeaderRecPtr
->maxKeyLength
= theBTreeCBPtr
->maxKeyLength
;
636 myHeaderRecPtr
->keyCompareType
= theBTreeCBPtr
->keyCompareType
;
638 myOffset
+= sizeof( BTHeaderRec
);
639 SETOFFSET( myBufferPtr
, theBTreeCBPtr
->nodeSize
, myOffset
, 2 );
641 myOffset
+= kBTreeHeaderUserBytes
;
642 SETOFFSET( myBufferPtr
, theBTreeCBPtr
->nodeSize
, myOffset
, 3 );
644 /* FIGURE OUT HOW MANY MAP NODES (IF ANY): */
645 myNodeBitsInHeader
= 8 * (theBTreeCBPtr
->nodeSize
646 - sizeof(BTNodeDescriptor
)
647 - sizeof(BTHeaderRec
)
648 - kBTreeHeaderUserBytes
649 - (4 * sizeof(SInt16
)) );
651 if ( myHeaderRecPtr
->totalNodes
> myNodeBitsInHeader
)
653 UInt32 nodeBitsInMapNode
;
655 myNodeDescPtr
->fLink
= myHeaderRecPtr
->lastLeafNode
+ 1;
656 nodeBitsInMapNode
= 8 * (theBTreeCBPtr
->nodeSize
657 - sizeof(BTNodeDescriptor
)
658 - (2 * sizeof(SInt16
))
660 *theMapNodeCountPtr
= (myHeaderRecPtr
->totalNodes
- myNodeBitsInHeader
+
661 (nodeBitsInMapNode
- 1)) / nodeBitsInMapNode
;
662 myHeaderRecPtr
->freeNodes
= myHeaderRecPtr
->freeNodes
- *theMapNodeCountPtr
;
666 * FILL IN THE MAP RECORD, MARKING NODES THAT ARE IN USE.
667 * Note - worst case (32MB alloc blk) will have only 18 nodes in use.
669 myBitMapPtr
= ((UInt8
*)myBufferPtr
+ myOffset
);
670 temp
= myHeaderRecPtr
->totalNodes
- myHeaderRecPtr
->freeNodes
;
672 /* Working a byte at a time is endian safe */
679 *myBitMapPtr
= ~(0xFF >> temp
);
680 myOffset
+= myNodeBitsInHeader
/ 8;
682 SETOFFSET( myBufferPtr
, theBTreeCBPtr
->nodeSize
, myOffset
, 4 );
685 ( myHeaderRecPtr
->totalNodes
- myHeaderRecPtr
->freeNodes
- *theMapNodeCountPtr
)
686 * theBTreeCBPtr
->nodeSize
;
688 /* write header node */
689 myErr
= UpdateNode( theBTreeCBPtr
, &myNode
);
690 M_ExitOnError( myErr
);
695 (void) ReleaseNode( theBTreeCBPtr
, &myNode
);
699 } /* InitializeBTree */
705 * This routine manufactures and writes out a B-Tree map
706 * node (or nodes if there are more than one).
708 * this routine originally came from newfs_hfs.tproj ( see
709 * WriteMapNodes in file makehfs.c) and was modified for fsck_hfs.
712 static OSErr
WriteMapNodes( BTreeControlBlock
* theBTreeCBPtr
,
713 UInt32 theFirstMapNode
,
714 UInt32 theNodeCount
)
718 UInt32 mapRecordBytes
;
719 BTNodeDescriptor
* myNodeDescPtr
;
720 BlockDescriptor myNode
;
722 myNode
.buffer
= NULL
;
725 * Note - worst case (32MB alloc blk) will have
726 * only 18 map nodes. So don't bother optimizing
727 * this section to do multiblock writes!
729 for ( i
= 0; i
< theNodeCount
; i
++ )
731 myErr
= GetNewNode( theBTreeCBPtr
, theFirstMapNode
, &myNode
);
732 M_ExitOnError( myErr
);
734 myNodeDescPtr
= (BTNodeDescriptor
*) myNode
.buffer
;
735 myNodeDescPtr
->kind
= kBTMapNode
;
736 myNodeDescPtr
->numRecords
= 1;
738 /* note: must be long word aligned (hence the extra -2) */
739 mapRecordBytes
= theBTreeCBPtr
->nodeSize
- sizeof(BTNodeDescriptor
) - 2 * sizeof(SInt16
) - 2;
741 SETOFFSET( myNodeDescPtr
, theBTreeCBPtr
->nodeSize
, sizeof(BTNodeDescriptor
), 1 );
742 SETOFFSET( myNodeDescPtr
, theBTreeCBPtr
->nodeSize
, sizeof(BTNodeDescriptor
) + mapRecordBytes
, 2) ;
744 if ( (i
+ 1) < theNodeCount
)
745 myNodeDescPtr
->fLink
= ++theFirstMapNode
; /* point to next map node */
747 myNodeDescPtr
->fLink
= 0; /* this is the last map node */
749 myErr
= UpdateNode( theBTreeCBPtr
, &myNode
);
750 M_ExitOnError( myErr
);
756 (void) ReleaseNode( theBTreeCBPtr
, &myNode
);
760 } /* WriteMapNodes */
766 * This routine will realease all space associated with the BTree
767 * file represented by the FCB passed in.
774 kResourceForkType
= 0xFF
777 static OSErr
DeleteBTree( SGlobPtr theSGlobPtr
, SFCB
* theFCBPtr
)
783 Boolean checkExtentsBTree
= true;
785 myVCBPtr
= theFCBPtr
->fcbVolume
;
786 isHFSPlus
= ( myVCBPtr
->vcbSignature
== kHFSPlusSigWord
) ;
790 for ( i
= 0; i
< kHFSPlusExtentDensity
; ++i
)
792 if ( theFCBPtr
->fcbExtents32
[ i
].blockCount
== 0 )
794 checkExtentsBTree
= false;
797 (void) BlockDeallocate( myVCBPtr
,
798 theFCBPtr
->fcbExtents32
[ i
].startBlock
,
799 theFCBPtr
->fcbExtents32
[ i
].blockCount
);
800 theFCBPtr
->fcbExtents32
[ i
].startBlock
= 0;
801 theFCBPtr
->fcbExtents32
[ i
].blockCount
= 0;
806 for ( i
= 0; i
< kHFSExtentDensity
; ++i
)
808 if ( theFCBPtr
->fcbExtents16
[ i
].blockCount
== 0 )
810 checkExtentsBTree
= false;
813 (void) BlockDeallocate( myVCBPtr
,
814 theFCBPtr
->fcbExtents16
[ i
].startBlock
,
815 theFCBPtr
->fcbExtents16
[ i
].blockCount
);
816 theFCBPtr
->fcbExtents16
[ i
].startBlock
= 0;
817 theFCBPtr
->fcbExtents16
[ i
].blockCount
= 0;
821 if ( checkExtentsBTree
)
823 (void) ReleaseExtentsInExtentsBTree( theSGlobPtr
, theFCBPtr
);
824 (void) FlushExtentFile( myVCBPtr
);
827 (void) MarkVCBDirty( myVCBPtr
);
828 (void) FlushAlternateVolumeControlBlock( myVCBPtr
, isHFSPlus
);
837 * ReleaseExtentsInExtentsBTree
839 * This routine will locate extents in the extent BTree then release the space
840 * associated with the extents. It will also delete the BTree record for the
845 static OSErr
ReleaseExtentsInExtentsBTree( SGlobPtr theSGlobPtr
,
848 BTreeIterator myIterator
;
849 ExtentRecord myExtentRecord
;
850 FSBufferDescriptor myBTRec
;
851 ExtentKey
* myKeyPtr
;
858 myVCBPtr
= theFCBPtr
->fcbVolume
;
859 isHFSPlus
= ( myVCBPtr
->vcbSignature
== kHFSPlusSigWord
);
861 // position just before the first extent record for the given File ID. We
862 // pass in the file ID and a start block of 0 which will put us in a
863 // position for BTIterateRecord (with kBTreeNextRecord) to get the first
865 ClearMemory( &myIterator
, sizeof(myIterator
) );
866 myBTRec
.bufferAddress
= &myExtentRecord
;
867 myBTRec
.itemCount
= 1;
868 myBTRec
.itemSize
= sizeof(myExtentRecord
);
869 myKeyPtr
= (ExtentKey
*) &myIterator
.key
;
871 BuildExtentKey( isHFSPlus
, kDataForkType
, theFCBPtr
->fcbFileID
,
872 0, (void *) myKeyPtr
);
874 // it is now a simple process of getting the next extent record and
875 // cleaning up the allocated space for each one until we hit a
876 // different file ID.
879 myErr
= BTIterateRecord( theSGlobPtr
->calculatedExtentsFCB
,
880 kBTreeNextRecord
, &myIterator
,
881 &myBTRec
, &myRecLen
);
882 if ( noErr
!= myErr
)
888 /* deallocate space for the extents we found */
891 // we're done if this is a different File ID
892 if ( myKeyPtr
->hfsPlus
.fileID
!= theFCBPtr
->fcbFileID
||
893 myKeyPtr
->hfsPlus
.forkType
!= kDataForkType
)
896 for ( i
= 0; i
< kHFSPlusExtentDensity
; ++i
)
898 if ( myExtentRecord
.hfsPlus
[ i
].blockCount
== 0 )
901 (void) BlockDeallocate( myVCBPtr
,
902 myExtentRecord
.hfsPlus
[ i
].startBlock
,
903 myExtentRecord
.hfsPlus
[ i
].blockCount
);
908 // we're done if this is a different File ID
909 if ( myKeyPtr
->hfs
.fileID
!= theFCBPtr
->fcbFileID
||
910 myKeyPtr
->hfs
.forkType
!= kDataForkType
)
913 for ( i
= 0; i
< kHFSExtentDensity
; ++i
)
915 if ( myExtentRecord
.hfs
[ i
].blockCount
== 0 )
918 (void) BlockDeallocate( myVCBPtr
,
919 myExtentRecord
.hfs
[ i
].startBlock
,
920 myExtentRecord
.hfs
[ i
].blockCount
);
924 /* get rid of this extent BTree record */
925 myErr
= DeleteBTreeRecord( theSGlobPtr
->calculatedExtentsFCB
, myKeyPtr
);
930 } /* ReleaseExtentsInExtentsBTree */
934 * ValidateExtentRecordLength
935 * This routine will ensure that an extent record is the right size.
936 * This should always be the size of HFSPlusExtentRecord.
938 static OSErr
ValidateExtentRecordLength (SGlobPtr s
, ExtentRecord
* theRecPtr
, UInt32 theRecSize
)
940 Boolean isHFSPlus
= ( s
->calculatedVCB
->vcbSignature
== kHFSPlusSigWord
);
942 if (theRecSize
!= sizeof(HFSPlusExtentRecord
))
945 if (theRecSize
!= sizeof(HFSExtentRecord
))
953 * ValidateAttributeRecordLength
955 * This routine will make sure that the given HFS+ attributes file record
956 * is of the correct length.
959 static OSErr
ValidateAttributeRecordLength (SGlobPtr s
, HFSPlusAttrRecord
* theRecPtr
, UInt32 theRecSize
)
961 OSErr retval
= noErr
;
962 static UInt32 maxInlineSize
;
964 if (maxInlineSize
== 0) {
965 /* The maximum size of an inline attribute record is nodesize / 2 minus a bit */
966 /* These calculations taken from hfs_xattr.c:getmaxinlineattrsize */
967 maxInlineSize
= s
->calculatedAttributesBTCB
->nodeSize
;
968 maxInlineSize
-= sizeof(BTNodeDescriptor
); // Minus node descriptor
969 maxInlineSize
-= 3 * sizeof(u_int16_t
); // Minus 3 index slots
970 maxInlineSize
/= 2; // 2 key/rec pairs minimum
971 maxInlineSize
-= sizeof(HFSPlusAttrKey
); // Minus maximum key size
972 maxInlineSize
&= 0xFFFFFFFE; // Multiple of two
974 switch (theRecPtr
->recordType
) {
975 case kHFSPlusAttrInlineData
:
976 if (theRecSize
> maxInlineSize
) {
978 plog("Inline Attribute size %u is larger than maxsize %u\n", theRecSize
, maxInlineSize
);
982 case kHFSPlusAttrForkData
:
983 if (theRecSize
!= sizeof(HFSPlusAttrForkData
)) {
985 plog("Fork Data attribute size %u is larger then HFSPlusAttrForkData size %u\n", theRecSize
, sizeof(HFSPlusAttrForkData
));
989 case kHFSPlusAttrExtents
:
990 if (theRecSize
!= sizeof(HFSPlusAttrExtents
)) {
992 plog("Extents Data attribute size %u is larger than HFSPlusAttrExtents size %u\n", theRecSize
, sizeof(HFSPlusAttrExtents
));
997 // Right now, we don't support any other kind
999 plog("Unknown attribute type %u\n", theRecPtr
->recordType
);
1007 * ValidateCatalogRecordLength
1009 * This routine will make sure the given HFS (plus and standard) catalog record
1010 * is of the correct length.
1014 static OSErr
ValidateCatalogRecordLength( SGlobPtr theSGlobPtr
,
1015 CatalogRecord
* theRecPtr
,
1019 Boolean isHFSPlus
= false;
1021 myVCBPtr
= theSGlobPtr
->calculatedVCB
;
1022 isHFSPlus
= ( myVCBPtr
->vcbSignature
== kHFSPlusSigWord
);
1026 switch ( theRecPtr
->recordType
)
1028 case kHFSPlusFolderRecord
:
1029 if ( theRecSize
!= sizeof( HFSPlusCatalogFolder
) )
1035 case kHFSPlusFileRecord
:
1036 if ( theRecSize
!= sizeof(HFSPlusCatalogFile
) )
1042 case kHFSPlusFolderThreadRecord
:
1045 case kHFSPlusFileThreadRecord
:
1046 if ( theRecSize
> sizeof(HFSPlusCatalogThread
) ||
1047 theRecSize
< sizeof(HFSPlusCatalogThread
) - sizeof(HFSUniStr255
) + sizeof(UniChar
) )
1059 switch ( theRecPtr
->recordType
)
1061 case kHFSFolderRecord
:
1062 if ( theRecSize
!= sizeof(HFSCatalogFolder
) )
1066 case kHFSFileRecord
:
1067 if ( theRecSize
!= sizeof(HFSCatalogFile
) )
1071 case kHFSFolderThreadRecord
:
1073 case kHFSFileThreadRecord
:
1074 if ( theRecSize
!= sizeof(HFSCatalogThread
))
1085 } /* ValidateCatalogRecordLength */
1089 static void PrintNodeDescriptor( NodeDescPtr thePtr
)
1091 plog( "\n xxxxxxxx BTNodeDescriptor xxxxxxxx \n" );
1092 plog( " fLink %d \n", thePtr
->fLink
);
1093 plog( " bLink %d \n", thePtr
->bLink
);
1094 plog( " kind %d ", thePtr
->kind
);
1095 if ( thePtr
->kind
== kBTLeafNode
)
1096 plog( "%s \n", "kBTLeafNode" );
1097 else if ( thePtr
->kind
== kBTIndexNode
)
1098 plog( "%s \n", "kBTIndexNode" );
1099 else if ( thePtr
->kind
== kBTHeaderNode
)
1100 plog( "%s \n", "kBTHeaderNode" );
1101 else if ( thePtr
->kind
== kBTMapNode
)
1102 plog( "%s \n", "kBTMapNode" );
1104 plog( "do not know?? \n" );
1105 plog( " height %d \n", thePtr
->height
);
1106 plog( " numRecords %d \n", thePtr
->numRecords
);
1108 } /* PrintNodeDescriptor */
1111 static void PrintBTHeaderRec( BTHeaderRec
* thePtr
)
1113 plog( "\n xxxxxxxx BTHeaderRec xxxxxxxx \n" );
1114 plog( " treeDepth %d \n", thePtr
->treeDepth
);
1115 plog( " rootNode %d \n", thePtr
->rootNode
);
1116 plog( " leafRecords %d \n", thePtr
->leafRecords
);
1117 plog( " firstLeafNode %d \n", thePtr
->firstLeafNode
);
1118 plog( " lastLeafNode %d \n", thePtr
->lastLeafNode
);
1119 plog( " nodeSize %d \n", thePtr
->nodeSize
);
1120 plog( " maxKeyLength %d \n", thePtr
->maxKeyLength
);
1121 plog( " totalNodes %d \n", thePtr
->totalNodes
);
1122 plog( " freeNodes %d \n", thePtr
->freeNodes
);
1123 plog( " clumpSize %d \n", thePtr
->clumpSize
);
1124 plog( " btreeType 0x%02X \n", thePtr
->btreeType
);
1125 plog( " attributes 0x%02X \n", thePtr
->attributes
);
1127 } /* PrintBTHeaderRec */
1130 static void PrintBTreeKey( KeyPtr thePtr
, BTreeControlBlock
* theBTreeCBPtr
)
1133 UInt8
* myPtr
= (UInt8
*)thePtr
;
1139 myKeyLength
= CalcKeySize( theBTreeCBPtr
, thePtr
) ;
1140 plog( "\n xxxxxxxx BTreeKey (length %d) xxxxxxxx \n", myKeyLength
);
1141 for ( i
= 0; i
< myKeyLength
; i
++ )
1143 byte
= *(myPtr
+ i
);
1144 plog( "%02X ", byte
);
1145 if (byte
< 32 || byte
> 126)
1146 ascii
[i
& 0xF] = '.';
1148 ascii
[i
& 0xF] = byte
;
1150 if ((i
& 0xF) == 0xF)
1152 plog(" %s\n", ascii
);
1159 for (j
= i
& 0xF; j
< 16; ++j
)
1162 plog(" %s\n", ascii
);
1164 } /* PrintBTreeKey */
1166 static void PrintBTreeData(void *data
, UInt32 length
)
1169 UInt8
* myPtr
= (UInt8
*)data
;
1175 plog( "\n xxxxxxxx BTreeData (length %d) xxxxxxxx \n", length
);
1176 for ( i
= 0; i
< length
; i
++ )
1178 byte
= *(myPtr
+ i
);
1179 plog( "%02X ", byte
);
1180 if (byte
< 32 || byte
> 126)
1181 ascii
[i
& 0xF] = '.';
1183 ascii
[i
& 0xF] = byte
;
1185 if ((i
& 0xF) == 0xF)
1187 plog(" %s\n", ascii
);
1194 for (j
= i
& 0xF; j
< 16; ++j
)
1197 plog(" %s\n", ascii
);
1201 static void PrintIndexNodeRec( UInt32 theNodeNum
)
1203 plog( "\n xxxxxxxx IndexNodeRec xxxxxxxx \n" );
1204 plog( " node number %d \n", theNodeNum
);
1206 } /* PrintIndexNodeRec */
1208 static void PrintLeafNodeRec( HFSPlusCatalogFolder
* thePtr
)
1210 plog( "\n xxxxxxxx LeafNodeRec xxxxxxxx \n" );
1211 plog( " recordType %d ", thePtr
->recordType
);
1212 if ( thePtr
->recordType
== kHFSPlusFolderRecord
)
1213 plog( "%s \n", "kHFSPlusFolderRecord" );
1214 else if ( thePtr
->recordType
== kHFSPlusFileRecord
)
1215 plog( "%s \n", "kHFSPlusFileRecord" );
1216 else if ( thePtr
->recordType
== kHFSPlusFolderThreadRecord
)
1217 plog( "%s \n", "kHFSPlusFolderThreadRecord" );
1218 else if ( thePtr
->recordType
== kHFSPlusFileThreadRecord
)
1219 plog( "%s \n", "kHFSPlusFileThreadRecord" );
1221 plog( "do not know?? \n" );
1223 } /* PrintLeafNodeRec */
1226 #endif // DEBUG_REBUILD