vfs_mount.c revision 243719
190075Sobrien/*- 2110611Skan * Copyright (c) 1999-2004 Poul-Henning Kamp 390075Sobrien * Copyright (c) 1999 Michael Smith 490075Sobrien * Copyright (c) 1989, 1993 590075Sobrien * The Regents of the University of California. All rights reserved. 690075Sobrien * (c) UNIX System Laboratories, Inc. 790075Sobrien * All or some portions of this file are derived from material licensed 890075Sobrien * to the University of California by American Telephone and Telegraph 990075Sobrien * Co. or Unix System Laboratories, Inc. and are reproduced herein with 1090075Sobrien * the permission of UNIX System Laboratories, Inc. 1190075Sobrien * 1290075Sobrien * Redistribution and use in source and binary forms, with or without 1390075Sobrien * modification, are permitted provided that the following conditions 1490075Sobrien * are met: 1590075Sobrien * 1. Redistributions of source code must retain the above copyright 1690075Sobrien * notice, this list of conditions and the following disclaimer. 1790075Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1890075Sobrien * notice, this list of conditions and the following disclaimer in the 1990075Sobrien * documentation and/or other materials provided with the distribution. 2090075Sobrien * 4. Neither the name of the University nor the names of its contributors 2190075Sobrien * may be used to endorse or promote products derived from this software 2290075Sobrien * without specific prior written permission. 2390075Sobrien * 2490075Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 2590075Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2690075Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2790075Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2890075Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2990075Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 3090075Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3190075Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3290075Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3390075Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3490075Sobrien * SUCH DAMAGE. 3590075Sobrien */ 3690075Sobrien 3790075Sobrien#include <sys/cdefs.h> 3890075Sobrien__FBSDID("$FreeBSD: head/sys/kern/vfs_mount.c 243719 2012-11-30 22:49:28Z pjd $"); 3990075Sobrien 4090075Sobrien#include <sys/param.h> 4190075Sobrien#include <sys/conf.h> 4290075Sobrien#include <sys/fcntl.h> 4390075Sobrien#include <sys/jail.h> 4490075Sobrien#include <sys/kernel.h> 4590075Sobrien#include <sys/libkern.h> 4690075Sobrien#include <sys/malloc.h> 4790075Sobrien#include <sys/mount.h> 4890075Sobrien#include <sys/mutex.h> 4990075Sobrien#include <sys/namei.h> 5090075Sobrien#include <sys/priv.h> 5190075Sobrien#include <sys/proc.h> 5290075Sobrien#include <sys/filedesc.h> 5390075Sobrien#include <sys/reboot.h> 5490075Sobrien#include <sys/sbuf.h> 5590075Sobrien#include <sys/syscallsubr.h> 5690075Sobrien#include <sys/sysproto.h> 5790075Sobrien#include <sys/sx.h> 5890075Sobrien#include <sys/sysctl.h> 5990075Sobrien#include <sys/sysent.h> 6090075Sobrien#include <sys/systm.h> 6190075Sobrien#include <sys/vnode.h> 6290075Sobrien#include <vm/uma.h> 6390075Sobrien 6490075Sobrien#include <geom/geom.h> 6590075Sobrien 6690075Sobrien#include <machine/stdarg.h> 6790075Sobrien 6890075Sobrien#include <security/audit/audit.h> 6990075Sobrien#include <security/mac/mac_framework.h> 7090075Sobrien 7190075Sobrien#define VFS_MOUNTARG_SIZE_MAX (1024 * 64) 7290075Sobrien 7390075Sobrienstatic int vfs_domount(struct thread *td, const char *fstype, char *fspath, 7490075Sobrien uint64_t fsflags, struct vfsoptlist **optlist); 7590075Sobrienstatic void free_mntarg(struct mntarg *ma); 7690075Sobrien 7790075Sobrienstatic int usermount = 0; 7890075SobrienSYSCTL_INT(_vfs, OID_AUTO, usermount, CTLFLAG_RW, &usermount, 0, 7990075Sobrien "Unprivileged users may mount and unmount file systems"); 8090075Sobrien 8190075SobrienMALLOC_DEFINE(M_MOUNT, "mount", "vfs mount structure"); 8290075Sobrienstatic uma_zone_t mount_zone; 8390075Sobrien 8490075Sobrien/* List of mounted filesystems. */ 8590075Sobrienstruct mntlist mountlist = TAILQ_HEAD_INITIALIZER(mountlist); 8690075Sobrien 8790075Sobrien/* For any iteration/modification of mountlist */ 8890075Sobrienstruct mtx mountlist_mtx; 8990075SobrienMTX_SYSINIT(mountlist, &mountlist_mtx, "mountlist", MTX_DEF); 9090075Sobrien 9190075Sobrien/* 9290075Sobrien * Global opts, taken by all filesystems 9390075Sobrien */ 9490075Sobrienstatic const char *global_opts[] = { 9590075Sobrien "errmsg", 9690075Sobrien "fstype", 9790075Sobrien "fspath", 9890075Sobrien "ro", 9990075Sobrien "rw", 10090075Sobrien "nosuid", 10190075Sobrien "noexec", 10290075Sobrien NULL 10390075Sobrien}; 10490075Sobrien 10590075Sobrienstatic int 10690075Sobrienmount_init(void *mem, int size, int flags) 10790075Sobrien{ 10890075Sobrien struct mount *mp; 10990075Sobrien 11090075Sobrien mp = (struct mount *)mem; 11190075Sobrien mtx_init(&mp->mnt_mtx, "struct mount mtx", NULL, MTX_DEF); 11290075Sobrien lockinit(&mp->mnt_explock, PVFS, "explock", 0, 0); 11390075Sobrien return (0); 11490075Sobrien} 11590075Sobrien 11690075Sobrienstatic void 11790075Sobrienmount_fini(void *mem, int size) 11890075Sobrien{ 11990075Sobrien struct mount *mp; 12090075Sobrien 12190075Sobrien mp = (struct mount *)mem; 12290075Sobrien lockdestroy(&mp->mnt_explock); 12390075Sobrien mtx_destroy(&mp->mnt_mtx); 12490075Sobrien} 12590075Sobrien 12690075Sobrienstatic void 12790075Sobrienvfs_mount_init(void *dummy __unused) 12890075Sobrien{ 12990075Sobrien 13090075Sobrien mount_zone = uma_zcreate("Mountpoints", sizeof(struct mount), NULL, 13190075Sobrien NULL, mount_init, mount_fini, UMA_ALIGN_PTR, UMA_ZONE_NOFREE); 13290075Sobrien} 13390075SobrienSYSINIT(vfs_mount, SI_SUB_VFS, SI_ORDER_ANY, vfs_mount_init, NULL); 13490075Sobrien 13590075Sobrien/* 13690075Sobrien * --------------------------------------------------------------------- 13790075Sobrien * Functions for building and sanitizing the mount options 13890075Sobrien */ 13990075Sobrien 14090075Sobrien/* Remove one mount option. */ 14190075Sobrienstatic void 14290075Sobrienvfs_freeopt(struct vfsoptlist *opts, struct vfsopt *opt) 14390075Sobrien{ 14490075Sobrien 14590075Sobrien TAILQ_REMOVE(opts, opt, link); 14690075Sobrien free(opt->name, M_MOUNT); 14790075Sobrien if (opt->value != NULL) 14890075Sobrien free(opt->value, M_MOUNT); 14990075Sobrien free(opt, M_MOUNT); 15090075Sobrien} 15190075Sobrien 15290075Sobrien/* Release all resources related to the mount options. */ 15390075Sobrienvoid 15490075Sobrienvfs_freeopts(struct vfsoptlist *opts) 15590075Sobrien{ 15690075Sobrien struct vfsopt *opt; 15790075Sobrien 15890075Sobrien while (!TAILQ_EMPTY(opts)) { 15990075Sobrien opt = TAILQ_FIRST(opts); 16090075Sobrien vfs_freeopt(opts, opt); 16190075Sobrien } 16290075Sobrien free(opts, M_MOUNT); 16390075Sobrien} 16490075Sobrien 16590075Sobrienvoid 16690075Sobrienvfs_deleteopt(struct vfsoptlist *opts, const char *name) 16790075Sobrien{ 16890075Sobrien struct vfsopt *opt, *temp; 16990075Sobrien 17090075Sobrien if (opts == NULL) 17190075Sobrien return; 17290075Sobrien TAILQ_FOREACH_SAFE(opt, opts, link, temp) { 17390075Sobrien if (strcmp(opt->name, name) == 0) 17490075Sobrien vfs_freeopt(opts, opt); 17590075Sobrien } 17690075Sobrien} 17790075Sobrien 17890075Sobrienstatic int 17990075Sobrienvfs_isopt_ro(const char *opt) 18090075Sobrien{ 18190075Sobrien 18290075Sobrien if (strcmp(opt, "ro") == 0 || strcmp(opt, "rdonly") == 0 || 18390075Sobrien strcmp(opt, "norw") == 0) 18490075Sobrien return (1); 18590075Sobrien return (0); 18690075Sobrien} 18790075Sobrien 18890075Sobrienstatic int 18990075Sobrienvfs_isopt_rw(const char *opt) 19090075Sobrien{ 19190075Sobrien 19290075Sobrien if (strcmp(opt, "rw") == 0 || strcmp(opt, "noro") == 0) 19390075Sobrien return (1); 19490075Sobrien return (0); 19590075Sobrien} 19690075Sobrien 19790075Sobrien/* 19890075Sobrien * Check if options are equal (with or without the "no" prefix). 19990075Sobrien */ 20096263Sobrienstatic int 20196263Sobrienvfs_equalopts(const char *opt1, const char *opt2) 20290075Sobrien{ 20390075Sobrien char *p; 20490075Sobrien 20590075Sobrien /* "opt" vs. "opt" or "noopt" vs. "noopt" */ 20690075Sobrien if (strcmp(opt1, opt2) == 0) 20790075Sobrien return (1); 20890075Sobrien /* "noopt" vs. "opt" */ 20990075Sobrien if (strncmp(opt1, "no", 2) == 0 && strcmp(opt1 + 2, opt2) == 0) 21090075Sobrien return (1); 21190075Sobrien /* "opt" vs. "noopt" */ 21290075Sobrien if (strncmp(opt2, "no", 2) == 0 && strcmp(opt1, opt2 + 2) == 0) 21390075Sobrien return (1); 21490075Sobrien while ((p = strchr(opt1, '.')) != NULL && 21590075Sobrien !strncmp(opt1, opt2, ++p - opt1)) { 21690075Sobrien opt2 += p - opt1; 21790075Sobrien opt1 = p; 21890075Sobrien /* "foo.noopt" vs. "foo.opt" */ 21990075Sobrien if (strncmp(opt1, "no", 2) == 0 && strcmp(opt1 + 2, opt2) == 0) 22090075Sobrien return (1); 22190075Sobrien /* "foo.opt" vs. "foo.noopt" */ 22290075Sobrien if (strncmp(opt2, "no", 2) == 0 && strcmp(opt1, opt2 + 2) == 0) 22390075Sobrien return (1); 22490075Sobrien } 22590075Sobrien /* "ro" / "rdonly" / "norw" / "rw" / "noro" */ 22690075Sobrien if ((vfs_isopt_ro(opt1) || vfs_isopt_rw(opt1)) && 22790075Sobrien (vfs_isopt_ro(opt2) || vfs_isopt_rw(opt2))) 22890075Sobrien return (1); 22990075Sobrien return (0); 23090075Sobrien} 23190075Sobrien 23290075Sobrien/* 23390075Sobrien * If a mount option is specified several times, 23490075Sobrien * (with or without the "no" prefix) only keep 23590075Sobrien * the last occurence of it. 23690075Sobrien */ 23790075Sobrienstatic void 23890075Sobrienvfs_sanitizeopts(struct vfsoptlist *opts) 23990075Sobrien{ 24090075Sobrien struct vfsopt *opt, *opt2, *tmp; 24190075Sobrien 24290075Sobrien TAILQ_FOREACH_REVERSE(opt, opts, vfsoptlist, link) { 24390075Sobrien opt2 = TAILQ_PREV(opt, vfsoptlist, link); 24490075Sobrien while (opt2 != NULL) { 24590075Sobrien if (vfs_equalopts(opt->name, opt2->name)) { 24690075Sobrien tmp = TAILQ_PREV(opt2, vfsoptlist, link); 24790075Sobrien vfs_freeopt(opts, opt2); 24890075Sobrien opt2 = tmp; 24990075Sobrien } else { 25090075Sobrien opt2 = TAILQ_PREV(opt2, vfsoptlist, link); 25190075Sobrien } 25290075Sobrien } 25390075Sobrien } 25490075Sobrien} 25590075Sobrien 25690075Sobrien/* 25790075Sobrien * Build a linked list of mount options from a struct uio. 25890075Sobrien */ 25990075Sobrienint 26090075Sobrienvfs_buildopts(struct uio *auio, struct vfsoptlist **options) 26190075Sobrien{ 26290075Sobrien struct vfsoptlist *opts; 26390075Sobrien struct vfsopt *opt; 26490075Sobrien size_t memused, namelen, optlen; 26590075Sobrien unsigned int i, iovcnt; 26690075Sobrien int error; 26790075Sobrien 26890075Sobrien opts = malloc(sizeof(struct vfsoptlist), M_MOUNT, M_WAITOK); 26990075Sobrien TAILQ_INIT(opts); 27090075Sobrien memused = 0; 27190075Sobrien iovcnt = auio->uio_iovcnt; 27290075Sobrien for (i = 0; i < iovcnt; i += 2) { 27390075Sobrien namelen = auio->uio_iov[i].iov_len; 27490075Sobrien optlen = auio->uio_iov[i + 1].iov_len; 27590075Sobrien memused += sizeof(struct vfsopt) + optlen + namelen; 27690075Sobrien /* 27790075Sobrien * Avoid consuming too much memory, and attempts to overflow 27890075Sobrien * memused. 27990075Sobrien */ 28090075Sobrien if (memused > VFS_MOUNTARG_SIZE_MAX || 28190075Sobrien optlen > VFS_MOUNTARG_SIZE_MAX || 28290075Sobrien namelen > VFS_MOUNTARG_SIZE_MAX) { 28390075Sobrien error = EINVAL; 28490075Sobrien goto bad; 28590075Sobrien } 28690075Sobrien 28790075Sobrien opt = malloc(sizeof(struct vfsopt), M_MOUNT, M_WAITOK); 28890075Sobrien opt->name = malloc(namelen, M_MOUNT, M_WAITOK); 28990075Sobrien opt->value = NULL; 29090075Sobrien opt->len = 0; 29190075Sobrien opt->pos = i / 2; 29290075Sobrien opt->seen = 0; 29390075Sobrien 29490075Sobrien /* 29590075Sobrien * Do this early, so jumps to "bad" will free the current 29690075Sobrien * option. 29790075Sobrien */ 29890075Sobrien TAILQ_INSERT_TAIL(opts, opt, link); 29990075Sobrien 30090075Sobrien if (auio->uio_segflg == UIO_SYSSPACE) { 30190075Sobrien bcopy(auio->uio_iov[i].iov_base, opt->name, namelen); 30290075Sobrien } else { 30390075Sobrien error = copyin(auio->uio_iov[i].iov_base, opt->name, 30490075Sobrien namelen); 30590075Sobrien if (error) 30690075Sobrien goto bad; 30790075Sobrien } 30890075Sobrien /* Ensure names are null-terminated strings. */ 30990075Sobrien if (namelen == 0 || opt->name[namelen - 1] != '\0') { 31090075Sobrien error = EINVAL; 31190075Sobrien goto bad; 31290075Sobrien } 31390075Sobrien if (optlen != 0) { 31490075Sobrien opt->len = optlen; 31590075Sobrien opt->value = malloc(optlen, M_MOUNT, M_WAITOK); 31690075Sobrien if (auio->uio_segflg == UIO_SYSSPACE) { 31790075Sobrien bcopy(auio->uio_iov[i + 1].iov_base, opt->value, 31890075Sobrien optlen); 31990075Sobrien } else { 32090075Sobrien error = copyin(auio->uio_iov[i + 1].iov_base, 32190075Sobrien opt->value, optlen); 32290075Sobrien if (error) 32390075Sobrien goto bad; 32490075Sobrien } 32590075Sobrien } 32690075Sobrien } 32790075Sobrien vfs_sanitizeopts(opts); 32890075Sobrien *options = opts; 32990075Sobrien return (0); 33090075Sobrienbad: 33190075Sobrien vfs_freeopts(opts); 33290075Sobrien return (error); 33390075Sobrien} 33490075Sobrien 33590075Sobrien/* 33690075Sobrien * Merge the old mount options with the new ones passed 33790075Sobrien * in the MNT_UPDATE case. 33890075Sobrien * 33990075Sobrien * XXX: This function will keep a "nofoo" option in the new 34090075Sobrien * options. E.g, if the option's canonical name is "foo", 34190075Sobrien * "nofoo" ends up in the mount point's active options. 34290075Sobrien */ 34390075Sobrienstatic void 34490075Sobrienvfs_mergeopts(struct vfsoptlist *toopts, struct vfsoptlist *oldopts) 34590075Sobrien{ 34690075Sobrien struct vfsopt *opt, *new; 34790075Sobrien 34890075Sobrien TAILQ_FOREACH(opt, oldopts, link) { 34990075Sobrien new = malloc(sizeof(struct vfsopt), M_MOUNT, M_WAITOK); 35090075Sobrien new->name = strdup(opt->name, M_MOUNT); 35190075Sobrien if (opt->len != 0) { 35290075Sobrien new->value = malloc(opt->len, M_MOUNT, M_WAITOK); 35390075Sobrien bcopy(opt->value, new->value, opt->len); 35490075Sobrien } else 35590075Sobrien new->value = NULL; 35690075Sobrien new->len = opt->len; 35790075Sobrien new->seen = opt->seen; 35890075Sobrien TAILQ_INSERT_HEAD(toopts, new, link); 35990075Sobrien } 36090075Sobrien vfs_sanitizeopts(toopts); 36190075Sobrien} 36290075Sobrien 36390075Sobrien/* 36490075Sobrien * Mount a filesystem. 36590075Sobrien */ 36690075Sobrienint 36790075Sobriensys_nmount(td, uap) 36890075Sobrien struct thread *td; 36990075Sobrien struct nmount_args /* { 37090075Sobrien struct iovec *iovp; 37190075Sobrien unsigned int iovcnt; 37290075Sobrien int flags; 37390075Sobrien } */ *uap; 37490075Sobrien{ 37590075Sobrien struct uio *auio; 37690075Sobrien int error; 37790075Sobrien u_int iovcnt; 37890075Sobrien uint64_t flags; 37990075Sobrien 38090075Sobrien /* 38190075Sobrien * Mount flags are now 64-bits. On 32-bit archtectures only 38290075Sobrien * 32-bits are passed in, but from here on everything handles 38390075Sobrien * 64-bit flags correctly. 38490075Sobrien */ 38590075Sobrien flags = uap->flags; 38690075Sobrien 38790075Sobrien AUDIT_ARG_FFLAGS(flags); 38896263Sobrien CTR4(KTR_VFS, "%s: iovp %p with iovcnt %d and flags %d", __func__, 38990075Sobrien uap->iovp, uap->iovcnt, flags); 39090075Sobrien 39190075Sobrien /* 39290075Sobrien * Filter out MNT_ROOTFS. We do not want clients of nmount() in 39390075Sobrien * userspace to set this flag, but we must filter it out if we want 39490075Sobrien * MNT_UPDATE on the root file system to work. 39590075Sobrien * MNT_ROOTFS should only be set by the kernel when mounting its 39690075Sobrien * root file system. 39790075Sobrien */ 39890075Sobrien flags &= ~MNT_ROOTFS; 39990075Sobrien 40090075Sobrien iovcnt = uap->iovcnt; 40190075Sobrien /* 40296263Sobrien * Check that we have an even number of iovec's 40396263Sobrien * and that we have at least two options. 40490075Sobrien */ 40590075Sobrien if ((iovcnt & 1) || (iovcnt < 4)) { 40690075Sobrien CTR2(KTR_VFS, "%s: failed for invalid iovcnt %d", __func__, 40790075Sobrien uap->iovcnt); 40890075Sobrien return (EINVAL); 40990075Sobrien } 41090075Sobrien 41190075Sobrien error = copyinuio(uap->iovp, iovcnt, &auio); 41290075Sobrien if (error) { 41390075Sobrien CTR2(KTR_VFS, "%s: failed for invalid uio op with %d errno", 41490075Sobrien __func__, error); 41590075Sobrien return (error); 41690075Sobrien } 41790075Sobrien error = vfs_donmount(td, flags, auio); 41890075Sobrien 41990075Sobrien free(auio, M_IOV); 42090075Sobrien return (error); 42190075Sobrien} 42290075Sobrien 42390075Sobrien/* 42490075Sobrien * --------------------------------------------------------------------- 42590075Sobrien * Various utility functions 42690075Sobrien */ 42790075Sobrien 42890075Sobrienvoid 42990075Sobrienvfs_ref(struct mount *mp) 43090075Sobrien{ 43190075Sobrien 43290075Sobrien CTR2(KTR_VFS, "%s: mp %p", __func__, mp); 43390075Sobrien MNT_ILOCK(mp); 43490075Sobrien MNT_REF(mp); 43590075Sobrien MNT_IUNLOCK(mp); 43690075Sobrien} 43790075Sobrien 43890075Sobrienvoid 43990075Sobrienvfs_rel(struct mount *mp) 44090075Sobrien{ 44190075Sobrien 44290075Sobrien CTR2(KTR_VFS, "%s: mp %p", __func__, mp); 44390075Sobrien MNT_ILOCK(mp); 44490075Sobrien MNT_REL(mp); 44590075Sobrien MNT_IUNLOCK(mp); 44690075Sobrien} 44790075Sobrien 44890075Sobrien/* 44990075Sobrien * Allocate and initialize the mount point struct. 45090075Sobrien */ 45190075Sobrienstruct mount * 45290075Sobrienvfs_mount_alloc(struct vnode *vp, struct vfsconf *vfsp, const char *fspath, 45390075Sobrien struct ucred *cred) 45490075Sobrien{ 45590075Sobrien struct mount *mp; 45690075Sobrien 45790075Sobrien mp = uma_zalloc(mount_zone, M_WAITOK); 45890075Sobrien bzero(&mp->mnt_startzero, 45990075Sobrien __rangeof(struct mount, mnt_startzero, mnt_endzero)); 46090075Sobrien TAILQ_INIT(&mp->mnt_nvnodelist); 46190075Sobrien mp->mnt_nvnodelistsize = 0; 46290075Sobrien TAILQ_INIT(&mp->mnt_activevnodelist); 46390075Sobrien mp->mnt_activevnodelistsize = 0; 46490075Sobrien mp->mnt_ref = 0; 46590075Sobrien (void) vfs_busy(mp, MBF_NOWAIT); 46690075Sobrien mp->mnt_op = vfsp->vfc_vfsops; 46790075Sobrien mp->mnt_vfc = vfsp; 46890075Sobrien vfsp->vfc_refcount++; /* XXX Unlocked */ 46990075Sobrien mp->mnt_stat.f_type = vfsp->vfc_typenum; 47090075Sobrien mp->mnt_gen++; 47190075Sobrien strlcpy(mp->mnt_stat.f_fstypename, vfsp->vfc_name, MFSNAMELEN); 47290075Sobrien mp->mnt_vnodecovered = vp; 47390075Sobrien mp->mnt_cred = crdup(cred); 47490075Sobrien mp->mnt_stat.f_owner = cred->cr_uid; 47590075Sobrien strlcpy(mp->mnt_stat.f_mntonname, fspath, MNAMELEN); 47690075Sobrien mp->mnt_iosize_max = DFLTPHYS; 47790075Sobrien#ifdef MAC 47890075Sobrien mac_mount_init(mp); 47990075Sobrien mac_mount_create(cred, mp); 48090075Sobrien#endif 48190075Sobrien arc4rand(&mp->mnt_hashseed, sizeof mp->mnt_hashseed, 0); 48290075Sobrien TAILQ_INIT(&mp->mnt_uppers); 48390075Sobrien return (mp); 48490075Sobrien} 48590075Sobrien 48690075Sobrien/* 48790075Sobrien * Destroy the mount struct previously allocated by vfs_mount_alloc(). 48890075Sobrien */ 48990075Sobrienvoid 49090075Sobrienvfs_mount_destroy(struct mount *mp) 49190075Sobrien{ 49290075Sobrien 49390075Sobrien MNT_ILOCK(mp); 49490075Sobrien mp->mnt_kern_flag |= MNTK_REFEXPIRE; 49590075Sobrien if (mp->mnt_kern_flag & MNTK_MWAIT) { 49690075Sobrien mp->mnt_kern_flag &= ~MNTK_MWAIT; 49790075Sobrien wakeup(mp); 49890075Sobrien } 49990075Sobrien while (mp->mnt_ref) 50090075Sobrien msleep(mp, MNT_MTX(mp), PVFS, "mntref", 0); 50190075Sobrien KASSERT(mp->mnt_ref == 0, 50290075Sobrien ("%s: invalid refcount in the drain path @ %s:%d", __func__, 50390075Sobrien __FILE__, __LINE__)); 50490075Sobrien if (mp->mnt_writeopcount != 0) 50590075Sobrien panic("vfs_mount_destroy: nonzero writeopcount"); 50690075Sobrien if (mp->mnt_secondary_writes != 0) 50790075Sobrien panic("vfs_mount_destroy: nonzero secondary_writes"); 50890075Sobrien mp->mnt_vfc->vfc_refcount--; 50990075Sobrien if (!TAILQ_EMPTY(&mp->mnt_nvnodelist)) { 51090075Sobrien struct vnode *vp; 51190075Sobrien 51290075Sobrien TAILQ_FOREACH(vp, &mp->mnt_nvnodelist, v_nmntvnodes) 51390075Sobrien vprint("", vp); 51490075Sobrien panic("unmount: dangling vnode"); 51590075Sobrien } 51690075Sobrien KASSERT(TAILQ_EMPTY(&mp->mnt_uppers), ("mnt_uppers")); 51790075Sobrien if (mp->mnt_nvnodelistsize != 0) 51890075Sobrien panic("vfs_mount_destroy: nonzero nvnodelistsize"); 51990075Sobrien if (mp->mnt_activevnodelistsize != 0) 52090075Sobrien panic("vfs_mount_destroy: nonzero activevnodelistsize"); 52190075Sobrien if (mp->mnt_lockref != 0) 52290075Sobrien panic("vfs_mount_destroy: nonzero lock refcount"); 52390075Sobrien MNT_IUNLOCK(mp); 52490075Sobrien#ifdef MAC 52590075Sobrien mac_mount_destroy(mp); 52690075Sobrien#endif 52790075Sobrien if (mp->mnt_opt != NULL) 52890075Sobrien vfs_freeopts(mp->mnt_opt); 52990075Sobrien crfree(mp->mnt_cred); 53090075Sobrien uma_zfree(mount_zone, mp); 53190075Sobrien} 53290075Sobrien 53390075Sobrienint 53490075Sobrienvfs_donmount(struct thread *td, uint64_t fsflags, struct uio *fsoptions) 53590075Sobrien{ 53690075Sobrien struct vfsoptlist *optlist; 53790075Sobrien struct vfsopt *opt, *tmp_opt; 53890075Sobrien char *fstype, *fspath, *errmsg; 53990075Sobrien int error, fstypelen, fspathlen, errmsg_len, errmsg_pos; 54090075Sobrien 54190075Sobrien errmsg = fspath = NULL; 54290075Sobrien errmsg_len = fspathlen = 0; 54390075Sobrien errmsg_pos = -1; 54490075Sobrien 54590075Sobrien error = vfs_buildopts(fsoptions, &optlist); 54690075Sobrien if (error) 54790075Sobrien return (error); 54890075Sobrien 54990075Sobrien if (vfs_getopt(optlist, "errmsg", (void **)&errmsg, &errmsg_len) == 0) 55090075Sobrien errmsg_pos = vfs_getopt_pos(optlist, "errmsg"); 55190075Sobrien 55290075Sobrien /* 55390075Sobrien * We need these two options before the others, 55490075Sobrien * and they are mandatory for any filesystem. 55590075Sobrien * Ensure they are NUL terminated as well. 55690075Sobrien */ 55790075Sobrien fstypelen = 0; 55890075Sobrien error = vfs_getopt(optlist, "fstype", (void **)&fstype, &fstypelen); 55990075Sobrien if (error || fstype[fstypelen - 1] != '\0') { 56090075Sobrien error = EINVAL; 56190075Sobrien if (errmsg != NULL) 56290075Sobrien strncpy(errmsg, "Invalid fstype", errmsg_len); 56390075Sobrien goto bail; 56490075Sobrien } 56590075Sobrien fspathlen = 0; 56690075Sobrien error = vfs_getopt(optlist, "fspath", (void **)&fspath, &fspathlen); 56790075Sobrien if (error || fspath[fspathlen - 1] != '\0') { 56890075Sobrien error = EINVAL; 56990075Sobrien if (errmsg != NULL) 57090075Sobrien strncpy(errmsg, "Invalid fspath", errmsg_len); 57190075Sobrien goto bail; 57290075Sobrien } 57390075Sobrien 57490075Sobrien /* 57590075Sobrien * We need to see if we have the "update" option 57690075Sobrien * before we call vfs_domount(), since vfs_domount() has special 57790075Sobrien * logic based on MNT_UPDATE. This is very important 57890075Sobrien * when we want to update the root filesystem. 57990075Sobrien */ 58090075Sobrien TAILQ_FOREACH_SAFE(opt, optlist, link, tmp_opt) { 58190075Sobrien if (strcmp(opt->name, "update") == 0) { 58290075Sobrien fsflags |= MNT_UPDATE; 58390075Sobrien vfs_freeopt(optlist, opt); 58490075Sobrien } 58590075Sobrien else if (strcmp(opt->name, "async") == 0) 58690075Sobrien fsflags |= MNT_ASYNC; 58790075Sobrien else if (strcmp(opt->name, "force") == 0) { 58890075Sobrien fsflags |= MNT_FORCE; 58990075Sobrien vfs_freeopt(optlist, opt); 59090075Sobrien } 59190075Sobrien else if (strcmp(opt->name, "reload") == 0) { 59290075Sobrien fsflags |= MNT_RELOAD; 59390075Sobrien vfs_freeopt(optlist, opt); 59490075Sobrien } 59590075Sobrien else if (strcmp(opt->name, "multilabel") == 0) 59690075Sobrien fsflags |= MNT_MULTILABEL; 59790075Sobrien else if (strcmp(opt->name, "noasync") == 0) 59890075Sobrien fsflags &= ~MNT_ASYNC; 59990075Sobrien else if (strcmp(opt->name, "noatime") == 0) 60090075Sobrien fsflags |= MNT_NOATIME; 60190075Sobrien else if (strcmp(opt->name, "atime") == 0) { 60290075Sobrien free(opt->name, M_MOUNT); 60390075Sobrien opt->name = strdup("nonoatime", M_MOUNT); 60490075Sobrien } 60590075Sobrien else if (strcmp(opt->name, "noclusterr") == 0) 60690075Sobrien fsflags |= MNT_NOCLUSTERR; 60790075Sobrien else if (strcmp(opt->name, "clusterr") == 0) { 60890075Sobrien free(opt->name, M_MOUNT); 60990075Sobrien opt->name = strdup("nonoclusterr", M_MOUNT); 61090075Sobrien } 61190075Sobrien else if (strcmp(opt->name, "noclusterw") == 0) 61290075Sobrien fsflags |= MNT_NOCLUSTERW; 61390075Sobrien else if (strcmp(opt->name, "clusterw") == 0) { 61490075Sobrien free(opt->name, M_MOUNT); 61590075Sobrien opt->name = strdup("nonoclusterw", M_MOUNT); 61690075Sobrien } 61790075Sobrien else if (strcmp(opt->name, "noexec") == 0) 61890075Sobrien fsflags |= MNT_NOEXEC; 61990075Sobrien else if (strcmp(opt->name, "exec") == 0) { 62090075Sobrien free(opt->name, M_MOUNT); 62190075Sobrien opt->name = strdup("nonoexec", M_MOUNT); 62290075Sobrien } 62390075Sobrien else if (strcmp(opt->name, "nosuid") == 0) 62490075Sobrien fsflags |= MNT_NOSUID; 62590075Sobrien else if (strcmp(opt->name, "suid") == 0) { 62690075Sobrien free(opt->name, M_MOUNT); 62790075Sobrien opt->name = strdup("nonosuid", M_MOUNT); 62890075Sobrien } 62990075Sobrien else if (strcmp(opt->name, "nosymfollow") == 0) 63090075Sobrien fsflags |= MNT_NOSYMFOLLOW; 63190075Sobrien else if (strcmp(opt->name, "symfollow") == 0) { 63290075Sobrien free(opt->name, M_MOUNT); 63390075Sobrien opt->name = strdup("nonosymfollow", M_MOUNT); 63490075Sobrien } 63590075Sobrien else if (strcmp(opt->name, "noro") == 0) 63690075Sobrien fsflags &= ~MNT_RDONLY; 63790075Sobrien else if (strcmp(opt->name, "rw") == 0) 63890075Sobrien fsflags &= ~MNT_RDONLY; 63990075Sobrien else if (strcmp(opt->name, "ro") == 0) 64090075Sobrien fsflags |= MNT_RDONLY; 64190075Sobrien else if (strcmp(opt->name, "rdonly") == 0) { 64290075Sobrien free(opt->name, M_MOUNT); 64390075Sobrien opt->name = strdup("ro", M_MOUNT); 64490075Sobrien fsflags |= MNT_RDONLY; 64590075Sobrien } 64690075Sobrien else if (strcmp(opt->name, "suiddir") == 0) 64790075Sobrien fsflags |= MNT_SUIDDIR; 64890075Sobrien else if (strcmp(opt->name, "sync") == 0) 64990075Sobrien fsflags |= MNT_SYNCHRONOUS; 65090075Sobrien else if (strcmp(opt->name, "union") == 0) 65190075Sobrien fsflags |= MNT_UNION; 65290075Sobrien } 65390075Sobrien 65490075Sobrien /* 65590075Sobrien * Be ultra-paranoid about making sure the type and fspath 65690075Sobrien * variables will fit in our mp buffers, including the 65790075Sobrien * terminating NUL. 65890075Sobrien */ 65990075Sobrien if (fstypelen >= MFSNAMELEN - 1 || fspathlen >= MNAMELEN - 1) { 66090075Sobrien error = ENAMETOOLONG; 66190075Sobrien goto bail; 66290075Sobrien } 66390075Sobrien 66490075Sobrien error = vfs_domount(td, fstype, fspath, fsflags, &optlist); 66590075Sobrienbail: 66690075Sobrien /* copyout the errmsg */ 66790075Sobrien if (errmsg_pos != -1 && ((2 * errmsg_pos + 1) < fsoptions->uio_iovcnt) 66890075Sobrien && errmsg_len > 0 && errmsg != NULL) { 66990075Sobrien if (fsoptions->uio_segflg == UIO_SYSSPACE) { 67090075Sobrien bcopy(errmsg, 67190075Sobrien fsoptions->uio_iov[2 * errmsg_pos + 1].iov_base, 67290075Sobrien fsoptions->uio_iov[2 * errmsg_pos + 1].iov_len); 67390075Sobrien } else { 67490075Sobrien copyout(errmsg, 67590075Sobrien fsoptions->uio_iov[2 * errmsg_pos + 1].iov_base, 67690075Sobrien fsoptions->uio_iov[2 * errmsg_pos + 1].iov_len); 67790075Sobrien } 67890075Sobrien } 67990075Sobrien 68090075Sobrien if (optlist != NULL) 68190075Sobrien vfs_freeopts(optlist); 68290075Sobrien return (error); 68390075Sobrien} 68490075Sobrien 68590075Sobrien/* 68690075Sobrien * Old mount API. 68790075Sobrien */ 68890075Sobrien#ifndef _SYS_SYSPROTO_H_ 68990075Sobrienstruct mount_args { 69090075Sobrien char *type; 69190075Sobrien char *path; 69290075Sobrien int flags; 69390075Sobrien caddr_t data; 69490075Sobrien}; 69590075Sobrien#endif 69690075Sobrien/* ARGSUSED */ 69790075Sobrienint 69890075Sobriensys_mount(td, uap) 69990075Sobrien struct thread *td; 70090075Sobrien struct mount_args /* { 70190075Sobrien char *type; 70290075Sobrien char *path; 70390075Sobrien int flags; 70490075Sobrien caddr_t data; 70590075Sobrien } */ *uap; 70690075Sobrien{ 70790075Sobrien char *fstype; 70890075Sobrien struct vfsconf *vfsp = NULL; 70990075Sobrien struct mntarg *ma = NULL; 71090075Sobrien uint64_t flags; 71190075Sobrien int error; 71290075Sobrien 71390075Sobrien /* 71490075Sobrien * Mount flags are now 64-bits. On 32-bit archtectures only 71590075Sobrien * 32-bits are passed in, but from here on everything handles 71690075Sobrien * 64-bit flags correctly. 71790075Sobrien */ 71890075Sobrien flags = uap->flags; 71990075Sobrien 72090075Sobrien AUDIT_ARG_FFLAGS(flags); 72190075Sobrien 72290075Sobrien /* 72390075Sobrien * Filter out MNT_ROOTFS. We do not want clients of mount() in 72490075Sobrien * userspace to set this flag, but we must filter it out if we want 72590075Sobrien * MNT_UPDATE on the root file system to work. 72690075Sobrien * MNT_ROOTFS should only be set by the kernel when mounting its 72790075Sobrien * root file system. 72890075Sobrien */ 72990075Sobrien flags &= ~MNT_ROOTFS; 73090075Sobrien 73190075Sobrien fstype = malloc(MFSNAMELEN, M_TEMP, M_WAITOK); 73290075Sobrien error = copyinstr(uap->type, fstype, MFSNAMELEN, NULL); 73390075Sobrien if (error) { 73490075Sobrien free(fstype, M_TEMP); 73590075Sobrien return (error); 73690075Sobrien } 73790075Sobrien 73890075Sobrien AUDIT_ARG_TEXT(fstype); 73990075Sobrien mtx_lock(&Giant); 74090075Sobrien vfsp = vfs_byname_kld(fstype, td, &error); 74190075Sobrien free(fstype, M_TEMP); 74290075Sobrien if (vfsp == NULL) { 74390075Sobrien mtx_unlock(&Giant); 74490075Sobrien return (ENOENT); 74590075Sobrien } 74690075Sobrien if (vfsp->vfc_vfsops->vfs_cmount == NULL) { 74790075Sobrien mtx_unlock(&Giant); 74890075Sobrien return (EOPNOTSUPP); 74990075Sobrien } 75090075Sobrien 75190075Sobrien ma = mount_argsu(ma, "fstype", uap->type, MNAMELEN); 75290075Sobrien ma = mount_argsu(ma, "fspath", uap->path, MNAMELEN); 75390075Sobrien ma = mount_argb(ma, flags & MNT_RDONLY, "noro"); 75490075Sobrien ma = mount_argb(ma, !(flags & MNT_NOSUID), "nosuid"); 75590075Sobrien ma = mount_argb(ma, !(flags & MNT_NOEXEC), "noexec"); 75690075Sobrien 75790075Sobrien error = vfsp->vfc_vfsops->vfs_cmount(ma, uap->data, flags); 75890075Sobrien mtx_unlock(&Giant); 75990075Sobrien return (error); 76090075Sobrien} 76190075Sobrien 76290075Sobrien/* 76390075Sobrien * vfs_domount_first(): first file system mount (not update) 76490075Sobrien */ 76590075Sobrienstatic int 76690075Sobrienvfs_domount_first( 76790075Sobrien struct thread *td, /* Calling thread. */ 76890075Sobrien struct vfsconf *vfsp, /* File system type. */ 76990075Sobrien char *fspath, /* Mount path. */ 77090075Sobrien struct vnode *vp, /* Vnode to be covered. */ 77190075Sobrien uint64_t fsflags, /* Flags common to all filesystems. */ 77290075Sobrien struct vfsoptlist **optlist /* Options local to the filesystem. */ 773103445Skan ) 774103445Skan{ 775103445Skan struct vattr va; 776103445Skan struct mount *mp; 777103445Skan struct vnode *newdp; 778103445Skan int error; 779103445Skan 780103445Skan mtx_assert(&Giant, MA_OWNED); 781103445Skan ASSERT_VOP_ELOCKED(vp, __func__); 782103445Skan KASSERT((fsflags & MNT_UPDATE) == 0, ("MNT_UPDATE shouldn't be here")); 783103445Skan 784103445Skan /* 785103445Skan * If the user is not root, ensure that they own the directory 786103445Skan * onto which we are attempting to mount. 787103445Skan */ 788103445Skan error = VOP_GETATTR(vp, &va, td->td_ucred); 789103445Skan if (error == 0 && va.va_uid != td->td_ucred->cr_uid) 790103445Skan error = priv_check_cred(td->td_ucred, PRIV_VFS_ADMIN, 0); 791103445Skan if (error == 0) 792103445Skan error = vinvalbuf(vp, V_SAVE, 0, 0); 793103445Skan if (error == 0 && vp->v_type != VDIR) 794103445Skan error = ENOTDIR; 795103445Skan if (error == 0) { 796103445Skan VI_LOCK(vp); 797103445Skan if ((vp->v_iflag & VI_MOUNT) == 0 && vp->v_mountedhere == NULL) 798103445Skan vp->v_iflag |= VI_MOUNT; 799103445Skan else 800103445Skan error = EBUSY; 801103445Skan VI_UNLOCK(vp); 802103445Skan } 803103445Skan if (error != 0) { 804103445Skan vput(vp); 80590075Sobrien return (error); 80690075Sobrien } 80790075Sobrien VOP_UNLOCK(vp, 0); 80890075Sobrien 80990075Sobrien /* Allocate and initialize the filesystem. */ 81090075Sobrien mp = vfs_mount_alloc(vp, vfsp, fspath, td->td_ucred); 81190075Sobrien /* XXXMAC: pass to vfs_mount_alloc? */ 81290075Sobrien mp->mnt_optnew = *optlist; 81390075Sobrien /* Set the mount level flags. */ 81490075Sobrien mp->mnt_flag = (fsflags & (MNT_UPDATEMASK | MNT_ROOTFS | MNT_RDONLY)); 81590075Sobrien 81690075Sobrien /* 81790075Sobrien * Mount the filesystem. 81890075Sobrien * XXX The final recipients of VFS_MOUNT just overwrite the ndp they 81990075Sobrien * get. No freeing of cn_pnbuf. 82090075Sobrien */ 82190075Sobrien error = VFS_MOUNT(mp); 82290075Sobrien if (error != 0) { 82390075Sobrien vfs_unbusy(mp); 82490075Sobrien vfs_mount_destroy(mp); 82590075Sobrien VI_LOCK(vp); 82690075Sobrien vp->v_iflag &= ~VI_MOUNT; 82790075Sobrien VI_UNLOCK(vp); 82890075Sobrien vrele(vp); 82990075Sobrien return (error); 83090075Sobrien } 83190075Sobrien 83290075Sobrien if (mp->mnt_opt != NULL) 83390075Sobrien vfs_freeopts(mp->mnt_opt); 83490075Sobrien mp->mnt_opt = mp->mnt_optnew; 83590075Sobrien *optlist = NULL; 83690075Sobrien (void)VFS_STATFS(mp, &mp->mnt_stat); 83790075Sobrien 83890075Sobrien /* 83990075Sobrien * Prevent external consumers of mount options from reading mnt_optnew. 84090075Sobrien */ 84190075Sobrien mp->mnt_optnew = NULL; 84290075Sobrien 84390075Sobrien MNT_ILOCK(mp); 84490075Sobrien if ((mp->mnt_flag & MNT_ASYNC) != 0 && 84590075Sobrien (mp->mnt_kern_flag & MNTK_NOASYNC) == 0) 84690075Sobrien mp->mnt_kern_flag |= MNTK_ASYNC; 84790075Sobrien else 84890075Sobrien mp->mnt_kern_flag &= ~MNTK_ASYNC; 84990075Sobrien MNT_IUNLOCK(mp); 85090075Sobrien 85190075Sobrien vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 85290075Sobrien cache_purge(vp); 85390075Sobrien VI_LOCK(vp); 85490075Sobrien vp->v_iflag &= ~VI_MOUNT; 85590075Sobrien VI_UNLOCK(vp); 85690075Sobrien vp->v_mountedhere = mp; 85790075Sobrien /* Place the new filesystem at the end of the mount list. */ 85890075Sobrien mtx_lock(&mountlist_mtx); 85990075Sobrien TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list); 86090075Sobrien mtx_unlock(&mountlist_mtx); 86190075Sobrien vfs_event_signal(NULL, VQ_MOUNT, 0); 86290075Sobrien if (VFS_ROOT(mp, LK_EXCLUSIVE, &newdp)) 86390075Sobrien panic("mount: lost mount"); 86490075Sobrien VOP_UNLOCK(newdp, 0); 86590075Sobrien VOP_UNLOCK(vp, 0); 86690075Sobrien mountcheckdirs(vp, newdp); 86790075Sobrien vrele(newdp); 86890075Sobrien if ((mp->mnt_flag & MNT_RDONLY) == 0) 86990075Sobrien vfs_allocate_syncvnode(mp); 87090075Sobrien vfs_unbusy(mp); 87190075Sobrien return (0); 87290075Sobrien} 87390075Sobrien 87490075Sobrien/* 87590075Sobrien * vfs_domount_update(): update of mounted file system 87690075Sobrien */ 87790075Sobrienstatic int 87890075Sobrienvfs_domount_update( 87990075Sobrien struct thread *td, /* Calling thread. */ 88090075Sobrien struct vnode *vp, /* Mount point vnode. */ 88190075Sobrien uint64_t fsflags, /* Flags common to all filesystems. */ 88290075Sobrien struct vfsoptlist **optlist /* Options local to the filesystem. */ 88390075Sobrien ) 88490075Sobrien{ 88590075Sobrien struct oexport_args oexport; 88690075Sobrien struct export_args export; 88790075Sobrien struct mount *mp; 88890075Sobrien int error, export_error; 88990075Sobrien uint64_t flag; 89090075Sobrien 89190075Sobrien mtx_assert(&Giant, MA_OWNED); 89290075Sobrien ASSERT_VOP_ELOCKED(vp, __func__); 89390075Sobrien KASSERT((fsflags & MNT_UPDATE) != 0, ("MNT_UPDATE should be here")); 89490075Sobrien 89590075Sobrien if ((vp->v_vflag & VV_ROOT) == 0) { 89690075Sobrien vput(vp); 89790075Sobrien return (EINVAL); 89890075Sobrien } 89990075Sobrien mp = vp->v_mount; 90090075Sobrien /* 90190075Sobrien * We only allow the filesystem to be reloaded if it 90290075Sobrien * is currently mounted read-only. 90390075Sobrien */ 90490075Sobrien flag = mp->mnt_flag; 90590075Sobrien if ((fsflags & MNT_RELOAD) != 0 && (flag & MNT_RDONLY) == 0) { 90690075Sobrien vput(vp); 90790075Sobrien return (EOPNOTSUPP); /* Needs translation */ 90890075Sobrien } 90990075Sobrien /* 91090075Sobrien * Only privileged root, or (if MNT_USER is set) the user that 91190075Sobrien * did the original mount is permitted to update it. 91290075Sobrien */ 91390075Sobrien error = vfs_suser(mp, td); 91496263Sobrien if (error != 0) { 91590075Sobrien vput(vp); 91690075Sobrien return (error); 91790075Sobrien } 91890075Sobrien if (vfs_busy(mp, MBF_NOWAIT)) { 91990075Sobrien vput(vp); 92090075Sobrien return (EBUSY); 92190075Sobrien } 92290075Sobrien VI_LOCK(vp); 92390075Sobrien if ((vp->v_iflag & VI_MOUNT) != 0 || vp->v_mountedhere != NULL) { 92490075Sobrien VI_UNLOCK(vp); 92590075Sobrien vfs_unbusy(mp); 92690075Sobrien vput(vp); 92796263Sobrien return (EBUSY); 92890075Sobrien } 92990075Sobrien vp->v_iflag |= VI_MOUNT; 93090075Sobrien VI_UNLOCK(vp); 93190075Sobrien VOP_UNLOCK(vp, 0); 93290075Sobrien 93390075Sobrien MNT_ILOCK(mp); 93490075Sobrien mp->mnt_flag &= ~MNT_UPDATEMASK; 93590075Sobrien mp->mnt_flag |= fsflags & (MNT_RELOAD | MNT_FORCE | MNT_UPDATE | 93690075Sobrien MNT_SNAPSHOT | MNT_ROOTFS | MNT_UPDATEMASK | MNT_RDONLY); 93790075Sobrien if ((mp->mnt_flag & MNT_ASYNC) == 0) 93896263Sobrien mp->mnt_kern_flag &= ~MNTK_ASYNC; 93990075Sobrien MNT_IUNLOCK(mp); 94096263Sobrien mp->mnt_optnew = *optlist; 94190075Sobrien vfs_mergeopts(mp->mnt_optnew, mp->mnt_opt); 94296263Sobrien 94390075Sobrien /* 94490075Sobrien * Mount the filesystem. 94590075Sobrien * XXX The final recipients of VFS_MOUNT just overwrite the ndp they 94690075Sobrien * get. No freeing of cn_pnbuf. 94790075Sobrien */ 94890075Sobrien error = VFS_MOUNT(mp); 94990075Sobrien 95090075Sobrien export_error = 0; 95196263Sobrien if (error == 0) { 95290075Sobrien /* Process the export option. */ 95390075Sobrien if (vfs_copyopt(mp->mnt_optnew, "export", &export, 95490075Sobrien sizeof(export)) == 0) { 95590075Sobrien export_error = vfs_export(mp, &export); 95690075Sobrien } else if (vfs_copyopt(mp->mnt_optnew, "export", &oexport, 95790075Sobrien sizeof(oexport)) == 0) { 95890075Sobrien export.ex_flags = oexport.ex_flags; 95990075Sobrien export.ex_root = oexport.ex_root; 96090075Sobrien export.ex_anon = oexport.ex_anon; 96190075Sobrien export.ex_addr = oexport.ex_addr; 96290075Sobrien export.ex_addrlen = oexport.ex_addrlen; 96390075Sobrien export.ex_mask = oexport.ex_mask; 96490075Sobrien export.ex_masklen = oexport.ex_masklen; 96590075Sobrien export.ex_indexfile = oexport.ex_indexfile; 96690075Sobrien export.ex_numsecflavors = 0; 96790075Sobrien export_error = vfs_export(mp, &export); 96890075Sobrien } 96990075Sobrien } 97090075Sobrien 97190075Sobrien MNT_ILOCK(mp); 97290075Sobrien if (error == 0) { 97390075Sobrien mp->mnt_flag &= ~(MNT_UPDATE | MNT_RELOAD | MNT_FORCE | 97490075Sobrien MNT_SNAPSHOT); 97590075Sobrien } else { 97690075Sobrien /* 97790075Sobrien * If we fail, restore old mount flags. MNT_QUOTA is special, 97890075Sobrien * because it is not part of MNT_UPDATEMASK, but it could have 97990075Sobrien * changed in the meantime if quotactl(2) was called. 98090075Sobrien * All in all we want current value of MNT_QUOTA, not the old 98190075Sobrien * one. 98296263Sobrien */ 98390075Sobrien mp->mnt_flag = (mp->mnt_flag & MNT_QUOTA) | (flag & ~MNT_QUOTA); 98496263Sobrien } 98596263Sobrien if ((mp->mnt_flag & MNT_ASYNC) != 0 && 98690075Sobrien (mp->mnt_kern_flag & MNTK_NOASYNC) == 0) 98790075Sobrien mp->mnt_kern_flag |= MNTK_ASYNC; 98890075Sobrien else 98990075Sobrien mp->mnt_kern_flag &= ~MNTK_ASYNC; 99090075Sobrien MNT_IUNLOCK(mp); 99190075Sobrien 99290075Sobrien if (error != 0) 99390075Sobrien goto end; 99490075Sobrien 99590075Sobrien if (mp->mnt_opt != NULL) 99690075Sobrien vfs_freeopts(mp->mnt_opt); 99790075Sobrien mp->mnt_opt = mp->mnt_optnew; 99890075Sobrien *optlist = NULL; 99990075Sobrien (void)VFS_STATFS(mp, &mp->mnt_stat); 100090075Sobrien /* 100190075Sobrien * Prevent external consumers of mount options from reading 100290075Sobrien * mnt_optnew. 100390075Sobrien */ 100490075Sobrien mp->mnt_optnew = NULL; 100590075Sobrien 100690075Sobrien if ((mp->mnt_flag & MNT_RDONLY) == 0) 100790075Sobrien vfs_allocate_syncvnode(mp); 100890075Sobrien else 100990075Sobrien vfs_deallocate_syncvnode(mp); 101090075Sobrienend: 101190075Sobrien vfs_unbusy(mp); 101290075Sobrien VI_LOCK(vp); 101390075Sobrien vp->v_iflag &= ~VI_MOUNT; 101490075Sobrien VI_UNLOCK(vp); 101590075Sobrien vrele(vp); 101690075Sobrien return (error != 0 ? error : export_error); 101790075Sobrien} 101890075Sobrien 101990075Sobrien/* 102090075Sobrien * vfs_domount(): actually attempt a filesystem mount. 102190075Sobrien */ 102290075Sobrienstatic int 102390075Sobrienvfs_domount( 102490075Sobrien struct thread *td, /* Calling thread. */ 102590075Sobrien const char *fstype, /* Filesystem type. */ 102690075Sobrien char *fspath, /* Mount path. */ 102790075Sobrien uint64_t fsflags, /* Flags common to all filesystems. */ 102890075Sobrien struct vfsoptlist **optlist /* Options local to the filesystem. */ 102990075Sobrien ) 103090075Sobrien{ 103190075Sobrien struct vfsconf *vfsp; 103290075Sobrien struct nameidata nd; 103390075Sobrien struct vnode *vp; 103490075Sobrien char *pathbuf; 103590075Sobrien int error; 103690075Sobrien 103790075Sobrien /* 103890075Sobrien * Be ultra-paranoid about making sure the type and fspath 103996263Sobrien * variables will fit in our mp buffers, including the 104090075Sobrien * terminating NUL. 104196263Sobrien */ 104290075Sobrien if (strlen(fstype) >= MFSNAMELEN || strlen(fspath) >= MNAMELEN) 104390075Sobrien return (ENAMETOOLONG); 104490075Sobrien 104590075Sobrien if (jailed(td->td_ucred) || usermount == 0) { 104690075Sobrien if ((error = priv_check(td, PRIV_VFS_MOUNT)) != 0) 104790075Sobrien return (error); 104890075Sobrien } 104990075Sobrien 105090075Sobrien /* 105190075Sobrien * Do not allow NFS export or MNT_SUIDDIR by unprivileged users. 105290075Sobrien */ 105390075Sobrien if (fsflags & MNT_EXPORTED) { 105490075Sobrien error = priv_check(td, PRIV_VFS_MOUNT_EXPORTED); 105590075Sobrien if (error) 105690075Sobrien return (error); 105790075Sobrien } 105890075Sobrien if (fsflags & MNT_SUIDDIR) { 105990075Sobrien error = priv_check(td, PRIV_VFS_MOUNT_SUIDDIR); 106090075Sobrien if (error) 106190075Sobrien return (error); 106290075Sobrien } 106390075Sobrien /* 106490075Sobrien * Silently enforce MNT_NOSUID and MNT_USER for unprivileged users. 106590075Sobrien */ 106690075Sobrien if ((fsflags & (MNT_NOSUID | MNT_USER)) != (MNT_NOSUID | MNT_USER)) { 106790075Sobrien if (priv_check(td, PRIV_VFS_MOUNT_NONUSER) != 0) 106890075Sobrien fsflags |= MNT_NOSUID | MNT_USER; 106990075Sobrien } 107090075Sobrien 107190075Sobrien /* Load KLDs before we lock the covered vnode to avoid reversals. */ 107290075Sobrien vfsp = NULL; 107390075Sobrien if ((fsflags & MNT_UPDATE) == 0) { 107490075Sobrien /* Don't try to load KLDs if we're mounting the root. */ 107590075Sobrien if (fsflags & MNT_ROOTFS) 107690075Sobrien vfsp = vfs_byname(fstype); 107790075Sobrien else 107890075Sobrien vfsp = vfs_byname_kld(fstype, td, &error); 107990075Sobrien if (vfsp == NULL) 108090075Sobrien return (ENODEV); 108190075Sobrien if (jailed(td->td_ucred) && !(vfsp->vfc_flags & VFCF_JAIL)) 108290075Sobrien return (EPERM); 108390075Sobrien } 108490075Sobrien 108590075Sobrien /* 108696263Sobrien * Get vnode to be covered or mount point's vnode in case of MNT_UPDATE. 108790075Sobrien */ 108890075Sobrien NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | AUDITVNODE1, 108990075Sobrien UIO_SYSSPACE, fspath, td); 109096263Sobrien error = namei(&nd); 109190075Sobrien if (error != 0) 109290075Sobrien return (error); 109390075Sobrien mtx_lock(&Giant); 109490075Sobrien NDFREE(&nd, NDF_ONLY_PNBUF); 109590075Sobrien vp = nd.ni_vp; 109690075Sobrien if ((fsflags & MNT_UPDATE) == 0) { 109790075Sobrien pathbuf = malloc(MNAMELEN, M_TEMP, M_WAITOK); 109890075Sobrien strcpy(pathbuf, fspath); 109990075Sobrien error = vn_path_to_global_path(td, vp, pathbuf, MNAMELEN); 110090075Sobrien /* debug.disablefullpath == 1 results in ENODEV */ 110190075Sobrien if (error == 0 || error == ENODEV) { 110290075Sobrien error = vfs_domount_first(td, vfsp, pathbuf, vp, 110396263Sobrien fsflags, optlist); 110490075Sobrien } 110596263Sobrien free(pathbuf, M_TEMP); 110690075Sobrien } else 110790075Sobrien error = vfs_domount_update(td, vp, fsflags, optlist); 110890075Sobrien mtx_unlock(&Giant); 110990075Sobrien 111090075Sobrien ASSERT_VI_UNLOCKED(vp, __func__); 111190075Sobrien ASSERT_VOP_UNLOCKED(vp, __func__); 111290075Sobrien 111390075Sobrien return (error); 111490075Sobrien} 111590075Sobrien 111690075Sobrien/* 111790075Sobrien * Unmount a filesystem. 111896263Sobrien * 111990075Sobrien * Note: unmount takes a path to the vnode mounted on as argument, not 112096263Sobrien * special file (as before). 112190075Sobrien */ 112290075Sobrien#ifndef _SYS_SYSPROTO_H_ 112390075Sobrienstruct unmount_args { 112490075Sobrien char *path; 112590075Sobrien int flags; 112690075Sobrien}; 112790075Sobrien#endif 112890075Sobrien/* ARGSUSED */ 112990075Sobrienint 113090075Sobriensys_unmount(td, uap) 113190075Sobrien struct thread *td; 113290075Sobrien register struct unmount_args /* { 113396263Sobrien char *path; 113490075Sobrien int flags; 113596263Sobrien } */ *uap; 113690075Sobrien{ 113790075Sobrien struct nameidata nd; 113890075Sobrien struct mount *mp; 113990075Sobrien char *pathbuf; 114090075Sobrien int error, id0, id1; 114196263Sobrien 114290075Sobrien AUDIT_ARG_VALUE(uap->flags); 114390075Sobrien if (jailed(td->td_ucred) || usermount == 0) { 114490075Sobrien error = priv_check(td, PRIV_VFS_UNMOUNT); 114590075Sobrien if (error) 114690075Sobrien return (error); 114790075Sobrien } 114896263Sobrien 114990075Sobrien pathbuf = malloc(MNAMELEN, M_TEMP, M_WAITOK); 115090075Sobrien error = copyinstr(uap->path, pathbuf, MNAMELEN, NULL); 115196263Sobrien if (error) { 115290075Sobrien free(pathbuf, M_TEMP); 115390075Sobrien return (error); 115490075Sobrien } 115590075Sobrien mtx_lock(&Giant); 115690075Sobrien if (uap->flags & MNT_BYFSID) { 115790075Sobrien AUDIT_ARG_TEXT(pathbuf); 115890075Sobrien /* Decode the filesystem ID. */ 115990075Sobrien if (sscanf(pathbuf, "FSID:%d:%d", &id0, &id1) != 2) { 116090075Sobrien mtx_unlock(&Giant); 116190075Sobrien free(pathbuf, M_TEMP); 116290075Sobrien return (EINVAL); 116390075Sobrien } 116490075Sobrien 116590075Sobrien mtx_lock(&mountlist_mtx); 116690075Sobrien TAILQ_FOREACH_REVERSE(mp, &mountlist, mntlist, mnt_list) { 116790075Sobrien if (mp->mnt_stat.f_fsid.val[0] == id0 && 116890075Sobrien mp->mnt_stat.f_fsid.val[1] == id1) 116990075Sobrien break; 117090075Sobrien } 117190075Sobrien mtx_unlock(&mountlist_mtx); 117290075Sobrien } else { 117390075Sobrien /* 117490075Sobrien * Try to find global path for path argument. 117590075Sobrien */ 117690075Sobrien NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | AUDITVNODE1, 117790075Sobrien UIO_SYSSPACE, pathbuf, td); 117890075Sobrien if (namei(&nd) == 0) { 117990075Sobrien NDFREE(&nd, NDF_ONLY_PNBUF); 118090075Sobrien error = vn_path_to_global_path(td, nd.ni_vp, pathbuf, 118190075Sobrien MNAMELEN); 118290075Sobrien if (error == 0 || error == ENODEV) 118390075Sobrien vput(nd.ni_vp); 118490075Sobrien } 118590075Sobrien mtx_lock(&mountlist_mtx); 118690075Sobrien TAILQ_FOREACH_REVERSE(mp, &mountlist, mntlist, mnt_list) { 118790075Sobrien if (strcmp(mp->mnt_stat.f_mntonname, pathbuf) == 0) 118890075Sobrien break; 118990075Sobrien } 119090075Sobrien mtx_unlock(&mountlist_mtx); 119190075Sobrien } 119290075Sobrien free(pathbuf, M_TEMP); 119390075Sobrien if (mp == NULL) { 119490075Sobrien /* 119590075Sobrien * Previously we returned ENOENT for a nonexistent path and 119690075Sobrien * EINVAL for a non-mountpoint. We cannot tell these apart 119790075Sobrien * now, so in the !MNT_BYFSID case return the more likely 119890075Sobrien * EINVAL for compatibility. 119990075Sobrien */ 120090075Sobrien mtx_unlock(&Giant); 120190075Sobrien return ((uap->flags & MNT_BYFSID) ? ENOENT : EINVAL); 120290075Sobrien } 120390075Sobrien 120490075Sobrien /* 120590075Sobrien * Don't allow unmounting the root filesystem. 120690075Sobrien */ 120790075Sobrien if (mp->mnt_flag & MNT_ROOTFS) { 120890075Sobrien mtx_unlock(&Giant); 120990075Sobrien return (EINVAL); 121090075Sobrien } 121190075Sobrien error = dounmount(mp, uap->flags, td); 121290075Sobrien mtx_unlock(&Giant); 121390075Sobrien return (error); 121490075Sobrien} 121590075Sobrien 121690075Sobrien/* 121790075Sobrien * Do the actual filesystem unmount. 121890075Sobrien */ 121990075Sobrienint 122090075Sobriendounmount(mp, flags, td) 122190075Sobrien struct mount *mp; 122290075Sobrien int flags; 122390075Sobrien struct thread *td; 122490075Sobrien{ 122590075Sobrien struct vnode *coveredvp, *fsrootvp; 122690075Sobrien int error; 122790075Sobrien uint64_t async_flag; 122890075Sobrien int mnt_gen_r; 122990075Sobrien 123090075Sobrien mtx_assert(&Giant, MA_OWNED); 123196263Sobrien 123290075Sobrien if ((coveredvp = mp->mnt_vnodecovered) != NULL) { 123390075Sobrien mnt_gen_r = mp->mnt_gen; 123490075Sobrien VI_LOCK(coveredvp); 123590075Sobrien vholdl(coveredvp); 123696263Sobrien vn_lock(coveredvp, LK_EXCLUSIVE | LK_INTERLOCK | LK_RETRY); 123796263Sobrien vdrop(coveredvp); 123896263Sobrien /* 123996263Sobrien * Check for mp being unmounted while waiting for the 124096263Sobrien * covered vnode lock. 124196263Sobrien */ 124296263Sobrien if (coveredvp->v_mountedhere != mp || 124390075Sobrien coveredvp->v_mountedhere->mnt_gen != mnt_gen_r) { 124490075Sobrien VOP_UNLOCK(coveredvp, 0); 124590075Sobrien return (EBUSY); 124690075Sobrien } 124790075Sobrien } 124890075Sobrien /* 124990075Sobrien * Only privileged root, or (if MNT_USER is set) the user that did the 125090075Sobrien * original mount is permitted to unmount this filesystem. 125190075Sobrien */ 125290075Sobrien error = vfs_suser(mp, td); 125396263Sobrien if (error) { 125496263Sobrien if (coveredvp) 125596263Sobrien VOP_UNLOCK(coveredvp, 0); 125696263Sobrien return (error); 125790075Sobrien } 125896263Sobrien 125996263Sobrien MNT_ILOCK(mp); 126096263Sobrien if ((mp->mnt_kern_flag & MNTK_UNMOUNT) != 0 || 126196263Sobrien !TAILQ_EMPTY(&mp->mnt_uppers)) { 126296263Sobrien MNT_IUNLOCK(mp); 126390075Sobrien if (coveredvp) 126490075Sobrien VOP_UNLOCK(coveredvp, 0); 126596263Sobrien return (EBUSY); 126696263Sobrien } 126796263Sobrien mp->mnt_kern_flag |= MNTK_UNMOUNT | MNTK_NOINSMNTQ; 126896263Sobrien /* Allow filesystems to detect that a forced unmount is in progress. */ 126996263Sobrien if (flags & MNT_FORCE) 127090075Sobrien mp->mnt_kern_flag |= MNTK_UNMOUNTF; 127190075Sobrien error = 0; 127290075Sobrien if (mp->mnt_lockref) { 127396263Sobrien mp->mnt_kern_flag |= MNTK_DRAINING; 127496263Sobrien error = msleep(&mp->mnt_lockref, MNT_MTX(mp), PVFS, 127596263Sobrien "mount drain", 0); 127696263Sobrien } 127796263Sobrien MNT_IUNLOCK(mp); 127896263Sobrien KASSERT(mp->mnt_lockref == 0, 127996263Sobrien ("%s: invalid lock refcount in the drain path @ %s:%d", 128096263Sobrien __func__, __FILE__, __LINE__)); 128196263Sobrien KASSERT(error == 0, 128296263Sobrien ("%s: invalid return value for msleep in the drain path @ %s:%d", 128396263Sobrien __func__, __FILE__, __LINE__)); 128496263Sobrien vn_start_write(NULL, &mp, V_WAIT); 128596263Sobrien 128696263Sobrien if (mp->mnt_flag & MNT_EXPUBLIC) 128796263Sobrien vfs_setpublicfs(NULL, NULL, NULL); 128896263Sobrien 128996263Sobrien vfs_msync(mp, MNT_WAIT); 129096263Sobrien MNT_ILOCK(mp); 129196263Sobrien async_flag = mp->mnt_flag & MNT_ASYNC; 129296263Sobrien mp->mnt_flag &= ~MNT_ASYNC; 129396263Sobrien mp->mnt_kern_flag &= ~MNTK_ASYNC; 129496263Sobrien MNT_IUNLOCK(mp); 129596263Sobrien cache_purgevfs(mp); /* remove cache entries for this file sys */ 129696263Sobrien vfs_deallocate_syncvnode(mp); 129796263Sobrien /* 129890075Sobrien * For forced unmounts, move process cdir/rdir refs on the fs root 129990075Sobrien * vnode to the covered vnode. For non-forced unmounts we want 130090075Sobrien * such references to cause an EBUSY error. 130190075Sobrien */ 130290075Sobrien if ((flags & MNT_FORCE) && 130390075Sobrien VFS_ROOT(mp, LK_EXCLUSIVE, &fsrootvp) == 0) { 130490075Sobrien if (mp->mnt_vnodecovered != NULL) 130590075Sobrien mountcheckdirs(fsrootvp, mp->mnt_vnodecovered); 130690075Sobrien if (fsrootvp == rootvnode) { 130790075Sobrien vrele(rootvnode); 130890075Sobrien rootvnode = NULL; 130990075Sobrien } 131090075Sobrien vput(fsrootvp); 131190075Sobrien } 131290075Sobrien if (((mp->mnt_flag & MNT_RDONLY) || 131390075Sobrien (error = VFS_SYNC(mp, MNT_WAIT)) == 0) || (flags & MNT_FORCE) != 0) 131490075Sobrien error = VFS_UNMOUNT(mp, flags); 131590075Sobrien vn_finished_write(mp); 131690075Sobrien /* 131790075Sobrien * If we failed to flush the dirty blocks for this mount point, 131890075Sobrien * undo all the cdir/rdir and rootvnode changes we made above. 131990075Sobrien * Unless we failed to do so because the device is reporting that 132090075Sobrien * it doesn't exist anymore. 1321110611Skan */ 1322110611Skan if (error && error != ENXIO) { 1323110611Skan if ((flags & MNT_FORCE) && 1324110611Skan VFS_ROOT(mp, LK_EXCLUSIVE, &fsrootvp) == 0) { 1325110611Skan if (mp->mnt_vnodecovered != NULL) 1326110611Skan mountcheckdirs(mp->mnt_vnodecovered, fsrootvp); 1327110611Skan if (rootvnode == NULL) { 1328110611Skan rootvnode = fsrootvp; 1329110611Skan vref(rootvnode); 1330110611Skan } 1331110611Skan vput(fsrootvp); 1332110611Skan } 1333110611Skan MNT_ILOCK(mp); 1334110611Skan mp->mnt_kern_flag &= ~MNTK_NOINSMNTQ; 1335110611Skan if ((mp->mnt_flag & MNT_RDONLY) == 0) { 133690075Sobrien MNT_IUNLOCK(mp); 133790075Sobrien vfs_allocate_syncvnode(mp); 133890075Sobrien MNT_ILOCK(mp); 133990075Sobrien } 134090075Sobrien mp->mnt_kern_flag &= ~(MNTK_UNMOUNT | MNTK_UNMOUNTF); 134190075Sobrien mp->mnt_flag |= async_flag; 134290075Sobrien if ((mp->mnt_flag & MNT_ASYNC) != 0 && 134390075Sobrien (mp->mnt_kern_flag & MNTK_NOASYNC) == 0) 134490075Sobrien mp->mnt_kern_flag |= MNTK_ASYNC; 134590075Sobrien if (mp->mnt_kern_flag & MNTK_MWAIT) { 134690075Sobrien mp->mnt_kern_flag &= ~MNTK_MWAIT; 134790075Sobrien wakeup(mp); 134890075Sobrien } 134990075Sobrien MNT_IUNLOCK(mp); 135090075Sobrien if (coveredvp) 135190075Sobrien VOP_UNLOCK(coveredvp, 0); 135290075Sobrien return (error); 135390075Sobrien } 135490075Sobrien mtx_lock(&mountlist_mtx); 135590075Sobrien TAILQ_REMOVE(&mountlist, mp, mnt_list); 135690075Sobrien mtx_unlock(&mountlist_mtx); 135790075Sobrien if (coveredvp != NULL) { 135890075Sobrien coveredvp->v_mountedhere = NULL; 135990075Sobrien vput(coveredvp); 136090075Sobrien } 136190075Sobrien vfs_event_signal(NULL, VQ_UNMOUNT, 0); 136290075Sobrien vfs_mount_destroy(mp); 136390075Sobrien return (0); 136490075Sobrien} 136590075Sobrien 136690075Sobrien/* 136790075Sobrien * Report errors during filesystem mounting. 136890075Sobrien */ 136990075Sobrienvoid 137090075Sobrienvfs_mount_error(struct mount *mp, const char *fmt, ...) 137190075Sobrien{ 137290075Sobrien struct vfsoptlist *moptlist = mp->mnt_optnew; 137390075Sobrien va_list ap; 137490075Sobrien int error, len; 137590075Sobrien char *errmsg; 137690075Sobrien 137790075Sobrien error = vfs_getopt(moptlist, "errmsg", (void **)&errmsg, &len); 137890075Sobrien if (error || errmsg == NULL || len <= 0) 137990075Sobrien return; 138090075Sobrien 138190075Sobrien va_start(ap, fmt); 138290075Sobrien vsnprintf(errmsg, (size_t)len, fmt, ap); 138390075Sobrien va_end(ap); 138490075Sobrien} 138590075Sobrien 138690075Sobrienvoid 138790075Sobrienvfs_opterror(struct vfsoptlist *opts, const char *fmt, ...) 138890075Sobrien{ 138990075Sobrien va_list ap; 139090075Sobrien int error, len; 139196263Sobrien char *errmsg; 139290075Sobrien 139390075Sobrien error = vfs_getopt(opts, "errmsg", (void **)&errmsg, &len); 139490075Sobrien if (error || errmsg == NULL || len <= 0) 139590075Sobrien return; 139690075Sobrien 139790075Sobrien va_start(ap, fmt); 139890075Sobrien vsnprintf(errmsg, (size_t)len, fmt, ap); 139990075Sobrien va_end(ap); 140090075Sobrien} 140196263Sobrien 140290075Sobrien/* 140390075Sobrien * --------------------------------------------------------------------- 140490075Sobrien * Functions for querying mount options/arguments from filesystems. 140590075Sobrien */ 140690075Sobrien 140790075Sobrien/* 140890075Sobrien * Check that no unknown options are given 140990075Sobrien */ 141090075Sobrienint 141196263Sobrienvfs_filteropt(struct vfsoptlist *opts, const char **legal) 141290075Sobrien{ 141390075Sobrien struct vfsopt *opt; 141490075Sobrien char errmsg[255]; 141590075Sobrien const char **t, *p, *q; 141690075Sobrien int ret = 0; 141790075Sobrien 141890075Sobrien TAILQ_FOREACH(opt, opts, link) { 141990075Sobrien p = opt->name; 142090075Sobrien q = NULL; 142190075Sobrien if (p[0] == 'n' && p[1] == 'o') 142290075Sobrien q = p + 2; 142390075Sobrien for(t = global_opts; *t != NULL; t++) { 142490075Sobrien if (strcmp(*t, p) == 0) 142596263Sobrien break; 142690075Sobrien if (q != NULL) { 142790075Sobrien if (strcmp(*t, q) == 0) 142890075Sobrien break; 142990075Sobrien } 143090075Sobrien } 143190075Sobrien if (*t != NULL) 143290075Sobrien continue; 143390075Sobrien for(t = legal; *t != NULL; t++) { 143490075Sobrien if (strcmp(*t, p) == 0) 143590075Sobrien break; 143690075Sobrien if (q != NULL) { 143796263Sobrien if (strcmp(*t, q) == 0) 143890075Sobrien break; 143990075Sobrien } 144090075Sobrien } 144190075Sobrien if (*t != NULL) 144290075Sobrien continue; 144390075Sobrien snprintf(errmsg, sizeof(errmsg), 144490075Sobrien "mount option <%s> is unknown", p); 144590075Sobrien ret = EINVAL; 144690075Sobrien } 144790075Sobrien if (ret != 0) { 144890075Sobrien TAILQ_FOREACH(opt, opts, link) { 144990075Sobrien if (strcmp(opt->name, "errmsg") == 0) { 145090075Sobrien strncpy((char *)opt->value, errmsg, opt->len); 145190075Sobrien break; 145290075Sobrien } 145396263Sobrien } 145490075Sobrien if (opt == NULL) 145590075Sobrien printf("%s\n", errmsg); 145690075Sobrien } 1457110611Skan return (ret); 145890075Sobrien} 145990075Sobrien 146090075Sobrien/* 146190075Sobrien * Get a mount option by its name. 146290075Sobrien * 146390075Sobrien * Return 0 if the option was found, ENOENT otherwise. 146490075Sobrien * If len is non-NULL it will be filled with the length 146590075Sobrien * of the option. If buf is non-NULL, it will be filled 146690075Sobrien * with the address of the option. 146790075Sobrien */ 146890075Sobrienint 146990075Sobrienvfs_getopt(opts, name, buf, len) 147090075Sobrien struct vfsoptlist *opts; 147190075Sobrien const char *name; 147290075Sobrien void **buf; 147390075Sobrien int *len; 147490075Sobrien{ 147590075Sobrien struct vfsopt *opt; 147690075Sobrien 147790075Sobrien KASSERT(opts != NULL, ("vfs_getopt: caller passed 'opts' as NULL")); 147890075Sobrien 147990075Sobrien TAILQ_FOREACH(opt, opts, link) { 148090075Sobrien if (strcmp(name, opt->name) == 0) { 148190075Sobrien opt->seen = 1; 148290075Sobrien if (len != NULL) 148390075Sobrien *len = opt->len; 148490075Sobrien if (buf != NULL) 148590075Sobrien *buf = opt->value; 148690075Sobrien return (0); 148790075Sobrien } 148890075Sobrien } 148990075Sobrien return (ENOENT); 149090075Sobrien} 149190075Sobrien 149290075Sobrienint 149390075Sobrienvfs_getopt_pos(struct vfsoptlist *opts, const char *name) 149490075Sobrien{ 149590075Sobrien struct vfsopt *opt; 149690075Sobrien 149790075Sobrien if (opts == NULL) 149890075Sobrien return (-1); 149990075Sobrien 150090075Sobrien TAILQ_FOREACH(opt, opts, link) { 150190075Sobrien if (strcmp(name, opt->name) == 0) { 150290075Sobrien opt->seen = 1; 150390075Sobrien return (opt->pos); 150490075Sobrien } 150590075Sobrien } 150690075Sobrien return (-1); 150790075Sobrien} 150890075Sobrien 150990075Sobrienint 151090075Sobrienvfs_getopt_size(struct vfsoptlist *opts, const char *name, off_t *value) 151190075Sobrien{ 151290075Sobrien char *opt_value, *vtp; 151390075Sobrien quad_t iv; 151490075Sobrien int error, opt_len; 151590075Sobrien 151690075Sobrien error = vfs_getopt(opts, name, (void **)&opt_value, &opt_len); 151790075Sobrien if (error != 0) 151890075Sobrien return (error); 151990075Sobrien if (opt_len == 0 || opt_value == NULL) 152090075Sobrien return (EINVAL); 152190075Sobrien if (opt_value[0] == '\0' || opt_value[opt_len - 1] != '\0') 152290075Sobrien return (EINVAL); 152390075Sobrien iv = strtoq(opt_value, &vtp, 0); 152490075Sobrien if (vtp == opt_value || (vtp[0] != '\0' && vtp[1] != '\0')) 152590075Sobrien return (EINVAL); 152690075Sobrien if (iv < 0) 152790075Sobrien return (EINVAL); 152890075Sobrien switch (vtp[0]) { 152990075Sobrien case 't': 153090075Sobrien case 'T': 153190075Sobrien iv *= 1024; 153290075Sobrien case 'g': 153390075Sobrien case 'G': 153490075Sobrien iv *= 1024; 153590075Sobrien case 'm': 153690075Sobrien case 'M': 153790075Sobrien iv *= 1024; 153890075Sobrien case 'k': 153990075Sobrien case 'K': 154090075Sobrien iv *= 1024; 154190075Sobrien case '\0': 154290075Sobrien break; 154390075Sobrien default: 154490075Sobrien return (EINVAL); 154590075Sobrien } 154690075Sobrien *value = iv; 154790075Sobrien 154890075Sobrien return (0); 154990075Sobrien} 155090075Sobrien 155190075Sobrienchar * 155290075Sobrienvfs_getopts(struct vfsoptlist *opts, const char *name, int *error) 155390075Sobrien{ 155490075Sobrien struct vfsopt *opt; 155590075Sobrien 155690075Sobrien *error = 0; 155790075Sobrien TAILQ_FOREACH(opt, opts, link) { 155890075Sobrien if (strcmp(name, opt->name) != 0) 155990075Sobrien continue; 156090075Sobrien opt->seen = 1; 156190075Sobrien if (opt->len == 0 || 1562 ((char *)opt->value)[opt->len - 1] != '\0') { 1563 *error = EINVAL; 1564 return (NULL); 1565 } 1566 return (opt->value); 1567 } 1568 *error = ENOENT; 1569 return (NULL); 1570} 1571 1572int 1573vfs_flagopt(struct vfsoptlist *opts, const char *name, uint64_t *w, 1574 uint64_t val) 1575{ 1576 struct vfsopt *opt; 1577 1578 TAILQ_FOREACH(opt, opts, link) { 1579 if (strcmp(name, opt->name) == 0) { 1580 opt->seen = 1; 1581 if (w != NULL) 1582 *w |= val; 1583 return (1); 1584 } 1585 } 1586 if (w != NULL) 1587 *w &= ~val; 1588 return (0); 1589} 1590 1591int 1592vfs_scanopt(struct vfsoptlist *opts, const char *name, const char *fmt, ...) 1593{ 1594 va_list ap; 1595 struct vfsopt *opt; 1596 int ret; 1597 1598 KASSERT(opts != NULL, ("vfs_getopt: caller passed 'opts' as NULL")); 1599 1600 TAILQ_FOREACH(opt, opts, link) { 1601 if (strcmp(name, opt->name) != 0) 1602 continue; 1603 opt->seen = 1; 1604 if (opt->len == 0 || opt->value == NULL) 1605 return (0); 1606 if (((char *)opt->value)[opt->len - 1] != '\0') 1607 return (0); 1608 va_start(ap, fmt); 1609 ret = vsscanf(opt->value, fmt, ap); 1610 va_end(ap); 1611 return (ret); 1612 } 1613 return (0); 1614} 1615 1616int 1617vfs_setopt(struct vfsoptlist *opts, const char *name, void *value, int len) 1618{ 1619 struct vfsopt *opt; 1620 1621 TAILQ_FOREACH(opt, opts, link) { 1622 if (strcmp(name, opt->name) != 0) 1623 continue; 1624 opt->seen = 1; 1625 if (opt->value == NULL) 1626 opt->len = len; 1627 else { 1628 if (opt->len != len) 1629 return (EINVAL); 1630 bcopy(value, opt->value, len); 1631 } 1632 return (0); 1633 } 1634 return (ENOENT); 1635} 1636 1637int 1638vfs_setopt_part(struct vfsoptlist *opts, const char *name, void *value, int len) 1639{ 1640 struct vfsopt *opt; 1641 1642 TAILQ_FOREACH(opt, opts, link) { 1643 if (strcmp(name, opt->name) != 0) 1644 continue; 1645 opt->seen = 1; 1646 if (opt->value == NULL) 1647 opt->len = len; 1648 else { 1649 if (opt->len < len) 1650 return (EINVAL); 1651 opt->len = len; 1652 bcopy(value, opt->value, len); 1653 } 1654 return (0); 1655 } 1656 return (ENOENT); 1657} 1658 1659int 1660vfs_setopts(struct vfsoptlist *opts, const char *name, const char *value) 1661{ 1662 struct vfsopt *opt; 1663 1664 TAILQ_FOREACH(opt, opts, link) { 1665 if (strcmp(name, opt->name) != 0) 1666 continue; 1667 opt->seen = 1; 1668 if (opt->value == NULL) 1669 opt->len = strlen(value) + 1; 1670 else if (strlcpy(opt->value, value, opt->len) >= opt->len) 1671 return (EINVAL); 1672 return (0); 1673 } 1674 return (ENOENT); 1675} 1676 1677/* 1678 * Find and copy a mount option. 1679 * 1680 * The size of the buffer has to be specified 1681 * in len, if it is not the same length as the 1682 * mount option, EINVAL is returned. 1683 * Returns ENOENT if the option is not found. 1684 */ 1685int 1686vfs_copyopt(opts, name, dest, len) 1687 struct vfsoptlist *opts; 1688 const char *name; 1689 void *dest; 1690 int len; 1691{ 1692 struct vfsopt *opt; 1693 1694 KASSERT(opts != NULL, ("vfs_copyopt: caller passed 'opts' as NULL")); 1695 1696 TAILQ_FOREACH(opt, opts, link) { 1697 if (strcmp(name, opt->name) == 0) { 1698 opt->seen = 1; 1699 if (len != opt->len) 1700 return (EINVAL); 1701 bcopy(opt->value, dest, opt->len); 1702 return (0); 1703 } 1704 } 1705 return (ENOENT); 1706} 1707 1708/* 1709 * These are helper functions for filesystems to traverse all 1710 * their vnodes. See MNT_VNODE_FOREACH() in sys/mount.h. 1711 * 1712 * This interface has been deprecated in favor of MNT_VNODE_FOREACH_ALL. 1713 */ 1714 1715MALLOC_DECLARE(M_VNODE_MARKER); 1716 1717struct vnode * 1718__mnt_vnode_next(struct vnode **mvp, struct mount *mp) 1719{ 1720 struct vnode *vp; 1721 1722 mtx_assert(MNT_MTX(mp), MA_OWNED); 1723 1724 KASSERT((*mvp)->v_mount == mp, ("marker vnode mount list mismatch")); 1725 if (should_yield()) { 1726 MNT_IUNLOCK(mp); 1727 kern_yield(PRI_UNCHANGED); 1728 MNT_ILOCK(mp); 1729 } 1730 vp = TAILQ_NEXT(*mvp, v_nmntvnodes); 1731 while (vp != NULL && vp->v_type == VMARKER) 1732 vp = TAILQ_NEXT(vp, v_nmntvnodes); 1733 1734 /* Check if we are done */ 1735 if (vp == NULL) { 1736 __mnt_vnode_markerfree(mvp, mp); 1737 return (NULL); 1738 } 1739 TAILQ_REMOVE(&mp->mnt_nvnodelist, *mvp, v_nmntvnodes); 1740 TAILQ_INSERT_AFTER(&mp->mnt_nvnodelist, vp, *mvp, v_nmntvnodes); 1741 return (vp); 1742} 1743 1744struct vnode * 1745__mnt_vnode_first(struct vnode **mvp, struct mount *mp) 1746{ 1747 struct vnode *vp; 1748 1749 mtx_assert(MNT_MTX(mp), MA_OWNED); 1750 1751 vp = TAILQ_FIRST(&mp->mnt_nvnodelist); 1752 while (vp != NULL && vp->v_type == VMARKER) 1753 vp = TAILQ_NEXT(vp, v_nmntvnodes); 1754 1755 /* Check if we are done */ 1756 if (vp == NULL) { 1757 *mvp = NULL; 1758 return (NULL); 1759 } 1760 MNT_REF(mp); 1761 MNT_IUNLOCK(mp); 1762 *mvp = (struct vnode *) malloc(sizeof(struct vnode), 1763 M_VNODE_MARKER, 1764 M_WAITOK | M_ZERO); 1765 MNT_ILOCK(mp); 1766 (*mvp)->v_type = VMARKER; 1767 1768 vp = TAILQ_FIRST(&mp->mnt_nvnodelist); 1769 while (vp != NULL && vp->v_type == VMARKER) 1770 vp = TAILQ_NEXT(vp, v_nmntvnodes); 1771 1772 /* Check if we are done */ 1773 if (vp == NULL) { 1774 MNT_IUNLOCK(mp); 1775 free(*mvp, M_VNODE_MARKER); 1776 MNT_ILOCK(mp); 1777 *mvp = NULL; 1778 MNT_REL(mp); 1779 return (NULL); 1780 } 1781 (*mvp)->v_mount = mp; 1782 TAILQ_INSERT_AFTER(&mp->mnt_nvnodelist, vp, *mvp, v_nmntvnodes); 1783 return (vp); 1784} 1785 1786 1787void 1788__mnt_vnode_markerfree(struct vnode **mvp, struct mount *mp) 1789{ 1790 1791 if (*mvp == NULL) 1792 return; 1793 1794 mtx_assert(MNT_MTX(mp), MA_OWNED); 1795 1796 KASSERT((*mvp)->v_mount == mp, ("marker vnode mount list mismatch")); 1797 TAILQ_REMOVE(&mp->mnt_nvnodelist, *mvp, v_nmntvnodes); 1798 MNT_IUNLOCK(mp); 1799 free(*mvp, M_VNODE_MARKER); 1800 MNT_ILOCK(mp); 1801 *mvp = NULL; 1802 MNT_REL(mp); 1803} 1804 1805int 1806__vfs_statfs(struct mount *mp, struct statfs *sbp) 1807{ 1808 int error; 1809 1810 error = mp->mnt_op->vfs_statfs(mp, &mp->mnt_stat); 1811 if (sbp != &mp->mnt_stat) 1812 *sbp = mp->mnt_stat; 1813 return (error); 1814} 1815 1816void 1817vfs_mountedfrom(struct mount *mp, const char *from) 1818{ 1819 1820 bzero(mp->mnt_stat.f_mntfromname, sizeof mp->mnt_stat.f_mntfromname); 1821 strlcpy(mp->mnt_stat.f_mntfromname, from, 1822 sizeof mp->mnt_stat.f_mntfromname); 1823} 1824 1825/* 1826 * --------------------------------------------------------------------- 1827 * This is the api for building mount args and mounting filesystems from 1828 * inside the kernel. 1829 * 1830 * The API works by accumulation of individual args. First error is 1831 * latched. 1832 * 1833 * XXX: should be documented in new manpage kernel_mount(9) 1834 */ 1835 1836/* A memory allocation which must be freed when we are done */ 1837struct mntaarg { 1838 SLIST_ENTRY(mntaarg) next; 1839}; 1840 1841/* The header for the mount arguments */ 1842struct mntarg { 1843 struct iovec *v; 1844 int len; 1845 int error; 1846 SLIST_HEAD(, mntaarg) list; 1847}; 1848 1849/* 1850 * Add a boolean argument. 1851 * 1852 * flag is the boolean value. 1853 * name must start with "no". 1854 */ 1855struct mntarg * 1856mount_argb(struct mntarg *ma, int flag, const char *name) 1857{ 1858 1859 KASSERT(name[0] == 'n' && name[1] == 'o', 1860 ("mount_argb(...,%s): name must start with 'no'", name)); 1861 1862 return (mount_arg(ma, name + (flag ? 2 : 0), NULL, 0)); 1863} 1864 1865/* 1866 * Add an argument printf style 1867 */ 1868struct mntarg * 1869mount_argf(struct mntarg *ma, const char *name, const char *fmt, ...) 1870{ 1871 va_list ap; 1872 struct mntaarg *maa; 1873 struct sbuf *sb; 1874 int len; 1875 1876 if (ma == NULL) { 1877 ma = malloc(sizeof *ma, M_MOUNT, M_WAITOK | M_ZERO); 1878 SLIST_INIT(&ma->list); 1879 } 1880 if (ma->error) 1881 return (ma); 1882 1883 ma->v = realloc(ma->v, sizeof *ma->v * (ma->len + 2), 1884 M_MOUNT, M_WAITOK); 1885 ma->v[ma->len].iov_base = (void *)(uintptr_t)name; 1886 ma->v[ma->len].iov_len = strlen(name) + 1; 1887 ma->len++; 1888 1889 sb = sbuf_new_auto(); 1890 va_start(ap, fmt); 1891 sbuf_vprintf(sb, fmt, ap); 1892 va_end(ap); 1893 sbuf_finish(sb); 1894 len = sbuf_len(sb) + 1; 1895 maa = malloc(sizeof *maa + len, M_MOUNT, M_WAITOK | M_ZERO); 1896 SLIST_INSERT_HEAD(&ma->list, maa, next); 1897 bcopy(sbuf_data(sb), maa + 1, len); 1898 sbuf_delete(sb); 1899 1900 ma->v[ma->len].iov_base = maa + 1; 1901 ma->v[ma->len].iov_len = len; 1902 ma->len++; 1903 1904 return (ma); 1905} 1906 1907/* 1908 * Add an argument which is a userland string. 1909 */ 1910struct mntarg * 1911mount_argsu(struct mntarg *ma, const char *name, const void *val, int len) 1912{ 1913 struct mntaarg *maa; 1914 char *tbuf; 1915 1916 if (val == NULL) 1917 return (ma); 1918 if (ma == NULL) { 1919 ma = malloc(sizeof *ma, M_MOUNT, M_WAITOK | M_ZERO); 1920 SLIST_INIT(&ma->list); 1921 } 1922 if (ma->error) 1923 return (ma); 1924 maa = malloc(sizeof *maa + len, M_MOUNT, M_WAITOK | M_ZERO); 1925 SLIST_INSERT_HEAD(&ma->list, maa, next); 1926 tbuf = (void *)(maa + 1); 1927 ma->error = copyinstr(val, tbuf, len, NULL); 1928 return (mount_arg(ma, name, tbuf, -1)); 1929} 1930 1931/* 1932 * Plain argument. 1933 * 1934 * If length is -1, treat value as a C string. 1935 */ 1936struct mntarg * 1937mount_arg(struct mntarg *ma, const char *name, const void *val, int len) 1938{ 1939 1940 if (ma == NULL) { 1941 ma = malloc(sizeof *ma, M_MOUNT, M_WAITOK | M_ZERO); 1942 SLIST_INIT(&ma->list); 1943 } 1944 if (ma->error) 1945 return (ma); 1946 1947 ma->v = realloc(ma->v, sizeof *ma->v * (ma->len + 2), 1948 M_MOUNT, M_WAITOK); 1949 ma->v[ma->len].iov_base = (void *)(uintptr_t)name; 1950 ma->v[ma->len].iov_len = strlen(name) + 1; 1951 ma->len++; 1952 1953 ma->v[ma->len].iov_base = (void *)(uintptr_t)val; 1954 if (len < 0) 1955 ma->v[ma->len].iov_len = strlen(val) + 1; 1956 else 1957 ma->v[ma->len].iov_len = len; 1958 ma->len++; 1959 return (ma); 1960} 1961 1962/* 1963 * Free a mntarg structure 1964 */ 1965static void 1966free_mntarg(struct mntarg *ma) 1967{ 1968 struct mntaarg *maa; 1969 1970 while (!SLIST_EMPTY(&ma->list)) { 1971 maa = SLIST_FIRST(&ma->list); 1972 SLIST_REMOVE_HEAD(&ma->list, next); 1973 free(maa, M_MOUNT); 1974 } 1975 free(ma->v, M_MOUNT); 1976 free(ma, M_MOUNT); 1977} 1978 1979/* 1980 * Mount a filesystem 1981 */ 1982int 1983kernel_mount(struct mntarg *ma, uint64_t flags) 1984{ 1985 struct uio auio; 1986 int error; 1987 1988 KASSERT(ma != NULL, ("kernel_mount NULL ma")); 1989 KASSERT(ma->v != NULL, ("kernel_mount NULL ma->v")); 1990 KASSERT(!(ma->len & 1), ("kernel_mount odd ma->len (%d)", ma->len)); 1991 1992 auio.uio_iov = ma->v; 1993 auio.uio_iovcnt = ma->len; 1994 auio.uio_segflg = UIO_SYSSPACE; 1995 1996 error = ma->error; 1997 if (!error) 1998 error = vfs_donmount(curthread, flags, &auio); 1999 free_mntarg(ma); 2000 return (error); 2001} 2002 2003/* 2004 * A printflike function to mount a filesystem. 2005 */ 2006int 2007kernel_vmount(int flags, ...) 2008{ 2009 struct mntarg *ma = NULL; 2010 va_list ap; 2011 const char *cp; 2012 const void *vp; 2013 int error; 2014 2015 va_start(ap, flags); 2016 for (;;) { 2017 cp = va_arg(ap, const char *); 2018 if (cp == NULL) 2019 break; 2020 vp = va_arg(ap, const void *); 2021 ma = mount_arg(ma, cp, vp, (vp != NULL ? -1 : 0)); 2022 } 2023 va_end(ap); 2024 2025 error = kernel_mount(ma, flags); 2026 return (error); 2027} 2028 2029void 2030vfs_oexport_conv(const struct oexport_args *oexp, struct export_args *exp) 2031{ 2032 2033 bcopy(oexp, exp, sizeof(*oexp)); 2034 exp->ex_numsecflavors = 0; 2035} 2036