vfs_mount.c revision 215747
16059Samurai/*-
26059Samurai * Copyright (c) 1999-2004 Poul-Henning Kamp
36059Samurai * Copyright (c) 1999 Michael Smith
46059Samurai * Copyright (c) 1989, 1993
56059Samurai *	The Regents of the University of California.  All rights reserved.
66059Samurai * (c) UNIX System Laboratories, Inc.
76059Samurai * All or some portions of this file are derived from material licensed
86059Samurai * to the University of California by American Telephone and Telegraph
96059Samurai * Co. or Unix System Laboratories, Inc. and are reproduced herein with
106059Samurai * the permission of UNIX System Laboratories, Inc.
116059Samurai *
126059Samurai * Redistribution and use in source and binary forms, with or without
136059Samurai * modification, are permitted provided that the following conditions
146059Samurai * are met:
156059Samurai * 1. Redistributions of source code must retain the above copyright
166059Samurai *    notice, this list of conditions and the following disclaimer.
176059Samurai * 2. Redistributions in binary form must reproduce the above copyright
186059Samurai *    notice, this list of conditions and the following disclaimer in the
198857Srgrimes *    documentation and/or other materials provided with the distribution.
2037191Sbrian * 4. Neither the name of the University nor the names of its contributors
218857Srgrimes *    may be used to endorse or promote products derived from this software
226059Samurai *    without specific prior written permission.
2336285Sbrian *
2430715Sbrian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2526031Sbrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2630715Sbrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2726031Sbrian * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2830715Sbrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2926031Sbrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3030715Sbrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3136285Sbrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3230715Sbrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3331343Sbrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3426031Sbrian * SUCH DAMAGE.
3531343Sbrian */
3630715Sbrian
3726516Sbrian#include <sys/cdefs.h>
3830715Sbrian__FBSDID("$FreeBSD: head/sys/kern/vfs_mount.c 215747 2010-11-23 13:49:15Z pluknet $");
3936285Sbrian
4030715Sbrian#include <sys/param.h>
4130715Sbrian#include <sys/conf.h>
4230715Sbrian#include <sys/fcntl.h>
4330715Sbrian#include <sys/jail.h>
4430715Sbrian#include <sys/kernel.h>
4530715Sbrian#include <sys/libkern.h>
4630715Sbrian#include <sys/malloc.h>
4737009Sbrian#include <sys/mount.h>
4831343Sbrian#include <sys/mutex.h>
4930715Sbrian#include <sys/namei.h>
5030715Sbrian#include <sys/priv.h>
5130715Sbrian#include <sys/proc.h>
526059Samurai#include <sys/filedesc.h>
536059Samurai#include <sys/reboot.h>
5431690Sbrian#include <sys/syscallsubr.h>
5536285Sbrian#include <sys/sysproto.h>
5636285Sbrian#include <sys/sx.h>
576059Samurai#include <sys/sysctl.h>
586059Samurai#include <sys/sysent.h>
5931343Sbrian#include <sys/systm.h>
6026031Sbrian#include <sys/vnode.h>
6131343Sbrian#include <vm/uma.h>
6236285Sbrian
636059Samurai#include <geom/geom.h>
6426142Sbrian
6525630Sbrian#include <machine/stdarg.h>
6636285Sbrian
6736285Sbrian#include <security/audit/audit.h>
6830715Sbrian#include <security/mac/mac_framework.h>
6930715Sbrian
7030715Sbrian#define	VFS_MOUNTARG_SIZE_MAX	(1024 * 64)
7131080Sbrian
7236285Sbrianstatic int	vfs_domount(struct thread *td, const char *fstype,
7336285Sbrian		    char *fspath, int fsflags, void *fsdata);
7436285Sbrianstatic void	free_mntarg(struct mntarg *ma);
7536285Sbrian
7636285Sbrianstatic int	usermount = 0;
7736285SbrianSYSCTL_INT(_vfs, OID_AUTO, usermount, CTLFLAG_RW, &usermount, 0,
7836285Sbrian    "Unprivileged users may mount and unmount file systems");
7936285Sbrian
8036285SbrianMALLOC_DEFINE(M_MOUNT, "mount", "vfs mount structure");
8136285SbrianMALLOC_DEFINE(M_VNODE_MARKER, "vnodemarker", "vnode marker");
826059Samuraistatic uma_zone_t mount_zone;
8336285Sbrian
8436285Sbrian/* List of mounted filesystems. */
8536285Sbrianstruct mntlist mountlist = TAILQ_HEAD_INITIALIZER(mountlist);
8636285Sbrian
8736285Sbrian/* For any iteration/modification of mountlist */
8836285Sbrianstruct mtx mountlist_mtx;
8936285SbrianMTX_SYSINIT(mountlist, &mountlist_mtx, "mountlist", MTX_DEF);
9036285Sbrian
9136285Sbrian/*
9236285Sbrian * Global opts, taken by all filesystems
9336285Sbrian */
9436285Sbrianstatic const char *global_opts[] = {
9536285Sbrian	"errmsg",
9636285Sbrian	"fstype",
9736285Sbrian	"fspath",
9836285Sbrian	"ro",
9936285Sbrian	"rw",
10036285Sbrian	"nosuid",
10136285Sbrian	"noexec",
10236285Sbrian	NULL
10336285Sbrian};
10436285Sbrian
10536285Sbrianstatic int
10636285Sbrianmount_init(void *mem, int size, int flags)
10736285Sbrian{
1086059Samurai	struct mount *mp;
10936285Sbrian
11036285Sbrian	mp = (struct mount *)mem;
11136285Sbrian	mtx_init(&mp->mnt_mtx, "struct mount mtx", NULL, MTX_DEF);
11236285Sbrian	lockinit(&mp->mnt_explock, PVFS, "explock", 0, 0);
11336285Sbrian	return (0);
11436285Sbrian}
11536285Sbrian
11636285Sbrianstatic void
11736285Sbrianmount_fini(void *mem, int size)
11836285Sbrian{
11936285Sbrian	struct mount *mp;
12036285Sbrian
12136285Sbrian	mp = (struct mount *)mem;
12236285Sbrian	lockdestroy(&mp->mnt_explock);
12336285Sbrian	mtx_destroy(&mp->mnt_mtx);
12436285Sbrian}
12536285Sbrian
12636285Sbrianstatic void
12737191Sbrianvfs_mount_init(void *dummy __unused)
12836285Sbrian{
12936285Sbrian
13036285Sbrian	mount_zone = uma_zcreate("Mountpoints", sizeof(struct mount), NULL,
13136285Sbrian	    NULL, mount_init, mount_fini, UMA_ALIGN_PTR, UMA_ZONE_NOFREE);
13236285Sbrian}
13336285SbrianSYSINIT(vfs_mount, SI_SUB_VFS, SI_ORDER_ANY, vfs_mount_init, NULL);
13436285Sbrian
13536285Sbrian/*
13636285Sbrian * ---------------------------------------------------------------------
13736285Sbrian * Functions for building and sanitizing the mount options
13836285Sbrian */
13936285Sbrian
14036285Sbrian/* Remove one mount option. */
14136934Sbrianstatic void
14231343Sbrianvfs_freeopt(struct vfsoptlist *opts, struct vfsopt *opt)
14336285Sbrian{
14436285Sbrian
14536285Sbrian	TAILQ_REMOVE(opts, opt, link);
14631343Sbrian	free(opt->name, M_MOUNT);
1476059Samurai	if (opt->value != NULL)
14836285Sbrian		free(opt->value, M_MOUNT);
14936285Sbrian	free(opt, M_MOUNT);
15036285Sbrian}
15136285Sbrian
15236285Sbrian/* Release all resources related to the mount options. */
15336285Sbrianvoid
15436285Sbrianvfs_freeopts(struct vfsoptlist *opts)
15536285Sbrian{
15636285Sbrian	struct vfsopt *opt;
15736285Sbrian
15836285Sbrian	while (!TAILQ_EMPTY(opts)) {
1596059Samurai		opt = TAILQ_FIRST(opts);
16031343Sbrian		vfs_freeopt(opts, opt);
1616059Samurai	}
16228679Sbrian	free(opts, M_MOUNT);
16336285Sbrian}
16436285Sbrian
1656059Samuraivoid
16636285Sbrianvfs_deleteopt(struct vfsoptlist *opts, const char *name)
16736285Sbrian{
16826516Sbrian	struct vfsopt *opt, *temp;
16936285Sbrian
17026516Sbrian	if (opts == NULL)
17136285Sbrian		return;
17236285Sbrian	TAILQ_FOREACH_SAFE(opt, opts, link, temp)  {
17336285Sbrian		if (strcmp(opt->name, name) == 0)
17436285Sbrian			vfs_freeopt(opts, opt);
17536285Sbrian	}
17636285Sbrian}
17728679Sbrian
1786059Samurai/*
17926516Sbrian * Check if options are equal (with or without the "no" prefix).
1806059Samurai */
18136285Sbrianstatic int
18231372Sbrianvfs_equalopts(const char *opt1, const char *opt2)
18336285Sbrian{
18436285Sbrian	char *p;
18536285Sbrian
18631372Sbrian	/* "opt" vs. "opt" or "noopt" vs. "noopt" */
18731372Sbrian	if (strcmp(opt1, opt2) == 0)
18831372Sbrian		return (1);
18931372Sbrian	/* "noopt" vs. "opt" */
19031372Sbrian	if (strncmp(opt1, "no", 2) == 0 && strcmp(opt1 + 2, opt2) == 0)
19131372Sbrian		return (1);
1926059Samurai	/* "opt" vs. "noopt" */
19336285Sbrian	if (strncmp(opt2, "no", 2) == 0 && strcmp(opt1, opt2 + 2) == 0)
19436285Sbrian		return (1);
19536285Sbrian	while ((p = strchr(opt1, '.')) != NULL &&
19636285Sbrian	    !strncmp(opt1, opt2, ++p - opt1)) {
19736285Sbrian		opt2 += p - opt1;
19836285Sbrian		opt1 = p;
19936285Sbrian		/* "foo.noopt" vs. "foo.opt" */
20036285Sbrian		if (strncmp(opt1, "no", 2) == 0 && strcmp(opt1 + 2, opt2) == 0)
20131372Sbrian			return (1);
20236285Sbrian		/* "foo.opt" vs. "foo.noopt" */
2036059Samurai		if (strncmp(opt2, "no", 2) == 0 && strcmp(opt1, opt2 + 2) == 0)
20431372Sbrian			return (1);
20536285Sbrian	}
20626516Sbrian	return (0);
20726516Sbrian}
2086059Samurai
2096059Samurai/*
21036285Sbrian * If a mount option is specified several times,
21136285Sbrian * (with or without the "no" prefix) only keep
2126059Samurai * the last occurence of it.
21336285Sbrian */
21436285Sbrianstatic void
21536285Sbrianvfs_sanitizeopts(struct vfsoptlist *opts)
2166059Samurai{
21736285Sbrian	struct vfsopt *opt, *opt2, *tmp;
21836285Sbrian
21936285Sbrian	TAILQ_FOREACH_REVERSE(opt, opts, vfsoptlist, link) {
22036285Sbrian		opt2 = TAILQ_PREV(opt, vfsoptlist, link);
22136285Sbrian		while (opt2 != NULL) {
22236285Sbrian			if (vfs_equalopts(opt->name, opt2->name)) {
22336285Sbrian				tmp = TAILQ_PREV(opt2, vfsoptlist, link);
22436285Sbrian				vfs_freeopt(opts, opt2);
2256059Samurai				opt2 = tmp;
22636285Sbrian			} else {
22736285Sbrian				opt2 = TAILQ_PREV(opt2, vfsoptlist, link);
2286059Samurai			}
2296059Samurai		}
2306059Samurai	}
23136285Sbrian}
2326059Samurai
23336285Sbrian/*
23436285Sbrian * Build a linked list of mount options from a struct uio.
23511336Samurai */
23636285Sbrianint
23736285Sbrianvfs_buildopts(struct uio *auio, struct vfsoptlist **options)
23836285Sbrian{
2396059Samurai	struct vfsoptlist *opts;
24026516Sbrian	struct vfsopt *opt;
24136285Sbrian	size_t memused, namelen, optlen;
24236285Sbrian	unsigned int i, iovcnt;
24336285Sbrian	int error;
24432711Sbrian
24536285Sbrian	opts = malloc(sizeof(struct vfsoptlist), M_MOUNT, M_WAITOK);
24636285Sbrian	TAILQ_INIT(opts);
24736285Sbrian	memused = 0;
24836285Sbrian	iovcnt = auio->uio_iovcnt;
24936285Sbrian	for (i = 0; i < iovcnt; i += 2) {
25031121Sbrian		namelen = auio->uio_iov[i].iov_len;
25136285Sbrian		optlen = auio->uio_iov[i + 1].iov_len;
25236285Sbrian		memused += sizeof(struct vfsopt) + optlen + namelen;
25336285Sbrian		/*
25436285Sbrian		 * Avoid consuming too much memory, and attempts to overflow
25536285Sbrian		 * memused.
25636285Sbrian		 */
25736285Sbrian		if (memused > VFS_MOUNTARG_SIZE_MAX ||
25836285Sbrian		    optlen > VFS_MOUNTARG_SIZE_MAX ||
25936285Sbrian		    namelen > VFS_MOUNTARG_SIZE_MAX) {
26036285Sbrian			error = EINVAL;
26136285Sbrian			goto bad;
26236285Sbrian		}
26336285Sbrian
26436285Sbrian		opt = malloc(sizeof(struct vfsopt), M_MOUNT, M_WAITOK);
26536285Sbrian		opt->name = malloc(namelen, M_MOUNT, M_WAITOK);
26636285Sbrian		opt->value = NULL;
26736285Sbrian		opt->len = 0;
26836285Sbrian		opt->pos = i / 2;
26936928Sbrian		opt->seen = 0;
27037019Sbrian
27136285Sbrian		/*
27236285Sbrian		 * Do this early, so jumps to "bad" will free the current
27336285Sbrian		 * option.
27436285Sbrian		 */
27536285Sbrian		TAILQ_INSERT_TAIL(opts, opt, link);
27636285Sbrian
27736285Sbrian		if (auio->uio_segflg == UIO_SYSSPACE) {
27837008Sbrian			bcopy(auio->uio_iov[i].iov_base, opt->name, namelen);
27936285Sbrian		} else {
28036285Sbrian			error = copyin(auio->uio_iov[i].iov_base, opt->name,
28136285Sbrian			    namelen);
28232403Sbrian			if (error)
28336285Sbrian				goto bad;
28436285Sbrian		}
28526516Sbrian		/* Ensure names are null-terminated strings. */
2866059Samurai		if (namelen == 0 || opt->name[namelen - 1] != '\0') {
2876059Samurai			error = EINVAL;
28836285Sbrian			goto bad;
28936285Sbrian		}
29036285Sbrian		if (optlen != 0) {
29136285Sbrian			opt->len = optlen;
29236285Sbrian			opt->value = malloc(optlen, M_MOUNT, M_WAITOK);
29336285Sbrian			if (auio->uio_segflg == UIO_SYSSPACE) {
29436285Sbrian				bcopy(auio->uio_iov[i + 1].iov_base, opt->value,
29510528Samurai				    optlen);
29636285Sbrian			} else {
29728536Sbrian				error = copyin(auio->uio_iov[i + 1].iov_base,
29836285Sbrian				    opt->value, optlen);
29936285Sbrian				if (error)
30036465Sbrian					goto bad;
30136465Sbrian			}
30236928Sbrian		}
30336285Sbrian	}
30436285Sbrian	vfs_sanitizeopts(opts);
30536285Sbrian	*options = opts;
30634536Sbrian	return (0);
30736285Sbrianbad:
30836285Sbrian	vfs_freeopts(opts);
30936285Sbrian	return (error);
31036285Sbrian}
31136285Sbrian
31236285Sbrian/*
31336285Sbrian * Merge the old mount options with the new ones passed
31428536Sbrian * in the MNT_UPDATE case.
31528536Sbrian *
31628536Sbrian * XXX This function will keep a "nofoo" option in the
31731343Sbrian *     new options if there is no matching "foo" option
31810528Samurai *     to be cancelled in the old options.  This is a bug
31910528Samurai *     if the option's canonical name is "foo".  E.g., "noro"
32010528Samurai *     shouldn't end up in the mount point's active options,
32131343Sbrian *     but it can.
32231343Sbrian */
32320813Sjkhstatic void
32418856Ssosvfs_mergeopts(struct vfsoptlist *toopts, struct vfsoptlist *opts)
32526911Sbrian{
32636285Sbrian	struct vfsopt *opt, *opt2, *new;
32736285Sbrian
32826516Sbrian	TAILQ_FOREACH(opt, opts, link) {
32910528Samurai		/*
33026911Sbrian		 * Check that this option hasn't been redefined
33128679Sbrian		 * nor cancelled with a "no" mount option.
33236285Sbrian		 */
33336285Sbrian		opt2 = TAILQ_FIRST(toopts);
33436285Sbrian		while (opt2 != NULL) {
33536285Sbrian			if (strcmp(opt2->name, opt->name) == 0)
33628381Sbrian				goto next;
33736285Sbrian			if (strncmp(opt2->name, "no", 2) == 0 &&
33836285Sbrian			    strcmp(opt2->name + 2, opt->name) == 0) {
33936285Sbrian				vfs_freeopt(toopts, opt2);
34036285Sbrian				goto next;
34128381Sbrian			}
34236285Sbrian			opt2 = TAILQ_NEXT(opt2, link);
34328679Sbrian		}
34428381Sbrian		/* We want this option, duplicate it. */
34528381Sbrian		new = malloc(sizeof(struct vfsopt), M_MOUNT, M_WAITOK);
34634536Sbrian		new->name = malloc(strlen(opt->name) + 1, M_MOUNT, M_WAITOK);
34734536Sbrian		strcpy(new->name, opt->name);
34828679Sbrian		if (opt->len != 0) {
34936285Sbrian			new->value = malloc(opt->len, M_MOUNT, M_WAITOK);
35018531Sbde			bcopy(opt->value, new->value, opt->len);
35136285Sbrian		} else {
35236285Sbrian			new->value = NULL;
35332017Sbrian		}
35436285Sbrian		new->len = opt->len;
35536285Sbrian		new->seen = opt->seen;
35636285Sbrian		TAILQ_INSERT_TAIL(toopts, new, link);
35736285Sbriannext:
35836285Sbrian		continue;
35936285Sbrian	}
36036285Sbrian}
36128679Sbrian
36228679Sbrian/*
36328679Sbrian * Mount a filesystem.
36428679Sbrian */
36526516Sbrianint
36636285Sbriannmount(td, uap)
36726516Sbrian	struct thread *td;
36831061Sbrian	struct nmount_args /* {
36936285Sbrian		struct iovec *iovp;
37028679Sbrian		unsigned int iovcnt;
37136285Sbrian		int flags;
37236285Sbrian	} */ *uap;
37336285Sbrian{
37436285Sbrian	struct uio *auio;
37536285Sbrian	int error;
37636285Sbrian	u_int iovcnt;
37736285Sbrian
37836285Sbrian	AUDIT_ARG_FFLAGS(uap->flags);
37931343Sbrian	CTR4(KTR_VFS, "%s: iovp %p with iovcnt %d and flags %d", __func__,
38036285Sbrian	    uap->iovp, uap->iovcnt, uap->flags);
38131343Sbrian
38231343Sbrian	/*
38328679Sbrian	 * Filter out MNT_ROOTFS.  We do not want clients of nmount() in
38428679Sbrian	 * userspace to set this flag, but we must filter it out if we want
38510528Samurai	 * MNT_UPDATE on the root file system to work.
38628679Sbrian	 * MNT_ROOTFS should only be set by the kernel when mounting its
38728679Sbrian	 * root file system.
38836832Sbrian	 */
38928679Sbrian	uap->flags &= ~MNT_ROOTFS;
39028679Sbrian
39136285Sbrian	iovcnt = uap->iovcnt;
39236285Sbrian	/*
39331343Sbrian	 * Check that we have an even number of iovec's
39430316Sbrian	 * and that we have at least two options.
39536285Sbrian	 */
39632017Sbrian	if ((iovcnt & 1) || (iovcnt < 4)) {
39736285Sbrian		CTR2(KTR_VFS, "%s: failed for invalid iovcnt %d", __func__,
39831343Sbrian		    uap->iovcnt);
39930316Sbrian		return (EINVAL);
40020813Sjkh	}
40136285Sbrian
40236285Sbrian	error = copyinuio(uap->iovp, iovcnt, &auio);
40328679Sbrian	if (error) {
40410528Samurai		CTR2(KTR_VFS, "%s: failed for invalid uio op with %d errno",
40536285Sbrian		    __func__, error);
40636285Sbrian		return (error);
40736285Sbrian	}
40836285Sbrian	error = vfs_donmount(td, uap->flags, auio);
40910528Samurai
41031343Sbrian	free(auio, M_IOV);
41110528Samurai	return (error);
41220813Sjkh}
41336285Sbrian
41436285Sbrian/*
41520813Sjkh * ---------------------------------------------------------------------
41636285Sbrian * Various utility functions
41710528Samurai */
41810528Samurai
41931343Sbrianvoid
42031343Sbrianvfs_ref(struct mount *mp)
42131343Sbrian{
42236285Sbrian
42331343Sbrian	CTR2(KTR_VFS, "%s: mp %p", __func__, mp);
42431343Sbrian	MNT_ILOCK(mp);
42531343Sbrian	MNT_REF(mp);
42631343Sbrian	MNT_IUNLOCK(mp);
42731343Sbrian}
42831343Sbrian
42931343Sbrianvoid
43031343Sbrianvfs_rel(struct mount *mp)
43131343Sbrian{
43231343Sbrian
43330715Sbrian	CTR2(KTR_VFS, "%s: mp %p", __func__, mp);
43436285Sbrian	MNT_ILOCK(mp);
43528679Sbrian	MNT_REL(mp);
43628679Sbrian	MNT_IUNLOCK(mp);
43732109Sbrian}
43836285Sbrian
43932109Sbrian/*
44036285Sbrian * Allocate and initialize the mount point struct.
44136285Sbrian */
44236285Sbrianstruct mount *
44336285Sbrianvfs_mount_alloc(struct vnode *vp, struct vfsconf *vfsp, const char *fspath,
44431121Sbrian    struct ucred *cred)
44531121Sbrian{
44628679Sbrian	struct mount *mp;
44731372Sbrian
44836934Sbrian	mp = uma_zalloc(mount_zone, M_WAITOK);
44936934Sbrian	bzero(&mp->mnt_startzero,
45036285Sbrian	    __rangeof(struct mount, mnt_startzero, mnt_endzero));
45136285Sbrian	TAILQ_INIT(&mp->mnt_nvnodelist);
45236285Sbrian	mp->mnt_nvnodelistsize = 0;
45336285Sbrian	mp->mnt_ref = 0;
45428679Sbrian	(void) vfs_busy(mp, MBF_NOWAIT);
45532109Sbrian	mp->mnt_op = vfsp->vfc_vfsops;
45636285Sbrian	mp->mnt_vfc = vfsp;
45732109Sbrian	vfsp->vfc_refcount++;	/* XXX Unlocked */
45836285Sbrian	mp->mnt_stat.f_type = vfsp->vfc_typenum;
45928679Sbrian	mp->mnt_gen++;
46036285Sbrian	strlcpy(mp->mnt_stat.f_fstypename, vfsp->vfc_name, MFSNAMELEN);
46128679Sbrian	mp->mnt_vnodecovered = vp;
46236285Sbrian	mp->mnt_cred = crdup(cred);
46328679Sbrian	mp->mnt_stat.f_owner = cred->cr_uid;
46436285Sbrian	strlcpy(mp->mnt_stat.f_mntonname, fspath, MNAMELEN);
46536285Sbrian	mp->mnt_iosize_max = DFLTPHYS;
46636285Sbrian#ifdef MAC
46728679Sbrian	mac_mount_init(mp);
46836285Sbrian	mac_mount_create(cred, mp);
46936285Sbrian#endif
47037008Sbrian	arc4rand(&mp->mnt_hashseed, sizeof mp->mnt_hashseed, 0);
47128679Sbrian	return (mp);
47236285Sbrian}
47337160Sbrian
47436285Sbrian/*
47536285Sbrian * Destroy the mount struct previously allocated by vfs_mount_alloc().
47636285Sbrian */
47736285Sbrianvoid
47836285Sbrianvfs_mount_destroy(struct mount *mp)
47936285Sbrian{
48036285Sbrian
48136285Sbrian	MNT_ILOCK(mp);
48228679Sbrian	mp->mnt_kern_flag |= MNTK_REFEXPIRE;
48328679Sbrian	if (mp->mnt_kern_flag & MNTK_MWAIT) {
48436285Sbrian		mp->mnt_kern_flag &= ~MNTK_MWAIT;
48528679Sbrian		wakeup(mp);
48628679Sbrian	}
48728679Sbrian	while (mp->mnt_ref)
48836285Sbrian		msleep(mp, MNT_MTX(mp), PVFS, "mntref", 0);
48931372Sbrian	KASSERT(mp->mnt_ref == 0,
49036285Sbrian	    ("%s: invalid refcount in the drain path @ %s:%d", __func__,
49131372Sbrian	    __FILE__, __LINE__));
49228679Sbrian	if (mp->mnt_writeopcount != 0)
49331343Sbrian		panic("vfs_mount_destroy: nonzero writeopcount");
49428679Sbrian	if (mp->mnt_secondary_writes != 0)
4956059Samurai		panic("vfs_mount_destroy: nonzero secondary_writes");
4966059Samurai	mp->mnt_vfc->vfc_refcount--;
49728536Sbrian	if (!TAILQ_EMPTY(&mp->mnt_nvnodelist)) {
49831343Sbrian		struct vnode *vp;
4996059Samurai
50036285Sbrian		TAILQ_FOREACH(vp, &mp->mnt_nvnodelist, v_nmntvnodes)
50136285Sbrian			vprint("", vp);
50236285Sbrian		panic("unmount: dangling vnode");
5036059Samurai	}
50426516Sbrian	if (mp->mnt_nvnodelistsize != 0)
50536285Sbrian		panic("vfs_mount_destroy: nonzero nvnodelistsize");
50628679Sbrian	if (mp->mnt_lockref != 0)
50736285Sbrian		panic("vfs_mount_destroy: nonzero lock refcount");
50836285Sbrian	MNT_IUNLOCK(mp);
50936285Sbrian#ifdef MAC
51036285Sbrian	mac_mount_destroy(mp);
51136285Sbrian#endif
5126059Samurai	if (mp->mnt_opt != NULL)
51331077Sbrian		vfs_freeopts(mp->mnt_opt);
5146059Samurai	crfree(mp->mnt_cred);
5156059Samurai	uma_zfree(mount_zone, mp);
51628679Sbrian}
51736285Sbrian
5186059Samuraiint
51936285Sbrianvfs_donmount(struct thread *td, int fsflags, struct uio *fsoptions)
52031077Sbrian{
5216059Samurai	struct vfsoptlist *optlist;
5226059Samurai	struct vfsopt *opt, *noro_opt, *tmp_opt;
52328679Sbrian	char *fstype, *fspath, *errmsg;
52431343Sbrian	int error, fstypelen, fspathlen, errmsg_len, errmsg_pos;
52528327Sbrian	int has_rw, has_noro;
52636285Sbrian
52736285Sbrian	errmsg = fspath = NULL;
52836285Sbrian	errmsg_len = has_noro = has_rw = fspathlen = 0;
52928327Sbrian	errmsg_pos = -1;
53036285Sbrian
53136285Sbrian	error = vfs_buildopts(fsoptions, &optlist);
53228461Sbrian	if (error)
53336285Sbrian		return (error);
53436285Sbrian
53536285Sbrian	if (vfs_getopt(optlist, "errmsg", (void **)&errmsg, &errmsg_len) == 0)
53628461Sbrian		errmsg_pos = vfs_getopt_pos(optlist, "errmsg");
53736285Sbrian
53836285Sbrian	/*
53928461Sbrian	 * We need these two options before the others,
54036285Sbrian	 * and they are mandatory for any filesystem.
54128461Sbrian	 * Ensure they are NUL terminated as well.
54231077Sbrian	 */
54328327Sbrian	fstypelen = 0;
54428327Sbrian	error = vfs_getopt(optlist, "fstype", (void **)&fstype, &fstypelen);
54528679Sbrian	if (error || fstype[fstypelen - 1] != '\0') {
54631343Sbrian		error = EINVAL;
5476059Samurai		if (errmsg != NULL)
54836285Sbrian			strncpy(errmsg, "Invalid fstype", errmsg_len);
54931077Sbrian		goto bail;
5506059Samurai	}
5516059Samurai	fspathlen = 0;
55228679Sbrian	error = vfs_getopt(optlist, "fspath", (void **)&fspath, &fspathlen);
55336285Sbrian	if (error || fspath[fspathlen - 1] != '\0') {
55426326Sbrian		error = EINVAL;
55536285Sbrian		if (errmsg != NULL)
55626326Sbrian			strncpy(errmsg, "Invalid fspath", errmsg_len);
55736285Sbrian		goto bail;
55836285Sbrian	}
55936285Sbrian
56036285Sbrian	/*
56131077Sbrian	 * We need to see if we have the "update" option
56226326Sbrian	 * before we call vfs_domount(), since vfs_domount() has special
56326326Sbrian	 * logic based on MNT_UPDATE.  This is very important
56430715Sbrian	 * when we want to update the root filesystem.
56536285Sbrian	 */
56636285Sbrian	TAILQ_FOREACH_SAFE(opt, optlist, link, tmp_opt) {
56736285Sbrian		if (strcmp(opt->name, "update") == 0) {
56836285Sbrian			fsflags |= MNT_UPDATE;
56936285Sbrian			vfs_freeopt(optlist, opt);
57036285Sbrian		}
57136285Sbrian		else if (strcmp(opt->name, "async") == 0)
57236285Sbrian			fsflags |= MNT_ASYNC;
57336285Sbrian		else if (strcmp(opt->name, "force") == 0) {
57436285Sbrian			fsflags |= MNT_FORCE;
57536285Sbrian			vfs_freeopt(optlist, opt);
57636285Sbrian		}
57736285Sbrian		else if (strcmp(opt->name, "reload") == 0) {
57836285Sbrian			fsflags |= MNT_RELOAD;
57936285Sbrian			vfs_freeopt(optlist, opt);
58036285Sbrian		}
58136285Sbrian		else if (strcmp(opt->name, "multilabel") == 0)
58236285Sbrian			fsflags |= MNT_MULTILABEL;
58336285Sbrian		else if (strcmp(opt->name, "noasync") == 0)
58436285Sbrian			fsflags &= ~MNT_ASYNC;
58536285Sbrian		else if (strcmp(opt->name, "noatime") == 0)
58636285Sbrian			fsflags |= MNT_NOATIME;
58736285Sbrian		else if (strcmp(opt->name, "atime") == 0) {
58836285Sbrian			free(opt->name, M_MOUNT);
58936285Sbrian			opt->name = strdup("nonoatime", M_MOUNT);
59036285Sbrian		}
59136285Sbrian		else if (strcmp(opt->name, "noclusterr") == 0)
59236285Sbrian			fsflags |= MNT_NOCLUSTERR;
59336285Sbrian		else if (strcmp(opt->name, "clusterr") == 0) {
59436285Sbrian			free(opt->name, M_MOUNT);
59536285Sbrian			opt->name = strdup("nonoclusterr", M_MOUNT);
59636285Sbrian		}
59736285Sbrian		else if (strcmp(opt->name, "noclusterw") == 0)
59836285Sbrian			fsflags |= MNT_NOCLUSTERW;
59936285Sbrian		else if (strcmp(opt->name, "clusterw") == 0) {
60036285Sbrian			free(opt->name, M_MOUNT);
60128679Sbrian			opt->name = strdup("nonoclusterw", M_MOUNT);
60236285Sbrian		}
60336285Sbrian		else if (strcmp(opt->name, "noexec") == 0)
60436285Sbrian			fsflags |= MNT_NOEXEC;
60528679Sbrian		else if (strcmp(opt->name, "exec") == 0) {
60631343Sbrian			free(opt->name, M_MOUNT);
60728679Sbrian			opt->name = strdup("nonoexec", M_MOUNT);
6086059Samurai		}
6096059Samurai		else if (strcmp(opt->name, "nosuid") == 0)
61030715Sbrian			fsflags |= MNT_NOSUID;
61131343Sbrian		else if (strcmp(opt->name, "suid") == 0) {
6126059Samurai			free(opt->name, M_MOUNT);
61326516Sbrian			opt->name = strdup("nonosuid", M_MOUNT);
61426516Sbrian		}
61528679Sbrian		else if (strcmp(opt->name, "nosymfollow") == 0)
6166059Samurai			fsflags |= MNT_NOSYMFOLLOW;
61726516Sbrian		else if (strcmp(opt->name, "symfollow") == 0) {
61826516Sbrian			free(opt->name, M_MOUNT);
61926516Sbrian			opt->name = strdup("nonosymfollow", M_MOUNT);
6206059Samurai		}
62125566Sbrian		else if (strcmp(opt->name, "noro") == 0) {
62226516Sbrian			fsflags &= ~MNT_RDONLY;
62328679Sbrian			has_noro = 1;
62428679Sbrian		}
62526516Sbrian		else if (strcmp(opt->name, "rw") == 0) {
6266059Samurai			fsflags &= ~MNT_RDONLY;
6276059Samurai			has_rw = 1;
62828679Sbrian		}
62926516Sbrian		else if (strcmp(opt->name, "ro") == 0)
63028679Sbrian			fsflags |= MNT_RDONLY;
63128679Sbrian		else if (strcmp(opt->name, "rdonly") == 0) {
63226516Sbrian			free(opt->name, M_MOUNT);
6336059Samurai			opt->name = strdup("ro", M_MOUNT);
6346059Samurai			fsflags |= MNT_RDONLY;
6356059Samurai		}
6366059Samurai		else if (strcmp(opt->name, "suiddir") == 0)
6376059Samurai			fsflags |= MNT_SUIDDIR;
6386059Samurai		else if (strcmp(opt->name, "sync") == 0)
63926516Sbrian			fsflags |= MNT_SYNCHRONOUS;
6406059Samurai		else if (strcmp(opt->name, "union") == 0)
6416059Samurai			fsflags |= MNT_UNION;
64236285Sbrian	}
64336285Sbrian
64436285Sbrian	/*
64536285Sbrian	 * If "rw" was specified as a mount option, and we
64636285Sbrian	 * are trying to update a mount-point from "ro" to "rw",
64736285Sbrian	 * we need a mount option "noro", since in vfs_mergeopts(),
64836285Sbrian	 * "noro" will cancel "ro", but "rw" will not do anything.
64936285Sbrian	 */
65036285Sbrian	if (has_rw && !has_noro) {
65136285Sbrian		noro_opt = malloc(sizeof(struct vfsopt), M_MOUNT, M_WAITOK);
65236285Sbrian		noro_opt->name = strdup("noro", M_MOUNT);
65336285Sbrian		noro_opt->value = NULL;
65436285Sbrian		noro_opt->len = 0;
65536285Sbrian		noro_opt->pos = -1;
65636285Sbrian		noro_opt->seen = 1;
65736285Sbrian		TAILQ_INSERT_TAIL(optlist, noro_opt, link);
65836285Sbrian	}
65936285Sbrian
66036285Sbrian	/*
66130715Sbrian	 * Be ultra-paranoid about making sure the type and fspath
66236285Sbrian	 * variables will fit in our mp buffers, including the
66336285Sbrian	 * terminating NUL.
6646059Samurai	 */
66528679Sbrian	if (fstypelen >= MFSNAMELEN - 1 || fspathlen >= MNAMELEN - 1) {
6666059Samurai		error = ENAMETOOLONG;
6676059Samurai		goto bail;
66831343Sbrian	}
66936285Sbrian
6706059Samurai	error = vfs_domount(td, fstype, fspath, fsflags, optlist);
67136285Sbrianbail:
6726059Samurai	/* copyout the errmsg */
67336285Sbrian	if (errmsg_pos != -1 && ((2 * errmsg_pos + 1) < fsoptions->uio_iovcnt)
67436285Sbrian	    && errmsg_len > 0 && errmsg != NULL) {
67536285Sbrian		if (fsoptions->uio_segflg == UIO_SYSSPACE) {
67636285Sbrian			bcopy(errmsg,
67736285Sbrian			    fsoptions->uio_iov[2 * errmsg_pos + 1].iov_base,
67836285Sbrian			    fsoptions->uio_iov[2 * errmsg_pos + 1].iov_len);
67936285Sbrian		} else {
68036285Sbrian			copyout(errmsg,
68136285Sbrian			    fsoptions->uio_iov[2 * errmsg_pos + 1].iov_base,
68236285Sbrian			    fsoptions->uio_iov[2 * errmsg_pos + 1].iov_len);
68336285Sbrian		}
68436285Sbrian	}
68536285Sbrian
68636285Sbrian	if (error != 0)
68736285Sbrian		vfs_freeopts(optlist);
68836285Sbrian	return (error);
68936285Sbrian}
69036285Sbrian
69136285Sbrian/*
69236285Sbrian * Old mount API.
69336285Sbrian */
69436285Sbrian#ifndef _SYS_SYSPROTO_H_
69536285Sbrianstruct mount_args {
69636285Sbrian	char	*type;
69736285Sbrian	char	*path;
69836285Sbrian	int	flags;
69931343Sbrian	caddr_t	data;
70036285Sbrian};
70136285Sbrian#endif
70226516Sbrian/* ARGSUSED */
70326516Sbrianint
70436285Sbrianmount(td, uap)
70528679Sbrian	struct thread *td;
70636285Sbrian	struct mount_args /* {
70736285Sbrian		char *type;
70826516Sbrian		char *path;
70926516Sbrian		int flags;
7106059Samurai		caddr_t data;
7116059Samurai	} */ *uap;
71237009Sbrian{
71337009Sbrian	char *fstype;
7146059Samurai	struct vfsconf *vfsp = NULL;
7156059Samurai	struct mntarg *ma = NULL;
7166059Samurai	int error;
7176059Samurai
7186059Samurai	AUDIT_ARG_FFLAGS(uap->flags);
7196059Samurai
7206059Samurai	/*
72137009Sbrian	 * Filter out MNT_ROOTFS.  We do not want clients of mount() in
72237009Sbrian	 * userspace to set this flag, but we must filter it out if we want
72337009Sbrian	 * MNT_UPDATE on the root file system to work.
72431121Sbrian	 * MNT_ROOTFS should only be set by the kernel when mounting its
7256059Samurai	 * root file system.
72631822Sbrian	 */
72731822Sbrian	uap->flags &= ~MNT_ROOTFS;
72831822Sbrian
72931822Sbrian	fstype = malloc(MFSNAMELEN, M_TEMP, M_WAITOK);
73031828Sbrian	error = copyinstr(uap->type, fstype, MFSNAMELEN, NULL);
73131828Sbrian	if (error) {
73231828Sbrian		free(fstype, M_TEMP);
73331822Sbrian		return (error);
73431822Sbrian	}
73531822Sbrian
73631822Sbrian	AUDIT_ARG_TEXT(fstype);
73731828Sbrian	mtx_lock(&Giant);
73831828Sbrian	vfsp = vfs_byname_kld(fstype, td, &error);
73931828Sbrian	free(fstype, M_TEMP);
74031828Sbrian	if (vfsp == NULL) {
74136285Sbrian		mtx_unlock(&Giant);
74236285Sbrian		return (ENOENT);
74336285Sbrian	}
74436285Sbrian	if (vfsp->vfc_vfsops->vfs_cmount == NULL) {
74536285Sbrian		mtx_unlock(&Giant);
74631822Sbrian		return (EOPNOTSUPP);
74731822Sbrian	}
74831822Sbrian
74931121Sbrian	ma = mount_argsu(ma, "fstype", uap->type, MNAMELEN);
75036285Sbrian	ma = mount_argsu(ma, "fspath", uap->path, MNAMELEN);
75137008Sbrian	ma = mount_argb(ma, uap->flags & MNT_RDONLY, "noro");
75231121Sbrian	ma = mount_argb(ma, !(uap->flags & MNT_NOSUID), "nosuid");
75331156Sbrian	ma = mount_argb(ma, !(uap->flags & MNT_NOEXEC), "noexec");
75436285Sbrian
75531156Sbrian	error = vfsp->vfc_vfsops->vfs_cmount(ma, uap->data, uap->flags);
75631156Sbrian	mtx_unlock(&Giant);
75731156Sbrian	return (error);
75831156Sbrian}
75931156Sbrian
76031962Sbrian/*
76131962Sbrian * vfs_domount_first(): first file system mount (not update)
76231156Sbrian */
76331156Sbrianstatic int
76431156Sbrianvfs_domount_first(
76531156Sbrian	struct thread *td,	/* Calling thread. */
76631962Sbrian	struct vfsconf *vfsp,	/* File system type. */
76731156Sbrian	char *fspath,		/* Mount path. */
76831822Sbrian	struct vnode *vp,	/* Vnode to be covered. */
76936285Sbrian	int fsflags,		/* Flags common to all filesystems. */
77031822Sbrian	void *fsdata		/* Options local to the filesystem. */
77131962Sbrian	)
77231156Sbrian{
77331156Sbrian	struct vattr va;
77436285Sbrian	struct mount *mp;
77531156Sbrian	struct vnode *newdp;
77637008Sbrian	int error;
77731156Sbrian
7786059Samurai	mtx_assert(&Giant, MA_OWNED);
7796059Samurai	ASSERT_VOP_ELOCKED(vp, __func__);
78031121Sbrian	KASSERT((fsflags & MNT_UPDATE) == 0, ("MNT_UPDATE shouldn't be here"));
78136285Sbrian
78236285Sbrian	/*
78331121Sbrian	 * If the user is not root, ensure that they own the directory
78431121Sbrian	 * onto which we are attempting to mount.
78537009Sbrian	 */
78631121Sbrian	error = VOP_GETATTR(vp, &va, td->td_ucred);
78737009Sbrian	if (error == 0 && va.va_uid != td->td_ucred->cr_uid)
78837008Sbrian		error = priv_check_cred(td->td_ucred, PRIV_VFS_ADMIN, 0);
78931121Sbrian	if (error == 0)
79031121Sbrian		error = vinvalbuf(vp, V_SAVE, 0, 0);
7916059Samurai	if (error == 0 && vp->v_type != VDIR)
79231343Sbrian		error = ENOTDIR;
7936059Samurai	if (error == 0) {
79436285Sbrian		VI_LOCK(vp);
79536285Sbrian		if ((vp->v_iflag & VI_MOUNT) == 0 && vp->v_mountedhere == NULL)
79636285Sbrian			vp->v_iflag |= VI_MOUNT;
79736285Sbrian		else
79836285Sbrian			error = EBUSY;
7996059Samurai		VI_UNLOCK(vp);
80036285Sbrian	}
80126516Sbrian	if (error != 0) {
80226516Sbrian		vput(vp);
8036059Samurai		return (error);
8046059Samurai	}
8056059Samurai	VOP_UNLOCK(vp, 0);
80631343Sbrian
8076059Samurai	/* Allocate and initialize the filesystem. */
80836285Sbrian	mp = vfs_mount_alloc(vp, vfsp, fspath, td->td_ucred);
80936285Sbrian	/* XXXMAC: pass to vfs_mount_alloc? */
81026516Sbrian	mp->mnt_optnew = fsdata;
8116059Samurai	/* Set the mount level flags. */
81236285Sbrian	mp->mnt_flag = (fsflags & (MNT_UPDATEMASK | MNT_ROOTFS | MNT_RDONLY));
81336285Sbrian
81436285Sbrian	/*
81536285Sbrian	 * Mount the filesystem.
81636285Sbrian	 * XXX The final recipients of VFS_MOUNT just overwrite the ndp they
8176059Samurai	 * get.  No freeing of cn_pnbuf.
81836285Sbrian	 */
81936285Sbrian        error = VFS_MOUNT(mp);
82036285Sbrian	if (error != 0) {
82136285Sbrian		vfs_unbusy(mp);
8226059Samurai		vfs_mount_destroy(mp);
8236059Samurai		VI_LOCK(vp);
8246059Samurai		vp->v_iflag &= ~VI_MOUNT;
82531343Sbrian		VI_UNLOCK(vp);
8266059Samurai		vrele(vp);
82736285Sbrian		return (error);
82836285Sbrian	}
82936285Sbrian
83036285Sbrian	if (mp->mnt_opt != NULL)
83136285Sbrian		vfs_freeopts(mp->mnt_opt);
83236285Sbrian	mp->mnt_opt = mp->mnt_optnew;
83326516Sbrian	(void)VFS_STATFS(mp, &mp->mnt_stat);
83426516Sbrian
8356059Samurai	/*
8366059Samurai	 * Prevent external consumers of mount options from reading mnt_optnew.
8376059Samurai	 */
83836285Sbrian	mp->mnt_optnew = NULL;
8396059Samurai
84037160Sbrian	MNT_ILOCK(mp);
84136285Sbrian	if ((mp->mnt_flag & MNT_ASYNC) != 0 && mp->mnt_noasync == 0)
84237160Sbrian		mp->mnt_kern_flag |= MNTK_ASYNC;
84337160Sbrian	else
84437160Sbrian		mp->mnt_kern_flag &= ~MNTK_ASYNC;
84537160Sbrian	MNT_IUNLOCK(mp);
84637160Sbrian
84737160Sbrian	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
84837160Sbrian	cache_purge(vp);
84937160Sbrian	VI_LOCK(vp);
85037160Sbrian	vp->v_iflag &= ~VI_MOUNT;
85137160Sbrian	VI_UNLOCK(vp);
85237160Sbrian	vp->v_mountedhere = mp;
85337160Sbrian	/* Place the new filesystem at the end of the mount list. */
8546059Samurai	mtx_lock(&mountlist_mtx);
85537160Sbrian	TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list);
85637160Sbrian	mtx_unlock(&mountlist_mtx);
85737160Sbrian	vfs_event_signal(NULL, VQ_MOUNT, 0);
85836285Sbrian	if (VFS_ROOT(mp, LK_EXCLUSIVE, &newdp))
85937160Sbrian		panic("mount: lost mount");
86037160Sbrian	VOP_UNLOCK(newdp, 0);
86137160Sbrian	VOP_UNLOCK(vp, 0);
86237160Sbrian	mountcheckdirs(vp, newdp);
86337160Sbrian	vrele(newdp);
86437160Sbrian	if ((mp->mnt_flag & MNT_RDONLY) == 0)
86537160Sbrian		vfs_allocate_syncvnode(mp);
86637160Sbrian	vfs_unbusy(mp);
86737160Sbrian	return (0);
86837160Sbrian}
86937160Sbrian
87037160Sbrian/*
87137160Sbrian * vfs_domount_update(): update of mounted file system
87236285Sbrian */
87337160Sbrianstatic int
87437160Sbrianvfs_domount_update(
87537160Sbrian	struct thread *td,	/* Calling thread. */
87637160Sbrian	struct vnode *vp,	/* Mount point vnode. */
87737160Sbrian	int fsflags,		/* Flags common to all filesystems. */
87837160Sbrian	void *fsdata		/* Options local to the filesystem. */
87937160Sbrian	)
88037160Sbrian{
88137160Sbrian	struct oexport_args oexport;
88236285Sbrian	struct export_args export;
88336285Sbrian	struct mount *mp;
88436285Sbrian	int error, flag;
88526516Sbrian
8866059Samurai	mtx_assert(&Giant, MA_OWNED);
8876059Samurai	ASSERT_VOP_ELOCKED(vp, __func__);
88825067Sbrian	KASSERT((fsflags & MNT_UPDATE) != 0, ("MNT_UPDATE should be here"));
88936285Sbrian
8906059Samurai	if ((vp->v_vflag & VV_ROOT) == 0) {
89137007Sbrian		vput(vp);
89237007Sbrian		return (EINVAL);
89337007Sbrian	}
89437007Sbrian	mp = vp->v_mount;
89537007Sbrian	/*
89637007Sbrian	 * We only allow the filesystem to be reloaded if it
89737007Sbrian	 * is currently mounted read-only.
89837007Sbrian	 */
89937007Sbrian	flag = mp->mnt_flag;
9006059Samurai	if ((fsflags & MNT_RELOAD) != 0 && (flag & MNT_RDONLY) == 0) {
90137007Sbrian		vput(vp);
90237007Sbrian		return (EOPNOTSUPP);	/* Needs translation */
90337007Sbrian	}
90437007Sbrian	/*
90537007Sbrian	 * Only privileged root, or (if MNT_USER is set) the user that
90637007Sbrian	 * did the original mount is permitted to update it.
90737007Sbrian	 */
90837007Sbrian	error = vfs_suser(mp, td);
90937007Sbrian	if (error != 0) {
91037007Sbrian		vput(vp);
91137007Sbrian		return (error);
91237007Sbrian	}
91336285Sbrian	if (vfs_busy(mp, MBF_NOWAIT)) {
91436285Sbrian		vput(vp);
91536285Sbrian		return (EBUSY);
91636285Sbrian	}
91736285Sbrian	VI_LOCK(vp);
9186059Samurai	if ((vp->v_iflag & VI_MOUNT) != 0 || vp->v_mountedhere != NULL) {
9196059Samurai		VI_UNLOCK(vp);
92025067Sbrian		vfs_unbusy(mp);
92136285Sbrian		vput(vp);
92211336Samurai		return (EBUSY);
92337018Sbrian	}
92437018Sbrian	vp->v_iflag |= VI_MOUNT;
92537018Sbrian	VI_UNLOCK(vp);
92637018Sbrian	VOP_UNLOCK(vp, 0);
92737018Sbrian
92837018Sbrian	MNT_ILOCK(mp);
92937018Sbrian	mp->mnt_flag &= ~MNT_UPDATEMASK;
93037018Sbrian	mp->mnt_flag |= fsflags & (MNT_RELOAD | MNT_FORCE | MNT_UPDATE |
93137018Sbrian	    MNT_SNAPSHOT | MNT_ROOTFS | MNT_UPDATEMASK | MNT_RDONLY);
93237018Sbrian	if ((mp->mnt_flag & MNT_ASYNC) == 0)
93337018Sbrian		mp->mnt_kern_flag &= ~MNTK_ASYNC;
93437018Sbrian	MNT_IUNLOCK(mp);
93537018Sbrian	mp->mnt_optnew = fsdata;
93637018Sbrian	vfs_mergeopts(mp->mnt_optnew, mp->mnt_opt);
93737060Sbrian
93837018Sbrian	/*
93937018Sbrian	 * Mount the filesystem.
94036285Sbrian	 * XXX The final recipients of VFS_MOUNT just overwrite the ndp they
94136285Sbrian	 * get.  No freeing of cn_pnbuf.
94236285Sbrian	 */
94336285Sbrian        error = VFS_MOUNT(mp);
94425067Sbrian
94525067Sbrian	if (error == 0) {
94625067Sbrian		/* Process the export option. */
94736285Sbrian		if (vfs_copyopt(mp->mnt_optnew, "export", &export,
94825067Sbrian		    sizeof(export)) == 0) {
94936285Sbrian			error = vfs_export(mp, &export);
95036285Sbrian		} else if (vfs_copyopt(mp->mnt_optnew, "export", &oexport,
95111336Samurai		    sizeof(oexport)) == 0) {
95236285Sbrian			export.ex_flags = oexport.ex_flags;
95336285Sbrian			export.ex_root = oexport.ex_root;
95436285Sbrian			export.ex_anon = oexport.ex_anon;
95536285Sbrian			export.ex_addr = oexport.ex_addr;
95611336Samurai			export.ex_addrlen = oexport.ex_addrlen;
95736285Sbrian			export.ex_mask = oexport.ex_mask;
95836285Sbrian			export.ex_masklen = oexport.ex_masklen;
95936285Sbrian			export.ex_indexfile = oexport.ex_indexfile;
96036285Sbrian			export.ex_numsecflavors = 0;
96136285Sbrian			error = vfs_export(mp, &export);
96236285Sbrian		}
96336285Sbrian	}
96436285Sbrian
96536285Sbrian	MNT_ILOCK(mp);
96636285Sbrian	if (error == 0) {
96736285Sbrian		mp->mnt_flag &=	~(MNT_UPDATE | MNT_RELOAD | MNT_FORCE |
96836285Sbrian		    MNT_SNAPSHOT);
96936285Sbrian	} else {
97036285Sbrian		/*
97136285Sbrian		 * If we fail, restore old mount flags. MNT_QUOTA is special,
97236285Sbrian		 * because it is not part of MNT_UPDATEMASK, but it could have
97324939Sbrian		 * changed in the meantime if quotactl(2) was called.
97426516Sbrian		 * All in all we want current value of MNT_QUOTA, not the old
97511336Samurai		 * one.
97611336Samurai		 */
97725067Sbrian		mp->mnt_flag = (mp->mnt_flag & MNT_QUOTA) | (flag & ~MNT_QUOTA);
97831343Sbrian	}
97928327Sbrian	if ((mp->mnt_flag & MNT_ASYNC) != 0 && mp->mnt_noasync == 0)
98036285Sbrian		mp->mnt_kern_flag |= MNTK_ASYNC;
98136285Sbrian	else
98236285Sbrian		mp->mnt_kern_flag &= ~MNTK_ASYNC;
98336285Sbrian	MNT_IUNLOCK(mp);
98436285Sbrian
98536285Sbrian	if (error != 0)
98636285Sbrian		goto end;
98736285Sbrian
98836285Sbrian	if (mp->mnt_opt != NULL)
98928461Sbrian		vfs_freeopts(mp->mnt_opt);
99028327Sbrian	mp->mnt_opt = mp->mnt_optnew;
99128327Sbrian	(void)VFS_STATFS(mp, &mp->mnt_stat);
99228327Sbrian	/*
99328327Sbrian	 * Prevent external consumers of mount options from reading
99428327Sbrian	 * mnt_optnew.
99531081Sbrian	 */
99631081Sbrian	mp->mnt_optnew = NULL;
99731081Sbrian
99828327Sbrian	if ((mp->mnt_flag & MNT_RDONLY) == 0)
99931343Sbrian		vfs_allocate_syncvnode(mp);
100026940Sbrian	else
100126940Sbrian		vfs_deallocate_syncvnode(mp);
100226940Sbrianend:
100336285Sbrian	vfs_unbusy(mp);
100431081Sbrian	VI_LOCK(vp);
100531081Sbrian	vp->v_iflag &= ~VI_MOUNT;
100631081Sbrian	VI_UNLOCK(vp);
100736285Sbrian	vrele(vp);
100836285Sbrian	return (error);
100936285Sbrian}
101036285Sbrian
101136285Sbrian/*
101236285Sbrian * vfs_domount(): actually attempt a filesystem mount.
101336285Sbrian */
101431081Sbrianstatic int
101531081Sbrianvfs_domount(
101636285Sbrian	struct thread *td,	/* Calling thread. */
101736285Sbrian	const char *fstype,	/* Filesystem type. */
101836285Sbrian	char *fspath,		/* Mount path. */
101936285Sbrian	int fsflags,		/* Flags common to all filesystems. */
102031081Sbrian	void *fsdata		/* Options local to the filesystem. */
102136285Sbrian	)
102231081Sbrian{
102336285Sbrian	struct vfsconf *vfsp;
102436285Sbrian	struct nameidata nd;
102531081Sbrian	struct vnode *vp;
102636285Sbrian	int error;
102731081Sbrian
102836285Sbrian	/*
102928679Sbrian	 * Be ultra-paranoid about making sure the type and fspath
103031081Sbrian	 * variables will fit in our mp buffers, including the
103128679Sbrian	 * terminating NUL.
103228679Sbrian	 */
103331081Sbrian	if (strlen(fstype) >= MFSNAMELEN || strlen(fspath) >= MNAMELEN)
103431081Sbrian		return (ENAMETOOLONG);
103531081Sbrian
103631081Sbrian	if (jailed(td->td_ucred) || usermount == 0) {
103731081Sbrian		if ((error = priv_check(td, PRIV_VFS_MOUNT)) != 0)
103831081Sbrian			return (error);
103936285Sbrian	}
104036285Sbrian
104136285Sbrian	/*
104236285Sbrian	 * Do not allow NFS export or MNT_SUIDDIR by unprivileged users.
104336285Sbrian	 */
104436285Sbrian	if (fsflags & MNT_EXPORTED) {
104536285Sbrian		error = priv_check(td, PRIV_VFS_MOUNT_EXPORTED);
104636285Sbrian		if (error)
104727346Sbrian			return (error);
104836285Sbrian	}
104928679Sbrian	if (fsflags & MNT_SUIDDIR) {
105031081Sbrian		error = priv_check(td, PRIV_VFS_MOUNT_SUIDDIR);
105131081Sbrian		if (error)
105228679Sbrian			return (error);
105336285Sbrian	}
105436285Sbrian	/*
105536285Sbrian	 * Silently enforce MNT_NOSUID and MNT_USER for unprivileged users.
105636285Sbrian	 */
105731081Sbrian	if ((fsflags & (MNT_NOSUID | MNT_USER)) != (MNT_NOSUID | MNT_USER)) {
105831081Sbrian		if (priv_check(td, PRIV_VFS_MOUNT_NONUSER) != 0)
105931081Sbrian			fsflags |= MNT_NOSUID | MNT_USER;
106031081Sbrian	}
106131081Sbrian
106236285Sbrian	/* Load KLDs before we lock the covered vnode to avoid reversals. */
106328679Sbrian	vfsp = NULL;
106431081Sbrian	if ((fsflags & MNT_UPDATE) == 0) {
106527346Sbrian		/* Don't try to load KLDs if we're mounting the root. */
106631081Sbrian		if (fsflags & MNT_ROOTFS)
106736285Sbrian			vfsp = vfs_byname(fstype);
106836285Sbrian		else
106936285Sbrian			vfsp = vfs_byname_kld(fstype, td, &error);
107036285Sbrian		if (vfsp == NULL)
107136285Sbrian			return (ENODEV);
107236285Sbrian		if (jailed(td->td_ucred) && !(vfsp->vfc_flags & VFCF_JAIL))
107336285Sbrian			return (EPERM);
107427346Sbrian	}
107531081Sbrian
107626940Sbrian	/*
107726940Sbrian	 * Get vnode to be covered or mount point's vnode in case of MNT_UPDATE.
107826940Sbrian	 */
107926940Sbrian	NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | MPSAFE | AUDITVNODE1,
108026940Sbrian	    UIO_SYSSPACE, fspath, td);
108131343Sbrian	error = namei(&nd);
10826059Samurai	if (error != 0)
108336285Sbrian		return (error);
108436285Sbrian	if (!NDHASGIANT(&nd))
10856059Samurai		mtx_lock(&Giant);
10866059Samurai	NDFREE(&nd, NDF_ONLY_PNBUF);
10876059Samurai	vp = nd.ni_vp;
108831343Sbrian	if ((fsflags & MNT_UPDATE) == 0) {
10896059Samurai		error = vfs_domount_first(td, vfsp, fspath, vp, fsflags,
10906059Samurai		    fsdata);
109136285Sbrian	} else {
109236285Sbrian		error = vfs_domount_update(td, vp, fsflags, fsdata);
10936059Samurai	}
10946059Samurai	mtx_unlock(&Giant);
109536285Sbrian
109631343Sbrian	ASSERT_VI_UNLOCKED(vp, __func__);
10976059Samurai	ASSERT_VOP_UNLOCKED(vp, __func__);
10986059Samurai
10996059Samurai	return (error);
110036285Sbrian}
110136285Sbrian
11026059Samurai/*
110326516Sbrian * Unmount a filesystem.
11046059Samurai *
11056059Samurai * Note: unmount takes a path to the vnode mounted on as argument, not
110630715Sbrian * special file (as before).
110731343Sbrian */
11086059Samurai#ifndef _SYS_SYSPROTO_H_
11096059Samuraistruct unmount_args {
11106059Samurai	char	*path;
11116059Samurai	int	flags;
111232124Sbrian};
111332124Sbrian#endif
111432124Sbrian/* ARGSUSED */
111532124Sbrianint
111632124Sbrianunmount(td, uap)
111732124Sbrian	struct thread *td;
111832124Sbrian	register struct unmount_args /* {
111928679Sbrian		char *path;
11206059Samurai		int flags;
11216059Samurai	} */ *uap;
11226059Samurai{
112331343Sbrian	struct mount *mp;
11246059Samurai	char *pathbuf;
112536285Sbrian	int error, id0, id1;
112632267Sbrian
112732267Sbrian	AUDIT_ARG_VALUE(uap->flags);
112832267Sbrian	if (jailed(td->td_ucred) || usermount == 0) {
112936285Sbrian		error = priv_check(td, PRIV_VFS_UNMOUNT);
113036285Sbrian		if (error)
11316059Samurai			return (error);
113236285Sbrian	}
113328679Sbrian
113426516Sbrian	pathbuf = malloc(MNAMELEN, M_TEMP, M_WAITOK);
113536285Sbrian	error = copyinstr(uap->path, pathbuf, MNAMELEN, NULL);
113636285Sbrian	if (error) {
113736285Sbrian		free(pathbuf, M_TEMP);
113828394Sbrian		return (error);
113936285Sbrian	}
114036285Sbrian	mtx_lock(&Giant);
114136285Sbrian	if (uap->flags & MNT_BYFSID) {
114236285Sbrian		AUDIT_ARG_TEXT(pathbuf);
114328679Sbrian		/* Decode the filesystem ID. */
114436285Sbrian		if (sscanf(pathbuf, "FSID:%d:%d", &id0, &id1) != 2) {
114536285Sbrian			mtx_unlock(&Giant);
114636285Sbrian			free(pathbuf, M_TEMP);
114736285Sbrian			return (EINVAL);
114836285Sbrian		}
114936285Sbrian
115036285Sbrian		mtx_lock(&mountlist_mtx);
11519440Samurai		TAILQ_FOREACH_REVERSE(mp, &mountlist, mntlist, mnt_list) {
11526059Samurai			if (mp->mnt_stat.f_fsid.val[0] == id0 &&
11536059Samurai			    mp->mnt_stat.f_fsid.val[1] == id1)
11546059Samurai				break;
115528394Sbrian		}
11566059Samurai		mtx_unlock(&mountlist_mtx);
11576059Samurai	} else {
11586059Samurai		AUDIT_ARG_UPATH1(td, pathbuf);
115936285Sbrian		mtx_lock(&mountlist_mtx);
116036285Sbrian		TAILQ_FOREACH_REVERSE(mp, &mountlist, mntlist, mnt_list) {
116136285Sbrian			if (strcmp(mp->mnt_stat.f_mntonname, pathbuf) == 0)
11626059Samurai				break;
116336285Sbrian		}
116436285Sbrian		mtx_unlock(&mountlist_mtx);
116536285Sbrian	}
116636285Sbrian	free(pathbuf, M_TEMP);
116736285Sbrian	if (mp == NULL) {
11686059Samurai		/*
116928537Sbrian		 * Previously we returned ENOENT for a nonexistent path and
117036285Sbrian		 * EINVAL for a non-mountpoint.  We cannot tell these apart
117136928Sbrian		 * now, so in the !MNT_BYFSID case return the more likely
117232267Sbrian		 * EINVAL for compatibility.
117331121Sbrian		 */
117426516Sbrian		mtx_unlock(&Giant);
11756059Samurai		return ((uap->flags & MNT_BYFSID) ? ENOENT : EINVAL);
11766059Samurai	}
117718752Sjkh
117831343Sbrian	/*
11796059Samurai	 * Don't allow unmounting the root filesystem.
118036285Sbrian	 */
118131343Sbrian	if (mp->mnt_flag & MNT_ROOTFS) {
118236285Sbrian		mtx_unlock(&Giant);
118336285Sbrian		return (EINVAL);
118436285Sbrian	}
118536285Sbrian	error = dounmount(mp, uap->flags, td);
118636285Sbrian	mtx_unlock(&Giant);
118736285Sbrian	return (error);
11886059Samurai}
118936285Sbrian
119036285Sbrian/*
119136285Sbrian * Do the actual filesystem unmount.
119236285Sbrian */
119336285Sbrianint
119426551Sbriandounmount(mp, flags, td)
119531343Sbrian	struct mount *mp;
119626551Sbrian	int flags;
119736285Sbrian	struct thread *td;
119836285Sbrian{
119936285Sbrian	struct vnode *coveredvp, *fsrootvp;
120036285Sbrian	int error;
120136285Sbrian	int async_flag;
120236285Sbrian	int mnt_gen_r;
120336285Sbrian
120436285Sbrian	mtx_assert(&Giant, MA_OWNED);
120536285Sbrian
120636285Sbrian	if ((coveredvp = mp->mnt_vnodecovered) != NULL) {
120726551Sbrian		mnt_gen_r = mp->mnt_gen;
120828679Sbrian		VI_LOCK(coveredvp);
120936285Sbrian		vholdl(coveredvp);
121036285Sbrian		vn_lock(coveredvp, LK_EXCLUSIVE | LK_INTERLOCK | LK_RETRY);
121136285Sbrian		vdrop(coveredvp);
121236285Sbrian		/*
121336285Sbrian		 * Check for mp being unmounted while waiting for the
121436285Sbrian		 * covered vnode lock.
121536285Sbrian		 */
121636285Sbrian		if (coveredvp->v_mountedhere != mp ||
121728679Sbrian		    coveredvp->v_mountedhere->mnt_gen != mnt_gen_r) {
121828679Sbrian			VOP_UNLOCK(coveredvp, 0);
121936285Sbrian			return (EBUSY);
122036285Sbrian		}
122136285Sbrian	}
122236285Sbrian	/*
122336285Sbrian	 * Only privileged root, or (if MNT_USER is set) the user that did the
122436285Sbrian	 * original mount is permitted to unmount this filesystem.
122536285Sbrian	 */
122636285Sbrian	error = vfs_suser(mp, td);
122728679Sbrian	if (error) {
122836285Sbrian		if (coveredvp)
122936285Sbrian			VOP_UNLOCK(coveredvp, 0);
123036285Sbrian		return (error);
123136285Sbrian	}
123236285Sbrian
123336285Sbrian	MNT_ILOCK(mp);
123436285Sbrian	if (mp->mnt_kern_flag & MNTK_UNMOUNT) {
123536285Sbrian		MNT_IUNLOCK(mp);
123636285Sbrian		if (coveredvp)
123736285Sbrian			VOP_UNLOCK(coveredvp, 0);
123836285Sbrian		return (EBUSY);
123936285Sbrian	}
124036285Sbrian	mp->mnt_kern_flag |= MNTK_UNMOUNT | MNTK_NOINSMNTQ;
124136285Sbrian	/* Allow filesystems to detect that a forced unmount is in progress. */
124236285Sbrian	if (flags & MNT_FORCE)
124336285Sbrian		mp->mnt_kern_flag |= MNTK_UNMOUNTF;
124436285Sbrian	error = 0;
124528679Sbrian	if (mp->mnt_lockref) {
124636285Sbrian		if ((flags & MNT_FORCE) == 0) {
124736285Sbrian			mp->mnt_kern_flag &= ~(MNTK_UNMOUNT | MNTK_NOINSMNTQ |
124828679Sbrian			    MNTK_UNMOUNTF);
124928679Sbrian			if (mp->mnt_kern_flag & MNTK_MWAIT) {
125036285Sbrian				mp->mnt_kern_flag &= ~MNTK_MWAIT;
125136285Sbrian				wakeup(mp);
125228679Sbrian			}
125336285Sbrian			MNT_IUNLOCK(mp);
125436285Sbrian			if (coveredvp)
125536285Sbrian				VOP_UNLOCK(coveredvp, 0);
125636285Sbrian			return (EBUSY);
125736285Sbrian		}
125836285Sbrian		mp->mnt_kern_flag |= MNTK_DRAINING;
125936285Sbrian		error = msleep(&mp->mnt_lockref, MNT_MTX(mp), PVFS,
126036285Sbrian		    "mount drain", 0);
126136285Sbrian	}
126236285Sbrian	MNT_IUNLOCK(mp);
126336285Sbrian	KASSERT(mp->mnt_lockref == 0,
126436285Sbrian	    ("%s: invalid lock refcount in the drain path @ %s:%d",
126536285Sbrian	    __func__, __FILE__, __LINE__));
126636285Sbrian	KASSERT(error == 0,
126736285Sbrian	    ("%s: invalid return value for msleep in the drain path @ %s:%d",
126836285Sbrian	    __func__, __FILE__, __LINE__));
126936285Sbrian	vn_start_write(NULL, &mp, V_WAIT);
127036285Sbrian
127136285Sbrian	if (mp->mnt_flag & MNT_EXPUBLIC)
127236285Sbrian		vfs_setpublicfs(NULL, NULL, NULL);
127336285Sbrian
127436285Sbrian	vfs_msync(mp, MNT_WAIT);
127536285Sbrian	MNT_ILOCK(mp);
127636285Sbrian	async_flag = mp->mnt_flag & MNT_ASYNC;
127728679Sbrian	mp->mnt_flag &= ~MNT_ASYNC;
127836285Sbrian	mp->mnt_kern_flag &= ~MNTK_ASYNC;
127936285Sbrian	MNT_IUNLOCK(mp);
128036285Sbrian	cache_purgevfs(mp);	/* remove cache entries for this file sys */
128136285Sbrian	vfs_deallocate_syncvnode(mp);
128236285Sbrian	/*
128336285Sbrian	 * For forced unmounts, move process cdir/rdir refs on the fs root
128436285Sbrian	 * vnode to the covered vnode.  For non-forced unmounts we want
128536285Sbrian	 * such references to cause an EBUSY error.
128636285Sbrian	 */
128736285Sbrian	if ((flags & MNT_FORCE) &&
128836285Sbrian	    VFS_ROOT(mp, LK_EXCLUSIVE, &fsrootvp) == 0) {
128936285Sbrian		if (mp->mnt_vnodecovered != NULL)
129036285Sbrian			mountcheckdirs(fsrootvp, mp->mnt_vnodecovered);
129136285Sbrian		if (fsrootvp == rootvnode) {
129236285Sbrian			vrele(rootvnode);
129336285Sbrian			rootvnode = NULL;
129436285Sbrian		}
129536285Sbrian		vput(fsrootvp);
129636285Sbrian	}
129736285Sbrian	if (((mp->mnt_flag & MNT_RDONLY) ||
129836285Sbrian	     (error = VFS_SYNC(mp, MNT_WAIT)) == 0) || (flags & MNT_FORCE) != 0)
129936285Sbrian		error = VFS_UNMOUNT(mp, flags);
130036285Sbrian	vn_finished_write(mp);
130129696Sbrian	/*
130236285Sbrian	 * If we failed to flush the dirty blocks for this mount point,
130337020Sbrian	 * undo all the cdir/rdir and rootvnode changes we made above.
130436285Sbrian	 * Unless we failed to do so because the device is reporting that
130536285Sbrian	 * it doesn't exist anymore.
130636285Sbrian	 */
130736285Sbrian	if (error && error != ENXIO) {
130836285Sbrian		if ((flags & MNT_FORCE) &&
130936285Sbrian		    VFS_ROOT(mp, LK_EXCLUSIVE, &fsrootvp) == 0) {
131036285Sbrian			if (mp->mnt_vnodecovered != NULL)
131129696Sbrian				mountcheckdirs(mp->mnt_vnodecovered, fsrootvp);
131228679Sbrian			if (rootvnode == NULL) {
131336285Sbrian				rootvnode = fsrootvp;
131436285Sbrian				vref(rootvnode);
131536285Sbrian			}
131636285Sbrian			vput(fsrootvp);
131736285Sbrian		}
131836285Sbrian		MNT_ILOCK(mp);
131936285Sbrian		mp->mnt_kern_flag &= ~MNTK_NOINSMNTQ;
132036285Sbrian		if ((mp->mnt_flag & MNT_RDONLY) == 0) {
132136285Sbrian			MNT_IUNLOCK(mp);
132236285Sbrian			vfs_allocate_syncvnode(mp);
132328679Sbrian			MNT_ILOCK(mp);
132436285Sbrian		}
132536285Sbrian		mp->mnt_kern_flag &= ~(MNTK_UNMOUNT | MNTK_UNMOUNTF);
132636285Sbrian		mp->mnt_flag |= async_flag;
132736285Sbrian		if ((mp->mnt_flag & MNT_ASYNC) != 0 && mp->mnt_noasync == 0)
132836285Sbrian			mp->mnt_kern_flag |= MNTK_ASYNC;
132936285Sbrian		if (mp->mnt_kern_flag & MNTK_MWAIT) {
133036285Sbrian			mp->mnt_kern_flag &= ~MNTK_MWAIT;
133136285Sbrian			wakeup(mp);
133236285Sbrian		}
133336285Sbrian		MNT_IUNLOCK(mp);
133436285Sbrian		if (coveredvp)
133536285Sbrian			VOP_UNLOCK(coveredvp, 0);
133636285Sbrian		return (error);
133736285Sbrian	}
133836285Sbrian	mtx_lock(&mountlist_mtx);
133936285Sbrian	TAILQ_REMOVE(&mountlist, mp, mnt_list);
134036285Sbrian	mtx_unlock(&mountlist_mtx);
134136285Sbrian	if (coveredvp != NULL) {
134236285Sbrian		coveredvp->v_mountedhere = NULL;
134336285Sbrian		vput(coveredvp);
134436285Sbrian	}
134536285Sbrian	vfs_event_signal(NULL, VQ_UNMOUNT, 0);
134636285Sbrian	vfs_mount_destroy(mp);
134736285Sbrian	return (0);
134828679Sbrian}
134936285Sbrian
135036285Sbrian/*
135128679Sbrian * Report errors during filesystem mounting.
135228679Sbrian */
135336285Sbrianvoid
135436285Sbrianvfs_mount_error(struct mount *mp, const char *fmt, ...)
135528679Sbrian{
135636285Sbrian	struct vfsoptlist *moptlist = mp->mnt_optnew;
135736285Sbrian	va_list ap;
135836285Sbrian	int error, len;
135936285Sbrian	char *errmsg;
136036285Sbrian
136136285Sbrian	error = vfs_getopt(moptlist, "errmsg", (void **)&errmsg, &len);
136236285Sbrian	if (error || errmsg == NULL || len <= 0)
136329549Sbrian		return;
136436285Sbrian
136536285Sbrian	va_start(ap, fmt);
136636285Sbrian	vsnprintf(errmsg, (size_t)len, fmt, ap);
136736285Sbrian	va_end(ap);
136836285Sbrian}
136936285Sbrian
137036285Sbrianvoid
137136285Sbrianvfs_opterror(struct vfsoptlist *opts, const char *fmt, ...)
137236285Sbrian{
137336285Sbrian	va_list ap;
137436285Sbrian	int error, len;
137536285Sbrian	char *errmsg;
137636285Sbrian
137736285Sbrian	error = vfs_getopt(opts, "errmsg", (void **)&errmsg, &len);
137836285Sbrian	if (error || errmsg == NULL || len <= 0)
137936285Sbrian		return;
138036285Sbrian
138136285Sbrian	va_start(ap, fmt);
138236285Sbrian	vsnprintf(errmsg, (size_t)len, fmt, ap);
138336285Sbrian	va_end(ap);
138436285Sbrian}
138536285Sbrian
138636285Sbrian/*
138736285Sbrian * ---------------------------------------------------------------------
138836285Sbrian * Functions for querying mount options/arguments from filesystems.
138936285Sbrian */
139036285Sbrian
139136285Sbrian/*
139236285Sbrian * Check that no unknown options are given
139336285Sbrian */
139436285Sbrianint
139536285Sbrianvfs_filteropt(struct vfsoptlist *opts, const char **legal)
139636285Sbrian{
139736285Sbrian	struct vfsopt *opt;
139836285Sbrian	char errmsg[255];
139936285Sbrian	const char **t, *p, *q;
140036285Sbrian	int ret = 0;
140136285Sbrian
140236285Sbrian	TAILQ_FOREACH(opt, opts, link) {
140336285Sbrian		p = opt->name;
140436285Sbrian		q = NULL;
140536285Sbrian		if (p[0] == 'n' && p[1] == 'o')
140636285Sbrian			q = p + 2;
140736285Sbrian		for(t = global_opts; *t != NULL; t++) {
140836285Sbrian			if (strcmp(*t, p) == 0)
140936285Sbrian				break;
141036285Sbrian			if (q != NULL) {
141136285Sbrian				if (strcmp(*t, q) == 0)
141236285Sbrian					break;
141336285Sbrian			}
141436285Sbrian		}
141536285Sbrian		if (*t != NULL)
141636285Sbrian			continue;
141736285Sbrian		for(t = legal; *t != NULL; t++) {
141836285Sbrian			if (strcmp(*t, p) == 0)
141936285Sbrian				break;
142036285Sbrian			if (q != NULL) {
142136285Sbrian				if (strcmp(*t, q) == 0)
142236285Sbrian					break;
142336285Sbrian			}
142436285Sbrian		}
142536285Sbrian		if (*t != NULL)
142636285Sbrian			continue;
142736285Sbrian		snprintf(errmsg, sizeof(errmsg),
142836285Sbrian		    "mount option <%s> is unknown", p);
142936285Sbrian		ret = EINVAL;
143036285Sbrian	}
143136285Sbrian	if (ret != 0) {
143236285Sbrian		TAILQ_FOREACH(opt, opts, link) {
143336285Sbrian			if (strcmp(opt->name, "errmsg") == 0) {
14346059Samurai				strncpy((char *)opt->value, errmsg, opt->len);
143536285Sbrian				break;
143636285Sbrian			}
14376059Samurai		}
14386059Samurai		if (opt == NULL)
143928679Sbrian			printf("%s\n", errmsg);
144031343Sbrian	}
144120812Sjkh	return (ret);
144236285Sbrian}
144336285Sbrian
144436285Sbrian/*
144536285Sbrian * Get a mount option by its name.
144636285Sbrian *
144720812Sjkh * Return 0 if the option was found, ENOENT otherwise.
144826516Sbrian * If len is non-NULL it will be filled with the length
144926516Sbrian * of the option. If buf is non-NULL, it will be filled
145020812Sjkh * with the address of the option.
145126516Sbrian */
145220812Sjkhint
145320812Sjkhvfs_getopt(opts, name, buf, len)
145430715Sbrian	struct vfsoptlist *opts;
145536285Sbrian	const char *name;
145636285Sbrian	void **buf;
145728679Sbrian	int *len;
145836285Sbrian{
145928679Sbrian	struct vfsopt *opt;
146036285Sbrian
146136285Sbrian	KASSERT(opts != NULL, ("vfs_getopt: caller passed 'opts' as NULL"));
146236285Sbrian
146336285Sbrian	TAILQ_FOREACH(opt, opts, link) {
146436285Sbrian		if (strcmp(name, opt->name) == 0) {
146536285Sbrian			opt->seen = 1;
146636285Sbrian			if (len != NULL)
146736285Sbrian				*len = opt->len;
146836285Sbrian			if (buf != NULL)
146936285Sbrian				*buf = opt->value;
147036285Sbrian			return (0);
147136285Sbrian		}
147236285Sbrian	}
147336285Sbrian	return (ENOENT);
147436285Sbrian}
147536285Sbrian
147636285Sbrianint
147736285Sbrianvfs_getopt_pos(struct vfsoptlist *opts, const char *name)
147836285Sbrian{
147936285Sbrian	struct vfsopt *opt;
148036285Sbrian
148136285Sbrian	if (opts == NULL)
148236285Sbrian		return (-1);
148336285Sbrian
148436285Sbrian	TAILQ_FOREACH(opt, opts, link) {
148536285Sbrian		if (strcmp(name, opt->name) == 0) {
148636285Sbrian			opt->seen = 1;
148736285Sbrian			return (opt->pos);
148836285Sbrian		}
148936285Sbrian	}
149036285Sbrian	return (-1);
149131343Sbrian}
149236285Sbrian
149336285Sbrianchar *
149436285Sbrianvfs_getopts(struct vfsoptlist *opts, const char *name, int *error)
149536285Sbrian{
149636712Sbrian	struct vfsopt *opt;
149736712Sbrian
149836712Sbrian	*error = 0;
149936285Sbrian	TAILQ_FOREACH(opt, opts, link) {
150036285Sbrian		if (strcmp(name, opt->name) != 0)
150136285Sbrian			continue;
150236285Sbrian		opt->seen = 1;
150336285Sbrian		if (opt->len == 0 ||
150436285Sbrian		    ((char *)opt->value)[opt->len - 1] != '\0') {
150536285Sbrian			*error = EINVAL;
150636285Sbrian			return (NULL);
150736285Sbrian		}
150836285Sbrian		return (opt->value);
150936285Sbrian	}
151036285Sbrian	*error = ENOENT;
151136285Sbrian	return (NULL);
151236285Sbrian}
151336285Sbrian
151436285Sbrianint
151536285Sbrianvfs_flagopt(struct vfsoptlist *opts, const char *name, u_int *w, u_int val)
151636285Sbrian{
151736285Sbrian	struct vfsopt *opt;
151836285Sbrian
151936285Sbrian	TAILQ_FOREACH(opt, opts, link) {
152036285Sbrian		if (strcmp(name, opt->name) == 0) {
152136285Sbrian			opt->seen = 1;
152236285Sbrian			if (w != NULL)
152336285Sbrian				*w |= val;
152436285Sbrian			return (1);
152528679Sbrian		}
152636774Sbrian	}
152736285Sbrian	if (w != NULL)
152836285Sbrian		*w &= ~val;
152936285Sbrian	return (0);
153036285Sbrian}
153136285Sbrian
153236285Sbrianint
153336285Sbrianvfs_scanopt(struct vfsoptlist *opts, const char *name, const char *fmt, ...)
153436285Sbrian{
153536285Sbrian	va_list ap;
153636285Sbrian	struct vfsopt *opt;
153728679Sbrian	int ret;
153831343Sbrian
153928679Sbrian	KASSERT(opts != NULL, ("vfs_getopt: caller passed 'opts' as NULL"));
15406059Samurai
15416059Samurai	TAILQ_FOREACH(opt, opts, link) {
15426059Samurai		if (strcmp(name, opt->name) != 0)
154331343Sbrian			continue;
15446059Samurai		opt->seen = 1;
154536285Sbrian		if (opt->len == 0 || opt->value == NULL)
154636285Sbrian			return (0);
154736285Sbrian		if (((char *)opt->value)[opt->len - 1] != '\0')
154836285Sbrian			return (0);
154936285Sbrian		va_start(ap, fmt);
155026516Sbrian		ret = vsscanf(opt->value, fmt, ap);
15516059Samurai		va_end(ap);
155236285Sbrian		return (ret);
155326516Sbrian	}
155426516Sbrian	return (0);
15556059Samurai}
15566059Samurai
15576059Samuraiint
15586059Samuraivfs_setopt(struct vfsoptlist *opts, const char *name, void *value, int len)
155931343Sbrian{
15606059Samurai	struct vfsopt *opt;
15616059Samurai
156236285Sbrian	TAILQ_FOREACH(opt, opts, link) {
15636059Samurai		if (strcmp(name, opt->name) != 0)
156436285Sbrian			continue;
156531598Sbrian		opt->seen = 1;
156631598Sbrian		if (opt->value == NULL)
156736285Sbrian			opt->len = len;
156836285Sbrian		else {
156936285Sbrian			if (opt->len != len)
157036285Sbrian				return (EINVAL);
157131598Sbrian			bcopy(value, opt->value, len);
157236285Sbrian		}
157336285Sbrian		return (0);
157436285Sbrian	}
157536285Sbrian	return (ENOENT);
157636285Sbrian}
157736285Sbrian
157836285Sbrianint
157936285Sbrianvfs_setopt_part(struct vfsoptlist *opts, const char *name, void *value, int len)
158036285Sbrian{
158131598Sbrian	struct vfsopt *opt;
158236285Sbrian
158334536Sbrian	TAILQ_FOREACH(opt, opts, link) {
158436285Sbrian		if (strcmp(name, opt->name) != 0)
158536285Sbrian			continue;
158636285Sbrian		opt->seen = 1;
158736285Sbrian		if (opt->value == NULL)
158836285Sbrian			opt->len = len;
158936285Sbrian		else {
159036285Sbrian			if (opt->len < len)
159136285Sbrian				return (EINVAL);
159236285Sbrian			opt->len = len;
159331598Sbrian			bcopy(value, opt->value, len);
15946059Samurai		}
159536285Sbrian		return (0);
159636285Sbrian	}
159736285Sbrian	return (ENOENT);
159836285Sbrian}
159936285Sbrian
160031598Sbrianint
160131598Sbrianvfs_setopts(struct vfsoptlist *opts, const char *name, const char *value)
160236285Sbrian{
160336285Sbrian	struct vfsopt *opt;
160436285Sbrian
160536285Sbrian	TAILQ_FOREACH(opt, opts, link) {
160636285Sbrian		if (strcmp(name, opt->name) != 0)
160736285Sbrian			continue;
160831598Sbrian		opt->seen = 1;
16096059Samurai		if (opt->value == NULL)
16106059Samurai			opt->len = strlen(value) + 1;
16116059Samurai		else if (strlcpy(opt->value, value, opt->len) >= opt->len)
161231343Sbrian			return (EINVAL);
16136059Samurai		return (0);
161431598Sbrian	}
161536285Sbrian	return (ENOENT);
16166059Samurai}
161736285Sbrian
161836285Sbrian/*
161936285Sbrian * Find and copy a mount option.
162036285Sbrian *
162136285Sbrian * The size of the buffer has to be specified
162236285Sbrian * in len, if it is not the same length as the
162336285Sbrian * mount option, EINVAL is returned.
162436285Sbrian * Returns ENOENT if the option is not found.
162536285Sbrian */
162636285Sbrianint
162736285Sbrianvfs_copyopt(opts, name, dest, len)
162836285Sbrian	struct vfsoptlist *opts;
162936285Sbrian	const char *name;
163036285Sbrian	void *dest;
163136285Sbrian	int len;
163236285Sbrian{
163336285Sbrian	struct vfsopt *opt;
163436285Sbrian
163536285Sbrian	KASSERT(opts != NULL, ("vfs_copyopt: caller passed 'opts' as NULL"));
163631598Sbrian
163736285Sbrian	TAILQ_FOREACH(opt, opts, link) {
163836285Sbrian		if (strcmp(name, opt->name) == 0) {
163936285Sbrian			opt->seen = 1;
164031598Sbrian			if (len != opt->len)
164134536Sbrian				return (EINVAL);
164226516Sbrian			bcopy(opt->value, dest, opt->len);
164326516Sbrian			return (0);
164426516Sbrian		}
16456059Samurai	}
16466059Samurai	return (ENOENT);
164731343Sbrian}
164826031Sbrian
164926031Sbrian/*
165036285Sbrian * This is a helper function for filesystems to traverse their
165136285Sbrian * vnodes.  See MNT_VNODE_FOREACH() in sys/mount.h
165236285Sbrian */
165336285Sbrian
165436285Sbrianstruct vnode *
165528679Sbrian__mnt_vnode_next(struct vnode **mvp, struct mount *mp)
165636285Sbrian{
165728679Sbrian	struct vnode *vp;
165836285Sbrian
165936285Sbrian	mtx_assert(MNT_MTX(mp), MA_OWNED);
166036285Sbrian
166136285Sbrian	KASSERT((*mvp)->v_mount == mp, ("marker vnode mount list mismatch"));
166228679Sbrian	if ((*mvp)->v_yield++ == 500) {
166336285Sbrian		MNT_IUNLOCK(mp);
166436285Sbrian		(*mvp)->v_yield = 0;
166536285Sbrian		uio_yield();
166636285Sbrian		MNT_ILOCK(mp);
166736285Sbrian	}
166836285Sbrian	vp = TAILQ_NEXT(*mvp, v_nmntvnodes);
166928679Sbrian	while (vp != NULL && vp->v_type == VMARKER)
167036285Sbrian		vp = TAILQ_NEXT(vp, v_nmntvnodes);
167136285Sbrian
167228679Sbrian	/* Check if we are done */
167336285Sbrian	if (vp == NULL) {
167428679Sbrian		__mnt_vnode_markerfree(mvp, mp);
167526031Sbrian		return (NULL);
167626031Sbrian	}
167726031Sbrian	TAILQ_REMOVE(&mp->mnt_nvnodelist, *mvp, v_nmntvnodes);
167826031Sbrian	TAILQ_INSERT_AFTER(&mp->mnt_nvnodelist, vp, *mvp, v_nmntvnodes);
167931343Sbrian	return (vp);
168026031Sbrian}
168136285Sbrian
168236285Sbrianstruct vnode *
168336285Sbrian__mnt_vnode_first(struct vnode **mvp, struct mount *mp)
168436285Sbrian{
168536285Sbrian	struct vnode *vp;
168636285Sbrian
168726031Sbrian	mtx_assert(MNT_MTX(mp), MA_OWNED);
168836285Sbrian
168926516Sbrian	vp = TAILQ_FIRST(&mp->mnt_nvnodelist);
169026516Sbrian	while (vp != NULL && vp->v_type == VMARKER)
169126031Sbrian		vp = TAILQ_NEXT(vp, v_nmntvnodes);
169226031Sbrian
169326031Sbrian	/* Check if we are done */
169431343Sbrian	if (vp == NULL) {
169526031Sbrian		*mvp = NULL;
169636285Sbrian		return (NULL);
169736285Sbrian	}
169837191Sbrian	MNT_REF(mp);
169937191Sbrian	MNT_IUNLOCK(mp);
170036285Sbrian	*mvp = (struct vnode *) malloc(sizeof(struct vnode),
170137191Sbrian				       M_VNODE_MARKER,
170226516Sbrian				       M_WAITOK | M_ZERO);
170326142Sbrian	MNT_ILOCK(mp);
170435449Sbrian	(*mvp)->v_type = VMARKER;
170536285Sbrian
170626516Sbrian	vp = TAILQ_FIRST(&mp->mnt_nvnodelist);
170726031Sbrian	while (vp != NULL && vp->v_type == VMARKER)
170826031Sbrian		vp = TAILQ_NEXT(vp, v_nmntvnodes);
170926031Sbrian
171026031Sbrian	/* Check if we are done */
171131343Sbrian	if (vp == NULL) {
171226031Sbrian		MNT_IUNLOCK(mp);
171336285Sbrian		free(*mvp, M_VNODE_MARKER);
171436285Sbrian		MNT_ILOCK(mp);
171536285Sbrian		*mvp = NULL;
171637191Sbrian		MNT_REL(mp);
171737191Sbrian		return (NULL);
171828679Sbrian	}
171928679Sbrian	(*mvp)->v_mount = mp;
172036285Sbrian	TAILQ_INSERT_AFTER(&mp->mnt_nvnodelist, vp, *mvp, v_nmntvnodes);
172136285Sbrian	return (vp);
172237191Sbrian}
172337191Sbrian
172428679Sbrian
172528679Sbrianvoid
172636285Sbrian__mnt_vnode_markerfree(struct vnode **mvp, struct mount *mp)
172728679Sbrian{
172835449Sbrian
172928679Sbrian	if (*mvp == NULL)
173026031Sbrian		return;
173131343Sbrian
173231121Sbrian	mtx_assert(MNT_MTX(mp), MA_OWNED);
173331121Sbrian
173436285Sbrian	KASSERT((*mvp)->v_mount == mp, ("marker vnode mount list mismatch"));
173536285Sbrian	TAILQ_REMOVE(&mp->mnt_nvnodelist, *mvp, v_nmntvnodes);
173631121Sbrian	MNT_IUNLOCK(mp);
173731121Sbrian	free(*mvp, M_VNODE_MARKER);
173831121Sbrian	MNT_ILOCK(mp);
173931343Sbrian	*mvp = NULL;
174031121Sbrian	MNT_REL(mp);
174131121Sbrian}
174231121Sbrian
174331121Sbrian
174431343Sbrianint
174531121Sbrian__vfs_statfs(struct mount *mp, struct statfs *sbp)
174636285Sbrian{
174736285Sbrian	int error;
174836285Sbrian
174936285Sbrian	error = mp->mnt_op->vfs_statfs(mp, &mp->mnt_stat);
175036285Sbrian	if (sbp != &mp->mnt_stat)
175136285Sbrian		*sbp = mp->mnt_stat;
175236285Sbrian	return (error);
175331121Sbrian}
175436285Sbrian
175531121Sbrianvoid
175631121Sbrianvfs_mountedfrom(struct mount *mp, const char *from)
175731121Sbrian{
175836285Sbrian
175936285Sbrian	bzero(mp->mnt_stat.f_mntfromname, sizeof mp->mnt_stat.f_mntfromname);
176036285Sbrian	strlcpy(mp->mnt_stat.f_mntfromname, from,
176136285Sbrian	    sizeof mp->mnt_stat.f_mntfromname);
176236285Sbrian}
176336285Sbrian
176436285Sbrian/*
176536285Sbrian * ---------------------------------------------------------------------
176636285Sbrian * This is the api for building mount args and mounting filesystems from
176736285Sbrian * inside the kernel.
176836285Sbrian *
176936285Sbrian * The API works by accumulation of individual args.  First error is
177036285Sbrian * latched.
177136285Sbrian *
177236285Sbrian * XXX: should be documented in new manpage kernel_mount(9)
177336285Sbrian */
177436285Sbrian
177536285Sbrian/* A memory allocation which must be freed when we are done */
177636285Sbrianstruct mntaarg {
177736285Sbrian	SLIST_ENTRY(mntaarg)	next;
177836285Sbrian};
177936285Sbrian
178036285Sbrian/* The header for the mount arguments */
178136285Sbrianstruct mntarg {
178236285Sbrian	struct iovec *v;
178336285Sbrian	int len;
178436285Sbrian	int error;
178536285Sbrian	SLIST_HEAD(, mntaarg)	list;
178636285Sbrian};
178736285Sbrian
178836285Sbrian/*
178936285Sbrian * Add a boolean argument.
179036285Sbrian *
179136285Sbrian * flag is the boolean value.
179236285Sbrian * name must start with "no".
179336285Sbrian */
179436285Sbrianstruct mntarg *
179536285Sbrianmount_argb(struct mntarg *ma, int flag, const char *name)
179636285Sbrian{
179736285Sbrian
179836285Sbrian	KASSERT(name[0] == 'n' && name[1] == 'o',
179936285Sbrian	    ("mount_argb(...,%s): name must start with 'no'", name));
180036285Sbrian
180136285Sbrian	return (mount_arg(ma, name + (flag ? 2 : 0), NULL, 0));
180236285Sbrian}
180336285Sbrian
180436285Sbrian/*
180536285Sbrian * Add an argument printf style
180636285Sbrian */
180736285Sbrianstruct mntarg *
180836285Sbrianmount_argf(struct mntarg *ma, const char *name, const char *fmt, ...)
180936285Sbrian{
181036285Sbrian	va_list ap;
181136285Sbrian	struct mntaarg *maa;
181236285Sbrian	struct sbuf *sb;
181336285Sbrian	int len;
181436285Sbrian
181536285Sbrian	if (ma == NULL) {
181636285Sbrian		ma = malloc(sizeof *ma, M_MOUNT, M_WAITOK | M_ZERO);
181736285Sbrian		SLIST_INIT(&ma->list);
181836285Sbrian	}
181936285Sbrian	if (ma->error)
182036285Sbrian		return (ma);
182136285Sbrian
182236285Sbrian	ma->v = realloc(ma->v, sizeof *ma->v * (ma->len + 2),
182336285Sbrian	    M_MOUNT, M_WAITOK);
182436285Sbrian	ma->v[ma->len].iov_base = (void *)(uintptr_t)name;
182536285Sbrian	ma->v[ma->len].iov_len = strlen(name) + 1;
182636285Sbrian	ma->len++;
182736285Sbrian
182836285Sbrian	sb = sbuf_new_auto();
182936285Sbrian	va_start(ap, fmt);
183036285Sbrian	sbuf_vprintf(sb, fmt, ap);
183136285Sbrian	va_end(ap);
183236285Sbrian	sbuf_finish(sb);
183336285Sbrian	len = sbuf_len(sb) + 1;
183436285Sbrian	maa = malloc(sizeof *maa + len, M_MOUNT, M_WAITOK | M_ZERO);
183536285Sbrian	SLIST_INSERT_HEAD(&ma->list, maa, next);
183636285Sbrian	bcopy(sbuf_data(sb), maa + 1, len);
183736285Sbrian	sbuf_delete(sb);
183836285Sbrian
183936285Sbrian	ma->v[ma->len].iov_base = maa + 1;
184036285Sbrian	ma->v[ma->len].iov_len = len;
184136285Sbrian	ma->len++;
184236285Sbrian
184336285Sbrian	return (ma);
184436285Sbrian}
184536285Sbrian
184636285Sbrian/*
184736285Sbrian * Add an argument which is a userland string.
184836285Sbrian */
184936285Sbrianstruct mntarg *
185036285Sbrianmount_argsu(struct mntarg *ma, const char *name, const void *val, int len)
185136285Sbrian{
185236285Sbrian	struct mntaarg *maa;
185336285Sbrian	char *tbuf;
185436285Sbrian
185536285Sbrian	if (val == NULL)
185636285Sbrian		return (ma);
185736285Sbrian	if (ma == NULL) {
185836285Sbrian		ma = malloc(sizeof *ma, M_MOUNT, M_WAITOK | M_ZERO);
185936285Sbrian		SLIST_INIT(&ma->list);
186036285Sbrian	}
186136285Sbrian	if (ma->error)
186236285Sbrian		return (ma);
186336285Sbrian	maa = malloc(sizeof *maa + len, M_MOUNT, M_WAITOK | M_ZERO);
186436285Sbrian	SLIST_INSERT_HEAD(&ma->list, maa, next);
186536285Sbrian	tbuf = (void *)(maa + 1);
186636285Sbrian	ma->error = copyinstr(val, tbuf, len, NULL);
186736285Sbrian	return (mount_arg(ma, name, tbuf, -1));
186836285Sbrian}
186936285Sbrian
187036285Sbrian/*
187136285Sbrian * Plain argument.
187236285Sbrian *
187336285Sbrian * If length is -1, treat value as a C string.
187436285Sbrian */
187536285Sbrianstruct mntarg *
187636285Sbrianmount_arg(struct mntarg *ma, const char *name, const void *val, int len)
187736285Sbrian{
187836285Sbrian
187936285Sbrian	if (ma == NULL) {
188036285Sbrian		ma = malloc(sizeof *ma, M_MOUNT, M_WAITOK | M_ZERO);
188136285Sbrian		SLIST_INIT(&ma->list);
188236285Sbrian	}
188336285Sbrian	if (ma->error)
188436285Sbrian		return (ma);
188536285Sbrian
188636285Sbrian	ma->v = realloc(ma->v, sizeof *ma->v * (ma->len + 2),
188736285Sbrian	    M_MOUNT, M_WAITOK);
188836285Sbrian	ma->v[ma->len].iov_base = (void *)(uintptr_t)name;
188936285Sbrian	ma->v[ma->len].iov_len = strlen(name) + 1;
189036285Sbrian	ma->len++;
189136285Sbrian
189236285Sbrian	ma->v[ma->len].iov_base = (void *)(uintptr_t)val;
189336285Sbrian	if (len < 0)
189436285Sbrian		ma->v[ma->len].iov_len = strlen(val) + 1;
189536285Sbrian	else
189636285Sbrian		ma->v[ma->len].iov_len = len;
189736285Sbrian	ma->len++;
189836285Sbrian	return (ma);
189936285Sbrian}
190036285Sbrian
190136285Sbrian/*
190236285Sbrian * Free a mntarg structure
190336285Sbrian */
190436285Sbrianstatic void
190536285Sbrianfree_mntarg(struct mntarg *ma)
190636285Sbrian{
190736285Sbrian	struct mntaarg *maa;
190836285Sbrian
190936285Sbrian	while (!SLIST_EMPTY(&ma->list)) {
191036285Sbrian		maa = SLIST_FIRST(&ma->list);
191136285Sbrian		SLIST_REMOVE_HEAD(&ma->list, next);
191236285Sbrian		free(maa, M_MOUNT);
191336285Sbrian	}
191436285Sbrian	free(ma->v, M_MOUNT);
191536285Sbrian	free(ma, M_MOUNT);
191636285Sbrian}
191736285Sbrian
191836285Sbrian/*
191936285Sbrian * Mount a filesystem
192036285Sbrian */
192136285Sbrianint
192236285Sbriankernel_mount(struct mntarg *ma, int flags)
192336285Sbrian{
192436285Sbrian	struct uio auio;
192536285Sbrian	int error;
192636285Sbrian
192736285Sbrian	KASSERT(ma != NULL, ("kernel_mount NULL ma"));
192836285Sbrian	KASSERT(ma->v != NULL, ("kernel_mount NULL ma->v"));
192936285Sbrian	KASSERT(!(ma->len & 1), ("kernel_mount odd ma->len (%d)", ma->len));
193036285Sbrian
193136285Sbrian	auio.uio_iov = ma->v;
193236285Sbrian	auio.uio_iovcnt = ma->len;
193336285Sbrian	auio.uio_segflg = UIO_SYSSPACE;
193436285Sbrian
193536285Sbrian	error = ma->error;
193636285Sbrian	if (!error)
193736285Sbrian		error = vfs_donmount(curthread, flags, &auio);
193836285Sbrian	free_mntarg(ma);
193936285Sbrian	return (error);
194036285Sbrian}
194136285Sbrian
194236285Sbrian/*
194336285Sbrian * A printflike function to mount a filesystem.
194436285Sbrian */
194536285Sbrianint
194636285Sbriankernel_vmount(int flags, ...)
194736285Sbrian{
194836285Sbrian	struct mntarg *ma = NULL;
194936285Sbrian	va_list ap;
195036285Sbrian	const char *cp;
195136285Sbrian	const void *vp;
195236285Sbrian	int error;
195336285Sbrian
195436285Sbrian	va_start(ap, flags);
195536285Sbrian	for (;;) {
195636285Sbrian		cp = va_arg(ap, const char *);
195736285Sbrian		if (cp == NULL)
195836285Sbrian			break;
195936285Sbrian		vp = va_arg(ap, const void *);
196036285Sbrian		ma = mount_arg(ma, cp, vp, (vp != NULL ? -1 : 0));
196136285Sbrian	}
196236285Sbrian	va_end(ap);
196336285Sbrian
196436285Sbrian	error = kernel_mount(ma, flags);
196536285Sbrian	return (error);
196636285Sbrian}
196736285Sbrian
196836285Sbrianvoid
196936285Sbrianvfs_oexport_conv(const struct oexport_args *oexp, struct export_args *exp)
197036285Sbrian{
197136285Sbrian
197236285Sbrian	bcopy(oexp, exp, sizeof(*oexp));
197336285Sbrian	exp->ex_numsecflavors = 0;
197436285Sbrian}
197536285Sbrian