xref: /illumos-gate/usr/src/cmd/fs.d/fsck.c (revision 07a48826732249fcd3aa8dd53c8389595e9f1fbc)
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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
27 /*	  All Rights Reserved  	*/
28 
29 #include	<stdio.h>
30 #include	<errno.h>
31 #include	<limits.h>
32 #include	<fcntl.h>
33 #include	<string.h>
34 #include	<sys/types.h>
35 #include	<sys/stat.h>
36 #include	<sys/wait.h>
37 #include	<sys/vfstab.h>
38 #include	<sys/mntent.h>
39 #include	<sys/sysmacros.h>
40 #include	<locale.h>
41 #include	<libintl.h>
42 #include	<sys/dkio.h>
43 
44 #define	DEV_BSIZE	512
45 #define	ARGV_MAX	16
46 #define	FSTYPE_MAX	8
47 #define	VFS_PATH	"/usr/lib/fs"
48 #define	VFS_PATH2	"/etc/fs"
49 
50 #define	CHECK(xx, yy)\
51 	if (xx == (yy)-1) {\
52 		fprintf(stderr, gettext("%s: too many arguments\n"), myname); \
53 		usage(); \
54 	}
55 #define	OPTION(flag)\
56 		options++; \
57 		nargv[nargc++] = flag; \
58 		CHECK(nargc, ARGV_MAX); \
59 		break
60 #define	OPTARG(flag)\
61 		nargv[nargc++] = flag; \
62 		CHECK(nargc, ARGV_MAX); \
63 		if (optarg) {\
64 			nargv[nargc++] = optarg; \
65 			CHECK(nargc, ARGV_MAX); \
66 		}\
67 		break
68 
69 
70 int	nrun, ndisks;
71 int	maxrun = 8;	/* should be based on the machine resources */
72 
73 extern char	*default_fstype();
74 
75 int	nargc = 2;
76 int	options = 0;
77 int	mnt_passno = 0;
78 int	exitstat = 0;
79 int	verbose = 0;
80 char	*nargv[ARGV_MAX];
81 char	*myname, *fstype;
82 char	*malloc();
83 char	vfstab[] = VFSTAB;
84 char	pflg = 0, Vflg = 0;
85 
86 /*
87  * Keep an idea of the last device arg type as a hint to the
88  * type of the next arg. In the case of mountall, it's very likely
89  * to be the same type and the next entry in the file. This should
90  * help speed vfstab lookups.
91  */
92 enum dev_arg_t { UNKNOWN, SPECIAL, FSCKDEV, MOUNTPT };
93 enum dev_arg_t arg_hint = UNKNOWN;
94 
95 static struct devlist {
96 	char *name;
97 	char *fsname;
98 	pid_t pid;
99 	struct devlist *nxt;
100 } *newdev(), *getdev();
101 
102 /*
103  * private copy vfstab functions
104  */
105 static struct vfstab	vfsave = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
106 
107 static void usage(void);
108 static void fsck_dopreen(struct devlist **devp, int ndevs);
109 static void waiter(struct devlist **blp, struct devlist **badlist);
110 static void print_badlist(struct devlist *lp);
111 static void startdisk(struct devlist *dp);
112 static void do_exec(char *fstype, char *nargv[]);
113 static void prnt_cmd(FILE *fd, char *fstype);
114 static void vfserror(int flag);
115 
116 static int
117 vfdup(struct vfstab *vp)
118 {
119 	if (vfsave.vfs_special != NULL) {
120 		free(vfsave.vfs_special);
121 		vfsave.vfs_special = NULL;
122 	}
123 	if ((vp->vfs_special != NULL) &&
124 	    ((vfsave.vfs_special = strdup(vp->vfs_special)) == NULL)) {
125 		perror(myname);
126 		return (4);	/* XXX */
127 	}
128 
129 	if (vfsave.vfs_fsckdev != NULL) {
130 		free(vfsave.vfs_fsckdev);
131 		vfsave.vfs_fsckdev = NULL;
132 	}
133 	if ((vp->vfs_fsckdev != NULL) &&
134 	    ((vfsave.vfs_fsckdev = strdup(vp->vfs_fsckdev)) == NULL)) {
135 		perror(myname);
136 		return (4);	/* XXX */
137 	}
138 
139 	if (vfsave.vfs_mountp != NULL) {
140 		free(vfsave.vfs_mountp);
141 		vfsave.vfs_mountp = NULL;
142 	}
143 	if ((vp->vfs_mountp != NULL) &&
144 	    ((vfsave.vfs_mountp = strdup(vp->vfs_mountp)) == NULL)) {
145 		perror(myname);
146 		return (4);	/* XXX */
147 	}
148 
149 	if (vfsave.vfs_fstype != NULL) {
150 		free(vfsave.vfs_fstype);
151 		vfsave.vfs_fstype = NULL;
152 	}
153 	if ((vp->vfs_fstype != NULL) &&
154 	    ((vfsave.vfs_fstype = strdup(vp->vfs_fstype)) == NULL)) {
155 		perror(myname);
156 		return (4);	/* XXX */
157 	}
158 
159 	if (vfsave.vfs_fsckpass != NULL) {
160 		free(vfsave.vfs_fsckpass);
161 		vfsave.vfs_fsckpass = NULL;
162 	}
163 	if ((vp->vfs_fsckpass != NULL) &&
164 	    ((vfsave.vfs_fsckpass = strdup(vp->vfs_fsckpass)) == NULL)) {
165 		perror(myname);
166 		return (4);	/* XXX */
167 	}
168 
169 	if (vfsave.vfs_automnt != NULL) {
170 		free(vfsave.vfs_automnt);
171 		vfsave.vfs_automnt = NULL;
172 	}
173 	if ((vp->vfs_automnt != NULL) &&
174 	    ((vfsave.vfs_automnt = strdup(vp->vfs_automnt)) == NULL)) {
175 		perror(myname);
176 		return (4);	/* XXX */
177 	}
178 
179 	if (vfsave.vfs_mntopts != NULL) {
180 		free(vfsave.vfs_mntopts);
181 		vfsave.vfs_mntopts = NULL;
182 	}
183 	if ((vp->vfs_mntopts != NULL) &&
184 	    ((vfsave.vfs_mntopts = strdup(vp->vfs_mntopts)) == NULL)) {
185 		perror(myname);
186 		return (4);	/* XXX */
187 	}
188 
189 	*vp = vfsave;
190 	return (0);
191 }
192 
193 static int
194 mygetvfsent(FILE *fp, struct vfstab *vp)
195 {
196 	int	error;
197 
198 	if ((error = getvfsent(fp, vp)) != 0)
199 		return (error);
200 	return (vfdup(vp));
201 }
202 
203 static int
204 mygetvfsany(FILE *fp, struct vfstab *vp, struct vfstab *vrefp)
205 {
206 	int	error;
207 
208 	if ((error = getvfsany(fp, vp, vrefp)) != 0)
209 		return (error);
210 	return (vfdup(vp));
211 }
212 
213 int
214 main(int argc, char *argv[])
215 {
216 	int	cc, ret, other_than_ufs = 0;
217 	int	questflg = 0, Fflg = 0, Vflg = 0, sanity = 0;
218 	char	*subopt;
219 	FILE	*fd = NULL;
220 	int	devfd;
221 	struct vfstab	vget, vref;
222 	struct dk_minfo dkminfo;
223 	int preencnt = 0;
224 	struct devlist *dp, *devs = NULL;
225 	int status;
226 	uint_t lbs;
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 
415 		while (optind < argc) {
416 			/*
417 			 * If "-F FStype" is specified, use that fs type.
418 			 * Otherwise, determine the fs type from /etc/vfstab
419 			 * if the entry exists.  Otherwise, determine the
420 			 * local or remote fs type from /etc/default/df
421 			 * or /etc/dfs/fstypes respectively.
422 			 */
423 			if (fstype == NULL) {
424 				if ((argc > 3) && (sanity)) {
425 					usage();
426 				}
427 				/* must check for both special && raw devices */
428 				vfsnull(&vref);
429 
430 				/*
431 				 * Find the vfstab entry for this device.
432 				 * arg_hint tells us what to try to match,
433 				 * based on the type of the last arg. If
434 				 * arg_hint equals UNKNOWN, then we're not
435 				 * sure of the type and need to fallthrough
436 				 * all 3 possibilities for vfstab lookup.
437 				 * Try it as a mountpt first, since that's
438 				 * what mountall gives us.
439 				 */
440 try_again:
441 				switch (arg_hint) {
442 				case UNKNOWN:
443 					/* FALLTHROUGH */
444 
445 				case MOUNTPT:
446 					vref.vfs_mountp = argv[optind];
447 					if ((ret = mygetvfsany(fd, &vget,
448 						&vref)) == -1 ||
449 						vget.vfs_fstype == NULL) {
450 
451 						vref.vfs_mountp = NULL;
452 						rewind(fd);
453 
454 						if (arg_hint == MOUNTPT) {
455 							arg_hint = UNKNOWN;
456 							goto try_again;
457 						}
458 						/* FALLTHROUGH */
459 					} else {
460 						/* Found it */
461 						if (vget.vfs_fsckdev != NULL) {
462 							argv[optind] =
463 							vget.vfs_fsckdev;
464 						}
465 						arg_hint = MOUNTPT;
466 						break;
467 					}
468 
469 				case FSCKDEV:
470 					vref.vfs_fsckdev = argv[optind];
471 
472 					/*
473 					 * Check the media sector size
474 					 */
475 					if (((devfd = open(vref.vfs_fsckdev,
476 					    O_RDWR)) >= 0) && (ioctl(devfd,
477 					    DKIOCGMEDIAINFO, &dkminfo) !=
478 					    -1)) {
479 						lbs =  dkminfo.dki_lbsize;
480 						if (lbs != 0 && ISP2(lbs /
481 						    DEV_BSIZE) &&
482 						    lbs != DEV_BSIZE) {
483 							fprintf(stderr,
484 							    gettext("The device"
485 							    " sector size is"
486 							    " not supported by"
487 							    " fsck\n"));
488 							(void) close(devfd);
489 							exit(1);
490 						}
491 					}
492 
493 					if (devfd >= 0) {
494 						(void) close(devfd);
495 					}
496 
497 					if ((ret = mygetvfsany(fd, &vget,
498 						&vref)) == -1 ||
499 						vget.vfs_fstype == NULL) {
500 
501 						vref.vfs_fsckdev = NULL;
502 						rewind(fd);
503 
504 						if (arg_hint == FSCKDEV) {
505 							arg_hint = UNKNOWN;
506 							goto try_again;
507 						}
508 						/* FALLTHROUGH */
509 					} else {
510 						/* Found it */
511 						arg_hint = FSCKDEV;
512 						break;
513 					}
514 
515 				case SPECIAL:
516 					vref.vfs_special = argv[optind];
517 					if ((ret = mygetvfsany(fd, &vget,
518 						&vref)) == -1 ||
519 						vget.vfs_fstype == NULL) {
520 
521 						vref.vfs_special = NULL;
522 						rewind(fd);
523 
524 						if (arg_hint == SPECIAL) {
525 							arg_hint = UNKNOWN;
526 							goto try_again;
527 						}
528 						/* FALLTHROUGH */
529 					} else {
530 						/* Found it */
531 						arg_hint = SPECIAL;
532 						break;
533 					}
534 				}
535 
536 				if (ret == 0 && vget.vfs_fstype) {
537 					if ((pflg) && (strcmp(vget.vfs_fstype,
538 					    MNTTYPE_UFS) == 0) && (preen_addev(
539 					    vget.vfs_fsckdev) == 0)) {
540 						preencnt++;
541 						dp = newdev(&vget);
542 						dp->nxt = devs;
543 						devs = dp;
544 					} else {
545 						status = execute(argv[optind],
546 						    vget.vfs_fstype, Vflg, fd);
547 						if (status > exitstat)
548 							exitstat = status;
549 					}
550 				} else if (ret == -1 ||
551 				    vget.vfs_fstype == NULL) {
552 					fstype =
553 					    default_fstype(argv[optind]);
554 					status = execute(argv[optind], fstype,
555 					    Vflg, fd);
556 					/* return the highest exit code */
557 					if (status > exitstat)
558 						exitstat = status;
559 				} else
560 					vfserror(ret);
561 			} else {
562 				status = execute(argv[optind], fstype,
563 				    Vflg, NULL);
564 				/* return the highest exit code */
565 				if (status > exitstat)
566 					exitstat = status;
567 			}
568 			optind++;
569 		}
570 		if (fd != NULL)
571 			fclose(fd);
572 		if ((pflg) && (exitstat == 0)) {
573 			fsck_dopreen(&devs, preencnt);
574 		}
575 	}
576 	return (exitstat);
577 }
578 
579 static void
580 fsck_dopreen(struct devlist **devp, int ndevs)
581 {
582 	char name[1024];
583 	int rc;
584 	int i;
585 	struct devlist *bl, *bdp;
586 	struct devlist *badlist;
587 
588 	bl = badlist = NULL;
589 	while (ndevs > 0) {
590 		if (nrun > maxrun)
591 			waiter(&bl, &badlist);
592 		rc = preen_getdev(name);
593 		switch (rc) {
594 		case 0:
595 			break;
596 		case 1:
597 			bdp = getdev(name, devp);
598 			if (bdp == NULL) {
599 				fprintf(stderr,
600 					gettext("%s: unknown dev: `%s'\n"),
601 					myname, name);
602 				exit(1);
603 			}
604 			bdp->nxt = bl;
605 			bl = bdp;
606 			startdisk(bdp);
607 			ndevs--;
608 			break;
609 		case 2:
610 			waiter(&bl, &badlist);
611 			break;
612 		default:
613 			fprintf(stderr,
614 			gettext("%s: bad return `%d' from preen_getdev\n"),
615 				myname, rc);
616 			break;
617 		}
618 	}
619 	while (bl != NULL) {
620 		waiter(&bl, &badlist);
621 	}
622 
623 	if (badlist != NULL)
624 		print_badlist(badlist);
625 }
626 
627 static void
628 startdisk(struct devlist *dp)
629 {
630 	pid_t pid;
631 
632 	nrun++;
633 	if ((pid = fork()) == -1) {
634 		perror("fork");
635 		exit(1);
636 	} else if (pid == 0) {
637 		exitstat = execute(dp->name, MNTTYPE_UFS, Vflg, NULL);
638 		exit(exitstat);
639 	} else {
640 		dp->pid = pid;
641 	}
642 }
643 
644 static void
645 waiter(struct devlist **blp, struct devlist **badlist)
646 {
647 	pid_t curpid;
648 	int status;
649 	struct devlist *bdp, *pbdp;
650 
651 	curpid = wait(&status);
652 	if (curpid == -1) {
653 		perror("wait");
654 		exit(1);
655 	}
656 
657 	for (pbdp = NULL, bdp = *blp; bdp != NULL; pbdp = bdp, bdp = bdp->nxt) {
658 		if (bdp->pid == curpid) {
659 			break;
660 		}
661 	}
662 	if (bdp == NULL)
663 		return;
664 	nrun--;
665 
666 	if (pbdp)
667 		pbdp->nxt = bdp->nxt;
668 	else
669 		*blp = bdp->nxt;
670 	preen_releasedev(bdp->name);
671 
672 	if (WTERMSIG(status)) {
673 		printf(gettext("%s (%s): EXITED WITH SIGNAL %d\n"),
674 			bdp->name, bdp->fsname, WTERMSIG(status));
675 		status = status&0377 | 8<<8;
676 	}
677 	if (WHIBYTE(status) != 0) {
678 		if (WHIBYTE(status) > exitstat)
679 			exitstat = WHIBYTE(status);
680 		while (*badlist != NULL)
681 			badlist = &(*badlist)->nxt;
682 		*badlist = bdp;
683 		bdp->nxt = NULL;
684 	}
685 }
686 
687 static void
688 print_badlist(struct devlist *lp)
689 {
690 	int x, len;
691 
692 	printf(
693 gettext("\nTHE FOLLOWING FILE SYSTEM(S) HAD AN UNEXPECTED INCONSISTENCY:"));
694 	for (x = 3; lp != NULL; lp = lp->nxt) {
695 		len = strlen(lp->name) + strlen(lp->fsname) + 5;
696 		x += len;
697 		if (x >= 80) {
698 			printf("\n   ");
699 			x = len + 3;
700 		} else {
701 			printf(" ");
702 		}
703 		printf("%s (%s)%s", lp->name, lp->fsname,
704 		    lp->nxt ? "," : "\n");
705 	}
706 }
707 
708 /*
709  * allocate and initialize a `devlist' structure
710  */
711 static
712 struct devlist *
713 newdev(struct vfstab *vfsp)
714 {
715 	struct devlist *dp;
716 	extern char *strdup();
717 
718 	dp = (struct devlist *)malloc(sizeof (struct devlist));
719 	if (dp == NULL) {
720 		fprintf(stderr, gettext("%s: out of memory\n"), myname);
721 		exit(1);
722 	}
723 	dp->name = strdup(vfsp->vfs_fsckdev);
724 	dp->fsname = strdup(vfsp->vfs_mountp);
725 	if (dp->name == NULL || dp->fsname == NULL) {
726 		fprintf(stderr, gettext("%s: out of memory\n"), myname);
727 		exit(1);
728 	}
729 	return (dp);
730 }
731 
732 /*
733  * locate the devlist structure in the given list that matches `name'.
734  * If found, the structure is removed from the list, and a pointer to
735  * it is returned.  If not, NULL is returned.
736  */
737 static
738 struct devlist *
739 getdev(char *name, struct devlist **list)
740 {
741 	struct devlist *p, *lp;
742 
743 	for (lp = NULL, p = *list; p != NULL; lp = p, p = p->nxt) {
744 		if (strcmp(p->name, name) == 0)
745 			break;
746 	}
747 
748 	if (p != NULL) {
749 		if (lp != NULL)
750 			lp->nxt = p->nxt;
751 		else
752 			*list = p->nxt;
753 	}
754 	return (p);
755 }
756 
757 /* see if all numbers */
758 int
759 numbers(char *yp)
760 {
761 	if (yp == NULL)
762 		return (0);
763 	while ('0' <= *yp && *yp <= '9')
764 		yp++;
765 	if (*yp)
766 		return (0);
767 	return (1);
768 }
769 
770 int
771 execute(char *fsckdev, char *fstype, int Vflg, FILE *fd)
772 {
773 	int	st;
774 	pid_t	fk;
775 	char	full_path[PATH_MAX];
776 	char	*vfs_path = VFS_PATH;
777 	int	status = 0;
778 
779 	nargv[nargc] = fsckdev;
780 
781 	if (Vflg) {
782 		prnt_cmd(stdout, fstype);
783 		return (0);
784 	}
785 
786 	if (fd)
787 		fcntl(fileno(fd), F_SETFD, 1);	/* close on exec */
788 
789 	if ((fk = fork()) == (pid_t)-1) {
790 		fprintf(stderr,
791 			gettext("%s: cannot fork.  Try again later\n"),
792 			myname);
793 		perror(myname);
794 		exit(1);
795 	}
796 
797 	if (fk == 0) {
798 		/* Try to exec the fstype dependent portion of the fsck. */
799 		do_exec(fstype, nargv);
800 	} else {
801 		/* parent waits for child */
802 		if (wait(&st) == (pid_t)-1) {
803 			fprintf(stderr, gettext("%s: bad wait\n"), myname);
804 			perror(myname);
805 			exit(1);
806 		}
807 
808 		if ((st & 0xff) == 0x7f) {
809 			fprintf(stderr,
810 				gettext("%s: warning: the following command"
811 				" (process %d) was stopped by signal %d\n"),
812 				myname, fk, (st >> 8) & 0xff);
813 			prnt_cmd(stderr, fstype);
814 			status = ((st >> 8) & 0xff) | 0x80;
815 		} else if (st & 0xff) {
816 			if (st & 0x80)
817 				fprintf(stderr,
818 				gettext("%s: warning: the following command"
819 				" (process %d) was terminated by signal %d"
820 				" and dumped core\n"),
821 				myname, fk, st & 0x7f);
822 			else
823 				fprintf(stderr,
824 				gettext("%s: warning: the following command"
825 				" (process %d) was terminated by signal %d\n"),
826 				myname, fk, st & 0x7f);
827 
828 			prnt_cmd(stderr, fstype);
829 			status = ((st & 0xff) | 0x80);
830 		} else if (st & 0xff00)
831 			status = (st >> 8) & 0xff;
832 	}
833 
834 	return (status);
835 }
836 
837 static void
838 do_exec(char *fstype, char *nargv[])
839 {
840 	char	full_path[PATH_MAX];
841 	char	*vfs_path = VFS_PATH;
842 
843 	if (strlen(fstype) > (size_t)FSTYPE_MAX) {
844 		fprintf(stderr,
845 			gettext("%s: Fstype %s exceeds %d characters\n"),
846 			myname, fstype, FSTYPE_MAX);
847 		exit(1);
848 	}
849 	/* build the full pathname of the fstype dependent command. */
850 	sprintf(full_path, "%s/%s/%s", vfs_path, fstype, myname);
851 
852 	/* set the new argv[0] to the filename */
853 	nargv[1] = myname;
854 	/* Try to exec the fstype dependent portion of the fsck. */
855 	execv(full_path, &nargv[1]);
856 	if (errno == EACCES) {
857 		fprintf(stderr,
858 			gettext("%s: cannot execute %s - permission denied\n"),
859 			myname, full_path);
860 	}
861 	if (errno == ENOEXEC) {
862 		nargv[0] = "sh";
863 		nargv[1] = full_path;
864 		execv("/sbin/sh", &nargv[0]);
865 	}
866 	/* second path to try */
867 	vfs_path = VFS_PATH2;
868 	/* build the full pathname of the fstype dependent command. */
869 	sprintf(full_path, "%s/%s/%s", vfs_path, fstype, myname);
870 
871 	/* set the new argv[0] to the filename */
872 	nargv[1] = myname;
873 	/* Try to exec the second fstype dependent portion of the fsck. */
874 	execv(full_path, &nargv[1]);
875 	if (errno == EACCES) {
876 		fprintf(stderr,
877 			gettext("%s: cannot execute %s - permission denied\n"),
878 			myname, full_path);
879 		exit(1);
880 	}
881 	if (errno == ENOEXEC) {
882 		nargv[0] = "sh";
883 		nargv[1] = full_path;
884 		execv("/sbin/sh", &nargv[0]);
885 	}
886 	fprintf(stderr,
887 		gettext("%s: operation not applicable to FSType %s\n"),
888 		myname, fstype);
889 	exit(1);
890 }
891 
892 static void
893 prnt_cmd(FILE *fd, char *fstype)
894 {
895 	char	**argp;
896 
897 	fprintf(fd, "%s -F %s", myname, fstype);
898 	for (argp = &nargv[2]; *argp; argp++)
899 		fprintf(fd, " %s", *argp);
900 	fprintf(fd, "\n");
901 }
902 
903 static void
904 vfserror(int flag)
905 {
906 	switch (flag) {
907 	case VFS_TOOLONG:
908 		fprintf(stderr,
909 			gettext("%s: line in vfstab exceeds %d characters\n"),
910 			myname, VFS_LINE_MAX-2);
911 		break;
912 	case VFS_TOOFEW:
913 		fprintf(stderr,
914 			gettext("%s: line in vfstab has too few entries\n"),
915 			myname);
916 		break;
917 	case VFS_TOOMANY:
918 		fprintf(stderr,
919 			gettext("%s: line in vfstab has too many entries\n"),
920 			myname);
921 		break;
922 	}
923 	exit(1);
924 }
925 
926 static void
927 usage(void)
928 {
929 	fprintf(stderr,
930 		gettext("Usage:\n%s [-F FSType] [-V] [-m] [special ...]\n"
931 			"%s [-F FSType] [-V] [-y|Y|n|N]"
932 			" [-o specific_options] [special ...]\n"),
933 			myname, myname);
934 
935 	exit(1);
936 }
937