summaryrefslogtreecommitdiffstats
path: root/sys/kern/vfs_mount.c
diff options
context:
space:
mode:
authormux <mux@FreeBSD.org>2002-07-02 17:09:22 +0000
committermux <mux@FreeBSD.org>2002-07-02 17:09:22 +0000
commiteb5a0f4a7e3bc12038c158f02c57a11ec098b5a8 (patch)
tree24cc092d22964f80698c7a1f3bb3547b24e81294 /sys/kern/vfs_mount.c
parent86ce3862f7dc4893fb2bc2f87a6a96c042172036 (diff)
downloadFreeBSD-src-eb5a0f4a7e3bc12038c158f02c57a11ec098b5a8.zip
FreeBSD-src-eb5a0f4a7e3bc12038c158f02c57a11ec098b5a8.tar.gz
Move every code related to mount(2) in a new file, vfs_mount.c.
The file vfs_conf.c which was dealing with root mounting has been repo-copied into vfs_mount.c to preserve history. This makes nmount related development easier, and help reducing the size of vfs_syscalls.c, which is still an enormous file. Reviewed by: rwatson Repo-copy by: peter
Diffstat (limited to 'sys/kern/vfs_mount.c')
-rw-r--r--sys/kern/vfs_mount.c1250
1 files changed, 1213 insertions, 37 deletions
diff --git a/sys/kern/vfs_mount.c b/sys/kern/vfs_mount.c
index 20d9b90..289cb76 100644
--- a/sys/kern/vfs_mount.c
+++ b/sys/kern/vfs_mount.c
@@ -1,4 +1,40 @@
-/*-
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
* Copyright (c) 1999 Michael Smith
* All rights reserved.
* Copyright (c) 1999 Poul-Henning Kamp
@@ -12,11 +48,11 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
@@ -25,51 +61,57 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $FreeBSD$
+ * $FreeBSD$
*/
-/*
- * Locate and mount the root filesystem.
- *
- * The root filesystem is detailed in the kernel environment variable
- * vfs.root.mountfrom, which is expected to be in the general format
- *
- * <vfsname>:[<path>]
- * vfsname := the name of a VFS known to the kernel and capable
- * of being mounted as root
- * path := disk device name or other data used by the filesystem
- * to locate its physical store
- *
- */
-
-#include "opt_rootdevname.h"
-
#include <sys/param.h>
-#include <sys/systm.h>
-#include <sys/kernel.h>
-#include <sys/lock.h>
-#include <sys/mutex.h>
-#include <sys/vnode.h>
-#include <sys/mount.h>
-#include <sys/malloc.h>
-#include <sys/reboot.h>
-#include <sys/diskslice.h>
-#include <sys/disklabel.h>
#include <sys/conf.h>
#include <sys/cons.h>
+#include <sys/kernel.h>
+#include <sys/linker.h>
+#include <sys/malloc.h>
+#include <sys/mount.h>
+#include <sys/mutex.h>
+#include <sys/namei.h>
#include <sys/proc.h>
+#include <sys/reboot.h>
+#include <sys/sysproto.h>
+#include <sys/sx.h>
+#include <sys/vnode.h>
+#include <sys/sysctl.h>
+#include <sys/sysent.h>
+#include <sys/systm.h>
+
+#include <machine/stdarg.h>
+#include "opt_rootdevname.h"
#include "opt_ddb.h"
#ifdef DDB
#include <ddb/ddb.h>
#endif
-#include <paths.h>
+#define ROOTNAME "root_device"
+
+static void checkdirs(struct vnode *olddp, struct vnode *newdp);
+static int vfs_nmount(struct thread *td, int, struct uio *);
+static int vfs_mountroot_try(char *mountfrom);
+static int vfs_mountroot_ask(void);
+static void gets(char *cp);
+
+static int usermount = 0; /* if 1, non-root can mount fs. */
+SYSCTL_INT(_vfs, OID_AUTO, usermount, CTLFLAG_RW, &usermount, 0, "");
MALLOC_DEFINE(M_MOUNT, "mount", "vfs mount structure");
-#define ROOTNAME "root_device"
+/* List of mounted filesystems. */
+struct mntlist mountlist = TAILQ_HEAD_INITIALIZER(mountlist);
+
+/* For any iteration/modification of mountlist */
+struct mtx mountlist_mtx;
+
+/* For any iteration/modification of mnt_vnodelist */
+struct mtx mntvnode_mtx;
/*
* The vnode of the system's root (/ in the filesystem, without chroot
@@ -77,6 +119,17 @@ MALLOC_DEFINE(M_MOUNT, "mount", "vfs mount structure");
*/
struct vnode *rootvnode;
+/*
+ * The root filesystem is detailed in the kernel environment variable
+ * vfs.root.mountfrom, which is expected to be in the general format
+ *
+ * <vfsname>:[<path>]
+ * vfsname := the name of a VFS known to the kernel and capable
+ * of being mounted as root
+ * path := disk device name or other data used by the filesystem
+ * to locate its physical store
+ */
+
/*
* The root specifiers we will try if RB_CDROM is specified.
*/
@@ -87,16 +140,1082 @@ static char *cdrom_rootdevnames[] = {
NULL
};
-static int vfs_mountroot_try(char *mountfrom);
-static int vfs_mountroot_ask(void);
-static void gets(char *cp);
-
/* legacy find-root code */
char *rootdevnames[2] = {NULL, NULL};
static int setrootbyname(char *name);
dev_t rootdev = NODEV;
/*
+ * Release all resources related to the
+ * mount options.
+ */
+static void
+vfs_freeopts(struct vfsoptlist *opts)
+{
+ struct vfsopt *opt;
+
+ while (!TAILQ_EMPTY(opts)) {
+ opt = TAILQ_FIRST(opts);
+ TAILQ_REMOVE(opts, opt, link);
+ free(opt->name, M_MOUNT);
+ free(opt->value, M_MOUNT);
+ free(opt, M_MOUNT);
+ }
+ free(opts, M_MOUNT);
+}
+
+/*
+ * Build a linked list of mount options from a struct uio.
+ */
+static int
+vfs_buildopts(struct uio *auio, struct vfsoptlist **options)
+{
+ struct vfsoptlist *opts;
+ struct vfsopt *opt;
+ unsigned int i, iovcnt;
+ int error, namelen, optlen;
+
+ iovcnt = auio->uio_iovcnt;
+ opts = malloc(sizeof(struct vfsoptlist), M_MOUNT, M_WAITOK);
+ TAILQ_INIT(opts);
+ for (i = 0; i < iovcnt; i += 2) {
+ opt = malloc(sizeof(struct vfsopt), M_MOUNT, M_WAITOK);
+ namelen = auio->uio_iov[i].iov_len;
+ optlen = auio->uio_iov[i + 1].iov_len;
+ opt->name = malloc(namelen, M_MOUNT, M_WAITOK);
+ opt->value = malloc(optlen, M_MOUNT, M_WAITOK);
+ opt->len = optlen;
+ if (auio->uio_segflg == UIO_SYSSPACE) {
+ bcopy(auio->uio_iov[i].iov_base, opt->name, namelen);
+ bcopy(auio->uio_iov[i + 1].iov_base, opt->value,
+ optlen);
+ } else {
+ error = copyin(auio->uio_iov[i].iov_base, opt->name,
+ namelen);
+ if (!error)
+ error = copyin(auio->uio_iov[i + 1].iov_base,
+ opt->value, optlen);
+ if (error)
+ goto bad;
+ }
+ TAILQ_INSERT_TAIL(opts, opt, link);
+ }
+ *options = opts;
+ return (0);
+bad:
+ vfs_freeopts(opts);
+ return (error);
+}
+
+/*
+ * New mount API.
+ */
+int
+nmount(td, uap)
+ struct thread *td;
+ struct nmount_args /* {
+ syscallarg(struct iovec *) iovp;
+ syscallarg(unsigned int) iovcnt;
+ syscallarg(int) flags;
+ } */ *uap;
+{
+ struct uio auio;
+ struct iovec *iov, *needfree;
+ struct iovec aiov[UIO_SMALLIOV];
+ unsigned int i;
+ int error;
+ u_int iovlen, iovcnt;
+
+ iovcnt = SCARG(uap, iovcnt);
+ iovlen = iovcnt * sizeof (struct iovec);
+ /*
+ * Check that we have an even number of iovec's
+ * and that we have at least two options.
+ */
+ if ((iovcnt & 1) || (iovcnt < 4) || (iovcnt > UIO_MAXIOV))
+ return (EINVAL);
+
+ if (iovcnt > UIO_SMALLIOV) {
+ MALLOC(iov, struct iovec *, iovlen, M_IOV, M_WAITOK);
+ needfree = iov;
+ } else {
+ iov = aiov;
+ needfree = NULL;
+ }
+ auio.uio_iov = iov;
+ auio.uio_iovcnt = iovcnt;
+ auio.uio_segflg = UIO_USERSPACE;
+ if ((error = copyin(uap->iovp, iov, iovlen)))
+ goto finish;
+
+ for (i = 0; i < iovcnt; i++) {
+ if (iov->iov_len > MMAXOPTIONLEN) {
+ error = EINVAL;
+ goto finish;
+ }
+ iov++;
+ }
+ error = vfs_nmount(td, SCARG(uap, flags), &auio);
+finish:
+ if (needfree != NULL)
+ free(needfree, M_TEMP);
+ return (error);
+}
+
+int
+kernel_mount(iovp, iovcnt, flags)
+ struct iovec *iovp;
+ unsigned int iovcnt;
+ int flags;
+{
+ struct uio auio;
+ int error;
+
+ /*
+ * Check that we have an even number of iovec's
+ * and that we have at least two options.
+ */
+ if ((iovcnt & 1) || (iovcnt < 4))
+ return (EINVAL);
+
+ auio.uio_iov = iovp;
+ auio.uio_iovcnt = iovcnt;
+ auio.uio_segflg = UIO_SYSSPACE;
+
+ error = vfs_nmount(curthread, flags, &auio);
+ return (error);
+}
+
+int
+kernel_vmount(int flags, ...)
+{
+ struct iovec *iovp;
+ struct uio auio;
+ va_list ap;
+ unsigned int iovcnt, iovlen, len;
+ const char *cp;
+ char *buf, *pos;
+ size_t n;
+ int error, i;
+
+ len = 0;
+ va_start(ap, flags);
+ for (iovcnt = 0; (cp = va_arg(ap, const char *)) != NULL; iovcnt++)
+ len += strlen(cp) + 1;
+ va_end(ap);
+
+ if (iovcnt < 4 || iovcnt & 1)
+ return (EINVAL);
+
+ iovlen = iovcnt * sizeof (struct iovec);
+ MALLOC(iovp, struct iovec *, iovlen, M_MOUNT, M_WAITOK);
+ MALLOC(buf, char *, len, M_MOUNT, M_WAITOK);
+ pos = buf;
+ va_start(ap, flags);
+ for (i = 0; i < iovcnt; i++) {
+ cp = va_arg(ap, const char *);
+ copystr(cp, pos, len - (pos - buf), &n);
+ iovp[i].iov_base = pos;
+ iovp[i].iov_len = n;
+ pos += n;
+ }
+ va_end(ap);
+
+ auio.uio_iov = iovp;
+ auio.uio_iovcnt = iovcnt;
+ auio.uio_segflg = UIO_SYSSPACE;
+
+ error = vfs_nmount(curthread, flags, &auio);
+ FREE(iovp, M_MOUNT);
+ FREE(buf, M_MOUNT);
+ return (error);
+}
+
+/*
+ * vfs_nmount(): actually attempt a filesystem mount.
+ */
+static int
+vfs_nmount(td, fsflags, fsoptions)
+ struct thread *td;
+ int fsflags; /* Flags common to all filesystems. */
+ struct uio *fsoptions; /* Options local to the filesystem. */
+{
+ linker_file_t lf;
+ struct vnode *vp;
+ struct mount *mp;
+ struct vfsconf *vfsp;
+ struct vfsoptlist *optlist;
+ char *fstype, *fspath;
+ int error, flag = 0, kern_flag = 0;
+ int fstypelen, fspathlen;
+ struct vattr va;
+ struct nameidata nd;
+
+ error = vfs_buildopts(fsoptions, &optlist);
+ if (error)
+ return (error);
+
+ /*
+ * We need these two options before the others,
+ * and they are mandatory for any filesystem.
+ * Ensure they are NUL terminated as well.
+ */
+ fstypelen = 0;
+ error = vfs_getopt(optlist, "fstype", (void **)&fstype, &fstypelen);
+ if (error || fstype[fstypelen - 1] != '\0') {
+ error = EINVAL;
+ goto bad;
+ }
+ fspathlen = 0;
+ error = vfs_getopt(optlist, "fspath", (void **)&fspath, &fspathlen);
+ if (error || fspath[fspathlen - 1] != '\0') {
+ error = EINVAL;
+ goto bad;
+ }
+
+ /*
+ * Be ultra-paranoid about making sure the type and fspath
+ * variables will fit in our mp buffers, including the
+ * terminating NUL.
+ */
+ if (fstypelen >= MFSNAMELEN - 1 || fspathlen >= MNAMELEN - 1) {
+ error = ENAMETOOLONG;
+ goto bad;
+ }
+
+ if (usermount == 0) {
+ error = suser(td);
+ if (error)
+ goto bad;
+ }
+ /*
+ * Do not allow NFS export by non-root users.
+ */
+ if (fsflags & MNT_EXPORTED) {
+ error = suser(td);
+ if (error)
+ goto bad;
+ }
+ /*
+ * Silently enforce MNT_NOSUID and MNT_NODEV for non-root users.
+ */
+ if (suser(td))
+ fsflags |= MNT_NOSUID | MNT_NODEV;
+ /*
+ * Get vnode to be covered
+ */
+ NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, fspath, td);
+ if ((error = namei(&nd)) != 0)
+ goto bad;
+ NDFREE(&nd, NDF_ONLY_PNBUF);
+ vp = nd.ni_vp;
+ if (fsflags & MNT_UPDATE) {
+ if ((vp->v_flag & VROOT) == 0) {
+ vput(vp);
+ error = EINVAL;
+ goto bad;
+ }
+ mp = vp->v_mount;
+ flag = mp->mnt_flag;
+ kern_flag = mp->mnt_kern_flag;
+ /*
+ * We only allow the filesystem to be reloaded if it
+ * is currently mounted read-only.
+ */
+ if ((fsflags & MNT_RELOAD) &&
+ ((mp->mnt_flag & MNT_RDONLY) == 0)) {
+ vput(vp);
+ error = EOPNOTSUPP; /* Needs translation */
+ goto bad;
+ }
+ /*
+ * Only root, or the user that did the original mount is
+ * permitted to update it.
+ */
+ if (mp->mnt_stat.f_owner != td->td_ucred->cr_uid) {
+ error = suser(td);
+ if (error) {
+ vput(vp);
+ goto bad;
+ }
+ }
+ if (vfs_busy(mp, LK_NOWAIT, 0, td)) {
+ vput(vp);
+ error = EBUSY;
+ goto bad;
+ }
+ mtx_lock(&vp->v_interlock);
+ if ((vp->v_flag & VMOUNT) != 0 || vp->v_mountedhere != NULL) {
+ mtx_unlock(&vp->v_interlock);
+ vfs_unbusy(mp, td);
+ vput(vp);
+ error = EBUSY;
+ goto bad;
+ }
+ vp->v_flag |= VMOUNT;
+ mtx_unlock(&vp->v_interlock);
+ mp->mnt_flag |= fsflags &
+ (MNT_RELOAD | MNT_FORCE | MNT_UPDATE | MNT_SNAPSHOT);
+ VOP_UNLOCK(vp, 0, td);
+ goto update;
+ }
+ /*
+ * If the user is not root, ensure that they own the directory
+ * onto which we are attempting to mount.
+ */
+ error = VOP_GETATTR(vp, &va, td->td_ucred, td);
+ if (error) {
+ vput(vp);
+ goto bad;
+ }
+ if (va.va_uid != td->td_ucred->cr_uid) {
+ error = suser(td);
+ if (error) {
+ vput(vp);
+ goto bad;
+ }
+ }
+ if ((error = vinvalbuf(vp, V_SAVE, td->td_ucred, td, 0, 0)) != 0) {
+ vput(vp);
+ goto bad;
+ }
+ if (vp->v_type != VDIR) {
+ vput(vp);
+ error = ENOTDIR;
+ goto bad;
+ }
+ for (vfsp = vfsconf; vfsp; vfsp = vfsp->vfc_next)
+ if (!strcmp(vfsp->vfc_name, fstype))
+ break;
+ if (vfsp == NULL) {
+ /* Only load modules for root (very important!). */
+ error = suser(td);
+ if (error) {
+ vput(vp);
+ goto bad;
+ }
+ error = securelevel_gt(td->td_ucred, 0);
+ if (error) {
+ vput(vp);
+ goto bad;
+ }
+ error = linker_load_file(fstype, &lf);
+ if (error || lf == NULL) {
+ vput(vp);
+ if (lf == NULL)
+ error = ENODEV;
+ goto bad;
+ }
+ lf->userrefs++;
+ /* Look up again to see if the VFS was loaded. */
+ for (vfsp = vfsconf; vfsp; vfsp = vfsp->vfc_next)
+ if (!strcmp(vfsp->vfc_name, fstype))
+ break;
+ if (vfsp == NULL) {
+ lf->userrefs--;
+ linker_file_unload(lf);
+ vput(vp);
+ error = ENODEV;
+ goto bad;
+ }
+ }
+ mtx_lock(&vp->v_interlock);
+ if ((vp->v_flag & VMOUNT) != 0 ||
+ vp->v_mountedhere != NULL) {
+ mtx_unlock(&vp->v_interlock);
+ vput(vp);
+ error = EBUSY;
+ goto bad;
+ }
+ vp->v_flag |= VMOUNT;
+ mtx_unlock(&vp->v_interlock);
+
+ /*
+ * Allocate and initialize the filesystem.
+ */
+ mp = malloc(sizeof(struct mount), M_MOUNT, M_WAITOK | M_ZERO);
+ TAILQ_INIT(&mp->mnt_nvnodelist);
+ TAILQ_INIT(&mp->mnt_reservedvnlist);
+ lockinit(&mp->mnt_lock, PVFS, "vfslock", 0, LK_NOPAUSE);
+ (void)vfs_busy(mp, LK_NOWAIT, 0, td);
+ mp->mnt_op = vfsp->vfc_vfsops;
+ mp->mnt_vfc = vfsp;
+ vfsp->vfc_refcount++;
+ mp->mnt_stat.f_type = vfsp->vfc_typenum;
+ mp->mnt_flag |= vfsp->vfc_flags & MNT_VISFLAGMASK;
+ strncpy(mp->mnt_stat.f_fstypename, fstype, MFSNAMELEN);
+ mp->mnt_vnodecovered = vp;
+ mp->mnt_stat.f_owner = td->td_ucred->cr_uid;
+ strncpy(mp->mnt_stat.f_mntonname, fspath, MNAMELEN);
+ mp->mnt_iosize_max = DFLTPHYS;
+ VOP_UNLOCK(vp, 0, td);
+
+update:
+ mp->mnt_optnew = optlist;
+ /*
+ * Check if the fs implements the new VFS_NMOUNT()
+ * function, since the new system call was used.
+ */
+ if (mp->mnt_op->vfs_mount != NULL) {
+ printf("%s doesn't support the new mount syscall\n",
+ mp->mnt_vfc->vfc_name);
+ mtx_lock(&vp->v_interlock);
+ vp->v_flag &= ~VMOUNT;
+ mtx_unlock(&vp->v_interlock);
+ if (mp->mnt_flag & MNT_UPDATE)
+ vfs_unbusy(mp, td);
+ else {
+ mp->mnt_vfc->vfc_refcount--;
+ vfs_unbusy(mp, td);
+ free(mp, M_MOUNT);
+ }
+ vrele(vp);
+ error = EOPNOTSUPP;
+ goto bad;
+ }
+
+ /*
+ * Set the mount level flags.
+ */
+ if (fsflags & MNT_RDONLY)
+ mp->mnt_flag |= MNT_RDONLY;
+ else if (mp->mnt_flag & MNT_RDONLY)
+ mp->mnt_kern_flag |= MNTK_WANTRDWR;
+ mp->mnt_flag &=~ MNT_UPDATEMASK;
+ mp->mnt_flag |= fsflags & (MNT_UPDATEMASK | MNT_FORCE);
+ /*
+ * Mount the filesystem.
+ * XXX The final recipients of VFS_MOUNT just overwrite the ndp they
+ * get. No freeing of cn_pnbuf.
+ */
+ error = VFS_NMOUNT(mp, &nd, td);
+ if (!error) {
+ if (mp->mnt_opt != NULL)
+ vfs_freeopts(mp->mnt_opt);
+ mp->mnt_opt = mp->mnt_optnew;
+ }
+ /*
+ * Prevent external consumers of mount
+ * options to read mnt_optnew.
+ */
+ mp->mnt_optnew = NULL;
+ if (mp->mnt_flag & MNT_UPDATE) {
+ if (mp->mnt_kern_flag & MNTK_WANTRDWR)
+ mp->mnt_flag &= ~MNT_RDONLY;
+ mp->mnt_flag &=~
+ (MNT_UPDATE | MNT_RELOAD | MNT_FORCE | MNT_SNAPSHOT);
+ mp->mnt_kern_flag &=~ MNTK_WANTRDWR;
+ if (error) {
+ mp->mnt_flag = flag;
+ mp->mnt_kern_flag = kern_flag;
+ }
+ if ((mp->mnt_flag & MNT_RDONLY) == 0) {
+ if (mp->mnt_syncer == NULL)
+ error = vfs_allocate_syncvnode(mp);
+ } else {
+ if (mp->mnt_syncer != NULL)
+ vrele(mp->mnt_syncer);
+ mp->mnt_syncer = NULL;
+ }
+ vfs_unbusy(mp, td);
+ mtx_lock(&vp->v_interlock);
+ vp->v_flag &= ~VMOUNT;
+ mtx_unlock(&vp->v_interlock);
+ vrele(vp);
+ return (error);
+ }
+ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td);
+ /*
+ * Put the new filesystem on the mount list after root.
+ */
+ cache_purge(vp);
+ if (!error) {
+ struct vnode *newdp;
+
+ mtx_lock(&vp->v_interlock);
+ vp->v_flag &= ~VMOUNT;
+ vp->v_mountedhere = mp;
+ mtx_unlock(&vp->v_interlock);
+ mtx_lock(&mountlist_mtx);
+ TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list);
+ mtx_unlock(&mountlist_mtx);
+ if (VFS_ROOT(mp, &newdp))
+ panic("mount: lost mount");
+ checkdirs(vp, newdp);
+ vput(newdp);
+ VOP_UNLOCK(vp, 0, td);
+ if ((mp->mnt_flag & MNT_RDONLY) == 0)
+ error = vfs_allocate_syncvnode(mp);
+ vfs_unbusy(mp, td);
+ if ((error = VFS_START(mp, 0, td)) != 0) {
+ vrele(vp);
+ goto bad;
+ }
+ } else {
+ mtx_lock(&vp->v_interlock);
+ vp->v_flag &= ~VMOUNT;
+ mtx_unlock(&vp->v_interlock);
+ mp->mnt_vfc->vfc_refcount--;
+ vfs_unbusy(mp, td);
+ free(mp, M_MOUNT);
+ vput(vp);
+ goto bad;
+ }
+ return (0);
+bad:
+ vfs_freeopts(optlist);
+ return (error);
+}
+
+/*
+ * Old mount API.
+ */
+#ifndef _SYS_SYSPROTO_H_
+struct mount_args {
+ char *type;
+ char *path;
+ int flags;
+ caddr_t data;
+};
+#endif
+/* ARGSUSED */
+int
+mount(td, uap)
+ struct thread *td;
+ struct mount_args /* {
+ syscallarg(char *) type;
+ syscallarg(char *) path;
+ syscallarg(int) flags;
+ syscallarg(caddr_t) data;
+ } */ *uap;
+{
+ char *fstype;
+ char *fspath;
+ int error;
+
+ fstype = malloc(MFSNAMELEN, M_TEMP, M_WAITOK);
+ fspath = malloc(MNAMELEN, M_TEMP, M_WAITOK);
+
+ /*
+ * vfs_mount() actually takes a kernel string for `type' and
+ * `path' now, so extract them.
+ */
+ error = copyinstr(SCARG(uap, type), fstype, MFSNAMELEN, NULL);
+ if (error)
+ goto finish;
+ error = copyinstr(SCARG(uap, path), fspath, MNAMELEN, NULL);
+ if (error)
+ goto finish;
+ error = vfs_mount(td, fstype, fspath, SCARG(uap, flags),
+ SCARG(uap, data));
+finish:
+ free(fstype, M_TEMP);
+ free(fspath, M_TEMP);
+ return (error);
+}
+
+/*
+ * vfs_mount(): actually attempt a filesystem mount.
+ *
+ * This routine is designed to be a "generic" entry point for routines
+ * that wish to mount a filesystem. All parameters except `fsdata' are
+ * pointers into kernel space. `fsdata' is currently still a pointer
+ * into userspace.
+ */
+int
+vfs_mount(td, fstype, fspath, fsflags, fsdata)
+ struct thread *td;
+ const char *fstype;
+ char *fspath;
+ int fsflags;
+ void *fsdata;
+{
+ linker_file_t lf;
+ struct vnode *vp;
+ struct mount *mp;
+ struct vfsconf *vfsp;
+ int error, flag = 0, kern_flag = 0;
+ struct vattr va;
+ struct nameidata nd;
+
+ /*
+ * Be ultra-paranoid about making sure the type and fspath
+ * variables will fit in our mp buffers, including the
+ * terminating NUL.
+ */
+ if (strlen(fstype) >= MFSNAMELEN || strlen(fspath) >= MNAMELEN)
+ return (ENAMETOOLONG);
+
+ if (usermount == 0) {
+ error = suser(td);
+ if (error)
+ return (error);
+ }
+ /*
+ * Do not allow NFS export by non-root users.
+ */
+ if (fsflags & MNT_EXPORTED) {
+ error = suser(td);
+ if (error)
+ return (error);
+ }
+ /*
+ * Silently enforce MNT_NOSUID and MNT_NODEV for non-root users.
+ */
+ if (suser(td))
+ fsflags |= MNT_NOSUID | MNT_NODEV;
+ /*
+ * Get vnode to be covered
+ */
+ NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, fspath, td);
+ if ((error = namei(&nd)) != 0)
+ return (error);
+ NDFREE(&nd, NDF_ONLY_PNBUF);
+ vp = nd.ni_vp;
+ if (fsflags & MNT_UPDATE) {
+ if ((vp->v_flag & VROOT) == 0) {
+ vput(vp);
+ return (EINVAL);
+ }
+ mp = vp->v_mount;
+ flag = mp->mnt_flag;
+ kern_flag = mp->mnt_kern_flag;
+ /*
+ * We only allow the filesystem to be reloaded if it
+ * is currently mounted read-only.
+ */
+ if ((fsflags & MNT_RELOAD) &&
+ ((mp->mnt_flag & MNT_RDONLY) == 0)) {
+ vput(vp);
+ return (EOPNOTSUPP); /* Needs translation */
+ }
+ /*
+ * Only root, or the user that did the original mount is
+ * permitted to update it.
+ */
+ if (mp->mnt_stat.f_owner != td->td_ucred->cr_uid) {
+ error = suser(td);
+ if (error) {
+ vput(vp);
+ return (error);
+ }
+ }
+ if (vfs_busy(mp, LK_NOWAIT, 0, td)) {
+ vput(vp);
+ return (EBUSY);
+ }
+ mtx_lock(&vp->v_interlock);
+ if ((vp->v_flag & VMOUNT) != 0 || vp->v_mountedhere != NULL) {
+ mtx_unlock(&vp->v_interlock);
+ vfs_unbusy(mp, td);
+ vput(vp);
+ return (EBUSY);
+ }
+ vp->v_flag |= VMOUNT;
+ mtx_unlock(&vp->v_interlock);
+ mp->mnt_flag |= fsflags &
+ (MNT_RELOAD | MNT_FORCE | MNT_UPDATE | MNT_SNAPSHOT);
+ VOP_UNLOCK(vp, 0, td);
+ goto update;
+ }
+ /*
+ * If the user is not root, ensure that they own the directory
+ * onto which we are attempting to mount.
+ */
+ error = VOP_GETATTR(vp, &va, td->td_ucred, td);
+ if (error) {
+ vput(vp);
+ return (error);
+ }
+ if (va.va_uid != td->td_ucred->cr_uid) {
+ error = suser(td);
+ if (error) {
+ vput(vp);
+ return (error);
+ }
+ }
+ if ((error = vinvalbuf(vp, V_SAVE, td->td_ucred, td, 0, 0)) != 0) {
+ vput(vp);
+ return (error);
+ }
+ if (vp->v_type != VDIR) {
+ vput(vp);
+ return (ENOTDIR);
+ }
+ for (vfsp = vfsconf; vfsp; vfsp = vfsp->vfc_next)
+ if (!strcmp(vfsp->vfc_name, fstype))
+ break;
+ if (vfsp == NULL) {
+ /* Only load modules for root (very important!). */
+ error = suser(td);
+ if (error) {
+ vput(vp);
+ return (error);
+ }
+ error = securelevel_gt(td->td_ucred, 0);
+ if (error) {
+ vput(vp);
+ return (error);
+ }
+ error = linker_load_file(fstype, &lf);
+ if (error || lf == NULL) {
+ vput(vp);
+ if (lf == NULL)
+ error = ENODEV;
+ return (error);
+ }
+ lf->userrefs++;
+ /* Look up again to see if the VFS was loaded. */
+ for (vfsp = vfsconf; vfsp; vfsp = vfsp->vfc_next)
+ if (!strcmp(vfsp->vfc_name, fstype))
+ break;
+ if (vfsp == NULL) {
+ lf->userrefs--;
+ linker_file_unload(lf);
+ vput(vp);
+ return (ENODEV);
+ }
+ }
+ mtx_lock(&vp->v_interlock);
+ if ((vp->v_flag & VMOUNT) != 0 ||
+ vp->v_mountedhere != NULL) {
+ mtx_unlock(&vp->v_interlock);
+ vput(vp);
+ return (EBUSY);
+ }
+ vp->v_flag |= VMOUNT;
+ mtx_unlock(&vp->v_interlock);
+
+ /*
+ * Allocate and initialize the filesystem.
+ */
+ mp = malloc(sizeof(struct mount), M_MOUNT, M_WAITOK | M_ZERO);
+ TAILQ_INIT(&mp->mnt_nvnodelist);
+ TAILQ_INIT(&mp->mnt_reservedvnlist);
+ lockinit(&mp->mnt_lock, PVFS, "vfslock", 0, LK_NOPAUSE);
+ (void)vfs_busy(mp, LK_NOWAIT, 0, td);
+ mp->mnt_op = vfsp->vfc_vfsops;
+ mp->mnt_vfc = vfsp;
+ vfsp->vfc_refcount++;
+ mp->mnt_stat.f_type = vfsp->vfc_typenum;
+ mp->mnt_flag |= vfsp->vfc_flags & MNT_VISFLAGMASK;
+ strncpy(mp->mnt_stat.f_fstypename, fstype, MFSNAMELEN);
+ mp->mnt_vnodecovered = vp;
+ mp->mnt_stat.f_owner = td->td_ucred->cr_uid;
+ strncpy(mp->mnt_stat.f_mntonname, fspath, MNAMELEN);
+ mp->mnt_iosize_max = DFLTPHYS;
+ VOP_UNLOCK(vp, 0, td);
+update:
+ /*
+ * Check if the fs implements the old VFS_MOUNT()
+ * function, since the old system call was used.
+ */
+ if (mp->mnt_op->vfs_mount == NULL) {
+ printf("%s doesn't support the old mount syscall\n",
+ mp->mnt_vfc->vfc_name);
+ mtx_lock(&vp->v_interlock);
+ vp->v_flag &= ~VMOUNT;
+ mtx_unlock(&vp->v_interlock);
+ if (mp->mnt_flag & MNT_UPDATE)
+ vfs_unbusy(mp, td);
+ else {
+ mp->mnt_vfc->vfc_refcount--;
+ vfs_unbusy(mp, td);
+ free(mp, M_MOUNT);
+ }
+ vrele(vp);
+ return (EOPNOTSUPP);
+ }
+
+ /*
+ * Set the mount level flags.
+ */
+ if (fsflags & MNT_RDONLY)
+ mp->mnt_flag |= MNT_RDONLY;
+ else if (mp->mnt_flag & MNT_RDONLY)
+ mp->mnt_kern_flag |= MNTK_WANTRDWR;
+ mp->mnt_flag &=~ MNT_UPDATEMASK;
+ mp->mnt_flag |= fsflags & (MNT_UPDATEMASK | MNT_FORCE);
+ /*
+ * Mount the filesystem.
+ * XXX The final recipients of VFS_MOUNT just overwrite the ndp they
+ * get. No freeing of cn_pnbuf.
+ */
+ error = VFS_MOUNT(mp, fspath, fsdata, &nd, td);
+ if (mp->mnt_flag & MNT_UPDATE) {
+ if (mp->mnt_kern_flag & MNTK_WANTRDWR)
+ mp->mnt_flag &= ~MNT_RDONLY;
+ mp->mnt_flag &=~
+ (MNT_UPDATE | MNT_RELOAD | MNT_FORCE | MNT_SNAPSHOT);
+ mp->mnt_kern_flag &=~ MNTK_WANTRDWR;
+ if (error) {
+ mp->mnt_flag = flag;
+ mp->mnt_kern_flag = kern_flag;
+ }
+ if ((mp->mnt_flag & MNT_RDONLY) == 0) {
+ if (mp->mnt_syncer == NULL)
+ error = vfs_allocate_syncvnode(mp);
+ } else {
+ if (mp->mnt_syncer != NULL)
+ vrele(mp->mnt_syncer);
+ mp->mnt_syncer = NULL;
+ }
+ vfs_unbusy(mp, td);
+ mtx_lock(&vp->v_interlock);
+ vp->v_flag &= ~VMOUNT;
+ mtx_unlock(&vp->v_interlock);
+ vrele(vp);
+ return (error);
+ }
+ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td);
+ /*
+ * Put the new filesystem on the mount list after root.
+ */
+ cache_purge(vp);
+ if (!error) {
+ struct vnode *newdp;
+
+ mtx_lock(&vp->v_interlock);
+ vp->v_flag &= ~VMOUNT;
+ vp->v_mountedhere = mp;
+ mtx_unlock(&vp->v_interlock);
+ mtx_lock(&mountlist_mtx);
+ TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list);
+ mtx_unlock(&mountlist_mtx);
+ if (VFS_ROOT(mp, &newdp))
+ panic("mount: lost mount");
+ checkdirs(vp, newdp);
+ vput(newdp);
+ VOP_UNLOCK(vp, 0, td);
+ if ((mp->mnt_flag & MNT_RDONLY) == 0)
+ error = vfs_allocate_syncvnode(mp);
+ vfs_unbusy(mp, td);
+ if ((error = VFS_START(mp, 0, td)) != 0)
+ vrele(vp);
+ } else {
+ mtx_lock(&vp->v_interlock);
+ vp->v_flag &= ~VMOUNT;
+ mtx_unlock(&vp->v_interlock);
+ mp->mnt_vfc->vfc_refcount--;
+ vfs_unbusy(mp, td);
+ free(mp, M_MOUNT);
+ vput(vp);
+ }
+ return (error);
+}
+
+/*
+ * Scan all active processes to see if any of them have a current
+ * or root directory of `olddp'. If so, replace them with the new
+ * mount point.
+ */
+static void
+checkdirs(olddp, newdp)
+ struct vnode *olddp, *newdp;
+{
+ struct filedesc *fdp;
+ struct proc *p;
+ int nrele;
+
+ if (olddp->v_usecount == 1)
+ return;
+ sx_slock(&allproc_lock);
+ LIST_FOREACH(p, &allproc, p_list) {
+ PROC_LOCK(p);
+ fdp = p->p_fd;
+ if (fdp == NULL) {
+ PROC_UNLOCK(p);
+ continue;
+ }
+ nrele = 0;
+ FILEDESC_LOCK(fdp);
+ if (fdp->fd_cdir == olddp) {
+ VREF(newdp);
+ fdp->fd_cdir = newdp;
+ nrele++;
+ }
+ if (fdp->fd_rdir == olddp) {
+ VREF(newdp);
+ fdp->fd_rdir = newdp;
+ nrele++;
+ }
+ FILEDESC_UNLOCK(fdp);
+ PROC_UNLOCK(p);
+ while (nrele--)
+ vrele(olddp);
+ }
+ sx_sunlock(&allproc_lock);
+ if (rootvnode == olddp) {
+ vrele(rootvnode);
+ VREF(newdp);
+ rootvnode = newdp;
+ }
+}
+
+/*
+ * Unmount a filesystem.
+ *
+ * Note: unmount takes a path to the vnode mounted on as argument,
+ * not special file (as before).
+ */
+#ifndef _SYS_SYSPROTO_H_
+struct unmount_args {
+ char *path;
+ int flags;
+};
+#endif
+/* ARGSUSED */
+int
+unmount(td, uap)
+ struct thread *td;
+ register struct unmount_args /* {
+ syscallarg(char *) path;
+ syscallarg(int) flags;
+ } */ *uap;
+{
+ register struct vnode *vp;
+ struct mount *mp;
+ int error;
+ struct nameidata nd;
+
+ NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE,
+ SCARG(uap, path), td);
+ if ((error = namei(&nd)) != 0)
+ return (error);
+ vp = nd.ni_vp;
+ NDFREE(&nd, NDF_ONLY_PNBUF);
+ mp = vp->v_mount;
+
+ /*
+ * Only root, or the user that did the original mount is
+ * permitted to unmount this filesystem.
+ */
+ if (mp->mnt_stat.f_owner != td->td_ucred->cr_uid) {
+ error = suser(td);
+ if (error) {
+ vput(vp);
+ return (error);
+ }
+ }
+
+ /*
+ * Don't allow unmounting the root filesystem.
+ */
+ if (mp->mnt_flag & MNT_ROOTFS) {
+ vput(vp);
+ return (EINVAL);
+ }
+
+ /*
+ * Must be the root of the filesystem
+ */
+ if ((vp->v_flag & VROOT) == 0) {
+ vput(vp);
+ return (EINVAL);
+ }
+ vput(vp);
+ return (dounmount(mp, SCARG(uap, flags), td));
+}
+
+/*
+ * Do the actual filesystem unmount.
+ */
+int
+dounmount(mp, flags, td)
+ struct mount *mp;
+ int flags;
+ struct thread *td;
+{
+ struct vnode *coveredvp, *fsrootvp;
+ int error;
+ int async_flag;
+
+ mtx_lock(&mountlist_mtx);
+ if (mp->mnt_kern_flag & MNTK_UNMOUNT) {
+ mtx_unlock(&mountlist_mtx);
+ return (EBUSY);
+ }
+ mp->mnt_kern_flag |= MNTK_UNMOUNT;
+ /* Allow filesystems to detect that a forced unmount is in progress. */
+ if (flags & MNT_FORCE)
+ mp->mnt_kern_flag |= MNTK_UNMOUNTF;
+ error = lockmgr(&mp->mnt_lock, LK_DRAIN | LK_INTERLOCK |
+ ((flags & MNT_FORCE) ? 0 : LK_NOWAIT), &mountlist_mtx, td);
+ if (error) {
+ mp->mnt_kern_flag &= ~(MNTK_UNMOUNT | MNTK_UNMOUNTF);
+ if (mp->mnt_kern_flag & MNTK_MWAIT)
+ wakeup(mp);
+ return (error);
+ }
+ vn_start_write(NULL, &mp, V_WAIT);
+
+ if (mp->mnt_flag & MNT_EXPUBLIC)
+ vfs_setpublicfs(NULL, NULL, NULL);
+
+ vfs_msync(mp, MNT_WAIT);
+ async_flag = mp->mnt_flag & MNT_ASYNC;
+ mp->mnt_flag &=~ MNT_ASYNC;
+ cache_purgevfs(mp); /* remove cache entries for this file sys */
+ if (mp->mnt_syncer != NULL)
+ vrele(mp->mnt_syncer);
+ /* Move process cdir/rdir refs on fs root to underlying vnode. */
+ if (VFS_ROOT(mp, &fsrootvp) == 0) {
+ if (mp->mnt_vnodecovered != NULL)
+ checkdirs(fsrootvp, mp->mnt_vnodecovered);
+ if (fsrootvp == rootvnode) {
+ vrele(rootvnode);
+ rootvnode = NULL;
+ }
+ vput(fsrootvp);
+ }
+ if (((mp->mnt_flag & MNT_RDONLY) ||
+ (error = VFS_SYNC(mp, MNT_WAIT, td->td_ucred, td)) == 0) ||
+ (flags & MNT_FORCE)) {
+ error = VFS_UNMOUNT(mp, flags, td);
+ }
+ vn_finished_write(mp);
+ if (error) {
+ /* Undo cdir/rdir and rootvnode changes made above. */
+ if (VFS_ROOT(mp, &fsrootvp) == 0) {
+ if (mp->mnt_vnodecovered != NULL)
+ checkdirs(mp->mnt_vnodecovered, fsrootvp);
+ if (rootvnode == NULL) {
+ rootvnode = fsrootvp;
+ vref(rootvnode);
+ }
+ vput(fsrootvp);
+ }
+ if ((mp->mnt_flag & MNT_RDONLY) == 0 && mp->mnt_syncer == NULL)
+ (void) vfs_allocate_syncvnode(mp);
+ mtx_lock(&mountlist_mtx);
+ mp->mnt_kern_flag &= ~(MNTK_UNMOUNT | MNTK_UNMOUNTF);
+ mp->mnt_flag |= async_flag;
+ lockmgr(&mp->mnt_lock, LK_RELEASE | LK_INTERLOCK,
+ &mountlist_mtx, td);
+ if (mp->mnt_kern_flag & MNTK_MWAIT)
+ wakeup(mp);
+ return (error);
+ }
+ mtx_lock(&mountlist_mtx);
+ TAILQ_REMOVE(&mountlist, mp, mnt_list);
+ if ((coveredvp = mp->mnt_vnodecovered) != NULL)
+ coveredvp->v_mountedhere = NULL;
+ mp->mnt_vfc->vfc_refcount--;
+ if (!TAILQ_EMPTY(&mp->mnt_nvnodelist))
+ panic("unmount: dangling vnode");
+ lockmgr(&mp->mnt_lock, LK_RELEASE | LK_INTERLOCK, &mountlist_mtx, td);
+ lockdestroy(&mp->mnt_lock);
+ if (coveredvp != NULL)
+ vrele(coveredvp);
+ if (mp->mnt_kern_flag & MNTK_MWAIT)
+ wakeup(mp);
+ if (mp->mnt_op->vfs_mount == NULL)
+ vfs_freeopts(mp->mnt_opt);
+ free(mp, M_MOUNT);
+ return (0);
+}
+
+/*
* Find and mount the root filesystem
*/
void
@@ -394,3 +1513,60 @@ DB_SHOW_COMMAND(disk, db_getdiskbyname)
db_printf("No disk device matched.\n");
}
#endif
+
+/*
+ * Get a mount option by its name.
+ *
+ * Return 0 if the option was found, ENOENT otherwise.
+ * If len is non-NULL it will be filled with the length
+ * of the option. If buf is non-NULL, it will be filled
+ * with the address of the option.
+ */
+int
+vfs_getopt(opts, name, buf, len)
+ struct vfsoptlist *opts;
+ const char *name;
+ void **buf;
+ int *len;
+{
+ struct vfsopt *opt;
+
+ TAILQ_FOREACH(opt, opts, link) {
+ if (strcmp(name, opt->name) == 0) {
+ if (len != NULL)
+ *len = opt->len;
+ if (buf != NULL)
+ *buf = opt->value;
+ return (0);
+ }
+ }
+ return (ENOENT);
+}
+
+/*
+ * Find and copy a mount option.
+ *
+ * The size of the buffer has to be specified
+ * in len, if it is not the same length as the
+ * mount option, EINVAL is returned.
+ * Returns ENOENT if the option is not found.
+ */
+int
+vfs_copyopt(opts, name, dest, len)
+ struct vfsoptlist *opts;
+ const char *name;
+ void *dest;
+ int len;
+{
+ struct vfsopt *opt;
+
+ TAILQ_FOREACH(opt, opts, link) {
+ if (strcmp(name, opt->name) == 0) {
+ if (len != opt->len)
+ return (EINVAL);
+ bcopy(opt->value, dest, opt->len);
+ return (0);
+ }
+ }
+ return (ENOENT);
+}
OpenPOWER on IntegriCloud