xref: /illumos-gate/usr/src/cmd/fs.d/fsck.c (revision cc6c5292fa8a241fe50604cf6a918edfbf7cd7d2)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 
31 #pragma ident	"%Z%%M%	%I%	%E% SMI"
32 
33 #include	<stdio.h>
34 #include	<errno.h>
35 #include	<limits.h>
36 #include	<fcntl.h>
37 #include	<string.h>
38 #include	<sys/types.h>
39 #include	<sys/stat.h>
40 #include	<sys/wait.h>
41 #include	<sys/vfstab.h>
42 #include	<sys/mntent.h>
43 #include	<locale.h>
44 #include	<libintl.h>
45 
46 #define	ARGV_MAX	16
47 #define	FSTYPE_MAX	8
48 #define	VFS_PATH	"/usr/lib/fs"
49 #define	VFS_PATH2	"/etc/fs"
50 
51 #define	CHECK(xx, yy)\
52 	if (xx == (yy)-1) {\
53 		fprintf(stderr, gettext("%s: too many arguments\n"), myname); \
54 		usage(); \
55 	}
56 #define	OPTION(flag)\
57 		options++; \
58 		nargv[nargc++] = flag; \
59 		CHECK(nargc, ARGV_MAX); \
60 		break
61 #define	OPTARG(flag)\
62 		nargv[nargc++] = flag; \
63 		CHECK(nargc, ARGV_MAX); \
64 		if (optarg) {\
65 			nargv[nargc++] = optarg; \
66 			CHECK(nargc, ARGV_MAX); \
67 		}\
68 		break
69 
70 
71 int	nrun, ndisks;
72 int	maxrun = 8;	/* should be based on the machine resources */
73 
74 extern char	*optarg;
75 extern int	optind;
76 extern char	*default_fstype();
77 
78 int	nargc = 2;
79 int	options = 0;
80 int	mnt_passno = 0;
81 int	exitstat = 0;
82 int	verbose = 0;
83 char	*nargv[ARGV_MAX];
84 char	*myname, *fstype;
85 char	*malloc();
86 char	vfstab[] = VFSTAB;
87 char	pflg = 0, Vflg = 0;
88 
89 /*
90  * Keep an idea of the last device arg type as a hint to the
91  * type of the next arg. In the case of mountall, it's very likely
92  * to be the same type and the next entry in the file. This should
93  * help speed vfstab lookups.
94  */
95 enum dev_arg_t { UNKNOWN, SPECIAL, FSCKDEV, MOUNTPT };
96 enum dev_arg_t arg_hint = UNKNOWN;
97 
98 static struct devlist {
99 	char *name;
100 	char *fsname;
101 	pid_t pid;
102 	struct devlist *nxt;
103 } *newdev(), *getdev();
104 
105 /*
106  * private copy vfstab functions
107  */
108 static struct vfstab	vfsave = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
109 
110 static int
111 vfdup(struct vfstab *vp)
112 {
113 	if (vfsave.vfs_special != NULL) {
114 		free(vfsave.vfs_special);
115 		vfsave.vfs_special = NULL;
116 	}
117 	if ((vp->vfs_special != NULL) &&
118 	    ((vfsave.vfs_special = strdup(vp->vfs_special)) == NULL)) {
119 		perror(myname);
120 		return (4);	/* XXX */
121 	}
122 
123 	if (vfsave.vfs_fsckdev != NULL) {
124 		free(vfsave.vfs_fsckdev);
125 		vfsave.vfs_fsckdev = NULL;
126 	}
127 	if ((vp->vfs_fsckdev != NULL) &&
128 	    ((vfsave.vfs_fsckdev = strdup(vp->vfs_fsckdev)) == NULL)) {
129 		perror(myname);
130 		return (4);	/* XXX */
131 	}
132 
133 	if (vfsave.vfs_mountp != NULL) {
134 		free(vfsave.vfs_mountp);
135 		vfsave.vfs_mountp = NULL;
136 	}
137 	if ((vp->vfs_mountp != NULL) &&
138 	    ((vfsave.vfs_mountp = strdup(vp->vfs_mountp)) == NULL)) {
139 		perror(myname);
140 		return (4);	/* XXX */
141 	}
142 
143 	if (vfsave.vfs_fstype != NULL) {
144 		free(vfsave.vfs_fstype);
145 		vfsave.vfs_fstype = NULL;
146 	}
147 	if ((vp->vfs_fstype != NULL) &&
148 	    ((vfsave.vfs_fstype = strdup(vp->vfs_fstype)) == NULL)) {
149 		perror(myname);
150 		return (4);	/* XXX */
151 	}
152 
153 	if (vfsave.vfs_fsckpass != NULL) {
154 		free(vfsave.vfs_fsckpass);
155 		vfsave.vfs_fsckpass = NULL;
156 	}
157 	if ((vp->vfs_fsckpass != NULL) &&
158 	    ((vfsave.vfs_fsckpass = strdup(vp->vfs_fsckpass)) == NULL)) {
159 		perror(myname);
160 		return (4);	/* XXX */
161 	}
162 
163 	if (vfsave.vfs_automnt != NULL) {
164 		free(vfsave.vfs_automnt);
165 		vfsave.vfs_automnt = NULL;
166 	}
167 	if ((vp->vfs_automnt != NULL) &&
168 	    ((vfsave.vfs_automnt = strdup(vp->vfs_automnt)) == NULL)) {
169 		perror(myname);
170 		return (4);	/* XXX */
171 	}
172 
173 	if (vfsave.vfs_mntopts != NULL) {
174 		free(vfsave.vfs_mntopts);
175 		vfsave.vfs_mntopts = NULL;
176 	}
177 	if ((vp->vfs_mntopts != NULL) &&
178 	    ((vfsave.vfs_mntopts = strdup(vp->vfs_mntopts)) == NULL)) {
179 		perror(myname);
180 		return (4);	/* XXX */
181 	}
182 
183 	*vp = vfsave;
184 	return (0);
185 }
186 
187 static int
188 mygetvfsent(FILE *fp, struct vfstab *vp)
189 {
190 	int	error;
191 
192 	if ((error = getvfsent(fp, vp)) != 0)
193 		return (error);
194 	return (vfdup(vp));
195 }
196 
197 static int
198 mygetvfsany(FILE *fp, struct vfstab *vp, struct vfstab *vrefp)
199 {
200 	int	error;
201 
202 	if ((error = getvfsany(fp, vp, vrefp)) != 0)
203 		return (error);
204 	return (vfdup(vp));
205 }
206 
207 main(argc, argv)
208 	int	argc;
209 	char	*argv[];
210 {
211 	int	cc, ret, other_than_ufs = 0;
212 	int	questflg = 0, Fflg = 0, Vflg = 0, sanity = 0;
213 	char	*subopt;
214 	FILE	*fd = NULL;
215 	struct vfstab	vget, vref;
216 	int preencnt = 0;
217 	struct devlist *dp, *devs = NULL;
218 	int status;
219 
220 	(void) setlocale(LC_ALL, "");
221 #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
222 #define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
223 #endif
224 	(void) textdomain(TEXT_DOMAIN);
225 
226 	myname = strrchr(argv[0], '/');
227 	if (myname)
228 		myname++;
229 	else
230 		myname = argv[0];
231 
232 	while ((cc = getopt(argc, argv, "?F:mnNo:vVyY")) != -1) {
233 		switch (cc) {
234 		case '?':
235 			questflg++;
236 			if (questflg > 1)
237 				usage();
238 			nargv[nargc++] = "-?";
239 			CHECK(nargc, ARGV_MAX);
240 			break;
241 		case 'F':
242 			Fflg++;
243 			/* check for more that one -F */
244 			if (Fflg > 1) {
245 				fprintf(stderr,
246 				gettext("%s: more than one fstype specified\n"),
247 					myname);
248 				usage();
249 			}
250 			fstype = optarg;
251 			if (strlen(fstype) > (size_t)FSTYPE_MAX) {
252 				fprintf(stderr,
253 			gettext("%s: Fstype %s exceeds %d characters\n"),
254 					myname, fstype, FSTYPE_MAX);
255 						exit(1);
256 			}
257 			break;
258 		case 'm':
259 			sanity++;
260 			OPTION("-m");
261 		case 'n':
262 			OPTION("-n");
263 		case 'N':
264 			OPTION("-N");
265 		case 'o':
266 			subopt = optarg;
267 			while (*subopt != '\0') {
268 				if (*subopt == 'p') {
269 					pflg++;
270 					break;
271 				}
272 				subopt++;
273 			}
274 			OPTARG("-o");
275 		case 'v':
276 			OPTION("-v");
277 		case 'V':
278 			Vflg++;
279 			if (Vflg > 1)
280 				usage();
281 			break;
282 		case 'y':
283 			OPTION("-y");
284 		case 'Y':
285 			OPTION("-Y");
286 		}
287 		optarg = NULL;
288 	}
289 
290 	/* copy '--' to specific */
291 	if (strcmp(argv[optind-1], "--") == 0) {
292 		nargv[nargc++] = argv[optind-1];
293 		CHECK(nargc, ARGV_MAX);
294 	}
295 
296 	if (questflg) {
297 		if (Fflg) {
298 			nargc = 2;
299 			nargv[nargc++] = "-?";
300 			nargv[nargc] = NULL;
301 			do_exec(fstype, nargv);
302 		}
303 		usage();
304 	}
305 
306 	if ((sanity) && (options > 1)) {
307 		usage();
308 	}
309 
310 	if (optind == argc) {	/* no device name is specified */
311 		if (fstype == NULL) {
312 			if ((argc > 2) && (sanity)) {
313 				usage();
314 			}
315 		}
316 		/*
317 		 * Try to check UFS filesystems first, then check other
318 		 * filesystems if they exist.
319 		 * Note: Parallel checking is only available in UFS for now.
320 		 */
321 		if (fstype == NULL || strcmp(fstype, MNTTYPE_UFS) == 0) {
322 			if ((fd = fopen(vfstab, "r")) == NULL) {
323 				fprintf(stderr,
324 					gettext("%s: cannot open vfstab\n"),
325 					myname);
326 				exit(1);
327 			}
328 			while ((ret = mygetvfsent(fd, &vget)) == 0) {
329 				if (strcmp(vget.vfs_fstype, MNTTYPE_UFS) &&
330 				    numbers(vget.vfs_fsckpass)) {
331 					other_than_ufs ++;
332 					continue;
333 				}
334 				if (numbers(vget.vfs_fsckpass))
335 					mnt_passno = atoi(vget.vfs_fsckpass);
336 				else
337 					continue;
338 				if (mnt_passno < 1)
339 					continue;
340 				if (pflg == 0 || mnt_passno == 1) {
341 					status = execute(vget.vfs_fsckdev,
342 					    MNTTYPE_UFS, Vflg, fd);
343 					/* return the highest exit code */
344 					if (status > exitstat)
345 						exitstat = status;
346 				} else if (preen_addev(vget.vfs_fsckdev) == 0) {
347 					preencnt++;
348 					dp = newdev(&vget);
349 					dp->nxt = devs;
350 					devs = dp;
351 				} else {
352 					/*
353 					 * preening setup failed, so
354 					 * execute serially here...
355 					 */
356 					fprintf(stderr,
357 					gettext("%s: preen_addev error\n"),
358 						myname);
359 					status = execute(vget.vfs_fsckdev,
360 					    MNTTYPE_UFS, Vflg, fd);
361 					/* return the highest exit code */
362 					if (status > exitstat)
363 						exitstat = status;
364 				}
365 			}
366 			fclose(fd);
367 			if (ret > 0)
368 				vfserror(ret);
369 			if (pflg && exitstat == 0) {
370 				fsck_dopreen(&devs, preencnt);
371 			}
372 		}
373 		else
374 			other_than_ufs = 1;
375 
376 		if (other_than_ufs) {
377 			if ((fd = fopen(vfstab, "r")) == NULL) {
378 				fprintf(stderr,
379 					gettext("%s: cannot open vfstab\n"),
380 					myname);
381 				exit(1);
382 			}
383 			while ((ret = mygetvfsent(fd, &vget)) == 0)
384 				if (strcmp(vget.vfs_fstype, MNTTYPE_UFS) &&
385 				    numbers(vget.vfs_fsckpass) &&
386 				    vget.vfs_fsckdev != NULL &&
387 				    (fstype == NULL ||
388 				    strcmp(fstype, vget.vfs_fstype) == 0)) {
389 					status = execute(vget.vfs_fsckdev,
390 					    vget.vfs_fstype, Vflg, fd);
391 					/* return the highest exit code */
392 					if (status > exitstat)
393 						exitstat = status;
394 				}
395 			fclose(fd);
396 			if (ret > 0)
397 				vfserror(ret);
398 		}
399 
400 	} else {	/* device name is specified */
401 		if (fstype == NULL && (fd = fopen(vfstab, "r")) == NULL) {
402 			fprintf(stderr, gettext("%s: cannot open vfstab\n"),
403 				myname);
404 			exit(1);
405 		}
406 		while (optind < argc) {
407 			/*
408 			 * If "-F FStype" is specified, use that fs type.
409 			 * Otherwise, determine the fs type from /etc/vfstab
410 			 * if the entry exists.  Otherwise, determine the
411 			 * local or remote fs type from /etc/default/df
412 			 * or /etc/dfs/fstypes respectively.
413 			 */
414 			if (fstype == NULL) {
415 				if ((argc > 3) && (sanity)) {
416 					usage();
417 				}
418 				/* must check for both special && raw devices */
419 				vfsnull(&vref);
420 
421 				/*
422 				 * Find the vfstab entry for this device.
423 				 * arg_hint tells us what to try to match,
424 				 * based on the type of the last arg. If
425 				 * arg_hint equals UNKNOWN, then we're not
426 				 * sure of the type and need to fallthrough
427 				 * all 3 possibilities for vfstab lookup.
428 				 * Try it as a mountpt first, since that's
429 				 * what mountall gives us.
430 				 */
431 try_again:
432 				switch (arg_hint) {
433 				case UNKNOWN:
434 					/* FALLTHROUGH */
435 
436 				case MOUNTPT:
437 					vref.vfs_mountp = argv[optind];
438 					if ((ret = mygetvfsany(fd, &vget,
439 						&vref)) == -1 ||
440 						vget.vfs_fstype == NULL) {
441 
442 						vref.vfs_mountp = NULL;
443 						rewind(fd);
444 
445 						if (arg_hint == MOUNTPT) {
446 							arg_hint = UNKNOWN;
447 							goto try_again;
448 						}
449 						/* FALLTHROUGH */
450 					} else {
451 						/* Found it */
452 						if (vget.vfs_fsckdev != NULL) {
453 							argv[optind] =
454 							vget.vfs_fsckdev;
455 						}
456 						arg_hint = MOUNTPT;
457 						break;
458 					}
459 
460 				case FSCKDEV:
461 					vref.vfs_fsckdev = argv[optind];
462 					if ((ret = mygetvfsany(fd, &vget,
463 						&vref)) == -1 ||
464 						vget.vfs_fstype == NULL) {
465 
466 						vref.vfs_fsckdev = NULL;
467 						rewind(fd);
468 
469 						if (arg_hint == FSCKDEV) {
470 							arg_hint = UNKNOWN;
471 							goto try_again;
472 						}
473 						/* FALLTHROUGH */
474 					} else {
475 						/* Found it */
476 						arg_hint = FSCKDEV;
477 						break;
478 					}
479 
480 				case SPECIAL:
481 					vref.vfs_special = argv[optind];
482 					if ((ret = mygetvfsany(fd, &vget,
483 						&vref)) == -1 ||
484 						vget.vfs_fstype == NULL) {
485 
486 						vref.vfs_special = NULL;
487 						rewind(fd);
488 
489 						if (arg_hint == SPECIAL) {
490 							arg_hint = UNKNOWN;
491 							goto try_again;
492 						}
493 						/* FALLTHROUGH */
494 					} else {
495 						/* Found it */
496 						arg_hint = SPECIAL;
497 						break;
498 					}
499 				}
500 
501 				if (ret == 0 && vget.vfs_fstype) {
502 					if ((pflg) && (strcmp(vget.vfs_fstype,
503 					    MNTTYPE_UFS) == 0) && (preen_addev(
504 					    vget.vfs_fsckdev) == 0)) {
505 						preencnt++;
506 						dp = newdev(&vget);
507 						dp->nxt = devs;
508 						devs = dp;
509 					} else {
510 						status = execute(argv[optind],
511 						    vget.vfs_fstype, Vflg, fd);
512 						if (status > exitstat)
513 							exitstat = status;
514 					}
515 				} else if (ret == -1 ||
516 				    vget.vfs_fstype == NULL) {
517 					fstype =
518 					    default_fstype(argv[optind]);
519 					status = execute(argv[optind], fstype,
520 					    Vflg, fd);
521 					/* return the highest exit code */
522 					if (status > exitstat)
523 						exitstat = status;
524 				} else
525 					vfserror(ret);
526 			} else {
527 				status = execute(argv[optind], fstype,
528 				    Vflg, NULL);
529 				/* return the highest exit code */
530 				if (status > exitstat)
531 					exitstat = status;
532 			}
533 			optind++;
534 		}
535 		if (fd != NULL)
536 			fclose(fd);
537 		if ((pflg) && (exitstat == 0)) {
538 			fsck_dopreen(&devs, preencnt);
539 		}
540 	}
541 	exit(exitstat);
542 }
543 
544 static
545 fsck_dopreen(devp, ndevs)
546 	struct devlist **devp;
547 	int ndevs;
548 {
549 	char name[1024];
550 	int rc;
551 	register int i;
552 	struct devlist *bl, *bdp;
553 	struct devlist *badlist;
554 
555 	bl = badlist = NULL;
556 	while (ndevs > 0) {
557 		if (nrun > maxrun)
558 			waiter(&bl, &badlist);
559 		rc = preen_getdev(name);
560 		switch (rc) {
561 		case 0:
562 			break;
563 		case 1:
564 			bdp = getdev(name, devp);
565 			if (bdp == NULL) {
566 				fprintf(stderr,
567 					gettext("%s: unknown dev: `%s'\n"),
568 					myname, name);
569 				exit(1);
570 			}
571 			bdp->nxt = bl;
572 			bl = bdp;
573 			startdisk(bdp);
574 			ndevs--;
575 			break;
576 		case 2:
577 			waiter(&bl, &badlist);
578 			break;
579 		default:
580 			fprintf(stderr,
581 			gettext("%s: bad return `%d' from preen_getdev\n"),
582 				myname, rc);
583 			break;
584 		}
585 	}
586 	while (bl != NULL) {
587 		waiter(&bl, &badlist);
588 	}
589 
590 	if (badlist != NULL)
591 		print_badlist(badlist);
592 }
593 
594 static
595 startdisk(dp)
596 	struct devlist *dp;
597 {
598 	pid_t pid;
599 
600 	nrun++;
601 	if ((pid = fork()) == -1) {
602 		perror("fork");
603 		exit(1);
604 	} else if (pid == 0) {
605 		exitstat = execute(dp->name, MNTTYPE_UFS, Vflg, NULL);
606 		exit(exitstat);
607 	} else {
608 		dp->pid = pid;
609 	}
610 }
611 
612 static
613 waiter(blp, badlist)
614 	struct devlist **blp;
615 	struct devlist **badlist;
616 {
617 	pid_t curpid;
618 	int status;
619 	register struct devlist *bdp, *pbdp;
620 
621 	curpid = wait(&status);
622 	if (curpid == -1) {
623 		perror("wait");
624 		exit(1);
625 	}
626 
627 	for (pbdp = NULL, bdp = *blp; bdp != NULL; pbdp = bdp, bdp = bdp->nxt) {
628 		if (bdp->pid == curpid) {
629 			break;
630 		}
631 	}
632 	if (bdp == NULL)
633 		return;
634 	nrun--;
635 
636 	if (pbdp)
637 		pbdp->nxt = bdp->nxt;
638 	else
639 		*blp = bdp->nxt;
640 	preen_releasedev(bdp->name);
641 
642 	if (WTERMSIG(status)) {
643 		printf(gettext("%s (%s): EXITED WITH SIGNAL %d\n"),
644 			bdp->name, bdp->fsname, WTERMSIG(status));
645 		status = status&0377 | 8<<8;
646 	}
647 	if (WHIBYTE(status) != 0) {
648 		if (WHIBYTE(status) > exitstat)
649 			exitstat = WHIBYTE(status);
650 		while (*badlist != NULL)
651 			badlist = &(*badlist)->nxt;
652 		*badlist = bdp;
653 		bdp->nxt = NULL;
654 	}
655 }
656 
657 static
658 print_badlist(lp)
659 	struct devlist *lp;
660 {
661 	int x, len;
662 
663 	printf(
664 gettext("\nTHE FOLLOWING FILE SYSTEM(S) HAD AN UNEXPECTED INCONSISTENCY:"));
665 	for (x = 3; lp != NULL; lp = lp->nxt) {
666 		len = strlen(lp->name) + strlen(lp->fsname) + 5;
667 		x += len;
668 		if (x >= 80) {
669 			printf("\n   ");
670 			x = len + 3;
671 		} else {
672 			printf(" ");
673 		}
674 		printf("%s (%s)%s", lp->name, lp->fsname,
675 		    lp->nxt ? "," : "\n");
676 	}
677 }
678 
679 /*
680  * allocate and initialize a `devlist' structure
681  */
682 static
683 struct devlist *
684 newdev(vfsp)
685 	struct vfstab *vfsp;
686 {
687 	struct devlist *dp;
688 	extern char *strdup();
689 
690 	dp = (struct devlist *)malloc(sizeof (struct devlist));
691 	if (dp == NULL) {
692 		fprintf(stderr, gettext("%s: out of memory\n"), myname);
693 		exit(1);
694 	}
695 	dp->name = strdup(vfsp->vfs_fsckdev);
696 	dp->fsname = strdup(vfsp->vfs_mountp);
697 	if (dp->name == NULL || dp->fsname == NULL) {
698 		fprintf(stderr, gettext("%s: out of memory\n"), myname);
699 		exit(1);
700 	}
701 	return (dp);
702 }
703 
704 /*
705  * locate the devlist structure in the given list that matches `name'.
706  * If found, the structure is removed from the list, and a pointer to
707  * it is returned.  If not, NULL is returned.
708  */
709 static
710 struct devlist *
711 getdev(name, list)
712 	char *name;
713 	struct devlist **list;
714 {
715 	register struct devlist *p, *lp;
716 
717 	for (lp = NULL, p = *list; p != NULL; lp = p, p = p->nxt) {
718 		if (strcmp(p->name, name) == 0)
719 			break;
720 	}
721 
722 	if (p != NULL) {
723 		if (lp != NULL)
724 			lp->nxt = p->nxt;
725 		else
726 			*list = p->nxt;
727 	}
728 	return (p);
729 }
730 
731 /* see if all numbers */
732 numbers(yp)
733 	char	*yp;
734 {
735 	if (yp == NULL)
736 		return (0);
737 	while ('0' <= *yp && *yp <= '9')
738 		yp++;
739 	if (*yp)
740 		return (0);
741 	return (1);
742 }
743 
744 execute(fsckdev, fstype, Vflg, fd)
745 	char	*fsckdev, *fstype;
746 	int	Vflg;
747 	FILE	*fd;
748 {
749 	int	st;
750 	pid_t	fk;
751 	char	full_path[PATH_MAX];
752 	char	*vfs_path = VFS_PATH;
753 	int	status = 0;
754 
755 	nargv[nargc] = fsckdev;
756 
757 	if (Vflg) {
758 		prnt_cmd(stdout, fstype);
759 		return (0);
760 	}
761 
762 	if (fd)
763 		fcntl(fileno(fd), F_SETFD, 1);	/* close on exec */
764 
765 	if ((fk = fork()) == (pid_t)-1) {
766 		fprintf(stderr,
767 			gettext("%s: cannot fork.  Try again later\n"),
768 			myname);
769 		perror(myname);
770 		exit(1);
771 	}
772 
773 	if (fk == 0) {
774 		/* Try to exec the fstype dependent portion of the fsck. */
775 		do_exec(fstype, nargv);
776 	} else {
777 		/* parent waits for child */
778 		if (wait(&st) == (pid_t)-1) {
779 			fprintf(stderr, gettext("%s: bad wait\n"), myname);
780 			perror(myname);
781 			exit(1);
782 		}
783 
784 		if ((st & 0xff) == 0x7f) {
785 			fprintf(stderr,
786 				gettext("%s: warning: the following command"
787 				" (process %d) was stopped by signal %d\n"),
788 				myname, fk, (st >> 8) & 0xff);
789 			prnt_cmd(stderr, fstype);
790 			status = ((st >> 8) & 0xff) | 0x80;
791 		} else if (st & 0xff) {
792 			if (st & 0x80)
793 				fprintf(stderr,
794 				gettext("%s: warning: the following command"
795 				" (process %d) was terminated by signal %d"
796 				" and dumped core\n"),
797 				myname, fk, st & 0x7f);
798 			else
799 				fprintf(stderr,
800 				gettext("%s: warning: the following command"
801 				" (process %d) was terminated by signal %d\n"),
802 				myname, fk, st & 0x7f);
803 
804 			prnt_cmd(stderr, fstype);
805 			status = ((st & 0xff) | 0x80);
806 		} else if (st & 0xff00)
807 			status = (st >> 8) & 0xff;
808 	}
809 
810 	return (status);
811 }
812 
813 do_exec(fstype, nargv)
814 	char	*fstype, *nargv[];
815 {
816 	char	full_path[PATH_MAX];
817 	char	*vfs_path = VFS_PATH;
818 
819 	if (strlen(fstype) > (size_t)FSTYPE_MAX) {
820 		fprintf(stderr,
821 			gettext("%s: Fstype %s exceeds %d characters\n"),
822 			myname, fstype, FSTYPE_MAX);
823 		exit(1);
824 	}
825 	/* build the full pathname of the fstype dependent command. */
826 	sprintf(full_path, "%s/%s/%s", vfs_path, fstype, myname);
827 
828 	/* set the new argv[0] to the filename */
829 	nargv[1] = myname;
830 	/* Try to exec the fstype dependent portion of the fsck. */
831 	execv(full_path, &nargv[1]);
832 	if (errno == EACCES) {
833 		fprintf(stderr,
834 			gettext("%s: cannot execute %s - permission denied\n"),
835 			myname, full_path);
836 	}
837 	if (errno == ENOEXEC) {
838 		nargv[0] = "sh";
839 		nargv[1] = full_path;
840 		execv("/sbin/sh", &nargv[0]);
841 	}
842 	/* second path to try */
843 	vfs_path = VFS_PATH2;
844 	/* build the full pathname of the fstype dependent command. */
845 	sprintf(full_path, "%s/%s/%s", vfs_path, fstype, myname);
846 
847 	/* set the new argv[0] to the filename */
848 	nargv[1] = myname;
849 	/* Try to exec the second fstype dependent portion of the fsck. */
850 	execv(full_path, &nargv[1]);
851 	if (errno == EACCES) {
852 		fprintf(stderr,
853 			gettext("%s: cannot execute %s - permission denied\n"),
854 			myname, full_path);
855 		exit(1);
856 	}
857 	if (errno == ENOEXEC) {
858 		nargv[0] = "sh";
859 		nargv[1] = full_path;
860 		execv("/sbin/sh", &nargv[0]);
861 	}
862 	fprintf(stderr,
863 		gettext("%s: operation not applicable to FSType %s\n"),
864 		myname, fstype);
865 	exit(1);
866 }
867 
868 prnt_cmd(fd, fstype)
869 	FILE	*fd;
870 	char	*fstype;
871 {
872 	char	**argp;
873 
874 	fprintf(fd, "%s -F %s", myname, fstype);
875 	for (argp = &nargv[2]; *argp; argp++)
876 		fprintf(fd, " %s", *argp);
877 	fprintf(fd, "\n");
878 }
879 
880 vfserror(flag)
881 	int	flag;
882 {
883 	switch (flag) {
884 	case VFS_TOOLONG:
885 		fprintf(stderr,
886 			gettext("%s: line in vfstab exceeds %d characters\n"),
887 			myname, VFS_LINE_MAX-2);
888 		break;
889 	case VFS_TOOFEW:
890 		fprintf(stderr,
891 			gettext("%s: line in vfstab has too few entries\n"),
892 			myname);
893 		break;
894 	case VFS_TOOMANY:
895 		fprintf(stderr,
896 			gettext("%s: line in vfstab has too many entries\n"),
897 			myname);
898 		break;
899 	}
900 	exit(1);
901 }
902 
903 int opterr = 1, optind = 1, optopt = 0;
904 char *optarg = 0;
905 
906 int
907 getopt(int argc, char * const *argv, const char *opts)
908 {
909 	static int sp = 1;
910 	register int c;
911 	register char *cp;
912 
913 	if (sp == 1)
914 		if (optind >= argc ||
915 		    argv[optind][0] != '-' || argv[optind][1] == '\0')
916 			return (-1);
917 		else if (strcmp(argv[optind], "--") == 0) {
918 			optind++;
919 			return (-1);
920 		}
921 	optopt = c = argv[optind][sp];
922 	if (c == ':' || (cp = strchr(opts, c)) == 0) {
923 		if (opterr)
924 			fprintf(stderr,
925 				gettext("%s: illegal option -- %c\n"),
926 				*argv, c);
927 		if (argv[optind][++sp] == '\0') {
928 			optind++;
929 			sp = 1;
930 		}
931 		return ('?');
932 	}
933 	if (*++cp == ':') {
934 		if (argv[optind][sp+1] != '\0')
935 			optarg = &argv[optind++][sp+1];
936 		else if (++optind >= argc) {
937 			if (opterr)
938 				fprintf(stderr,
939 		gettext("%s: option requires an argument -- %c\n"), *argv, c);
940 			sp = 1;
941 			return ('?');
942 		} else
943 			optarg = argv[optind++];
944 		sp = 1;
945 	} else if (*cp == ';') {
946 		if (argv[optind][++sp] != '\0')
947 			if (isoptarg(c, &argv[optind][sp])) {
948 				optarg = &argv[optind++][sp];
949 				sp = 1;
950 			} else
951 				optarg = NULL;
952 		else {
953 			sp = 1;
954 			if (++optind >= argc || !isoptarg(c, &argv[optind][0]))
955 				optarg = NULL;
956 			else
957 				optarg = argv[optind++];
958 		}
959 	} else {
960 		if (argv[optind][++sp] == '\0') {
961 			sp = 1;
962 			optind++;
963 		}
964 		optarg = NULL;
965 	}
966 	return (c);
967 }
968 
969 isoptarg(cc, arg)
970 	int	cc;
971 	char	*arg;
972 {
973 	if (cc == 's' || cc == 'S') {
974 		while (*arg >= '0' && *arg <= '9')
975 			arg++;
976 		if (*arg++ != ':')
977 			return (0);
978 		while (*arg >= '0' && *arg <= '9')
979 			arg++;
980 		if (*arg)
981 			return (0);
982 		return (1);
983 	}
984 	return (0);
985 }
986 
987 usage()
988 {
989 	fprintf(stderr,
990 		gettext("Usage:\n%s [-F FSType] [-V] [-m] [special ...]\n"
991 			"%s [-F FSType] [-V] [-y|Y|n|N]"
992 			" [-o specific_options] [special ...]\n"),
993 			myname, myname);
994 
995 	exit(1);
996 }
997