1 /*
2 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
5
6 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
7 /* All Rights Reserved */
8
9
10 /*
11 * Copyright (c) 1980, 1986, 1990 The Regents of the University of California.
12 * All rights reserved.
13 *
14 * Redistribution and use in source and binary forms are permitted
15 * provided that: (1) source distributions retain this entire copyright
16 * notice and comment, and (2) distributions including binaries display
17 * the following acknowledgement: ``This product includes software
18 * developed by the University of California, Berkeley and its contributors''
19 * in the documentation or other materials provided with the distribution
20 * and in all advertising materials mentioning features or use of this
21 * software. Neither the name of the University nor the names of its
22 * contributors may be used to endorse or promote products derived
23 * from this software without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED '`AS IS'' AND WITHOUT ANY EXPRESS OR
25 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
26 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
27 */
28
29 /*
30 * In-core structures:
31 * blockmap[]
32 * A bitmap of block usage very similar to what's on disk, but
33 * for the entire filesystem rather than just a cylinder group.
34 * Zero indicates free, one indicates allocated. Note that this
35 * is opposite the interpretation of a cylinder group's free block
36 * bitmap.
37 *
38 * statemap[]
39 * Tracks what is known about each inode in the filesystem.
40 * The fundamental state value is one of USTATE, FSTATE, DSTATE,
41 * or SSTATE (unallocated, file, directory, shadow/acl).
42 *
43 * There are optional modifying attributes as well: INZLINK,
44 * INFOUND, INCLEAR, INORPHAN, and INDELAYD. The IN prefix
45 * stands for inode. INZLINK declares that no links (di_nlink ==
46 * 0) to the inode have been found. It is used instead of
47 * examining di_nlink because we've always got the statemap[] in
48 * memory, and on average the odds are against having any given
49 * inode in the cache. INFOUND flags that an inode was
50 * encountered during the descent of the filesystem. In other
51 * words, it's reachable, either by name or by being an acl or
52 * attribute. INCLEAR declares an intent to call clri() on an
53 * inode. The INCLEAR and INZLINK attributes are treated in a
54 * mutually exclusive manner with INCLEAR taking higher precedence
55 * as the intent is to clear the inode.
56 *
57 * INORPHAN indicates that the inode has already been seen once
58 * in pass3 and determined to be an orphan, so any additional
59 * encounters don't need to waste cycles redetermining that status.
60 * It also means we don't ask the user about doing something to the
61 * inode N times.
62 *
63 * INDELAYD marks inodes that pass1 determined needed to be truncated.
64 * They can't be truncated during that pass, because it depends on
65 * having a stable world for building the block and inode tables from.
66 *
67 * The IN flags rarely used directly, but instead are
68 * pre-combined through the {D,F,S}ZLINK, DFOUND, and
69 * {D,F,S}CLEAR convenience macros. This mainly matters when
70 * trying to use grep on the source.
71 *
72 * Three state-test macros are provided: S_IS_DUNFOUND(),
73 * S_IS_DVALID(), and S_IS_ZLINK(). The first is true when an
74 * inode's state indicates that it is either a simple directory
75 * (DSTATE without the INFOUND or INCLEAR modifiers) or a
76 * directory with the INZLINK modifier set. By definition, if a
77 * directory has zero links, then it can't be found. As for
78 * S_IS_DVALID(), it decides if a directory inode is alive.
79 * Effectively, this translates to whether or not it's been
80 * flagged for clearing. If not, then it's valid for current
81 * purposes. This is true even if INZLINK is set, as we may find
82 * a reference to it later. Finally, S_IS_ZLINK() just picks out
83 * the INZLINK flag from the state.
84 *
85 * The S_*() macros all work on a state value. To simplify a
86 * bit, the INO_IS_{DUNFOUND,DVALID}() macros take an inode
87 * number argument. The inode is looked up in the statemap[] and
88 * the result handed off to the corresponding S_*() macro. This
89 * is partly a holdover from working with different data
90 * structures (with the same net intent) in the BSD fsck.
91 *
92 * lncntp
93 * Each entry is initialized to the di_link from the on-disk
94 * inode. Each time we find one of those links, we decrement it.
95 * Once all the traversing is done, we should have a zero. If we
96 * have a positive value, then some reference disappeared
97 * (probably from a directory that got nuked); deal with it by
98 * fixing the count. If we have a negative value, then we found
99 * an extra reference. This is a can't-happen, except in the
100 * special case of when we reconnect a directory to its parent or
101 * to lost+found. An exact match between lncntp[] and the on-disk
102 * inode means it's completely unreferenced.
103 *
104 * aclphead
105 * This is a hash table of the acl inodes in the filesystem.
106 *
107 * aclpsort
108 * The same acls as in aclphead, but as a simple linear array.
109 * It is used to hold the acl pointers for sorting and scanning
110 * in pass3b.
111 */
112
113 #include <stdio.h>
114 #include <stdlib.h>
115 #include <unistd.h>
116 #include <sys/types.h>
117 #include <sys/param.h>
118 #include <sys/int_types.h>
119 #include <sys/mntent.h>
120 #include <sys/fs/ufs_fs.h>
121 #include <sys/vnode.h>
122 #include <sys/fs/ufs_inode.h>
123 #include <sys/stat.h>
124 #include <fcntl.h>
125 #include <sys/wait.h>
126 #include <sys/mnttab.h>
127 #include <signal.h>
128 #include <string.h>
129 #include <sys/vfstab.h>
130 #include <sys/statvfs.h>
131 #include <sys/filio.h>
132 #include <ustat.h>
133 #include <errno.h>
134 #include "fsck.h"
135
136 static void usage(void);
137 static long argtol(int, char *, char *, int);
138 static void checkfilesys(char *);
139 static void check_sanity(char *);
140 static void report_limbo(const void *, VISIT, int);
141
142 #define QUICK_CHECK 'm' /* are things ok according to superblock? */
143 #define ALL_no 'n' /* auto-answer interactive questions `no' */
144 #define ALL_NO 'N' /* auto-answer interactive questions `no' */
145 #define UFS_OPTS 'o' /* ufs-specific options, see subopts[] */
146 #define ECHO_CMD 'V' /* echo the command line */
147 #define ALL_yes 'y' /* auto-answer interactive questions `yes' */
148 #define ALL_YES 'Y' /* auto-answer interactive questions `yes' */
149 #define VERBOSE 'v' /* be chatty */
150
151 static char *subopts[] = {
152 #define PREEN 0 /* non-interactive mode (parent is parallel) */
153 "p",
154 #define BLOCK 1 /* alternate superblock */
155 "b",
156 #define DEBUG 2 /* yammer */
157 "d",
158 #define ONLY_WRITES 3 /* check all writable filesystems */
159 "w",
160 #define FORCE 4 /* force checking, even if clean */
161 "f",
162 NULL
163 };
164
165 /*
166 * Filesystems that are `magical' - if they exist in vfstab,
167 * then they have to be mounted for the system to have gotten
168 * far enough to be able to run fsck. Thus, don't get all
169 * bent out of shape if we're asked to check it and it is mounted.
170 */
171 char *magic_fs[] = {
172 "", /* MAGIC_NONE, for normal filesystems */
173 "/", /* MAGIC_ROOT */
174 "/usr", /* MAGIC_USR */
175 NULL /* MAGIC_LIMIT */
176 };
177
178 daddr32_t bflag;
179 daddr32_t n_blks;
180 daddr32_t maxfsblock;
181 int debug;
182 int errorlocked;
183 int exitstat;
184 int fflag;
185 int fsmodified;
186 int fswritefd;
187 int iscorrupt;
188 int islog;
189 int islogok;
190 int interrupted;
191 int mflag;
192 int mountfd;
193 int overflowed_lf;
194 int rflag;
195 int reattached_dir;
196 int broke_dir_link;
197 int verbose;
198 char hotroot;
199 char mountedfs;
200 char nflag;
201 char preen;
202 char rerun;
203 char *blockmap;
204 char *devname;
205 char yflag;
206 short *lncntp;
207 ushort_t *statemap;
208 fsck_ino_t maxino;
209 fsck_ino_t countdirs;
210 fsck_ino_t n_files;
211 void *limbo_dirs;
212
213 int
main(int argc,char * argv[])214 main(int argc, char *argv[])
215 {
216 int c;
217 int wflag = 0;
218 char *suboptions, *value;
219 struct rlimit rlimit;
220 extern int optind;
221 extern char *optarg;
222
223 while ((c = getopt(argc, argv, "mnNo:VvyY")) != EOF) {
224 switch (c) {
225
226 case QUICK_CHECK:
227 mflag++;
228 break;
229
230 case ALL_no:
231 case ALL_NO:
232 nflag++;
233 yflag = 0;
234 break;
235
236 case VERBOSE:
237 verbose++;
238 break;
239
240 case UFS_OPTS:
241 /*
242 * ufs specific options.
243 */
244 if (optarg == NULL) {
245 usage();
246 /*
247 * lint does not believe this, nor does it
248 * believe #pragma does_not_return(usage)
249 */
250 /* NOTREACHED */
251 }
252 suboptions = optarg;
253 while (*suboptions != '\0') {
254 switch (getsubopt(&suboptions, subopts,
255 &value)) {
256
257 case PREEN:
258 preen++;
259 break;
260
261 case BLOCK:
262 bflag = argtol(BLOCK, "block",
263 value, 10);
264 (void) printf("Alternate super block "
265 "location: %ld.\n",
266 (long)bflag);
267 break;
268
269 case DEBUG:
270 debug++;
271 verbose++;
272 break;
273
274 case ONLY_WRITES:
275 /* check only writable filesystems */
276 wflag++;
277 break;
278
279 case FORCE:
280 fflag++;
281 break;
282
283 default:
284 usage();
285 }
286 }
287 break;
288
289 case ECHO_CMD:
290 {
291 int opt_count;
292 char *opt_text;
293
294 (void) printf("fsck -F ufs ");
295 for (opt_count = 1; opt_count < argc;
296 opt_count++) {
297 opt_text = argv[opt_count];
298 if (opt_text)
299 (void) printf("%s ", opt_text);
300 }
301 (void) printf("\n");
302 }
303 break;
304
305 case ALL_yes:
306 case ALL_YES:
307 yflag++;
308 nflag = 0;
309 break;
310
311 default:
312 usage();
313 }
314 }
315 argc -= optind;
316 argv += optind;
317
318 if (argc == 0)
319 usage();
320
321 rflag++; /* check raw devices where we can */
322 if (signal(SIGINT, SIG_IGN) != SIG_IGN)
323 (void) signal(SIGINT, catch);
324 if (preen)
325 (void) signal(SIGQUIT, catchquit);
326
327 /*
328 * Push up our allowed memory limit so we can cope
329 * with huge file systems.
330 */
331 if (getrlimit(RLIMIT_DATA, &rlimit) == 0) {
332 rlimit.rlim_cur = rlimit.rlim_max;
333 (void) setrlimit(RLIMIT_DATA, &rlimit);
334 }
335
336 /*
337 * There are a lot of places where we just exit if a problem is
338 * found. This means that we won't necessarily check everything
339 * we were asked to. It would be nice to do everything, and
340 * then provide a summary when we're done. However, the
341 * interface doesn't really allow us to do that in any useful
342 * way. So, we'll just bail on the first unrecoverable
343 * problem encountered. If we've been run by the generic
344 * wrapper, we were only given one filesystem to check, so the
345 * multi-fs case implies being run manually; that means the
346 * user can rerun us on the remaining filesystems when it's
347 * convenient for them.
348 */
349 while (argc-- > 0) {
350 if (wflag && !writable(*argv)) {
351 (void) fprintf(stderr, "not writeable '%s'\n", *argv);
352 argv++;
353 if (exitstat == 0)
354 exitstat = EXBADPARM;
355 } else {
356 checkfilesys(*argv++);
357 }
358 }
359 if (interrupted)
360 exitstat = EXSIGNAL;
361 exit(exitstat);
362 }
363
364 /*
365 * A relatively intelligent strtol(). Note that if str is NULL, we'll
366 * exit, so ret does not actually need to be pre-initialized. Lint
367 * doesn't believe this, and it's harmless enough to make lint happy here.
368 */
369 static long
argtol(int flag,char * req,char * str,int base)370 argtol(int flag, char *req, char *str, int base)
371 {
372 char *cp = str;
373 long ret = -1;
374
375 errno = 0;
376 if (str != NULL)
377 ret = strtol(str, &cp, base);
378 if (cp == str || *cp) {
379 (void) fprintf(stderr, "-%c flag requires a %s\n", flag, req);
380 exit(EXBADPARM);
381 }
382 if (errno != 0) {
383 (void) fprintf(stderr, "-%c %s value out of range\n",
384 flag, req);
385 }
386
387 return (ret);
388 }
389
390 /*
391 * Check the specified file system.
392 */
393 static void
checkfilesys(char * filesys)394 checkfilesys(char *filesys)
395 {
396 daddr32_t n_ffree, n_bfree;
397 char *devstr;
398 fsck_ino_t files;
399 daddr32_t blks;
400 fsck_ino_t inumber;
401 int zlinks_printed;
402 fsck_ino_t limbo_victim;
403 double dbl_nffree, dbl_dsize;
404 int quiet_dups;
405
406 mountfd = -1;
407 hotroot = 0;
408 mountedfs = M_NOMNT;
409 reattached_dir = 0;
410 broke_dir_link = 0;
411 iscorrupt = 1; /* assume failure in setup() */
412 islog = 0;
413 islogok = 0;
414 overflowed_lf = 0;
415 errorlocked = is_errorlocked(filesys);
416 limbo_dirs = NULL;
417
418 if ((devstr = setup(filesys)) == NULL) {
419 if (!iscorrupt) {
420 return;
421 }
422
423 if (preen)
424 pfatal("CAN'T CHECK FILE SYSTEM.");
425 if (exitstat == 0)
426 exitstat = mflag ? EXUMNTCHK : EXERRFATAL;
427 exit(exitstat);
428 } else {
429 devname = devstr;
430 }
431
432 if (mflag) {
433 check_sanity(filesys);
434 /* NOTREACHED */
435 }
436
437 if (debug)
438 printclean();
439
440 iscorrupt = 0; /* setup() succeeded, assume good filesystem */
441
442 /*
443 * 1: scan inodes tallying blocks used
444 */
445 if (!preen) {
446 /* hotroot is reported as such in setup() if debug is on */
447 if (mountedfs != M_NOMNT)
448 (void) printf("** Currently Mounted on %s\n",
449 sblock.fs_fsmnt);
450 else
451 (void) printf("** Last Mounted on %s\n",
452 sblock.fs_fsmnt);
453 (void) printf("** Phase 1 - Check Blocks and Sizes\n");
454 }
455 pass1();
456
457 /*
458 * 1b: locate first references to duplicates, if any
459 */
460 if (have_dups()) {
461 if (preen)
462 pfatal("INTERNAL ERROR: dups with -o p");
463 (void) printf("** Phase 1b - Rescan For More DUPS\n");
464 pass1b();
465 }
466
467 /*
468 * 2: traverse directories from root to mark all connected directories
469 */
470 if (!preen)
471 (void) printf("** Phase 2 - Check Pathnames\n");
472 pass2();
473
474 /*
475 * 3a: scan inodes looking for disconnected directories.
476 */
477 if (!preen)
478 (void) printf("** Phase 3a - Check Connectivity\n");
479 pass3a();
480
481 /*
482 * 3b: check acls
483 */
484 if (!preen)
485 (void) printf("** Phase 3b - Verify Shadows/ACLs\n");
486 pass3b();
487
488 /*
489 * 4: scan inodes looking for disconnected files; check reference counts
490 */
491 if (!preen)
492 (void) printf("** Phase 4 - Check Reference Counts\n");
493 pass4();
494
495 /*
496 * 5: check and repair resource counts in cylinder groups
497 */
498 if (!preen)
499 (void) printf("** Phase 5 - Check Cylinder Groups\n");
500 recount:
501 pass5();
502
503 if (overflowed_lf) {
504 iscorrupt = 1;
505 }
506
507 if (!nflag && mountedfs == M_RW) {
508 (void) printf("FILESYSTEM MAY STILL BE INCONSISTENT.\n");
509 rerun = 1;
510 }
511
512 if (have_dups()) {
513 quiet_dups = (reply("LIST REMAINING DUPS") == 0);
514 if (report_dups(quiet_dups) > 0)
515 iscorrupt = 1;
516
517 (void) printf("WARNING: DATA LOSS MAY HAVE OCCURRED DUE TO "
518 "DUP BLOCKS.\nVERIFY FILE CONTENTS BEFORE USING.\n");
519 }
520
521 if (limbo_dirs != NULL) {
522 /*
523 * Don't force iscorrupt, as this is sufficiently
524 * harmless that the filesystem can be mounted and
525 * used. We just leak some inodes and/or blocks.
526 */
527 pwarn("Orphan directories not cleared or reconnected:\n");
528
529 twalk(limbo_dirs, report_limbo);
530
531 while (limbo_dirs != NULL) {
532 limbo_victim = *(fsck_ino_t *)limbo_dirs;
533 if (limbo_victim != 0) {
534 (void) tdelete((void *)limbo_victim,
535 &limbo_dirs,
536 ino_t_cmp);
537 }
538 }
539
540 rerun = 1;
541 }
542
543 if (iscorrupt) {
544 if (mountedfs == M_RW)
545 (void) printf("FS IS MOUNTED R/W AND"
546 " FSCK DID ITS BEST TO FIX"
547 " INCONSISTENCIES.\n");
548 else
549 (void) printf("FILESYSTEM MAY STILL BE"
550 " INCONSISTENT.\n");
551 rerun = 1;
552 }
553
554 /*
555 * iscorrupt must be stable at this point.
556 * updateclean() returns true when it had to discard the log.
557 * This can only happen once, since sblock.fs_logbno gets
558 * cleared as part of that operation.
559 */
560 if (updateclean()) {
561 if (!preen)
562 (void) printf(
563 "Log was discarded, updating cyl groups\n");
564 goto recount;
565 }
566
567 if (debug)
568 printclean();
569
570 ckfini();
571
572 /*
573 * print out summary statistics
574 */
575 n_ffree = sblock.fs_cstotal.cs_nffree;
576 n_bfree = sblock.fs_cstotal.cs_nbfree;
577 files = maxino - UFSROOTINO - sblock.fs_cstotal.cs_nifree - n_files;
578 blks = n_blks +
579 sblock.fs_ncg * (cgdmin(&sblock, 0) - cgsblock(&sblock, 0));
580 blks += cgsblock(&sblock, 0) - cgbase(&sblock, 0);
581 blks += howmany(sblock.fs_cssize, sblock.fs_fsize);
582 blks = maxfsblock - (n_ffree + sblock.fs_frag * n_bfree) - blks;
583 if (debug && (files > 0 || blks > 0)) {
584 countdirs = sblock.fs_cstotal.cs_ndir - countdirs;
585 pwarn("Reclaimed: %d directories, %d files, %lld fragments\n",
586 countdirs, files - countdirs,
587 (longlong_t)blks);
588 }
589
590 dbl_nffree = (double)n_ffree;
591 dbl_dsize = (double)sblock.fs_dsize;
592
593 if (!verbose) {
594 /*
595 * Done as one big string to try for a single write,
596 * so the output doesn't get interleaved with other
597 * preening fscks.
598 */
599 pwarn("%ld files, %lld used, %lld free "
600 "(%lld frags, %lld blocks, %.1f%% fragmentation)\n",
601 (long)n_files, (longlong_t)n_blks,
602 (longlong_t)n_ffree + sblock.fs_frag * n_bfree,
603 (longlong_t)n_ffree, (longlong_t)n_bfree,
604 (dbl_nffree * 100.0) / dbl_dsize);
605 } else {
606 pwarn("\nFilesystem summary:\n");
607 pwarn("Inodes in use: %ld\n", (long)n_files);
608 pwarn("Blocks in use: %lld\n", (longlong_t)n_blks);
609 pwarn("Total free fragments: %lld\n",
610 (longlong_t)n_ffree + sblock.fs_frag * n_bfree);
611 pwarn("Free fragments not in blocks: %lld\n",
612 (longlong_t)n_ffree);
613 pwarn("Total free blocks: %lld\n", (longlong_t)n_bfree);
614 pwarn("Fragment/block fragmentation: %.1f%%\n",
615 (dbl_nffree * 100.0) / dbl_dsize);
616 pwarn("");
617
618 if (files < 0)
619 pwarn("%d inodes missing\n", -files);
620 if (blks < 0)
621 pwarn("%lld blocks missing\n", -(longlong_t)blks);
622
623 zlinks_printed = 0;
624 for (inumber = UFSROOTINO; inumber < maxino; inumber++) {
625 if (S_IS_ZLINK(statemap[inumber])) {
626 if (zlinks_printed == 0) {
627 pwarn("The following zero "
628 "link count inodes remain:");
629 }
630 if (zlinks_printed) {
631 if ((zlinks_printed % 9) == 0)
632 (void) puts(",\n");
633 else
634 (void) puts(", ");
635 }
636 (void) printf("%u", inumber);
637 zlinks_printed++;
638 }
639 }
640 if ((zlinks_printed != 0) && ((zlinks_printed % 9) != 0))
641 (void) putchar('\n');
642 }
643
644 /*
645 * Clean up after ourselves, so we can do the next filesystem.
646 */
647 free_dup_state();
648 inocleanup();
649 free(blockmap);
650 free(statemap);
651 free((void *)lncntp);
652 lncntp = NULL;
653 blockmap = NULL;
654 statemap = NULL;
655 if (iscorrupt && exitstat == 0)
656 exitstat = EXFNDERRS;
657 if (fsmodified)
658 (void) printf("\n***** FILE SYSTEM WAS MODIFIED *****\n");
659 if (overflowed_lf)
660 (void) printf("\n***** %s FULL, MUST REMOVE ENTRIES *****\n",
661 lfname);
662 if (reattached_dir) {
663 (void) printf("ORPHANED DIRECTORIES REATTACHED; DIR LINK "
664 "COUNTS MAY NOT BE CORRECT.\n");
665 rerun = 1;
666 }
667 if (broke_dir_link) {
668 (void) printf(
669 "DIRECTORY HARDLINK BROKEN; LOOPS MAY STILL EXIST.\n");
670 rerun = 1;
671 }
672 if (iscorrupt)
673 (void) printf("***** FILE SYSTEM IS BAD *****\n");
674
675 if (rerun) {
676 if (mountedfs == M_RW)
677 (void) printf("\n***** PLEASE RERUN FSCK ON UNMOUNTED"
678 " FILE SYSTEM *****\n");
679 else
680 (void) printf("\n***** PLEASE RERUN FSCK *****\n");
681 }
682
683 if ((exitstat == 0) &&
684 (((mountedfs != M_NOMNT) && !errorlocked) || hotroot)) {
685 exitstat = EXROOTOKAY;
686 }
687
688 if ((exitstat == 0) && rerun)
689 exitstat = EXFNDERRS;
690
691 if (mountedfs != M_NOMNT) {
692 if (!fsmodified)
693 return;
694 /*
695 * _FIOFFS is much more effective than a simple sync().
696 * Note that the original fswritefd was discarded in
697 * ckfini().
698 */
699 fswritefd = open(devstr, O_RDWR, 0);
700 if (fswritefd != -1) {
701 (void) ioctl(fswritefd, _FIOFFS, NULL);
702 (void) close(fswritefd);
703 }
704
705 if (!preen)
706 (void) printf("\n***** REBOOT NOW *****\n");
707
708 exitstat = EXREBOOTNOW;
709 }
710 }
711
712 /*
713 * fsck -m: does the filesystem pass cursory examination
714 *
715 * XXX This is very redundant with setup(). The right thing would be
716 * for setup() to modify its behaviour when mflag is set (less
717 * chatty, exit instead of return, etc).
718 */
719 void
check_sanity(char * filename)720 check_sanity(char *filename)
721 {
722 struct stat64 stbd, stbr;
723 char *devname;
724 struct ustat usb;
725 char vfsfilename[MAXPATHLEN];
726 struct vfstab vfsbuf;
727 FILE *vfstab;
728 struct statvfs vfs_stat;
729 int found_magic[MAGIC_LIMIT];
730 int magic_cnt;
731 int is_magic = 0;
732 int is_block = 0;
733 int is_file = 0;
734
735 (void) memset((void *)found_magic, 0, sizeof (found_magic));
736
737 if (stat64(filename, &stbd) < 0) {
738 (void) fprintf(stderr,
739 "ufs fsck: sanity check failed : cannot stat %s\n", filename);
740 exit(EXNOSTAT);
741 }
742
743 if (S_ISBLK(stbd.st_mode)) {
744 is_block = 1;
745 } else if (S_ISCHR(stbd.st_mode)) {
746 is_block = 0;
747 } else if (S_ISREG(stbd.st_mode)) {
748 is_file = 1;
749 }
750
751 /*
752 * Determine if this is the root file system via vfstab. Give up
753 * silently on failures. The whole point of this is to be tolerant
754 * of the magic file systems being already mounted.
755 */
756 if (!is_file && (vfstab = fopen(VFSTAB, "r")) != NULL) {
757 for (magic_cnt = 0; magic_cnt < MAGIC_LIMIT; magic_cnt++) {
758 if (magic_cnt == MAGIC_NONE)
759 continue;
760 if (getvfsfile(vfstab, &vfsbuf,
761 magic_fs[magic_cnt]) == 0) {
762 if (is_block)
763 devname = vfsbuf.vfs_special;
764 else
765 devname = vfsbuf.vfs_fsckdev;
766 if (stat64(devname, &stbr) == 0) {
767 if (stbr.st_rdev == stbd.st_rdev) {
768 found_magic[magic_cnt] = 1;
769 is_magic = magic_cnt;
770 break;
771 }
772 }
773 }
774 }
775 }
776
777 /*
778 * Only works if filename is a block device or if
779 * character and block device has the same dev_t value.
780 * This is currently true, but nothing really forces it.
781 */
782 if (!is_magic && (ustat(stbd.st_rdev, &usb) == 0)) {
783 (void) fprintf(stderr,
784 "ufs fsck: sanity check: %s already mounted\n", filename);
785 exit(EXMOUNTED);
786 }
787
788 if (is_magic) {
789 (void) strcpy(vfsfilename, magic_fs[is_magic]);
790 if (statvfs(vfsfilename, &vfs_stat) != 0) {
791 (void) fprintf(stderr, "ufs fsck: Cannot stat %s\n",
792 vfsfilename);
793 exit(EXNOSTAT);
794 }
795
796 if (!(vfs_stat.f_flag & ST_RDONLY)) {
797 /*
798 * The file system is mounted read/write
799 * We need to exit saying this. If it's only
800 * mounted readonly, we can continue.
801 */
802
803 (void) fprintf(stderr,
804 "ufs fsck: sanity check:"
805 "%s already mounted read/write\n", filename);
806 exit(EXMOUNTED);
807 }
808 }
809
810 /*
811 * We know that at boot, the ufs root file system is mounted
812 * read-only first. After fsck runs, it is remounted as
813 * read-write. Therefore, we do not need to check for different
814 * values for fs_state between the root file system and the
815 * rest of the file systems.
816 */
817 if (islog && !islogok) {
818 (void) fprintf(stderr,
819 "ufs fsck: sanity check: %s needs checking\n", filename);
820 exit(EXUMNTCHK);
821 }
822 if ((sblock.fs_state + (long)sblock.fs_time == FSOKAY) &&
823 (sblock.fs_clean == FSCLEAN || sblock.fs_clean == FSSTABLE ||
824 (sblock.fs_clean == FSLOG && islog))) {
825 (void) fprintf(stderr,
826 "ufs fsck: sanity check: %s okay\n", filename);
827 } else {
828 (void) fprintf(stderr,
829 "ufs fsck: sanity check: %s needs checking\n", filename);
830 exit(EXUMNTCHK);
831 }
832 exit(EXOKAY);
833 }
834
835 caddr_t
hasvfsopt(struct vfstab * vfs,char * opt)836 hasvfsopt(struct vfstab *vfs, char *opt)
837 {
838 struct mnttab mtab;
839
840 if (vfs->vfs_mntopts == NULL)
841 return (NULL);
842 mtab.mnt_mntopts = vfs->vfs_mntopts;
843 return (hasmntopt(&mtab, opt));
844 }
845
846 void
usage(void)847 usage(void)
848 {
849 (void) fprintf(stderr,
850 "ufs usage: fsck [-F ufs] [-m] [-n] [-V] [-v] [-y] "
851 "[-o p,b=#,w,f] [special ....]\n");
852
853 exit(EXBADPARM);
854 }
855
856 /*ARGSUSED*/
857 static void
report_limbo(const void * node,VISIT order,int level)858 report_limbo(const void *node, VISIT order, int level)
859 {
860 fsck_ino_t ino = *(fsck_ino_t *)node;
861
862 if ((order == postorder) || (order == leaf)) {
863 (void) printf(" Inode %d\n", ino);
864 }
865 }
866