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