xref: /titanic_50/usr/src/cmd/fs.d/ufs/mount/mount.c (revision fa9e4066f08beec538e775443c5be79dd423fcab)
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 2004 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  * University Copyright- Copyright (c) 1982, 1986, 1988
32  * The Regents of the University of California
33  * All Rights Reserved
34  *
35  * University Acknowledgment- Portions of this document are derived from
36  * software developed by the University of California, Berkeley, and its
37  * contributors.
38  */
39 
40 #pragma ident	"%Z%%M%	%I%	%E% SMI"
41 
42 /*
43  * mount
44  */
45 #include <ctype.h>
46 #include <string.h>
47 #include <fcntl.h>
48 #include <signal.h>
49 #include <poll.h>
50 #include <sys/mkdev.h>
51 #include <sys/param.h>
52 #include <sys/types.h>
53 #include <sys/mntent.h>
54 #include <stdlib.h>
55 
56 #define	bcopy(f, t, n)	memcpy(t, f, n)
57 #define	bzero(s, n)	memset(s, 0, n)
58 #define	bcmp(s, d, n)	memcmp(s, d, n)
59 
60 #define	index(s, r)	strchr(s, r)
61 #define	rindex(s, r)	strrchr(s, r)
62 
63 #include <errno.h>
64 #include <sys/vfs.h>
65 #include <sys/stat.h>
66 #include <stdio.h>
67 #include <unistd.h>
68 #include <sys/mnttab.h>
69 #include <sys/mount.h>
70 #include <sys/mntio.h>
71 #include <sys/wait.h>
72 #include <sys/fstyp.h>
73 #include <sys/fsid.h>
74 #include <sys/vfstab.h>
75 #include <sys/filio.h>
76 #include <sys/fs/ufs_fs.h>
77 
78 #include <sys/fs/ufs_mount.h>
79 #include <sys/fs/ufs_filio.h>
80 
81 #include <locale.h>
82 #include <fslib.h>
83 
84 static int	ro = 0;
85 static int	largefiles = 0; /* flag - add default nolargefiles to mnttab */
86 
87 static int	gflg = 0;
88 static int	mflg = 0;
89 static int 	Oflg = 0;
90 static int	qflg = 0;
91 
92 #define	NAME_MAX	64		/* sizeof "fstype myname" */
93 
94 static int	checkislog(char *);
95 static void	disable_logging(char *, char *);
96 static int	eatmntopt(struct mnttab *, char *);
97 static void	enable_logging(char *, char *);
98 static void	fixopts(struct mnttab *, char *);
99 static void	mountfs(struct mnttab *);
100 static void	replace_opts(char *, int, char *, char *);
101 static int	replace_opts_dflt(char *, int, const char *, const char *);
102 static void	rmopt(struct mnttab *, char *);
103 static void	rpterr(char *, char *);
104 static void	usage(void);
105 
106 static char	fstype[] = MNTTYPE_UFS;
107 static char	opts[MAX_MNTOPT_STR];
108 static char	typename[NAME_MAX], *myname;
109 static char	*fop_subopts[] = { MNTOPT_ONERROR, NULL };
110 #define	NOMATCH	(-1)
111 #define	ONERROR	(0)		/* index within fop_subopts */
112 
113 static struct fop_subopt {
114 	char	*str;
115 	int	 flag;
116 } fop_subopt_list[] = {
117 	{ UFSMNT_ONERROR_PANIC_STR,	UFSMNT_ONERROR_PANIC	},
118 	{ UFSMNT_ONERROR_LOCK_STR,	UFSMNT_ONERROR_LOCK	},
119 	{ UFSMNT_ONERROR_UMOUNT_STR,	UFSMNT_ONERROR_UMOUNT	},
120 	{ NULL,				UFSMNT_ONERROR_DEFAULT	}
121 };
122 
123 
124 /*
125  * Check if the specified filesystem is already mounted.
126  */
127 static boolean_t
128 in_mnttab(char *mountp)
129 {
130 	FILE *file;
131 	int found = B_FALSE;
132 	struct mnttab mntent;
133 
134 	if ((file = fopen(MNTTAB, "r")) == NULL)
135 		return (B_FALSE);
136 	while (getmntent(file, &mntent) == 0) {
137 		if (mntent.mnt_mountp != NULL &&
138 		    strcmp(mntent.mnt_mountp, mountp) == 0 &&
139 		    mntent.mnt_fstype != NULL &&
140 		    strcmp(mntent.mnt_fstype, MNTTYPE_UFS) == 0) {
141 			found = B_TRUE;
142 			break;
143 		}
144 	}
145 	(void) fclose(file);
146 	return (found);
147 }
148 
149 /*
150  * Find opt in mntopt
151  */
152 static char *
153 findopt(char *mntopt, char *opt)
154 {
155 	int nc, optlen = strlen(opt);
156 
157 	while (*mntopt) {
158 		nc = strcspn(mntopt, ", =");
159 		if (strncmp(mntopt, opt, nc) == 0)
160 			if (optlen == nc)
161 				return (mntopt);
162 		mntopt += nc;
163 		mntopt += strspn(mntopt, ", =");
164 	}
165 	return (NULL);
166 }
167 
168 void
169 main(int argc, char *argv[])
170 {
171 	struct mnttab mnt;
172 	int	c;
173 
174 	(void) setlocale(LC_ALL, "");
175 #if !defined(TEXT_DOMAIN)
176 #define	TEXT_DOMAIN	"SYS_TEST"
177 #endif
178 	(void) textdomain(TEXT_DOMAIN);
179 
180 	myname = strrchr(argv[0], '/');
181 	if (myname)
182 		myname++;
183 	else
184 		myname = argv[0];
185 	(void) snprintf(typename, sizeof (typename), "%s %s", fstype, myname);
186 	argv[0] = typename;
187 
188 	opts[0] = '\0';
189 
190 	/*
191 	 * Set options
192 	 */
193 	while ((c = getopt(argc, argv, "gmo:pqrVO")) != EOF) {
194 		switch (c) {
195 
196 		case 'g':
197 			gflg++;
198 			break;
199 
200 		case 'o':
201 			if (strlcpy(opts, optarg, sizeof (opts)) >=
202 			    sizeof (opts)) {
203 				(void) fprintf(stderr, gettext("option string "
204 				    "argument too long\n"));
205 			}
206 			break;
207 
208 		case 'O':
209 			Oflg++;
210 			break;
211 
212 		case 'r':
213 			ro++;
214 			break;
215 
216 		case 'm':
217 			mflg++;
218 			break;
219 
220 		case 'q':
221 			qflg++;
222 			break;
223 
224 		default:
225 			usage();
226 		}
227 	}
228 
229 	if ((argc - optind) != 2)
230 		usage();
231 
232 	mnt.mnt_special = argv[optind];
233 	mnt.mnt_mountp = argv[optind+1];
234 	mnt.mnt_fstype = fstype;
235 
236 	/*
237 	 * Process options.  The resulting options string overwrites the
238 	 * original.
239 	 *
240 	 * XXX:	This code doesn't do a good job of resolving options that are
241 	 *	specified multiple times or that are given in conflicting
242 	 *	forms (e.g., both "largefiles" and "nolargefiles").  It also
243 	 *	doesn't produce well defined behavior for options that may
244 	 *	also be specified as flags (e.g, "-r" and "ro"/"rw") when both
245 	 *	are present.
246 	 *
247 	 *	The proper way to deal with such conflicts is to start with
248 	 *	the default value (i.e., the one if no flag or option is
249 	 *	specified), override it with the last mentioned option pair
250 	 *	in the -o option string, and finally, override that with
251 	 *	the flag value. This allows "mount -r" command to mount a
252 	 *	file system read only that is listed rw in /etc/vfstab.
253 	 */
254 	mnt.mnt_mntopts = opts;
255 	if (findopt(mnt.mnt_mntopts, "m"))
256 		mflg++;
257 	if ((gflg || findopt(mnt.mnt_mntopts, MNTOPT_GLOBAL)) &&
258 			findopt(mnt.mnt_mntopts, MNTOPT_NBMAND)) {
259 		(void) fprintf(stderr, gettext("NBMAND option not supported on"
260 						" global filesystem\n"));
261 		exit(32);
262 	}
263 
264 	replace_opts(opts, ro, MNTOPT_RO, MNTOPT_RW);
265 	replace_opts(opts, largefiles, MNTOPT_NOLARGEFILES, MNTOPT_LARGEFILES);
266 	gflg = replace_opts_dflt(opts, gflg, MNTOPT_GLOBAL, MNTOPT_NOGLOBAL);
267 
268 	if (findopt(mnt.mnt_mntopts, MNTOPT_RQ)) {
269 		rmopt(&mnt, MNTOPT_RQ);
270 		replace_opts(opts, 1, MNTOPT_QUOTA, MNTOPT_NOQUOTA);
271 	}
272 
273 	mountfs(&mnt);
274 	/* NOTREACHED */
275 }
276 
277 static void
278 reportlogerror(int ret, char *mp, char *special, char *cmd, fiolog_t *flp)
279 {
280 	/* No error */
281 	if ((ret != -1) && (flp->error == FIOLOG_ENONE))
282 		return;
283 
284 	/* logging was not enabled/disabled */
285 	if (ret == -1 || flp->error != FIOLOG_ENONE)
286 		(void) fprintf(stderr, gettext("Could not %s logging"
287 				" for %s on %s.\n"), cmd, mp, special);
288 
289 	/* ioctl returned error */
290 	if (ret == -1)
291 		return;
292 
293 	/* Some more info */
294 	switch (flp->error) {
295 	case FIOLOG_ENONE :
296 		if (flp->nbytes_requested &&
297 		    (flp->nbytes_requested != flp->nbytes_actual)) {
298 		    (void) fprintf(stderr, gettext("The log has been resized"
299 					" from %d bytes to %d bytes.\n"),
300 					flp->nbytes_requested,
301 					flp->nbytes_actual);
302 		}
303 		return;
304 	case FIOLOG_ETRANS :
305 		(void) fprintf(stderr, gettext("Solaris Volume Manager logging"
306 				" is already enabled.\n"));
307 		(void) fprintf(stderr, gettext("Please see the"
308 				" commands metadetach(1M)"
309 				" or metaclear(1M).\n"));
310 		break;
311 	case FIOLOG_EROFS :
312 		(void) fprintf(stderr, gettext("File system is mounted read "
313 						"only.\n"));
314 		(void) fprintf(stderr, gettext("Please see the remount "
315 				"option described in mount_ufs(1M).\n"));
316 		break;
317 	case FIOLOG_EULOCK :
318 		(void) fprintf(stderr, gettext("File system is locked.\n"));
319 		(void) fprintf(stderr, gettext("Please see the -u option "
320 						"described in lockfs(1M).\n"));
321 		break;
322 	case FIOLOG_EWLOCK :
323 		(void) fprintf(stderr, gettext("The file system could not be"
324 					" write locked.\n"));
325 		(void) fprintf(stderr, gettext("Please see the -w option "
326 						"described in lockfs(1M).\n"));
327 		break;
328 	case FIOLOG_ECLEAN :
329 		(void) fprintf(stderr, gettext("The file system may not be"
330 					" stable.\n"));
331 		(void) fprintf(stderr, gettext("Please see the -n option"
332 				" for fsck(1M).\n"));
333 		break;
334 	case FIOLOG_ENOULOCK :
335 		(void) fprintf(stderr, gettext("The file system could not be"
336 					" unlocked.\n"));
337 		(void) fprintf(stderr, gettext("Please see the -u option "
338 						"described in lockfs(1M).\n"));
339 		break;
340 	default :
341 		(void) fprintf(stderr, gettext("Unknown internal error"
342 					" %d.\n"), flp->error);
343 		break;
344 	}
345 }
346 
347 static int
348 checkislog(char *mp)
349 {
350 	int fd;
351 	uint32_t islog;
352 
353 	fd = open(mp, O_RDONLY);
354 	islog = 0;
355 	(void) ioctl(fd, _FIOISLOG, &islog);
356 	(void) close(fd);
357 	return ((int)islog);
358 }
359 
360 static void
361 enable_logging(char *mp, char *special)
362 {
363 	int fd, ret, islog;
364 	fiolog_t fl;
365 
366 	fd = open(mp, O_RDONLY);
367 	if (fd == -1) {
368 		perror(mp);
369 		return;
370 	}
371 	fl.nbytes_requested = 0;
372 	fl.nbytes_actual = 0;
373 	fl.error = FIOLOG_ENONE;
374 	ret = ioctl(fd, _FIOLOGENABLE, &fl);
375 	if (ret == -1)
376 		perror(mp);
377 	(void) close(fd);
378 
379 	/* is logging enabled? */
380 	islog = checkislog(mp);
381 
382 	/* report errors, if any */
383 	if (ret == -1 || !islog)
384 		reportlogerror(ret, mp, special, "enable", &fl);
385 }
386 
387 static void
388 disable_logging(char *mp, char *special)
389 {
390 	int fd, ret, islog;
391 	fiolog_t fl;
392 
393 	fd = open(mp, O_RDONLY);
394 	if (fd == -1) {
395 		perror(mp);
396 		return;
397 	}
398 	fl.error = FIOLOG_ENONE;
399 	ret = ioctl(fd, _FIOLOGDISABLE, &fl);
400 	if (ret == -1)
401 		perror(mp);
402 	(void) close(fd);
403 
404 	/* is logging enabled? */
405 	islog = checkislog(mp);
406 
407 	/* report errors, if any */
408 	if (ret == -1 || islog)
409 		reportlogerror(ret, mp, special, "disable", &fl);
410 }
411 
412 
413 /*
414  * attempt to mount file system, return errno or 0
415  */
416 void
417 mountfs(struct mnttab *mnt)
418 {
419 	char			 opt[MAX_MNTOPT_STR];
420 	char			 opt2[MAX_MNTOPT_STR];
421 	char			*opts =	opt;
422 	int			 flags = MS_OPTIONSTR;
423 	struct ufs_args		 args;
424 	int			 need_separator = 0;
425 	int			mount_attempts = 5;
426 
427 	(void) bzero((char *)&args, sizeof (args));
428 	(void) strcpy(opts, mnt->mnt_mntopts);
429 	opt2[0] = '\0';
430 
431 	flags |= Oflg ? MS_OVERLAY : 0;
432 	flags |= eatmntopt(mnt, MNTOPT_RO) ? MS_RDONLY : 0;
433 	flags |= eatmntopt(mnt, MNTOPT_REMOUNT) ? MS_REMOUNT : 0;
434 	flags |= eatmntopt(mnt, MNTOPT_GLOBAL) ? MS_GLOBAL : 0;
435 
436 	if (eatmntopt(mnt, MNTOPT_NOINTR))
437 		args.flags |= UFSMNT_NOINTR;
438 	if (eatmntopt(mnt, MNTOPT_INTR))
439 		args.flags &= ~UFSMNT_NOINTR;
440 	if (eatmntopt(mnt, MNTOPT_SYNCDIR))
441 		args.flags |= UFSMNT_SYNCDIR;
442 	if (eatmntopt(mnt, MNTOPT_FORCEDIRECTIO)) {
443 		args.flags |= UFSMNT_FORCEDIRECTIO;
444 		args.flags &= ~UFSMNT_NOFORCEDIRECTIO;
445 	}
446 	if (eatmntopt(mnt, MNTOPT_NOFORCEDIRECTIO)) {
447 		args.flags |= UFSMNT_NOFORCEDIRECTIO;
448 		args.flags &= ~UFSMNT_FORCEDIRECTIO;
449 	}
450 	if (eatmntopt(mnt, MNTOPT_NOSETSEC))
451 		args.flags |= UFSMNT_NOSETSEC;
452 	if (eatmntopt(mnt, MNTOPT_LARGEFILES))
453 		args.flags |= UFSMNT_LARGEFILES;
454 	if (eatmntopt(mnt, MNTOPT_NOLARGEFILES))
455 		args.flags &= ~UFSMNT_LARGEFILES;
456 	args.flags |= UFSMNT_LOGGING;	/* default is logging */
457 	(void) eatmntopt(mnt, MNTOPT_LOGGING);
458 	if (eatmntopt(mnt, MNTOPT_NOLOGGING))
459 		args.flags &= ~UFSMNT_LOGGING;
460 	if (eatmntopt(mnt, MNTOPT_NOATIME))
461 		args.flags |= UFSMNT_NOATIME;
462 	if (eatmntopt(mnt, MNTOPT_DFRATIME))
463 		args.flags &= ~UFSMNT_NODFRATIME;
464 	if (eatmntopt(mnt, MNTOPT_NODFRATIME))
465 		args.flags |= UFSMNT_NODFRATIME;
466 
467 	while (*opts != '\0') {
468 		char	*argval;
469 
470 		switch (getsubopt(&opts, fop_subopts, &argval)) {
471 		case ONERROR:
472 			if (argval) {
473 				struct fop_subopt	*s;
474 				int			 found = 0;
475 
476 				for (s = fop_subopt_list;
477 				    s->str && !found;
478 				    s++) {
479 					if (strcmp(argval, s->str) == 0) {
480 						args.flags |= s->flag;
481 						found = 1;
482 					}
483 				}
484 				if (!found) {
485 					usage();
486 				}
487 
488 				if (need_separator)
489 					(void) strcat(opt2, ",");
490 				(void) strcat(opt2, MNTOPT_ONERROR);
491 				(void) strcat(opt2, "=");
492 				(void) strcat(opt2, argval);
493 				need_separator = 1;
494 
495 			} else {
496 				args.flags |= UFSMNT_ONERROR_DEFAULT;
497 			}
498 			break;
499 
500 		case NOMATCH:
501 		default:
502 			if (argval) {
503 				if (need_separator)
504 					(void) strcat(opt2, ",");
505 				(void) strcat(opt2, argval);
506 				need_separator = 1;
507 			}
508 			break;
509 
510 		}
511 	}
512 
513 	if (*opt2 != '\0')
514 		(void) strcpy(opt, opt2);
515 	opts = opt;
516 	if ((args.flags & UFSMNT_ONERROR_FLGMASK) == 0)
517 		args.flags |= UFSMNT_ONERROR_DEFAULT;
518 
519 	(void) signal(SIGHUP,  SIG_IGN);
520 	(void) signal(SIGQUIT, SIG_IGN);
521 	(void) signal(SIGINT,  SIG_IGN);
522 
523 	errno = 0;
524 	flags |= MS_DATA | MS_OPTIONSTR;
525 	if (mflg)
526 		flags |= MS_NOMNTTAB;
527 	if (flags & MS_REMOUNT) {
528 		replace_opts(mnt->mnt_mntopts, 1, MNTOPT_RW, MNTOPT_RO);
529 	}
530 	fixopts(mnt, opts);
531 
532 	/*
533 	 * For global filesystems we want to pass in logging option
534 	 * so that it shows up in the mnttab of all nodes. We add
535 	 * logging option if its not specified.
536 	 */
537 	if (gflg || findopt(mnt->mnt_mntopts, MNTOPT_GLOBAL)) {
538 		if (mnt->mnt_mntopts != '\0')
539 			(void) strcat(mnt->mnt_mntopts, ",");
540 		(void) strcat(mnt->mnt_mntopts, MNTOPT_LOGGING);
541 		args.flags |= UFSMNT_LOGGING;
542 	}
543 
544 again:	if (mount(mnt->mnt_special, mnt->mnt_mountp, flags, fstype,
545 		&args, sizeof (args), mnt->mnt_mntopts, MAX_MNTOPT_STR) != 0) {
546 		if (errno == EBUSY && !(flags & MS_OVERLAY)) {
547 			/*
548 			 * Because of bug 6176743, any attempt to mount any
549 			 * filesystem could fail for reasons described in that
550 			 * bug.  We're trying to detect that situation here by
551 			 * checking that the filesystem we're mounting is not
552 			 * in /etc/mnttab yet.  When that bug is fixed, this
553 			 * code can be removed.
554 			 */
555 			if (!in_mnttab(mnt->mnt_mountp) &&
556 			    mount_attempts-- > 0) {
557 				(void) poll(NULL, 0, 50);
558 				goto again;
559 			}
560 		}
561 		rpterr(mnt->mnt_special, mnt->mnt_mountp);
562 		exit(32);
563 	}
564 
565 	if (!(flags & MS_RDONLY)) {
566 		if (args.flags & UFSMNT_LOGGING)
567 			enable_logging(mnt->mnt_mountp, mnt->mnt_special);
568 		else
569 			disable_logging(mnt->mnt_mountp, mnt->mnt_special);
570 	}
571 
572 	if (!qflg) {
573 		cmp_requested_to_actual_options(opts, mnt->mnt_mntopts,
574 		    mnt->mnt_special, mnt->mnt_mountp);
575 	}
576 
577 	if (checkislog(mnt->mnt_mountp)) {
578 		/* update mnttab file if necessary */
579 		if (!mflg) {
580 			struct stat64 statb;
581 			struct mnttagdesc mtdesc;
582 			int fd;
583 
584 			if (stat64(mnt->mnt_mountp, &statb) != 0)
585 				exit(32);
586 			/* do tag ioctl */
587 			mtdesc.mtd_major = major(statb.st_dev);
588 			mtdesc.mtd_minor = minor(statb.st_dev);
589 			mtdesc.mtd_mntpt = mnt->mnt_mountp;
590 			mtdesc.mtd_tag = MNTOPT_LOGGING;
591 			if ((fd = open(MNTTAB, O_RDONLY, 0)) < 0)
592 				exit(32);
593 			if (ioctl(fd, MNTIOC_SETTAG, &mtdesc) != 0) {
594 				(void) close(fd);
595 				exit(32);
596 			}
597 			(void) close(fd);
598 		}
599 	}
600 	exit(0);
601 }
602 
603 /*
604  * same as findopt but remove the option from the option string and return
605  * true or false
606  */
607 static int
608 eatmntopt(struct mnttab *mnt, char *opt)
609 {
610 	int has;
611 
612 	has = (findopt(mnt->mnt_mntopts, opt) != NULL);
613 	rmopt(mnt, opt);
614 	return (has);
615 }
616 
617 /*
618  * remove an option string from the option list
619  */
620 static void
621 rmopt(struct mnttab *mnt, char *opt)
622 {
623 	char *str;
624 	char *optstart;
625 
626 	while (optstart = findopt(mnt->mnt_mntopts, opt)) {
627 		for (str = optstart;
628 		    *str != ','	&& *str != '\0' && *str != ' ';
629 		    str++)
630 			/* NULL */;
631 		if (*str == ',') {
632 			str++;
633 		} else if (optstart != mnt->mnt_mntopts) {
634 			optstart--;
635 		}
636 		while (*optstart++ = *str++)
637 			;
638 	}
639 }
640 
641 /*
642  * mnt->mnt_ops has un-eaten opts, opts is the original opts list.
643  * Set mnt->mnt_opts to the original, the kernel will then remove
644  * the ones it cannot deal with.
645  * Set "opts" to the the original options for later comparison in
646  * cmp_....().  But strip the options which aren't returned by
647  * the kernel: "noglobal", "global" and "quota".
648  * And strip the options which aren't set through mount: "logging",
649  * "nologging" from those passed to mount(2).
650  */
651 static void
652 fixopts(struct mnttab *mnt, char *opts)
653 {
654 	struct mnttab omnt;
655 
656 	omnt.mnt_mntopts = opts;
657 
658 	/*
659 	 * Options not passed to the kernel and possibly not returned;
660 	 * these are dealt with using ioctl; and the ioctl may fail.
661 	 */
662 	rmopt(&omnt, MNTOPT_LOGGING);
663 	rmopt(&omnt, MNTOPT_NOLOGGING);
664 
665 	/*
666 	 * Set the options for ``/etc/mnttab'' to be the original
667 	 * options from main(); except for the option "f" and "remount".
668 	 */
669 	(void) strlcpy(mnt->mnt_mntopts, opts, MAX_MNTOPT_STR);
670 	rmopt(mnt, "f");
671 	rmopt(mnt, MNTOPT_REMOUNT);
672 
673 	rmopt(&omnt, MNTOPT_GLOBAL);
674 	rmopt(&omnt, MNTOPT_NOGLOBAL);
675 	rmopt(&omnt, MNTOPT_QUOTA);
676 }
677 
678 static void
679 usage(void)
680 {
681 	(void) fprintf(stdout, gettext(
682 "ufs usage:\n"
683 "mount [-F ufs] [generic options] [-o suboptions] {special | mount_point}\n"));
684 	(void) fprintf(stdout, gettext(
685 			"\tsuboptions are: \n"
686 			"\t	ro,rw,nosuid,remount,f,m,\n"
687 			"\t	global,noglobal,\n"
688 			"\t	largefiles,nolargefiles,\n"
689 			"\t	forcedirectio,noforcedirectio\n"
690 			"\t	logging,nologging,\n"
691 			"\t	nbmand,nonbmand,\n"
692 			"\t	onerror[={panic | lock | umount}]\n"));
693 
694 	exit(32);
695 }
696 
697 /*
698  * Returns the next option in the option string.
699  */
700 static char *
701 getnextopt(char **p)
702 {
703 	char *cp = *p;
704 	char *retstr;
705 
706 	while (*cp && isspace(*cp))
707 		cp++;
708 	retstr = cp;
709 	while (*cp && *cp != ',')
710 		cp++;
711 	/* strip empty options */
712 	while (*cp == ',') {
713 		*cp = '\0';
714 		cp++;
715 	}
716 	*p = cp;
717 	return (retstr);
718 }
719 
720 /*
721  * "trueopt" and "falseopt" are two settings of a Boolean option.
722  * If "flag" is true, forcibly set the option to the "true" setting; otherwise,
723  * if the option isn't present, set it to the false setting.
724  */
725 static void
726 replace_opts(char *options, int flag, char *trueopt, char *falseopt)
727 {
728 	char *f;
729 	char *tmpoptsp;
730 	int found;
731 	char tmptopts[MNTMAXSTR];
732 
733 	(void) strcpy(tmptopts, options);
734 	tmpoptsp = tmptopts;
735 	(void) strcpy(options, "");
736 
737 	found = 0;
738 	for (f = getnextopt(&tmpoptsp); *f; f = getnextopt(&tmpoptsp)) {
739 		if (options[0] != '\0')
740 			(void) strcat(options, ",");
741 		if (strcmp(f, trueopt) == 0) {
742 			(void) strcat(options, f);
743 			found++;
744 		} else if (strcmp(f, falseopt) == 0) {
745 			if (flag)
746 				(void) strcat(options, trueopt);
747 			else
748 				(void) strcat(options, f);
749 			found++;
750 		} else
751 			(void) strcat(options, f);
752 	}
753 	if (!found) {
754 		if (options[0] != '\0')
755 			(void) strcat(options, ",");
756 		(void) strcat(options, flag ? trueopt : falseopt);
757 	}
758 }
759 
760 /*
761  * "trueopt" and "falseopt" are two settings of a Boolean option and "dflt" is
762  * a default value for the option.  Rewrite the contents of options to include
763  * only the last mentioned occurrence of trueopt and falseopt.  If neither is
764  * mentioned, append one or the other to options, according to the value of
765  * dflt.  Return the resulting value of the option in boolean form.
766  *
767  * Note that the routine is implemented to have the resulting occurrence of
768  * trueopt or falseopt appear at the end of the resulting option string.
769  *
770  * N.B.	This routine should take the place of replace_opts, but there are
771  *	probably some compatibility issues to resolve before doing so.  It
772  *	should certainly be used to handle new options that don't have
773  *	compatibility issues.
774  */
775 static int
776 replace_opts_dflt(
777 	char *options,
778 	int dflt,
779 	const char *trueopt,
780 	const char *falseopt)
781 {
782 	char *f;
783 	char *tmpoptsp;
784 	int last;
785 	char tmptopts[MNTMAXSTR];
786 
787 	/*
788 	 * Transfer the contents of options to tmptopts, in anticipation of
789 	 * copying a subset of the contents back to options.
790 	 */
791 	(void) strcpy(tmptopts, options);
792 	tmpoptsp = tmptopts;
793 	(void) strcpy(options, "");
794 
795 	/*
796 	 * Loop over each option value, copying non-matching values back into
797 	 * options and updating the last seen occurrence of trueopt or
798 	 * falseopt.
799 	 */
800 	last = dflt;
801 	for (f = getnextopt(&tmpoptsp); *f; f = getnextopt(&tmpoptsp)) {
802 		/* Check for both forms of the option of interest. */
803 		if (strcmp(f, trueopt) == 0) {
804 			last = 1;
805 		} else if (strcmp(f, falseopt) == 0) {
806 			last = 0;
807 		} else {
808 			/* Not what we're looking for; transcribe. */
809 			if (options[0] != '\0')
810 				(void) strcat(options, ",");
811 			(void) strcat(options, f);
812 		}
813 	}
814 
815 	/*
816 	 * Transcribe the correct form of the option of interest, using the
817 	 * default value if it wasn't overwritten above.
818 	 */
819 	if (options[0] != '\0')
820 		(void) strcat(options, ",");
821 	(void) strcat(options, last ? trueopt : falseopt);
822 
823 	return (last);
824 }
825 
826 static void
827 rpterr(char *bs, char *mp)
828 {
829 	switch (errno) {
830 	case EPERM:
831 		(void) fprintf(stderr, gettext("%s: Insufficient privileges\n"),
832 		    myname);
833 		break;
834 	case ENXIO:
835 		(void) fprintf(stderr, gettext("%s: %s no such device\n"),
836 								myname, bs);
837 		break;
838 	case ENOTDIR:
839 		(void) fprintf(stderr,
840 			gettext(
841 	"%s: %s not a directory\n\tor a component of %s is not a directory\n"),
842 			myname, mp, bs);
843 		break;
844 	case ENOENT:
845 		(void) fprintf(stderr, gettext(
846 			"%s: %s or %s, no such file or directory\n"),
847 			myname, bs, mp);
848 		break;
849 	case EINVAL:
850 		(void) fprintf(stderr, gettext("%s: %s is not this fstype\n"),
851 			myname, bs);
852 		break;
853 	case EBUSY:
854 		(void) fprintf(stderr,
855 		    gettext("%s: %s is already mounted or %s is busy\n"),
856 		    myname, bs, mp);
857 		break;
858 	case ENOTBLK:
859 		(void) fprintf(stderr, gettext(
860 			"%s: %s not a block device\n"), myname, bs);
861 		break;
862 	case EROFS:
863 		(void) fprintf(stderr, gettext("%s: %s write-protected\n"),
864 			myname, bs);
865 		break;
866 	case ENOSPC:
867 		(void) fprintf(stderr, gettext(
868 			"%s: The state of %s is not okay\n"
869 			"\tand it was attempted to be mounted read/write\n"),
870 			myname, bs);
871 		(void) printf(gettext(
872 			"mount: Please run fsck and try again\n"));
873 		break;
874 	case EFBIG:
875 		(void) fprintf(stderr, gettext(
876 			"%s: Large files may be present on %s,\n"
877 			"\tand it was attempted to be mounted nolargefiles\n"),
878 		    myname, bs);
879 		break;
880 	default:
881 		perror(myname);
882 		(void) fprintf(stderr, gettext("%s: Cannot mount %s\n"),
883 				myname, bs);
884 	}
885 }
886