10Sstevel@tonic-gate /*
2*8895SMilan.Cermak@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
30Sstevel@tonic-gate * Use is subject to license terms.
40Sstevel@tonic-gate */
50Sstevel@tonic-gate
60Sstevel@tonic-gate /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
70Sstevel@tonic-gate /* All Rights Reserved */
80Sstevel@tonic-gate
90Sstevel@tonic-gate
100Sstevel@tonic-gate /*
110Sstevel@tonic-gate * Copyright (c) 1980, 1986, 1990 The Regents of the University of California.
120Sstevel@tonic-gate * All rights reserved.
130Sstevel@tonic-gate *
140Sstevel@tonic-gate * Redistribution and use in source and binary forms are permitted
150Sstevel@tonic-gate * provided that: (1) source distributions retain this entire copyright
160Sstevel@tonic-gate * notice and comment, and (2) distributions including binaries display
170Sstevel@tonic-gate * the following acknowledgement: ``This product includes software
180Sstevel@tonic-gate * developed by the University of California, Berkeley and its contributors''
190Sstevel@tonic-gate * in the documentation or other materials provided with the distribution
200Sstevel@tonic-gate * and in all advertising materials mentioning features or use of this
210Sstevel@tonic-gate * software. Neither the name of the University nor the names of its
220Sstevel@tonic-gate * contributors may be used to endorse or promote products derived
230Sstevel@tonic-gate * from this software without specific prior written permission.
24392Sswilcox * THIS SOFTWARE IS PROVIDED '`AS IS'' AND WITHOUT ANY EXPRESS OR
250Sstevel@tonic-gate * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
260Sstevel@tonic-gate * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
270Sstevel@tonic-gate */
280Sstevel@tonic-gate
29392Sswilcox /*
30392Sswilcox * In-core structures:
31392Sswilcox * blockmap[]
32392Sswilcox * A bitmap of block usage very similar to what's on disk, but
33392Sswilcox * for the entire filesystem rather than just a cylinder group.
34392Sswilcox * Zero indicates free, one indicates allocated. Note that this
35392Sswilcox * is opposite the interpretation of a cylinder group's free block
36392Sswilcox * bitmap.
37392Sswilcox *
38392Sswilcox * statemap[]
39392Sswilcox * Tracks what is known about each inode in the filesystem.
40392Sswilcox * The fundamental state value is one of USTATE, FSTATE, DSTATE,
41392Sswilcox * or SSTATE (unallocated, file, directory, shadow/acl).
42392Sswilcox *
43392Sswilcox * There are optional modifying attributes as well: INZLINK,
44392Sswilcox * INFOUND, INCLEAR, INORPHAN, and INDELAYD. The IN prefix
45392Sswilcox * stands for inode. INZLINK declares that no links (di_nlink ==
46392Sswilcox * 0) to the inode have been found. It is used instead of
47392Sswilcox * examining di_nlink because we've always got the statemap[] in
48392Sswilcox * memory, and on average the odds are against having any given
49392Sswilcox * inode in the cache. INFOUND flags that an inode was
50392Sswilcox * encountered during the descent of the filesystem. In other
51392Sswilcox * words, it's reachable, either by name or by being an acl or
52392Sswilcox * attribute. INCLEAR declares an intent to call clri() on an
536106Sowenr * inode. The INCLEAR and INZLINK attributes are treated in a
546106Sowenr * mutually exclusive manner with INCLEAR taking higher precedence
556106Sowenr * as the intent is to clear the inode.
56392Sswilcox *
57392Sswilcox * INORPHAN indicates that the inode has already been seen once
58392Sswilcox * in pass3 and determined to be an orphan, so any additional
59392Sswilcox * encounters don't need to waste cycles redetermining that status.
60392Sswilcox * It also means we don't ask the user about doing something to the
61392Sswilcox * inode N times.
62392Sswilcox *
63392Sswilcox * INDELAYD marks inodes that pass1 determined needed to be truncated.
64392Sswilcox * They can't be truncated during that pass, because it depends on
65392Sswilcox * having a stable world for building the block and inode tables from.
66392Sswilcox *
67392Sswilcox * The IN flags rarely used directly, but instead are
68392Sswilcox * pre-combined through the {D,F,S}ZLINK, DFOUND, and
69392Sswilcox * {D,F,S}CLEAR convenience macros. This mainly matters when
70392Sswilcox * trying to use grep on the source.
71392Sswilcox *
72392Sswilcox * Three state-test macros are provided: S_IS_DUNFOUND(),
73392Sswilcox * S_IS_DVALID(), and S_IS_ZLINK(). The first is true when an
74392Sswilcox * inode's state indicates that it is either a simple directory
75392Sswilcox * (DSTATE without the INFOUND or INCLEAR modifiers) or a
76392Sswilcox * directory with the INZLINK modifier set. By definition, if a
77392Sswilcox * directory has zero links, then it can't be found. As for
78392Sswilcox * S_IS_DVALID(), it decides if a directory inode is alive.
79392Sswilcox * Effectively, this translates to whether or not it's been
80392Sswilcox * flagged for clearing. If not, then it's valid for current
81392Sswilcox * purposes. This is true even if INZLINK is set, as we may find
82392Sswilcox * a reference to it later. Finally, S_IS_ZLINK() just picks out
83392Sswilcox * the INZLINK flag from the state.
84392Sswilcox *
85392Sswilcox * The S_*() macros all work on a state value. To simplify a
86392Sswilcox * bit, the INO_IS_{DUNFOUND,DVALID}() macros take an inode
87392Sswilcox * number argument. The inode is looked up in the statemap[] and
88392Sswilcox * the result handed off to the corresponding S_*() macro. This
89392Sswilcox * is partly a holdover from working with different data
90392Sswilcox * structures (with the same net intent) in the BSD fsck.
91392Sswilcox *
92392Sswilcox * lncntp
93392Sswilcox * Each entry is initialized to the di_link from the on-disk
94392Sswilcox * inode. Each time we find one of those links, we decrement it.
95392Sswilcox * Once all the traversing is done, we should have a zero. If we
96392Sswilcox * have a positive value, then some reference disappeared
97392Sswilcox * (probably from a directory that got nuked); deal with it by
98392Sswilcox * fixing the count. If we have a negative value, then we found
99392Sswilcox * an extra reference. This is a can't-happen, except in the
100392Sswilcox * special case of when we reconnect a directory to its parent or
101392Sswilcox * to lost+found. An exact match between lncntp[] and the on-disk
102392Sswilcox * inode means it's completely unreferenced.
103392Sswilcox *
104392Sswilcox * aclphead
105392Sswilcox * This is a hash table of the acl inodes in the filesystem.
106392Sswilcox *
107392Sswilcox * aclpsort
108392Sswilcox * The same acls as in aclphead, but as a simple linear array.
109392Sswilcox * It is used to hold the acl pointers for sorting and scanning
110392Sswilcox * in pass3b.
111392Sswilcox */
1120Sstevel@tonic-gate
113392Sswilcox #include <stdio.h>
114392Sswilcox #include <stdlib.h>
115392Sswilcox #include <unistd.h>
116392Sswilcox #include <sys/types.h>
117392Sswilcox #include <sys/param.h>
118392Sswilcox #include <sys/int_types.h>
119392Sswilcox #include <sys/mntent.h>
1200Sstevel@tonic-gate #include <sys/fs/ufs_fs.h>
1210Sstevel@tonic-gate #include <sys/vnode.h>
1220Sstevel@tonic-gate #include <sys/fs/ufs_inode.h>
1230Sstevel@tonic-gate #include <sys/stat.h>
124392Sswilcox #include <fcntl.h>
1250Sstevel@tonic-gate #include <sys/wait.h>
1260Sstevel@tonic-gate #include <sys/mnttab.h>
127392Sswilcox #include <signal.h>
1280Sstevel@tonic-gate #include <string.h>
1290Sstevel@tonic-gate #include <sys/vfstab.h>
1300Sstevel@tonic-gate #include <sys/statvfs.h>
131392Sswilcox #include <sys/filio.h>
132392Sswilcox #include <ustat.h>
1330Sstevel@tonic-gate #include <errno.h>
134392Sswilcox #include "fsck.h"
1350Sstevel@tonic-gate
136392Sswilcox static void usage(void);
137392Sswilcox static long argtol(int, char *, char *, int);
138392Sswilcox static void checkfilesys(char *);
139392Sswilcox static void check_sanity(char *);
140392Sswilcox static void report_limbo(const void *, VISIT, int);
1410Sstevel@tonic-gate
142392Sswilcox #define QUICK_CHECK 'm' /* are things ok according to superblock? */
143392Sswilcox #define ALL_no 'n' /* auto-answer interactive questions `no' */
144392Sswilcox #define ALL_NO 'N' /* auto-answer interactive questions `no' */
145392Sswilcox #define UFS_OPTS 'o' /* ufs-specific options, see subopts[] */
146392Sswilcox #define ECHO_CMD 'V' /* echo the command line */
147392Sswilcox #define ALL_yes 'y' /* auto-answer interactive questions `yes' */
148392Sswilcox #define ALL_YES 'Y' /* auto-answer interactive questions `yes' */
149392Sswilcox #define VERBOSE 'v' /* be chatty */
1500Sstevel@tonic-gate
151392Sswilcox static char *subopts[] = {
152392Sswilcox #define PREEN 0 /* non-interactive mode (parent is parallel) */
1530Sstevel@tonic-gate "p",
154392Sswilcox #define BLOCK 1 /* alternate superblock */
1550Sstevel@tonic-gate "b",
156392Sswilcox #define DEBUG 2 /* yammer */
1570Sstevel@tonic-gate "d",
158392Sswilcox #define ONLY_WRITES 3 /* check all writable filesystems */
1590Sstevel@tonic-gate "w",
160392Sswilcox #define FORCE 4 /* force checking, even if clean */
1610Sstevel@tonic-gate "f",
1620Sstevel@tonic-gate NULL
1630Sstevel@tonic-gate };
1640Sstevel@tonic-gate
165392Sswilcox /*
166392Sswilcox * Filesystems that are `magical' - if they exist in vfstab,
167392Sswilcox * then they have to be mounted for the system to have gotten
168392Sswilcox * far enough to be able to run fsck. Thus, don't get all
169392Sswilcox * bent out of shape if we're asked to check it and it is mounted.
170392Sswilcox */
171392Sswilcox char *magic_fs[] = {
172392Sswilcox "", /* MAGIC_NONE, for normal filesystems */
173392Sswilcox "/", /* MAGIC_ROOT */
174392Sswilcox "/usr", /* MAGIC_USR */
175392Sswilcox NULL /* MAGIC_LIMIT */
176392Sswilcox };
177392Sswilcox
1781051Smaheshvs int
main(int argc,char * argv[])179392Sswilcox main(int argc, char *argv[])
1800Sstevel@tonic-gate {
181392Sswilcox int c;
182392Sswilcox int wflag = 0;
183392Sswilcox char *suboptions, *value;
184392Sswilcox struct rlimit rlimit;
185392Sswilcox extern int optind;
186392Sswilcox extern char *optarg;
1870Sstevel@tonic-gate
188392Sswilcox while ((c = getopt(argc, argv, "mnNo:VvyY")) != EOF) {
1890Sstevel@tonic-gate switch (c) {
1900Sstevel@tonic-gate
191392Sswilcox case QUICK_CHECK:
1920Sstevel@tonic-gate mflag++;
1930Sstevel@tonic-gate break;
1940Sstevel@tonic-gate
195392Sswilcox case ALL_no:
196392Sswilcox case ALL_NO:
1970Sstevel@tonic-gate nflag++;
1980Sstevel@tonic-gate yflag = 0;
1990Sstevel@tonic-gate break;
2000Sstevel@tonic-gate
201392Sswilcox case VERBOSE:
202392Sswilcox verbose++;
203392Sswilcox break;
204392Sswilcox
205392Sswilcox case UFS_OPTS:
2060Sstevel@tonic-gate /*
2070Sstevel@tonic-gate * ufs specific options.
2080Sstevel@tonic-gate */
209392Sswilcox if (optarg == NULL) {
210392Sswilcox usage();
211392Sswilcox /*
212392Sswilcox * lint does not believe this, nor does it
213392Sswilcox * believe #pragma does_not_return(usage)
214392Sswilcox */
215392Sswilcox /* NOTREACHED */
216392Sswilcox }
2170Sstevel@tonic-gate suboptions = optarg;
2180Sstevel@tonic-gate while (*suboptions != '\0') {
219392Sswilcox switch (getsubopt(&suboptions, subopts,
220392Sswilcox &value)) {
2210Sstevel@tonic-gate
2220Sstevel@tonic-gate case PREEN:
2230Sstevel@tonic-gate preen++;
2240Sstevel@tonic-gate break;
2250Sstevel@tonic-gate
2260Sstevel@tonic-gate case BLOCK:
227392Sswilcox bflag = argtol(BLOCK, "block",
228392Sswilcox value, 10);
229392Sswilcox (void) printf("Alternate super block "
230392Sswilcox "location: %ld.\n",
231392Sswilcox (long)bflag);
2320Sstevel@tonic-gate break;
2330Sstevel@tonic-gate
2340Sstevel@tonic-gate case DEBUG:
2350Sstevel@tonic-gate debug++;
236392Sswilcox verbose++;
2370Sstevel@tonic-gate break;
2380Sstevel@tonic-gate
2390Sstevel@tonic-gate case ONLY_WRITES:
2400Sstevel@tonic-gate /* check only writable filesystems */
2410Sstevel@tonic-gate wflag++;
2420Sstevel@tonic-gate break;
2430Sstevel@tonic-gate
2440Sstevel@tonic-gate case FORCE:
2450Sstevel@tonic-gate fflag++;
2460Sstevel@tonic-gate break;
2470Sstevel@tonic-gate
2480Sstevel@tonic-gate default:
2490Sstevel@tonic-gate usage();
2500Sstevel@tonic-gate }
2510Sstevel@tonic-gate }
2520Sstevel@tonic-gate break;
2530Sstevel@tonic-gate
254392Sswilcox case ECHO_CMD:
2550Sstevel@tonic-gate {
2560Sstevel@tonic-gate int opt_count;
2570Sstevel@tonic-gate char *opt_text;
2580Sstevel@tonic-gate
259392Sswilcox (void) printf("fsck -F ufs ");
2600Sstevel@tonic-gate for (opt_count = 1; opt_count < argc;
2616106Sowenr opt_count++) {
2620Sstevel@tonic-gate opt_text = argv[opt_count];
2630Sstevel@tonic-gate if (opt_text)
264392Sswilcox (void) printf("%s ", opt_text);
2650Sstevel@tonic-gate }
266392Sswilcox (void) printf("\n");
2670Sstevel@tonic-gate }
2680Sstevel@tonic-gate break;
2690Sstevel@tonic-gate
270392Sswilcox case ALL_yes:
271392Sswilcox case ALL_YES:
2720Sstevel@tonic-gate yflag++;
2730Sstevel@tonic-gate nflag = 0;
2740Sstevel@tonic-gate break;
2750Sstevel@tonic-gate
276392Sswilcox default:
2770Sstevel@tonic-gate usage();
2780Sstevel@tonic-gate }
2790Sstevel@tonic-gate }
2800Sstevel@tonic-gate argc -= optind;
281392Sswilcox argv += optind;
282392Sswilcox
283392Sswilcox if (argc == 0)
284392Sswilcox usage();
285392Sswilcox
286392Sswilcox rflag++; /* check raw devices where we can */
287392Sswilcox if (signal(SIGINT, SIG_IGN) != SIG_IGN)
2880Sstevel@tonic-gate (void) signal(SIGINT, catch);
2890Sstevel@tonic-gate if (preen)
2900Sstevel@tonic-gate (void) signal(SIGQUIT, catchquit);
2910Sstevel@tonic-gate
292392Sswilcox /*
293392Sswilcox * Push up our allowed memory limit so we can cope
294392Sswilcox * with huge file systems.
295392Sswilcox */
296392Sswilcox if (getrlimit(RLIMIT_DATA, &rlimit) == 0) {
297392Sswilcox rlimit.rlim_cur = rlimit.rlim_max;
298392Sswilcox (void) setrlimit(RLIMIT_DATA, &rlimit);
299392Sswilcox }
300392Sswilcox
301392Sswilcox /*
302392Sswilcox * There are a lot of places where we just exit if a problem is
303392Sswilcox * found. This means that we won't necessarily check everything
304392Sswilcox * we were asked to. It would be nice to do everything, and
305392Sswilcox * then provide a summary when we're done. However, the
306392Sswilcox * interface doesn't really allow us to do that in any useful
307392Sswilcox * way. So, we'll just bail on the first unrecoverable
308392Sswilcox * problem encountered. If we've been run by the generic
309392Sswilcox * wrapper, we were only given one filesystem to check, so the
310392Sswilcox * multi-fs case implies being run manually; that means the
311392Sswilcox * user can rerun us on the remaining filesystems when it's
312392Sswilcox * convenient for them.
313392Sswilcox */
314392Sswilcox while (argc-- > 0) {
315392Sswilcox if (wflag && !writable(*argv)) {
316392Sswilcox (void) fprintf(stderr, "not writeable '%s'\n", *argv);
317392Sswilcox argv++;
318392Sswilcox if (exitstat == 0)
319392Sswilcox exitstat = EXBADPARM;
320392Sswilcox } else {
321392Sswilcox checkfilesys(*argv++);
3220Sstevel@tonic-gate }
3230Sstevel@tonic-gate }
324392Sswilcox if (interrupted)
325392Sswilcox exitstat = EXSIGNAL;
326392Sswilcox exit(exitstat);
3270Sstevel@tonic-gate }
3280Sstevel@tonic-gate
329392Sswilcox /*
330392Sswilcox * A relatively intelligent strtol(). Note that if str is NULL, we'll
331392Sswilcox * exit, so ret does not actually need to be pre-initialized. Lint
332392Sswilcox * doesn't believe this, and it's harmless enough to make lint happy here.
333392Sswilcox */
334392Sswilcox static long
argtol(int flag,char * req,char * str,int base)335392Sswilcox argtol(int flag, char *req, char *str, int base)
336392Sswilcox {
337392Sswilcox char *cp = str;
338392Sswilcox long ret = -1;
3390Sstevel@tonic-gate
340392Sswilcox errno = 0;
341392Sswilcox if (str != NULL)
342392Sswilcox ret = strtol(str, &cp, base);
343392Sswilcox if (cp == str || *cp) {
344392Sswilcox (void) fprintf(stderr, "-%c flag requires a %s\n", flag, req);
345392Sswilcox exit(EXBADPARM);
346392Sswilcox }
347392Sswilcox if (errno != 0) {
348392Sswilcox (void) fprintf(stderr, "-%c %s value out of range\n",
349392Sswilcox flag, req);
350392Sswilcox }
351392Sswilcox
352392Sswilcox return (ret);
353392Sswilcox }
354392Sswilcox
355392Sswilcox /*
356392Sswilcox * Check the specified file system.
357392Sswilcox */
358392Sswilcox static void
checkfilesys(char * filesys)359392Sswilcox checkfilesys(char *filesys)
3600Sstevel@tonic-gate {
3610Sstevel@tonic-gate daddr32_t n_ffree, n_bfree;
3620Sstevel@tonic-gate char *devstr;
363392Sswilcox fsck_ino_t files;
364392Sswilcox daddr32_t blks;
365392Sswilcox fsck_ino_t inumber;
366392Sswilcox int zlinks_printed;
367392Sswilcox fsck_ino_t limbo_victim;
368392Sswilcox double dbl_nffree, dbl_dsize;
369392Sswilcox int quiet_dups;
3700Sstevel@tonic-gate
3710Sstevel@tonic-gate mountfd = -1;
3720Sstevel@tonic-gate hotroot = 0;
373392Sswilcox mountedfs = M_NOMNT;
374392Sswilcox reattached_dir = 0;
375392Sswilcox broke_dir_link = 0;
376392Sswilcox iscorrupt = 1; /* assume failure in setup() */
3770Sstevel@tonic-gate islog = 0;
3780Sstevel@tonic-gate islogok = 0;
379392Sswilcox overflowed_lf = 0;
3800Sstevel@tonic-gate errorlocked = is_errorlocked(filesys);
381392Sswilcox limbo_dirs = NULL;
3820Sstevel@tonic-gate
383392Sswilcox if ((devstr = setup(filesys)) == NULL) {
384392Sswilcox if (!iscorrupt) {
3850Sstevel@tonic-gate return;
386392Sswilcox }
387392Sswilcox
3880Sstevel@tonic-gate if (preen)
3890Sstevel@tonic-gate pfatal("CAN'T CHECK FILE SYSTEM.");
390*8895SMilan.Cermak@Sun.COM if (exitstat == 0)
391*8895SMilan.Cermak@Sun.COM exitstat = mflag ? EXUMNTCHK : EXERRFATAL;
3920Sstevel@tonic-gate exit(exitstat);
393392Sswilcox } else {
394392Sswilcox devname = devstr;
3950Sstevel@tonic-gate }
396392Sswilcox
397392Sswilcox if (mflag) {
398392Sswilcox check_sanity(filesys);
399392Sswilcox /* NOTREACHED */
400392Sswilcox }
401392Sswilcox
4020Sstevel@tonic-gate if (debug)
4030Sstevel@tonic-gate printclean();
404392Sswilcox
405392Sswilcox iscorrupt = 0; /* setup() succeeded, assume good filesystem */
406392Sswilcox
4070Sstevel@tonic-gate /*
4080Sstevel@tonic-gate * 1: scan inodes tallying blocks used
4090Sstevel@tonic-gate */
410392Sswilcox if (!preen) {
411392Sswilcox /* hotroot is reported as such in setup() if debug is on */
412392Sswilcox if (mountedfs != M_NOMNT)
413392Sswilcox (void) printf("** Currently Mounted on %s\n",
414392Sswilcox sblock.fs_fsmnt);
4150Sstevel@tonic-gate else
416392Sswilcox (void) printf("** Last Mounted on %s\n",
417392Sswilcox sblock.fs_fsmnt);
418392Sswilcox (void) printf("** Phase 1 - Check Blocks and Sizes\n");
4190Sstevel@tonic-gate }
4200Sstevel@tonic-gate pass1();
4210Sstevel@tonic-gate
4220Sstevel@tonic-gate /*
4230Sstevel@tonic-gate * 1b: locate first references to duplicates, if any
4240Sstevel@tonic-gate */
425392Sswilcox if (have_dups()) {
4260Sstevel@tonic-gate if (preen)
427392Sswilcox pfatal("INTERNAL ERROR: dups with -o p");
428392Sswilcox (void) printf("** Phase 1b - Rescan For More DUPS\n");
4290Sstevel@tonic-gate pass1b();
4300Sstevel@tonic-gate }
4310Sstevel@tonic-gate
4320Sstevel@tonic-gate /*
433504Sswilcox * 2: traverse directories from root to mark all connected directories
434392Sswilcox */
435392Sswilcox if (!preen)
436392Sswilcox (void) printf("** Phase 2 - Check Pathnames\n");
437504Sswilcox pass2();
4380Sstevel@tonic-gate
4390Sstevel@tonic-gate /*
440392Sswilcox * 3a: scan inodes looking for disconnected directories.
4410Sstevel@tonic-gate */
442392Sswilcox if (!preen)
443392Sswilcox (void) printf("** Phase 3a - Check Connectivity\n");
444392Sswilcox pass3a();
4450Sstevel@tonic-gate
4460Sstevel@tonic-gate /*
4470Sstevel@tonic-gate * 3b: check acls
4480Sstevel@tonic-gate */
449392Sswilcox if (!preen)
450392Sswilcox (void) printf("** Phase 3b - Verify Shadows/ACLs\n");
4510Sstevel@tonic-gate pass3b();
4520Sstevel@tonic-gate
4530Sstevel@tonic-gate /*
4540Sstevel@tonic-gate * 4: scan inodes looking for disconnected files; check reference counts
4550Sstevel@tonic-gate */
456392Sswilcox if (!preen)
457392Sswilcox (void) printf("** Phase 4 - Check Reference Counts\n");
4580Sstevel@tonic-gate pass4();
4590Sstevel@tonic-gate
4600Sstevel@tonic-gate /*
4610Sstevel@tonic-gate * 5: check and repair resource counts in cylinder groups
4620Sstevel@tonic-gate */
463392Sswilcox if (!preen)
464392Sswilcox (void) printf("** Phase 5 - Check Cylinder Groups\n");
465392Sswilcox recount:
4660Sstevel@tonic-gate pass5();
4670Sstevel@tonic-gate
468392Sswilcox if (overflowed_lf) {
469392Sswilcox iscorrupt = 1;
470392Sswilcox }
471392Sswilcox
4721215Sswilcox if (!nflag && mountedfs == M_RW) {
4732716Smishra (void) printf("FILESYSTEM MAY STILL BE INCONSISTENT.\n");
4742716Smishra rerun = 1;
475392Sswilcox }
476392Sswilcox
477392Sswilcox if (have_dups()) {
478392Sswilcox quiet_dups = (reply("LIST REMAINING DUPS") == 0);
479392Sswilcox if (report_dups(quiet_dups) > 0)
480392Sswilcox iscorrupt = 1;
481392Sswilcox
482392Sswilcox (void) printf("WARNING: DATA LOSS MAY HAVE OCCURRED DUE TO "
483392Sswilcox "DUP BLOCKS.\nVERIFY FILE CONTENTS BEFORE USING.\n");
484392Sswilcox }
485392Sswilcox
486392Sswilcox if (limbo_dirs != NULL) {
487392Sswilcox /*
488392Sswilcox * Don't force iscorrupt, as this is sufficiently
489392Sswilcox * harmless that the filesystem can be mounted and
490392Sswilcox * used. We just leak some inodes and/or blocks.
491392Sswilcox */
492392Sswilcox pwarn("Orphan directories not cleared or reconnected:\n");
493392Sswilcox
494392Sswilcox twalk(limbo_dirs, report_limbo);
495392Sswilcox
496392Sswilcox while (limbo_dirs != NULL) {
497392Sswilcox limbo_victim = *(fsck_ino_t *)limbo_dirs;
498392Sswilcox if (limbo_victim != NULL) {
499392Sswilcox (void) tdelete((void *)limbo_victim,
500392Sswilcox &limbo_dirs,
501392Sswilcox ino_t_cmp);
502392Sswilcox }
503392Sswilcox }
504392Sswilcox
505392Sswilcox rerun = 1;
506392Sswilcox }
507392Sswilcox
508392Sswilcox if (iscorrupt) {
5092716Smishra if (mountedfs == M_RW)
5102716Smishra (void) printf("FS IS MOUNTED R/W AND"
5112716Smishra " FSCK DID ITS BEST TO FIX"
5122716Smishra " INCONSISTENCIES.\n");
5132716Smishra else
5142716Smishra (void) printf("FILESYSTEM MAY STILL BE"
5152716Smishra " INCONSISTENT.\n");
516392Sswilcox rerun = 1;
517392Sswilcox }
518392Sswilcox
519392Sswilcox /*
520392Sswilcox * iscorrupt must be stable at this point.
521392Sswilcox * updateclean() returns true when it had to discard the log.
522392Sswilcox * This can only happen once, since sblock.fs_logbno gets
523392Sswilcox * cleared as part of that operation.
524392Sswilcox */
525392Sswilcox if (updateclean()) {
526392Sswilcox if (!preen)
527392Sswilcox (void) printf(
528392Sswilcox "Log was discarded, updating cyl groups\n");
529392Sswilcox goto recount;
530392Sswilcox }
531392Sswilcox
5320Sstevel@tonic-gate if (debug)
5330Sstevel@tonic-gate printclean();
5340Sstevel@tonic-gate
535392Sswilcox ckfini();
536392Sswilcox
5370Sstevel@tonic-gate /*
5380Sstevel@tonic-gate * print out summary statistics
5390Sstevel@tonic-gate */
5400Sstevel@tonic-gate n_ffree = sblock.fs_cstotal.cs_nffree;
5410Sstevel@tonic-gate n_bfree = sblock.fs_cstotal.cs_nbfree;
542392Sswilcox files = maxino - UFSROOTINO - sblock.fs_cstotal.cs_nifree - n_files;
543392Sswilcox blks = n_blks +
544392Sswilcox sblock.fs_ncg * (cgdmin(&sblock, 0) - cgsblock(&sblock, 0));
545392Sswilcox blks += cgsblock(&sblock, 0) - cgbase(&sblock, 0);
546392Sswilcox blks += howmany(sblock.fs_cssize, sblock.fs_fsize);
547392Sswilcox blks = maxfsblock - (n_ffree + sblock.fs_frag * n_bfree) - blks;
548392Sswilcox if (debug && (files > 0 || blks > 0)) {
549392Sswilcox countdirs = sblock.fs_cstotal.cs_ndir - countdirs;
550392Sswilcox pwarn("Reclaimed: %d directories, %d files, %lld fragments\n",
551392Sswilcox countdirs, files - countdirs,
552392Sswilcox (longlong_t)blks);
553392Sswilcox }
554392Sswilcox
555392Sswilcox dbl_nffree = (double)n_ffree;
556392Sswilcox dbl_dsize = (double)sblock.fs_dsize;
557392Sswilcox
558392Sswilcox if (!verbose) {
559392Sswilcox /*
560392Sswilcox * Done as one big string to try for a single write,
561392Sswilcox * so the output doesn't get interleaved with other
562392Sswilcox * preening fscks.
563392Sswilcox */
564392Sswilcox pwarn("%ld files, %lld used, %lld free "
565392Sswilcox "(%lld frags, %lld blocks, %.1f%% fragmentation)\n",
566392Sswilcox (long)n_files, (longlong_t)n_blks,
567392Sswilcox (longlong_t)n_ffree + sblock.fs_frag * n_bfree,
568392Sswilcox (longlong_t)n_ffree, (longlong_t)n_bfree,
569392Sswilcox (dbl_nffree * 100.0) / dbl_dsize);
570392Sswilcox } else {
571392Sswilcox pwarn("\nFilesystem summary:\n");
572392Sswilcox pwarn("Inodes in use: %ld\n", (long)n_files);
573392Sswilcox pwarn("Blocks in use: %lld\n", (longlong_t)n_blks);
574392Sswilcox pwarn("Total free fragments: %lld\n",
575392Sswilcox (longlong_t)n_ffree + sblock.fs_frag * n_bfree);
576392Sswilcox pwarn("Free fragments not in blocks: %lld\n",
577392Sswilcox (longlong_t)n_ffree);
578392Sswilcox pwarn("Total free blocks: %lld\n", (longlong_t)n_bfree);
579392Sswilcox pwarn("Fragment/block fragmentation: %.1f%%\n",
580392Sswilcox (dbl_nffree * 100.0) / dbl_dsize);
581392Sswilcox pwarn("");
582392Sswilcox
583392Sswilcox if (files < 0)
584392Sswilcox pwarn("%d inodes missing\n", -files);
585392Sswilcox if (blks < 0)
586392Sswilcox pwarn("%lld blocks missing\n", -(longlong_t)blks);
587392Sswilcox
588392Sswilcox zlinks_printed = 0;
589392Sswilcox for (inumber = UFSROOTINO; inumber < maxino; inumber++) {
590392Sswilcox if (S_IS_ZLINK(statemap[inumber])) {
591392Sswilcox if (zlinks_printed == 0) {
592392Sswilcox pwarn("The following zero "
593392Sswilcox "link count inodes remain:");
594392Sswilcox }
595392Sswilcox if (zlinks_printed) {
596392Sswilcox if ((zlinks_printed % 9) == 0)
597392Sswilcox (void) puts(",\n");
598392Sswilcox else
599392Sswilcox (void) puts(", ");
600392Sswilcox }
601392Sswilcox (void) printf("%u", inumber);
602392Sswilcox zlinks_printed++;
603392Sswilcox }
6040Sstevel@tonic-gate }
605392Sswilcox if ((zlinks_printed != 0) && ((zlinks_printed % 9) != 0))
606392Sswilcox (void) putchar('\n');
6070Sstevel@tonic-gate }
608392Sswilcox
609392Sswilcox /*
610392Sswilcox * Clean up after ourselves, so we can do the next filesystem.
611392Sswilcox */
612392Sswilcox free_dup_state();
6130Sstevel@tonic-gate inocleanup();
6140Sstevel@tonic-gate free(blockmap);
6150Sstevel@tonic-gate free(statemap);
616392Sswilcox free((void *)lncntp);
6170Sstevel@tonic-gate lncntp = NULL;
618392Sswilcox blockmap = NULL;
619392Sswilcox statemap = NULL;
620392Sswilcox if (iscorrupt && exitstat == 0)
621392Sswilcox exitstat = EXFNDERRS;
622392Sswilcox if (fsmodified)
623392Sswilcox (void) printf("\n***** FILE SYSTEM WAS MODIFIED *****\n");
624392Sswilcox if (overflowed_lf)
625392Sswilcox (void) printf("\n***** %s FULL, MUST REMOVE ENTRIES *****\n",
626392Sswilcox lfname);
627392Sswilcox if (reattached_dir) {
628392Sswilcox (void) printf("ORPHANED DIRECTORIES REATTACHED; DIR LINK "
629392Sswilcox "COUNTS MAY NOT BE CORRECT.\n");
630392Sswilcox rerun = 1;
631392Sswilcox }
632392Sswilcox if (broke_dir_link) {
633392Sswilcox (void) printf(
634392Sswilcox "DIRECTORY HARDLINK BROKEN; LOOPS MAY STILL EXIST.\n");
635392Sswilcox rerun = 1;
636392Sswilcox }
6370Sstevel@tonic-gate if (iscorrupt)
638392Sswilcox (void) printf("***** FILE SYSTEM IS BAD *****\n");
639392Sswilcox
6402716Smishra if (rerun) {
6412716Smishra if (mountedfs == M_RW)
6422716Smishra (void) printf("\n***** PLEASE RERUN FSCK ON UNMOUNTED"
6432716Smishra " FILE SYSTEM *****\n");
6442716Smishra else
6452716Smishra (void) printf("\n***** PLEASE RERUN FSCK *****\n");
6462716Smishra }
647392Sswilcox
648392Sswilcox if ((exitstat == 0) &&
649392Sswilcox (((mountedfs != M_NOMNT) && !errorlocked) || hotroot)) {
650392Sswilcox exitstat = EXROOTOKAY;
651392Sswilcox }
652392Sswilcox
653392Sswilcox if ((exitstat == 0) && rerun)
654392Sswilcox exitstat = EXFNDERRS;
6550Sstevel@tonic-gate
656392Sswilcox if (mountedfs != M_NOMNT) {
657392Sswilcox if (!fsmodified)
658392Sswilcox return;
659392Sswilcox /*
660392Sswilcox * _FIOFFS is much more effective than a simple sync().
661392Sswilcox * Note that the original fswritefd was discarded in
662392Sswilcox * ckfini().
663392Sswilcox */
664392Sswilcox fswritefd = open(devstr, O_RDWR, 0);
665392Sswilcox if (fswritefd != -1) {
666392Sswilcox (void) ioctl(fswritefd, _FIOFFS, NULL);
667392Sswilcox (void) close(fswritefd);
6680Sstevel@tonic-gate }
6690Sstevel@tonic-gate
670392Sswilcox if (!preen)
671392Sswilcox (void) printf("\n***** REBOOT NOW *****\n");
672392Sswilcox
673392Sswilcox exitstat = EXREBOOTNOW;
6740Sstevel@tonic-gate }
6750Sstevel@tonic-gate }
6760Sstevel@tonic-gate
6770Sstevel@tonic-gate /*
678392Sswilcox * fsck -m: does the filesystem pass cursory examination
679392Sswilcox *
680392Sswilcox * XXX This is very redundant with setup(). The right thing would be
681392Sswilcox * for setup() to modify its behaviour when mflag is set (less
682392Sswilcox * chatty, exit instead of return, etc).
6830Sstevel@tonic-gate */
6840Sstevel@tonic-gate void
check_sanity(char * filename)685392Sswilcox check_sanity(char *filename)
6860Sstevel@tonic-gate {
6870Sstevel@tonic-gate struct stat64 stbd, stbr;
688392Sswilcox char *devname;
6890Sstevel@tonic-gate struct ustat usb;
6900Sstevel@tonic-gate char vfsfilename[MAXPATHLEN];
6910Sstevel@tonic-gate struct vfstab vfsbuf;
6920Sstevel@tonic-gate FILE *vfstab;
6930Sstevel@tonic-gate struct statvfs vfs_stat;
694392Sswilcox int found_magic[MAGIC_LIMIT];
695392Sswilcox int magic_cnt;
696392Sswilcox int is_magic = 0;
6976734Sjohnlev int is_block = 0;
6986734Sjohnlev int is_file = 0;
699392Sswilcox
700392Sswilcox (void) memset((void *)found_magic, 0, sizeof (found_magic));
7010Sstevel@tonic-gate
7020Sstevel@tonic-gate if (stat64(filename, &stbd) < 0) {
703392Sswilcox (void) fprintf(stderr,
7040Sstevel@tonic-gate "ufs fsck: sanity check failed : cannot stat %s\n", filename);
705392Sswilcox exit(EXNOSTAT);
7060Sstevel@tonic-gate }
7070Sstevel@tonic-gate
7086734Sjohnlev if (S_ISBLK(stbd.st_mode)) {
7090Sstevel@tonic-gate is_block = 1;
7106734Sjohnlev } else if (S_ISCHR(stbd.st_mode)) {
7110Sstevel@tonic-gate is_block = 0;
7126734Sjohnlev } else if (S_ISREG(stbd.st_mode)) {
7136734Sjohnlev is_file = 1;
7140Sstevel@tonic-gate }
7150Sstevel@tonic-gate
7160Sstevel@tonic-gate /*
7170Sstevel@tonic-gate * Determine if this is the root file system via vfstab. Give up
718392Sswilcox * silently on failures. The whole point of this is to be tolerant
719392Sswilcox * of the magic file systems being already mounted.
7200Sstevel@tonic-gate */
7216734Sjohnlev if (!is_file && (vfstab = fopen(VFSTAB, "r")) != NULL) {
722392Sswilcox for (magic_cnt = 0; magic_cnt < MAGIC_LIMIT; magic_cnt++) {
723392Sswilcox if (magic_cnt == MAGIC_NONE)
724392Sswilcox continue;
725392Sswilcox if (getvfsfile(vfstab, &vfsbuf,
726392Sswilcox magic_fs[magic_cnt]) == 0) {
727392Sswilcox if (is_block)
728392Sswilcox devname = vfsbuf.vfs_special;
729392Sswilcox else
730392Sswilcox devname = vfsbuf.vfs_fsckdev;
731392Sswilcox if (stat64(devname, &stbr) == 0) {
732392Sswilcox if (stbr.st_rdev == stbd.st_rdev) {
733392Sswilcox found_magic[magic_cnt] = 1;
734392Sswilcox is_magic = magic_cnt;
735392Sswilcox break;
736392Sswilcox }
737392Sswilcox }
738392Sswilcox }
7390Sstevel@tonic-gate }
7400Sstevel@tonic-gate }
7410Sstevel@tonic-gate
7420Sstevel@tonic-gate /*
743392Sswilcox * Only works if filename is a block device or if
744392Sswilcox * character and block device has the same dev_t value.
745392Sswilcox * This is currently true, but nothing really forces it.
7460Sstevel@tonic-gate */
747392Sswilcox if (!is_magic && (ustat(stbd.st_rdev, &usb) == 0)) {
748392Sswilcox (void) fprintf(stderr,
749392Sswilcox "ufs fsck: sanity check: %s already mounted\n", filename);
750392Sswilcox exit(EXMOUNTED);
7510Sstevel@tonic-gate }
7520Sstevel@tonic-gate
753392Sswilcox if (is_magic) {
754392Sswilcox (void) strcpy(vfsfilename, magic_fs[is_magic]);
7550Sstevel@tonic-gate if (statvfs(vfsfilename, &vfs_stat) != 0) {
756392Sswilcox (void) fprintf(stderr, "ufs fsck: Cannot stat %s\n",
757392Sswilcox vfsfilename);
758392Sswilcox exit(EXNOSTAT);
7590Sstevel@tonic-gate }
7600Sstevel@tonic-gate
7610Sstevel@tonic-gate if (!(vfs_stat.f_flag & ST_RDONLY)) {
7620Sstevel@tonic-gate /*
7630Sstevel@tonic-gate * The file system is mounted read/write
764392Sswilcox * We need to exit saying this. If it's only
7650Sstevel@tonic-gate * mounted readonly, we can continue.
7660Sstevel@tonic-gate */
7670Sstevel@tonic-gate
768392Sswilcox (void) fprintf(stderr,
7696106Sowenr "ufs fsck: sanity check:"
7706106Sowenr "%s already mounted read/write\n", filename);
771392Sswilcox exit(EXMOUNTED);
7720Sstevel@tonic-gate }
7730Sstevel@tonic-gate }
7740Sstevel@tonic-gate
7750Sstevel@tonic-gate /*
776392Sswilcox * We know that at boot, the ufs root file system is mounted
777392Sswilcox * read-only first. After fsck runs, it is remounted as
778392Sswilcox * read-write. Therefore, we do not need to check for different
779392Sswilcox * values for fs_state between the root file system and the
780392Sswilcox * rest of the file systems.
7810Sstevel@tonic-gate */
7820Sstevel@tonic-gate if (islog && !islogok) {
783392Sswilcox (void) fprintf(stderr,
784392Sswilcox "ufs fsck: sanity check: %s needs checking\n", filename);
785392Sswilcox exit(EXUMNTCHK);
7860Sstevel@tonic-gate }
7870Sstevel@tonic-gate if ((sblock.fs_state + (long)sblock.fs_time == FSOKAY) &&
7886106Sowenr (sblock.fs_clean == FSCLEAN || sblock.fs_clean == FSSTABLE ||
7896106Sowenr (sblock.fs_clean == FSLOG && islog))) {
790392Sswilcox (void) fprintf(stderr,
791392Sswilcox "ufs fsck: sanity check: %s okay\n", filename);
7920Sstevel@tonic-gate } else {
793392Sswilcox (void) fprintf(stderr,
794392Sswilcox "ufs fsck: sanity check: %s needs checking\n", filename);
795392Sswilcox exit(EXUMNTCHK);
7960Sstevel@tonic-gate }
797392Sswilcox exit(EXOKAY);
7980Sstevel@tonic-gate }
7990Sstevel@tonic-gate
800392Sswilcox caddr_t
hasvfsopt(struct vfstab * vfs,char * opt)801392Sswilcox hasvfsopt(struct vfstab *vfs, char *opt)
8020Sstevel@tonic-gate {
803392Sswilcox struct mnttab mtab;
8040Sstevel@tonic-gate
8050Sstevel@tonic-gate if (vfs->vfs_mntopts == NULL)
8060Sstevel@tonic-gate return (NULL);
807392Sswilcox mtab.mnt_mntopts = vfs->vfs_mntopts;
808392Sswilcox return (hasmntopt(&mtab, opt));
8090Sstevel@tonic-gate }
8100Sstevel@tonic-gate
8110Sstevel@tonic-gate void
usage(void)812392Sswilcox usage(void)
8130Sstevel@tonic-gate {
8140Sstevel@tonic-gate (void) fprintf(stderr,
815392Sswilcox "ufs usage: fsck [-F ufs] [-m] [-n] [-V] [-v] [-y] "
816392Sswilcox "[-o p,b=#,w,f] [special ....]\n");
817392Sswilcox
818392Sswilcox exit(EXBADPARM);
8190Sstevel@tonic-gate }
820392Sswilcox
821392Sswilcox /*ARGSUSED*/
822392Sswilcox static void
report_limbo(const void * node,VISIT order,int level)823392Sswilcox report_limbo(const void *node, VISIT order, int level)
824392Sswilcox {
825392Sswilcox fsck_ino_t ino = *(fsck_ino_t *)node;
826392Sswilcox
827392Sswilcox if ((order == postorder) || (order == leaf)) {
828392Sswilcox (void) printf(" Inode %d\n", ino);
829392Sswilcox }
830392Sswilcox }
831