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