+ /*
+ * Must be super user
+ */
+ if (error = suser(ndp->ni_cred, &u.u_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 & MNT_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->mnt_flag & MNT_RDONLY) == 0 &&
+ (uap->flags & MNT_RDONLY) != 0) {
+ vput(vp);
+ RETURN (EOPNOTSUPP); /* Needs translation */
+ }
+ flag = mp->mnt_flag;
+ mp->mnt_flag |= MNT_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->mnt_op = vfssw[uap->type];
+ mp->mnt_flag = 0;
+ mp->mnt_exroot = 0;
+ mp->mnt_mounth = NULLVP;
+ 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->mnt_vnodecovered = vp;
+update:
+ /*
+ * Set the mount level flags.
+ */
+ if (uap->flags & MNT_RDONLY)
+ mp->mnt_flag |= MNT_RDONLY;
+ else
+ mp->mnt_flag &= ~MNT_RDONLY;
+ if (uap->flags & MNT_NOSUID)
+ mp->mnt_flag |= MNT_NOSUID;
+ else
+ mp->mnt_flag &= ~MNT_NOSUID;
+ if (uap->flags & MNT_NOEXEC)
+ mp->mnt_flag |= MNT_NOEXEC;
+ else
+ mp->mnt_flag &= ~MNT_NOEXEC;
+ if (uap->flags & MNT_NODEV)
+ mp->mnt_flag |= MNT_NODEV;
+ else
+ mp->mnt_flag &= ~MNT_NODEV;
+ if (uap->flags & MNT_SYNCHRONOUS)
+ mp->mnt_flag |= MNT_SYNCHRONOUS;
+ else
+ mp->mnt_flag &= ~MNT_SYNCHRONOUS;
+ /*
+ * Mount the filesystem.
+ */
+ error = VFS_MOUNT(mp, uap->dir, uap->data, ndp);
+ if (mp->mnt_flag & MNT_UPDATE) {
+ mp->mnt_flag &= ~MNT_UPDATE;
+ vrele(vp);
+ if (error)
+ mp->mnt_flag = flag;
+ RETURN (error);
+ }
+ /*
+ * Put the new filesystem on the mount list after root.
+ */
+ mp->mnt_next = rootfs->mnt_next;
+ mp->mnt_prev = rootfs;
+ rootfs->mnt_next = mp;
+ mp->mnt_next->mnt_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);