summaryrefslogtreecommitdiffstats
path: root/sys
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
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')
-rw-r--r--sys/conf/files2
-rw-r--r--sys/kern/vfs_conf.c396
-rw-r--r--sys/kern/vfs_extattr.c1042
-rw-r--r--sys/kern/vfs_mount.c1250
-rw-r--r--sys/kern/vfs_subr.c109
-rw-r--r--sys/kern/vfs_syscalls.c1042
-rw-r--r--sys/sys/mount.h2
7 files changed, 1214 insertions, 2629 deletions
diff --git a/sys/conf/files b/sys/conf/files
index 9994c11..a01c435 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -936,11 +936,11 @@ kern/vfs_aio.c optional vfs_aio
kern/vfs_bio.c standard
kern/vfs_cache.c standard
kern/vfs_cluster.c standard
-kern/vfs_conf.c standard
kern/vfs_default.c standard
kern/vfs_export.c standard
kern/vfs_init.c standard
kern/vfs_lookup.c standard
+kern/vfs_mount.c standard
kern/vfs_subr.c standard
kern/vfs_syscalls.c standard
kern/vfs_vnops.c standard
diff --git a/sys/kern/vfs_conf.c b/sys/kern/vfs_conf.c
deleted file mode 100644
index 20d9b90..0000000
--- a/sys/kern/vfs_conf.c
+++ /dev/null
@@ -1,396 +0,0 @@
-/*-
- * Copyright (c) 1999 Michael Smith
- * All rights reserved.
- * Copyright (c) 1999 Poul-Henning Kamp
- * All rights reserved.
- *
- * 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.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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
- * 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.
- *
- * $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/proc.h>
-
-#include "opt_ddb.h"
-
-#ifdef DDB
-#include <ddb/ddb.h>
-#endif
-
-#include <paths.h>
-
-MALLOC_DEFINE(M_MOUNT, "mount", "vfs mount structure");
-
-#define ROOTNAME "root_device"
-
-/*
- * The vnode of the system's root (/ in the filesystem, without chroot
- * active.)
- */
-struct vnode *rootvnode;
-
-/*
- * The root specifiers we will try if RB_CDROM is specified.
- */
-static char *cdrom_rootdevnames[] = {
- "cd9660:cd0a",
- "cd9660:acd0a",
- "cd9660:wcd0a",
- 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;
-
-/*
- * Find and mount the root filesystem
- */
-void
-vfs_mountroot(void *foo __unused)
-{
- char *cp;
- int i, error;
-
- /*
- * The root filesystem information is compiled in, and we are
- * booted with instructions to use it.
- */
-#ifdef ROOTDEVNAME
- if ((boothowto & RB_DFLTROOT) &&
- !vfs_mountroot_try(ROOTDEVNAME))
- return;
-#endif
- /*
- * We are booted with instructions to prompt for the root filesystem,
- * or to use the compiled-in default when it doesn't exist.
- */
- if (boothowto & (RB_DFLTROOT | RB_ASKNAME)) {
- if (!vfs_mountroot_ask())
- return;
- }
-
- /*
- * We've been given the generic "use CDROM as root" flag. This is
- * necessary because one media may be used in many different
- * devices, so we need to search for them.
- */
- if (boothowto & RB_CDROM) {
- for (i = 0; cdrom_rootdevnames[i] != NULL; i++) {
- if (!vfs_mountroot_try(cdrom_rootdevnames[i]))
- return;
- }
- }
-
- /*
- * Try to use the value read by the loader from /etc/fstab, or
- * supplied via some other means. This is the preferred
- * mechanism.
- */
- if ((cp = getenv("vfs.root.mountfrom")) != NULL) {
- error = vfs_mountroot_try(cp);
- freeenv(cp);
- if (!error)
- return;
- }
-
- /*
- * Try values that may have been computed by the machine-dependant
- * legacy code.
- */
- if (!vfs_mountroot_try(rootdevnames[0]))
- return;
- if (!vfs_mountroot_try(rootdevnames[1]))
- return;
-
- /*
- * If we have a compiled-in default, and haven't already tried it, try
- * it now.
- */
-#ifdef ROOTDEVNAME
- if (!(boothowto & RB_DFLTROOT))
- if (!vfs_mountroot_try(ROOTDEVNAME))
- return;
-#endif
-
- /*
- * Everything so far has failed, prompt on the console if we haven't
- * already tried that.
- */
- if (!(boothowto & (RB_DFLTROOT | RB_ASKNAME)) && !vfs_mountroot_ask())
- return;
- panic("Root mount failed, startup aborted.");
-}
-
-/*
- * Mount (mountfrom) as the root filesystem.
- */
-static int
-vfs_mountroot_try(char *mountfrom)
-{
- struct mount *mp;
- char *vfsname, *path;
- int error;
- char patt[32];
- int s;
-
- vfsname = NULL;
- path = NULL;
- mp = NULL;
- error = EINVAL;
-
- if (mountfrom == NULL)
- return(error); /* don't complain */
-
- s = splcam(); /* Overkill, but annoying without it */
- printf("Mounting root from %s\n", mountfrom);
- splx(s);
-
- /* parse vfs name and path */
- vfsname = malloc(MFSNAMELEN, M_MOUNT, M_WAITOK);
- path = malloc(MNAMELEN, M_MOUNT, M_WAITOK);
- vfsname[0] = path[0] = 0;
- sprintf(patt, "%%%d[a-z0-9]:%%%ds", MFSNAMELEN, MNAMELEN);
- if (sscanf(mountfrom, patt, vfsname, path) < 1)
- goto done;
-
- /* allocate a root mount */
- error = vfs_rootmountalloc(vfsname, path[0] != 0 ? path : ROOTNAME,
- &mp);
- if (error != 0) {
- printf("Can't allocate root mount for filesystem '%s': %d\n",
- vfsname, error);
- goto done;
- }
- mp->mnt_flag |= MNT_ROOTFS;
-
- /* do our best to set rootdev */
- if ((path[0] != 0) && setrootbyname(path))
- printf("setrootbyname failed\n");
-
- /* If the root device is a type "memory disk", mount RW */
- if (rootdev != NODEV && devsw(rootdev) &&
- (devsw(rootdev)->d_flags & D_MEMDISK))
- mp->mnt_flag &= ~MNT_RDONLY;
-
- /*
- * Set the mount path to be something useful, because the
- * filesystem code isn't responsible now for initialising
- * f_mntonname unless they want to override the default
- * (which is `path'.)
- */
- strncpy(mp->mnt_stat.f_mntonname, "/", MNAMELEN);
-
- error = VFS_MOUNT(mp, NULL, NULL, NULL, curthread);
-
-done:
- if (vfsname != NULL)
- free(vfsname, M_MOUNT);
- if (path != NULL)
- free(path, M_MOUNT);
- if (error != 0) {
- if (mp != NULL) {
- vfs_unbusy(mp, curthread);
- free(mp, M_MOUNT);
- }
- printf("Root mount failed: %d\n", error);
- } else {
-
- /* register with list of mounted filesystems */
- mtx_lock(&mountlist_mtx);
- TAILQ_INSERT_HEAD(&mountlist, mp, mnt_list);
- mtx_unlock(&mountlist_mtx);
-
- /* sanity check system clock against root filesystem timestamp */
- inittodr(mp->mnt_time);
- vfs_unbusy(mp, curthread);
- }
- return(error);
-}
-
-/*
- * Spin prompting on the console for a suitable root filesystem
- */
-static int
-vfs_mountroot_ask(void)
-{
- char name[128];
- int i;
- dev_t dev;
-
- for(;;) {
- printf("\nManual root filesystem specification:\n");
- printf(" <fstype>:<device> Mount <device> using filesystem <fstype>\n");
-#if defined(__i386__) || defined(__ia64__)
- printf(" eg. ufs:da0s1a\n");
-#else
- printf(" eg. ufs:da0a\n");
-#endif
- printf(" ? List valid disk boot devices\n");
- printf(" <empty line> Abort manual input\n");
- printf("\nmountroot> ");
- gets(name);
- if (name[0] == 0)
- return(1);
- if (name[0] == '?') {
- printf("Possibly valid devices for 'ufs' root:\n");
- for (i = 0; i < NUMCDEVSW; i++) {
- dev = makedev(i, 0);
- if (devsw(dev) != NULL)
- printf(" \"%s\"", devsw(dev)->d_name);
- }
- printf("\n");
- continue;
- }
- if (!vfs_mountroot_try(name))
- return(0);
- }
-}
-
-/*
- * Local helper function for vfs_mountroot_ask.
- */
-static void
-gets(char *cp)
-{
- char *lp;
- int c;
-
- lp = cp;
- for (;;) {
- printf("%c", c = cngetc() & 0177);
- switch (c) {
- case -1:
- case '\n':
- case '\r':
- *lp++ = '\0';
- return;
- case '\b':
- case '\177':
- if (lp > cp) {
- printf(" \b");
- lp--;
- }
- continue;
- case '#':
- lp--;
- if (lp < cp)
- lp = cp;
- continue;
- case '@':
- case 'u' & 037:
- lp = cp;
- printf("%c", '\n');
- continue;
- default:
- *lp++ = c;
- }
- }
-}
-
-/*
- * Convert a given name to the dev_t of the disk-like device
- * it refers to.
- */
-dev_t
-getdiskbyname(char *name) {
- char *cp;
- dev_t dev;
-
- cp = name;
- if (!bcmp(cp, "/dev/", 5))
- cp += 5;
-
- dev = NODEV;
- EVENTHANDLER_INVOKE(dev_clone, cp, strlen(cp), &dev);
- return (dev);
-}
-
-/*
- * Set rootdev to match (name), given that we expect it to
- * refer to a disk-like device.
- */
-static int
-setrootbyname(char *name)
-{
- dev_t diskdev;
-
- diskdev = getdiskbyname(name);
- if (diskdev != NODEV) {
- rootdev = diskdev;
- return (0);
- }
-
- return (1);
-}
-
-/* Show the dev_t for a disk specified by name */
-#ifdef DDB
-DB_SHOW_COMMAND(disk, db_getdiskbyname)
-{
- dev_t dev;
-
- if (modif[0] == '\0') {
- db_error("usage: show disk/devicename");
- return;
- }
- dev = getdiskbyname(modif);
- if (dev != NODEV)
- db_printf("dev_t = %p\n", dev);
- else
- db_printf("No disk device matched.\n");
-}
-#endif
diff --git a/sys/kern/vfs_extattr.c b/sys/kern/vfs_extattr.c
index 8fcb651..17da270 100644
--- a/sys/kern/vfs_extattr.c
+++ b/sys/kern/vfs_extattr.c
@@ -76,7 +76,6 @@
#include <vm/uma.h>
static int change_dir(struct nameidata *ndp, struct thread *td);
-static void checkdirs(struct vnode *olddp, struct vnode *newdp);
static int chroot_refuse_vdir_fds(struct filedesc *fdp);
static int getutimes(const struct timeval *, struct timespec *);
static int setfown(struct thread *td, struct vnode *, uid_t, gid_t);
@@ -86,1051 +85,10 @@ static int setutimes(struct thread *td, struct vnode *,
const struct timespec *, int);
static int vn_access(struct vnode *vp, int user_flags, struct ucred *cred,
struct thread *td);
-static int vfs_nmount(struct thread *td, int, struct uio *);
-
-static int usermount = 0; /* if 1, non-root can mount fs. */
int (*union_dircheckp)(struct thread *td, struct vnode **, struct file *);
int (*softdep_fsync_hook)(struct vnode *);
-SYSCTL_INT(_vfs, OID_AUTO, usermount, CTLFLAG_RW, &usermount, 0, "");
-
-/*
- * Virtual File System System Calls
- */
-
-#ifndef _SYS_SYSPROTO_H_
-struct nmount_args {
- struct iovec *iovp;
- unsigned int iovcnt;
- int flags;
-};
-#endif
-/* ARGSUSED */
-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);
-}
-
-/*
- * Release all resources related to the
- * mount options.
- */
-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);
-}
-
-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);
-}
-
/*
* Sync each mounted filesystem.
*/
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);
+}
diff --git a/sys/kern/vfs_subr.c b/sys/kern/vfs_subr.c
index a20e637..f8baa12 100644
--- a/sys/kern/vfs_subr.c
+++ b/sys/kern/vfs_subr.c
@@ -138,15 +138,6 @@ int vfs_ioopt;
SYSCTL_INT(_vfs, OID_AUTO, ioopt, CTLFLAG_RW, &vfs_ioopt, 0, "");
#endif
-/* 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;
-
/*
* Cache for the mount type id assigned to NFS. This is used for
* special checks in nfs/nfs_nqlease.c and vm/vnode_pager.c.
@@ -498,106 +489,6 @@ vfs_timestamp(tsp)
}
/*
- * Build a linked list of mount options from a struct uio.
- */
-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);
-}
-
-/*
- * 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);
-}
-
-/*
* Set vnode attributes to VNOVAL
*/
void
diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c
index 8fcb651..17da270 100644
--- a/sys/kern/vfs_syscalls.c
+++ b/sys/kern/vfs_syscalls.c
@@ -76,7 +76,6 @@
#include <vm/uma.h>
static int change_dir(struct nameidata *ndp, struct thread *td);
-static void checkdirs(struct vnode *olddp, struct vnode *newdp);
static int chroot_refuse_vdir_fds(struct filedesc *fdp);
static int getutimes(const struct timeval *, struct timespec *);
static int setfown(struct thread *td, struct vnode *, uid_t, gid_t);
@@ -86,1051 +85,10 @@ static int setutimes(struct thread *td, struct vnode *,
const struct timespec *, int);
static int vn_access(struct vnode *vp, int user_flags, struct ucred *cred,
struct thread *td);
-static int vfs_nmount(struct thread *td, int, struct uio *);
-
-static int usermount = 0; /* if 1, non-root can mount fs. */
int (*union_dircheckp)(struct thread *td, struct vnode **, struct file *);
int (*softdep_fsync_hook)(struct vnode *);
-SYSCTL_INT(_vfs, OID_AUTO, usermount, CTLFLAG_RW, &usermount, 0, "");
-
-/*
- * Virtual File System System Calls
- */
-
-#ifndef _SYS_SYSPROTO_H_
-struct nmount_args {
- struct iovec *iovp;
- unsigned int iovcnt;
- int flags;
-};
-#endif
-/* ARGSUSED */
-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);
-}
-
-/*
- * Release all resources related to the
- * mount options.
- */
-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);
-}
-
-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);
-}
-
/*
* Sync each mounted filesystem.
*/
diff --git a/sys/sys/mount.h b/sys/sys/mount.h
index 73789f2..43eb847 100644
--- a/sys/sys/mount.h
+++ b/sys/sys/mount.h
@@ -436,8 +436,6 @@ extern char *mountrootfsname;
int dounmount(struct mount *, int, struct thread *td);
int kernel_mount(struct iovec *iovp, unsigned int iovcnt, int flags);
int kernel_vmount(int flags, ...);
-int vfs_buildopts(struct uio *, struct vfsoptlist **);
-void vfs_freeopts(struct vfsoptlist *);
int vfs_getopt(struct vfsoptlist *, const char *, void **, int *);
int vfs_copyopt(struct vfsoptlist *, const char *, void *, int);
int vfs_mount(struct thread *td, const char *type, char *path,
OpenPOWER on IntegriCloud