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