xref: /titanic_51/usr/src/cmd/fs.d/ufs/fsck/main.c (revision 8eea8e29cc4374d1ee24c25a07f45af132db3499)
1 /*
2  * Copyright 2003 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 #pragma ident	"%Z%%M%	%I%	%E% SMI"
30 
31 #include <stdio.h>
32 #include <sys/param.h>
33 #include <sys/types.h>
34 #include <sys/int_types.h>
35 #include <sys/sysmacros.h>
36 #include <sys/mntent.h>
37 
38 #define	bcopy(f, t, n)    memcpy(t, f, n)
39 #define	bzero(s, n)	memset(s, 0, n)
40 #define	bcmp(s, d, n)	memcmp(s, d, n)
41 
42 #define	index(s, r)	strchr(s, r)
43 #define	rindex(s, r)	strrchr(s, r)
44 
45 #include <sys/fs/ufs_fs.h>
46 #include <sys/vnode.h>
47 #include <sys/fs/ufs_inode.h>
48 #include <sys/stat.h>
49 #include <sys/wait.h>
50 #include <sys/mnttab.h>
51 #include <sys/signal.h>
52 #include <string.h>
53 #include <ctype.h>	/* use isdigit macro rather than 4.1 libc routine */
54 #include "fsck.h"
55 #include <sys/vfstab.h>
56 #include <sys/ustat.h>
57 #include <sys/statvfs.h>
58 #include <errno.h>
59 
60 int	mflag = 0;		/* sanity check only */
61 char	hotroot;
62 
63 uint_t largefile_count = 0;	/* global largefile counter */
64 
65 extern int	optind;
66 extern char	*optarg;
67 
68 char	*mntopt();
69 char	*malloc();
70 void	catch(), catchquit(), voidquit();
71 int	returntosingle;
72 void	checkfilesys();
73 void	update_lf();
74 void	main();
75 void	check_sanity();
76 void	usage();
77 struct dinode *getnextinode();
78 
79 char *subopts [] = {
80 #define	PREEN		0
81 	"p",
82 #define	BLOCK		1
83 	"b",
84 #define	DEBUG		2
85 	"d",
86 #define	READ_ONLY	3
87 	"r",
88 #define	ONLY_WRITES	4
89 	"w",
90 #define	CONVERT		5	/* setup.c convert between fffs and ffs */
91 	"c",
92 #define	FORCE		6	/* force checking, even if clean */
93 	"f",
94 	NULL
95 };
96 
97 char **sargv;
98 void
99 main(argc, argv)
100 	int	argc;
101 	char	*argv[];
102 {
103 	int	c;
104 	char	*suboptions,	*value;
105 	int	suboption;
106 
107 	/*
108 	 * Save argv pointer to be used if a hole in a directory's block list
109 	 * is found.
110 	 */
111 	sargv = argv;
112 	while ((c = getopt(argc, argv, "mnNo:VyYz")) != EOF) {
113 		switch (c) {
114 
115 		case 'm':
116 			mflag++;
117 			break;
118 
119 		case 'n':	/* default no answer flag */
120 		case 'N':
121 			nflag++;
122 			yflag = 0;
123 			break;
124 
125 		case 'o':
126 			/*
127 			 * ufs specific options.
128 			 */
129 			suboptions = optarg;
130 			while (*suboptions != '\0') {
131 				switch ((suboption = getsubopt(&suboptions,
132 							subopts, &value))) {
133 
134 				case PREEN:
135 					preen++;
136 					break;
137 
138 				case BLOCK:
139 					if (value == NULL) {
140 						usage();
141 					} else {
142 						bflag = atoi(value);
143 					}
144 				printf("Alternate super block location: %d.\n",
145 					    bflag);
146 					break;
147 
148 				case CONVERT:
149 					cvtflag++;
150 					break;
151 
152 				case DEBUG:
153 					debug++;
154 					break;
155 
156 				case READ_ONLY:
157 					break;
158 
159 				case ONLY_WRITES:
160 					/* check only writable filesystems */
161 					wflag++;
162 					break;
163 
164 				case FORCE:
165 					fflag++;
166 					break;
167 
168 				default:
169 					usage();
170 				}
171 			}
172 			break;
173 
174 		case 'V':
175 			{
176 				int	opt_count;
177 				char	*opt_text;
178 
179 				(void) fprintf(stdout, "fsck -F ufs ");
180 				for (opt_count = 1; opt_count < argc;
181 								opt_count++) {
182 					opt_text = argv[opt_count];
183 					if (opt_text)
184 						(void) fprintf(stdout, " %s ",
185 								opt_text);
186 				}
187 				(void) fprintf(stdout, "\n");
188 			}
189 			break;
190 
191 		case 'y':	/* default yes answer flag */
192 		case 'Y':
193 			yflag++;
194 			nflag = 0;
195 			break;
196 
197 		case '?':
198 			usage();
199 		}
200 	}
201 	argc -= optind;
202 	argv = &argv[optind];
203 	rflag++; /* check raw devices */
204 	if (signal(SIGINT, SIG_IGN) != (int)SIG_IGN)
205 		(void) signal(SIGINT, catch);
206 	if (preen)
207 		(void) signal(SIGQUIT, catchquit);
208 
209 	if (argc) {
210 		while (argc-- > 0) {
211 			if (wflag && !writable(*argv)) {
212 				(void) fprintf(stderr, "not writeable '%s'\n",
213 									*argv);
214 				argv++;
215 			} else
216 				checkfilesys(*argv++);
217 		}
218 		exit(exitstat);
219 	}
220 }
221 
222 
223 void
224 checkfilesys(filesys)
225 	char *filesys;
226 {
227 	daddr32_t n_ffree, n_bfree;
228 	struct dups *dp;
229 	struct zlncnt *zlnp;
230 	char *devstr;
231 
232 	mountfd = -1;
233 	hotroot = 0;
234 	mountedfs = 0;
235 	iscorrupt = 1;
236 	isconvert = 0;
237 	ismdd = 0;
238 	islog = 0;
239 	islogok = 0;
240 	dirholes = 0;
241 	needs_reclaim = 0;
242 	errorlocked = is_errorlocked(filesys);
243 
244 	if ((devstr = setup(filesys)) == 0) {
245 		if (iscorrupt == 0)
246 			return;
247 		if (preen)
248 			pfatal("CAN'T CHECK FILE SYSTEM.");
249 		if ((exitstat == 0) && (mflag))
250 			exitstat = 32;
251 		exit(exitstat);
252 	}
253 	else
254 		devname = devstr;
255 	if (mflag)
256 		check_sanity(filesys);	/* this never returns */
257 	if (debug)
258 		printclean();
259 	iscorrupt = 0;
260 	/*
261 	 * 1: scan inodes tallying blocks used
262 	 */
263 	if (preen == 0) {
264 		if (mountedfs)
265 			printf("** Currently Mounted on %s\n", sblock.fs_fsmnt);
266 		else
267 			printf("** Last Mounted on %s\n", sblock.fs_fsmnt);
268 		if (mflag) {
269 			printf("** Phase 1 - Sanity Check only\n");
270 			return;
271 		} else {
272 			printf("** Phase 1 - Check Blocks and Sizes\n");
273 		}
274 	}
275 	pass1();
276 
277 	/*
278 	 * 1b: locate first references to duplicates, if any
279 	 */
280 	if (duplist) {
281 		if (preen)
282 			pfatal("INTERNAL ERROR: dups with -p");
283 		printf("** Phase 1b - Rescan For More DUPS\n");
284 		pass1b();
285 	}
286 
287 	/*
288 	 * 2: traverse directories from root to mark all connected directories
289 	 */
290 	if (preen == 0) {
291 		printf("** Phase 2 - Check Pathnames\n");
292 	}
293 	pass2();
294 
295 	/*
296 	 * 3: scan inodes looking for disconnected directories
297 	 */
298 	if (preen == 0) {
299 		printf("** Phase 3 - Check Connectivity\n");
300 	}
301 	pass3();
302 
303 	/*
304 	 * 3b: check acls
305 	 */
306 	pass3b();
307 
308 	/*
309 	 * 4: scan inodes looking for disconnected files; check reference counts
310 	 */
311 	if (preen == 0) {
312 		printf("** Phase 4 - Check Reference Counts\n");
313 	}
314 	pass4();
315 
316 	/*
317 	 * 5: check and repair resource counts in cylinder groups
318 	 */
319 	if (preen == 0) {
320 		printf("** Phase 5 - Check Cyl groups\n");
321 	}
322 	pass5();
323 
324 	updateclean();
325 	if (debug)
326 		printclean();
327 
328 	/*
329 	 * print out summary statistics
330 	 */
331 	n_ffree = sblock.fs_cstotal.cs_nffree;
332 	n_bfree = sblock.fs_cstotal.cs_nbfree;
333 	pwarn("%d files, %d used, %d free ",
334 	    n_files, n_blks, n_ffree + sblock.fs_frag * n_bfree);
335 	if (preen)
336 		printf("\n");
337 	pwarn("(%d frags, %d blocks, %.1f%% fragmentation)\n",
338 	    n_ffree, n_bfree, (float)(n_ffree * 100) / sblock.fs_dsize);
339 	if (debug &&
340 	    (n_files -= maxino - UFSROOTINO - sblock.fs_cstotal.cs_nifree))
341 		printf("%d files missing\n", n_files);
342 	if (debug) {
343 		n_blks += sblock.fs_ncg *
344 			(cgdmin(&sblock, 0) - cgsblock(&sblock, 0));
345 		n_blks += cgsblock(&sblock, 0) - cgbase(&sblock, 0);
346 		n_blks += howmany(sblock.fs_cssize, sblock.fs_fsize);
347 		if (n_blks -= maxfsblock - (n_ffree + sblock.fs_frag * n_bfree))
348 			printf("%d blocks missing\n", n_blks);
349 		if (duplist != NULL) {
350 			printf("The following duplicate blocks remain:");
351 			for (dp = duplist; dp; dp = dp->next)
352 				printf(" %d,", dp->dup);
353 			printf("\n");
354 		}
355 		if (zlnhead != NULL) {
356 			printf("The following zero link count inodes remain:");
357 			for (zlnp = zlnhead; zlnp; zlnp = zlnp->next)
358 				printf(" %d,", zlnp->zlncnt);
359 			printf("\n");
360 		}
361 	}
362 	zlnhead = (struct zlncnt *)0;
363 	duplist = muldup = (struct dups *)0;
364 	inocleanup();
365 	ckfini();
366 	free(blockmap);
367 	free(statemap);
368 	free((char *)lncntp);
369 	lncntp = NULL;
370 	blockmap = statemap = NULL;
371 	if (iscorrupt)
372 		exitstat = 36;
373 	if (!fsmodified)
374 		return;
375 	if (!preen)
376 		printf("\n***** FILE SYSTEM WAS MODIFIED *****\n");
377 
378 	if (dirholes) {
379 
380 		if (preen) {
381 			printf("\nFixed directory holes, Re-checking %s\n",
382 				devname);
383 			execv("/usr/sbin/fsck", sargv);
384 			printf("Exec failed %s\n", strerror(errno));
385 		}
386 
387 		printf("\nFixed directories with holes, Run fsck once again\n");
388 		exitstat = 36;
389 	} else {
390 		if ((mountedfs && !errorlocked) || hotroot) {
391 			exitstat = 40;
392 		}
393 	}
394 }
395 
396 
397 /*
398  * exit 0 - file system is unmounted and okay
399  * exit 32 - file system is unmounted and needs checking
400  * exit 33 - file system is mounted
401  *          for root file system
402  * exit 34 - cannot stat device
403  */
404 
405 void
406 check_sanity(filename)
407 char	*filename;
408 {
409 	struct stat64 stbd, stbr;
410 	struct ustat usb;
411 	char *devname;
412 	char vfsfilename[MAXPATHLEN];
413 	struct vfstab vfsbuf;
414 	FILE *vfstab;
415 	struct statvfs vfs_stat;
416 	int is_root = 0;
417 	int is_usr = 0;
418 	int is_block = 0;
419 
420 	if (stat64(filename, &stbd) < 0) {
421 		fprintf(stderr,
422 		"ufs fsck: sanity check failed : cannot stat %s\n", filename);
423 		exit(34);
424 	}
425 
426 	if ((stbd.st_mode & S_IFMT) == S_IFBLK)
427 		is_block = 1;
428 	else if ((stbd.st_mode & S_IFMT) == S_IFCHR)
429 		is_block = 0;
430 	else {
431 		fprintf(stderr,
432 	"ufs fsck: sanity check failed: %s not block or character device\n",
433 		filename);
434 		exit(34);
435 	}
436 
437 	/*
438 	 * Determine if this is the root file system via vfstab. Give up
439 	 * silently on failures. The whole point of this is not to care
440 	 * if the root file system is already mounted.
441 	 *
442 	 * XXX - similar for /usr. This should be fixed to simply return
443 	 * a new code indicating, mounted and needs to be checked.
444 	 */
445 	if ((vfstab = fopen(VFSTAB, "r")) != 0) {
446 		if (getvfsfile(vfstab, &vfsbuf, "/") == 0) {
447 			if (is_block)
448 				devname = vfsbuf.vfs_special;
449 			else
450 				devname = vfsbuf.vfs_fsckdev;
451 			if (stat64(devname, &stbr) == 0)
452 				if (stbr.st_rdev == stbd.st_rdev)
453 					is_root = 1;
454 		}
455 		if (getvfsfile(vfstab, &vfsbuf, "/usr") == 0) {
456 			if (is_block)
457 				devname = vfsbuf.vfs_special;
458 			else
459 				devname = vfsbuf.vfs_fsckdev;
460 			if (stat64(devname, &stbr) == 0)
461 				if (stbr.st_rdev == stbd.st_rdev)
462 					is_usr = 1;
463 		}
464 	}
465 
466 
467 	/*
468 	 * XXX - only works if filename is a block device or if
469 	 * character and block device has the same dev_t value
470 	 */
471 	if (is_root == 0 && is_usr == 0 && ustat(stbd.st_rdev, &usb) == 0) {
472 		fprintf(stderr, "ufs fsck: sanity check: %s already mounted\n",
473 		filename);
474 		exit(33);
475 	}
476 
477 	if (is_root || is_usr) {
478 		if (is_root)
479 			strcpy(vfsfilename, "/");
480 		else
481 			strcpy(vfsfilename, "/usr");
482 		if (statvfs(vfsfilename, &vfs_stat) != 0) {
483 			fprintf(stderr,
484 				"ufs fsck: Cannot stat %s\n",
485 				vfsfilename);
486 			exit(34);
487 		}
488 
489 		if (!(vfs_stat.f_flag & ST_RDONLY)) {
490 			/*
491 			 * The file system is mounted read/write
492 			 * We need to exit saying that / or /usr is
493 			 * already mounted read/write. If it's only
494 			 * mounted readonly, we can continue.
495 			 */
496 
497 			fprintf(stderr,
498 				"ufs fsck: sanity check:"
499 				"%s already mounted read/write\n",
500 				filename);
501 			exit(33);
502 		}
503 	}
504 
505 	/*
506 	 * We mount the ufs root file system read-only first.  After fsck
507 	 * runs, we remount the root as read-write.  Therefore, we no longer
508 	 * check for different values for fs_state between the root file
509 	 * system and the rest of file systems.
510 	 */
511 	if (islog && !islogok) {
512 		fprintf(stderr, "ufs fsck: sanity check: %s needs checking\n",
513 			filename);
514 		exit(32);
515 	}
516 	if ((sblock.fs_state + (long)sblock.fs_time == FSOKAY) &&
517 		(sblock.fs_clean == FSCLEAN || sblock.fs_clean == FSSTABLE ||
518 		(sblock.fs_clean == FSLOG && islog))) {
519 		fprintf(stderr, "ufs fsck: sanity check: %s okay\n", filename);
520 	} else {
521 		fprintf(stderr, "ufs fsck: sanity check: %s needs checking\n",
522 			filename);
523 		exit(32);
524 	}
525 	exit(0);
526 }
527 
528 char *
529 unrawname(name)
530 	char *name;
531 {
532 	char *dp;
533 
534 	extern char *getfullblkname();
535 
536 	if ((dp = getfullblkname(name)) == NULL)
537 		return ("");
538 	return (dp);
539 }
540 
541 char *
542 rawname(name)
543 	char *name;
544 {
545 	char *dp;
546 
547 	extern char *getfullrawname();
548 
549 	if ((dp = getfullrawname(name)) == NULL)
550 		return ("");
551 	return (dp);
552 }
553 
554 char *
555 hasvfsopt(vfs, opt)
556 	struct vfstab *vfs;
557 	char *opt;
558 {
559 	char *f, *opts;
560 	static char *tmpopts;
561 
562 	if (vfs->vfs_mntopts == NULL)
563 		return (NULL);
564 	if (tmpopts == 0) {
565 		tmpopts = (char *)calloc(256, sizeof (char));
566 		if (tmpopts == 0)
567 			return (0);
568 	}
569 	strncpy(tmpopts, vfs->vfs_mntopts, (sizeof (tmpopts) - 1));
570 	opts = tmpopts;
571 	f = mntopt(&opts);
572 	for (; *f; f = mntopt(&opts)) {
573 		if (strncmp(opt, f, strlen(opt)) == 0)
574 			return (f - tmpopts + vfs->vfs_mntopts);
575 	}
576 	return (NULL);
577 }
578 
579 
580 void
581 usage()
582 {
583 	(void) fprintf(stderr,
584 "ufs usage: fsck [-F ufs] [generic options] [-o p,b=#,c,w] [special ....]\n");
585 	exit(31+1);
586 }
587