]> git.saurik.com Git - apple/hfs.git/blob - fsck_hfs/fsck_hfs.c
hfs-407.200.4.tar.gz
[apple/hfs.git] / fsck_hfs / fsck_hfs.c
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 */
64 const char *cdevname; /* name of device being checked */
65 char *progname;
66 char lflag; /* live fsck */
67 char nflag; /* assume a no response */
68 char yflag; /* assume a yes response */
69 char preen; /* just fix normal inconsistencies */
70 char force; /* force fsck even if clean (preen only) */
71 char quick; /* quick check returns clean, dirty, or failure */
72 char debug; /* output debugging info */
73 char disable_journal; /* If debug, and set, do not simulate journal replay */
74 char scanflag; /* Scan entire disk for bad blocks */
75 #if !TARGET_OS_EMBEDDED
76 char embedded = 0;
77 #else
78 char embedded = 1;
79 #endif
80
81 char hotroot; /* checking root device */
82 char hotmount; /* checking read-only mounted device */
83 char guiControl; /* this app should output info for gui control */
84 char xmlControl; /* Output XML (plist) messages -- implies guiControl as well */
85 char rebuildBTree; /* Rebuild requested btree files */
86 int rebuildOptions; /* Options to indicate which btree should be rebuilt */
87 char modeSetting; /* set the mode when creating "lost+found" directory */
88 char errorOnExit = 0; /* Exit on first error */
89 int upgrading; /* upgrading format */
90 int lostAndFoundMode = 0; /* octal mode used when creating "lost+found" directory */
91 uint64_t reqCacheSize; /* Cache size requested by the caller (may be specified by the user via -c) */
92
93 int fsmodified; /* 1 => write done to file system */
94 int fsreadfd; /* file descriptor for reading file system */
95 int fswritefd; /* file descriptor for writing file system */
96 Cache_t fscache;
97
98 /*
99 * Variables used to map physical block numbers to file paths
100 */
101 enum { BLOCK_LIST_INCREMENT = 512 };
102 int gBlkListEntries = 0;
103 u_int64_t *gBlockList = NULL;
104 int gFoundBlockEntries = 0;
105 struct found_blocks *gFoundBlocksList = NULL;
106 long gBlockSize = 512;
107 static void ScanDisk(int);
108 static int getblocklist(const char *filepath);
109
110
111 static int checkfilesys __P((char * filesys));
112 static int setup __P(( char *dev, int *canWritePtr ));
113 static void usage __P((void));
114 static void getWriteAccess __P(( char *dev, int *canWritePtr ));
115 extern char *unrawname __P((char *name));
116
117 int
118 main(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
318 int fs_fd=-1; // fd to the root-dir of the fs we're checking (only w/lfag == 1)
319
320 void
321 cleanup_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
330 static char *
331 mountpoint(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
354 done:
355 if (unraw)
356 free(unraw);
357
358 return retval;
359 }
360
361 static int
362 checkfilesys(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 EEXIT;
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 EEXIT;
444 }
445 } else if (stfs_buf.f_flags & MNT_RDONLY) {
446 hotmount = 1;
447 } else {
448 /* MNT_RDONLY is not set and this is not a live verification */
449 plog("ERROR: volume %s is mounted with write access. Re-run with (-l) to freeze volume.\n", mntonname);
450 return EEXIT;
451 }
452 }
453 }
454
455 if (debug && preen)
456 pwarn("starting\n");
457
458 if (setup( filesys, &canWrite ) == 0) {
459 if (preen)
460 pfatal("CAN'T CHECK FILE SYSTEM.");
461 result = EEXIT;
462 goto ExitThisRoutine;
463 }
464
465 if (preen == 0) {
466 if (hotroot && !guiControl)
467 plog("** Root file system\n");
468 }
469
470 /* start with defaults for dfa back-end */
471 chkLev = kAlwaysCheck;
472 repLev = kMajorRepairs;
473 logLev = kVerboseLog;
474
475 if (yflag)
476 repLev = kMajorRepairs;
477
478 if (quick) {
479 chkLev = kNeverCheck;
480 repLev = kNeverRepair;
481 logLev = kFatalLog;
482 } else if (force) {
483 chkLev = kForceCheck;
484 }
485 if (preen) {
486 repLev = kMinorRepairs;
487 chkLev = force ? kAlwaysCheck : kDirtyCheck;
488 logLev = kFatalLog;
489 }
490 if (debug)
491 logLev = kDebugLog;
492
493 if (nflag)
494 repLev = kNeverRepair;
495
496 if ( rebuildBTree ) {
497 chkLev = kPartialCheck;
498 repLev = kForceRepairs; // this will force rebuild of B-Tree file
499 }
500
501 fsckSetVerbosity(context, logLev);
502 /* All of fsck_hfs' output should go thorugh logstring */
503 fsckSetOutput(context, NULL);
504 /* Setup writer that will output to standard out */
505 fsckSetWriter(context, &outstring);
506 /* Setup logger that will write to log file */
507 fsckSetLogger(context, &logstring);
508 if (guiControl) {
509 if (xmlControl)
510 fsckSetOutputStyle(context, fsckOutputXML);
511 else
512 fsckSetOutputStyle(context, fsckOutputGUI);
513 } else {
514 fsckSetOutputStyle(context, fsckOutputTraditional);
515 }
516
517 if (errorOnExit && nflag) {
518 chkLev = kMajorCheck;
519 }
520
521 /*
522 * go check HFS volume...
523 */
524
525 if (rebuildOptions && canWrite == 0) {
526 plog("BTree rebuild requested but writing disabled\n");
527 result = EEXIT;
528 goto ExitThisRoutine;
529 }
530
531 if (gBlockList != NULL && scanflag != 0) {
532 plog("Cannot scan for bad blocks and ask for listed blocks to file mapping\n");
533 result = EEXIT;
534 goto ExitThisRoutine;
535 }
536 if (scanflag != 0) {
537 plog("Scanning entire disk for bad blocks\n");
538 ScanDisk(fsreadfd);
539 }
540
541 result = CheckHFS( filesys, fsreadfd, fswritefd, chkLev, repLev, context,
542 lostAndFoundMode, canWrite, &fsmodified,
543 lflag, rebuildOptions );
544 if (debug)
545 plog("\tCheckHFS returned %d, fsmodified = %d\n", result, fsmodified);
546
547 if (!hotmount) {
548 ckfini(1);
549 if (quick) {
550 if (result == 0) {
551 pwarn("QUICKCHECK ONLY; FILESYSTEM CLEAN\n");
552 result = 0;
553 goto ExitThisRoutine;
554 } else if (result == R_Dirty) {
555 pwarn("QUICKCHECK ONLY; FILESYSTEM DIRTY\n");
556 result = DIRTYEXIT;
557 goto ExitThisRoutine;
558 } else if (result == R_BadSig) {
559 pwarn("QUICKCHECK ONLY; NO HFS SIGNATURE FOUND\n");
560 result = DIRTYEXIT;
561 goto ExitThisRoutine;
562 } else {
563 result = EEXIT;
564 goto ExitThisRoutine;
565 }
566 }
567 } else {
568 struct statfs stfs_buf;
569
570 /*
571 * Check to see if root is mounted read-write.
572 */
573 if (statfs(mntonname, &stfs_buf) == 0)
574 flags = stfs_buf.f_flags;
575 else
576 flags = 0;
577 ckfini(flags & MNT_RDONLY);
578 }
579
580 /* XXX free any allocated memory here */
581
582 if (hotmount && fsmodified) {
583 struct hfs_mount_args args;
584 /*
585 * We modified the root. Do a mount update on
586 * it, unless it is read-write, so we can continue.
587 */
588 if (!preen)
589 fsckPrint(context, fsckVolumeModified);
590 if (flags & MNT_RDONLY) {
591 bzero(&args, sizeof(args));
592 flags |= MNT_UPDATE | MNT_RELOAD;
593 if (debug)
594 fprintf(stderr, "doing update / reload mount for %s now\n", mntonname);
595 if (mount("hfs", mntonname, flags, &args) == 0) {
596 if (result != 0)
597 result = EEXIT;
598 goto ExitThisRoutine;
599 } else {
600 //if (debug)
601 fprintf(stderr, "update/reload mount for %s failed: %s\n", mntonname, strerror(errno));
602 }
603 }
604 if (!preen)
605 plog("\n***** REBOOT NOW *****\n");
606 sync();
607 result = FIXEDROOTEXIT;
608 goto ExitThisRoutine;
609 }
610
611 if (result != 0 && result != MAJOREXIT)
612 result = EEXIT;
613
614 ExitThisRoutine:
615 if (lflag) {
616 if (fs_fd >= 0) {
617 fcntl(fs_fd, F_THAW_FS, NULL);
618 close(fs_fd);
619 fs_fd = -1;
620 }
621 }
622 if (mntonname)
623 free(mntonname);
624
625 if (context)
626 fsckDestroy(context);
627
628 return (result);
629 }
630
631
632 /*
633 * Setup for I/O to device
634 * Return 1 if successful, 0 if unsuccessful.
635 * canWrite - 1 if we can safely write to the raw device or 0 if not.
636 */
637 static int
638 setup( char *dev, int *canWritePtr )
639 {
640 struct stat statb;
641 int devBlockSize;
642 uint32_t cacheBlockSize;
643 uint32_t cacheTotalBlocks;
644 int preTouchMem = 0;
645
646 fswritefd = -1;
647 *canWritePtr = 0;
648
649 if (stat(dev, &statb) < 0) {
650 plog("Can't stat %s: %s\n", dev, strerror(errno));
651 return (0);
652 }
653 if ((statb.st_mode & S_IFMT) != S_IFCHR) {
654 pfatal("%s is not a character device", dev);
655 if (reply("CONTINUE") == 0)
656 return (0);
657 }
658 /* Always attempt to replay the journal */
659 if (!nflag && !quick) {
660 // We know we have a character device by now.
661 if (strncmp(dev, "/dev/rdisk", 10) == 0) {
662 char block_device[MAXPATHLEN+1];
663 int rv;
664 snprintf(block_device, sizeof(block_device), "/dev/%s", dev + 6);
665 rv = journal_replay(block_device);
666 if (debug)
667 plog("journal_replay(%s) returned %d\n", block_device, rv);
668 }
669 }
670 /* attempt to get write access to the block device and if not check if volume is */
671 /* mounted read-only. */
672 if (nflag == 0 && quick == 0) {
673 getWriteAccess( dev, canWritePtr );
674 }
675
676 if (nflag || quick || (fswritefd = open(dev, O_RDWR | (hotmount ? 0 : O_EXLOCK))) < 0) {
677 fswritefd = -1;
678 if (preen) {
679 pfatal("** %s (NO WRITE ACCESS)\n", dev);
680 }
681 }
682
683 if (preen == 0 && !guiControl) {
684 if (nflag || quick || fswritefd == -1) {
685 plog("** %s (NO WRITE)\n", dev);
686 } else {
687 plog("** %s\n", dev);
688 }
689 }
690
691 if (fswritefd == -1) {
692 if ((fsreadfd = open(dev, O_RDONLY)) < 0) {
693 plog("Can't open %s: %s\n", dev, strerror(errno));
694 return (0);
695 }
696 } else {
697 fsreadfd = dup(fswritefd);
698 if (fsreadfd < 0) {
699 plog("Can't dup fd for reading on %s: %s\n", dev, strerror(errno));
700 close(fswritefd);
701 return(0);
702 }
703 }
704
705
706 /* Get device block size to initialize cache */
707 if (ioctl(fsreadfd, DKIOCGETBLOCKSIZE, &devBlockSize) < 0) {
708 pfatal ("Can't get device block size\n");
709 return (0);
710 }
711
712 /*
713 * Calculate the cache block size and total blocks.
714 *
715 * If a quick check was requested, we'll only be checking to see if
716 * the volume was cleanly unmounted or journalled, so we won't need
717 * a lot of cache. Since lots of quick checks can be run in parallel
718 * when a new disk with several partitions comes on line, let's avoid
719 * the memory usage when we don't need it.
720 */
721 if (reqCacheSize == 0 && quick == 0) {
722 /*
723 * Auto-pick the cache size. The cache code will deal with minimum
724 * maximum values, so we just need to find out the size of memory, and
725 * how much of it we'll use.
726 *
727 * If we're looking at the root device, and it's not a live verify (lflag),
728 * then we will use half of physical memory; otherwise, we'll use an eigth.
729 *
730 */
731 uint64_t memSize;
732 size_t dsize = sizeof(memSize);
733 int rv;
734
735 rv = sysctlbyname("hw.memsize", &memSize, &dsize, NULL, 0);
736 if (rv == -1) {
737 (void)fplog(stderr, "sysctlbyname failed, not auto-setting cache size\n");
738 } else {
739 int d = (hotroot && !lflag) ? 2 : 8;
740 int safeMode = 0;
741 dsize = sizeof(safeMode);
742 rv = sysctlbyname("kern.safeboot", &safeMode, &dsize, NULL, 0);
743 if (rv != -1 && safeMode != 0 && hotroot && !lflag) {
744 #define kMaxSafeModeMem ((size_t)2 * 1024 * 1024 * 1024) /* 2Gbytes, means cache will max out at 1gbyte */
745 if (debug) {
746 (void)fplog(stderr, "Safe mode and single-user, setting memsize to a maximum of 2gbytes\n");
747 }
748 memSize = (memSize < kMaxSafeModeMem) ? memSize : kMaxSafeModeMem;
749 }
750 reqCacheSize = memSize / d;
751 }
752 }
753
754 CalculateCacheSizes(reqCacheSize, &cacheBlockSize, &cacheTotalBlocks, debug);
755
756 preTouchMem = (hotroot != 0) && (lflag != 0);
757 /* Initialize the cache */
758 if (CacheInit (&fscache, fsreadfd, fswritefd, devBlockSize,
759 cacheBlockSize, cacheTotalBlocks, CacheHashSize, preTouchMem) != EOK) {
760 pfatal("Can't initialize disk cache\n");
761 return (0);
762 }
763
764 return (1);
765 }
766
767
768 // This routine will attempt to open the block device with write access for the target
769 // volume in order to block others from mounting the volume with write access while we
770 // check / repair it. If we cannot get write access then we check to see if the volume
771 // has been mounted read-only. If it is read-only then we should be OK to write to
772 // the raw device. Note that this does not protect use from someone upgrading the mount
773 // from read-only to read-write.
774
775 static void getWriteAccess( char *dev, int *canWritePtr )
776 {
777 int i;
778 int myMountsCount;
779 void * myPtr;
780 char * myCharPtr;
781 struct statfs * myBufPtr;
782 void * myNamePtr;
783 int blockDevice_fd = -1;
784
785 myPtr = NULL;
786 myNamePtr = malloc( strlen(dev) + 2 );
787 if ( myNamePtr == NULL )
788 return;
789
790 strcpy( (char *)myNamePtr, dev );
791 if ( (myCharPtr = strrchr( (char *)myNamePtr, '/' )) != 0 ) {
792 if ( myCharPtr[1] == 'r' ) {
793 memmove(&myCharPtr[1], &myCharPtr[2], strlen(&myCharPtr[2]) + 1);
794 blockDevice_fd = open( (char *)myNamePtr, O_WRONLY | (hotmount ? 0 : O_EXLOCK) );
795 }
796 }
797
798 if ( blockDevice_fd > 0 ) {
799 // we got write access to the block device so we can safely write to raw device
800 *canWritePtr = 1;
801 goto ExitThisRoutine;
802 }
803
804 // get count of mounts then get the info for each
805 myMountsCount = getfsstat( NULL, 0, MNT_NOWAIT );
806 if ( myMountsCount < 0 )
807 goto ExitThisRoutine;
808
809 myPtr = (void *) malloc( sizeof(struct statfs) * myMountsCount );
810 if ( myPtr == NULL )
811 goto ExitThisRoutine;
812 myMountsCount = getfsstat( myPtr,
813 (int)(sizeof(struct statfs) * myMountsCount),
814 MNT_NOWAIT );
815 if ( myMountsCount < 0 )
816 goto ExitThisRoutine;
817
818 myBufPtr = (struct statfs *) myPtr;
819 for ( i = 0; i < myMountsCount; i++ )
820 {
821 if ( strcmp( myBufPtr->f_mntfromname, myNamePtr ) == 0 ) {
822 if ( myBufPtr->f_flags & MNT_RDONLY )
823 *canWritePtr = 1;
824 goto ExitThisRoutine;
825 }
826 myBufPtr++;
827 }
828 *canWritePtr = 1; // single user will get us here, f_mntfromname is not /dev/diskXXXX
829
830 ExitThisRoutine:
831 if ( myPtr != NULL )
832 free( myPtr );
833
834 if ( myNamePtr != NULL )
835 free( myNamePtr );
836
837 if (blockDevice_fd != -1) {
838 close(blockDevice_fd);
839 }
840
841 return;
842
843 } /* getWriteAccess */
844
845
846 static void
847 usage()
848 {
849 (void) fplog(stderr, "usage: %s [-b [size] B [path] c [size] e [mode] ESdfglx m [mode] npqruy] special-device\n", progname);
850 (void) fplog(stderr, " b size = size of physical blocks (in bytes) for -B option\n");
851 (void) fplog(stderr, " B path = file containing physical block numbers to map to paths\n");
852 (void) fplog(stderr, " c size = cache size (ex. 512m, 1g)\n");
853 (void) fplog(stderr, " e mode = emulate 'embedded' or 'desktop'\n");
854 (void) fplog(stderr, " E = exit on first major error\n");
855 (void) fplog(stderr, " d = output debugging info\n");
856 (void) fplog(stderr, " f = force fsck even if clean (preen only) \n");
857 (void) fplog(stderr, " g = GUI output mode\n");
858 (void) fplog(stderr, " x = XML output mode\n");
859 (void) fplog(stderr, " l = live fsck (lock down and test-only)\n");
860 (void) fplog(stderr, " m arg = octal mode used when creating lost+found directory \n");
861 (void) fplog(stderr, " n = assume a no response \n");
862 (void) fplog(stderr, " p = just fix normal inconsistencies \n");
863 (void) fplog(stderr, " q = quick check returns clean, dirty, or failure \n");
864 (void) fplog(stderr, " r = rebuild catalog btree \n");
865 (void) fplog(stderr, " S = Scan disk for bad blocks\n");
866 (void) fplog(stderr, " u = usage \n");
867 (void) fplog(stderr, " y = assume a yes response \n");
868
869 exit(1);
870 }
871
872
873 static void
874 AddBlockToList(long long block)
875 {
876
877 if ((gBlkListEntries % BLOCK_LIST_INCREMENT) == 0) {
878 void *tmp;
879
880 // gBlkListEntries += BLOCK_LIST_INCREMENT;
881 tmp = realloc(gBlockList, (gBlkListEntries + BLOCK_LIST_INCREMENT) * sizeof(u_int64_t));
882 if (tmp == NULL) {
883 pfatal("Can't allocate memory for block list (%llu entries).\n", gBlkListEntries);
884 }
885 gBlockList = (u_int64_t*)tmp;
886 }
887 gBlockList[gBlkListEntries++] = block;
888 return;
889 }
890
891 static int printStatus;
892 static void
893 siginfo(int signo)
894 {
895 printStatus = 1;
896 }
897
898 static void
899 ScanDisk(int fd)
900 {
901 uint32_t devBlockSize = 512;
902 uint64_t devBlockTotal;
903 off_t diskSize;
904 uint8_t *buffer = NULL;
905 size_t bufSize = 1024 * 1024;
906 ssize_t nread;
907 off_t curPos = 0;
908 void (*oldhandler)(int);
909 uint32_t numErrors = 0;
910 uint32_t maxErrors = 40; // Something more variable?
911
912 oldhandler = signal(SIGINFO, &siginfo);
913
914 #define PRSTAT \
915 do { \
916 if (diskSize) { \
917 fprintf(stderr, "Scanning offset %lld of %lld (%d%%)\n", \
918 curPos, diskSize, (int)((curPos * 100) / diskSize)); \
919 } else { \
920 fprintf(stderr, "Scanning offset %lld\n", curPos); \
921 } \
922 printStatus = 0; \
923 } while (0)
924
925 if (ioctl(fd, DKIOCGETBLOCKSIZE, &devBlockSize) == -1) {
926 devBlockSize = 512;
927 }
928
929 if (ioctl(fd, DKIOCGETBLOCKCOUNT, &devBlockTotal) == -1) {
930 diskSize = 0;
931 } else
932 diskSize = devBlockTotal * devBlockSize;
933
934 while (buffer == NULL && bufSize >= devBlockSize) {
935 buffer = malloc(bufSize);
936 if (buffer == NULL) {
937 bufSize /= 2;
938 }
939 }
940 if (buffer == NULL) {
941 pfatal("Cannot allocate buffer for disk scan.\n");
942 }
943
944 loop:
945
946 if (printStatus) {
947 PRSTAT;
948 }
949 while ((nread = pread(fd, buffer, bufSize, curPos)) == bufSize) {
950 curPos += bufSize;
951 if (printStatus) {
952 PRSTAT;
953 }
954 }
955
956 if (nread == 0) {
957 /* We're done with the disk */
958 goto done;
959 }
960 if (nread == -1) {
961 if (errno == EIO) {
962 /* Try reading devBlockSize blocks */
963 size_t total;
964 for (total = 0; total < bufSize; total += devBlockSize) {
965 nread = pread(fd, buffer, devBlockSize, curPos + total);
966 if (nread == -1) {
967 if (errno == EIO) {
968 if (debug)
969 fprintf(stderr, "Bad block at offset %lld\n", curPos + total);
970 AddBlockToList((curPos + total) / gBlockSize);
971 if (++numErrors > maxErrors) {
972 if (debug)
973 fprintf(stderr, "Got %u errors, maxing out so stopping scan\n", numErrors);
974 goto done;
975 }
976 continue;
977 } else {
978 pfatal("Got a non I/O error reading disk at offset %llu: %s\n",
979 curPos + total, strerror(errno));
980 // Hey, pfatal wasn't fatal!
981 // But that seems to work out for us for some reason.
982 }
983 }
984 if (nread == 0) {
985 /* End of disk, somehow. */
986 goto done;
987 }
988 if (nread != devBlockSize) {
989 pwarn("During disk scan, did not get block size (%zd) read, got %zd instead. Skipping rest of this block.\n", (size_t)devBlockSize, nread);
990 continue;
991 }
992 }
993 curPos += total;
994 goto loop;
995 } else if (errno == EINTR) {
996 goto loop;
997 } else {
998 pfatal("Got a non I/O error reading disk at offset %llu: %s\n", curPos, strerror(errno));
999 exit(EEXIT);
1000 }
1001 }
1002 if (nread < bufSize) {
1003 if ((nread % devBlockSize) == 0) {
1004 curPos += nread;
1005 } else {
1006 curPos = curPos + (((nread % devBlockSize) + 1) * devBlockSize);
1007 }
1008 goto loop;
1009 }
1010 goto loop;
1011 done:
1012 if (buffer)
1013 free(buffer);
1014 signal(SIGINFO, oldhandler);
1015 return;
1016
1017 }
1018
1019 static int
1020 getblocklist(const char *filepath)
1021 {
1022 FILE * file;
1023 long long block;
1024 size_t blockListCount; /* Number of elements allocated to gBlockList array */
1025
1026 blockListCount = BLOCK_LIST_INCREMENT;
1027 gBlockList = (u_int64_t *) malloc(blockListCount * sizeof(u_int64_t));
1028 if (gBlockList == NULL)
1029 pfatal("Can't allocate memory for block list.\n");
1030
1031 // printf("getblocklist: processing blocklist %s...\n", filepath);
1032
1033 if ((file = fopen(filepath, "r")) == NULL)
1034 pfatal("Can't open %s\n", filepath);
1035
1036 while (fscanf(file, "%lli", &block) > 0) {
1037 AddBlockToList(block);
1038 }
1039
1040 (void) fclose(file);
1041
1042 printf("%d blocks to match:\n", gBlkListEntries);
1043
1044 return (0);
1045 }