+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * @(#)vfs_syscalls.c 7.43 (Berkeley) %G%
+ */
+
+#include "param.h"
+#include "systm.h"
+#include "syscontext.h"
+#include "kernel.h"
+#include "file.h"
+#include "stat.h"
+#include "vnode.h"
+#include "mount.h"
+#include "proc.h"
+#include "uio.h"
+#include "malloc.h"
+
+#undef RETURN
+#define RETURN(val) { scp->u_error = (val); if (scp->u_spare[0] != 0) panic("lock count"); return; }
+
+/*
+ * Virtual File System System Calls
+ */
+
+/*
+ * mount system call
+ */
+mount(scp)
+ register struct syscontext *scp;
+{
+ register struct a {
+ int type;
+ char *dir;
+ int flags;
+ caddr_t data;
+ } *uap = (struct a *)scp->sc_ap;
+ register struct nameidata *ndp = &scp->sc_nd;
+ register struct vnode *vp;
+ register struct mount *mp;
+ int error, flag;
+
+ /*
+ * Must be super user
+ */
+ if (error = suser(scp->sc_cred, &scp->sc_acflag))
+ RETURN (error);
+ /*
+ * Get vnode to be covered
+ */
+ ndp->ni_nameiop = LOOKUP | FOLLOW | LOCKLEAF;
+ ndp->ni_segflg = UIO_USERSPACE;
+ ndp->ni_dirp = uap->dir;
+ if (error = namei(ndp))
+ RETURN (error);
+ vp = ndp->ni_vp;
+ if (uap->flags & M_UPDATE) {
+ if ((vp->v_flag & VROOT) == 0) {
+ vput(vp);
+ RETURN (EINVAL);
+ }
+ mp = vp->v_mount;
+ /*
+ * We allow going from read-only to read-write,
+ * but not from read-write to read-only.
+ */
+ if ((mp->m_flag & M_RDONLY) == 0 &&
+ (uap->flags & M_RDONLY) != 0) {
+ vput(vp);
+ RETURN (EOPNOTSUPP); /* Needs translation */
+ }
+ flag = mp->m_flag;
+ mp->m_flag |= M_UPDATE;
+ VOP_UNLOCK(vp);
+ goto update;
+ }
+ vinvalbuf(vp, 1);
+ if (vp->v_usecount != 1) {
+ vput(vp);
+ RETURN (EBUSY);
+ }
+ if (vp->v_type != VDIR) {
+ vput(vp);
+ RETURN (ENOTDIR);
+ }
+ if ((unsigned long)uap->type > MOUNT_MAXTYPE ||
+ vfssw[uap->type] == (struct vfsops *)0) {
+ vput(vp);
+ RETURN (ENODEV);
+ }
+
+ /*
+ * Allocate and initialize the file system.
+ */
+ mp = (struct mount *)malloc((u_long)sizeof(struct mount),
+ M_MOUNT, M_WAITOK);
+ mp->m_op = vfssw[uap->type];
+ mp->m_flag = 0;
+ mp->m_exroot = 0;
+ mp->m_mounth = (struct vnode *)0;
+ if (error = vfs_lock(mp)) {
+ free((caddr_t)mp, M_MOUNT);
+ vput(vp);
+ RETURN (error);
+ }
+ if (vp->v_mountedhere != (struct mount *)0) {
+ vfs_unlock(mp);
+ free((caddr_t)mp, M_MOUNT);
+ vput(vp);
+ RETURN (EBUSY);
+ }
+ vp->v_mountedhere = mp;
+ mp->m_vnodecovered = vp;
+update:
+ /*
+ * Set the mount level flags.
+ */
+ if (uap->flags & M_RDONLY)
+ mp->m_flag |= M_RDONLY;
+ else
+ mp->m_flag &= ~M_RDONLY;
+ if (uap->flags & M_NOSUID)
+ mp->m_flag |= M_NOSUID;
+ else
+ mp->m_flag &= ~M_NOSUID;
+ if (uap->flags & M_NOEXEC)
+ mp->m_flag |= M_NOEXEC;
+ else
+ mp->m_flag &= ~M_NOEXEC;
+ if (uap->flags & M_NODEV)
+ mp->m_flag |= M_NODEV;
+ else
+ mp->m_flag &= ~M_NODEV;
+ if (uap->flags & M_SYNCHRONOUS)
+ mp->m_flag |= M_SYNCHRONOUS;
+ else
+ mp->m_flag &= ~M_SYNCHRONOUS;
+ /*
+ * Mount the filesystem.
+ */
+ error = VFS_MOUNT(mp, uap->dir, uap->data, ndp);
+ if (mp->m_flag & M_UPDATE) {
+ mp->m_flag &= ~M_UPDATE;
+ vrele(vp);
+ if (error)
+ mp->m_flag = flag;
+ RETURN (error);
+ }
+ /*
+ * Put the new filesystem on the mount list after root.
+ */
+ mp->m_next = rootfs->m_next;
+ mp->m_prev = rootfs;
+ rootfs->m_next = mp;
+ mp->m_next->m_prev = mp;
+ cache_purge(vp);
+ if (!error) {
+ VOP_UNLOCK(vp);
+ vfs_unlock(mp);
+ error = VFS_START(mp, 0);
+ } else {
+ vfs_remove(mp);
+ free((caddr_t)mp, M_MOUNT);
+ vput(vp);
+ }
+ RETURN (error);
+}