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 498 // If we have write access on volume and we are allowed to write, 499 // mark the volume clean/dirty 500 if ((fsWriteRef != -1) && (dataArea.canWrite != 0)) { 501 Boolean update; 502 if (scavError) { 503 // Mark volume dirty 504 CheckForClean(&dataArea, kMarkVolumeDirty, &update); 505 } else { 506 // Mark volume clean 507 CheckForClean(&dataArea, kMarkVolumeClean, &update); 508 } 509 if (update) { 510 /* Report back that volume was modified */ 511 *modified = 1; 512 } 513 } 514 ScavCtrl( &dataArea, scavTerminate, &temp ); // Note: use a temp var so that real scav error can be returned 515 516 if (fsckContext) { 517 fsckPrint( fsckContext, fsckProgress, 100); // End each run with 100% message, if desired 518 draw_progress(100); 519 end_progress(); 520 } 521 if (exitEarly && majorErrors) 522 err = MAJOREXIT; 523 524 if (msgCounts) { 525 free(msgCounts); 526 } 527 528 return( err ); 529} 530 531 532/*------------------------------------------------------------------------------ 533 534Function: ScavCtrl - (Scavenger Control) 535 536Function: Controls the scavenging process. Interfaces with the User Interface 537 Layer (written in PASCAL). 538 539Input: ScavOp - scavenging operation to be performed: 540 541 scavInitialize = start initial volume check 542 scavVerify = start verify 543 scavRepair = start repair 544 scavTerminate = finished scavenge 545 546 GPtr - pointer to scavenger global area 547 548 549Output: ScavRes - scavenge result code (R_xxx, or 0 if no error) 550 551------------------------------------------------------------------------------*/ 552 553void ScavCtrl( SGlobPtr GPtr, UInt32 ScavOp, short *ScavRes ) 554{ 555 OSErr result; 556 unsigned int stat; 557#if SHOW_ELAPSED_TIMES 558 struct timeval myStartTime; 559 struct timeval myEndTime; 560 struct timeval myElapsedTime; 561 struct timezone zone; 562#endif 563 564 // 565 // initialize some stuff 566 // 567 result = noErr; // assume good status 568 *ScavRes = 0; 569 GPtr->ScavRes = 0; 570 571 // 572 // dispatch next scavenge operation 573 // 574 switch ( ScavOp ) 575 { 576 case scavInitialize: // INITIAL VOLUME CHECK 577 { 578 Boolean modified; 579 int clean; 580 581 if ( ( result = ScavSetUp( GPtr ) ) ) // set up BEFORE CheckForStop 582 break; 583 if ( IsBlueBoxSharedDrive( GPtr->DrvPtr ) ) 584 break; 585 if ( ( result = CheckForStop( GPtr ) ) ) // in order to initialize wrCnt 586 break; 587 588 /* Call for all chkLevel options and check return value only 589 * for kDirtyCheck for preen option and kNeverCheck for quick option 590 */ 591 clean = CheckForClean(GPtr, kCheckVolume, &modified); 592 if ((GPtr->chkLevel == kDirtyCheck) || (GPtr->chkLevel == kNeverCheck)) { 593 if (clean == 1) { 594 /* volume was unmounted cleanly */ 595 GPtr->cleanUnmount = true; 596 break; 597 } 598 599 if (GPtr->chkLevel == kNeverCheck) { 600 if (clean == -1) 601 result = R_BadSig; 602 else if (clean == 0) { 603 /* 604 * We lie for journaled file systems since 605 * they get cleaned up in mount by replaying 606 * the journal. 607 * Note: CheckIfJournaled will return negative 608 * if it finds lastMountedVersion = FSK!. 609 */ 610 if (CheckIfJournaled(GPtr, false)) 611 GPtr->cleanUnmount = true; 612 else 613 result = R_Dirty; 614 } 615 break; 616 } 617 } 618 619 if (CheckIfJournaled(GPtr, false) 620 && GPtr->chkLevel != kForceCheck 621 && !(GPtr->chkLevel == kPartialCheck && GPtr->repairLevel == kForceRepairs) 622 && !(GPtr->chkLevel == kAlwaysCheck && GPtr->repairLevel == kMajorRepairs)) { 623 break; 624 } 625 626 if (GPtr->liveVerifyState) { 627 fsckPrint(GPtr->context, hfsLiveVerifyCheck); 628 } else if (GPtr->canWrite == 0 && nflag == 0) { 629 fsckPrint(GPtr->context, hfsVerifyVolWithWrite); 630 } 631 632 /* 633 * In the first pass, if fsck_hfs is verifying a 634 * journaled volume, and it's not a live verification, 635 * check to see if the journal is empty. If it is not, 636 * flag it as a journal error, and print a message. 637 * (A live verify will almost certainly have a non-empty 638 * journal, but that should be safe in this case due 639 * to the freeze command flushing everything.) 640 */ 641 if ((GPtr->scanCount == 0) && 642 (CheckIfJournaled(GPtr, true) == 1) && 643 (GPtr->canWrite == 0 || GPtr->writeRef == -1) && 644 (lflag == 0)) { 645 fsckJournalInfo_t jnlInfo = { 0 }; 646 UInt64 numBlocks; 647 UInt32 blockSize; 648 jnlInfo.jnlfd = -1; 649 650 if (IsJournalEmpty(GPtr, &jnlInfo) == 0) { 651 // disable_journal can currently only be set with debug enabled 652 if (disable_journal) { 653 fsckPrint(GPtr->context, E_DirtyJournal); 654 GPtr->JStat |= S_DirtyJournal; 655 } else { 656 (void)GetDeviceSize(GPtr->calculatedVCB->vcbDriveNumber, &numBlocks, &blockSize); 657#if 0 658 // For debugging the cache. WAY to verbose to run with even normal debug 659 if (debug) { 660 printf("Before journal replay\n"); 661 dumpCache(&fscache); 662 } 663#endif 664 if (journal_open(jnlInfo.jnlfd, 665 jnlInfo.jnlOffset, 666 jnlInfo.jnlSize, 667 blockSize, 668 0, 669 jnlInfo.name, 670 ^(off_t start, void *data, size_t len) { 671 Buf_t *buf; 672 int rv; 673 rv = CacheRead(&fscache, start, (int)len, &buf); 674 if (rv != 0) 675 abort(); 676 memcpy(buf->Buffer, data, len); 677 rv = CacheWrite(&fscache, buf, 0, kLockWrite); 678 if (rv != 0) 679 abort(); 680 return 0;} 681 ) == -1) { 682 fsckPrint(GPtr->context, E_DirtyJournal); 683 GPtr->JStat |= S_DirtyJournal; 684 } else if (debug) { 685 plog("Journal replay simulation succeeded\n"); 686#if 0 687 // Still way too verbose to run 688 dumpCache(&fscache); 689#endif 690 } 691 } 692 } else { 693 if (debug) 694 plog("Journal is empty\n"); 695 } 696 if (jnlInfo.jnlfd != -1) 697 close(jnlInfo.jnlfd); 698 if (jnlInfo.name != NULL) 699 free(jnlInfo.name); 700 } 701 702 result = IVChk( GPtr ); 703 704 break; 705 } 706 707 case scavVerify: // VERIFY 708 { 709 710#if SHOW_ELAPSED_TIMES 711 gettimeofday( &myStartTime, &zone ); 712#endif 713 714 /* Initialize volume bitmap structure */ 715 if ( BitMapCheckBegin(GPtr) != 0) 716 break; 717 718#if SHOW_ELAPSED_TIMES 719 gettimeofday( &myEndTime, &zone ); 720 timersub( &myEndTime, &myStartTime, &myElapsedTime ); 721 plog( "\n%s - BitMapCheck elapsed time \n", __FUNCTION__ ); 722 plog( "########## secs %d msecs %d \n\n", 723 myElapsedTime.tv_sec, myElapsedTime.tv_usec ); 724#endif 725 726 if ( IsBlueBoxSharedDrive( GPtr->DrvPtr ) ) 727 break; 728 if ( ( result = CheckForStop( GPtr ) ) ) 729 break; 730 731#if SHOW_ELAPSED_TIMES 732 gettimeofday( &myStartTime, &zone ); 733#endif 734 735 /* Create calculated BTree structures */ 736 if ( ( result = CreateExtentsBTreeControlBlock( GPtr ) ) ) 737 break; 738 if ( ( result = CreateCatalogBTreeControlBlock( GPtr ) ) ) 739 break; 740 if ( ( result = CreateAttributesBTreeControlBlock( GPtr ) ) ) 741 break; 742 if ( ( result = CreateExtendedAllocationsFCB( GPtr ) ) ) 743 break; 744 745#if SHOW_ELAPSED_TIMES 746 gettimeofday( &myEndTime, &zone ); 747 timersub( &myEndTime, &myStartTime, &myElapsedTime ); 748 plog( "\n%s - create control blocks elapsed time \n", __FUNCTION__ ); 749 plog( ">>>>>>>>>>>>> secs %d msecs %d \n\n", 750 myElapsedTime.tv_sec, myElapsedTime.tv_usec ); 751#endif 752 753 // Now that preflight of the BTree structures is calculated, compute the CheckDisk items 754 CalculateItemCount( GPtr, &GPtr->itemsToProcess, &GPtr->onePercent ); 755 GPtr->itemsProcessed += GPtr->onePercent; // We do this 4 times as set up in CalculateItemCount() to smooth the scroll 756 757 if ( ( result = VLockedChk( GPtr ) ) ) 758 break; 759 760 GPtr->itemsProcessed += GPtr->onePercent; // We do this 4 times as set up in CalculateItemCount() to smooth the scroll 761 fsckPrint(GPtr->context, hfsExtBTCheck); 762 763#if SHOW_ELAPSED_TIMES 764 gettimeofday( &myStartTime, &zone ); 765#endif 766 767 /* Verify extent btree structure */ 768 if ((result = ExtBTChk(GPtr))) 769 break; 770 771#if SHOW_ELAPSED_TIMES 772 gettimeofday( &myEndTime, &zone ); 773 timersub( &myEndTime, &myStartTime, &myElapsedTime ); 774 plog( "\n%s - ExtBTChk elapsed time \n", __FUNCTION__ ); 775 plog( ">>>>>>>>>>>>> secs %d msecs %d \n\n", 776 myElapsedTime.tv_sec, myElapsedTime.tv_usec ); 777#endif 778 779 if ((result = CheckForStop(GPtr))) 780 break; 781 782 GPtr->itemsProcessed += GPtr->onePercent; // We do this 4 times as set up in CalculateItemCount() to smooth the scroll 783 784 /* Check extents of bad block file */ 785 if ((result = BadBlockFileExtentCheck(GPtr))) 786 break; 787 if ((result = CheckForStop(GPtr))) 788 break; 789 790 GPtr->itemsProcessed += GPtr->onePercent; // We do this 4 times as set up in CalculateItemCount() to smooth the scroll 791 GPtr->itemsProcessed += GPtr->onePercent; 792 fsckPrint(GPtr->context, hfsCatBTCheck); 793 794#if SHOW_ELAPSED_TIMES 795 gettimeofday( &myStartTime, &zone ); 796#endif 797 798 if ( GPtr->chkLevel == kPartialCheck ) 799 { 800 /* skip the rest of the verify code path the first time */ 801 /* through when we are rebuilding the catalog B-Tree file. */ 802 /* we will be back here after the rebuild. */ 803 if (GPtr->rebuildOptions & REBUILD_CATALOG) { 804 GPtr->CBTStat |= S_RebuildBTree; 805 } 806 if (GPtr->rebuildOptions & REBUILD_EXTENTS) { 807 GPtr->EBTStat |= S_RebuildBTree; 808 } 809 if (GPtr->rebuildOptions & REBUILD_ATTRIBUTE) { 810 GPtr->ABTStat |= S_RebuildBTree; 811 } 812 result = errRebuildBtree; 813 break; 814 } 815 816 /* Check catalog btree. For given fileID, the function accounts 817 * for all extents existing in catalog record as well as in 818 * overflow extent btree 819 */ 820 if ((result = CheckCatalogBTree(GPtr))) 821 break; 822 823#if SHOW_ELAPSED_TIMES 824 gettimeofday( &myEndTime, &zone ); 825 timersub( &myEndTime, &myStartTime, &myElapsedTime ); 826 plog( "\n%s - CheckCatalogBTree elapsed time \n", __FUNCTION__ ); 827 plog( ">>>>>>>>>>>>> secs %d msecs %d \n\n", 828 myElapsedTime.tv_sec, myElapsedTime.tv_usec ); 829#endif 830 831 if ((result = CheckForStop(GPtr))) 832 break; 833 834 if (scanflag == 0) { 835 fsckPrint(GPtr->context, hfsCatHierCheck); 836 837#if SHOW_ELAPSED_TIMES 838 gettimeofday( &myStartTime, &zone ); 839#endif 840 841 /* Check catalog hierarchy */ 842 if ((result = CatHChk(GPtr))) 843 break; 844 845#if SHOW_ELAPSED_TIMES 846 gettimeofday( &myEndTime, &zone ); 847 timersub( &myEndTime, &myStartTime, &myElapsedTime ); 848 plog( "\n%s - CatHChk elapsed time \n", __FUNCTION__ ); 849 plog( ">>>>>>>>>>>>> secs %d msecs %d \n\n", 850 myElapsedTime.tv_sec, myElapsedTime.tv_usec ); 851#endif 852 853 if ((result = CheckForStop(GPtr))) 854 break; 855 856 if (VolumeObjectIsHFSX(GPtr)) { 857 result = CheckFolderCount(GPtr); 858 if (result) 859 break; 860 861 if ((result=CheckForStop(GPtr))) 862 break; 863 } 864 } 865 /* Check attribute btree. The function accounts for all extents 866 * for extended attributes whose values are stored in 867 * allocation blocks 868 */ 869 if ((result = AttrBTChk(GPtr))) 870 break; 871 872 if ((result = CheckForStop(GPtr))) 873 break; 874 875 /* 876 * fsck_hfs has accounted for all valid allocation blocks by 877 * traversing all catalog records and attribute records. 878 * These traversals may have found overlapping extents. Note 879 * that the overlapping extents are detected in CaptureBitmapBits 880 * when it tries to set a bit corresponding to allocation block 881 * and finds that it is already set. Therefore fsck_hfs does not 882 * know the orignal file involved overlapped extents. 883 */ 884 if (GPtr->VIStat & S_OverlappingExtents) { 885 /* Find original files involved in overlapped extents */ 886 result = FindOrigOverlapFiles(GPtr); 887 if (result) { 888 break; 889 } 890 891 /* Print all unique overlapping file IDs and paths */ 892 (void) PrintOverlapFiles(GPtr); 893 } 894 895 if (scanflag == 0) { 896 /* Directory inodes store first link information in 897 * an extended attribute. Therefore start directory 898 * hard link check after extended attribute checks. 899 */ 900 result = dirhardlink_check(GPtr); 901 /* On error or unrepairable corruption, stop the verification */ 902 if ((result != 0) || (GPtr->CatStat & S_LinkErrNoRepair)) { 903 if (result == 0) { 904 result = -1; 905 } 906 907 break; 908 } 909 } 910 911 fsckPrint(GPtr->context, hfsVolBitmapCheck); 912 913#if SHOW_ELAPSED_TIMES 914 gettimeofday( &myStartTime, &zone ); 915#endif 916 917 /* Compare in-memory volume bitmap with on-disk bitmap */ 918 if ((result = CheckVolumeBitMap(GPtr, false))) 919 break; 920 921#if SHOW_ELAPSED_TIMES 922 gettimeofday( &myEndTime, &zone ); 923 timersub( &myEndTime, &myStartTime, &myElapsedTime ); 924 plog( "\n%s - CheckVolumeBitMap elapsed time \n", __FUNCTION__ ); 925 plog( ">>>>>>>>>>>>> secs %d msecs %d \n\n", 926 myElapsedTime.tv_sec, myElapsedTime.tv_usec ); 927#endif 928 929 if ((result = CheckForStop(GPtr))) 930 break; 931 932 fsckPrint(GPtr->context, hfsVolInfoCheck); 933 934#if SHOW_ELAPSED_TIMES 935 gettimeofday( &myStartTime, &zone ); 936#endif 937 938 /* Verify volume level information */ 939 if ((result = VInfoChk(GPtr))) 940 break; 941 942#if SHOW_ELAPSED_TIMES 943 gettimeofday( &myEndTime, &zone ); 944 timersub( &myEndTime, &myStartTime, &myElapsedTime ); 945 plog( "\n%s - VInfoChk elapsed time \n", __FUNCTION__ ); 946 plog( ">>>>>>>>>>>>> secs %d msecs %d \n\n", 947 myElapsedTime.tv_sec, myElapsedTime.tv_usec ); 948#endif 949 950 stat = GPtr->VIStat | GPtr->ABTStat | GPtr->EBTStat | GPtr->CBTStat | 951 GPtr->CatStat | GPtr->JStat; 952 953 if ( stat != 0 ) 954 { 955 if ( (GPtr->RepLevel == repairLevelNoProblemsFound) || (GPtr->RepLevel == repairLevelVolumeRecoverable) ) 956 { 957 // 2200106, We isolate very minor errors so that if the volume cannot be unmounted 958 // CheckDisk will just return noErr 959 unsigned int minorErrors = (GPtr->CatStat & ~S_LockedDirName) | 960 GPtr->VIStat | GPtr->ABTStat | GPtr->EBTStat | GPtr->CBTStat | GPtr->JStat; 961 if ( minorErrors == 0 ) 962 GPtr->RepLevel = repairLevelVeryMinorErrors; 963 else 964 GPtr->RepLevel = repairLevelVolumeRecoverable; 965 } 966 } 967 else if ( GPtr->RepLevel == repairLevelNoProblemsFound ) 968 { 969 } 970 971 GPtr->itemsProcessed = GPtr->itemsToProcess; 972 result = CheckForStop(GPtr); // one last check for modified volume 973 break; 974 } 975 976 case scavRepair: // REPAIR 977 { 978 if ( IsBlueBoxSharedDrive( GPtr->DrvPtr ) ) 979 break; 980 if ( ( result = CheckForStop(GPtr) ) ) 981 break; 982 if ( GPtr->CBTStat & S_RebuildBTree 983 || GPtr->EBTStat & S_RebuildBTree 984 || GPtr->ABTStat & S_RebuildBTree) { 985// fsckPrint(GPtr->context, hfsRebuildCatalogBTree); 986// fsckPrint(GPtr->context, hfsRebuildAttrBTree); 987// actually print nothing yet -- we print out when we are rebuilding the trees 988 } else { 989 fsckPrint(GPtr->context, fsckRepairingVolume); 990 if (embedded == 1 && debug == 0) 991 fsckPrint(GPtr->context, fsckLimitedRepairs); 992 } 993 result = RepairVolume( GPtr ); 994 break; 995 } 996 997 case scavTerminate: // CLEANUP AFTER SCAVENGE 998 { 999 result = ScavTerm(GPtr); 1000 break; 1001 } 1002 } // end ScavOp switch 1003 1004 1005 // 1006 // Map internal error codes to scavenger result codes 1007 // 1008 if ( (result < 0) || (result > Max_RCode) ) 1009 { 1010 switch ( ScavOp ) 1011 { 1012 case scavInitialize: 1013 case scavVerify: 1014 if ( result == ioErr ) 1015 result = R_RdErr; 1016 else if ( result == errRebuildBtree ) 1017 { 1018 GPtr->RepLevel = repairLevelCatalogBtreeRebuild; 1019 break; 1020 } 1021 else 1022 result = R_VFail; 1023 GPtr->RepLevel = repairLevelUnrepairable; 1024 break; 1025 case scavRepair: 1026 result = R_RFail; 1027 break; 1028 default: 1029 result = R_IntErr; 1030 } 1031 } 1032 1033 GPtr->ScavRes = result; 1034 1035 *ScavRes = result; 1036 1037} // end of ScavCtrl 1038 1039 1040 1041/*------------------------------------------------------------------------------ 1042 1043Function: CheckForStop 1044 1045Function: Checks for the user hitting the "STOP" button during a scavenge, 1046 which interrupts the operation. Additionally, we monitor the write 1047 count of a mounted volume, to be sure that the volume is not 1048 modified by another app while we scavenge. 1049 1050Input: GPtr - pointer to scavenger global area 1051 1052Output: Function result: 1053 0 - ok to continue 1054 R_UInt - STOP button hit 1055 R_Modified - another app has touched the volume 1056-------------------------------------------------------------------------------*/ 1057 1058short CheckForStop( SGlob *GPtr ) 1059{ 1060 OSErr err = noErr; // Initialize err to noErr 1061 long ticks = TickCount(); 1062 UInt16 dfaStage = (UInt16) GetDFAStage(); 1063 1064 //plog("%d, %d", dfaStage, kAboutToRepairStage); 1065 1066 //if ( ((ticks - 10) > GPtr->lastTickCount) || (dfaStage == kAboutToRepairStage) ) // To reduce cursor flicker on fast machines, call through on a timed interval 1067 //{ 1068 if ( GPtr->userCancelProc != nil ) 1069 { 1070 UInt64 progress = 0; 1071 Boolean progressChanged; 1072 // UInt16 elapsedTicks; 1073 1074 if ( dfaStage != kRepairStage ) 1075 { 1076 progress = GPtr->itemsProcessed * 100; 1077 progress /= GPtr->itemsToProcess; 1078 progressChanged = ( progress != GPtr->lastProgress ); 1079 GPtr->lastProgress = progress; 1080 1081 #if( DisplayTimeRemaining ) 1082 if ( (progressChanged) && (progress > 5) ) 1083 { 1084 elapsedTicks = TickCount() - GPtr->startTicks; 1085 GPtr->secondsRemaining = ( ( ( 100 * elapsedTicks ) / progress ) - elapsedTicks ) / 60; 1086 } 1087 #endif 1088 err = CallUserCancelProc( GPtr->userCancelProc, (UInt16)progress, (UInt16)GPtr->secondsRemaining, progressChanged, dfaStage, GPtr->context, GPtr->scanCount ); 1089 } 1090 else 1091 { 1092 (void) CallUserCancelProc( GPtr->userCancelProc, (UInt16)progress, 0, false, dfaStage, GPtr->context, GPtr->scanCount ); 1093 } 1094 1095 } 1096 1097 if ( err != noErr ) 1098 err = R_UInt; 1099 #if 0 1100 if ( GPtr->realVCB ) // If the volume is mounted 1101 if ( GPtr->realVCB->vcbWrCnt != GPtr->wrCnt ) 1102 err = R_Modified; // Its been modified behind our back 1103 #endif 1104 GPtr->lastTickCount = ticks; 1105 //} 1106 1107 return ( err ); 1108} 1109 1110 1111 1112/*------------------------------------------------------------------------------ 1113 1114Function: ScavSetUp - (Scavenger Set Up) 1115 1116Function: Sets up scavenger globals for a new scavenge operation. Memory is 1117 allocated for the Scavenger's static data structures (VCB, FCBs, 1118 BTCBs, and TPTs). The contents of the data structures are 1119 initialized to zero. 1120 1121Input: GPtr - pointer to scavenger global area 1122 1123Output: ScavSetUp - function result: 1124 0 = no error 1125 n = error code 1126------------------------------------------------------------------------------*/ 1127 1128struct ScavStaticStructures { 1129 SVCB vcb; 1130 SFCB fcbList[6]; 1131 BTreeControlBlock btcb[4]; // 4 btcb's 1132 SBTPT btreePath; // scavenger BTree path table 1133}; 1134typedef struct ScavStaticStructures ScavStaticStructures; 1135 1136 1137static int ScavSetUp( SGlob *GPtr) 1138{ 1139 OSErr err; 1140 SVCB * vcb; 1141#if !BSD 1142 DrvQEl *drvP; 1143 short ioRefNum; 1144#endif 1145 1146 GPtr->MinorRepairsP = nil; 1147 1148 GPtr->itemsProcessed = 0; 1149 GPtr->lastProgress = 0; 1150 GPtr->startTicks = TickCount(); 1151 1152 // 1153 // allocate the static data structures (VCB, FCB's, BTCB'S, DPT and BTPT) 1154 // 1155 { 1156 ScavStaticStructures *pointer; 1157 1158 pointer = (ScavStaticStructures *) AllocateClearMemory( sizeof(ScavStaticStructures) ); 1159 if ( pointer == nil ) { 1160 if ( fsckGetVerbosity(GPtr->context) >= kDebugLog ) { 1161 plog( "\t error %d - could not allocate %ld bytes of memory \n", 1162 R_NoMem, sizeof(ScavStaticStructures) ); 1163 } 1164 return( R_NoMem ); 1165 } 1166 GPtr->scavStaticPtr = pointer; 1167 1168 GPtr->DirPTPtr = AllocateClearMemory(sizeof(SDPR) * CMMaxDepth); 1169 if ( GPtr->DirPTPtr == nil ) { 1170 if ( fsckGetVerbosity(GPtr->context) >= kDebugLog ) { 1171 plog( "\t error %d - could not allocate %ld bytes of memory \n", 1172 R_NoMem, sizeof(SDPR) * CMMaxDepth ); 1173 } 1174 return( R_NoMem ); 1175 } 1176 GPtr->dirPathCount = CMMaxDepth; 1177 1178 GPtr->calculatedVCB = vcb = &pointer->vcb; 1179 vcb->vcbGPtr = GPtr; 1180 1181 GPtr->FCBAPtr = (Ptr) &pointer->fcbList; 1182 GPtr->calculatedExtentsFCB = &pointer->fcbList[0]; 1183 GPtr->calculatedCatalogFCB = &pointer->fcbList[1]; 1184 GPtr->calculatedAllocationsFCB = &pointer->fcbList[2]; 1185 GPtr->calculatedAttributesFCB = &pointer->fcbList[3]; 1186 GPtr->calculatedStartupFCB = &pointer->fcbList[4]; 1187 GPtr->calculatedRepairFCB = &pointer->fcbList[5]; 1188 1189 GPtr->calculatedExtentsBTCB = &pointer->btcb[0]; 1190 GPtr->calculatedCatalogBTCB = &pointer->btcb[1]; 1191 GPtr->calculatedRepairBTCB = &pointer->btcb[2]; 1192 GPtr->calculatedAttributesBTCB = &pointer->btcb[3]; 1193 1194 GPtr->BTPTPtr = (SBTPT*) &pointer->btreePath; 1195 } 1196 1197 1198 SetDFAStage( kVerifyStage ); 1199 SetFCBSPtr( GPtr->FCBAPtr ); 1200 1201 // 1202 // locate the driveQ element for drive being scavenged 1203 // 1204 GPtr->DrvPtr = 0; // <8> initialize so we can know if drive disappears 1205 1206 // 1207 // Set up Real structures 1208 // 1209#if !BSD 1210 err = FindDrive( &ioRefNum, &(GPtr->DrvPtr), GPtr->DrvNum ); 1211#endif 1212 if ( IsBlueBoxSharedDrive( GPtr->DrvPtr ) ) 1213 return noErr; 1214 1215 err = GetVolumeFeatures( GPtr ); // Sets up GPtr->volumeFeatures and GPtr->realVCB 1216 1217#if !BSD 1218 if ( GPtr->DrvPtr == NULL ) // <8> drive is no longer there! 1219 return ( R_NoVol ); 1220 else 1221 drvP = GPtr->DrvPtr; 1222 1223 // Save current value of vcbWrCnt, to detect modifications to volume by other apps etc 1224 if ( GPtr->volumeFeatures & volumeIsMountedMask ) 1225 { 1226 FlushVol( nil, GPtr->realVCB->vcbVRefNum ); // Ask HFS to update all changes to disk 1227 GPtr->wrCnt = GPtr->realVCB->vcbWrCnt; // Remember write count after writing changes 1228 } 1229#endif 1230 1231 // Finish initializing the VCB 1232 1233 // The calculated structures 1234#if BSD 1235 InitBlockCache(vcb); 1236 vcb->vcbDriveNumber = GPtr->DrvNum; 1237 vcb->vcbDriverReadRef = GPtr->DrvNum; 1238 vcb->vcbDriverWriteRef = -1; /* XXX need to get real fd here */ 1239#else 1240 vcb->vcbDriveNumber = drvP->dQDrive; 1241 vcb->vcbDriverReadRef = drvP->dQRefNum; 1242 vcb->vcbDriverWriteRef = drvP->dQRefNum; 1243 vcb->vcbFSID = drvP->dQFSID; 1244#endif 1245// vcb->vcbVRefNum = Vol_RefN; 1246 1247 // 1248 // finish initializing the FCB's 1249 // 1250 { 1251 SFCB *fcb; 1252 1253 // Create Calculated Extents FCB 1254 fcb = GPtr->calculatedExtentsFCB; 1255 fcb->fcbFileID = kHFSExtentsFileID; 1256 fcb->fcbVolume = vcb; 1257 fcb->fcbBtree = GPtr->calculatedExtentsBTCB; 1258 vcb->vcbExtentsFile = fcb; 1259 1260 // Create Calculated Catalog FCB 1261 fcb = GPtr->calculatedCatalogFCB; 1262 fcb->fcbFileID = kHFSCatalogFileID; 1263 fcb->fcbVolume = vcb; 1264 fcb->fcbBtree = GPtr->calculatedCatalogBTCB; 1265 vcb->vcbCatalogFile = fcb; 1266 1267 // Create Calculated Allocations FCB 1268 fcb = GPtr->calculatedAllocationsFCB; 1269 fcb->fcbFileID = kHFSAllocationFileID; 1270 fcb->fcbVolume = vcb; 1271 fcb->fcbBtree = NULL; // no BitMap B-Tree 1272 vcb->vcbAllocationFile = fcb; 1273 1274 // Create Calculated Attributes FCB 1275 fcb = GPtr->calculatedAttributesFCB; 1276 fcb->fcbFileID = kHFSAttributesFileID; 1277 fcb->fcbVolume = vcb; 1278 fcb->fcbBtree = GPtr->calculatedAttributesBTCB; 1279 vcb->vcbAttributesFile = fcb; 1280 1281 /* Create Calculated Startup FCB */ 1282 fcb = GPtr->calculatedStartupFCB; 1283 fcb->fcbFileID = kHFSStartupFileID; 1284 fcb->fcbVolume = vcb; 1285 fcb->fcbBtree = NULL; 1286 vcb->vcbStartupFile = fcb; 1287 } 1288 1289 // finish initializing the BTCB's 1290 { 1291 BTreeControlBlock *btcb; 1292 1293 btcb = GPtr->calculatedExtentsBTCB; // calculatedExtentsBTCB 1294 btcb->fcbPtr = GPtr->calculatedExtentsFCB; 1295 btcb->getBlockProc = GetFileBlock; 1296 btcb->releaseBlockProc = ReleaseFileBlock; 1297 btcb->setEndOfForkProc = SetEndOfForkProc; 1298 1299 btcb = GPtr->calculatedCatalogBTCB; // calculatedCatalogBTCB 1300 btcb->fcbPtr = GPtr->calculatedCatalogFCB; 1301 btcb->getBlockProc = GetFileBlock; 1302 btcb->releaseBlockProc = ReleaseFileBlock; 1303 btcb->setEndOfForkProc = SetEndOfForkProc; 1304 1305 btcb = GPtr->calculatedAttributesBTCB; // calculatedAttributesBTCB 1306 btcb->fcbPtr = GPtr->calculatedAttributesFCB; 1307 btcb->getBlockProc = GetFileBlock; 1308 btcb->releaseBlockProc = ReleaseFileBlock; 1309 btcb->setEndOfForkProc = SetEndOfForkProc; 1310 } 1311 1312 1313 // 1314 // Initialize some global stuff 1315 // 1316 1317 GPtr->RepLevel = repairLevelNoProblemsFound; 1318 GPtr->ErrCode = 0; 1319 GPtr->IntErr = noErr; 1320 GPtr->VIStat = 0; 1321 GPtr->ABTStat = 0; 1322 GPtr->EBTStat = 0; 1323 GPtr->CBTStat = 0; 1324 GPtr->CatStat = 0; 1325 GPtr->VeryMinorErrorsStat = 0; 1326 GPtr->JStat = 0; 1327 1328 /* Assume that the volume is dirty unmounted */ 1329 GPtr->cleanUnmount = false; 1330 1331 // 1332 // Initialize VolumeObject 1333 // 1334 1335 InitializeVolumeObject( GPtr ); 1336 1337 /* Check if the volume type of initialized object is valid. If not, return error */ 1338 if (VolumeObjectIsValid() == false) { 1339 return (R_BadSig); 1340 } 1341 1342 // Keep a valid file id list for HFS volumes 1343 GPtr->validFilesList = (UInt32**)NewHandle( 0 ); 1344 if ( GPtr->validFilesList == nil ) { 1345 if ( fsckGetVerbosity(GPtr->context) >= kDebugLog ) { 1346 plog( "\t error %d - could not allocate file ID list \n", R_NoMem ); 1347 } 1348 return( R_NoMem ); 1349 } 1350 1351 // Convert the security attribute name from utf8 to utf16. This will 1352 // avoid repeated conversion of all extended attributes to compare with 1353 // security attribute name 1354 (void) utf_decodestr((unsigned char *)KAUTH_FILESEC_XATTR, strlen(KAUTH_FILESEC_XATTR), GPtr->securityAttrName, &GPtr->securityAttrLen, sizeof(GPtr->securityAttrName)); 1355 1356 return( noErr ); 1357 1358} /* end of ScavSetUp */ 1359 1360 1361 1362 1363/*------------------------------------------------------------------------------ 1364 1365Function: ScavTerm - (Scavenge Termination)) 1366 1367Function: Terminates the current scavenging operation. Memory for the 1368 VCB, FCBs, BTCBs, volume bit map, and BTree bit maps is 1369 released. 1370 1371Input: GPtr - pointer to scavenger global area 1372 1373Output: ScavTerm - function result: 1374 0 = no error 1375 n = error code 1376------------------------------------------------------------------------------*/ 1377 1378static int ScavTerm( SGlobPtr GPtr ) 1379{ 1380 SFCB *fcbP; 1381 BTreeControlBlock *btcbP; 1382 RepairOrderPtr rP; 1383 OSErr err; 1384 ExtentsTable **extentsTableH; 1385 ExtentInfo *curExtentInfo; 1386 int i; 1387 1388 (void) BitMapCheckEnd(); 1389 1390 while( (rP = GPtr->MinorRepairsP) != nil ) // loop freeing leftover (undone) repair orders 1391 { 1392 GPtr->MinorRepairsP = rP->link; // (in case repairs were not made) 1393 DisposeMemory(rP); 1394 err = MemError(); 1395 } 1396 1397 if( GPtr->validFilesList != nil ) 1398 DisposeHandle( (Handle) GPtr->validFilesList ); 1399 1400 if( GPtr->overlappedExtents != nil ) { 1401 extentsTableH = GPtr->overlappedExtents; 1402 1403 /* Overlapped extents list also allocated memory for attribute name */ 1404 for (i=0; i<(**extentsTableH).count; i++) { 1405 curExtentInfo = &((**extentsTableH).extentInfo[i]); 1406 1407 /* Deallocate memory for attribute name, if any */ 1408 if (curExtentInfo->attrname) { 1409 free(curExtentInfo->attrname); 1410 } 1411 } 1412 1413 DisposeHandle( (Handle) GPtr->overlappedExtents ); 1414 } 1415 1416 if( GPtr->fileIdentifierTable != nil ) 1417 DisposeHandle( (Handle) GPtr->fileIdentifierTable ); 1418 1419 if( GPtr->calculatedVCB == nil ) // already freed? 1420 return( noErr ); 1421 1422 // If the FCB's and BTCB's have been set up, dispose of them 1423 fcbP = GPtr->calculatedExtentsFCB; // release extent file BTree bit map 1424 if ( fcbP != nil ) 1425 { 1426 btcbP = (BTreeControlBlock*)fcbP->fcbBtree; 1427 if ( btcbP != nil) 1428 { 1429 if( btcbP->refCon != nil ) 1430 { 1431 if(((BTreeExtensionsRec*)btcbP->refCon)->BTCBMPtr != nil) 1432 { 1433 DisposeMemory(((BTreeExtensionsRec*)btcbP->refCon)->BTCBMPtr); 1434 err = MemError(); 1435 } 1436 DisposeMemory( (Ptr)btcbP->refCon ); 1437 err = MemError(); 1438 btcbP->refCon = nil; 1439 } 1440 1441 fcbP = GPtr->calculatedCatalogFCB; // release catalog BTree bit map 1442 btcbP = (BTreeControlBlock*)fcbP->fcbBtree; 1443 1444 if( btcbP->refCon != nil ) 1445 { 1446 if(((BTreeExtensionsRec*)btcbP->refCon)->BTCBMPtr != nil) 1447 { 1448 DisposeMemory(((BTreeExtensionsRec*)btcbP->refCon)->BTCBMPtr); 1449 err = MemError(); 1450 } 1451 DisposeMemory( (Ptr)btcbP->refCon ); 1452 err = MemError(); 1453 btcbP->refCon = nil; 1454 } 1455 } 1456 } 1457 1458 DisposeMemory(GPtr->DirPTPtr); 1459 DisposeMemory((ScavStaticStructures *)GPtr->scavStaticPtr); 1460 GPtr->scavStaticPtr = nil; 1461 GPtr->calculatedVCB = nil; 1462 1463 return( noErr ); 1464} 1465 1466#define BLUE_BOX_SHARED_DRVR_NAME "\p.BlueBoxShared" 1467#define BLUE_BOX_FLOPPY_WHERE_STRING "\pdisk%d (Shared)" 1468#define SONY_DRVR_NAME "\p.Sony" 1469 1470/*------------------------------------------------------------------------------ 1471 1472Routine: IsBlueBoxSharedDrive 1473 1474Function: Given a DQE address, return a boolean that determines whether 1475 or not a drive is a Blue Box disk being accessed via Shared mode. 1476 Such drives do not support i/o and cannot be scavenged. 1477 1478Input: Arg 1 - DQE pointer 1479 1480Output: D0.L - 0 if drive not to be used 1481 1 otherwise 1482------------------------------------------------------------------------------*/ 1483 1484struct IconAndStringRec { 1485 char icon[ 256 ]; 1486 Str255 string; 1487}; 1488typedef struct IconAndStringRec IconAndStringRec, * IconAndStringRecPtr; 1489 1490 1491Boolean IsBlueBoxSharedDrive ( DrvQElPtr dqPtr ) 1492{ 1493#if 0 1494 Str255 blueBoxSharedDriverName = BLUE_BOX_SHARED_DRVR_NAME; 1495 Str255 blueBoxFloppyWhereString = BLUE_BOX_FLOPPY_WHERE_STRING; 1496 Str255 sonyDriverName = SONY_DRVR_NAME; 1497 DCtlHandle driverDCtlHandle; 1498 DCtlPtr driverDCtlPtr; 1499 DRVRHeaderPtr drvrHeaderPtr; 1500 StringPtr driverName; 1501 1502 if ( dqPtr == NULL ) 1503 return false; 1504 1505 // Now look at the name of the Driver name. If it is .BlueBoxShared keep it out of the list of available disks. 1506 driverDCtlHandle = GetDCtlEntry(dqPtr->dQRefNum); 1507 driverDCtlPtr = *driverDCtlHandle; 1508 if((((driverDCtlPtr->dCtlFlags) & Is_Native_Mask) == 0) && (driverDCtlPtr->dCtlDriver != nil)) 1509 { 1510 if (((driverDCtlPtr->dCtlFlags) & Is_Ram_Based_Mask) == 0) 1511 { 1512 drvrHeaderPtr = (DRVRHeaderPtr)driverDCtlPtr->dCtlDriver; 1513 } 1514 else 1515 { 1516 //��� bek - lock w/o unlock/restore? should be getstate/setstate? 1517 HLock((Handle)(driverDCtlPtr)->dCtlDriver); 1518 drvrHeaderPtr = (DRVRHeaderPtr)*((Handle)(driverDCtlPtr->dCtlDriver)); 1519 1520 } 1521 driverName = (StringPtr)&(drvrHeaderPtr->drvrName); 1522 if (!(IdenticalString(driverName,blueBoxSharedDriverName,nil))) 1523 { 1524 return( true ); 1525 } 1526 1527 // Special case for the ".Sony" floppy driver which might be accessed in Shared mode inside the Blue Box 1528 // Test its "where" string instead of the driver name. 1529 if (!(IdenticalString(driverName,sonyDriverName,nil))) 1530 { 1531 CntrlParam paramBlock; 1532 1533 paramBlock.ioCompletion = nil; 1534 paramBlock.ioNamePtr = nil; 1535 paramBlock.ioVRefNum = dqPtr->dQDrive; 1536 paramBlock.ioCRefNum = dqPtr->dQRefNum; 1537 paramBlock.csCode = kDriveIcon; // return physical icon 1538 1539 // If PBControl(kDriveIcon) returns an error then the driver is not the Blue Box driver. 1540 if ( noErr == PBControlSync( (ParmBlkPtr) ¶mBlock ) ) 1541 { 1542 IconAndStringRecPtr iconAndStringRecPtr; 1543 StringPtr whereStringPtr; 1544 1545 iconAndStringRecPtr = * (IconAndStringRecPtr*) & paramBlock.csParam; 1546 whereStringPtr = (StringPtr) & iconAndStringRecPtr->string; 1547 if (!(IdenticalString(whereStringPtr,blueBoxFloppyWhereString,nil))) 1548 { 1549 return( true ); 1550 } 1551 } 1552 } 1553 } 1554#endif 1555 1556 return false; 1557} 1558 1559 1560 1561 1562/*------------------------------------------------------------------------------ 1563 1564Function: printVerifyStatus - (Print Verify Status) 1565 1566Function: Prints out the Verify Status words. 1567 1568Input: GPtr - pointer to scavenger global area 1569 1570Output: None. 1571------------------------------------------------------------------------------*/ 1572static 1573void printVerifyStatus(SGlobPtr GPtr) 1574{ 1575 UInt32 stat; 1576 1577 stat = GPtr->VIStat | GPtr->ABTStat | GPtr->EBTStat | GPtr->CBTStat | GPtr->CatStat; 1578 1579 if ( stat != 0 ) { 1580 plog(" Verify Status: VIStat = 0x%04x, ABTStat = 0x%04x EBTStat = 0x%04x\n", 1581 GPtr->VIStat, GPtr->ABTStat, GPtr->EBTStat); 1582 plog(" CBTStat = 0x%04x CatStat = 0x%08x\n", 1583 GPtr->CBTStat, GPtr->CatStat); 1584 } 1585} 1586