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 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
);
1317 prevKeyLength
= keyLength
;
1321 if (result
== E_KeyOrd
)
1323 if (cur_debug_level
& d_dump_record
)
1325 for (index
= 0; index
< nodeP
->numRecords
; ++index
)
1327 GetRecordByIndex( (BTreeControlBlock
*)btcb
, nodeP
, (UInt16
) index
, &keyPtr
, &dataPtr
, &dataSize
);
1329 if (btcb
->attributes
& kBTBigKeysMask
)
1330 keyLength
= keyPtr
->length16
;
1332 keyLength
= keyPtr
->length8
;
1334 plog("Record %d (offset 0x%04X):\n", index
, (long)keyPtr
- (long)nodeP
);
1335 HexDump(keyPtr
, keyLength
+ sizeofKeyLength
, FALSE
);
1337 HexDump(dataPtr
, dataSize
, FALSE
);
1342 if (cur_debug_level
& d_dump_node
)
1345 HexDump(nodeP
, btcb
->nodeSize
, TRUE
);
1354 /*------------------------------------------------------------------------------
1356 Routine: ChkCName (Check Catalog Name)
1358 Function: Checks out a generic catalog name.
1360 Input: GPtr - pointer to scavenger global area.
1361 CNamePtr - pointer to CName.
1363 Output: ChkCName - function result:
1365 E_CName = invalid CName
1366 ------------------------------------------------------------------------------*/
1368 OSErr
ChkCName( SGlobPtr GPtr
, const CatalogName
*name
, Boolean unicode
)
1373 length
= CatalogNameLength( name
, unicode
);
1377 if ( (length
== 0) || (length
> kHFSPlusMaxFileNameChars
) )
1382 if ( (length
== 0) || (length
> kHFSMaxFileNameChars
) )
1390 /*------------------------------------------------------------------------------
1392 Routine: CmpMDB - (Compare Master Directory Block)
1394 Function: Compares the scavenger MDB info with the MDB on disk.
1396 Input: GPtr - pointer to scavenger global area
1398 Output: CmpMDB - function result:
1401 GPtr->VIStat - S_MDB flag set in VIStat if MDB is damaged.
1402 ------------------------------------------------------------------------------*/
1404 int CmpMDB( SGlobPtr GPtr
, HFSMasterDirectoryBlock
* mdbP
)
1410 short isMDBDamaged
= 0;
1413 GPtr
->TarID
= MDB_FNum
;
1414 vcb
= GPtr
->calculatedVCB
;
1417 * compare VCB info with MDB
1419 if ( mdbP
->drSigWord
!= vcb
->vcbSignature
) {
1420 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1421 plog( "\tinvalid MDB drSigWord \n" );
1424 if ( mdbP
->drCrDate
!= vcb
->vcbCreateDate
) {
1425 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1426 plog( "\tinvalid MDB drCrDate \n" );
1429 if ( mdbP
->drLsMod
!= vcb
->vcbModifyDate
) {
1430 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1431 plog( "\tinvalid MDB drLsMod \n" );
1434 if ( mdbP
->drAtrb
!= (UInt16
)vcb
->vcbAttributes
) {
1435 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1436 plog( "\tinvalid MDB drAtrb \n" );
1439 if ( mdbP
->drVBMSt
!= vcb
->vcbVBMSt
) {
1440 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1441 plog( "\tinvalid MDB drVBMSt \n" );
1444 if ( mdbP
->drNmAlBlks
!= vcb
->vcbTotalBlocks
) {
1445 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1446 plog( "\tinvalid MDB drNmAlBlks \n" );
1449 if ( mdbP
->drClpSiz
!= vcb
->vcbDataClumpSize
) {
1450 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1451 plog( "\tinvalid MDB drClpSiz \n" );
1454 if ( mdbP
->drAlBlSt
!= vcb
->vcbAlBlSt
) {
1455 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1456 plog( "\tinvalid MDB drAlBlSt \n" );
1459 if ( mdbP
->drNxtCNID
!= vcb
->vcbNextCatalogID
) {
1460 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1461 plog( "\tinvalid MDB drNxtCNID \n" );
1464 if ( CmpBlock( mdbP
->drVN
, vcb
->vcbVN
, mdbP
->drVN
[0]+1 ) ) {
1465 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1466 plog( "\tinvalid MDB drVN \n" );
1469 if ( mdbP
->drVolBkUp
!= vcb
->vcbBackupDate
) {
1470 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1471 plog( "\tinvalid MDB drVolBkUp \n" );
1474 if ( mdbP
->drVSeqNum
!= vcb
->vcbVSeqNum
) {
1475 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1476 plog( "\tinvalid MDB drVSeqNum \n" );
1479 if ( mdbP
->drWrCnt
!= vcb
->vcbWriteCount
) {
1480 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1481 plog( "\tinvalid MDB drWrCnt \n" );
1484 if ( mdbP
->drXTClpSiz
!= vcb
->vcbExtentsFile
->fcbClumpSize
) {
1485 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1486 plog( "\tinvalid MDB drXTClpSiz \n" );
1489 if ( mdbP
->drCTClpSiz
!= vcb
->vcbCatalogFile
->fcbClumpSize
) {
1490 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1491 plog( "\tinvalid MDB drCTClpSiz \n" );
1494 if ( mdbP
->drNmRtDirs
!= vcb
->vcbNmRtDirs
) {
1495 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1496 plog( "\tinvalid MDB drNmRtDirs \n" );
1499 if ( mdbP
->drFilCnt
!= vcb
->vcbFileCount
) {
1500 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1501 plog( "\tinvalid MDB drFilCnt \n" );
1504 if ( mdbP
->drDirCnt
!= vcb
->vcbFolderCount
) {
1505 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1506 plog( "\tinvalid MDB drDirCnt \n" );
1509 if ( CmpBlock(mdbP
->drFndrInfo
, vcb
->vcbFinderInfo
, 32 ) ) {
1510 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1511 plog( "\tinvalid MDB drFndrInfo \n" );
1516 * compare extent file allocation info with MDB
1518 fcbP
= vcb
->vcbExtentsFile
; /* compare PEOF for extent file */
1519 if ( mdbP
->drXTFlSize
!= fcbP
->fcbPhysicalSize
)
1522 WriteError ( GPtr
, E_MDBDamaged
, 3, 0 );
1524 for ( i
= 0; i
< GPtr
->numExtents
; i
++ )
1526 if ( (mdbP
->drXTExtRec
[i
].startBlock
!= fcbP
->fcbExtents16
[i
].startBlock
) ||
1527 (mdbP
->drXTExtRec
[i
].blockCount
!= fcbP
->fcbExtents16
[i
].blockCount
) )
1530 WriteError ( GPtr
, E_MDBDamaged
, 4, 0 );
1535 * compare catalog file allocation info with MDB
1537 fcbP
= vcb
->vcbCatalogFile
; /* compare PEOF for catalog file */
1538 if ( mdbP
->drCTFlSize
!= fcbP
->fcbPhysicalSize
)
1541 WriteError ( GPtr
, E_MDBDamaged
, 5, 0 );
1543 for ( i
= 0; i
< GPtr
->numExtents
; i
++ )
1545 if ( (mdbP
->drCTExtRec
[i
].startBlock
!= fcbP
->fcbExtents16
[i
].startBlock
) ||
1546 (mdbP
->drCTExtRec
[i
].blockCount
!= fcbP
->fcbExtents16
[i
].blockCount
) )
1549 WriteError ( GPtr
, E_MDBDamaged
, 6, 0 );
1553 if (isMDBDamaged
|| printMsg
) {
1554 GPtr
->VIStat
= GPtr
->VIStat
| S_MDB
;
1556 WriteError ( GPtr
, E_MDBDamaged
, 1, 0 );
1564 /*------------------------------------------------------------------------------
1566 Routine: CompareVolumeHeader - (Compare VolumeHeader Block)
1568 Function: Compares the scavenger VolumeHeader info with the VolumeHeader on disk.
1570 Input: GPtr - pointer to scavenger global area
1572 Output: CmpMDB - function result:
1575 GPtr->VIStat - S_MDB flag set in VIStat if MDB is damaged.
1576 ------------------------------------------------------------------------------*/
1578 OSErr
CompareVolumeHeader( SGlobPtr GPtr
, HFSPlusVolumeHeader
*volumeHeader
)
1583 UInt32 hfsPlusIOPosOffset
;
1584 UInt32 goodValue
, badValue
;
1585 char goodStr
[32], badStr
[32];
1589 vcb
= GPtr
->calculatedVCB
;
1590 GPtr
->TarID
= MDB_FNum
;
1592 hfsPlusIOPosOffset
= vcb
->vcbEmbeddedOffset
;
1594 goodValue
= badValue
= 0;
1598 // CatHChk will flag valence errors and display the good and bad values for
1599 // our file and folder counts. It will set S_Valence in CatStat when this
1600 // problem is detected. We do NOT want to flag the error here in that case
1601 // since the volume header counts cannot be trusted and it will lead to
1602 // confusing messages.
1603 if ( volumeHeader
->fileCount
!= vcb
->vcbFileCount
&&
1604 (GPtr
->CatStat
& S_Valence
) == 0 ) {
1605 fsckPrint(GPtr
->context
, E_FilCnt
);
1606 sprintf(goodStr
, "%u", vcb
->vcbFileCount
);
1607 sprintf(badStr
, "%u", volumeHeader
->fileCount
);
1608 fsckPrint(GPtr
->context
, E_BadValue
, goodStr
, badStr
);
1612 if ( volumeHeader
->folderCount
!= vcb
->vcbFolderCount
&&
1613 (GPtr
->CatStat
& S_Valence
) == 0 ) {
1614 fsckPrint(GPtr
->context
, E_DirCnt
);
1615 sprintf(goodStr
, "%u", vcb
->vcbFolderCount
);
1616 sprintf(badStr
, "%u", volumeHeader
->folderCount
);
1617 fsckPrint(GPtr
->context
, E_BadValue
, goodStr
, badStr
);
1622 if (volumeHeader
->freeBlocks
!= vcb
->vcbFreeBlocks
) {
1623 fsckPrint(GPtr
->context
, E_FreeBlocks
);
1624 sprintf(goodStr
, "%u", vcb
->vcbFreeBlocks
);
1625 sprintf(badStr
, "%u", volumeHeader
->freeBlocks
);
1626 fsckPrint(GPtr
->context
, E_BadValue
, goodStr
, badStr
);
1630 if ( volumeHeader
->catalogFile
.clumpSize
!= vcb
->vcbCatalogFile
->fcbClumpSize
) {
1631 fsckPrint(GPtr
->context
, E_InvalidClumpSize
);
1632 sprintf(goodStr
, "%u", vcb
->vcbCatalogFile
->fcbClumpSize
);
1633 sprintf(badStr
, "%u", volumeHeader
->catalogFile
.clumpSize
);
1634 fsckPrint(GPtr
->context
, E_BadValue
, goodStr
, badStr
);
1638 if ( volumeHeader
->signature
!= kHFSPlusSigWord
&&
1639 volumeHeader
->signature
!= kHFSXSigWord
) {
1640 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1641 plog( "\tinvalid VHB signature \n" );
1644 /* From HFS Plus Volume Format Specification (TN1150), "It is acceptable
1645 * for a bit in encodingsBitmap to be set even though no names on the
1646 * volume use that encoding". Therefore we do not report extra bits set in
1647 * on-disk encodingsBitmap as error but will repair it silently if any other
1648 * repairs are made. We complain about extra bits cleared in
1649 * on-disk encodingsBitmap when compared to calculated encodingsBitmap.
1651 if ( (volumeHeader
->encodingsBitmap
& vcb
->vcbEncodingsBitmap
)
1652 != vcb
->vcbEncodingsBitmap
) {
1653 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1654 plog( "\tinvalid VHB encodingsBitmap, disk=0x%qx calculated=0x%qx \n", volumeHeader
->encodingsBitmap
, vcb
->vcbEncodingsBitmap
);
1657 if ( (UInt16
) (hfsPlusIOPosOffset
/512) != vcb
->vcbAlBlSt
) {
1658 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1659 plog( "\tinvalid VHB AlBlSt \n" );
1662 if ( volumeHeader
->createDate
!= vcb
->vcbCreateDate
) {
1663 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1664 plog( "\tinvalid VHB createDate \n" );
1667 if ( volumeHeader
->modifyDate
!= vcb
->vcbModifyDate
) {
1668 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1669 plog( "\tinvalid VHB modifyDate \n" );
1672 if ( volumeHeader
->backupDate
!= vcb
->vcbBackupDate
) {
1673 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1674 plog( "\tinvalid VHB backupDate \n" );
1677 if ( volumeHeader
->checkedDate
!= vcb
->vcbCheckedDate
) {
1678 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1679 plog( "\tinvalid VHB checkedDate \n" );
1682 if ( volumeHeader
->rsrcClumpSize
!= vcb
->vcbRsrcClumpSize
) {
1683 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1684 plog( "\tinvalid VHB rsrcClumpSize (VH=%u, vcb=%u)\n", volumeHeader
->rsrcClumpSize
, vcb
->vcbRsrcClumpSize
);
1687 if ( volumeHeader
->dataClumpSize
!= vcb
->vcbDataClumpSize
) {
1688 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1689 plog( "\tinvalid VHB dataClumpSize \n" );
1692 if ( volumeHeader
->nextCatalogID
!= vcb
->vcbNextCatalogID
&&
1693 (volumeHeader
->attributes
& kHFSCatalogNodeIDsReused
) == 0) {
1694 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1695 plog( "\tinvalid VHB nextCatalogID \n" );
1698 if ( volumeHeader
->writeCount
!= vcb
->vcbWriteCount
) {
1699 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1700 plog( "\tinvalid VHB writeCount \n" );
1703 if ( volumeHeader
->nextAllocation
!= vcb
->vcbNextAllocation
) {
1704 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1705 plog( "\tinvalid VHB nextAllocation \n" );
1708 if ( volumeHeader
->totalBlocks
!= vcb
->vcbTotalBlocks
) {
1709 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1710 plog( "\tinvalid VHB totalBlocks \n" );
1713 if ( volumeHeader
->blockSize
!= vcb
->vcbBlockSize
) {
1714 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1715 plog( "\tinvalid VHB blockSize \n" );
1718 if ( volumeHeader
->attributes
!= vcb
->vcbAttributes
) {
1719 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1720 plog( "\tinvalid VHB attributes \n" );
1723 if ( volumeHeader
->extentsFile
.clumpSize
!= vcb
->vcbExtentsFile
->fcbClumpSize
) {
1724 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1725 plog( "\tinvalid VHB extentsFile.clumpSize \n" );
1728 if ( volumeHeader
->allocationFile
.clumpSize
!= vcb
->vcbAllocationFile
->fcbClumpSize
) {
1729 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1730 plog( "\tinvalid VHB allocationFile.clumpSize \n" );
1733 if ( (vcb
->vcbAttributesFile
!= NULL
) &&
1734 (volumeHeader
->attributesFile
.clumpSize
!= vcb
->vcbAttributesFile
->fcbClumpSize
)) {
1735 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1736 plog( "\tinvalid VHB attributesFile.clumpSize \n" );
1739 if ( CmpBlock( volumeHeader
->finderInfo
, vcb
->vcbFinderInfo
, sizeof(vcb
->vcbFinderInfo
) ) ) {
1740 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1741 plog( "\tinvalid VHB finderInfo \n" );
1746 * compare extent file allocation info with VolumeHeader
1748 fcbP
= vcb
->vcbExtentsFile
;
1749 if ( (UInt64
)volumeHeader
->extentsFile
.totalBlocks
* (UInt64
)vcb
->vcbBlockSize
!= fcbP
->fcbPhysicalSize
)
1752 WriteError ( GPtr
, E_VolumeHeaderDamaged
, 3, 0 );
1754 for ( i
=0; i
< GPtr
->numExtents
; i
++ )
1756 if ( (volumeHeader
->extentsFile
.extents
[i
].startBlock
!= fcbP
->fcbExtents32
[i
].startBlock
) ||
1757 (volumeHeader
->extentsFile
.extents
[i
].blockCount
!= fcbP
->fcbExtents32
[i
].blockCount
) )
1760 WriteError ( GPtr
, E_VolumeHeaderDamaged
, 4, 0 );
1765 * compare catalog file allocation info with MDB
1767 fcbP
= vcb
->vcbCatalogFile
; /* compare PEOF for catalog file */
1768 if ( (UInt64
)volumeHeader
->catalogFile
.totalBlocks
* (UInt64
)vcb
->vcbBlockSize
!= fcbP
->fcbPhysicalSize
)
1771 WriteError ( GPtr
, E_VolumeHeaderDamaged
, 5, 0 );
1773 for ( i
=0; i
< GPtr
->numExtents
; i
++ )
1775 if ( (volumeHeader
->catalogFile
.extents
[i
].startBlock
!= fcbP
->fcbExtents32
[i
].startBlock
) ||
1776 (volumeHeader
->catalogFile
.extents
[i
].blockCount
!= fcbP
->fcbExtents32
[i
].blockCount
) )
1779 WriteError ( GPtr
, E_VolumeHeaderDamaged
, 6, 0 );
1785 * compare bitmap file allocation info with MDB
1787 fcbP
= vcb
->vcbAllocationFile
;
1788 if ( (UInt64
)volumeHeader
->allocationFile
.totalBlocks
* (UInt64
)vcb
->vcbBlockSize
!= fcbP
->fcbPhysicalSize
)
1791 WriteError ( GPtr
, E_VolumeHeaderDamaged
, 7, 0 );
1793 for ( i
=0; i
< GPtr
->numExtents
; i
++ )
1795 if ( (volumeHeader
->allocationFile
.extents
[i
].startBlock
!= fcbP
->fcbExtents32
[i
].startBlock
) ||
1796 (volumeHeader
->allocationFile
.extents
[i
].blockCount
!= fcbP
->fcbExtents32
[i
].blockCount
) )
1799 WriteError ( GPtr
, E_VolumeHeaderDamaged
, 8, 0 );
1803 if (isVHDamaged
|| printMsg
) {
1804 GPtr
->VIStat
= GPtr
->VIStat
| S_MDB
;
1806 WriteError ( GPtr
, E_VolumeHeaderDamaged
, 2, 0 );