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