2 * Copyright (c) 1999-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: xxx put contents here xxx
28 Version: xxx put version here xxx
30 Copyright: � 1997-1999 by Apple Computer, Inc., all rights reserved.
33 #include <sys/ioctl.h>
37 #include "BTreePrivate.h"
39 #include "Scavenger.h"
42 // Prototypes for internal subroutines
43 static int BTKeyChk( SGlobPtr GPtr
, NodeDescPtr nodeP
, BTreeControlBlock
*btcb
);
46 /*------------------------------------------------------------------------------
48 Routine: ChkExtRec (Check Extent Record)
50 Function: Checks out a generic extent record.
52 Input: GPtr - pointer to scavenger global area.
53 extP - pointer to extent data record.
56 Output: lastExtentIndex - In normal case, it is set to the maximum number of
57 extents (3 or 8) for given file system. If the
58 function finds bad extent, it is set to the index
59 of the bad extent entry found.
60 ChkExtRec - function result:
63 ------------------------------------------------------------------------------*/
64 OSErr
ChkExtRec ( SGlobPtr GPtr
, UInt32 fileID
, const void *extents
, unsigned int *lastExtentIndex
)
70 UInt32 extentBlockCount
;
71 UInt32 extentStartBlock
;
73 maxNABlks
= GPtr
->calculatedVCB
->vcbTotalBlocks
;
75 isHFSPlus
= VolumeObjectIsHFSPlus( );
77 /* initialize default output for extent index */
78 *lastExtentIndex
= GPtr
->numExtents
;
80 for ( i
=0 ; i
<GPtr
->numExtents
; i
++ )
84 extentBlockCount
= ((HFSPlusExtentDescriptor
*)extents
)[i
].blockCount
;
85 extentStartBlock
= ((HFSPlusExtentDescriptor
*)extents
)[i
].startBlock
;
89 extentBlockCount
= ((HFSExtentDescriptor
*)extents
)[i
].blockCount
;
90 extentStartBlock
= ((HFSExtentDescriptor
*)extents
)[i
].startBlock
;
93 if ( extentStartBlock
>= maxNABlks
)
96 RcdError( GPtr
, E_ExtEnt
);
97 if (fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
98 plog ("\tCheckExtRecord: id=%u %d:(%u,%u), maxBlocks=%u (startBlock > maxBlocks)\n",
99 fileID
, i
, extentStartBlock
, extentBlockCount
, maxNABlks
);
103 /* Check if end of extent is beyond end of disk */
104 if ( extentBlockCount
>= (maxNABlks
- extentStartBlock
) )
106 *lastExtentIndex
= i
;
107 RcdError( GPtr
, E_ExtEnt
);
108 if (fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
109 plog ("\tCheckExtRecord: id=%u %d:(%u,%u), maxBlocks=%u (blockCount > (maxBlocks - startBlock))\n",
110 fileID
, i
, extentStartBlock
, extentBlockCount
, maxNABlks
);
114 /* This condition is not checked for standard HFS volumes as it is valid
115 * to have extent with allocation block number 0 on standard HFS.
118 ((extentStartBlock
== 0) && (extentBlockCount
!= 0)))
120 *lastExtentIndex
= i
;
121 RcdError( GPtr
, E_ExtEnt
);
122 if (fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
123 plog ("\tCheckExtRecord: id=%u %d:(%u,%u), (startBlock == 0)\n",
124 fileID
, i
, extentStartBlock
, extentBlockCount
);
129 if ((extentStartBlock
!= 0) && (extentBlockCount
== 0))
131 *lastExtentIndex
= i
;
132 RcdError( GPtr
, E_ExtEnt
);
133 if (fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
134 plog ("\tCheckExtRecord: id=%u %d:(%u,%u), (blockCount == 0)\n",
135 fileID
, i
, extentStartBlock
, extentBlockCount
);
141 if ( extentBlockCount
!= 0 )
143 *lastExtentIndex
= i
;
144 RcdError( GPtr
, E_ExtEnt
);
145 if (fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
146 plog ("\tCheckExtRecord: id=%u %d:(%u,%u), (blockCount != 0)\n",
147 fileID
, i
, extentStartBlock
, extentBlockCount
);
152 numABlks
= extentBlockCount
;
159 /*------------------------------------------------------------------------------
161 Routine: BTCheck - (BTree Check)
163 Function Description:
164 Checks out the internal structure of a Btree file. The BTree
165 structure is enunumerated top down starting from the root node.
167 A structure to store the current traversal state of each Btree level
168 is used. The function traverses Btree top to down till it finds
169 a leaf node - where it calls checkLeafRecord function for every
170 leaf record (if specified). The function then starts traversing
171 down from the next index node at previous BTree level. If all
172 index nodes in given BTree level are traversed top to down,
173 it starts traversing the next index node in a previous BTree level -
174 until it hits the root node.
177 The tree is traversed in depth-first traversal - i.e. we recursively
178 traverse the children of a node before visiting its sibling.
179 For the btree shown below, this function will traverse as follows:
186 (node B)-------------
190 (node C)------------- D)----- -------- (node F)
191 | E | I | H | | G | | leaf |
192 ------------- ----- --------
194 -------- -------- -------- --------
195 | leaf | | leaf | | leaf | | leaf |
196 -------- -------- -------- --------
197 (node E) (node I) (node H) (node G)
200 GPtr - pointer to scavenger global area
202 checkLeafRecord - pointer to function that should be
203 called for every leaf record.
206 Output: BTCheck - function result:
209 ------------------------------------------------------------------------------*/
212 BTCheck(SGlobPtr GPtr
, short refNum
, CheckLeafRecordProcPtr checkLeafRecord
)
218 short numRecs
; /* number of records in current node */
219 short index
; /* index to current index record in index node */
221 UInt8 parKey
[ kMaxKeyLength
+ 2 + 2 ]; /* parent key for comparison */
222 Boolean hasParKey
= false;
224 STPR
*tprP
; /* pointer to store BTree traversal state */
229 NodeDescPtr nodeDescP
;
230 UInt16
*statusFlag
= NULL
;
231 UInt32 leafRecords
= 0;
232 BTreeControlBlock
*calculatedBTCB
= GetBTreeControlBlock( refNum
);
237 if ( refNum
== kCalculatedCatalogRefNum
)
238 statusFlag
= &(GPtr
->CBTStat
);
239 else if ( refNum
== kCalculatedExtentRefNum
)
240 statusFlag
= &(GPtr
->EBTStat
);
241 else if ( refNum
== kCalculatedAttributesRefNum
)
242 statusFlag
= &(GPtr
->ABTStat
);
244 /* BTCheck is currently called only with the above three options.
245 * Initialize status flag correctly if we call BTCheck with other
255 * Check out BTree header node
257 result
= GetNode( calculatedBTCB
, kHeaderNodeNum
, &node
);
258 if ( result
!= noErr
)
260 if ( result
== fsBTInvalidNodeErr
) /* hfs_swap_BTNode failed */
262 RcdError( GPtr
, E_BadNode
);
269 nodeDescP
= node
.buffer
;
271 result
= AllocBTN( GPtr
, refNum
, 0 );
272 if (result
) goto exit
; /* node already allocated */
274 /* Check node kind */
275 if ( nodeDescP
->kind
!= kBTHeaderNode
)
277 RcdError( GPtr
, E_BadHdrN
);
281 /* Check total records allowed in header node */
282 if ( nodeDescP
->numRecords
!= Num_HRecs
)
284 RcdError( GPtr
, E_BadHdrN
);
288 /* Check node height */
289 if ( nodeDescP
->height
!= 0 )
291 RcdError( GPtr
, E_NHeight
);
297 * check out BTree Header record
299 header
= (BTHeaderRec
*) ((Byte
*)nodeDescP
+ sizeof(BTNodeDescriptor
));
300 recSize
= GetRecordSize( (BTreeControlBlock
*)calculatedBTCB
, (BTNodeDescriptor
*)nodeDescP
, 0 );
302 /* Check header size */
303 if ( recSize
!= sizeof(BTHeaderRec
) )
305 RcdError( GPtr
, E_LenBTH
);
309 /* Check tree depth */
310 if ( header
->treeDepth
> BTMaxDepth
)
312 RcdError( GPtr
, E_BTDepth
);
313 goto RebuildBTreeExit
;
315 calculatedBTCB
->treeDepth
= header
->treeDepth
;
317 /* Check validity of root node number */
318 if ( header
->rootNode
>= calculatedBTCB
->totalNodes
||
319 (header
->treeDepth
!= 0 && header
->rootNode
== kHeaderNodeNum
) )
322 plog("Header root node %u, calculated total nodes %u, tree depth %u, header node num %u\n",
323 header
->rootNode
, calculatedBTCB
->totalNodes
,
324 header
->treeDepth
, kHeaderNodeNum
);
326 RcdError( GPtr
, E_BTRoot
);
327 goto RebuildBTreeExit
;
329 calculatedBTCB
->rootNode
= header
->rootNode
;
331 /* Check if tree depth or root node are zero */
332 if ( (calculatedBTCB
->treeDepth
== 0) || (calculatedBTCB
->rootNode
== 0) )
334 /* If both are zero, empty BTree */
335 if ( calculatedBTCB
->treeDepth
!= calculatedBTCB
->rootNode
)
337 RcdError( GPtr
, E_BTDepth
);
338 goto RebuildBTreeExit
;
343 * Check the extents for the btree.
344 * HFS+ considers it an error for a node to be split across
345 * extents, on a journaled filesystem.
347 * If debug is set, then it continues examining the tree; otherwise,
348 * it exits with a rebuilt error.
350 if (CheckIfJournaled(GPtr
, true) &&
351 header
->nodeSize
> calculatedBTCB
->fcbPtr
->fcbVolume
->vcbBlockSize
) {
352 /* If it's journaled, it's HFS+ */
353 HFSPlusExtentRecord
*extp
= &calculatedBTCB
->fcbPtr
->fcbExtents32
;
355 int blocksPerNode
= header
->nodeSize
/ calculatedBTCB
->fcbPtr
->fcbVolume
->vcbBlockSize
; // How many blocks in a node
356 UInt32 totalBlocks
= 0;
359 * First, go through the first 8 extents
361 for (i
= 0; i
< kHFSPlusExtentDensity
; i
++) {
362 if (((*extp
)[i
].blockCount
% blocksPerNode
) != 0) {
363 result
= errRebuildBtree
;
364 *statusFlag
|= S_RebuildBTree
;
365 fsckPrint(GPtr
->context
, E_BTreeSplitNode
, calculatedBTCB
->fcbPtr
->fcbFileID
);
369 plog("Improperly split node in file id %u, offset %u (extent #%d), Extent <%u, %u>\n", calculatedBTCB
->fcbPtr
->fcbFileID
, totalBlocks
, i
, (*extp
)[i
].startBlock
, (*extp
)[i
].blockCount
);
372 totalBlocks
+= (*extp
)[i
].blockCount
;
376 * Now, iterate through the extents overflow file if necessary.
377 * Style note: This is in a block so I can have local variables.
378 * It used to have a conditional, but that wasn't needed.
382 BTreeIterator iterator
= { 0 };
383 FSBufferDescriptor btRecord
= { 0 };
384 HFSPlusExtentKey
*key
= (HFSPlusExtentKey
*)&iterator
.key
;
385 HFSPlusExtentRecord extRecord
= { 0 };
387 UInt32 fileID
= calculatedBTCB
->fcbPtr
->fcbFileID
;
388 static const int kDataForkType
= 0;
390 BuildExtentKey( true, kDataForkType
, fileID
, 0, (void*)key
);
391 btRecord
.bufferAddress
= &extRecord
;
392 btRecord
.itemCount
= 1;
393 btRecord
.itemSize
= sizeof(extRecord
);
395 while (noErr
== (err
= BTIterateRecord(GPtr
->calculatedExtentsFCB
, kBTreeNextRecord
, &iterator
, &btRecord
, &recordSize
))) {
396 if (key
->fileID
!= fileID
||
397 key
->forkType
!= kDataForkType
) {
400 for (i
= 0; i
< kHFSPlusExtentDensity
; i
++) {
401 if ((extRecord
[i
].blockCount
% blocksPerNode
) != 0) {
402 result
= errRebuildBtree
;
403 *statusFlag
|= S_RebuildBTree
;
404 fsckPrint(GPtr
->context
, E_BTreeSplitNode
, fileID
);
408 plog("Improperly split node in file id %u, startBlock %u, index %d (offset %u), extent <%u, %u>\n", fileID
, key
->startBlock
, i
, totalBlocks
, extRecord
[i
].startBlock
, extRecord
[i
].blockCount
);
411 totalBlocks
+= extRecord
[i
].blockCount
;
413 memset(&extRecord
, 0, sizeof(extRecord
));
419 plog( "\nB-Tree header rec: \n" );
420 plog( " treeDepth = %d \n", header
->treeDepth
);
421 plog( " rootNode = %d \n", header
->rootNode
);
422 plog( " leafRecords = %d \n", header
->leafRecords
);
423 plog( " firstLeafNode = %d \n", header
->firstLeafNode
);
424 plog( " lastLeafNode = %d \n", header
->lastLeafNode
);
425 plog( " totalNodes = %d \n", header
->totalNodes
);
426 plog( " freeNodes = %d \n", header
->freeNodes
);
429 if (calculatedBTCB
->rootNode
== 0) {
430 // Empty btree, no need to continue
434 * Set up tree path record for root level
437 /* BTPTPtr is an array of structure which stores the state
438 * of the btree traversal based on the current BTree level.
439 * It helps to traverse to parent node from a child node.
440 * tprP points to the correct offset to read/write.
442 tprP
= &(*GPtr
->BTPTPtr
)[0];
443 tprP
->TPRNodeN
= calculatedBTCB
->rootNode
;
444 tprP
->TPRRIndx
= -1; /* last index accessed in a node */
449 * Now enumerate the entire BTree
451 while ( GPtr
->BTLevel
> 0 )
453 tprP
= &(*GPtr
->BTPTPtr
)[GPtr
->BTLevel
-1];
454 nodeNum
= tprP
->TPRNodeN
;
455 index
= tprP
->TPRRIndx
;
457 GPtr
->TarBlock
= nodeNum
;
459 (void) ReleaseNode(calculatedBTCB
, &node
);
460 result
= GetNode( calculatedBTCB
, nodeNum
, &node
);
461 if ( result
!= noErr
)
463 if ( result
== fsBTInvalidNodeErr
) /* hfs_swap_BTNode failed */
465 RcdError( GPtr
, E_BadNode
);
471 /* Try to continue checking other nodes.
473 * Decrement the current btree level as we want to access
474 * the right sibling index record, if any, of our parent.
481 nodeDescP
= node
.buffer
;
484 * Check out and allocate the node if its the first time its been seen
489 // this will print out our leaf node order
490 if ( nodeDescP
->kind
== kBTLeafNode
)
492 static int myCounter
= 0;
493 if ( myCounter
> 19 )
498 plog( "%d ", nodeNum
);
504 /* Allocate BTree node */
505 result
= AllocBTN( GPtr
, refNum
, nodeNum
);
508 /* node already allocated can be fixed if it is an index node */
509 goto RebuildBTreeExit
;
512 /* Check keys in the node */
513 result
= BTKeyChk( GPtr
, nodeDescP
, calculatedBTCB
);
516 /* we should be able to fix any E_KeyOrd error or any B-Tree key */
517 /* errors with an index node. */
518 if ( E_KeyOrd
== result
|| nodeDescP
->kind
== kBTIndexNode
)
520 *statusFlag
|= S_RebuildBTree
;
521 result
= errRebuildBtree
;
529 /* Check backward link of this node */
530 if ( nodeDescP
->bLink
!= tprP
->TPRLtSib
)
533 RcdError( GPtr
, E_SibLk
);
535 printf("Node %d's back link is 0x%x; expected 0x%x\n"
536 " disk offset = 0x%llx, size = 0x%x\n",
537 nodeNum
, nodeDescP
->bLink
, tprP
->TPRLtSib
,
538 ((Buf_t
*)(node
.blockHeader
))->Offset
, ((Buf_t
*)(node
.blockHeader
))->Length
);
540 goto RebuildBTreeExit
;
542 if ( tprP
->TPRRtSib
== -1 )
544 tprP
->TPRRtSib
= nodeNum
; /* set Rt sibling for later verification */
548 /* Check forward link for this node */
549 if ( nodeDescP
->fLink
!= tprP
->TPRRtSib
)
552 RcdError( GPtr
, E_SibLk
);
554 printf("Node %d's forward link is 0x%x; expected 0x%x\n"
555 " disk offset = 0x%llx, size = 0x%x\n",
556 nodeNum
, nodeDescP
->fLink
, tprP
->TPRRtSib
,
557 ((Buf_t
*)(node
.blockHeader
))->Offset
, ((Buf_t
*)(node
.blockHeader
))->Length
);
559 goto RebuildBTreeExit
;
563 /* Check node kind - it should either be index node or leaf node */
564 if ( (nodeDescP
->kind
!= kBTIndexNode
) && (nodeDescP
->kind
!= kBTLeafNode
) )
567 RcdError( GPtr
, E_NType
);
568 if (!debug
) goto exit
;
570 /* Check if the height of this node is correct based on calculated
571 * tree depth and current btree level of the traversal
573 if ( nodeDescP
->height
!= calculatedBTCB
->treeDepth
- GPtr
->BTLevel
+ 1 )
576 RcdError( GPtr
, E_NHeight
);
577 if (!debug
) goto RebuildBTreeExit
;
580 if (result
&& (cur_debug_level
& d_dump_node
))
582 plog("Node %u:\n", node
.blockNum
);
583 HexDump(node
.buffer
, node
.blockSize
, TRUE
);
588 /* If we saved the first key in the parent (index) node in past, use it to compare
589 * with the key of the first record in the current node. This check should
590 * be performed for all nodes except the root node.
592 if ( hasParKey
== true )
594 GetRecordByIndex( (BTreeControlBlock
*)calculatedBTCB
, nodeDescP
, 0, &keyPtr
, &dataPtr
, &recSize
);
595 if ( CompareKeys( (BTreeControlBlockPtr
)calculatedBTCB
, (BTreeKey
*)parKey
, keyPtr
) != 0 )
599 plog("Index key doesn't match first node key\n");
600 if (cur_debug_level
& d_dump_record
)
602 plog("Found (child; node %u):\n", tprP
->TPRNodeN
);
603 HexDump(keyPtr
, CalcKeySize(calculatedBTCB
, keyPtr
), FALSE
);
604 plog("Expected (parent; node %u):\n", tprP
[-1].TPRNodeN
);
605 HexDump(parKey
, CalcKeySize(calculatedBTCB
, (BTreeKey
*)parKey
), FALSE
);
608 RcdError( GPtr
, E_IKey
);
609 *statusFlag
|= S_RebuildBTree
;
610 result
= errRebuildBtree
;
613 if ( nodeDescP
->kind
== kBTIndexNode
)
615 if ( ( result
= CheckForStop( GPtr
) ) )
619 GPtr
->itemsProcessed
++;
622 numRecs
= nodeDescP
->numRecords
;
625 * for an index node ...
627 if ( nodeDescP
->kind
== kBTIndexNode
)
629 index
++; /* on to next index record */
630 if ( index
>= numRecs
)
632 /* We have traversed children of all index records in this index node.
633 * Decrement the current btree level to access right sibling index record
634 * of previous btree level
637 continue; /* No more records */
640 /* Store current index for current Btree level */
641 tprP
->TPRRIndx
= index
;
642 /* Store current pointer as parent for next traversal */
644 /* Increase the current Btree level because we traverse top to down */
647 /* Validate current btree traversal level */
648 if ( GPtr
->BTLevel
> BTMaxDepth
)
650 RcdError( GPtr
, E_BTDepth
);
651 goto RebuildBTreeExit
;
653 /* Get the btree traversal state for current btree level */
654 tprP
= &(*GPtr
->BTPTPtr
)[GPtr
->BTLevel
-1];
656 /* Get index record in the current btree level at offset index in the given node */
657 GetRecordByIndex( (BTreeControlBlock
*)calculatedBTCB
, nodeDescP
,
658 index
, &keyPtr
, &dataPtr
, &recSize
);
660 nodeNum
= *(UInt32
*)dataPtr
;
661 /* Current node number should not be header node number or greater than total nodes */
662 if ( (nodeNum
== kHeaderNodeNum
) || (nodeNum
>= calculatedBTCB
->totalNodes
) )
664 RcdError( GPtr
, E_IndxLk
);
665 goto RebuildBTreeExit
;
669 * Make a copy of the parent's key so we can compare it
670 * with the child's key later.
672 keyLen
= ( calculatedBTCB
->attributes
& kBTBigKeysMask
)
673 ? keyPtr
->length16
+ sizeof(UInt16
)
674 : keyPtr
->length8
+ sizeof(UInt8
);
675 CopyMemory(keyPtr
, parKey
, keyLen
);
678 /* Store current node number for the child node */
679 tprP
->TPRNodeN
= nodeNum
;
680 /* Initialize index to records for the child node */
683 tprP
->TPRLtSib
= 0; /* left sibling */
686 /* Get node number for the previous index record in current index node */
687 GetRecordByIndex( (BTreeControlBlock
*)calculatedBTCB
, nodeDescP
, index
-1, &keyPtr
, &dataPtr
, &recSize
);
689 nodeNum
= *(UInt32
*)dataPtr
;
690 /* node number should not be header node number or greater than total nodes */
691 if ( (nodeNum
== kHeaderNodeNum
) || (nodeNum
>= calculatedBTCB
->totalNodes
) )
693 RcdError( GPtr
, E_IndxLk
);
694 goto RebuildBTreeExit
;
696 /* Store this as left sibling node */
697 tprP
->TPRLtSib
= nodeNum
;
701 if ( parentP
->TPRLtSib
!= 0 )
702 tprP
->TPRLtSib
= tprP
->TPRRtSib
; /* Fill in the missing link */
705 tprP
->TPRRtSib
= 0; /* right sibling */
706 if ( index
< (numRecs
-1) )
708 /* Get node number for the next index record in current index node */
709 GetRecordByIndex( (BTreeControlBlock
*)calculatedBTCB
, nodeDescP
, index
+1, &keyPtr
, &dataPtr
, &recSize
);
711 nodeNum
= *(UInt32
*)dataPtr
;
712 /* node number should not be header node number or greater than total nodes */
713 if ( (nodeNum
== kHeaderNodeNum
) || (nodeNum
>= calculatedBTCB
->totalNodes
) )
715 RcdError( GPtr
, E_IndxLk
);
716 goto RebuildBTreeExit
;
718 /* Store this as right sibling node */
719 tprP
->TPRRtSib
= nodeNum
;
723 if ( parentP
->TPRRtSib
!= 0 )
724 tprP
->TPRRtSib
= -1; /* Link to be filled in later */
729 * For a leaf node ...
733 /* If left sibling link is zero, this is first leaf node */
734 if ( tprP
->TPRLtSib
== 0 )
735 calculatedBTCB
->firstLeafNode
= nodeNum
;
736 /* If right sibling link is zero, this is last leaf node */
737 if ( tprP
->TPRRtSib
== 0 )
738 calculatedBTCB
->lastLeafNode
= nodeNum
;
739 leafRecords
+= nodeDescP
->numRecords
;
741 if (checkLeafRecord
!= NULL
) {
742 /* For total number of records in this leaf node, get each record sequentially
743 * and call function to check individual leaf record through the
744 * function pointer passed by the caller
746 for (i
= 0; i
< nodeDescP
->numRecords
; i
++) {
747 GetRecordByIndex(calculatedBTCB
, nodeDescP
, i
, &keyPtr
, &dataPtr
, &recSize
);
748 result
= checkLeafRecord(GPtr
, keyPtr
, dataPtr
, recSize
);
749 if (result
) goto exit
;
752 /* Decrement the current btree level as we want to access
753 * the right sibling index record, if any, of our parent.
760 calculatedBTCB
->leafRecords
= leafRecords
;
763 if (result
== noErr
&& (*statusFlag
& S_RebuildBTree
))
764 result
= errRebuildBtree
;
765 if (node
.buffer
!= NULL
)
766 (void) ReleaseNode(calculatedBTCB
, &node
);
771 /* force a B-Tree file rebuild */
772 *statusFlag
|= S_RebuildBTree
;
773 result
= errRebuildBtree
;
776 } /* end of BTCheck */
780 /*------------------------------------------------------------------------------
782 Routine: BTMapChk - (BTree Map Check)
784 Function: Checks out the structure of a BTree allocation map.
786 Input: GPtr - pointer to scavenger global area
787 fileRefNum - refnum of BTree file
789 Output: BTMapChk - function result:
792 ------------------------------------------------------------------------------*/
794 int BTMapChk( SGlobPtr GPtr
, short fileRefNum
)
802 NodeDescPtr nodeDescP
;
803 BTreeControlBlock
*calculatedBTCB
= GetBTreeControlBlock( fileRefNum
);
806 nodeNum
= 0; /* Start with header node */
809 mapSize
= ( calculatedBTCB
->totalNodes
+ 7 ) / 8; /* size in bytes */
812 * Enumerate the map structure starting with the map record in the header node
814 while ( mapSize
> 0 )
816 GPtr
->TarBlock
= nodeNum
;
818 if (node
.buffer
!= NULL
)
819 (void) ReleaseNode(calculatedBTCB
, &node
);
820 result
= GetNode( calculatedBTCB
, nodeNum
, &node
);
821 if ( result
!= noErr
)
823 if ( result
== fsBTInvalidNodeErr
) /* hfs_swap_BTNode failed */
825 RcdError( GPtr
, E_BadNode
);
831 nodeDescP
= node
.buffer
;
833 /* Check out the node if its not the header node */
837 result
= AllocBTN( GPtr
, fileRefNum
, nodeNum
);
838 if (result
) goto exit
; /* Error, node already allocated? */
840 if ( nodeDescP
->kind
!= kBTMapNode
)
842 RcdError( GPtr
, E_BadMapN
);
844 plog("Expected map node, got type %d\n", nodeDescP
->kind
);
848 if ( nodeDescP
->numRecords
!= Num_MRecs
)
850 RcdError( GPtr
, E_BadMapN
);
852 plog("Expected %d records in node, found %d\n", Num_MRecs
, nodeDescP
->numRecords
);
856 if ( nodeDescP
->height
!= 0 )
857 RcdError( GPtr
, E_NHeight
);
860 // Move on to the next map node
861 recSize
= GetRecordSize( (BTreeControlBlock
*)calculatedBTCB
, (BTNodeDescriptor
*)nodeDescP
, recIndx
);
862 mapSize
-= recSize
; /* Adjust remaining map size */
864 recIndx
= 0; /* Map record is now record 0 */
865 nodeNum
= nodeDescP
->fLink
;
872 if ( (nodeNum
!= 0) || (mapSize
> 0) )
874 RcdError( GPtr
, E_MapLk
);
875 result
= E_MapLk
; /* bad map node linkage */
878 if (node
.buffer
!= NULL
)
879 (void) ReleaseNode(calculatedBTCB
, &node
);
887 /*------------------------------------------------------------------------------
889 Routine: BTCheckUnusedNodes
891 Function: Examines all unused nodes and makes sure they are filled with zeroes.
892 If there are any unused nodes which are not zero filled, bit mask
893 S_UnusedNodesNotZero is set in output btStat; the function result
894 is zero in this case.
896 Input: GPtr - pointer to scavenger global area
897 fileRefNum - refnum of BTree file
899 Output: *btStat - bit mask S_UnusedNodesNotZero
900 BTCheckUnusedNodes - function result:
903 ------------------------------------------------------------------------------*/
905 int BTCheckUnusedNodes(SGlobPtr GPtr
, short fileRefNum
, UInt16
*btStat
)
907 BTreeControlBlock
*btcb
= GetBTreeControlBlock(fileRefNum
);
908 unsigned char *bitmap
= (unsigned char *) ((BTreeExtensionsRec
*)btcb
->refCon
)->BTCBMPtr
;
909 unsigned char mask
= 0x80;
912 BlockDescriptor node
;
916 for (nodeNum
= 0; nodeNum
< btcb
->totalNodes
; ++nodeNum
)
918 if ((*bitmap
& mask
) == 0)
924 /* Read the raw node, without going through hfs_swap_BTNode. */
925 err
= btcb
->getBlockProc(btcb
->fcbPtr
, nodeNum
, kGetBlock
, &node
);
928 if (debug
) plog("Couldn't read node #%u\n", nodeNum
);
933 * Make sure node->blockSize bytes at address node->buffer are zero.
935 buffer
= (UInt32
*) node
.buffer
;
936 bufferSize
= node
.blockSize
/ sizeof(UInt32
);
938 for (i
= 0; i
< bufferSize
; ++i
)
942 *btStat
|= S_UnusedNodesNotZero
;
943 GPtr
->TarBlock
= nodeNum
;
944 fsckPrint(GPtr
->context
, E_UnusedNodeNotZeroed
, nodeNum
);
948 /* Stop now; repair will zero all unused nodes. */
952 /* No need to check the rest of this node. */
957 /* Release the node without going through hfs_swap_BTNode. */
958 (void) btcb
->releaseBlockProc(btcb
->fcbPtr
, &node
, kReleaseBlock
);
962 /* Move to the next bit in the bitmap. */
973 (void) btcb
->releaseBlockProc(btcb
->fcbPtr
, &node
, kReleaseBlock
);
977 } /* end BTCheckUnusedNodes */
981 /*------------------------------------------------------------------------------
983 Routine: CmpBTH - (Compare BTree Header)
985 Function: Compares the scavenger BTH info with the BTH on disk.
987 Input: GPtr - pointer to scavenger global area
988 fileRefNum - file refnum
990 Output: CmpBTH - function result:
993 ------------------------------------------------------------------------------*/
995 OSErr
CmpBTH( SGlobPtr GPtr
, SInt16 fileRefNum
)
998 BTHeaderRec bTreeHeader
;
999 BTreeControlBlock
*calculatedBTCB
= GetBTreeControlBlock( fileRefNum
);
1002 short isBTHDamaged
= 0;
1005 switch (fileRefNum
) {
1006 case kCalculatedCatalogRefNum
:
1007 statP
= (SInt16
*)&GPtr
->CBTStat
;
1008 fcb
= GPtr
->calculatedCatalogFCB
;
1010 case kCalculatedExtentRefNum
:
1011 statP
= (SInt16
*)&GPtr
->EBTStat
;
1012 fcb
= GPtr
->calculatedExtentsFCB
;
1014 case kCalculatedAttributesRefNum
:
1015 statP
= (SInt16
*)&GPtr
->ABTStat
;
1016 fcb
= GPtr
->calculatedAttributesFCB
;
1023 * Get BTree header record from disk
1025 GPtr
->TarBlock
= 0; // Set target node number
1027 err
= GetBTreeHeader(GPtr
, fcb
, &bTreeHeader
);
1028 ReturnIfError( err
);
1030 if (calculatedBTCB
->leafRecords
!= bTreeHeader
.leafRecords
) {
1031 char goodStr
[32], badStr
[32];
1034 fsckPrint(GPtr
->context
, E_LeafCnt
);
1035 sprintf(goodStr
, "%ld", (long)calculatedBTCB
->leafRecords
);
1036 sprintf(badStr
, "%ld", (long)bTreeHeader
.leafRecords
);
1037 fsckPrint(GPtr
->context
, E_BadValue
, goodStr
, badStr
);
1040 if ( calculatedBTCB
->treeDepth
!= bTreeHeader
.treeDepth
) {
1041 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1042 plog("\tinvalid tree depth - calculated %d header %d \n",
1043 calculatedBTCB
->treeDepth
, bTreeHeader
.treeDepth
);
1045 } else if ( calculatedBTCB
->rootNode
!= bTreeHeader
.rootNode
) {
1046 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1047 plog("\tinvalid root node - calculated %d header %d \n",
1048 calculatedBTCB
->rootNode
, bTreeHeader
.rootNode
);
1050 } else if ( calculatedBTCB
->firstLeafNode
!= bTreeHeader
.firstLeafNode
) {
1051 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1052 plog("\tinvalid first leaf node - calculated %d header %d \n",
1053 calculatedBTCB
->firstLeafNode
, bTreeHeader
.firstLeafNode
);
1055 } else if ( calculatedBTCB
->lastLeafNode
!= bTreeHeader
.lastLeafNode
) {
1056 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1057 plog("\tinvalid last leaf node - calculated %d header %d \n",
1058 calculatedBTCB
->lastLeafNode
, bTreeHeader
.lastLeafNode
);
1060 } else if ( calculatedBTCB
->nodeSize
!= bTreeHeader
.nodeSize
) {
1061 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1062 plog("\tinvalid node size - calculated %d header %d \n",
1063 calculatedBTCB
->nodeSize
, bTreeHeader
.nodeSize
);
1065 } else if ( calculatedBTCB
->maxKeyLength
!= bTreeHeader
.maxKeyLength
) {
1066 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1067 plog("\tinvalid max key length - calculated %d header %d \n",
1068 calculatedBTCB
->maxKeyLength
, bTreeHeader
.maxKeyLength
);
1070 } else if ( calculatedBTCB
->totalNodes
!= bTreeHeader
.totalNodes
) {
1071 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1072 plog("\tinvalid total nodes - calculated %d header %d \n",
1073 calculatedBTCB
->totalNodes
, bTreeHeader
.totalNodes
);
1075 } else if ( calculatedBTCB
->freeNodes
!= bTreeHeader
.freeNodes
) {
1076 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1077 plog("\tinvalid free nodes - calculated %d header %d \n",
1078 calculatedBTCB
->freeNodes
, bTreeHeader
.freeNodes
);
1082 if (isBTHDamaged
|| printMsg
) {
1083 *statP
= *statP
| S_BTH
;
1085 fsckPrint(GPtr
->context
, E_InvalidBTreeHeader
);
1093 /*------------------------------------------------------------------------------
1097 Function: Compares two data blocks for equality.
1099 Input: Blk1Ptr - pointer to 1st data block.
1100 Blk2Ptr - pointer to 2nd data block.
1101 len - size of the blocks (in bytes)
1103 Output: CmpBlock - result code
1106 ------------------------------------------------------------------------------*/
1108 OSErr
CmpBlock( void *block1P
, void *block2P
, size_t length
)
1110 Byte
*blk1Ptr
= block1P
;
1111 Byte
*blk2Ptr
= block2P
;
1114 if ( *blk1Ptr
++ != *blk2Ptr
++ )
1123 /*------------------------------------------------------------------------------
1125 Routine: CmpBTM - (Compare BTree Map)
1127 Function: Compares the scavenger BTM with the BTM on disk.
1129 Input: GPtr - pointer to scavenger global area
1130 fileRefNum - file refnum
1132 Output: CmpBTM - function result:
1135 ------------------------------------------------------------------------------*/
1137 int CmpBTM( SGlobPtr GPtr
, short fileRefNum
)
1149 NodeDescPtr nodeDescP
;
1150 BTreeControlBlock
*calculatedBTCB
;
1154 calculatedBTCB
= GetBTreeControlBlock( fileRefNum
);
1156 switch (fileRefNum
) {
1157 case kCalculatedCatalogRefNum
:
1158 statP
= &GPtr
->CBTStat
;
1160 case kCalculatedExtentRefNum
:
1161 statP
= &GPtr
->EBTStat
;
1163 case kCalculatedAttributesRefNum
:
1164 statP
= &GPtr
->ABTStat
;
1170 nodeNum
= 0; /* start with header node */
1174 mapSize
= (calculatedBTCB
->totalNodes
+ 7) / 8; /* size in bytes */
1175 sbtmP
= ((BTreeExtensionsRec
*)calculatedBTCB
->refCon
)->BTCBMPtr
;
1179 * Enumerate BTree map records starting with map record in header node
1181 while ( mapSize
> 0 )
1183 GPtr
->TarBlock
= nodeNum
;
1185 if (node
.buffer
!= NULL
)
1186 (void) ReleaseNode(calculatedBTCB
, &node
);
1188 result
= GetNode( calculatedBTCB
, nodeNum
, &node
);
1189 if (result
) goto exit
; /* error, could't get map node */
1191 nodeDescP
= node
.buffer
;
1193 recSize
= GetRecordSize( (BTreeControlBlock
*)calculatedBTCB
, (BTNodeDescriptor
*)nodeDescP
, recIndx
);
1194 dataPtr
= GetRecordAddress( (BTreeControlBlock
*)calculatedBTCB
, (BTNodeDescriptor
*)nodeDescP
, recIndx
);
1196 size
= ( recSize
> mapSize
) ? mapSize
: recSize
;
1198 result
= CmpBlock( sbtmP
, dataPtr
, size
);
1199 if ( result
!= noErr
)
1201 *statP
= *statP
| S_BTM
; /* didn't match, mark it damaged */
1202 RcdError(GPtr
, E_BadMapN
);
1203 result
= 0; /* mismatch isn't fatal; let us continue */
1207 recIndx
= 0; /* map record is now record 0 */
1208 mapSize
-= size
; /* adjust remaining map size */
1209 sbtmP
= sbtmP
+ size
;
1210 nodeNum
= nodeDescP
->fLink
; /* next node number */
1217 * Make sure the unused portion of the last map record is zero
1219 for ( p
= (Ptr
)dataPtr
+ size
; p
< (Ptr
)dataPtr
+ recSize
; p
++ )
1221 *statP
= *statP
| S_BTM
; /* didn't match, mark it damaged */
1224 if (node
.buffer
!= NULL
)
1225 (void) ReleaseNode(calculatedBTCB
, &node
);
1232 /*------------------------------------------------------------------------------
1234 Routine: BTKeyChk - (BTree Key Check)
1236 Function: Checks out the key structure within a Btree node.
1238 Input: GPtr - pointer to scavenger global area
1239 NodePtr - pointer to target node
1240 BTCBPtr - pointer to BTreeControlBlock
1242 Output: BTKeyChk - function result:
1245 ------------------------------------------------------------------------------*/
1246 extern HFSPlusCatalogKey gMetaDataDirKey
;
1248 static int BTKeyChk( SGlobPtr GPtr
, NodeDescPtr nodeP
, BTreeControlBlock
*btcb
)
1253 UInt16 prevKeyLength
= 0;
1256 KeyPtr prevkeyP
= nil
;
1257 unsigned sizeofKeyLength
;
1260 if (btcb
->attributes
& kBTBigKeysMask
)
1261 sizeofKeyLength
= 2;
1263 sizeofKeyLength
= 1;
1265 if ( nodeP
->numRecords
== 0 )
1267 if ( (nodeP
->fLink
== 0) && (nodeP
->bLink
== 0) )
1269 RcdError( GPtr
, E_BadNode
);
1270 return( E_BadNode
);
1276 * Loop on number of records in node
1278 for ( index
= 0; index
< nodeP
->numRecords
; index
++)
1280 GetRecordByIndex( (BTreeControlBlock
*)btcb
, nodeP
, (UInt16
) index
, &keyPtr
, &dataPtr
, &dataSize
);
1282 if (btcb
->attributes
& kBTBigKeysMask
)
1283 keyLength
= keyPtr
->length16
;
1285 keyLength
= keyPtr
->length8
;
1287 if ( keyLength
> btcb
->maxKeyLength
)
1289 RcdError( GPtr
, E_KeyLen
);
1293 if ( prevkeyP
!= nil
)
1295 if ( CompareKeys( (BTreeControlBlockPtr
)btcb
, prevkeyP
, keyPtr
) >= 0 )
1298 * When the HFS+ MetaDataDirKey is out of order we mark
1299 * the result code so that it can be deleted later.
1301 if ((btcb
->maxKeyLength
== kHFSPlusCatalogKeyMaximumLength
) &&
1302 (CompareKeys(btcb
, prevkeyP
, (KeyPtr
)&gMetaDataDirKey
) == 0))
1304 if (fsckGetVerbosity(GPtr
->context
) > 0)
1305 plog("Problem: b-tree key for \"HFS+ Private Data\" directory is out of order.\n");
1306 return( E_KeyOrd
+ 1000 );
1310 RcdError( GPtr
, E_KeyOrd
);
1311 if (fsckGetVerbosity(GPtr
->context
) > 0)
1312 plog("Records %d and %d (0-based); offsets 0x%04X and 0x%04X\n", index
-1, index
, (long)prevkeyP
- (long)nodeP
, (long)keyPtr
- (long)nodeP
);
1318 prevKeyLength
= keyLength
;
1322 if (result
== E_KeyOrd
)
1324 if (cur_debug_level
& d_dump_record
)
1326 for (index
= 0; index
< nodeP
->numRecords
; ++index
)
1328 GetRecordByIndex( (BTreeControlBlock
*)btcb
, nodeP
, (UInt16
) index
, &keyPtr
, &dataPtr
, &dataSize
);
1330 if (btcb
->attributes
& kBTBigKeysMask
)
1331 keyLength
= keyPtr
->length16
;
1333 keyLength
= keyPtr
->length8
;
1335 plog("Record %d (offset 0x%04X):\n", index
, (long)keyPtr
- (long)nodeP
);
1336 HexDump(keyPtr
, keyLength
+ sizeofKeyLength
, FALSE
);
1338 HexDump(dataPtr
, dataSize
, FALSE
);
1343 if (cur_debug_level
& d_dump_node
)
1346 HexDump(nodeP
, btcb
->nodeSize
, TRUE
);
1355 /*------------------------------------------------------------------------------
1357 Routine: ChkCName (Check Catalog Name)
1359 Function: Checks out a generic catalog name.
1361 Input: GPtr - pointer to scavenger global area.
1362 CNamePtr - pointer to CName.
1364 Output: ChkCName - function result:
1366 E_CName = invalid CName
1367 ------------------------------------------------------------------------------*/
1369 OSErr
ChkCName( SGlobPtr GPtr
, const CatalogName
*name
, Boolean unicode
)
1374 length
= CatalogNameLength( name
, unicode
);
1378 if ( (length
== 0) || (length
> kHFSPlusMaxFileNameChars
) )
1383 if ( (length
== 0) || (length
> kHFSMaxFileNameChars
) )
1391 /*------------------------------------------------------------------------------
1393 Routine: CmpMDB - (Compare Master Directory Block)
1395 Function: Compares the scavenger MDB info with the MDB on disk.
1397 Input: GPtr - pointer to scavenger global area
1399 Output: CmpMDB - function result:
1402 GPtr->VIStat - S_MDB flag set in VIStat if MDB is damaged.
1403 ------------------------------------------------------------------------------*/
1405 int CmpMDB( SGlobPtr GPtr
, HFSMasterDirectoryBlock
* mdbP
)
1411 short isMDBDamaged
= 0;
1414 GPtr
->TarID
= MDB_FNum
;
1415 vcb
= GPtr
->calculatedVCB
;
1418 * compare VCB info with MDB
1420 if ( mdbP
->drSigWord
!= vcb
->vcbSignature
) {
1421 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1422 plog( "\tinvalid MDB drSigWord \n" );
1425 if ( mdbP
->drCrDate
!= vcb
->vcbCreateDate
) {
1426 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1427 plog( "\tinvalid MDB drCrDate \n" );
1430 if ( mdbP
->drLsMod
!= vcb
->vcbModifyDate
) {
1431 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1432 plog( "\tinvalid MDB drLsMod \n" );
1435 if ( mdbP
->drAtrb
!= (UInt16
)vcb
->vcbAttributes
) {
1436 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1437 plog( "\tinvalid MDB drAtrb \n" );
1440 if ( mdbP
->drVBMSt
!= vcb
->vcbVBMSt
) {
1441 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1442 plog( "\tinvalid MDB drVBMSt \n" );
1445 if ( mdbP
->drNmAlBlks
!= vcb
->vcbTotalBlocks
) {
1446 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1447 plog( "\tinvalid MDB drNmAlBlks \n" );
1450 if ( mdbP
->drClpSiz
!= vcb
->vcbDataClumpSize
) {
1451 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1452 plog( "\tinvalid MDB drClpSiz \n" );
1455 if ( mdbP
->drAlBlSt
!= vcb
->vcbAlBlSt
) {
1456 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1457 plog( "\tinvalid MDB drAlBlSt \n" );
1460 if ( mdbP
->drNxtCNID
!= vcb
->vcbNextCatalogID
) {
1461 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1462 plog( "\tinvalid MDB drNxtCNID \n" );
1465 if ( CmpBlock( mdbP
->drVN
, vcb
->vcbVN
, mdbP
->drVN
[0]+1 ) ) {
1466 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1467 plog( "\tinvalid MDB drVN \n" );
1470 if ( mdbP
->drVolBkUp
!= vcb
->vcbBackupDate
) {
1471 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1472 plog( "\tinvalid MDB drVolBkUp \n" );
1475 if ( mdbP
->drVSeqNum
!= vcb
->vcbVSeqNum
) {
1476 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1477 plog( "\tinvalid MDB drVSeqNum \n" );
1480 if ( mdbP
->drWrCnt
!= vcb
->vcbWriteCount
) {
1481 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1482 plog( "\tinvalid MDB drWrCnt \n" );
1485 if ( mdbP
->drXTClpSiz
!= vcb
->vcbExtentsFile
->fcbClumpSize
) {
1486 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1487 plog( "\tinvalid MDB drXTClpSiz \n" );
1490 if ( mdbP
->drCTClpSiz
!= vcb
->vcbCatalogFile
->fcbClumpSize
) {
1491 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1492 plog( "\tinvalid MDB drCTClpSiz \n" );
1495 if ( mdbP
->drNmRtDirs
!= vcb
->vcbNmRtDirs
) {
1496 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1497 plog( "\tinvalid MDB drNmRtDirs \n" );
1500 if ( mdbP
->drFilCnt
!= vcb
->vcbFileCount
) {
1501 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1502 plog( "\tinvalid MDB drFilCnt \n" );
1505 if ( mdbP
->drDirCnt
!= vcb
->vcbFolderCount
) {
1506 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1507 plog( "\tinvalid MDB drDirCnt \n" );
1510 if ( CmpBlock(mdbP
->drFndrInfo
, vcb
->vcbFinderInfo
, 32 ) ) {
1511 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1512 plog( "\tinvalid MDB drFndrInfo \n" );
1517 * compare extent file allocation info with MDB
1519 fcbP
= vcb
->vcbExtentsFile
; /* compare PEOF for extent file */
1520 if ( mdbP
->drXTFlSize
!= fcbP
->fcbPhysicalSize
)
1523 WriteError ( GPtr
, E_MDBDamaged
, 3, 0 );
1525 for ( i
= 0; i
< GPtr
->numExtents
; i
++ )
1527 if ( (mdbP
->drXTExtRec
[i
].startBlock
!= fcbP
->fcbExtents16
[i
].startBlock
) ||
1528 (mdbP
->drXTExtRec
[i
].blockCount
!= fcbP
->fcbExtents16
[i
].blockCount
) )
1531 WriteError ( GPtr
, E_MDBDamaged
, 4, 0 );
1536 * compare catalog file allocation info with MDB
1538 fcbP
= vcb
->vcbCatalogFile
; /* compare PEOF for catalog file */
1539 if ( mdbP
->drCTFlSize
!= fcbP
->fcbPhysicalSize
)
1542 WriteError ( GPtr
, E_MDBDamaged
, 5, 0 );
1544 for ( i
= 0; i
< GPtr
->numExtents
; i
++ )
1546 if ( (mdbP
->drCTExtRec
[i
].startBlock
!= fcbP
->fcbExtents16
[i
].startBlock
) ||
1547 (mdbP
->drCTExtRec
[i
].blockCount
!= fcbP
->fcbExtents16
[i
].blockCount
) )
1550 WriteError ( GPtr
, E_MDBDamaged
, 6, 0 );
1554 if (isMDBDamaged
|| printMsg
) {
1555 GPtr
->VIStat
= GPtr
->VIStat
| S_MDB
;
1557 WriteError ( GPtr
, E_MDBDamaged
, 1, 0 );
1565 /*------------------------------------------------------------------------------
1567 Routine: CompareVolumeHeader - (Compare VolumeHeader Block)
1569 Function: Compares the scavenger VolumeHeader info with the VolumeHeader on disk.
1571 Input: GPtr - pointer to scavenger global area
1573 Output: CmpMDB - function result:
1576 GPtr->VIStat - S_MDB flag set in VIStat if MDB is damaged.
1577 ------------------------------------------------------------------------------*/
1579 OSErr
CompareVolumeHeader( SGlobPtr GPtr
, HFSPlusVolumeHeader
*volumeHeader
)
1584 UInt32 hfsPlusIOPosOffset
;
1585 UInt32 goodValue
, badValue
;
1586 char goodStr
[32], badStr
[32];
1590 vcb
= GPtr
->calculatedVCB
;
1591 GPtr
->TarID
= MDB_FNum
;
1593 hfsPlusIOPosOffset
= vcb
->vcbEmbeddedOffset
;
1595 goodValue
= badValue
= 0;
1599 // CatHChk will flag valence errors and display the good and bad values for
1600 // our file and folder counts. It will set S_Valence in CatStat when this
1601 // problem is detected. We do NOT want to flag the error here in that case
1602 // since the volume header counts cannot be trusted and it will lead to
1603 // confusing messages.
1604 if ( volumeHeader
->fileCount
!= vcb
->vcbFileCount
&&
1605 (GPtr
->CatStat
& S_Valence
) == 0 ) {
1606 fsckPrint(GPtr
->context
, E_FilCnt
);
1607 sprintf(goodStr
, "%u", vcb
->vcbFileCount
);
1608 sprintf(badStr
, "%u", volumeHeader
->fileCount
);
1609 fsckPrint(GPtr
->context
, E_BadValue
, goodStr
, badStr
);
1613 if ( volumeHeader
->folderCount
!= vcb
->vcbFolderCount
&&
1614 (GPtr
->CatStat
& S_Valence
) == 0 ) {
1615 fsckPrint(GPtr
->context
, E_DirCnt
);
1616 sprintf(goodStr
, "%u", vcb
->vcbFolderCount
);
1617 sprintf(badStr
, "%u", volumeHeader
->folderCount
);
1618 fsckPrint(GPtr
->context
, E_BadValue
, goodStr
, badStr
);
1623 if (volumeHeader
->freeBlocks
!= vcb
->vcbFreeBlocks
) {
1624 fsckPrint(GPtr
->context
, E_FreeBlocks
);
1625 sprintf(goodStr
, "%u", vcb
->vcbFreeBlocks
);
1626 sprintf(badStr
, "%u", volumeHeader
->freeBlocks
);
1627 fsckPrint(GPtr
->context
, E_BadValue
, goodStr
, badStr
);
1631 if ( volumeHeader
->catalogFile
.clumpSize
!= vcb
->vcbCatalogFile
->fcbClumpSize
) {
1632 fsckPrint(GPtr
->context
, E_InvalidClumpSize
);
1633 sprintf(goodStr
, "%u", vcb
->vcbCatalogFile
->fcbClumpSize
);
1634 sprintf(badStr
, "%u", volumeHeader
->catalogFile
.clumpSize
);
1635 fsckPrint(GPtr
->context
, E_BadValue
, goodStr
, badStr
);
1639 if ( volumeHeader
->signature
!= kHFSPlusSigWord
&&
1640 volumeHeader
->signature
!= kHFSXSigWord
) {
1641 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1642 plog( "\tinvalid VHB signature \n" );
1645 /* From HFS Plus Volume Format Specification (TN1150), "It is acceptable
1646 * for a bit in encodingsBitmap to be set even though no names on the
1647 * volume use that encoding". Therefore we do not report extra bits set in
1648 * on-disk encodingsBitmap as error but will repair it silently if any other
1649 * repairs are made. We complain about extra bits cleared in
1650 * on-disk encodingsBitmap when compared to calculated encodingsBitmap.
1652 if ( (volumeHeader
->encodingsBitmap
& vcb
->vcbEncodingsBitmap
)
1653 != vcb
->vcbEncodingsBitmap
) {
1654 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1655 plog( "\tinvalid VHB encodingsBitmap, disk=0x%qx calculated=0x%qx \n", volumeHeader
->encodingsBitmap
, vcb
->vcbEncodingsBitmap
);
1658 if ( (UInt16
) (hfsPlusIOPosOffset
/512) != vcb
->vcbAlBlSt
) {
1659 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1660 plog( "\tinvalid VHB AlBlSt \n" );
1663 if ( volumeHeader
->createDate
!= vcb
->vcbCreateDate
) {
1664 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1665 plog( "\tinvalid VHB createDate \n" );
1668 if ( volumeHeader
->modifyDate
!= vcb
->vcbModifyDate
) {
1669 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1670 plog( "\tinvalid VHB modifyDate \n" );
1673 if ( volumeHeader
->backupDate
!= vcb
->vcbBackupDate
) {
1674 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1675 plog( "\tinvalid VHB backupDate \n" );
1678 if ( volumeHeader
->checkedDate
!= vcb
->vcbCheckedDate
) {
1679 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1680 plog( "\tinvalid VHB checkedDate \n" );
1683 if ( volumeHeader
->rsrcClumpSize
!= vcb
->vcbRsrcClumpSize
) {
1684 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1685 plog( "\tinvalid VHB rsrcClumpSize (VH=%u, vcb=%u)\n", volumeHeader
->rsrcClumpSize
, vcb
->vcbRsrcClumpSize
);
1688 if ( volumeHeader
->dataClumpSize
!= vcb
->vcbDataClumpSize
) {
1689 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1690 plog( "\tinvalid VHB dataClumpSize \n" );
1693 if ( volumeHeader
->nextCatalogID
!= vcb
->vcbNextCatalogID
&&
1694 (volumeHeader
->attributes
& kHFSCatalogNodeIDsReused
) == 0) {
1695 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1696 plog( "\tinvalid VHB nextCatalogID \n" );
1699 if ( volumeHeader
->writeCount
!= vcb
->vcbWriteCount
) {
1700 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1701 plog( "\tinvalid VHB writeCount \n" );
1704 if ( volumeHeader
->nextAllocation
!= vcb
->vcbNextAllocation
) {
1705 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1706 plog( "\tinvalid VHB nextAllocation \n" );
1709 if ( volumeHeader
->totalBlocks
!= vcb
->vcbTotalBlocks
) {
1710 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1711 plog( "\tinvalid VHB totalBlocks \n" );
1714 if ( volumeHeader
->blockSize
!= vcb
->vcbBlockSize
) {
1715 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1716 plog( "\tinvalid VHB blockSize \n" );
1719 if ( volumeHeader
->attributes
!= vcb
->vcbAttributes
) {
1720 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1721 plog( "\tinvalid VHB attributes \n" );
1724 if ( volumeHeader
->extentsFile
.clumpSize
!= vcb
->vcbExtentsFile
->fcbClumpSize
) {
1725 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1726 plog( "\tinvalid VHB extentsFile.clumpSize \n" );
1729 if ( volumeHeader
->allocationFile
.clumpSize
!= vcb
->vcbAllocationFile
->fcbClumpSize
) {
1730 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1731 plog( "\tinvalid VHB allocationFile.clumpSize \n" );
1734 if ( (vcb
->vcbAttributesFile
!= NULL
) &&
1735 (volumeHeader
->attributesFile
.clumpSize
!= vcb
->vcbAttributesFile
->fcbClumpSize
)) {
1736 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1737 plog( "\tinvalid VHB attributesFile.clumpSize \n" );
1740 if ( CmpBlock( volumeHeader
->finderInfo
, vcb
->vcbFinderInfo
, sizeof(vcb
->vcbFinderInfo
) ) ) {
1741 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1742 plog( "\tinvalid VHB finderInfo \n" );
1747 * compare extent file allocation info with VolumeHeader
1749 fcbP
= vcb
->vcbExtentsFile
;
1750 if ( (UInt64
)volumeHeader
->extentsFile
.totalBlocks
* (UInt64
)vcb
->vcbBlockSize
!= fcbP
->fcbPhysicalSize
)
1753 WriteError ( GPtr
, E_VolumeHeaderDamaged
, 3, 0 );
1755 for ( i
=0; i
< GPtr
->numExtents
; i
++ )
1757 if ( (volumeHeader
->extentsFile
.extents
[i
].startBlock
!= fcbP
->fcbExtents32
[i
].startBlock
) ||
1758 (volumeHeader
->extentsFile
.extents
[i
].blockCount
!= fcbP
->fcbExtents32
[i
].blockCount
) )
1761 WriteError ( GPtr
, E_VolumeHeaderDamaged
, 4, 0 );
1766 * compare catalog file allocation info with MDB
1768 fcbP
= vcb
->vcbCatalogFile
; /* compare PEOF for catalog file */
1769 if ( (UInt64
)volumeHeader
->catalogFile
.totalBlocks
* (UInt64
)vcb
->vcbBlockSize
!= fcbP
->fcbPhysicalSize
)
1772 WriteError ( GPtr
, E_VolumeHeaderDamaged
, 5, 0 );
1774 for ( i
=0; i
< GPtr
->numExtents
; i
++ )
1776 if ( (volumeHeader
->catalogFile
.extents
[i
].startBlock
!= fcbP
->fcbExtents32
[i
].startBlock
) ||
1777 (volumeHeader
->catalogFile
.extents
[i
].blockCount
!= fcbP
->fcbExtents32
[i
].blockCount
) )
1780 WriteError ( GPtr
, E_VolumeHeaderDamaged
, 6, 0 );
1786 * compare bitmap file allocation info with MDB
1788 fcbP
= vcb
->vcbAllocationFile
;
1789 if ( (UInt64
)volumeHeader
->allocationFile
.totalBlocks
* (UInt64
)vcb
->vcbBlockSize
!= fcbP
->fcbPhysicalSize
)
1792 WriteError ( GPtr
, E_VolumeHeaderDamaged
, 7, 0 );
1794 for ( i
=0; i
< GPtr
->numExtents
; i
++ )
1796 if ( (volumeHeader
->allocationFile
.extents
[i
].startBlock
!= fcbP
->fcbExtents32
[i
].startBlock
) ||
1797 (volumeHeader
->allocationFile
.extents
[i
].blockCount
!= fcbP
->fcbExtents32
[i
].blockCount
) )
1800 WriteError ( GPtr
, E_VolumeHeaderDamaged
, 8, 0 );
1804 if (isVHDamaged
|| printMsg
) {
1805 GPtr
->VIStat
= GPtr
->VIStat
| S_MDB
;
1807 WriteError ( GPtr
, E_VolumeHeaderDamaged
, 2, 0 );