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