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