xref: /csrg-svn/sbin/fsck/main.c (revision 36827)
1 /*
2  * Copyright (c) 1980 Regents of the University of California.
3  * All rights reserved.  The Berkeley software License Agreement
4  * specifies the terms and conditions for redistribution.
5  */
6 
7 #ifndef lint
8 char copyright[] =
9 "@(#) Copyright (c) 1980 Regents of the University of California.\n\
10  All rights reserved.\n";
11 #endif not lint
12 
13 #ifndef lint
14 static char sccsid[] = "@(#)main.c	5.9 (Berkeley) 02/17/89";
15 #endif not lint
16 
17 #include <sys/param.h>
18 #include <sys/inode.h>
19 #include <sys/fs.h>
20 #include <sys/stat.h>
21 #include <sys/wait.h>
22 #include <fstab.h>
23 #include <strings.h>
24 #include <ctype.h>
25 #include "fsck.h"
26 
27 char	*rawname(), *unrawname(), *blockcheck();
28 int	catch(), catchquit(), voidquit();
29 int	returntosingle;
30 int	(*signal())();
31 
32 main(argc, argv)
33 	int	argc;
34 	char	*argv[];
35 {
36 	struct fstab *fsp;
37 	int pid, passno, anygtr, sumstatus;
38 	char *name;
39 	struct worklist {
40 		int	pid;		/* pid of child doing the check */
41 		struct	worklist *next;	/* next in list */
42 		char	name[MAXMNTLEN];/* name of file system */
43 	} *listhead = 0, *freelist = 0, *badlist = 0;
44 	register struct worklist *wp, *pwp;
45 
46 	sync();
47 	while (--argc > 0 && **++argv == '-') {
48 		switch (*++*argv) {
49 
50 		case 'p':
51 			preen++;
52 			break;
53 
54 		case 'b':
55 			if (argv[0][1] != '\0') {
56 				bflag = atoi(argv[0]+1);
57 			} else {
58 				bflag = atoi(*++argv);
59 				argc--;
60 			}
61 			printf("Alternate super block location: %d\n", bflag);
62 			break;
63 
64 		case 'c':
65 			cvtflag++;
66 			break;
67 
68 		case 'd':
69 			debug++;
70 			break;
71 
72 		case 'm':
73 			if (!isdigit(argv[1][0]))
74 				errexit("-m flag requires a mode\n");
75 			sscanf(*++argv, "%o", &lfmode);
76 			if (lfmode &~ 07777)
77 				errexit("bad mode to -m: %o\n", lfmode);
78 			argc--;
79 			printf("** lost+found creation mode %o\n", lfmode);
80 			break;
81 
82 		case 'n':	/* default no answer flag */
83 		case 'N':
84 			nflag++;
85 			yflag = 0;
86 			break;
87 
88 		case 'y':	/* default yes answer flag */
89 		case 'Y':
90 			yflag++;
91 			nflag = 0;
92 			break;
93 
94 		default:
95 			errexit("%c option?\n", **argv);
96 		}
97 	}
98 	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
99 		(void)signal(SIGINT, catch);
100 	if (preen)
101 		(void)signal(SIGQUIT, catchquit);
102 	if (argc) {
103 		while (argc-- > 0) {
104 			hotroot = 0;
105 			checkfilesys(*argv++);
106 		}
107 		exit(0);
108 	}
109 	sumstatus = 0;
110 	passno = 1;
111 	do {
112 		anygtr = 0;
113 		if (setfsent() == 0)
114 			errexit("Can't open checklist file: %s\n", FSTAB);
115 		while ((fsp = getfsent()) != 0) {
116 			if (strcmp(fsp->fs_type, FSTAB_RW) &&
117 			    strcmp(fsp->fs_type, FSTAB_RO) &&
118 			    strcmp(fsp->fs_type, FSTAB_RQ))
119 				continue;
120 			if (preen == 0 ||
121 			    passno == 1 && fsp->fs_passno == passno) {
122 				name = blockcheck(fsp->fs_spec);
123 				if (name != NULL)
124 					checkfilesys(name);
125 				else if (preen)
126 					exit(8);
127 			} else if (fsp->fs_passno > passno) {
128 				anygtr = 1;
129 			} else if (fsp->fs_passno == passno) {
130 				name = blockcheck(fsp->fs_spec);
131 				if (name == NULL) {
132 					pwarn("BAD DISK NAME %s\n",
133 						fsp->fs_spec);
134 					sumstatus |= 8;
135 					continue;
136 				}
137 				pid = fork();
138 				if (pid < 0) {
139 					perror("fork");
140 					exit(8);
141 				}
142 				if (pid == 0) {
143 					(void)signal(SIGQUIT, voidquit);
144 					checkfilesys(name);
145 					exit(0);
146 				} else {
147 					if (freelist == 0) {
148 						wp = (struct worklist *) malloc
149 						    (sizeof(struct worklist));
150 					} else {
151 						wp = freelist;
152 						freelist = wp->next;
153 					}
154 					wp->next = listhead;
155 					listhead = wp;
156 					wp->pid = pid;
157 					sprintf(wp->name, "%s (%s)", name,
158 					    fsp->fs_file);
159 				}
160 			}
161 		}
162 		if (preen) {
163 			union wait status;
164 			while ((pid = wait(&status)) != -1) {
165 				sumstatus |= status.w_retcode;
166 				pwp = 0;
167 				for (wp = listhead; wp; pwp = wp, wp = wp->next)
168 					if (wp->pid == pid)
169 						break;
170 				if (wp == 0) {
171 					printf("Unknown pid %d\n", pid);
172 					continue;
173 				}
174 				if (pwp == 0)
175 					listhead = wp->next;
176 				else
177 					pwp->next = wp->next;
178 				if (status.w_retcode != 0) {
179 					wp->next = badlist;
180 					badlist = wp;
181 				} else {
182 					wp->next = freelist;
183 					freelist = wp;
184 				}
185 			}
186 		}
187 		passno++;
188 	} while (anygtr);
189 	if (sumstatus) {
190 		if (badlist == 0)
191 			exit(8);
192 		printf("THE FOLLOWING FILE SYSTEM%s HAD AN %s\n\t",
193 			badlist->next ? "S" : "", "UNEXPECTED INCONSISTENCY:");
194 		for (wp = badlist; wp; wp = wp->next)
195 			printf("%s%s", wp->name, wp->next ? ", " : "\n");
196 		exit(8);
197 	}
198 	(void)endfsent();
199 	if (returntosingle)
200 		exit(2);
201 	exit(0);
202 }
203 
204 checkfilesys(filesys)
205 	char *filesys;
206 {
207 	daddr_t n_ffree, n_bfree;
208 	struct dups *dp;
209 	struct zlncnt *zlnp;
210 
211 	devname = filesys;
212 	if (setup(filesys) == 0) {
213 		if (preen)
214 			pfatal("CAN'T CHECK FILE SYSTEM.");
215 		return;
216 	}
217 	/*
218 	 * 1: scan inodes tallying blocks used
219 	 */
220 	if (preen == 0) {
221 		printf("** Last Mounted on %s\n", sblock.fs_fsmnt);
222 		if (hotroot)
223 			printf("** Root file system\n");
224 		printf("** Phase 1 - Check Blocks and Sizes\n");
225 	}
226 	pass1();
227 
228 	/*
229 	 * 1b: locate first references to duplicates, if any
230 	 */
231 	if (duplist) {
232 		if (preen)
233 			pfatal("INTERNAL ERROR: dups with -p");
234 		printf("** Phase 1b - Rescan For More DUPS\n");
235 		pass1b();
236 	}
237 
238 	/*
239 	 * 2: traverse directories from root to mark all connected directories
240 	 */
241 	if (preen == 0)
242 		printf("** Phase 2 - Check Pathnames\n");
243 	pass2();
244 
245 	/*
246 	 * 3: scan inodes looking for disconnected directories
247 	 */
248 	if (preen == 0)
249 		printf("** Phase 3 - Check Connectivity\n");
250 	pass3();
251 
252 	/*
253 	 * 4: scan inodes looking for disconnected files; check reference counts
254 	 */
255 	if (preen == 0)
256 		printf("** Phase 4 - Check Reference Counts\n");
257 	pass4();
258 
259 	/*
260 	 * 5: check and repair resource counts in cylinder groups
261 	 */
262 	if (preen == 0)
263 		printf("** Phase 5 - Check Cyl groups\n");
264 	pass5();
265 
266 	/*
267 	 * print out summary statistics
268 	 */
269 	n_ffree = sblock.fs_cstotal.cs_nffree;
270 	n_bfree = sblock.fs_cstotal.cs_nbfree;
271 	pwarn("%d files, %d used, %d free ",
272 	    n_files, n_blks, n_ffree + sblock.fs_frag * n_bfree);
273 	printf("(%d frags, %d blocks, %.1f%% fragmentation)\n",
274 	    n_ffree, n_bfree, (float)(n_ffree * 100) / sblock.fs_dsize);
275 	if (debug && (n_files -= imax - ROOTINO - sblock.fs_cstotal.cs_nifree))
276 		printf("%d files missing\n", n_files);
277 	if (debug) {
278 		n_blks += sblock.fs_ncg *
279 			(cgdmin(&sblock, 0) - cgsblock(&sblock, 0));
280 		n_blks += cgsblock(&sblock, 0) - cgbase(&sblock, 0);
281 		n_blks += howmany(sblock.fs_cssize, sblock.fs_fsize);
282 		if (n_blks -= fmax - (n_ffree + sblock.fs_frag * n_bfree))
283 			printf("%d blocks missing\n", n_blks);
284 		if (duplist != NULL) {
285 			printf("The following duplicate blocks remain:");
286 			for (dp = duplist; dp; dp = dp->next)
287 				printf(" %d,", dp->dup);
288 			printf("\n");
289 		}
290 		if (zlnhead != NULL) {
291 			printf("The following zero link count inodes remain:");
292 			for (zlnp = zlnhead; zlnp; zlnp = zlnp->next)
293 				printf(" %d,", zlnp->zlncnt);
294 			printf("\n");
295 		}
296 	}
297 	zlnhead = (struct zlncnt *)0;
298 	duplist = (struct dups *)0;
299 	if (dfile.mod) {
300 		(void)time(&sblock.fs_time);
301 		sbdirty();
302 	}
303 	ckfini();
304 	free(blockmap);
305 	free(statemap);
306 	free((char *)lncntp);
307 	if (!dfile.mod)
308 		return;
309 	if (!preen) {
310 		printf("\n***** FILE SYSTEM WAS MODIFIED *****\n");
311 		if (hotroot)
312 			printf("\n***** REBOOT UNIX *****\n");
313 	}
314 	if (hotroot) {
315 		sync();
316 		exit(4);
317 	}
318 }
319 
320 char *
321 blockcheck(name)
322 	char *name;
323 {
324 	struct stat stslash, stblock, stchar;
325 	char *raw;
326 	int looped = 0;
327 
328 	hotroot = 0;
329 	if (stat("/", &stslash) < 0){
330 		perror("/");
331 		printf("Can't stat root\n");
332 		return (0);
333 	}
334 retry:
335 	if (stat(name, &stblock) < 0){
336 		perror(name);
337 		printf("Can't stat %s\n", name);
338 		return (0);
339 	}
340 	if ((stblock.st_mode & S_IFMT) == S_IFBLK) {
341 		if (stslash.st_dev == stblock.st_rdev) {
342 			hotroot++;
343 			return (name);
344 		}
345 		raw = rawname(name);
346 		if (stat(raw, &stchar) < 0){
347 			perror(raw);
348 			printf("Can't stat %s\n", raw);
349 			return (name);
350 		}
351 		if ((stchar.st_mode & S_IFMT) == S_IFCHR)
352 			return (raw);
353 		else {
354 			printf("%s is not a character device\n", raw);
355 			return (name);
356 		}
357 	} else if ((stblock.st_mode & S_IFMT) == S_IFCHR) {
358 		if (looped) {
359 			printf("Can't make sense out of name %s\n", name);
360 			return (0);
361 		}
362 		name = unrawname(name);
363 		looped++;
364 		goto retry;
365 	}
366 	printf("Can't make sense out of name %s\n", name);
367 	return (0);
368 }
369 
370 char *
371 unrawname(cp)
372 	char *cp;
373 {
374 	char *dp = rindex(cp, '/');
375 	struct stat stb;
376 
377 	if (dp == 0)
378 		return (cp);
379 	if (stat(cp, &stb) < 0)
380 		return (cp);
381 	if ((stb.st_mode&S_IFMT) != S_IFCHR)
382 		return (cp);
383 	if (*(dp+1) != 'r')
384 		return (cp);
385 	(void)strcpy(dp+1, dp+2);
386 	return (cp);
387 }
388 
389 char *
390 rawname(cp)
391 	char *cp;
392 {
393 	static char rawbuf[32];
394 	char *dp = rindex(cp, '/');
395 
396 	if (dp == 0)
397 		return (0);
398 	*dp = 0;
399 	(void)strcpy(rawbuf, cp);
400 	*dp = '/';
401 	(void)strcat(rawbuf, "/r");
402 	(void)strcat(rawbuf, dp+1);
403 	return (rawbuf);
404 }
405