1/* 2 * Copyright (c) 1999-2011 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 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 11 * file. 12 * 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. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23/* 24 File: SControl.c 25 26 Contains: This file contains the routines which control the scavenging operations. 27 28 Version: xxx put version here xxx 29 30 Written by: Bill Bruffey 31 32 Copyright: � 1985, 1986, 1992-1999 by Apple Computer, Inc., all rights reserved. 33*/ 34 35#define SHOW_ELAPSED_TIMES 0 36 37 38#if SHOW_ELAPSED_TIMES 39#include <sys/time.h> 40#endif 41 42#include "Scavenger.h" 43#include "fsck_journal.h" 44#include <setjmp.h> 45#include <unistd.h> 46 47#ifndef CONFIG_HFS_TRIM 48#define CONFIG_HFS_TRIM 1 49#endif 50 51#define DisplayTimeRemaining 0 52 53/* Variable containing diskdev_cmds tag number and date/time when the binary was built. 54 * This variable is populated automatically using version.pl by B&I when building the 55 * project. For development purposes, if the current directory name looks something 56 * like a tag name or a version number is provided to buildit, buildit populates it 57 * correctly. For all other ways to build the code, like 'make', the tag number will 58 * be left empty and only project name and build date/time will be shown. 59 * 60 * TODO: Get this building properly within Xcode, without need for the version.pl script! 61 */ 62extern const unsigned char fsck_hfsVersionString[]; 63 64int gGUIControl; 65extern char lflag; 66 67 68// Static function prototypes 69 70static void printVerifyStatus( SGlobPtr GPtr ); 71static Boolean IsBlueBoxSharedDrive ( DrvQElPtr dqPtr ); 72static int ScavSetUp( SGlobPtr GPtr ); 73static int ScavTerm( SGlobPtr GPtr ); 74 75/* this procedure receives progress calls and will allow canceling of procedures - we are using it here to print out the progress of the current operation for DFA and DiskUtility - ESP 1/10/00 */ 76 77int cancelProc(UInt16 progress, UInt16 secondsRemaining, Boolean progressChanged, UInt16 stage, void *context, int passno) 78{ 79 if (progressChanged) { 80 int base; 81 int pct; 82 int scale; 83 static int lastPct = -1; 84 if (passno < 0) { 85 base = 0; 86 scale = 100; 87 } else { 88 base = (passno * 100) / kMaxReScan; // Multiply by 100 because we're doing ints 89 scale = 100 / kMaxReScan; 90 } 91 pct = ((progress * scale) / 100) + base; 92 if (pct != lastPct && pct != 100) { 93 fsckPrint((fsck_ctx_t)context, fsckProgress, pct); 94 lastPct = pct; 95 draw_progress(pct); 96 } 97 } 98 return 0; 99} 100 101static const int kMaxMediumErrors = 25; 102 103/* 104 * Determine whether an error is major or minor. The main critera we chose for 105 * this is whether you can continue to use -- reading, creating, and deleting -- 106 * in a volume with the error present. This should at some point go into the 107 * message structure itself. 108 */ 109static int 110isMinorError(int msg, int *counts) 111{ 112 switch (msg) { 113 case hfsExtBTCheck: 114 case hfsCatBTCheck: 115 case hfsCatHierCheck: 116 case hfsExtAttrBTCheck: 117 case hfsVolBitmapCheck: 118 case hfsVolInfoCheck: 119 case hfsHardLinkCheck: 120 case hfsRebuildExtentBTree: 121 case hfsRebuildCatalogBTree: 122 case hfsRebuildAttrBTree: 123 case hfsCaseSensitive: 124 case hfsMultiLinkDirCheck: 125 case hfsJournalVolCheck: 126 case hfsLiveVerifyCheck: 127 case hfsVerifyVolWithWrite: 128 case hfsCheckHFS: 129 case hfsCheckNoJnl: 130 case E_DirVal: 131 case E_CName: 132 case E_NoFile: 133 case E_NoRtThd: 134 case E_NoThd: 135 case E_NoDir: 136 case E_RtDirCnt: 137 case E_RtFilCnt: 138 case E_DirCnt: 139 case E_FilCnt: 140 case E_CatDepth: 141 case E_NoFThdFlg: 142 case E_CatalogFlagsNotZero: 143 case E_BadFileName: 144 case E_InvalidClumpSize: 145 case E_LockedDirName: 146 case E_FreeBlocks: 147 case E_LeafCnt: 148 case E_BadValue: 149 case E_InvalidID: 150 case E_DiskFull: 151 case E_InvalidLinkCount: 152 case E_UnlinkedFile: 153 case E_InvalidPermissions: 154 case E_InvalidUID_Unused: 155 case E_IllegalName: 156 case E_IncorrectNumThdRcd: 157 case E_SymlinkCreate: 158 case E_IncorrectAttrCount: 159 case E_IncorrectSecurityCount: 160 case E_PEOAttr: 161 case E_LEOAttr: 162 case E_FldCount: 163 case E_HsFldCount: 164 case E_BadPermPrivDir: 165 case E_DirInodeBadFlags: 166 case E_DirInodeBadParent: 167 case E_DirInodeBadName: 168 case E_DirHardLinkChain: 169 case E_DirHardLinkOwnerFlags: 170 case E_DirHardLinkFinderInfo: 171 case E_DirLinkAncestorFlags: 172 case E_DirHardLinkNesting: 173 case E_InvalidLinkChainPrev: 174 case E_InvalidLinkChainNext: 175 case E_FileInodeBadFlags: 176 case E_FileInodeBadName: 177 case E_FileHardLinkChain: 178 case E_FileHardLinkFinderInfo: 179 case E_InvalidLinkChainFirst: 180 case E_FileLinkBadFlags: 181 case E_DirLinkBadFlags: 182 case E_OrphanFileLink: 183 case E_OrphanDirLink: 184 case E_OrphanFileInode: 185 case E_OrphanDirInode: 186 case E_UnusedNodeNotZeroed: 187 case E_VBMDamagedOverAlloc: 188 case E_BadSymLink: 189 case E_BadSymLinkLength: 190 case E_BadSymLinkName: 191 return 1; 192 /* 193 * A lot of EOF errors may indicate that there were some more significant 194 * problems with the volume; just one by itself, with no other volume layout 195 * problems, won't affect the volume usage. So we keep track of them. 196 */ 197 case E_PEOF: 198 case E_LEOF: 199 if (++counts[abs(msg)] > kMaxMediumErrors) 200 return 0; 201 return 1; 202 default: 203 return 0; 204 } 205} 206 207/*------------------------------------------------------------------------------ 208 209External 210 Routines: CheckHFS - Controls the scavenging process. 211 212------------------------------------------------------------------------------*/ 213 214static jmp_buf envBuf; 215int 216CheckHFS( const char *rdevnode, int fsReadRef, int fsWriteRef, int checkLevel, 217 int repairLevel, fsck_ctx_t fsckContext, int lostAndFoundMode, 218 int canWrite, int *modified, int liveMode, int rebuildOptions ) 219{ 220 SGlob dataArea; // Allocate the scav globals 221 short temp; 222 FileIdentifierTable *fileIdentifierTable = nil; 223 OSErr err = noErr; 224 OSErr scavError = 0; 225 int scanCount = 0; 226 int isJournaled = 0; 227 Boolean autoRepair; 228 Boolean exitEarly = 0; 229 __block int *msgCounts = NULL; 230 Boolean majorErrors = 0; 231 232 if (checkLevel == kMajorCheck) { 233 checkLevel = kForceCheck; 234 exitEarly = 1; 235 msgCounts = malloc(sizeof(int) * E_LastError); 236 } 237 238 autoRepair = (fsWriteRef != -1 && repairLevel != kNeverRepair); 239 240 /* Initialize the messages only once before the verify stage */ 241 if (fsckContext) { 242 extern fsck_message_t hfs_messages[]; 243 extern fsck_message_t hfs_errors[]; 244 245 if (fsckAddMessages(fsckContext, hfs_messages) == -1 || 246 fsckAddMessages(fsckContext, hfs_errors) == -1) { 247 // XXX 248 return -1; 249 } 250 } 251 252 /* 253 * Get the project name and version that is being built. 254 * 255 * The __fsck_hfsVersionString contents are of the form: 256 * "@(#)PROGRAM:fsck_hfs PROJECT:hfs-557~332\n" 257 */ 258 if (1) { 259 const char project[] = " PROJECT:"; 260 char *vstr, *tmp; 261 262 tmp = strstr((const char *)fsck_hfsVersionString, project); 263 if (tmp) { 264 vstr = strdup(tmp + strlen(project)); 265 tmp = strstr(vstr, "\n"); 266 if (tmp) 267 *tmp = 0; 268 } else { 269 vstr = strdup((const char *)fsck_hfsVersionString); 270 } 271 272 fsckPrint(fsckContext, fsckInformation, "fsck_hfs", vstr); 273 free(vstr); 274 } 275 276 if (setjmp(envBuf) == 1) { 277 /* 278 * setjmp() returns the second argument to longjmp(), so if it returns 1, then 279 * we've hit a major error. 280 */ 281 dataArea.RepLevel = repairLevelVeryMinorErrors; 282 majorErrors = 1; 283 goto EarlyExitLabel; 284 } else { 285 if (exitEarly && fsckContext) { 286 /* 287 * Set the after-printing block to a small bit of code that checks to see if 288 * the message in question corresponds to a major or a minor error. If it's 289 * major, we longjmp just above, which causes us to exit out early. 290 */ 291 fsckSetBlock(fsckContext, fsckPhaseAfterMessage, (fsckBlock_t) ^(fsck_ctx_t c, int msgNum, va_list args) { 292 if (abs(msgNum) > E_FirstError && abs(msgNum) < E_LastError) { 293 if (isMinorError(abs(msgNum), msgCounts) == 1) 294 return fsckBlockContinue; 295 longjmp(envBuf, 1); 296 return fsckBlockAbort; 297 } else { 298 return fsckBlockContinue; 299 } 300 }); 301 } 302 } 303DoAgain: 304 ClearMemory( &dataArea, sizeof(SGlob) ); 305 if (msgCounts) 306 memset(msgCounts, 0, sizeof(int) * E_LastError); 307 308 // Initialize some scavenger globals 309 dataArea.itemsProcessed = 0; // Initialize to 0% complete 310 dataArea.itemsToProcess = 1; 311 dataArea.chkLevel = checkLevel; 312 dataArea.repairLevel = repairLevel; 313 dataArea.rebuildOptions = rebuildOptions; 314 dataArea.canWrite = canWrite; 315 dataArea.writeRef = fsWriteRef; 316 dataArea.lostAndFoundMode = lostAndFoundMode; 317 dataArea.DrvNum = fsReadRef; 318 dataArea.liveVerifyState = liveMode; 319 dataArea.scanCount = scanCount; 320 if (strlcpy(dataArea.deviceNode, rdevnode, sizeof(dataArea.deviceNode)) != strlen(rdevnode)) { 321 dataArea.deviceNode[0] = '\0'; 322 } 323 324 /* there are cases where we cannot get the name of the volume so we */ 325 /* set our default name to one blank */ 326 dataArea.volumeName[ 0 ] = ' '; 327 dataArea.volumeName[ 1 ] = '\0'; 328 329 if (fsckContext) { 330 dataArea.context = fsckContext; 331 dataArea.guiControl = true; 332 dataArea.userCancelProc = cancelProc; 333 } 334 // 335 // Initialize the scavenger 336 // 337 ScavCtrl( &dataArea, scavInitialize, &scavError ); 338 if ( checkLevel == kNeverCheck || (checkLevel == kDirtyCheck && dataArea.cleanUnmount) || 339 scavError == R_NoMem || scavError == R_BadSig) { 340 // also need to bail when allocate fails in ScavSetUp or we bus error! 341 goto termScav; 342 } 343 344 isJournaled = CheckIfJournaled( &dataArea, false ); 345 if (isJournaled != 0 && 346 scanCount == 0 && 347 checkLevel != kForceCheck && 348 !(checkLevel == kPartialCheck && repairLevel == kForceRepairs)) { 349 if (fsckGetOutputStyle(dataArea.context) == fsckOutputTraditional) { 350 plog("fsck_hfs: Volume is journaled. No checking performed.\n"); 351 plog("fsck_hfs: Use the -f option to force checking.\n"); 352 } 353 scavError = 0; 354 goto termScav; 355 } 356 dataArea.calculatedVCB->vcbDriveNumber = fsReadRef; 357 dataArea.calculatedVCB->vcbDriverWriteRef = fsWriteRef; 358 359 // Only show the progress bar if we're doing a real check. 360 if (fsckContext) { 361 start_progress(); 362 } 363 364 // 365 // Now verify the volume 366 // 367 if ( scavError == noErr ) 368 ScavCtrl( &dataArea, scavVerify, &scavError ); 369 370EarlyExitLabel: 371 if (scavError == noErr && fsckGetVerbosity(dataArea.context) >= kDebugLog) 372 printVerifyStatus(&dataArea); 373 374 // Looped for maximum times for verify and repair. This was the last verify and 375 // we bail out if problems were found 376 if (scanCount >= kMaxReScan && (dataArea.RepLevel != repairLevelNoProblemsFound)) { 377 fsckPrint(dataArea.context, fsckVolumeNotRepairedTries, dataArea.volumeName, scanCount); 378 scavError = R_RFail; 379 goto termScav; 380 } 381 382 if ( dataArea.RepLevel == repairLevelUnrepairable ) 383 err = cdUnrepairableErr; 384 385 if ( !autoRepair && 386 (dataArea.RepLevel == repairLevelVolumeRecoverable || 387 dataArea.RepLevel == repairLevelCatalogBtreeRebuild || 388 dataArea.RepLevel == repairLevelVeryMinorErrors) ) { 389 fsckPrint(dataArea.context, fsckVolumeCorruptNeedsRepair, dataArea.volumeName); 390 scavError = R_VFail; 391 goto termScav; 392 } 393 394 if ( scavError == noErr && dataArea.RepLevel == repairLevelNoProblemsFound ) { 395 if (CONFIG_HFS_TRIM && 396 (dataArea.canWrite != 0) && (dataArea.writeRef != -1) && 397 IsTrimSupported()) 398 { 399 fsckPrint(dataArea.context, fsckTrimming); 400 TrimFreeBlocks(&dataArea); 401 } 402 403 if (scanCount == 0) { 404 fsckPrint(dataArea.context, fsckVolumeOK, dataArea.volumeName); 405 } else { 406 fsckPrint(dataArea.context, fsckRepairSuccessful, dataArea.volumeName); 407 } 408 } 409 410 // 411 // Repair the volume if it needs repairs, its repairable and we were able to unmount it 412 // 413 if ( dataArea.RepLevel == repairLevelNoProblemsFound && repairLevel == kForceRepairs ) 414 { 415 if (rebuildOptions & REBUILD_CATALOG) { 416 dataArea.CBTStat |= S_RebuildBTree; 417 } 418 if (rebuildOptions & REBUILD_EXTENTS) { 419 dataArea.EBTStat |= S_RebuildBTree; 420 } 421 if (rebuildOptions & REBUILD_ATTRIBUTE) { 422 dataArea.ABTStat |= S_RebuildBTree; 423 } 424 dataArea.RepLevel = repairLevelCatalogBtreeRebuild; 425 } 426 427 if ( ((scavError == noErr) || (scavError == errRebuildBtree)) && 428 (autoRepair == true) && 429 (dataArea.RepLevel != repairLevelUnrepairable) && 430 (dataArea.RepLevel != repairLevelNoProblemsFound) ) 431 { 432 // we cannot repair a volume when others have write access to the block device 433 // for the volume 434 435 if ( dataArea.canWrite == 0 ) { 436 scavError = R_WrErr; 437 fsckPrint(dataArea.context, fsckVolumeNotRepairedInUse, dataArea.volumeName); 438 } 439 else 440 ScavCtrl( &dataArea, scavRepair, &scavError ); 441 442 if ( scavError == noErr ) 443 { 444 *modified = 1; /* Report back that we made repairs */ 445 446 /* we just repaired a volume, so scan it again to check if it corrected everything properly */ 447 ScavCtrl( &dataArea, scavTerminate, &temp ); 448 repairLevel = kMajorRepairs; 449 checkLevel = kAlwaysCheck; 450 fsckPrint(dataArea.context, fsckRecheckingVolume); 451 scanCount++; 452 goto DoAgain; 453 } 454 else { 455 fsckPrint(dataArea.context, fsckVolumeNotRepaired, dataArea.volumeName); 456 } 457 } 458 else if ( scavError != noErr ) { 459 // Is this correct? 460 fsckPrint(dataArea.context, fsckVolumeVerifyIncomplete, dataArea.volumeName); 461 if ( fsckGetVerbosity(dataArea.context) >= kDebugLog ) 462 plog("\tvolume check failed with error %d \n", scavError); 463 } 464 465 // Set up structures for post processing 466 if ( (autoRepair == true) && (dataArea.fileIdentifierTable != nil) ) 467 { 468 // *repairInfo = *repairInfo | kVolumeHadOverlappingExtents; // Report back that volume has overlapping extents 469 fileIdentifierTable = (FileIdentifierTable *) AllocateMemory( GetHandleSize( (Handle) dataArea.fileIdentifierTable ) ); 470 CopyMemory( *(dataArea.fileIdentifierTable), fileIdentifierTable, GetHandleSize( (Handle) dataArea.fileIdentifierTable ) ); 471 } 472 473 474 // 475 // Post processing 476 // 477 if ( fileIdentifierTable != nil ) 478 { 479 DisposeMemory( fileIdentifierTable ); 480 } 481 482termScav: 483 if (gBlkListEntries != 0) 484 dumpblocklist(&dataArea); 485 486 if (err == noErr) { 487 err = scavError; 488 } 489 490 // 491 // Terminate the scavenger 492 // 493 494 if ( fsckGetVerbosity(dataArea.context) >= kDebugLog && 495 (err != noErr || dataArea.RepLevel != repairLevelNoProblemsFound) ) 496 PrintVolumeObject(); 497 if (err != 0 && embedded == 1) { 498 Buf_t *buf = NULL; 499 off_t offset = 1024; 500 uint32_t len = 512; // the size of an HFS+ volume header 501 int rv = CacheRead(&fscache, offset, len, &buf); 502 if (rv == 0) { 503 fprintf(stderr, "Offset %llu length %u:\n", offset, len); 504 DumpData(buf->Buffer, len); 505 CacheRelease(&fscache, buf, 0); 506 } else { 507 fprintf(stderr, "%s(%d): rv = %d\n", __FUNCTION__, __LINE__, rv); 508 } 509 fflush(stderr); 510 } 511 512 // If we have write access on volume and we are allowed to write, 513 // mark the volume clean/dirty 514 if ((fsWriteRef != -1) && (dataArea.canWrite != 0)) { 515 Boolean update; 516 if (scavError) { 517 // Mark volume dirty 518 CheckForClean(&dataArea, kMarkVolumeDirty, &update); 519 } else { 520 // Mark volume clean 521 CheckForClean(&dataArea, kMarkVolumeClean, &update); 522 } 523 if (update) { 524 /* Report back that volume was modified */ 525 *modified = 1; 526 } 527 } 528 ScavCtrl( &dataArea, scavTerminate, &temp ); // Note: use a temp var so that real scav error can be returned 529 530 if (fsckContext) { 531 fsckPrint( fsckContext, fsckProgress, 100); // End each run with 100% message, if desired 532 draw_progress(100); 533 end_progress(); 534 } 535 if (exitEarly && majorErrors) 536 err = MAJOREXIT; 537 538 if (msgCounts) { 539 free(msgCounts); 540 } 541 542 return( err ); 543} 544 545 546/*------------------------------------------------------------------------------ 547 548Function: ScavCtrl - (Scavenger Control) 549 550Function: Controls the scavenging process. Interfaces with the User Interface 551 Layer (written in PASCAL). 552 553Input: ScavOp - scavenging operation to be performed: 554 555 scavInitialize = start initial volume check 556 scavVerify = start verify 557 scavRepair = start repair 558 scavTerminate = finished scavenge 559 560 GPtr - pointer to scavenger global area 561 562 563Output: ScavRes - scavenge result code (R_xxx, or 0 if no error) 564 565------------------------------------------------------------------------------*/ 566 567void ScavCtrl( SGlobPtr GPtr, UInt32 ScavOp, short *ScavRes ) 568{ 569 OSErr result; 570 unsigned int stat; 571#if SHOW_ELAPSED_TIMES 572 struct timeval myStartTime; 573 struct timeval myEndTime; 574 struct timeval myElapsedTime; 575 struct timezone zone; 576#endif 577 578 // 579 // initialize some stuff 580 // 581 result = noErr; // assume good status 582 *ScavRes = 0; 583 GPtr->ScavRes = 0; 584 585 // 586 // dispatch next scavenge operation 587 // 588 switch ( ScavOp ) 589 { 590 case scavInitialize: // INITIAL VOLUME CHECK 591 { 592 Boolean modified; 593 int clean; 594 595 if ( ( result = ScavSetUp( GPtr ) ) ) // set up BEFORE CheckForStop 596 break; 597 if ( IsBlueBoxSharedDrive( GPtr->DrvPtr ) ) 598 break; 599 if ( ( result = CheckForStop( GPtr ) ) ) // in order to initialize wrCnt 600 break; 601 602 /* Call for all chkLevel options and check return value only 603 * for kDirtyCheck for preen option and kNeverCheck for quick option 604 */ 605 clean = CheckForClean(GPtr, kCheckVolume, &modified); 606 if ((GPtr->chkLevel == kDirtyCheck) || (GPtr->chkLevel == kNeverCheck)) { 607 if (clean == 1) { 608 /* volume was unmounted cleanly */ 609 GPtr->cleanUnmount = true; 610 break; 611 } 612 613 if (GPtr->chkLevel == kNeverCheck) { 614 if (clean == -1) 615 result = R_BadSig; 616 else if (clean == 0) { 617 /* 618 * We lie for journaled file systems since 619 * they get cleaned up in mount by replaying 620 * the journal. 621 * Note: CheckIfJournaled will return negative 622 * if it finds lastMountedVersion = FSK!. 623 */ 624 if (CheckIfJournaled(GPtr, false)) 625 GPtr->cleanUnmount = true; 626 else 627 result = R_Dirty; 628 } 629 break; 630 } 631 } 632 633 if (CheckIfJournaled(GPtr, false) 634 && GPtr->chkLevel != kForceCheck 635 && !(GPtr->chkLevel == kPartialCheck && GPtr->repairLevel == kForceRepairs) 636 && !(GPtr->chkLevel == kAlwaysCheck && GPtr->repairLevel == kMajorRepairs)) { 637 break; 638 } 639 640 if (GPtr->liveVerifyState) { 641 fsckPrint(GPtr->context, hfsLiveVerifyCheck); 642 } else if (GPtr->canWrite == 0 && nflag == 0) { 643 fsckPrint(GPtr->context, hfsVerifyVolWithWrite); 644 } 645 646 /* 647 * In the first pass, if fsck_hfs is verifying a 648 * journaled volume, and it's not a live verification, 649 * check to see if the journal is empty. If it is not, 650 * flag it as a journal error, and print a message. 651 * (A live verify will almost certainly have a non-empty 652 * journal, but that should be safe in this case due 653 * to the freeze command flushing everything.) 654 */ 655 if ((GPtr->scanCount == 0) && 656 (CheckIfJournaled(GPtr, true) == 1) && 657 (GPtr->canWrite == 0 || GPtr->writeRef == -1) && 658 (lflag == 0)) { 659 fsckJournalInfo_t jnlInfo = { 0 }; 660 UInt64 numBlocks; 661 UInt32 blockSize; 662 jnlInfo.jnlfd = -1; 663 664 if (IsJournalEmpty(GPtr, &jnlInfo) == 0) { 665 // disable_journal can currently only be set with debug enabled 666 if (disable_journal) { 667 fsckPrint(GPtr->context, E_DirtyJournal); 668 GPtr->JStat |= S_DirtyJournal; 669 } else { 670 (void)GetDeviceSize(GPtr->calculatedVCB->vcbDriveNumber, &numBlocks, &blockSize); 671#if 0 672 // For debugging the cache. WAY to verbose to run with even normal debug 673 if (debug) { 674 printf("Before journal replay\n"); 675 dumpCache(&fscache); 676 } 677#endif 678 if (journal_open(jnlInfo.jnlfd, 679 jnlInfo.jnlOffset, 680 jnlInfo.jnlSize, 681 blockSize, 682 0, 683 jnlInfo.name, 684 ^(off_t start, void *data, size_t len) { 685 Buf_t *buf; 686 int rv; 687 rv = CacheRead(&fscache, start, (int)len, &buf); 688 if (rv != 0) 689 abort(); 690 memcpy(buf->Buffer, data, len); 691 rv = CacheWrite(&fscache, buf, 0, kLockWrite); 692 if (rv != 0) 693 abort(); 694 return 0;} 695 ) == -1) { 696 fsckPrint(GPtr->context, E_DirtyJournal); 697 GPtr->JStat |= S_DirtyJournal; 698 } else if (debug) { 699 plog("Journal replay simulation succeeded\n"); 700#if 0 701 // Still way too verbose to run 702 dumpCache(&fscache); 703#endif 704 } 705 } 706 } else { 707 if (debug) 708 plog("Journal is empty\n"); 709 } 710 if (jnlInfo.jnlfd != -1) 711 close(jnlInfo.jnlfd); 712 if (jnlInfo.name != NULL) 713 free(jnlInfo.name); 714 } 715 716 result = IVChk( GPtr ); 717 718 break; 719 } 720 721 case scavVerify: // VERIFY 722 { 723 724#if SHOW_ELAPSED_TIMES 725 gettimeofday( &myStartTime, &zone ); 726#endif 727 728 /* Initialize volume bitmap structure */ 729 if ( BitMapCheckBegin(GPtr) != 0) 730 break; 731 732#if SHOW_ELAPSED_TIMES 733 gettimeofday( &myEndTime, &zone ); 734 timersub( &myEndTime, &myStartTime, &myElapsedTime ); 735 plog( "\n%s - BitMapCheck elapsed time \n", __FUNCTION__ ); 736 plog( "########## secs %d msecs %d \n\n", 737 myElapsedTime.tv_sec, myElapsedTime.tv_usec ); 738#endif 739 740 if ( IsBlueBoxSharedDrive( GPtr->DrvPtr ) ) 741 break; 742 if ( ( result = CheckForStop( GPtr ) ) ) 743 break; 744 745#if SHOW_ELAPSED_TIMES 746 gettimeofday( &myStartTime, &zone ); 747#endif 748 749 /* Create calculated BTree structures */ 750 if ( ( result = CreateExtentsBTreeControlBlock( GPtr ) ) ) 751 break; 752 if ( ( result = CreateCatalogBTreeControlBlock( GPtr ) ) ) 753 break; 754 if ( ( result = CreateAttributesBTreeControlBlock( GPtr ) ) ) 755 break; 756 if ( ( result = CreateExtendedAllocationsFCB( GPtr ) ) ) 757 break; 758 759#if SHOW_ELAPSED_TIMES 760 gettimeofday( &myEndTime, &zone ); 761 timersub( &myEndTime, &myStartTime, &myElapsedTime ); 762 plog( "\n%s - create control blocks elapsed time \n", __FUNCTION__ ); 763 plog( ">>>>>>>>>>>>> secs %d msecs %d \n\n", 764 myElapsedTime.tv_sec, myElapsedTime.tv_usec ); 765#endif 766 767 // Now that preflight of the BTree structures is calculated, compute the CheckDisk items 768 CalculateItemCount( GPtr, &GPtr->itemsToProcess, &GPtr->onePercent ); 769 GPtr->itemsProcessed += GPtr->onePercent; // We do this 4 times as set up in CalculateItemCount() to smooth the scroll 770 771 if ( ( result = VLockedChk( GPtr ) ) ) 772 break; 773 774 GPtr->itemsProcessed += GPtr->onePercent; // We do this 4 times as set up in CalculateItemCount() to smooth the scroll 775 fsckPrint(GPtr->context, hfsExtBTCheck); 776 777#if SHOW_ELAPSED_TIMES 778 gettimeofday( &myStartTime, &zone ); 779#endif 780 781 /* Verify extent btree structure */ 782 if ((result = ExtBTChk(GPtr))) 783 break; 784 785#if SHOW_ELAPSED_TIMES 786 gettimeofday( &myEndTime, &zone ); 787 timersub( &myEndTime, &myStartTime, &myElapsedTime ); 788 plog( "\n%s - ExtBTChk elapsed time \n", __FUNCTION__ ); 789 plog( ">>>>>>>>>>>>> secs %d msecs %d \n\n", 790 myElapsedTime.tv_sec, myElapsedTime.tv_usec ); 791#endif 792 793 if ((result = CheckForStop(GPtr))) 794 break; 795 796 GPtr->itemsProcessed += GPtr->onePercent; // We do this 4 times as set up in CalculateItemCount() to smooth the scroll 797 798 /* Check extents of bad block file */ 799 if ((result = BadBlockFileExtentCheck(GPtr))) 800 break; 801 if ((result = CheckForStop(GPtr))) 802 break; 803 804 GPtr->itemsProcessed += GPtr->onePercent; // We do this 4 times as set up in CalculateItemCount() to smooth the scroll 805 GPtr->itemsProcessed += GPtr->onePercent; 806 fsckPrint(GPtr->context, hfsCatBTCheck); 807 808#if SHOW_ELAPSED_TIMES 809 gettimeofday( &myStartTime, &zone ); 810#endif 811 812 if ( GPtr->chkLevel == kPartialCheck ) 813 { 814 /* skip the rest of the verify code path the first time */ 815 /* through when we are rebuilding the catalog B-Tree file. */ 816 /* we will be back here after the rebuild. */ 817 if (GPtr->rebuildOptions & REBUILD_CATALOG) { 818 GPtr->CBTStat |= S_RebuildBTree; 819 } 820 if (GPtr->rebuildOptions & REBUILD_EXTENTS) { 821 GPtr->EBTStat |= S_RebuildBTree; 822 } 823 if (GPtr->rebuildOptions & REBUILD_ATTRIBUTE) { 824 GPtr->ABTStat |= S_RebuildBTree; 825 } 826 result = errRebuildBtree; 827 break; 828 } 829 830 /* Check catalog btree. For given fileID, the function accounts 831 * for all extents existing in catalog record as well as in 832 * overflow extent btree 833 */ 834 if ((result = CheckCatalogBTree(GPtr))) 835 break; 836 837#if SHOW_ELAPSED_TIMES 838 gettimeofday( &myEndTime, &zone ); 839 timersub( &myEndTime, &myStartTime, &myElapsedTime ); 840 plog( "\n%s - CheckCatalogBTree elapsed time \n", __FUNCTION__ ); 841 plog( ">>>>>>>>>>>>> secs %d msecs %d \n\n", 842 myElapsedTime.tv_sec, myElapsedTime.tv_usec ); 843#endif 844 845 if ((result = CheckForStop(GPtr))) 846 break; 847 848 if (scanflag == 0) { 849 fsckPrint(GPtr->context, hfsCatHierCheck); 850 851#if SHOW_ELAPSED_TIMES 852 gettimeofday( &myStartTime, &zone ); 853#endif 854 855 /* Check catalog hierarchy */ 856 if ((result = CatHChk(GPtr))) 857 break; 858 859#if SHOW_ELAPSED_TIMES 860 gettimeofday( &myEndTime, &zone ); 861 timersub( &myEndTime, &myStartTime, &myElapsedTime ); 862 plog( "\n%s - CatHChk elapsed time \n", __FUNCTION__ ); 863 plog( ">>>>>>>>>>>>> secs %d msecs %d \n\n", 864 myElapsedTime.tv_sec, myElapsedTime.tv_usec ); 865#endif 866 867 if ((result = CheckForStop(GPtr))) 868 break; 869 870 if (VolumeObjectIsHFSX(GPtr)) { 871 result = CheckFolderCount(GPtr); 872 if (result) 873 break; 874 875 if ((result=CheckForStop(GPtr))) 876 break; 877 } 878 } 879 /* Check attribute btree. The function accounts for all extents 880 * for extended attributes whose values are stored in 881 * allocation blocks 882 */ 883 if ((result = AttrBTChk(GPtr))) 884 break; 885 886 if ((result = CheckForStop(GPtr))) 887 break; 888 889 /* 890 * fsck_hfs has accounted for all valid allocation blocks by 891 * traversing all catalog records and attribute records. 892 * These traversals may have found overlapping extents. Note 893 * that the overlapping extents are detected in CaptureBitmapBits 894 * when it tries to set a bit corresponding to allocation block 895 * and finds that it is already set. Therefore fsck_hfs does not 896 * know the orignal file involved overlapped extents. 897 */ 898 if (GPtr->VIStat & S_OverlappingExtents) { 899 /* Find original files involved in overlapped extents */ 900 result = FindOrigOverlapFiles(GPtr); 901 if (result) { 902 break; 903 } 904 905 /* Print all unique overlapping file IDs and paths */ 906 (void) PrintOverlapFiles(GPtr); 907 } 908 909 if (scanflag == 0) { 910 /* Directory inodes store first link information in 911 * an extended attribute. Therefore start directory 912 * hard link check after extended attribute checks. 913 */ 914 result = dirhardlink_check(GPtr); 915 /* On error or unrepairable corruption, stop the verification */ 916 if ((result != 0) || (GPtr->CatStat & S_LinkErrNoRepair)) { 917 if (result == 0) { 918 result = -1; 919 } 920 921 break; 922 } 923 } 924 925 fsckPrint(GPtr->context, hfsVolBitmapCheck); 926 927#if SHOW_ELAPSED_TIMES 928 gettimeofday( &myStartTime, &zone ); 929#endif 930 931 /* Compare in-memory volume bitmap with on-disk bitmap */ 932 if ((result = CheckVolumeBitMap(GPtr, false))) 933 break; 934 935#if SHOW_ELAPSED_TIMES 936 gettimeofday( &myEndTime, &zone ); 937 timersub( &myEndTime, &myStartTime, &myElapsedTime ); 938 plog( "\n%s - CheckVolumeBitMap elapsed time \n", __FUNCTION__ ); 939 plog( ">>>>>>>>>>>>> secs %d msecs %d \n\n", 940 myElapsedTime.tv_sec, myElapsedTime.tv_usec ); 941#endif 942 943 if ((result = CheckForStop(GPtr))) 944 break; 945 946 fsckPrint(GPtr->context, hfsVolInfoCheck); 947 948#if SHOW_ELAPSED_TIMES 949 gettimeofday( &myStartTime, &zone ); 950#endif 951 952 /* Verify volume level information */ 953 if ((result = VInfoChk(GPtr))) 954 break; 955 956#if SHOW_ELAPSED_TIMES 957 gettimeofday( &myEndTime, &zone ); 958 timersub( &myEndTime, &myStartTime, &myElapsedTime ); 959 plog( "\n%s - VInfoChk elapsed time \n", __FUNCTION__ ); 960 plog( ">>>>>>>>>>>>> secs %d msecs %d \n\n", 961 myElapsedTime.tv_sec, myElapsedTime.tv_usec ); 962#endif 963 964 stat = GPtr->VIStat | GPtr->ABTStat | GPtr->EBTStat | GPtr->CBTStat | 965 GPtr->CatStat | GPtr->JStat; 966 967 if ( stat != 0 ) 968 { 969 if ( (GPtr->RepLevel == repairLevelNoProblemsFound) || (GPtr->RepLevel == repairLevelVolumeRecoverable) ) 970 { 971 // 2200106, We isolate very minor errors so that if the volume cannot be unmounted 972 // CheckDisk will just return noErr 973 unsigned int minorErrors = (GPtr->CatStat & ~S_LockedDirName) | 974 GPtr->VIStat | GPtr->ABTStat | GPtr->EBTStat | GPtr->CBTStat | GPtr->JStat; 975 if ( minorErrors == 0 ) 976 GPtr->RepLevel = repairLevelVeryMinorErrors; 977 else 978 GPtr->RepLevel = repairLevelVolumeRecoverable; 979 } 980 } 981 else if ( GPtr->RepLevel == repairLevelNoProblemsFound ) 982 { 983 } 984 985 GPtr->itemsProcessed = GPtr->itemsToProcess; 986 result = CheckForStop(GPtr); // one last check for modified volume 987 break; 988 } 989 990 case scavRepair: // REPAIR 991 { 992 if ( IsBlueBoxSharedDrive( GPtr->DrvPtr ) ) 993 break; 994 if ( ( result = CheckForStop(GPtr) ) ) 995 break; 996 if ( GPtr->CBTStat & S_RebuildBTree 997 || GPtr->EBTStat & S_RebuildBTree 998 || GPtr->ABTStat & S_RebuildBTree) { 999// fsckPrint(GPtr->context, hfsRebuildCatalogBTree); 1000// fsckPrint(GPtr->context, hfsRebuildAttrBTree); 1001// actually print nothing yet -- we print out when we are rebuilding the trees 1002 } else { 1003 fsckPrint(GPtr->context, fsckRepairingVolume); 1004 if (embedded == 1 && debug == 0) 1005 fsckPrint(GPtr->context, fsckLimitedRepairs); 1006 } 1007 result = RepairVolume( GPtr ); 1008 break; 1009 } 1010 1011 case scavTerminate: // CLEANUP AFTER SCAVENGE 1012 { 1013 result = ScavTerm(GPtr); 1014 break; 1015 } 1016 } // end ScavOp switch 1017 1018 1019 // 1020 // Map internal error codes to scavenger result codes 1021 // 1022 if ( (result < 0) || (result > Max_RCode) ) 1023 { 1024 switch ( ScavOp ) 1025 { 1026 case scavInitialize: 1027 case scavVerify: 1028 if ( result == ioErr ) 1029 result = R_RdErr; 1030 else if ( result == errRebuildBtree ) 1031 { 1032 GPtr->RepLevel = repairLevelCatalogBtreeRebuild; 1033 break; 1034 } 1035 else 1036 result = R_VFail; 1037 GPtr->RepLevel = repairLevelUnrepairable; 1038 break; 1039 case scavRepair: 1040 result = R_RFail; 1041 break; 1042 default: 1043 result = R_IntErr; 1044 } 1045 } 1046 1047 GPtr->ScavRes = result; 1048 1049 *ScavRes = result; 1050 1051} // end of ScavCtrl 1052 1053 1054 1055/*------------------------------------------------------------------------------ 1056 1057Function: CheckForStop 1058 1059Function: Checks for the user hitting the "STOP" button during a scavenge, 1060 which interrupts the operation. Additionally, we monitor the write 1061 count of a mounted volume, to be sure that the volume is not 1062 modified by another app while we scavenge. 1063 1064Input: GPtr - pointer to scavenger global area 1065 1066Output: Function result: 1067 0 - ok to continue 1068 R_UInt - STOP button hit 1069 R_Modified - another app has touched the volume 1070-------------------------------------------------------------------------------*/ 1071 1072short CheckForStop( SGlob *GPtr ) 1073{ 1074 OSErr err = noErr; // Initialize err to noErr 1075 long ticks = TickCount(); 1076 UInt16 dfaStage = (UInt16) GetDFAStage(); 1077 1078 //plog("%d, %d", dfaStage, kAboutToRepairStage); 1079 1080 //if ( ((ticks - 10) > GPtr->lastTickCount) || (dfaStage == kAboutToRepairStage) ) // To reduce cursor flicker on fast machines, call through on a timed interval 1081 //{ 1082 if ( GPtr->userCancelProc != nil ) 1083 { 1084 UInt64 progress = 0; 1085 Boolean progressChanged; 1086 // UInt16 elapsedTicks; 1087 1088 if ( dfaStage != kRepairStage ) 1089 { 1090 progress = GPtr->itemsProcessed * 100; 1091 progress /= GPtr->itemsToProcess; 1092 progressChanged = ( progress != GPtr->lastProgress ); 1093 GPtr->lastProgress = progress; 1094 1095 #if( DisplayTimeRemaining ) 1096 if ( (progressChanged) && (progress > 5) ) 1097 { 1098 elapsedTicks = TickCount() - GPtr->startTicks; 1099 GPtr->secondsRemaining = ( ( ( 100 * elapsedTicks ) / progress ) - elapsedTicks ) / 60; 1100 } 1101 #endif 1102 err = CallUserCancelProc( GPtr->userCancelProc, (UInt16)progress, (UInt16)GPtr->secondsRemaining, progressChanged, dfaStage, GPtr->context, GPtr->scanCount ); 1103 } 1104 else 1105 { 1106 (void) CallUserCancelProc( GPtr->userCancelProc, (UInt16)progress, 0, false, dfaStage, GPtr->context, GPtr->scanCount ); 1107 } 1108 1109 } 1110 1111 if ( err != noErr ) 1112 err = R_UInt; 1113 #if 0 1114 if ( GPtr->realVCB ) // If the volume is mounted 1115 if ( GPtr->realVCB->vcbWrCnt != GPtr->wrCnt ) 1116 err = R_Modified; // Its been modified behind our back 1117 #endif 1118 GPtr->lastTickCount = ticks; 1119 //} 1120 1121 return ( err ); 1122} 1123 1124 1125 1126/*------------------------------------------------------------------------------ 1127 1128Function: ScavSetUp - (Scavenger Set Up) 1129 1130Function: Sets up scavenger globals for a new scavenge operation. Memory is 1131 allocated for the Scavenger's static data structures (VCB, FCBs, 1132 BTCBs, and TPTs). The contents of the data structures are 1133 initialized to zero. 1134 1135Input: GPtr - pointer to scavenger global area 1136 1137Output: ScavSetUp - function result: 1138 0 = no error 1139 n = error code 1140------------------------------------------------------------------------------*/ 1141 1142struct ScavStaticStructures { 1143 SVCB vcb; 1144 SFCB fcbList[6]; 1145 BTreeControlBlock btcb[4]; // 4 btcb's 1146 SBTPT btreePath; // scavenger BTree path table 1147}; 1148typedef struct ScavStaticStructures ScavStaticStructures; 1149 1150 1151static int ScavSetUp( SGlob *GPtr) 1152{ 1153 OSErr err; 1154 SVCB * vcb; 1155#if !BSD 1156 DrvQEl *drvP; 1157 short ioRefNum; 1158#endif 1159 1160 GPtr->MinorRepairsP = nil; 1161 1162 GPtr->itemsProcessed = 0; 1163 GPtr->lastProgress = 0; 1164 GPtr->startTicks = TickCount(); 1165 1166 // 1167 // allocate the static data structures (VCB, FCB's, BTCB'S, DPT and BTPT) 1168 // 1169 { 1170 ScavStaticStructures *pointer; 1171 1172 pointer = (ScavStaticStructures *) AllocateClearMemory( sizeof(ScavStaticStructures) ); 1173 if ( pointer == nil ) { 1174 if ( fsckGetVerbosity(GPtr->context) >= kDebugLog ) { 1175 plog( "\t error %d - could not allocate %ld bytes of memory \n", 1176 R_NoMem, sizeof(ScavStaticStructures) ); 1177 } 1178 return( R_NoMem ); 1179 } 1180 GPtr->scavStaticPtr = pointer; 1181 1182 GPtr->DirPTPtr = AllocateClearMemory(sizeof(SDPR) * CMMaxDepth); 1183 if ( GPtr->DirPTPtr == nil ) { 1184 if ( fsckGetVerbosity(GPtr->context) >= kDebugLog ) { 1185 plog( "\t error %d - could not allocate %ld bytes of memory \n", 1186 R_NoMem, sizeof(SDPR) * CMMaxDepth ); 1187 } 1188 return( R_NoMem ); 1189 } 1190 GPtr->dirPathCount = CMMaxDepth; 1191 1192 GPtr->calculatedVCB = vcb = &pointer->vcb; 1193 vcb->vcbGPtr = GPtr; 1194 1195 GPtr->FCBAPtr = (Ptr) &pointer->fcbList; 1196 GPtr->calculatedExtentsFCB = &pointer->fcbList[0]; 1197 GPtr->calculatedCatalogFCB = &pointer->fcbList[1]; 1198 GPtr->calculatedAllocationsFCB = &pointer->fcbList[2]; 1199 GPtr->calculatedAttributesFCB = &pointer->fcbList[3]; 1200 GPtr->calculatedStartupFCB = &pointer->fcbList[4]; 1201 GPtr->calculatedRepairFCB = &pointer->fcbList[5]; 1202 1203 GPtr->calculatedExtentsBTCB = &pointer->btcb[0]; 1204 GPtr->calculatedCatalogBTCB = &pointer->btcb[1]; 1205 GPtr->calculatedRepairBTCB = &pointer->btcb[2]; 1206 GPtr->calculatedAttributesBTCB = &pointer->btcb[3]; 1207 1208 GPtr->BTPTPtr = (SBTPT*) &pointer->btreePath; 1209 } 1210 1211 1212 SetDFAStage( kVerifyStage ); 1213 SetFCBSPtr( GPtr->FCBAPtr ); 1214 1215 // 1216 // locate the driveQ element for drive being scavenged 1217 // 1218 GPtr->DrvPtr = 0; // <8> initialize so we can know if drive disappears 1219 1220 // 1221 // Set up Real structures 1222 // 1223#if !BSD 1224 err = FindDrive( &ioRefNum, &(GPtr->DrvPtr), GPtr->DrvNum ); 1225#endif 1226 if ( IsBlueBoxSharedDrive( GPtr->DrvPtr ) ) 1227 return noErr; 1228 1229 err = GetVolumeFeatures( GPtr ); // Sets up GPtr->volumeFeatures and GPtr->realVCB 1230 1231#if !BSD 1232 if ( GPtr->DrvPtr == NULL ) // <8> drive is no longer there! 1233 return ( R_NoVol ); 1234 else 1235 drvP = GPtr->DrvPtr; 1236 1237 // Save current value of vcbWrCnt, to detect modifications to volume by other apps etc 1238 if ( GPtr->volumeFeatures & volumeIsMountedMask ) 1239 { 1240 FlushVol( nil, GPtr->realVCB->vcbVRefNum ); // Ask HFS to update all changes to disk 1241 GPtr->wrCnt = GPtr->realVCB->vcbWrCnt; // Remember write count after writing changes 1242 } 1243#endif 1244 1245 // Finish initializing the VCB 1246 1247 // The calculated structures 1248#if BSD 1249 InitBlockCache(vcb); 1250 vcb->vcbDriveNumber = GPtr->DrvNum; 1251 vcb->vcbDriverReadRef = GPtr->DrvNum; 1252 vcb->vcbDriverWriteRef = -1; /* XXX need to get real fd here */ 1253#else 1254 vcb->vcbDriveNumber = drvP->dQDrive; 1255 vcb->vcbDriverReadRef = drvP->dQRefNum; 1256 vcb->vcbDriverWriteRef = drvP->dQRefNum; 1257 vcb->vcbFSID = drvP->dQFSID; 1258#endif 1259// vcb->vcbVRefNum = Vol_RefN; 1260 1261 // 1262 // finish initializing the FCB's 1263 // 1264 { 1265 SFCB *fcb; 1266 1267 // Create Calculated Extents FCB 1268 fcb = GPtr->calculatedExtentsFCB; 1269 fcb->fcbFileID = kHFSExtentsFileID; 1270 fcb->fcbVolume = vcb; 1271 fcb->fcbBtree = GPtr->calculatedExtentsBTCB; 1272 vcb->vcbExtentsFile = fcb; 1273 1274 // Create Calculated Catalog FCB 1275 fcb = GPtr->calculatedCatalogFCB; 1276 fcb->fcbFileID = kHFSCatalogFileID; 1277 fcb->fcbVolume = vcb; 1278 fcb->fcbBtree = GPtr->calculatedCatalogBTCB; 1279 vcb->vcbCatalogFile = fcb; 1280 1281 // Create Calculated Allocations FCB 1282 fcb = GPtr->calculatedAllocationsFCB; 1283 fcb->fcbFileID = kHFSAllocationFileID; 1284 fcb->fcbVolume = vcb; 1285 fcb->fcbBtree = NULL; // no BitMap B-Tree 1286 vcb->vcbAllocationFile = fcb; 1287 1288 // Create Calculated Attributes FCB 1289 fcb = GPtr->calculatedAttributesFCB; 1290 fcb->fcbFileID = kHFSAttributesFileID; 1291 fcb->fcbVolume = vcb; 1292 fcb->fcbBtree = GPtr->calculatedAttributesBTCB; 1293 vcb->vcbAttributesFile = fcb; 1294 1295 /* Create Calculated Startup FCB */ 1296 fcb = GPtr->calculatedStartupFCB; 1297 fcb->fcbFileID = kHFSStartupFileID; 1298 fcb->fcbVolume = vcb; 1299 fcb->fcbBtree = NULL; 1300 vcb->vcbStartupFile = fcb; 1301 } 1302 1303 // finish initializing the BTCB's 1304 { 1305 BTreeControlBlock *btcb; 1306 1307 btcb = GPtr->calculatedExtentsBTCB; // calculatedExtentsBTCB 1308 btcb->fcbPtr = GPtr->calculatedExtentsFCB; 1309 btcb->getBlockProc = GetFileBlock; 1310 btcb->releaseBlockProc = ReleaseFileBlock; 1311 btcb->setEndOfForkProc = SetEndOfForkProc; 1312 1313 btcb = GPtr->calculatedCatalogBTCB; // calculatedCatalogBTCB 1314 btcb->fcbPtr = GPtr->calculatedCatalogFCB; 1315 btcb->getBlockProc = GetFileBlock; 1316 btcb->releaseBlockProc = ReleaseFileBlock; 1317 btcb->setEndOfForkProc = SetEndOfForkProc; 1318 1319 btcb = GPtr->calculatedAttributesBTCB; // calculatedAttributesBTCB 1320 btcb->fcbPtr = GPtr->calculatedAttributesFCB; 1321 btcb->getBlockProc = GetFileBlock; 1322 btcb->releaseBlockProc = ReleaseFileBlock; 1323 btcb->setEndOfForkProc = SetEndOfForkProc; 1324 } 1325 1326 1327 // 1328 // Initialize some global stuff 1329 // 1330 1331 GPtr->RepLevel = repairLevelNoProblemsFound; 1332 GPtr->ErrCode = 0; 1333 GPtr->IntErr = noErr; 1334 GPtr->VIStat = 0; 1335 GPtr->ABTStat = 0; 1336 GPtr->EBTStat = 0; 1337 GPtr->CBTStat = 0; 1338 GPtr->CatStat = 0; 1339 GPtr->VeryMinorErrorsStat = 0; 1340 GPtr->JStat = 0; 1341 1342 /* Assume that the volume is dirty unmounted */ 1343 GPtr->cleanUnmount = false; 1344 1345 // 1346 // Initialize VolumeObject 1347 // 1348 1349 InitializeVolumeObject( GPtr ); 1350 1351 /* Check if the volume type of initialized object is valid. If not, return error */ 1352 if (VolumeObjectIsValid() == false) { 1353 return (R_BadSig); 1354 } 1355 1356 // Keep a valid file id list for HFS volumes 1357 GPtr->validFilesList = (UInt32**)NewHandle( 0 ); 1358 if ( GPtr->validFilesList == nil ) { 1359 if ( fsckGetVerbosity(GPtr->context) >= kDebugLog ) { 1360 plog( "\t error %d - could not allocate file ID list \n", R_NoMem ); 1361 } 1362 return( R_NoMem ); 1363 } 1364 1365 // Convert the security attribute name from utf8 to utf16. This will 1366 // avoid repeated conversion of all extended attributes to compare with 1367 // security attribute name 1368 (void) utf_decodestr((unsigned char *)KAUTH_FILESEC_XATTR, strlen(KAUTH_FILESEC_XATTR), GPtr->securityAttrName, &GPtr->securityAttrLen, sizeof(GPtr->securityAttrName)); 1369 1370 return( noErr ); 1371 1372} /* end of ScavSetUp */ 1373 1374 1375 1376 1377/*------------------------------------------------------------------------------ 1378 1379Function: ScavTerm - (Scavenge Termination)) 1380 1381Function: Terminates the current scavenging operation. Memory for the 1382 VCB, FCBs, BTCBs, volume bit map, and BTree bit maps is 1383 released. 1384 1385Input: GPtr - pointer to scavenger global area 1386 1387Output: ScavTerm - function result: 1388 0 = no error 1389 n = error code 1390------------------------------------------------------------------------------*/ 1391 1392static int ScavTerm( SGlobPtr GPtr ) 1393{ 1394 SFCB *fcbP; 1395 BTreeControlBlock *btcbP; 1396 RepairOrderPtr rP; 1397 OSErr err; 1398 ExtentsTable **extentsTableH; 1399 ExtentInfo *curExtentInfo; 1400 int i; 1401 1402 (void) BitMapCheckEnd(); 1403 1404 while( (rP = GPtr->MinorRepairsP) != nil ) // loop freeing leftover (undone) repair orders 1405 { 1406 GPtr->MinorRepairsP = rP->link; // (in case repairs were not made) 1407 DisposeMemory(rP); 1408 err = MemError(); 1409 } 1410 1411 if( GPtr->validFilesList != nil ) 1412 DisposeHandle( (Handle) GPtr->validFilesList ); 1413 1414 if( GPtr->overlappedExtents != nil ) { 1415 extentsTableH = GPtr->overlappedExtents; 1416 1417 /* Overlapped extents list also allocated memory for attribute name */ 1418 for (i=0; i<(**extentsTableH).count; i++) { 1419 curExtentInfo = &((**extentsTableH).extentInfo[i]); 1420 1421 /* Deallocate memory for attribute name, if any */ 1422 if (curExtentInfo->attrname) { 1423 free(curExtentInfo->attrname); 1424 } 1425 } 1426 1427 DisposeHandle( (Handle) GPtr->overlappedExtents ); 1428 } 1429 1430 if( GPtr->fileIdentifierTable != nil ) 1431 DisposeHandle( (Handle) GPtr->fileIdentifierTable ); 1432 1433 if( GPtr->calculatedVCB == nil ) // already freed? 1434 return( noErr ); 1435 1436 // If the FCB's and BTCB's have been set up, dispose of them 1437 fcbP = GPtr->calculatedExtentsFCB; // release extent file BTree bit map 1438 if ( fcbP != nil ) 1439 { 1440 btcbP = (BTreeControlBlock*)fcbP->fcbBtree; 1441 if ( btcbP != nil) 1442 { 1443 if( btcbP->refCon != nil ) 1444 { 1445 if(((BTreeExtensionsRec*)btcbP->refCon)->BTCBMPtr != nil) 1446 { 1447 DisposeMemory(((BTreeExtensionsRec*)btcbP->refCon)->BTCBMPtr); 1448 err = MemError(); 1449 } 1450 DisposeMemory( (Ptr)btcbP->refCon ); 1451 err = MemError(); 1452 btcbP->refCon = nil; 1453 } 1454 1455 fcbP = GPtr->calculatedCatalogFCB; // release catalog BTree bit map 1456 btcbP = (BTreeControlBlock*)fcbP->fcbBtree; 1457 1458 if( btcbP->refCon != nil ) 1459 { 1460 if(((BTreeExtensionsRec*)btcbP->refCon)->BTCBMPtr != nil) 1461 { 1462 DisposeMemory(((BTreeExtensionsRec*)btcbP->refCon)->BTCBMPtr); 1463 err = MemError(); 1464 } 1465 DisposeMemory( (Ptr)btcbP->refCon ); 1466 err = MemError(); 1467 btcbP->refCon = nil; 1468 } 1469 } 1470 } 1471 1472 DisposeMemory(GPtr->DirPTPtr); 1473 DisposeMemory((ScavStaticStructures *)GPtr->scavStaticPtr); 1474 GPtr->scavStaticPtr = nil; 1475 GPtr->calculatedVCB = nil; 1476 1477 return( noErr ); 1478} 1479 1480#define BLUE_BOX_SHARED_DRVR_NAME "\p.BlueBoxShared" 1481#define BLUE_BOX_FLOPPY_WHERE_STRING "\pdisk%d (Shared)" 1482#define SONY_DRVR_NAME "\p.Sony" 1483 1484/*------------------------------------------------------------------------------ 1485 1486Routine: IsBlueBoxSharedDrive 1487 1488Function: Given a DQE address, return a boolean that determines whether 1489 or not a drive is a Blue Box disk being accessed via Shared mode. 1490 Such drives do not support i/o and cannot be scavenged. 1491 1492Input: Arg 1 - DQE pointer 1493 1494Output: D0.L - 0 if drive not to be used 1495 1 otherwise 1496------------------------------------------------------------------------------*/ 1497 1498struct IconAndStringRec { 1499 char icon[ 256 ]; 1500 Str255 string; 1501}; 1502typedef struct IconAndStringRec IconAndStringRec, * IconAndStringRecPtr; 1503 1504 1505Boolean IsBlueBoxSharedDrive ( DrvQElPtr dqPtr ) 1506{ 1507#if 0 1508 Str255 blueBoxSharedDriverName = BLUE_BOX_SHARED_DRVR_NAME; 1509 Str255 blueBoxFloppyWhereString = BLUE_BOX_FLOPPY_WHERE_STRING; 1510 Str255 sonyDriverName = SONY_DRVR_NAME; 1511 DCtlHandle driverDCtlHandle; 1512 DCtlPtr driverDCtlPtr; 1513 DRVRHeaderPtr drvrHeaderPtr; 1514 StringPtr driverName; 1515 1516 if ( dqPtr == NULL ) 1517 return false; 1518 1519 // Now look at the name of the Driver name. If it is .BlueBoxShared keep it out of the list of available disks. 1520 driverDCtlHandle = GetDCtlEntry(dqPtr->dQRefNum); 1521 driverDCtlPtr = *driverDCtlHandle; 1522 if((((driverDCtlPtr->dCtlFlags) & Is_Native_Mask) == 0) && (driverDCtlPtr->dCtlDriver != nil)) 1523 { 1524 if (((driverDCtlPtr->dCtlFlags) & Is_Ram_Based_Mask) == 0) 1525 { 1526 drvrHeaderPtr = (DRVRHeaderPtr)driverDCtlPtr->dCtlDriver; 1527 } 1528 else 1529 { 1530 //��� bek - lock w/o unlock/restore? should be getstate/setstate? 1531 HLock((Handle)(driverDCtlPtr)->dCtlDriver); 1532 drvrHeaderPtr = (DRVRHeaderPtr)*((Handle)(driverDCtlPtr->dCtlDriver)); 1533 1534 } 1535 driverName = (StringPtr)&(drvrHeaderPtr->drvrName); 1536 if (!(IdenticalString(driverName,blueBoxSharedDriverName,nil))) 1537 { 1538 return( true ); 1539 } 1540 1541 // Special case for the ".Sony" floppy driver which might be accessed in Shared mode inside the Blue Box 1542 // Test its "where" string instead of the driver name. 1543 if (!(IdenticalString(driverName,sonyDriverName,nil))) 1544 { 1545 CntrlParam paramBlock; 1546 1547 paramBlock.ioCompletion = nil; 1548 paramBlock.ioNamePtr = nil; 1549 paramBlock.ioVRefNum = dqPtr->dQDrive; 1550 paramBlock.ioCRefNum = dqPtr->dQRefNum; 1551 paramBlock.csCode = kDriveIcon; // return physical icon 1552 1553 // If PBControl(kDriveIcon) returns an error then the driver is not the Blue Box driver. 1554 if ( noErr == PBControlSync( (ParmBlkPtr) ¶mBlock ) ) 1555 { 1556 IconAndStringRecPtr iconAndStringRecPtr; 1557 StringPtr whereStringPtr; 1558 1559 iconAndStringRecPtr = * (IconAndStringRecPtr*) & paramBlock.csParam; 1560 whereStringPtr = (StringPtr) & iconAndStringRecPtr->string; 1561 if (!(IdenticalString(whereStringPtr,blueBoxFloppyWhereString,nil))) 1562 { 1563 return( true ); 1564 } 1565 } 1566 } 1567 } 1568#endif 1569 1570 return false; 1571} 1572 1573 1574 1575 1576/*------------------------------------------------------------------------------ 1577 1578Function: printVerifyStatus - (Print Verify Status) 1579 1580Function: Prints out the Verify Status words. 1581 1582Input: GPtr - pointer to scavenger global area 1583 1584Output: None. 1585------------------------------------------------------------------------------*/ 1586static 1587void printVerifyStatus(SGlobPtr GPtr) 1588{ 1589 UInt32 stat; 1590 1591 stat = GPtr->VIStat | GPtr->ABTStat | GPtr->EBTStat | GPtr->CBTStat | GPtr->CatStat; 1592 1593 if ( stat != 0 ) { 1594 plog(" Verify Status: VIStat = 0x%04x, ABTStat = 0x%04x EBTStat = 0x%04x\n", 1595 GPtr->VIStat, GPtr->ABTStat, GPtr->EBTStat); 1596 plog(" CBTStat = 0x%04x CatStat = 0x%08x\n", 1597 GPtr->CBTStat, GPtr->CatStat); 1598 } 1599} 1600