1/* 2 * Copyright (c) 1999-2000, 2002-2008 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#include <sys/types.h> 24#include <sys/stat.h> 25#include <sys/param.h> 26#include <sys/ucred.h> 27#include <sys/mount.h> 28#include <sys/ioctl.h> 29#include <sys/disk.h> 30#include <sys/sysctl.h> 31#include <setjmp.h> 32 33#include <hfs/hfs_mount.h> 34 35#include <errno.h> 36#include <fcntl.h> 37#include <stdio.h> 38#include <string.h> 39#include <unistd.h> 40#include <stdlib.h> 41#include <ctype.h> 42#include <signal.h> 43 44#include <TargetConditionals.h> 45 46#include "fsck_hfs.h" 47#include "fsck_msgnums.h" 48#include "fsck_hfs_msgnums.h" 49 50#include "fsck_debug.h" 51#include "dfalib/CheckHFS.h" 52 53/* 54 * These definitions are duplicated from xnu's hfs_readwrite.c, and could live 55 * in a shared header file if desired. On the other hand, the freeze and thaw 56 * commands are not really supposed to be public. 57 */ 58#ifndef F_FREEZE_FS 59#define F_FREEZE_FS 53 /* "freeze" all fs operations */ 60#define F_THAW_FS 54 /* "thaw" all fs operations */ 61#endif // F_FREEZE_FS 62 63/* Global Variables for front end */ 64const char *cdevname; /* name of device being checked */ 65char *progname; 66char lflag; /* live fsck */ 67char nflag; /* assume a no response */ 68char yflag; /* assume a yes response */ 69char preen; /* just fix normal inconsistencies */ 70char force; /* force fsck even if clean (preen only) */ 71char quick; /* quick check returns clean, dirty, or failure */ 72char debug; /* output debugging info */ 73char disable_journal; /* If debug, and set, do not simulate journal replay */ 74char scanflag; /* Scan entire disk for bad blocks */ 75#if !TARGET_OS_EMBEDDED 76char embedded = 0; 77#else 78char embedded = 1; 79#endif 80 81char hotroot; /* checking root device */ 82char hotmount; /* checking read-only mounted device */ 83char guiControl; /* this app should output info for gui control */ 84char xmlControl; /* Output XML (plist) messages -- implies guiControl as well */ 85char rebuildBTree; /* Rebuild requested btree files */ 86int rebuildOptions; /* Options to indicate which btree should be rebuilt */ 87char modeSetting; /* set the mode when creating "lost+found" directory */ 88char errorOnExit = 0; /* Exit on first error */ 89int upgrading; /* upgrading format */ 90int lostAndFoundMode = 0; /* octal mode used when creating "lost+found" directory */ 91uint64_t reqCacheSize; /* Cache size requested by the caller (may be specified by the user via -c) */ 92 93int fsmodified; /* 1 => write done to file system */ 94int fsreadfd; /* file descriptor for reading file system */ 95int fswritefd; /* file descriptor for writing file system */ 96Cache_t fscache; 97 98/* 99 * Variables used to map physical block numbers to file paths 100 */ 101enum { BLOCK_LIST_INCREMENT = 512 }; 102int gBlkListEntries = 0; 103u_int64_t *gBlockList = NULL; 104int gFoundBlockEntries = 0; 105struct found_blocks *gFoundBlocksList = NULL; 106long gBlockSize = 512; 107static void ScanDisk(int); 108static int getblocklist(const char *filepath); 109 110 111static int checkfilesys __P((char * filesys)); 112static int setup __P(( char *dev, int *canWritePtr )); 113static void usage __P((void)); 114static void getWriteAccess __P(( char *dev, int *canWritePtr )); 115extern char *unrawname __P((char *name)); 116 117int 118main(argc, argv) 119 int argc; 120 char *argv[]; 121{ 122 int ch; 123 int ret; 124 extern int optind; 125 extern char *optarg; 126 char * lastChar; 127 128 if ((progname = strrchr(*argv, '/'))) 129 ++progname; 130 else 131 progname = *argv; 132 133 while ((ch = getopt(argc, argv, "b:B:c:D:e:Edfglm:npqrR:SuyxJ")) != EOF) { 134 switch (ch) { 135 case 'b': 136 gBlockSize = atoi(optarg); 137 if ((gBlockSize < 512) || (gBlockSize & (gBlockSize-1))) { 138 (void) fprintf(stderr, "%s invalid block size %d\n", 139 progname, gBlockSize); 140 exit(2); 141 } 142 break; 143 case 'S': 144 scanflag = 1; 145 break; 146 case 'B': 147 getblocklist(optarg); 148 break; 149 case 'c': 150 /* Cache size to use in fsck_hfs */ 151 reqCacheSize = strtoull(optarg, &lastChar, 0); 152 if (*lastChar) { 153 switch (tolower(*lastChar)) { 154 case 'g': 155 reqCacheSize *= 1024ULL; 156 /* fall through */ 157 case 'm': 158 reqCacheSize *= 1024ULL; 159 /* fall through */ 160 case 'k': 161 reqCacheSize *= 1024ULL; 162 break; 163 default: 164 reqCacheSize = 0; 165 break; 166 }; 167 } 168 break; 169 170 case 'd': 171 debug++; 172 break; 173 174 case 'J': 175 disable_journal++; 176 break; 177 case 'D': 178 /* Input value should be in hex example: -D 0x5 */ 179 cur_debug_level = strtoul(optarg, NULL, 0); 180 if (cur_debug_level == 0) { 181 (void) fplog (stderr, "%s: invalid debug development argument. Assuming zero\n", progname); 182 } 183 break; 184 185 case 'e': 186 if (optarg) { 187 if (strcasecmp(optarg, "embedded") == 0) 188 embedded = 1; 189 else if (strcasecmp(optarg, "desktop") == 0) 190 embedded = 0; 191 } 192 break; 193 194 case 'E': 195 /* Exit on first error, after logging it */ 196 errorOnExit = 1; 197 break; 198 case 'f': 199 force++; 200 break; 201 202 case 'g': 203 guiControl++; 204 break; 205 206 case 'x': 207 guiControl = 1; 208 xmlControl++; 209 break; 210 211 case 'l': 212 lflag++; 213 nflag++; 214 yflag = 0; 215 force++; 216 break; 217 218 case 'm': 219 modeSetting++; 220 lostAndFoundMode = strtol( optarg, NULL, 8 ); 221 if ( lostAndFoundMode == 0 ) 222 { 223 (void) fplog(stderr, "%s: invalid mode argument \n", progname); 224 usage(); 225 } 226 break; 227 228 case 'n': 229 nflag++; 230 yflag = 0; 231 break; 232 233 case 'p': 234 preen++; 235 break; 236 237 case 'q': 238 quick++; 239 break; 240 241 case 'r': 242 // rebuild catalog btree 243 rebuildBTree++; 244 rebuildOptions |= REBUILD_CATALOG; 245 break; 246 247 case 'R': 248 if (optarg) { 249 char *cp = optarg; 250 while (*cp) { 251 switch (*cp) { 252 case 'a': 253 // rebuild attribute btree 254 rebuildBTree++; 255 rebuildOptions |= REBUILD_ATTRIBUTE; 256 break; 257 258 case 'c': 259 // rebuild catalog btree 260 rebuildBTree++; 261 rebuildOptions |= REBUILD_CATALOG; 262 break; 263 264 case 'e': 265 // rebuild extents overflow btree 266 rebuildBTree++; 267 rebuildOptions |= REBUILD_EXTENTS; 268 break; 269 270 default: 271 fprintf(stderr, "%s: unknown btree rebuild code `%c' (%#x)\n", progname, *cp, *cp); 272 exit(2); 273 } 274 cp++; 275 } 276 break; 277 } 278 279 case 'y': 280 yflag++; 281 nflag = 0; 282 break; 283 284 case 'u': 285 case '?': 286 default: 287 usage(); 288 } 289 } 290 291 argc -= optind; 292 argv += optind; 293 294 if (debug == 0 && disable_journal != 0) 295 disable_journal = 0; 296 297 if (gBlkListEntries != 0 && gBlockSize == 0) 298 gBlockSize = 512; 299 300 if (guiControl) 301 debug = 0; /* debugging is for command line only */ 302 303 if (signal(SIGINT, SIG_IGN) != SIG_IGN) 304 (void)signal(SIGINT, catch); 305 306 if (argc < 1) { 307 (void) fplog(stderr, "%s: missing special-device\n", progname); 308 usage(); 309 } 310 311 ret = 0; 312 while (argc-- > 0) 313 ret |= checkfilesys(blockcheck(*argv++)); 314 315 exit(ret); 316} 317 318int fs_fd=-1; // fd to the root-dir of the fs we're checking (only w/lfag == 1) 319 320void 321cleanup_fs_fd(void) 322{ 323 if (fs_fd >= 0) { 324 fcntl(fs_fd, F_THAW_FS, NULL); 325 close(fs_fd); 326 fs_fd = -1; 327 } 328} 329 330static char * 331mountpoint(const char *cdev) 332{ 333 char *retval = NULL; 334 struct statfs *fsinfo; 335 char *unraw = NULL; 336 int result; 337 int i; 338 339 unraw = strdup(cdev); 340 unrawname(unraw); 341 342 if (unraw == NULL) 343 goto done; 344 345 result = getmntinfo(&fsinfo, MNT_NOWAIT); 346 347 for (i = 0; i < result; i++) { 348 if (strcmp(unraw, fsinfo[i].f_mntfromname) == 0) { 349 retval = strdup(fsinfo[i].f_mntonname); 350 break; 351 } 352 } 353 354done: 355 if (unraw) 356 free(unraw); 357 358 return retval; 359} 360 361static int 362checkfilesys(char * filesys) 363{ 364 int flags; 365 int result = 0; 366 int chkLev, repLev, logLev; 367 int canWrite; 368 char *mntonname = NULL; 369 fsck_ctx_t context = NULL; 370 flags = 0; 371 cdevname = filesys; 372 canWrite = 0; 373 hotmount = hotroot; // hotroot will be 1 or 0 by this time 374 375 // 376 // initialize the printing/logging without actually printing anything 377 // DO NOT DELETE THIS or else you can deadlock during a live fsck 378 // when something is printed and we try to create the log file. 379 // 380 plog(""); 381 382 context = fsckCreate(); 383 384 mntonname = mountpoint(cdevname); 385 if (hotroot) { 386 if (mntonname) 387 free(mntonname); 388 mntonname = strdup("/"); 389 } 390 391 if (lflag) { 392 struct stat fs_stat; 393 394 /* 395 * Ensure that, if we're doing a live verify, that we're not trying 396 * to do input or output to the same device. This would cause a deadlock. 397 */ 398 399 if (stat(cdevname, &fs_stat) != -1 && 400 (((fs_stat.st_mode & S_IFMT) == S_IFCHR) || 401 ((fs_stat.st_mode & S_IFMT) == S_IFBLK))) { 402 struct stat io_stat; 403 404 if (fstat(fileno(stdin), &io_stat) != -1 && 405 (fs_stat.st_rdev == io_stat.st_dev)) { 406 plog("ERROR: input redirected from target volume for live verify.\n"); 407 return EEXIT; 408 } 409 if (fstat(fileno(stdout), &io_stat) != -1 && 410 (fs_stat.st_rdev == io_stat.st_dev)) { 411 plog("ERROR: output redirected to target volume for live verify.\n"); 412 return EEXIT; 413 } 414 if (fstat(fileno(stderr), &io_stat) != -1 && 415 (fs_stat.st_rdev == io_stat.st_dev)) { 416 plog("ERROR: error output redirected to target volume for live verify.\n"); 417 return EEXIT; 418 } 419 420 } 421 } 422 423 /* 424 * If the device is mounted somewhere, then we need to make sure that it's 425 * a read-only device, or that a live-verify has been requested. 426 */ 427 if (mntonname != NULL) { 428 struct statfs stfs_buf; 429 430 if (statfs(mntonname, &stfs_buf) == 0) { 431 if (lflag) { 432 // Need to try to freeze it 433 fs_fd = open(mntonname, O_RDONLY); 434 if (fs_fd < 0) { 435 plog("ERROR: could not open %s to freeze the volume.\n", mntonname); 436 free(mntonname); 437 return 0; 438 } 439 440 if (fcntl(fs_fd, F_FREEZE_FS, NULL) != 0) { 441 free(mntonname); 442 plog("ERROR: could not freeze volume (%s)\n", strerror(errno)); 443 return 0; 444 } 445 } else if (stfs_buf.f_flags & MNT_RDONLY) { 446 hotmount = 1; 447 } 448 } 449 } 450 451 if (debug && preen) 452 pwarn("starting\n"); 453 454 if (setup( filesys, &canWrite ) == 0) { 455 if (preen) 456 pfatal("CAN'T CHECK FILE SYSTEM."); 457 result = EEXIT; 458 goto ExitThisRoutine; 459 } 460 461 if (preen == 0) { 462 if (hotroot && !guiControl) 463 plog("** Root file system\n"); 464 } 465 466 /* start with defaults for dfa back-end */ 467 chkLev = kAlwaysCheck; 468 repLev = kMajorRepairs; 469 logLev = kVerboseLog; 470 471 if (yflag) 472 repLev = kMajorRepairs; 473 474 if (quick) { 475 chkLev = kNeverCheck; 476 repLev = kNeverRepair; 477 logLev = kFatalLog; 478 } else if (force) { 479 chkLev = kForceCheck; 480 } 481 if (preen) { 482 repLev = kMinorRepairs; 483 chkLev = force ? kAlwaysCheck : kDirtyCheck; 484 logLev = kFatalLog; 485 } 486 if (debug) 487 logLev = kDebugLog; 488 489 if (nflag) 490 repLev = kNeverRepair; 491 492 if ( rebuildBTree ) { 493 chkLev = kPartialCheck; 494 repLev = kForceRepairs; // this will force rebuild of B-Tree file 495 } 496 497 fsckSetVerbosity(context, logLev); 498 /* All of fsck_hfs' output should go thorugh logstring */ 499 fsckSetOutput(context, NULL); 500 /* Setup writer that will output to standard out */ 501 fsckSetWriter(context, &outstring); 502 /* Setup logger that will write to log file */ 503 fsckSetLogger(context, &logstring); 504 if (guiControl) { 505 if (xmlControl) 506 fsckSetOutputStyle(context, fsckOutputXML); 507 else 508 fsckSetOutputStyle(context, fsckOutputGUI); 509 } else { 510 fsckSetOutputStyle(context, fsckOutputTraditional); 511 } 512 513 if (errorOnExit && nflag) { 514 chkLev = kMajorCheck; 515 } 516 517 /* 518 * go check HFS volume... 519 */ 520 521 if (rebuildOptions && canWrite == 0) { 522 plog("BTree rebuild requested but writing disabled\n"); 523 result = EEXIT; 524 goto ExitThisRoutine; 525 } 526 527 if (gBlockList != NULL && scanflag != 0) { 528 plog("Cannot scan for bad blocks and ask for listed blocks to file mapping\n"); 529 result = EEXIT; 530 goto ExitThisRoutine; 531 } 532 if (scanflag != 0) { 533 plog("Scanning entire disk for bad blocks\n"); 534 ScanDisk(fsreadfd); 535 } 536 537 result = CheckHFS( filesys, fsreadfd, fswritefd, chkLev, repLev, context, 538 lostAndFoundMode, canWrite, &fsmodified, 539 lflag, rebuildOptions ); 540 if (debug) 541 plog("\tCheckHFS returned %d, fsmodified = %d\n", result, fsmodified); 542 543 if (!hotmount) { 544 ckfini(1); 545 if (quick) { 546 if (result == 0) { 547 pwarn("QUICKCHECK ONLY; FILESYSTEM CLEAN\n"); 548 result = 0; 549 goto ExitThisRoutine; 550 } else if (result == R_Dirty) { 551 pwarn("QUICKCHECK ONLY; FILESYSTEM DIRTY\n"); 552 result = DIRTYEXIT; 553 goto ExitThisRoutine; 554 } else if (result == R_BadSig) { 555 pwarn("QUICKCHECK ONLY; NO HFS SIGNATURE FOUND\n"); 556 result = DIRTYEXIT; 557 goto ExitThisRoutine; 558 } else { 559 result = EEXIT; 560 goto ExitThisRoutine; 561 } 562 } 563 } else { 564 struct statfs stfs_buf; 565 566 /* 567 * Check to see if root is mounted read-write. 568 */ 569 if (statfs(mntonname, &stfs_buf) == 0) 570 flags = stfs_buf.f_flags; 571 else 572 flags = 0; 573 ckfini(flags & MNT_RDONLY); 574 } 575 576 /* XXX free any allocated memory here */ 577 578 if (hotmount && fsmodified) { 579 struct hfs_mount_args args; 580 /* 581 * We modified the root. Do a mount update on 582 * it, unless it is read-write, so we can continue. 583 */ 584 if (!preen) 585 fsckPrint(context, fsckVolumeModified); 586 if (flags & MNT_RDONLY) { 587 bzero(&args, sizeof(args)); 588 flags |= MNT_UPDATE | MNT_RELOAD; 589 if (debug) 590 fprintf(stderr, "doing update / reload mount for %s now\n", mntonname); 591 if (mount("hfs", mntonname, flags, &args) == 0) { 592 if (result != 0) 593 result = EEXIT; 594 goto ExitThisRoutine; 595 } else { 596 //if (debug) 597 fprintf(stderr, "update/reload mount for %s failed: %s\n", mntonname, strerror(errno)); 598 } 599 } 600 if (!preen) 601 plog("\n***** REBOOT NOW *****\n"); 602 sync(); 603 result = FIXEDROOTEXIT; 604 goto ExitThisRoutine; 605 } 606 607 if (result != 0 && result != MAJOREXIT) 608 result = EEXIT; 609 610ExitThisRoutine: 611 if (lflag) { 612 if (fs_fd >= 0) { 613 fcntl(fs_fd, F_THAW_FS, NULL); 614 close(fs_fd); 615 fs_fd = -1; 616 } 617 } 618 if (mntonname) 619 free(mntonname); 620 621 if (context) 622 fsckDestroy(context); 623 624 return (result); 625} 626 627 628/* 629 * Setup for I/O to device 630 * Return 1 if successful, 0 if unsuccessful. 631 * canWrite - 1 if we can safely write to the raw device or 0 if not. 632 */ 633static int 634setup( char *dev, int *canWritePtr ) 635{ 636 struct stat statb; 637 int devBlockSize; 638 uint32_t cacheBlockSize; 639 uint32_t cacheTotalBlocks; 640 int preTouchMem = 0; 641 642 fswritefd = -1; 643 *canWritePtr = 0; 644 645 if (stat(dev, &statb) < 0) { 646 plog("Can't stat %s: %s\n", dev, strerror(errno)); 647 return (0); 648 } 649 if ((statb.st_mode & S_IFMT) != S_IFCHR) { 650 pfatal("%s is not a character device", dev); 651 if (reply("CONTINUE") == 0) 652 return (0); 653 } 654 /* Always attempt to replay the journal */ 655 if (!nflag && !quick) { 656 // We know we have a character device by now. 657 if (strncmp(dev, "/dev/rdisk", 10) == 0) { 658 char block_device[MAXPATHLEN+1]; 659 int rv; 660 snprintf(block_device, sizeof(block_device), "/dev/%s", dev + 6); 661 rv = journal_replay(block_device); 662 if (debug) 663 plog("journal_replay(%s) returned %d\n", block_device, rv); 664 } 665 } 666 /* attempt to get write access to the block device and if not check if volume is */ 667 /* mounted read-only. */ 668 if (nflag == 0 && quick == 0) { 669 getWriteAccess( dev, canWritePtr ); 670 } 671 672 if (nflag || quick || (fswritefd = open(dev, O_RDWR | (hotmount ? 0 : O_EXLOCK))) < 0) { 673 fswritefd = -1; 674 if (preen) { 675 pfatal("** %s (NO WRITE ACCESS)\n", dev); 676 } 677 } 678 679 if (preen == 0 && !guiControl) { 680 if (nflag || quick || fswritefd == -1) { 681 plog("** %s (NO WRITE)\n", dev); 682 } else { 683 plog("** %s\n", dev); 684 } 685 } 686 687 if (fswritefd == -1) { 688 if ((fsreadfd = open(dev, O_RDONLY)) < 0) { 689 plog("Can't open %s: %s\n", dev, strerror(errno)); 690 return (0); 691 } 692 } else { 693 fsreadfd = dup(fswritefd); 694 if (fsreadfd < 0) { 695 plog("Can't dup fd for reading on %s: %s\n", dev, strerror(errno)); 696 close(fswritefd); 697 return(0); 698 } 699 } 700 701 702 /* Get device block size to initialize cache */ 703 if (ioctl(fsreadfd, DKIOCGETBLOCKSIZE, &devBlockSize) < 0) { 704 pfatal ("Can't get device block size\n"); 705 return (0); 706 } 707 708 /* 709 * Calculate the cache block size and total blocks. 710 * 711 * If a quick check was requested, we'll only be checking to see if 712 * the volume was cleanly unmounted or journalled, so we won't need 713 * a lot of cache. Since lots of quick checks can be run in parallel 714 * when a new disk with several partitions comes on line, let's avoid 715 * the memory usage when we don't need it. 716 */ 717 if (reqCacheSize == 0 && quick == 0) { 718 /* 719 * Auto-pick the cache size. The cache code will deal with minimum 720 * maximum values, so we just need to find out the size of memory, and 721 * how much of it we'll use. 722 * 723 * If we're looking at the root device, and it's not a live verify (lflag), 724 * then we will use half of physical memory; otherwise, we'll use an eigth. 725 * 726 */ 727 uint64_t memSize; 728 size_t dsize = sizeof(memSize); 729 int rv; 730 731 rv = sysctlbyname("hw.memsize", &memSize, &dsize, NULL, 0); 732 if (rv == -1) { 733 (void)fplog(stderr, "sysctlbyname failed, not auto-setting cache size\n"); 734 } else { 735 int d = (hotroot && !lflag) ? 2 : 8; 736 int safeMode = 0; 737 dsize = sizeof(safeMode); 738 rv = sysctlbyname("kern.safeboot", &safeMode, &dsize, NULL, 0); 739 if (rv != -1 && safeMode != 0 && hotroot && !lflag) { 740#define kMaxSafeModeMem ((size_t)2 * 1024 * 1024 * 1024) /* 2Gbytes, means cache will max out at 1gbyte */ 741 if (debug) { 742 (void)fplog(stderr, "Safe mode and single-user, setting memsize to a maximum of 2gbytes\n"); 743 } 744 memSize = (memSize < kMaxSafeModeMem) ? memSize : kMaxSafeModeMem; 745 } 746 reqCacheSize = memSize / d; 747 } 748 } 749 750 CalculateCacheSizes(reqCacheSize, &cacheBlockSize, &cacheTotalBlocks, debug); 751 752 preTouchMem = (hotroot != 0) && (lflag != 0); 753 /* Initialize the cache */ 754 if (CacheInit (&fscache, fsreadfd, fswritefd, devBlockSize, 755 cacheBlockSize, cacheTotalBlocks, CacheHashSize, preTouchMem) != EOK) { 756 pfatal("Can't initialize disk cache\n"); 757 return (0); 758 } 759 760 return (1); 761} 762 763 764// This routine will attempt to open the block device with write access for the target 765// volume in order to block others from mounting the volume with write access while we 766// check / repair it. If we cannot get write access then we check to see if the volume 767// has been mounted read-only. If it is read-only then we should be OK to write to 768// the raw device. Note that this does not protect use from someone upgrading the mount 769// from read-only to read-write. 770 771static void getWriteAccess( char *dev, int *canWritePtr ) 772{ 773 int i; 774 int myMountsCount; 775 void * myPtr; 776 char * myCharPtr; 777 struct statfs * myBufPtr; 778 void * myNamePtr; 779 int blockDevice_fd = -1; 780 781 myPtr = NULL; 782 myNamePtr = malloc( strlen(dev) + 2 ); 783 if ( myNamePtr == NULL ) 784 return; 785 786 strcpy( (char *)myNamePtr, dev ); 787 if ( (myCharPtr = strrchr( (char *)myNamePtr, '/' )) != 0 ) { 788 if ( myCharPtr[1] == 'r' ) { 789 memmove(&myCharPtr[1], &myCharPtr[2], strlen(&myCharPtr[2]) + 1); 790 blockDevice_fd = open( (char *)myNamePtr, O_WRONLY | (hotmount ? 0 : O_EXLOCK) ); 791 } 792 } 793 794 if ( blockDevice_fd > 0 ) { 795 // we got write access to the block device so we can safely write to raw device 796 *canWritePtr = 1; 797 goto ExitThisRoutine; 798 } 799 800 // get count of mounts then get the info for each 801 myMountsCount = getfsstat( NULL, 0, MNT_NOWAIT ); 802 if ( myMountsCount < 0 ) 803 goto ExitThisRoutine; 804 805 myPtr = (void *) malloc( sizeof(struct statfs) * myMountsCount ); 806 if ( myPtr == NULL ) 807 goto ExitThisRoutine; 808 myMountsCount = getfsstat( myPtr, 809 (int)(sizeof(struct statfs) * myMountsCount), 810 MNT_NOWAIT ); 811 if ( myMountsCount < 0 ) 812 goto ExitThisRoutine; 813 814 myBufPtr = (struct statfs *) myPtr; 815 for ( i = 0; i < myMountsCount; i++ ) 816 { 817 if ( strcmp( myBufPtr->f_mntfromname, myNamePtr ) == 0 ) { 818 if ( myBufPtr->f_flags & MNT_RDONLY ) 819 *canWritePtr = 1; 820 goto ExitThisRoutine; 821 } 822 myBufPtr++; 823 } 824 *canWritePtr = 1; // single user will get us here, f_mntfromname is not /dev/diskXXXX 825 826ExitThisRoutine: 827 if ( myPtr != NULL ) 828 free( myPtr ); 829 830 if ( myNamePtr != NULL ) 831 free( myNamePtr ); 832 833 if (blockDevice_fd != -1) { 834 close(blockDevice_fd); 835 } 836 837 return; 838 839} /* getWriteAccess */ 840 841 842static void 843usage() 844{ 845 (void) fplog(stderr, "usage: %s [-b [size] B [path] c [size] e [mode] ESdfglx m [mode] npqruy] special-device\n", progname); 846 (void) fplog(stderr, " b size = size of physical blocks (in bytes) for -B option\n"); 847 (void) fplog(stderr, " B path = file containing physical block numbers to map to paths\n"); 848 (void) fplog(stderr, " c size = cache size (ex. 512m, 1g)\n"); 849 (void) fplog(stderr, " e mode = emulate 'embedded' or 'desktop'\n"); 850 (void) fplog(stderr, " E = exit on first major error\n"); 851 (void) fplog(stderr, " d = output debugging info\n"); 852 (void) fplog(stderr, " f = force fsck even if clean (preen only) \n"); 853 (void) fplog(stderr, " g = GUI output mode\n"); 854 (void) fplog(stderr, " x = XML output mode\n"); 855 (void) fplog(stderr, " l = live fsck (lock down and test-only)\n"); 856 (void) fplog(stderr, " m arg = octal mode used when creating lost+found directory \n"); 857 (void) fplog(stderr, " n = assume a no response \n"); 858 (void) fplog(stderr, " p = just fix normal inconsistencies \n"); 859 (void) fplog(stderr, " q = quick check returns clean, dirty, or failure \n"); 860 (void) fplog(stderr, " r = rebuild catalog btree \n"); 861 (void) fplog(stderr, " S = Scan disk for bad blocks\n"); 862 (void) fplog(stderr, " u = usage \n"); 863 (void) fplog(stderr, " y = assume a yes response \n"); 864 865 exit(1); 866} 867 868 869static void 870AddBlockToList(long long block) 871{ 872 873 if ((gBlkListEntries % BLOCK_LIST_INCREMENT) == 0) { 874 void *tmp; 875 876// gBlkListEntries += BLOCK_LIST_INCREMENT; 877 tmp = realloc(gBlockList, (gBlkListEntries + BLOCK_LIST_INCREMENT) * sizeof(u_int64_t)); 878 if (tmp == NULL) { 879 pfatal("Can't allocate memory for block list (%llu entries).\n", gBlkListEntries); 880 } 881 gBlockList = (u_int64_t*)tmp; 882 } 883 gBlockList[gBlkListEntries++] = block; 884 return; 885} 886 887static int printStatus; 888static void 889siginfo(int signo) 890{ 891 printStatus = 1; 892} 893 894static void 895ScanDisk(int fd) 896{ 897 uint32_t devBlockSize = 512; 898 uint64_t devBlockTotal; 899 off_t diskSize; 900 uint8_t *buffer = NULL; 901 size_t bufSize = 1024 * 1024; 902 ssize_t nread; 903 off_t curPos = 0; 904 void (*oldhandler)(int); 905 uint32_t numErrors = 0; 906 uint32_t maxErrors = 40; // Something more variable? 907 908 oldhandler = signal(SIGINFO, &siginfo); 909 910#define PRSTAT \ 911 do { \ 912 if (diskSize) { \ 913 fprintf(stderr, "Scanning offset %lld of %lld (%d%%)\n", \ 914 curPos, diskSize, (int)((curPos * 100) / diskSize)); \ 915 } else { \ 916 fprintf(stderr, "Scanning offset %lld\n", curPos); \ 917 } \ 918 printStatus = 0; \ 919 } while (0) 920 921 if (ioctl(fd, DKIOCGETBLOCKSIZE, &devBlockSize) == -1) { 922 devBlockSize = 512; 923 } 924 925 if (ioctl(fd, DKIOCGETBLOCKCOUNT, &devBlockTotal) == -1) { 926 diskSize = 0; 927 } else 928 diskSize = devBlockTotal * devBlockSize; 929 930 while (buffer == NULL && bufSize >= devBlockSize) { 931 buffer = malloc(bufSize); 932 if (buffer == NULL) { 933 bufSize /= 2; 934 } 935 } 936 if (buffer == NULL) { 937 pfatal("Cannot allocate buffer for disk scan.\n"); 938 } 939 940loop: 941 942 if (printStatus) { 943 PRSTAT; 944 } 945 while ((nread = pread(fd, buffer, bufSize, curPos)) == bufSize) { 946 curPos += bufSize; 947 if (printStatus) { 948 PRSTAT; 949 } 950 } 951 952 if (nread == 0) { 953 /* We're done with the disk */ 954 goto done; 955 } 956 if (nread == -1) { 957 if (errno == EIO) { 958 /* Try reading devBlockSize blocks */ 959 size_t total; 960 for (total = 0; total < bufSize; total += devBlockSize) { 961 nread = pread(fd, buffer, devBlockSize, curPos + total); 962 if (nread == -1) { 963 if (errno == EIO) { 964 if (debug) 965 fprintf(stderr, "Bad block at offset %lld\n", curPos + total); 966 AddBlockToList((curPos + total) / gBlockSize); 967 if (++numErrors > maxErrors) { 968 if (debug) 969 fprintf(stderr, "Got %u errors, maxing out so stopping scan\n", numErrors); 970 goto done; 971 } 972 continue; 973 } else { 974 pfatal("Got a non I/O error reading disk at offset %llu: %s\n", 975 curPos + total, strerror(errno)); 976 // Hey, pfatal wasn't fatal! 977 // But that seems to work out for us for some reason. 978 } 979 } 980 if (nread == 0) { 981 /* End of disk, somehow. */ 982 goto done; 983 } 984 if (nread != devBlockSize) { 985 pwarn("During disk scan, did not get block size (%zd) read, got %zd instead. Skipping rest of this block.\n", (size_t)devBlockSize, nread); 986 continue; 987 } 988 } 989 curPos += total; 990 goto loop; 991 } else if (errno == EINTR) { 992 goto loop; 993 } else { 994 pfatal("Got a non I/O error reading disk at offset %llu: %s\n", curPos, strerror(errno)); 995 exit(EEXIT); 996 } 997 } 998 if (nread < bufSize) { 999 if ((nread % devBlockSize) == 0) { 1000 curPos += nread; 1001 } else { 1002 curPos = curPos + (((nread % devBlockSize) + 1) * devBlockSize); 1003 } 1004 goto loop; 1005 } 1006 goto loop; 1007done: 1008 if (buffer) 1009 free(buffer); 1010 signal(SIGINFO, oldhandler); 1011 return; 1012 1013} 1014 1015static int 1016getblocklist(const char *filepath) 1017{ 1018 FILE * file; 1019 long long block; 1020 size_t blockListCount; /* Number of elements allocated to gBlockList array */ 1021 1022 blockListCount = BLOCK_LIST_INCREMENT; 1023 gBlockList = (u_int64_t *) malloc(blockListCount * sizeof(u_int64_t)); 1024 if (gBlockList == NULL) 1025 pfatal("Can't allocate memory for block list.\n"); 1026 1027// printf("getblocklist: processing blocklist %s...\n", filepath); 1028 1029 if ((file = fopen(filepath, "r")) == NULL) 1030 pfatal("Can't open %s\n", filepath); 1031 1032 while (fscanf(file, "%lli", &block) > 0) { 1033 AddBlockToList(block); 1034 } 1035 1036 (void) fclose(file); 1037 1038 printf("%d blocks to match:\n", gBlkListEntries); 1039 1040 return (0); 1041} 1042