|
|
1.1 ! root 1: /* ! 2: * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. ! 3: * ! 4: * @APPLE_LICENSE_HEADER_START@ ! 5: * ! 6: * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights ! 7: * Reserved. This file contains Original Code and/or Modifications of ! 8: * Original Code as defined in and that are subject to the Apple Public ! 9: * Source License Version 1.1 (the "License"). You may not use this file ! 10: * except in compliance with the License. Please obtain a copy of the ! 11: * License at http://www.apple.com/publicsource and read it before using ! 12: * this file. ! 13: * ! 14: * The Original Code and all software distributed under the License are ! 15: * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER ! 16: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, ! 17: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, ! 18: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the ! 19: * License for the specific language governing rights and limitations ! 20: * under the License. ! 21: * ! 22: * @APPLE_LICENSE_HEADER_END@ ! 23: */ ! 24: ! 25: /* ! 26: File: VolumeCheck.c ! 27: ! 28: Contains: Consistency checking code for HFS and HFS Plus volumes. ! 29: This code was based off MtCheck (in TFSVol.a). ! 30: ! 31: Version: HFS Plus 1.0 ! 32: ! 33: Copyright: � 1995-1998 by Apple Computer, Inc., all rights reserved. ! 34: ! 35: File Ownership: ! 36: ! 37: DRI: Don Brady ! 38: ! 39: Other Contact: xxx put other contact here xxx ! 40: ! 41: Technology: xxx put technology here xxx ! 42: ! 43: Writers: ! 44: ! 45: (NG) Nitin Ganatra ! 46: (DSH) Deric Horn ! 47: (msd) Mark Day ! 48: (djb) Don Brady ! 49: ! 50: Change History (most recent first): ! 51: ! 52: <Rhap> 6/3/98 djb Switch RenameCatalogNode to MoveRenameCatalogNode. ! 53: <Rhap> 4/2/98 djb FixOrphanFileThreads only works with MacOS. ! 54: <Rhap> 03/31/98 djb Sync up with final HFSVolumes.h header file. ! 55: <Rhap> 03/26/98 djb Minor cleanup. ! 56: <Rhap> 03/20/98 djb Add leaf-order checking back in. Set USE_BTREE_SCANNER to true ! 57: to enable disk order checking. Add VOLUME_CHECK_LOG messages. ! 58: Use PascalBinaryCompare to compare volume names. ! 59: ! 60: <CS16> 11/26/97 djb Radar #2003656 (and others) - remove B-tree header fixing. ! 61: <15> 10/31/97 DSH Modify so DFA can call without actually writing to the disk, ! 62: added consistencyStatus parameter to MountCheck and fixed bug in ! 63: freeBlocks calculation in CheckBitmapAndHeader(). ! 64: <CS14> 10/23/97 msd Bug 1685113. Fix incorrect volume header creation dates. ! 65: <CS13> 9/18/97 NG Don't call Gestalt(osAttr) to check if Temp Mem is available. ! 66: Instead, use GetExpandMemProcessMgrExists ! 67: <CS12> 9/7/97 djb Turn off DebugStr at MountCheck's ErrorExit. ! 68: <CS11> 9/5/97 msd Added CheckBitmapAndHeader to account for space used by the ! 69: volume header, alternate volume header, and the allocation file ! 70: (bitmap) itself. ! 71: <CS10> 9/4/97 msd Added CheckAttributes to account for space used by large ! 72: attribute values (the ones that occupy extents). ! 73: <CS9> 8/29/97 djb Fixed bug in CheckExtentsOverflow (radar #1675472 and #1675417). ! 74: <CS8> 8/19/97 msd In CheckCatalog, don't increment the counts associated with the ! 75: root directory twice; only do it in the bulk scan. When counting ! 76: folders, don't count the root directory itself. ! 77: <CS7> 8/11/97 djb Changed vcb check of largestCNID from "<" to "<=" (radar ! 78: #1670614). Use thread records when establishing the largest ! 79: CNID. ! 80: <CS6> 7/30/97 DSH Casting for SC, needed for DFA compiles ! 81: <CS5> 7/28/97 msd Use fast new B-Tree scanner. CheckCatalog was using a variable ! 82: before it was defined; the valance computations should have been ! 83: wrong. ! 84: <CS4> 7/16/97 DSH FilesInternal.i renamed FileMgrInternal.i to avoid name ! 85: collision ! 86: <CS3> 7/8/97 DSH Loading PrecompiledHeaders from define passed in on C line ! 87: <CS2> 6/12/97 djb Get in sync with HFS Plus format changes. ! 88: <CS1> 4/25/97 djb first checked in ! 89: ! 90: <HFS17> 4/16/97 djb Remove unused variables to prevent warnings. ! 91: <HFS16> 4/16/97 djb Always use new B-tree code. ! 92: <HFS15> 4/11/97 DSH use extended VCB fields catalogRefNum, and extentsRefNum. ! 93: <HFS14> 4/4/97 djb Get in sync with volume format changes. ! 94: <HFS13> 2/19/97 djb Use real B-tree control blocks. Removed caching code. ! 95: <HFS12> 1/17/97 msd Fix an illegal cast from one structure type to another in ! 96: CheckExtentsOverflow. ! 97: <HFS11> 1/16/97 djb Fixing extents overflow check for HFS+ volumes. ! 98: <HFS10> 1/15/97 djb Added support for checking HFS+ volumes. ! 99: <HFS9> 1/3/97 djb Added support for large keys.Integrated HFSVolumesPriv.h ! 100: changes. ! 101: <HFS8> 12/23/96 djb Fixed bug in CheckExtentsOverflow (bad key). ! 102: <HFS8> 12/23/96 djb Fixed bug in CheckExtentsOverflow (key was wrong). ! 103: <HFS7> 12/19/96 DSH All refs to VCB are now refs to ExtendedVCB ! 104: <HFS6> 12/19/96 djb Updated for new B-tree Manager interface. ! 105: <HFS5> 12/13/96 djb Use new Catalog SPI for GetCatalogNode. ! 106: <HFS4> 12/4/96 DSH Precompiled Headers ! 107: <HFS3> 11/21/96 msd Removed definition of kHFSSignature since it is now in ! 108: FilesInternal.h. ! 109: <HFS2> 11/19/96 DSH Removed FilesPriv.h, moved MountCheck prototype to ! 110: FilesInternal.h. ! 111: <HFS1> 11/13/96 djb first checked in ! 112: ! 113: To do: ! 114: - If a cnid of 5 is found in extents btree make sure vcb attribute is set correctly ! 115: */ ! 116: #define USE_BTREE_SCANNER 0 ! 117: ! 118: #if ( PRAGMA_LOAD_SUPPORTED ) ! 119: #pragma load PrecompiledHeaders ! 120: #else ! 121: #if TARGET_OS_MAC ! 122: #include <Gestalt.h> ! 123: #include <Errors.h> ! 124: #include <ExpandMemPriv.h> ! 125: #include <Files.h> ! 126: #include <StringComparePriv.h> ! 127: #include <Types.h> ! 128: #else ! 129: #include "../headers/system/MacOSTypes.h" ! 130: #include "../headers/system/MacOSStubs.h" ! 131: #endif /* TARGET_OS_MAC */ ! 132: #endif /* PRAGMA_LOAD_SUPPORTED */ ! 133: ! 134: #include "../headers/FileMgrInternal.h" ! 135: #include "../headers/HFSBtreesPriv.h" ! 136: #include "../headers/HFSVolumes.h" ! 137: #include "../headers/BTreesPrivate.h" ! 138: #include "../headers/BTreeScanner.h" ! 139: ! 140: ! 141: enum { ! 142: kTypeDataFork = 0x00, ! 143: kTypeResourceFork = 0xFF, ! 144: ! 145: kVCBAttrLocked = 0x8080, // volume locked (software or hardware) ! 146: kVCBFlagsVolumeDirty = 0x8000 ! 147: }; ! 148: ! 149: ! 150: enum { ! 151: kBTreePrevious = -1, ! 152: kBTreeCurrent = 0, ! 153: kBTreeNext = 1 ! 154: }; ! 155: ! 156: ! 157: enum { ! 158: kHFSOrphanedExtents = 0x00000001, ! 159: kHFSOverlapingExtents = 0x00000002, ! 160: kHFSCatalogBTreeLoop = 0x00000004, ! 161: kHFSExtentsBTreeLoop = 0x00000008, ! 162: kHFSOrphanedThreadRecords = 0x00000010, ! 163: kHFSMissingThreadRecords = 0x00000020, ! 164: kHFSInvalidPEOF = 0x00000040, ! 165: kHFSInvalidLEOF = 0x00000080, ! 166: kHFSInvalidValence = 0x00000100, ! 167: kHFSInvalidBTreeHeader = 0x00000200, ! 168: kHFSInvalidCatalogRecordType = 0x00000400, ! 169: kHFSInconsistentVolumeName = 0x00000800, ! 170: ! 171: kHFSMinorRepairsWereMade = 0x80000000 ! 172: }; ! 173: ! 174: enum { ! 175: kMinimumBtreeNodesForCache = 100 ! 176: }; ! 177: ! 178: #define kMDBValidAttributesMask 0x8780 ! 179: ! 180: #define badCatalogErr -1311 ! 181: ! 182: ! 183: typedef struct { ! 184: Handle tempBufferHand; ! 185: UInt32 tempBufferSize; ! 186: Ptr savedBufPtr; ! 187: Ptr savedGetNode; ! 188: Ptr savedRelNode; ! 189: Ptr savedNodeCache; ! 190: } BTreeState; ! 191: ! 192: ! 193: // Macros ! 194: ! 195: //void M_ExitOnError(OSErr result); ! 196: //#define M_ExitOnError( result ) if ( ( result ) != noErr ) goto ErrorExit; else ; ! 197: ! 198: BTreeControlBlock* GetBTreeControlBlock(short refNum); ! 199: #define GetBTreeControlBlock(refNum) ((BTreeControlBlock*) GetFileControlBlock((refNum))->fcbBTCBPtr) ! 200: ! 201: Ptr LMGetVector(Ptr vector); ! 202: #define LMGetVector(V) (*(Ptr*) (V)) ! 203: ! 204: void LMSetVector(Ptr vector, Ptr address); ! 205: #define LMSetVector(V,A) (*(Ptr *) (V) = (A)) ! 206: ! 207: Boolean HFSEqualString(ConstStr255Param str1, ConstStr255Param str2); ! 208: #define HFSEqualString(s1, s2) PascalBinaryCompare((s1), (s2)) ! 209: ! 210: #if DIAGNOSTIC ! 211: #ifdef KERNEL ! 212: #include <sys/systm.h> ! 213: #define PRINTIT kprintf ! 214: #else /* !KERNEL */ ! 215: #define PRINTIT printf ! 216: #endif /* KERNEL */ ! 217: #define VOLUME_CHECK_LOG(s) PRINTIT(" MountCheck: %s\n",(s)) ! 218: #else ! 219: #define VOLUME_CHECK_LOG(s) ! 220: #endif ! 221: ! 222: ! 223: #if TARGET_OS_MAC ! 224: #define GET_SCANNER_BUFFER() LMGetFSVars()->gAttributesBuffer ! 225: #define GET_SCANNER_BUFFER_SIZE() LMGetFSVars()->gAttributesBufferSize ! 226: #endif ! 227: ! 228: ! 229: #if TARGET_OS_RHAPSODY ! 230: #define GET_SCANNER_BUFFER() NULL ! 231: #define GET_SCANNER_BUFFER_SIZE() 0 ! 232: #endif ! 233: ! 234: ! 235: // local prototypes ! 236: ! 237: static OSErr CheckCatalog (ExtendedVCB* volume, UInt32* freeBlocks, UInt32 xCatalogBlocks, UInt32* consistencyStatus ); ! 238: ! 239: static OSErr CheckExtentsOverflow ( ExtendedVCB* volume, UInt32 *freeBlocks, UInt32 *xCatalogBlocks, UInt32* consistencyStatus ); ! 240: ! 241: static OSErr CheckAttributes ( ExtendedVCB *volume, UInt32 *freeBlocks, UInt32 *consistencyStatus ); ! 242: ! 243: static OSErr CheckBitmapAndHeader ( ExtendedVCB *volume, UInt32 *freeBlocks, UInt32 *consistencyStatus ); ! 244: ! 245: static void VolumeBitMapCheckExtents ( ExtendedVCB* volume, HFSPlusExtentDescriptor* theExtents, UInt32* consistencyStatus ); ! 246: ! 247: static OSErr CheckCreateDate (ExtendedVCB* volume, UInt32* consistencyStatus ); ! 248: ! 249: static Boolean ValidPEOF ( ExtendedVCB* volume, CatalogRecord *file ); ! 250: ! 251: #if TARGET_OS_MAC ! 252: static OSErr FixOrphanFileThreads ( ExtendedVCB* volume, UInt32 maxRecords, UInt32 *repairsDone); ! 253: #else ! 254: #define FixOrphanFileThreads(v, m, f) 0 ! 255: #endif ! 256: ! 257: #if TARGET_OS_RHAPSOY ! 258: static Handle GetTempBuffer (Size size); ! 259: #endif /* TARGET_OS_MAC */ ! 260: ! 261: static UInt32 CountExtentBlocks (HFSPlusExtentRecord extents, Boolean hfsPlus); ! 262: ! 263: static void ConvertToHFSPlusExtent (const HFSExtentRecord oldExtents, HFSPlusExtentRecord newExtents); ! 264: ! 265: static OSErr GetFileExtentRecord( const ExtendedVCB *vcb, const FCB *fcb, HFSPlusExtentRecord extents); ! 266: ! 267: // glue prototypes ! 268: ! 269: extern OSErr CacheReadInPlace(ExtendedVCB *volume, HIOParam *iopb, UInt32 currentPosition, UInt32 maximumBytes, UInt32 *actualBytes); ! 270: ! 271: extern pascal Ptr LowerBufPtr(long size); ! 272: ! 273: ! 274: // 68K prototypes ! 275: ! 276: extern OSErr FastGetNode(void); ! 277: ! 278: extern OSErr FastRelNode(void); ! 279: ! 280: ! 281: /*------------------------------------------------------------------------------- ! 282: ! 283: Routine: MountCheck ! 284: ! 285: Function: Do a quick check of the volume meta-data to make sure that ! 286: it is consistent. Minor inconsistencies in the MDB or volume ! 287: bitmap will be repaired all other inconsistencies will be ! 288: detected and reported in the consistencyStatus output. ! 289: ! 290: This check will terminated if any unexpected BTree errors ! 291: are encountered. ! 292: ! 293: Input: volume - pointer to volume control block ! 294: consistencyStatus - ???? ! 295: ! 296: Output: none ! 297: ! 298: Result: noErr - success ! 299: ! 300: -------------------------------------------------------------------------------*/ ! 301: OSErr ! 302: MountCheck( ExtendedVCB* volume, UInt32 *consistencyStatus ) ! 303: { ! 304: OSErr result; ! 305: UInt32 freeBlocks; ! 306: UInt32 bitmapFreeBlocks; ! 307: UInt32 xCatalogBlocks; ! 308: ! 309: ! 310: *consistencyStatus = 0; ! 311: freeBlocks = volume->totalBlocks; // initially all blocks are free ! 312: ! 313: if (volume->vcbSigWord == kHFSPlusSigWord) ! 314: { ! 315: //--- make sure the volume header's create date matches the MDB's create date ! 316: VOLUME_CHECK_LOG("checking HFS Plus wrapper's create date..."); ! 317: result = CheckCreateDate (volume, consistencyStatus); ! 318: M_ExitOnError(result); ! 319: } ! 320: ! 321: //--- make sure the allocation file, volume header, and alternate volume header are allocated ! 322: VOLUME_CHECK_LOG("checking allocation file..."); ! 323: result = CheckBitmapAndHeader ( volume, &freeBlocks, consistencyStatus ); ! 324: M_ExitOnError(result); ! 325: ! 326: //--- do a quick consistency check of the Extents Overflow b-tree ---------------------------- ! 327: VOLUME_CHECK_LOG("checking extents overflow b-tree..."); ! 328: result = CheckExtentsOverflow(volume, &freeBlocks, &xCatalogBlocks, consistencyStatus); ! 329: M_ExitOnError(result); ! 330: ! 331: //--- do a quick consistency check of the Catalog b-tree ------------------------------------ ! 332: VOLUME_CHECK_LOG("checking catalog b-tree..."); ! 333: result = CheckCatalog( volume, &freeBlocks, xCatalogBlocks, consistencyStatus ); ! 334: M_ExitOnError( result ); ! 335: ! 336: //--- do a quick consistency check of the Attributes b-tree ------------------------------------ ! 337: result = CheckAttributes( volume, &freeBlocks, consistencyStatus ); ! 338: M_ExitOnError( result ); ! 339: ! 340: if( freeBlocks != volume->freeBlocks && volume->vcbAtrb & kVCBAttrLocked ) ! 341: { ! 342: *consistencyStatus |= kHFSMinorRepairsWereMade; ! 343: VOLUME_CHECK_LOG("repairing free block count"); ! 344: } ! 345: ! 346: (void) UpdateFreeCount( volume ); // update freeBlocks from volume bitmap ! 347: ! 348: // make sure there are not any doubly allocated blocks... ! 349: // we are not concerned with orphaned blocks (since this is not fatal) ! 350: ! 351: bitmapFreeBlocks = volume->freeBlocks; ! 352: ! 353: if ( freeBlocks < bitmapFreeBlocks) // there are doubly allocated blocks! ! 354: { ! 355: *consistencyStatus |= kHFSOverlapingExtents; ! 356: VOLUME_CHECK_LOG("uh oh, there are doubly allocated file blocks!"); ! 357: goto ErrorExit; ! 358: } ! 359: else if ( freeBlocks > bitmapFreeBlocks) // there are orphaned blocks ! 360: { ! 361: *consistencyStatus |= kHFSOrphanedExtents; ! 362: VOLUME_CHECK_LOG("orphaned volume bitmap blocks detected"); ! 363: } ! 364: ! 365: return noErr; ! 366: ! 367: ErrorExit: ! 368: ! 369: VOLUME_CHECK_LOG("a fatal inconsistency was found! :( "); ! 370: ! 371: return badMDBErr; ! 372: } ! 373: ! 374: ! 375: /*------------------------------------------------------------------------------- ! 376: ! 377: Routine: VolumeCheckCatalog ! 378: ! 379: Function: Make sure the name of the root matches the volume name in the MDB. ! 380: Make sure the volume bitmap reflects the allocations of all file ! 381: extents. ! 382: ! 383: Assumption: ! 384: ! 385: Input: volume - pointer to volume control block ! 386: freeBlocks ! 387: xCatalogBlocks ! 388: consistencyStatus ! 389: ! 390: Output: none ! 391: ! 392: Result: noErr - success ! 393: ! 394: -------------------------------------------------------------------------------*/ ! 395: static OSErr ! 396: CheckCatalog ( ExtendedVCB *volume, UInt32 *freeBlocks, UInt32 xCatalogBlocks, UInt32 *consistencyStatus ) ! 397: { ! 398: UInt32 maxRecords; ! 399: UInt32 largestCNID; ! 400: UInt32 fileCount; ! 401: UInt16 rootFileCount; //XXX can this overflow on HFS Plus volumes? ! 402: UInt16 rootDirectoryCount; //XXX can this overflow on HFS Plus volumes? ! 403: UInt32 directoryCount; ! 404: UInt32 fileThreads, directoryThreads; ! 405: UInt32 fileThreadsExpected; ! 406: UInt32 catalogValence; ! 407: UInt32 recordsFound; ! 408: UInt32 catalogBlocks; ! 409: OSErr result; ! 410: CatalogNodeData nodeData; ! 411: CatalogRecord *record; ! 412: CatalogKey *key; ! 413: FSSpec spec; ! 414: UInt16 recordSize; ! 415: BTreeControlBlock* btree; ! 416: UInt32 hint; ! 417: FCB* file; ! 418: HFSPlusExtentRecord extentRecord; ! 419: BTScanState scanState; ! 420: Boolean hfsPlus = (volume->vcbSigWord == kHFSPlusSigWord); ! 421: UInt16 operation; ! 422: BTreeIterator btreeIterator; ! 423: FSBufferDescriptor btRecord; ! 424: CatalogRecord catalogRecord; ! 425: ! 426: fileThreads = fileThreadsExpected = directoryThreads = 0; ! 427: fileCount = directoryCount = 0; ! 428: rootFileCount = rootDirectoryCount = 0; ! 429: catalogValence = 0; ! 430: recordsFound = 0; ! 431: largestCNID = kHFSFirstUserCatalogNodeID; ! 432: ! 433: file = GetFileControlBlock(volume->catalogRefNum); ! 434: ! 435: result = GetFileExtentRecord(volume, file, extentRecord); ! 436: M_ExitOnError(result); ! 437: ! 438: catalogBlocks = CountExtentBlocks(extentRecord, hfsPlus); ! 439: *freeBlocks -= catalogBlocks; ! 440: VolumeBitMapCheckExtents(volume, extentRecord, consistencyStatus); ! 441: ! 442: ! 443: // Read in the root directory's record so we can perform some special checks ! 444: hint = 0; ! 445: result = GetCatalogNode(volume, kHFSRootFolderID, NULL, kNoHint, &spec, &nodeData, &hint); ! 446: M_ExitOnError(result); ! 447: ! 448: if ( DEBUG_BUILD && nodeData.nodeID != kHFSRootFolderID ) // works for small and large folders ! 449: DebugStr("\p CheckCatalog: nodeData.nodeID != kHFSRootFolderID"); ! 450: ! 451: // We'll see the root directory's record later, during the full BTree scan, so don't ! 452: // update the counts here. ! 453: // ++recordsFound; ! 454: // catalogValence += nodeData.valence; ! 455: ! 456: /* UUU */ ! 457: // make sure the name of the root directory matches the volume name (HFS only) ! 458: if ( !hfsPlus && false /*&& !HFSEqualString( spec.name, volume->vcbVN )*/ ) ! 459: { ! 460: if ( volume->vcbAtrb & kVCBAttrLocked ) ! 461: { ! 462: *consistencyStatus |= kHFSInconsistentVolumeName; ! 463: } ! 464: else // try and repair it ! 465: { ! 466: if (strlen( volume->vcbVN ) > 0) // check length of volume name ! 467: { ! 468: if (volume->vcbVN[0] > kHFSMaxVolumeNameChars) // if name is too long truncate it ! 469: volume->vcbVN[0] = kHFSMaxVolumeNameChars; ! 470: ! 471: if ( (!FORDISKFIRSTAID) || (GetDFAStage() == kRepairStage) ) ! 472: { ! 473: // the length of volume name is OK, rename the root directory ! 474: result = MoveRenameCatalogNode(volume, kHFSRootFolderID, NULL, hint, kHFSRootFolderID, volume->vcbVN, &hint); ! 475: M_ExitOnError(result); ! 476: } ! 477: *consistencyStatus |= kHFSMinorRepairsWereMade; ! 478: ! 479: // now we must reset for btree search below! ! 480: result = GetCatalogNode(volume, kHFSRootFolderID, NULL, kNoHint, &spec, &nodeData, &hint); ! 481: M_ExitOnError(result); ! 482: } ! 483: else if ( strlen( spec.name ) > 0 ) // check root name length ! 484: { ! 485: if ( spec.name[0] > kHFSMaxVolumeNameChars ) // if name is too long truncate it ! 486: spec.name[0] = kHFSMaxVolumeNameChars; ! 487: ! 488: // the root name length is OK, change the volume name to match root name ! 489: BlockMoveData( spec.name, volume->vcbVN, spec.name[0]+1); ! 490: volume->vcbFlags |= kVCBFlagsVolumeDirty; ! 491: *consistencyStatus |= kHFSMinorRepairsWereMade; ! 492: } ! 493: } ! 494: VOLUME_CHECK_LOG("inconsistent volume name (hfs disk)"); ! 495: } ! 496: ! 497: // make sure name locked bit is not set ! 498: // if (record.hfsFolder.finderDirInfo.frFlags & kNameLocked) //XXX need to use node data, not "record" ! 499: // { ! 500: // } ! 501: ! 502: // Make a conservative estimate of the upper limit of B*-Tree records that ! 503: // could conceivably be encountered in a scan of all the leaf nodes. ! 504: maxRecords = (file->fcbEOF) / sizeof(HFSCatalogFolder); ! 505: ! 506: if (USE_BTREE_SCANNER) // scan the tree in physical order (faster) ! 507: { ! 508: result = BTScanInitialize(file, 0, 0, 0, GET_SCANNER_BUFFER(), GET_SCANNER_BUFFER_SIZE(), &scanState); ! 509: if (result != noErr) ! 510: goto ErrorExit; ! 511: } ! 512: else // scan in leaf node order (slower but more reliable) ! 513: { ! 514: operation = kBTreeFirstRecord; // first leaf record ! 515: ! 516: key = (CatalogKey*) &btreeIterator.key; ! 517: ! 518: btRecord.bufferAddress = record = &catalogRecord; ! 519: btRecord.itemCount = 1; ! 520: btRecord.itemSize = sizeof(catalogRecord); ! 521: } ! 522: ! 523: ! 524: // visit all the leaf node data records in the catalog ! 525: while ( --maxRecords > 0 ) ! 526: { ! 527: if (USE_BTREE_SCANNER) ! 528: result = BTScanNextRecord(&scanState, false, (void**)&key, (void**)&record, (UInt32*) &recordSize); ! 529: else ! 530: result = BTIterateRecord(file, operation, &btreeIterator, &btRecord, &recordSize); ! 531: ! 532: if ( result != noErr ) ! 533: break; ! 534: ! 535: ++recordsFound; ! 536: operation = kBTreeNextRecord; ! 537: ! 538: switch (record->recordType) ! 539: { ! 540: case kHFSFolderRecord: ! 541: { ! 542: catalogValence += record->hfsFolder.valence; ! 543: ! 544: // Count all directories except the root itself ! 545: if (key->hfs.parentID != kHFSRootParentID) ! 546: ++directoryCount; ! 547: ! 548: // Count directories directly inside the root ! 549: if (key->hfs.parentID == kHFSRootFolderID) ! 550: ++rootDirectoryCount; ! 551: ! 552: if (record->hfsFolder.folderID > largestCNID) ! 553: largestCNID = record->hfsFolder.folderID; ! 554: break; ! 555: } ! 556: ! 557: case kHFSPlusFolderRecord: ! 558: { ! 559: catalogValence += record->hfsPlusFolder.valence; ! 560: ! 561: // Count all directories except the root itself ! 562: if (key->hfsPlus.parentID != kHFSRootParentID) ! 563: ++directoryCount; ! 564: ! 565: // Count directories directly inside the root ! 566: if (key->hfsPlus.parentID == kHFSRootFolderID) ! 567: ++rootDirectoryCount; ! 568: ! 569: if (record->hfsPlusFolder.folderID > largestCNID) ! 570: largestCNID = record->hfsPlusFolder.folderID; ! 571: break; ! 572: } ! 573: ! 574: case kHFSFileRecord: ! 575: { ! 576: ++fileCount; ! 577: if ( key->hfs.parentID == kHFSRootFolderID ) ! 578: ++rootFileCount; ! 579: ! 580: if ( record->hfsFile.fileID > largestCNID ) ! 581: largestCNID = record->hfsFile.fileID; ! 582: ! 583: if ( record->hfsFile.flags & kHFSThreadExistsMask ) ! 584: ++fileThreadsExpected; ! 585: ! 586: // check the blocks allocated to this file (both forks) ! 587: ! 588: ConvertToHFSPlusExtent(record->hfsFile.dataExtents, extentRecord); ! 589: *freeBlocks -= CountExtentBlocks(extentRecord, false); ! 590: VolumeBitMapCheckExtents(volume, extentRecord, consistencyStatus); ! 591: ! 592: ConvertToHFSPlusExtent(record->hfsFile.rsrcExtents, extentRecord); ! 593: *freeBlocks -= CountExtentBlocks(extentRecord, false); ! 594: VolumeBitMapCheckExtents(volume, extentRecord, consistencyStatus); ! 595: ! 596: // check the LEOF and PEOF (both forks) ! 597: ! 598: if ( (record->hfsFile.rsrcLogicalSize > record->hfsFile.rsrcPhysicalSize) || ! 599: (record->hfsFile.dataLogicalSize > record->hfsFile.dataPhysicalSize) ) ! 600: { ! 601: *consistencyStatus |= kHFSInvalidLEOF; ! 602: VOLUME_CHECK_LOG("inconsistent file leof (not fixed)"); ! 603: } ! 604: ! 605: if ( !ValidPEOF(volume, record) ) ! 606: { ! 607: *consistencyStatus |= kHFSInvalidPEOF; ! 608: VOLUME_CHECK_LOG("inconsistent file peof (not fixed)"); ! 609: } ! 610: break; ! 611: } ! 612: ! 613: case kHFSPlusFileRecord: ! 614: { ! 615: UInt32 blockSize = volume->blockSize; ! 616: ! 617: ++fileCount; ! 618: ++fileThreadsExpected; // HFS Plus files always have thread records ! 619: ! 620: if ( key->hfsPlus.parentID == kHFSRootFolderID ) ! 621: ++rootFileCount; ! 622: ! 623: if ( record->hfsPlusFile.fileID > largestCNID ) ! 624: largestCNID = record->hfsPlusFile.fileID; ! 625: ! 626: // check the blocks allocated to this file (both forks) ! 627: ! 628: *freeBlocks -= CountExtentBlocks(record->hfsPlusFile.dataFork.extents, true); ! 629: VolumeBitMapCheckExtents(volume, record->hfsPlusFile.dataFork.extents, consistencyStatus); ! 630: ! 631: *freeBlocks -= CountExtentBlocks(record->hfsPlusFile.resourceFork.extents, true); ! 632: VolumeBitMapCheckExtents(volume, record->hfsPlusFile.resourceFork.extents, consistencyStatus); ! 633: ! 634: // check the LEOF and PEOF (both forks) ! 635: ! 636: if ( (record->hfsPlusFile.resourceFork.logicalSize.lo > record->hfsPlusFile.resourceFork.totalBlocks * blockSize) || ! 637: (record->hfsPlusFile.dataFork.logicalSize.lo > record->hfsPlusFile.dataFork.totalBlocks * blockSize) ) ! 638: { ! 639: *consistencyStatus |= kHFSInvalidLEOF; ! 640: VOLUME_CHECK_LOG("inconsistent file leof (not fixed)"); ! 641: } ! 642: ! 643: if ( !ValidPEOF(volume, record) ) ! 644: { ! 645: *consistencyStatus |= kHFSInvalidPEOF; ! 646: VOLUME_CHECK_LOG("inconsistent file peof (not fixed)"); ! 647: } ! 648: break; ! 649: } ! 650: ! 651: case kHFSFolderThreadRecord: ! 652: { ! 653: ++directoryThreads; ! 654: if ( key->hfs.parentID > largestCNID ) // <CS7> ! 655: largestCNID = key->hfs.parentID; ! 656: break; ! 657: } ! 658: ! 659: case kHFSPlusFolderThreadRecord: ! 660: { ! 661: ++directoryThreads; ! 662: if ( key->hfsPlus.parentID > largestCNID ) // <CS7> ! 663: largestCNID = key->hfsPlus.parentID; ! 664: break; ! 665: } ! 666: ! 667: case kHFSFileThreadRecord: ! 668: { ! 669: ++fileThreads; ! 670: if ( key->hfs.parentID > largestCNID ) // <CS7> ! 671: largestCNID = key->hfs.parentID; ! 672: break; ! 673: } ! 674: ! 675: case kHFSPlusFileThreadRecord: ! 676: { ! 677: ++fileThreads; ! 678: if ( key->hfsPlus.parentID > largestCNID ) // <CS7> ! 679: largestCNID = key->hfsPlus.parentID; ! 680: break; ! 681: } ! 682: ! 683: default: ! 684: *consistencyStatus |= kHFSInvalidCatalogRecordType; ! 685: VOLUME_CHECK_LOG("unknown catalog record"); ! 686: break; ! 687: } ! 688: ! 689: } // end while ! 690: ! 691: if ( result == noErr ) ! 692: { ! 693: *consistencyStatus |= kHFSCatalogBTreeLoop; ! 694: goto ErrorExit; // punt on loop error ! 695: } ! 696: ! 697: if ( result != fsBTRecordNotFoundErr ) ! 698: goto ErrorExit; // punt on BTree errors ! 699: ! 700: ! 701: // Check if calculated totals match the Volume Control Block ! 702: ! 703: if (volume->vcbNxtCNID <= largestCNID) // <CS7> ! 704: { ! 705: volume->vcbNxtCNID = largestCNID + 1; ! 706: volume->vcbFlags |= kVCBFlagsVolumeDirty; ! 707: VOLUME_CHECK_LOG("fixed vcbNxtCNID"); ! 708: } ! 709: ! 710: if (volume->vcbFilCnt != fileCount) ! 711: { ! 712: volume->vcbFilCnt = fileCount; ! 713: volume->vcbFlags |= kVCBFlagsVolumeDirty; ! 714: VOLUME_CHECK_LOG("fixed vcbFilCnt (volume file count)"); ! 715: } ! 716: ! 717: if (volume->vcbDirCnt != directoryCount) ! 718: { ! 719: volume->vcbDirCnt = directoryCount; ! 720: volume->vcbFlags |= kVCBFlagsVolumeDirty; ! 721: VOLUME_CHECK_LOG("fixed vcbDirCnt (volume directory count)"); ! 722: } ! 723: ! 724: if (!hfsPlus && volume->vcbNmFls != rootFileCount) ! 725: { ! 726: volume->vcbNmFls = rootFileCount; ! 727: volume->vcbFlags |= kVCBFlagsVolumeDirty; ! 728: VOLUME_CHECK_LOG("fixed vcbNmFls (root file count)"); ! 729: } ! 730: ! 731: if (!hfsPlus && volume->vcbNmRtDirs != rootDirectoryCount) ! 732: { ! 733: volume->vcbNmRtDirs = rootDirectoryCount; ! 734: volume->vcbFlags |= kVCBFlagsVolumeDirty; ! 735: VOLUME_CHECK_LOG("fixed vcbNmRtDirs (root directory count)"); ! 736: } ! 737: ! 738: if ( catalogValence != (fileCount + directoryCount) ) ! 739: { ! 740: *consistencyStatus |= kHFSInvalidValence; // valence inconsistency detected ! 741: VOLUME_CHECK_LOG("valence inconsistency detected (not fixed)"); ! 742: } ! 743: ! 744: #if 0 ! 745: if (volume->vcbAtrb & ~kMDBValidAttributesMask) // check for invalid bits ! 746: { ! 747: volume->vcbAtrb &= kMDBValidAttributesMask; ! 748: volume->vcbFlags |= kVCBFlagsVolumeDirty; ! 749: } ! 750: #endif ! 751: ! 752: if ( (volume->vcbFlags & kVCBFlagsVolumeDirty) && (volume->vcbAtrb & kVCBAttrLocked) == 0 ) ! 753: *consistencyStatus |= kHFSMinorRepairsWereMade; ! 754: ! 755: if (directoryThreads > (directoryCount + 1)) ! 756: { ! 757: *consistencyStatus |= kHFSOrphanedThreadRecords; // too many directory thread records ! 758: VOLUME_CHECK_LOG("orphaned directory thread records detected (not fixed)"); ! 759: } ! 760: ! 761: if (fileThreads > fileThreadsExpected) ! 762: { ! 763: if ( volume->vcbAtrb & kVCBAttrLocked ) ! 764: { ! 765: *consistencyStatus |= kHFSOrphanedThreadRecords; // too many file thread records ! 766: VOLUME_CHECK_LOG("orphaned file thread records detected (not fixed)"); ! 767: } ! 768: else if ( !hfsPlus ) // try and repair orphaned thread records ! 769: { ! 770: // If we fix up some threads, see if we can come out even without counting all the file ! 771: // threads again. If we don't, then looks like we have to keep count and do a second ! 772: // pass of file records (gotta do that anyway when we fix the KHFSMissingThreadRecords ! 773: // case below) and to count thread records again with the FixOrphanFileThreads call. ! 774: ! 775: if ( (!FORDISKFIRSTAID) || (GetDFAStage() == kRepairStage) ) ! 776: { ! 777: UInt32 repairsDone = 0; ! 778: ! 779: (void) FixOrphanFileThreads(volume, recordsFound, &repairsDone); ! 780: if (repairsDone) ! 781: { ! 782: *consistencyStatus |= kHFSMinorRepairsWereMade; ! 783: VOLUME_CHECK_LOG("orphaned file thread records detected (fixed)"); ! 784: } ! 785: ! 786: if (fileThreads > (fileThreadsExpected + repairsDone) ) ! 787: { ! 788: *consistencyStatus |= kHFSOrphanedThreadRecords; // still too many thread records ! 789: VOLUME_CHECK_LOG("orphaned file thread records detected (fix attempt failed)"); ! 790: } ! 791: } ! 792: } ! 793: } ! 794: ! 795: if ((directoryThreads < (directoryCount + 1)) || (fileThreads < fileThreadsExpected)) ! 796: { ! 797: *consistencyStatus |= kHFSMissingThreadRecords; // not enough thread records ! 798: VOLUME_CHECK_LOG("some catalog thread records are missing (not fixed)"); ! 799: } ! 800: ! 801: btree = GetBTreeControlBlock(volume->catalogRefNum); ! 802: ! 803: // okay, lets see if the blocks used by the catalog looks right ! 804: catalogBlocks += xCatalogBlocks; ! 805: if ( (catalogBlocks * volume->blockSize) != file->fcbPLen ) ! 806: { ! 807: *consistencyStatus |= kHFSInvalidPEOF; ! 808: VOLUME_CHECK_LOG("inconsistent catalog file size detected (not fixed)"); ! 809: } ! 810: ! 811: if ( btree->totalNodes != (file->fcbPLen / btree->nodeSize) ) ! 812: { ! 813: *consistencyStatus |= kHFSInvalidBTreeHeader; ! 814: VOLUME_CHECK_LOG("inconsistent catalog b-tree node count detected (not fixed)"); ! 815: } ! 816: ! 817: if (btree->leafRecords != recordsFound) ! 818: { ! 819: // We now use the B-tree scanner instead of iterating through all ! 820: // the leaf nodes in order. So let's be more conservative and not ! 821: // fix the leafRecords field until we can do adequate testing of ! 822: // this new method of visiting nodes. -DJB ! 823: ! 824: if (USE_BTREE_SCANNER || volume->vcbAtrb & kVCBAttrLocked) ! 825: { ! 826: *consistencyStatus |= kHFSInvalidBTreeHeader; // invalid record count ! 827: VOLUME_CHECK_LOG("inconsistent catalog b-tree record count detected (not fixed)"); ! 828: } ! 829: else // go ahead and make this simple repair! ! 830: { ! 831: btree->leafRecords = recordsFound; ! 832: btree->flags |= kBTHeaderDirty; // mark BTreeControlBlock dirty ! 833: *consistencyStatus |= kHFSMinorRepairsWereMade; ! 834: VOLUME_CHECK_LOG("inconsistent catalog b-tree record count detected (fixed)"); ! 835: } ! 836: } ! 837: ! 838: if ( (!FORDISKFIRSTAID) || (GetDFAStage() == kRepairStage) ) ! 839: { ! 840: if ( btree->flags & kBTHeaderDirty ) // if we made changes, write them out ! 841: (void) BTFlushPath(file); ! 842: } ! 843: ! 844: return noErr; ! 845: ! 846: ErrorExit: ! 847: return badCatalogErr; ! 848: ! 849: } // end CheckCatalog ! 850: ! 851: ! 852: /*------------------------------------------------------------------------------- ! 853: ! 854: Routine: CheckExtentsOverflow ! 855: ! 856: Function: Make sure the volume bitmap reflects the allocations of all extents. ! 857: ! 858: Assumption: ! 859: ! 860: Input: volume - pointer to volume control block ! 861: freeBlocks ! 862: xCatalogBlocks ! 863: consistencyStatus ! 864: ! 865: Output: none ! 866: ! 867: Result: noErr - success ! 868: ! 869: -------------------------------------------------------------------------------*/ ! 870: static OSErr ! 871: CheckExtentsOverflow ( ExtendedVCB *volume, UInt32 *freeBlocks, UInt32 *xCatalogBlocks, UInt32 *consistencyStatus ) ! 872: { ! 873: OSErr result; ! 874: UInt32 maxRecords; ! 875: UInt32 fileID; ! 876: UInt32 largestCNID; ! 877: UInt32 recordsFound; ! 878: UInt32 extentFileBlocks; ! 879: UInt32 extentRecordBlocks; ! 880: HFSPlusExtentKey *extentKeyPtr; ! 881: HFSPlusExtentRecord *extentDataPtr; ! 882: HFSPlusExtentRecord extentRecord; ! 883: UInt16 recordSize; ! 884: BTreeControlBlock *btree; ! 885: FCB* file; ! 886: BTScanState scanState; ! 887: Boolean hfsPlus = (volume->vcbSigWord == kHFSPlusSigWord); ! 888: UInt16 operation; ! 889: BTreeIterator btreeIterator; ! 890: FSBufferDescriptor btRecord; ! 891: ! 892: *xCatalogBlocks = 0; // set this early since its a return value ! 893: largestCNID = kHFSFirstUserCatalogNodeID; ! 894: ! 895: file = GetFileControlBlock(volume->extentsRefNum); ! 896: ! 897: result = GetFileExtentRecord(volume, file, extentRecord); ! 898: ReturnIfError(result); ! 899: ! 900: extentFileBlocks = CountExtentBlocks(extentRecord, hfsPlus); ! 901: *freeBlocks -= extentFileBlocks; ! 902: VolumeBitMapCheckExtents(volume, extentRecord, consistencyStatus); ! 903: ! 904: // now look at the extent tree... ! 905: ! 906: // Make a conservative estimate of the upper limit of B*-Tree records that ! 907: // could conceivably be encountered in a scan of all the leaf nodes. ! 908: recordsFound = 0; ! 909: maxRecords = (file->fcbEOF) / (hfsPlus ? sizeof(HFSPlusExtentRecord) : sizeof(HFSExtentRecord)); ! 910: ! 911: if (USE_BTREE_SCANNER) // scan the tree in physical order (faster) ! 912: { ! 913: result = BTScanInitialize(file, 0, 0, 0, GET_SCANNER_BUFFER(), GET_SCANNER_BUFFER_SIZE(), &scanState); ! 914: if (result != noErr) ! 915: return badMDBErr; ! 916: } ! 917: else // scan in leaf node order (slower but more reliable) ! 918: { ! 919: operation = kBTreeFirstRecord; // first leaf record ! 920: extentKeyPtr = (HFSPlusExtentKey*) &btreeIterator.key; ! 921: ! 922: btRecord.bufferAddress = &extentRecord; ! 923: btRecord.itemCount = 1; ! 924: btRecord.itemSize = sizeof(extentRecord); ! 925: } ! 926: ! 927: // visit all the leaf node data records in the extents B*-Tree ! 928: while ( recordsFound < maxRecords ) ! 929: { ! 930: if (USE_BTREE_SCANNER) ! 931: result = BTScanNextRecord(&scanState, false, (void **) &extentKeyPtr, (void **) &extentDataPtr, (UInt32*) &recordSize); ! 932: else ! 933: result = BTIterateRecord(file, operation, &btreeIterator, &btRecord, &recordSize); ! 934: ! 935: if ( result != noErr ) ! 936: break; ! 937: ! 938: ++recordsFound; ! 939: ! 940: // check the blocks allocated to this extent record ! 941: ! 942: if (USE_BTREE_SCANNER) ! 943: { ! 944: if ( hfsPlus ) ! 945: BlockMoveData(extentDataPtr, &extentRecord, sizeof(extentRecord)); // just make a copy ! 946: else ! 947: ConvertToHFSPlusExtent(*((HFSExtentRecord *) extentDataPtr), extentRecord); // convert it ! 948: } ! 949: else ! 950: { ! 951: operation = kBTreeNextRecord; ! 952: if ( !hfsPlus ) ! 953: ConvertToHFSPlusExtent(*(HFSExtentRecord*) &extentRecord, extentRecord); // convert it in place (since its a copy) ! 954: } ! 955: ! 956: extentRecordBlocks = CountExtentBlocks(extentRecord, hfsPlus); ! 957: ! 958: *freeBlocks -= extentRecordBlocks; ! 959: VolumeBitMapCheckExtents(volume, extentRecord, consistencyStatus); ! 960: ! 961: if ( hfsPlus ) ! 962: fileID = extentKeyPtr->fileID; ! 963: else ! 964: fileID = ((HFSExtentKey*) extentKeyPtr)->fileID; ! 965: ! 966: // if these were catalog blocks, we need to keep track ! 967: if ( fileID == kHFSCatalogFileID ) ! 968: *xCatalogBlocks += extentRecordBlocks; ! 969: ! 970: if ( fileID > largestCNID ) ! 971: largestCNID = fileID; ! 972: ! 973: } // end while ! 974: ! 975: if ( result != fsBTRecordNotFoundErr) ! 976: { ! 977: VOLUME_CHECK_LOG("extents b-tree has serious problems!"); ! 978: ! 979: return badMDBErr; // punt on error (loop error or BTree error) ! 980: } ! 981: ! 982: if (volume->vcbNxtCNID < largestCNID) ! 983: { ! 984: volume->vcbNxtCNID = largestCNID + 1; ! 985: volume->vcbFlags |= kVCBFlagsVolumeDirty; ! 986: volume->vcbAtrb |= 1; // need a constant for this! ! 987: } ! 988: ! 989: btree = GetBTreeControlBlock(volume->extentsRefNum); ! 990: ! 991: if ( (extentFileBlocks * volume->blockSize) != file->fcbPLen ) ! 992: { ! 993: *consistencyStatus |= kHFSInvalidPEOF; ! 994: VOLUME_CHECK_LOG("inconsistent extents file size detected (not fixed)"); ! 995: } ! 996: ! 997: if ( btree->totalNodes != (file->fcbPLen / btree->nodeSize) ) ! 998: { ! 999: *consistencyStatus |= kHFSInvalidBTreeHeader; ! 1000: VOLUME_CHECK_LOG("inconsistent extents b-tree node count detected (not fixed)"); ! 1001: } ! 1002: ! 1003: if (btree->leafRecords != recordsFound) ! 1004: { ! 1005: // We now use the B-tree scanner instead of iterating through all ! 1006: // the leaf nodes in order. So let's be more conservative and not ! 1007: // fix the leafRecords field until we can do adequate testing of ! 1008: // this new method of visiting nodes. -DJB ! 1009: ! 1010: if (USE_BTREE_SCANNER || volume->vcbAtrb & kVCBAttrLocked) ! 1011: { ! 1012: *consistencyStatus |= kHFSInvalidBTreeHeader; ! 1013: VOLUME_CHECK_LOG("inconsistent extents b-tree record count detected (not fixed)"); ! 1014: } ! 1015: else // go ahead and make this simple repair! ! 1016: { ! 1017: btree->leafRecords = recordsFound; ! 1018: btree->flags |= kBTHeaderDirty; // mark BTreeControlBlock dirty ! 1019: *consistencyStatus |= kHFSMinorRepairsWereMade; ! 1020: VOLUME_CHECK_LOG("inconsistent extents b-tree record count detected (fixed)"); ! 1021: } ! 1022: } ! 1023: ! 1024: if ( (!FORDISKFIRSTAID) || (GetDFAStage() == kRepairStage) ) ! 1025: { ! 1026: if ( btree->flags & kBTHeaderDirty ) // if we made changes, write them out ! 1027: (void) BTFlushPath(file); ! 1028: } ! 1029: ! 1030: return noErr; ! 1031: ! 1032: } // end CheckExtentsOverflow ! 1033: ! 1034: ! 1035: ! 1036: /*------------------------------------------------------------------------------- ! 1037: ! 1038: Routine: CheckAttributes ! 1039: ! 1040: Function: Make sure the volume bitmap reflects the allocations of all attributes ! 1041: that occupy extents. ! 1042: ! 1043: Assumption: ! 1044: ! 1045: Input: volume - pointer to volume control block ! 1046: freeBlocks ! 1047: consistencyStatus ! 1048: ! 1049: Output: none ! 1050: ! 1051: Result: noErr - success ! 1052: ! 1053: -------------------------------------------------------------------------------*/ ! 1054: static OSErr ! 1055: CheckAttributes ( ExtendedVCB *volume, UInt32 *freeBlocks, UInt32 *consistencyStatus ) ! 1056: { ! 1057: OSErr result; ! 1058: UInt32 maxRecords; ! 1059: UInt32 recordsFound; ! 1060: UInt32 attributesFileBlocks; ! 1061: UInt32 extentRecordBlocks; ! 1062: AttributeKey *attributeKey; ! 1063: HFSPlusAttrRecord *attributeData; ! 1064: HFSPlusExtentRecord extentRecord; ! 1065: UInt32 recordSize; ! 1066: BTreeControlBlock *btree; ! 1067: FCB* file; ! 1068: BTScanState scanState; ! 1069: Boolean hfsPlus = (volume->vcbSigWord == kHFSPlusSigWord); ! 1070: ! 1071: // ! 1072: // If there isn't an attributes B-tree, there's no work to do ! 1073: // ! 1074: if (volume->attributesRefNum == 0) ! 1075: return noErr; ! 1076: ! 1077: file = GetFileControlBlock(volume->attributesRefNum); ! 1078: ! 1079: result = GetFileExtentRecord(volume, file, extentRecord); ! 1080: ReturnIfError(result); ! 1081: ! 1082: attributesFileBlocks = CountExtentBlocks(extentRecord, hfsPlus); ! 1083: *freeBlocks -= attributesFileBlocks; ! 1084: VolumeBitMapCheckExtents(volume, extentRecord, consistencyStatus); ! 1085: ! 1086: ! 1087: // now look at the attributes tree... ! 1088: ! 1089: // Make a conservative estimate of the upper limit of B*-Tree records that ! 1090: // could conceivably be encountered in a scan of all the leaf nodes. ! 1091: recordsFound = 0; ! 1092: maxRecords = (file->fcbEOF) / 8; // 8 = minimum data length (for zero length inline attribute) ! 1093: ! 1094: // Prepare to scan the tree in physical order ! 1095: result = BTScanInitialize(file, 0, 0, 0, GET_SCANNER_BUFFER(), GET_SCANNER_BUFFER_SIZE(), &scanState); ! 1096: if (result != noErr) ! 1097: return badMDBErr; ! 1098: ! 1099: // visit all the leaf node data records in the extents B*-Tree ! 1100: while ( recordsFound < maxRecords ) ! 1101: { ! 1102: result = BTScanNextRecord(&scanState, false, (void **) &attributeKey, (void **) &attributeData, &recordSize); ! 1103: ! 1104: if ( result != noErr ) ! 1105: break; ! 1106: ! 1107: ++recordsFound; ! 1108: ! 1109: // If this block has information about extents, make sure they're allocated ! 1110: switch (attributeData->recordType) { ! 1111: case kHFSPlusAttrInlineData: ! 1112: break; ! 1113: case kHFSPlusAttrForkData: ! 1114: extentRecordBlocks = CountExtentBlocks(attributeData->forkData.theFork.extents, true); ! 1115: *freeBlocks -= extentRecordBlocks; ! 1116: VolumeBitMapCheckExtents(volume, attributeData->forkData.theFork.extents, consistencyStatus); ! 1117: break; ! 1118: case kHFSPlusAttrExtents: ! 1119: extentRecordBlocks = CountExtentBlocks(attributeData->overflowExtents.extents, true); ! 1120: *freeBlocks -= extentRecordBlocks; ! 1121: VolumeBitMapCheckExtents(volume, attributeData->overflowExtents.extents, consistencyStatus); ! 1122: break; ! 1123: default: ! 1124: if (DEBUG_BUILD) ! 1125: DebugStr("\pHFS+: Unknown attribute record"); ! 1126: break; ! 1127: } // end switch ! 1128: } // end while ! 1129: ! 1130: if ( result != fsBTRecordNotFoundErr ) ! 1131: { ! 1132: VOLUME_CHECK_LOG("extents b-tree has serious problems!"); ! 1133: return badMDBErr; // punt on error (loop error or BTree error) ! 1134: } ! 1135: ! 1136: btree = GetBTreeControlBlock(volume->attributesRefNum); ! 1137: ! 1138: if ( (attributesFileBlocks * volume->blockSize) != file->fcbPLen ) ! 1139: *consistencyStatus |= kHFSInvalidPEOF; ! 1140: ! 1141: if ( btree->totalNodes != (file->fcbPLen / btree->nodeSize) ) ! 1142: *consistencyStatus |= kHFSInvalidBTreeHeader; ! 1143: ! 1144: if (btree->leafRecords != recordsFound) ! 1145: { ! 1146: // We now use the B-tree scanner instead of iterating through all ! 1147: // the leaf nodes in order. So let's be more conservative and not ! 1148: // fix the leafRecords field until we can do adequate testing of ! 1149: // this new method of visiting nodes. -DJB ! 1150: ! 1151: if (USE_BTREE_SCANNER || volume->vcbAtrb & kVCBAttrLocked) ! 1152: { ! 1153: *consistencyStatus |= kHFSInvalidBTreeHeader; ! 1154: } ! 1155: else // go ahead and make this simple repair! ! 1156: { ! 1157: btree->leafRecords = recordsFound; ! 1158: btree->flags |= kBTHeaderDirty; // mark BTreeControlBlock dirty ! 1159: *consistencyStatus |= kHFSMinorRepairsWereMade; ! 1160: } ! 1161: } ! 1162: ! 1163: if ( (!FORDISKFIRSTAID) || (GetDFAStage() == kRepairStage) ) ! 1164: { ! 1165: if ( btree->flags & kBTHeaderDirty ) // if we made changes, write them out ! 1166: (void) BTFlushPath(file); ! 1167: } ! 1168: return noErr; ! 1169: ! 1170: } // end CheckAttributes ! 1171: ! 1172: ! 1173: ! 1174: /*------------------------------------------------------------------------------- ! 1175: ! 1176: Routine: CheckBitmapAndHeader ! 1177: ! 1178: Function: Make sure the volume header, alternate volume header, and the bitmap ! 1179: itself are marked as used in the bitmap. ! 1180: ! 1181: Assumption: ! 1182: ! 1183: Input: volume - pointer to volume control block ! 1184: freeBlocks ! 1185: consistencyStatus ! 1186: ! 1187: Output: none ! 1188: ! 1189: Result: noErr - success ! 1190: ! 1191: -------------------------------------------------------------------------------*/ ! 1192: static OSErr ! 1193: CheckBitmapAndHeader ( ExtendedVCB *volume, UInt32 *freeBlocks, UInt32 *consistencyStatus ) ! 1194: { ! 1195: OSErr result; ! 1196: UInt32 blocksUsed; ! 1197: FCB* file; ! 1198: HFSPlusExtentRecord extentRecord; ! 1199: ! 1200: // ! 1201: // If there isn't an allocation file, there's no work to do ! 1202: // ! 1203: if (volume->allocationsRefNum == 0) ! 1204: return noErr; ! 1205: ! 1206: file = GetFileControlBlock(volume->allocationsRefNum); ! 1207: ! 1208: result = GetFileExtentRecord(volume, file, extentRecord); ! 1209: ReturnIfError(result); ! 1210: ! 1211: // Mark the space used by the allocation file (bitmap) itself ! 1212: blocksUsed = CountExtentBlocks(extentRecord, true); ! 1213: *freeBlocks -= blocksUsed; ! 1214: VolumeBitMapCheckExtents(volume, extentRecord, consistencyStatus); ! 1215: ! 1216: // Now mark sectors zero through two (boot blocks and volume header) ! 1217: // and the last two sectors (alternate volume header and a reserved ! 1218: // sector). The number of allocation blocks depends on the size of ! 1219: // an allocation block. What we do is create a fake extent record ! 1220: // for these blocks, and call VolumeBitMapCheckExtents as usual. ! 1221: ! 1222: ClearMemory(&extentRecord, sizeof(extentRecord)); ! 1223: ! 1224: extentRecord[0].startBlock = 0; ! 1225: extentRecord[0].blockCount = 1 + (1024 / volume->blockSize); // sectors 0-2 ! 1226: ! 1227: if (volume->blockSize == 512) { ! 1228: extentRecord[1].startBlock = volume->totalBlocks - 2; ! 1229: extentRecord[1].blockCount = 2; ! 1230: } else { ! 1231: extentRecord[1].startBlock = volume->totalBlocks - 1; ! 1232: extentRecord[1].blockCount = 1; ! 1233: } ! 1234: ! 1235: *freeBlocks -= extentRecord[0].blockCount + extentRecord[1].blockCount; ! 1236: VolumeBitMapCheckExtents(volume, extentRecord, consistencyStatus); ! 1237: ! 1238: return noErr; ! 1239: } ! 1240: ! 1241: ! 1242: // For Disk First Aid, this routine will not update the BitMap, but will set the ! 1243: // kHFSMinorRepairsWereMade bit to indicate the bitmap needs updating. ! 1244: static void VolumeBitMapCheckExtents ( ! 1245: ExtendedVCB* volume, ! 1246: HFSPlusExtentDescriptor* theExtents, ! 1247: UInt32* consistencyStatus ) ! 1248: { ! 1249: if ( BlockCheck(volume, theExtents) == -1 ) ! 1250: { ! 1251: *consistencyStatus |= kHFSMinorRepairsWereMade; ! 1252: VOLUME_CHECK_LOG("repairing volume bitmap"); ! 1253: } ! 1254: } ! 1255: ! 1256: ! 1257: /*------------------------------------------------------------------------------- ! 1258: ! 1259: Routine: ValidPEOF ! 1260: ! 1261: Function: Make sure the local extents match the PEOF. ! 1262: Currently this only checks the local extent. ! 1263: We could also check the oveflow extents for ! 1264: a more complete (but slower) test. ! 1265: ! 1266: Assumption: ! 1267: ! 1268: Input: ! 1269: ! 1270: Output: ! 1271: ! 1272: Result: ! 1273: ! 1274: -------------------------------------------------------------------------------*/ ! 1275: ! 1276: static Boolean ! 1277: ValidPEOF( ExtendedVCB* volume, CatalogRecord *file ) ! 1278: { ! 1279: UInt32 minPEOF; ! 1280: UInt32 allocBlkSize = volume->blockSize; ! 1281: ! 1282: if (file->recordType == kHFSPlusFileRecord) ! 1283: { ! 1284: UInt32 minBlocks; ! 1285: ! 1286: minBlocks = CountExtentBlocks(file->hfsPlusFile.resourceFork.extents, true); ! 1287: ! 1288: if ( file->hfsPlusFile.resourceFork.totalBlocks < minBlocks ) ! 1289: return false; ! 1290: else if ( (file->hfsPlusFile.resourceFork.totalBlocks > minBlocks) && (file->hfsPlusFile.resourceFork.extents[kHFSPlusExtentDensity-1].blockCount == 0) ) ! 1291: return false; ! 1292: ! 1293: minBlocks = CountExtentBlocks(file->hfsPlusFile.dataFork.extents, true); ! 1294: ! 1295: if ( file->hfsPlusFile.dataFork.totalBlocks < minBlocks ) ! 1296: return false; ! 1297: else if ( (file->hfsPlusFile.dataFork.totalBlocks > minBlocks) && (file->hfsPlusFile.dataFork.extents[kHFSPlusExtentDensity-1].blockCount == 0) ) ! 1298: return false; ! 1299: } ! 1300: else ! 1301: { ! 1302: HFSPlusExtentRecord extentRecord; ! 1303: ! 1304: ConvertToHFSPlusExtent(file->hfsFile.rsrcExtents, extentRecord); ! 1305: minPEOF = CountExtentBlocks(extentRecord, false) * allocBlkSize; ! 1306: ! 1307: if ( file->hfsFile.rsrcPhysicalSize < minPEOF ) ! 1308: return false; ! 1309: else if ( (file->hfsFile.rsrcPhysicalSize > minPEOF) && (extentRecord[kHFSExtentDensity-1].blockCount == 0) ) ! 1310: return false; ! 1311: ! 1312: ! 1313: ConvertToHFSPlusExtent(file->hfsFile.dataExtents, extentRecord); ! 1314: minPEOF = CountExtentBlocks(extentRecord, false) * allocBlkSize; ! 1315: ! 1316: if ( file->hfsFile.dataPhysicalSize < minPEOF ) ! 1317: return false; ! 1318: else if ( (file->hfsFile.dataPhysicalSize > minPEOF) && (extentRecord[kHFSExtentDensity-1].blockCount == 0) ) ! 1319: return false; ! 1320: } ! 1321: ! 1322: ! 1323: return true; ! 1324: } ! 1325: ! 1326: ! 1327: /*------------------------------------------------------------------------------- ! 1328: ! 1329: Routine: FixOrphanFileThreads ! 1330: ! 1331: Function: For orphan thread records during VolumeCheckConsistency have the catalog ! 1332: searched again for file records that match. If their thread record flag ! 1333: is off, mark it. If it is on, do nothing. If the file record doesn't exist, ! 1334: delete the thread record. ! 1335: ! 1336: Assumption: This is a function to be called only if the thread records detected during ! 1337: CheckCatalog was greater than the file records that had their flag set for ! 1338: file thread existance. I've thought of doing this differently, like keeping ! 1339: a table of all file records or threads scanned in the first pass, but the ! 1340: table gets large. You can't predict how large so may to hold upto the max ! 1341: fileIDs possible. (2**32). Using two such tables to match file threads ! 1342: against file records would be bad. Less space would be keeping a bitmap of ! 1343: all fileIDS (to 2**32) and marking those with threads. Then a separate ! 1344: bitmap for those file records with thread flags on marked.You do a byte by ! 1345: byte comparison across the whole bitmap. When they differ, you know if you're ! 1346: missing ! 1347: threads or orphans threads. (Note 0s would also mean IDs represented by that ! 1348: bit in the bitmap that is non-existant). Though, this sounds clever, it still ! 1349: seems it would take 8bits/byte * 512 bytes/sector * 8 sectors/ vmpage = 2**15 ! 1350: bits per vmpage and 2**17 vmpages to cover the bitmap! Too large. SO, let's ! 1351: just scan the catalog again. Yep, it's timeconsuming, but we are only taking ! 1352: the hit in repair mode for that particular problem. No extra memory for tables ! 1353: or bitmaps are even required during the original scan. ! 1354: ! 1355: Input: ! 1356: ! 1357: Output: ! 1358: ! 1359: Result: E_NoError - success ! 1360: != E_NoError - failure ! 1361: ! 1362: -------------------------------------------------------------------------------*/ ! 1363: #if TARGET_OS_MAC ! 1364: static OSErr FixOrphanFileThreads(ExtendedVCB* volume, UInt32 maxRecords, UInt32 *repairsDone) ! 1365: { ! 1366: UInt16 recordSize; ! 1367: OSErr tempResult, result = noErr; ! 1368: CatalogKey key; ! 1369: FSSpec spec; ! 1370: CatalogRecord record; ! 1371: CatalogNodeData nodeData; ! 1372: UInt32 hint = 0; ! 1373: ! 1374: // let's iterate the catalog looking for file thread records. As we hit each one, ! 1375: // do a search for the file record. If it exists and the thread flag is off,set ! 1376: // it. If it exists, and the thread flag is on, all is fine and dandy. It it ! 1377: // doesn't exist, remove the orphaned thread record. ! 1378: ! 1379: *repairsDone = 0; ! 1380: ! 1381: result = GetCatalogNode(volume, kHFSRootFolderID, NULL, kNoHint, &spec, &nodeData, &hint); ! 1382: M_ExitOnError(result); ! 1383: ! 1384: // visit all the leaf node data records in the catalog ! 1385: while ( --maxRecords > 0 ) // starts out at recordsFound value passed in ! 1386: { ! 1387: tempResult = GetBTreeRecord(volume->catalogRefNum, kBTreeNext, &key, &record, ! 1388: &recordSize, &hint ); ! 1389: if ( tempResult ) ! 1390: { ! 1391: // DebugStr("\p FixOrphanFileThreads: did we run out of records?"); ! 1392: break; ! 1393: } ! 1394: ! 1395: // if this is a file thread record, check it out... ! 1396: if (record.recordType == kHFSFileThreadRecord) ! 1397: { ! 1398: CatalogKey currentKey; ! 1399: CatalogKey fileKey; ! 1400: UInt32 fileHint; ! 1401: ! 1402: // save our current position ! 1403: currentKey = key; ! 1404: ! 1405: // create a key for the file record ! 1406: fileHint = 0; ! 1407: fileKey.hfs.parentID = record.hfsThread.parentID; ! 1408: BlockMoveData ( record.hfsThread.nodeName, fileKey.hfs.nodeName, ! 1409: record.hfsThread.nodeName[0] + 1); ! 1410: ! 1411: // try and get the file record for this thread record... ! 1412: tempResult = SearchBTreeRecord ( volume->catalogRefNum, &fileKey, fileHint, &key, &record, &recordSize, &fileHint ); ! 1413: ! 1414: if (tempResult == noErr) ! 1415: { ! 1416: if ((record.hfsFile.flags & kHFSThreadExistsMask) == 0) ! 1417: { ! 1418: // thread record exists but file record doesn't think so....set it ! 1419: record.hfsFile.flags |= kHFSThreadExistsMask; ! 1420: ! 1421: // update the file record on disk ! 1422: if ( ReplaceBTreeRecord(volume->catalogRefNum, &key, fileHint, &record, recordSize, &fileHint) == noErr ) ! 1423: ++(*repairsDone); ! 1424: } ! 1425: } ! 1426: else if (tempResult == btNotFound) ! 1427: { ! 1428: // BTDelete invalidates the current node mark so before we delete the thread record, ! 1429: // point currentKey and hint at something valid (ie the previous record) ! 1430: ! 1431: (void) SearchBTreeRecord( volume->catalogRefNum, ¤tKey, hint, &key, &record, &recordSize, &hint ); ! 1432: (void) GetBTreeRecord( volume->catalogRefNum, kBTreePrevious, &key, &record, &recordSize, &hint ); ! 1433: ! 1434: // file record doesn't exist. Delete the thread record. ! 1435: if ( DeleteBTreeRecord( volume->catalogRefNum, ¤tKey) == noErr ) ! 1436: ++(*repairsDone); ! 1437: ! 1438: currentKey = key; // now point to the record before the thread record ! 1439: } ! 1440: ! 1441: // now we need to reset the current node mark for GetBTreeRecord iteration... ! 1442: (void) SearchBTreeRecord( volume->catalogRefNum, ¤tKey, hint, &key, &record, &recordSize, &hint ); ! 1443: ! 1444: } // end if thread ! 1445: ! 1446: } // end while ! 1447: ! 1448: return( result ); ! 1449: ErrorExit: ! 1450: return ( result ); //XXX which error should it be ??? ! 1451: } // end FixOrphanFileThreads ! 1452: #endif /* TARGET_OS_MAC */ ! 1453: ! 1454: //_______________________________________________________________________ ! 1455: // ! 1456: // GetTempBuffer ! 1457: // ! 1458: // This routine allocates a temporary buffer. ! 1459: // ! 1460: // Note: At startup TempNewHandle may not be available. ! 1461: //_______________________________________________________________________ ! 1462: #if TARGET_OS_MAC ! 1463: static Handle GetTempBuffer (Size size) ! 1464: { ! 1465: OSErr error; ! 1466: Handle tempBuffer; ! 1467: ! 1468: if ( GetExpandMemProcessMgrExists() ) ! 1469: tempBuffer = TempNewHandle(size, &error); ! 1470: else ! 1471: tempBuffer = NewHandleSys(size); ! 1472: ! 1473: return tempBuffer; ! 1474: } ! 1475: #endif /* TARGET_OS_MAC */ ! 1476: ! 1477: ! 1478: ! 1479: static UInt32 ! 1480: CountExtentBlocks(HFSPlusExtentRecord extents, Boolean hfsPlus) ! 1481: { ! 1482: UInt32 blocks; ! 1483: ! 1484: // grab the first 3 ! 1485: blocks = extents[0].blockCount + extents[1].blockCount + extents[2].blockCount; ! 1486: ! 1487: if (hfsPlus) ! 1488: { ! 1489: UInt32 i; ! 1490: ! 1491: for (i = 3; i < kHFSPlusExtentDensity; ++i) ! 1492: blocks += extents[i].blockCount; // HFS Plus has additional extents ! 1493: } ! 1494: ! 1495: return blocks; ! 1496: } ! 1497: ! 1498: ! 1499: static void ! 1500: ConvertToHFSPlusExtent( const HFSExtentRecord oldExtents, HFSPlusExtentRecord newExtents) ! 1501: { ! 1502: UInt16 i; ! 1503: ! 1504: // go backwards so we can convert in place! ! 1505: ! 1506: for (i = kHFSPlusExtentDensity-1; i > 2; --i) ! 1507: { ! 1508: newExtents[i].blockCount = 0; ! 1509: newExtents[i].startBlock = 0; ! 1510: } ! 1511: ! 1512: newExtents[2].blockCount = oldExtents[2].blockCount; ! 1513: newExtents[2].startBlock = oldExtents[2].startBlock; ! 1514: newExtents[1].blockCount = oldExtents[1].blockCount; ! 1515: newExtents[1].startBlock = oldExtents[1].startBlock; ! 1516: newExtents[0].blockCount = oldExtents[0].blockCount; ! 1517: newExtents[0].startBlock = oldExtents[0].startBlock; ! 1518: } ! 1519: ! 1520: ! 1521: static OSErr ! 1522: GetFileExtentRecord( const ExtendedVCB *vcb, const FCB *fcb, HFSPlusExtentRecord extents) ! 1523: { ! 1524: OSErr err; ! 1525: ExtendedFCB *extendedFCB; ! 1526: ! 1527: err = noErr; ! 1528: ! 1529: if (vcb->vcbSigWord == kHFSSigWord) ! 1530: { ! 1531: ConvertToHFSPlusExtent(fcb->fcbExtRec, extents); ! 1532: } ! 1533: else ! 1534: { ! 1535: /* XXX PPD: Is there any reason this isn't the same for EVERY platform? */ ! 1536: #if TARGET_OS_MAC ! 1537: extendedFCB = ParallelFCBFromRefnum( GetFileRefNumFromFCB(fcb) ); ! 1538: #else ! 1539: extendedFCB = GetParallelFCB( GetFileRefNumFromFCB(fcb) ); ! 1540: #endif ! 1541: ! 1542: if (extendedFCB == NULL) ! 1543: { ! 1544: if ( DEBUG_BUILD ) ! 1545: DebugStr("\pFATAL: Extended FCB not found!"); ! 1546: err = fsDSIntErr; ! 1547: } ! 1548: else ! 1549: { ! 1550: BlockMoveData(extendedFCB->extents, extents, sizeof(HFSPlusExtentRecord)); ! 1551: } ! 1552: } ! 1553: ! 1554: return err; ! 1555: } ! 1556: ! 1557: ! 1558: ! 1559: //_______________________________________________________________________ ! 1560: // ! 1561: // CheckCreateDate ! 1562: // ! 1563: // For HFS Plus volumes, make sure the createDate in the VolumeHeader ! 1564: // is the same as in the MDB. If not, just fix it. ! 1565: // ! 1566: //_______________________________________________________________________ ! 1567: ! 1568: static OSErr CheckCreateDate (ExtendedVCB* volume, UInt32* consistencyStatus ) ! 1569: { ! 1570: OSErr err; ! 1571: HFSMasterDirectoryBlock *mdb; ! 1572: UInt32 createDate; ! 1573: ! 1574: return noErr; ! 1575: ! 1576: // ! 1577: // Read in the MDB ! 1578: // ! 1579: #if TARGET_OS_MAC ! 1580: err = GetBlock_glue(gbReleaseMask, 2, (Ptr *) &mdb, volume->vcbVRefNum, volume); ! 1581: #else ! 1582: err = GetBlock_glue(gbReleaseMask, 2, (Ptr *) &mdb, kNoFileReference, volume); ! 1583: #endif ! 1584: if (err != noErr) return err; ! 1585: ! 1586: // ! 1587: // Make sure it really is a wrappered volume with an MDB ! 1588: // ! 1589: if (mdb->drSigWord != kHFSSigWord || mdb->drEmbedSigWord != kHFSPlusSigWord) ! 1590: return noErr; // must not be a wrapper, so nothing to fix ! 1591: ! 1592: createDate = mdb->drCrDate; ! 1593: ! 1594: if (volume->vcbCrDate != createDate) { ! 1595: volume->vcbCrDate = createDate; // fix create date in the VCB ! 1596: volume->vcbFlags |= kVCBFlagsVolumeDirty; // make it dirty so change will be written to volume header ! 1597: *consistencyStatus |= kHFSMinorRepairsWereMade; // the problem is now fixed ! 1598: } ! 1599: ! 1600: return noErr; ! 1601: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.