/* $OpenBSD: t9.2,v 1.2 2013/12/01 16:40:56 krw Exp $ *//* $NetBSD: vfs_syscalls.c,v 1.71 1996/04/23 10:29:02 mycroft Exp $ */ /* * 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. 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. * * @(#)vfs_syscalls.c 8.28 (Berkeley) 12/10/94 */ #include <sys/param.h>#include <sys/systm.h>#include <sys/namei.h>#include <sys/filedesc.h>#include <sys/kernel.h>#include <sys/file.h>#include <sys/stat.h>#include <sys/vnode.h>#include <sys/mount.h>#include <sys/proc.h>#include <sys/uio.h>#include <sys/malloc.h>#include <sys/dirent.h>#include <sys/extattr.h> #include <sys/syscallargs.h> #include <uvm/uvm_extern.h>#include <sys/sysctl.h> extern int suid_clear;int usermount = 0; /* sysctl: by default, users may not mount */ static int change_dir(struct nameidata *, struct proc *); void checkdirs(struct vnode *); /* * Redirection info so we don't have to include the union fs routines in * the kernel directly. This way, we can build unionfs as an LKM. The * pointer gets filled in later, when we modload the LKM, or when the * compiled-in unionfs code gets initialized. For now, we just set * it to a stub routine. */ int (*union_check_p)(struct proc *, struct vnode **, struct file *, struct uio, int *) = NULL; /* * Virtual File System System Calls */ /* * Mount a file system. *//* ARGSUSED */intsys_mount(p, v, retval) struct proc *p; void *v; register_t *retval;{ register struct sys_mount_args /* { syscallarg(char *) type; syscallarg(char *) path; syscallarg(int) flags; syscallarg(void *) data; } */ *uap = v; register struct vnode *vp; register struct mount *mp; int error, flag = 0;#ifdef COMPAT_43 u_long fstypenum = 0;#endif char fstypename[MFSNAMELEN]; char fspath[MNAMELEN]; struct vattr va; struct nameidata nd; struct vfsconf *vfsp; struct timeval tv; if (usermount == 0 && (error = suser(p->p_ucred, &p->p_acflag))) return (error); /* * Mount points must fit in MNAMELEN, not MAXPATHLEN. */ error = copyinstr(SCARG(uap, path), fspath, MNAMELEN, NULL); if (error) return(error); /* * Get vnode to be covered */ NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, fspath, p); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; if (SCARG(uap, flags) & MNT_UPDATE) { if ((vp->v_flag & VROOT) == 0) { vput(vp); return (EINVAL); } mp = vp->v_mount; flag = mp->mnt_flag; /* * We only allow the filesystem to be reloaded if it * is currently mounted read-only. */ if ((SCARG(uap, flags) & MNT_RELOAD) && ((mp->mnt_flag & MNT_RDONLY) == 0)) { vput(vp); return (EOPNOTSUPP); /* Needs translation */ } mp->mnt_flag |= SCARG(uap, flags) & (MNT_RELOAD | MNT_UPDATE); /* * Only root, or the user that did the original mount is * permitted to update it. */ if (mp->mnt_stat.f_owner != p->p_ucred->cr_uid && (error = suser(p->p_ucred, &p->p_acflag))) { vput(vp); return (error); } /* * Do not allow NFS export by non-root users. Silently * enforce MNT_NOSUID and MNT_NODEV for non-root users. */ if (p->p_ucred->cr_uid != 0) { if (SCARG(uap, flags) & MNT_EXPORTED) { vput(vp); return (EPERM); } SCARG(uap, flags) |= MNT_NOSUID | MNT_NODEV; } if ((error = vfs_busy(mp, LK_NOWAIT, 0, p)) != 0) { vput(vp); return (error); } VOP_UNLOCK(vp, 0, p); goto update; } /* * If the user is not root, ensure that they own the directory * onto which we are attempting to mount. */ if ((error = VOP_GETATTR(vp, &va, p->p_ucred, p)) || (va.va_uid != p->p_ucred->cr_uid && (error = suser(p->p_ucred, &p->p_acflag)))) { vput(vp); return (error); } /* * Do not allow NFS export by non-root users. Silently * enforce MNT_NOSUID and MNT_NODEV for non-root users. */ if (p->p_ucred->cr_uid != 0) { if (SCARG(uap, flags) & MNT_EXPORTED) { vput(vp); return (EPERM); } SCARG(uap, flags) |= MNT_NOSUID | MNT_NODEV; } if ((error = vinvalbuf(vp, V_SAVE, p->p_ucred, p, 0, 0)) != 0) return (error); if (vp->v_type != VDIR) { vput(vp); return (ENOTDIR); } error = copyinstr(SCARG(uap, type), fstypename, MFSNAMELEN, NULL); if (error) {#ifdef COMPAT_43 /* * Historically filesystem types were identified by number. * If we get an integer for the filesystem type instead of a * string, we check to see if it matches one of the historic * filesystem types. */ fstypenum = (u_long)SCARG(uap, type); for (vfsp = vfsconf; vfsp; vfsp = vfsp->vfc_next) if (vfsp->vfc_typenum == fstypenum) break; if (vfsp == NULL) { vput(vp); return (ENODEV); } strncpy(fstypename, vfsp->vfc_name, MFSNAMELEN); #else vput(vp); return (error);#endif } for (vfsp = vfsconf; vfsp; vfsp = vfsp->vfc_next) { if (!strcmp(vfsp->vfc_name, fstypename)) break; } if (vfsp == NULL) { vput(vp); return (EOPNOTSUPP); } if (vp->v_mountedhere != NULL) { vput(vp); return (EBUSY); } /* * Allocate and initialize the file system. */ mp = (struct mount *)malloc((u_long)sizeof(struct mount), M_MOUNT, M_WAITOK); bzero((char *)mp, (u_long)sizeof(struct mount)); lockinit(&mp->mnt_lock, PVFS, "vfslock", 0, 0); /* This error never happens, but it makes auditing easier */ if ((error = vfs_busy(mp, LK_NOWAIT, 0, p))) return (error); mp->mnt_op = vfsp->vfc_vfsops; mp->mnt_vfc = vfsp; mp->mnt_flag |= (vfsp->vfc_flags & MNT_VISFLAGMASK); strncpy(mp->mnt_stat.f_fstypename, vfsp->vfc_name, MFSNAMELEN); mp->mnt_vnodecovered = vp; mp->mnt_stat.f_owner = p->p_ucred->cr_uid;update: /* * Set the mount level flags. */ if (SCARG(uap, flags) & MNT_RDONLY) mp->mnt_flag |= MNT_RDONLY; else if (mp->mnt_flag & MNT_RDONLY) mp->mnt_flag |= MNT_WANTRDWR; mp->mnt_flag &=~ (MNT_NOSUID | MNT_NOEXEC | MNT_NODEV | MNT_SYNCHRONOUS | MNT_UNION | MNT_ASYNC | MNT_SOFTDEP | MNT_NOATIME | MNT_FORCE); mp->mnt_flag |= SCARG(uap, flags) & (MNT_NOSUID | MNT_NOEXEC | MNT_NODEV | MNT_SYNCHRONOUS | MNT_UNION | MNT_ASYNC | MNT_SOFTDEP | MNT_NOATIME | MNT_FORCE); /* * Mount the filesystem. */ error = VFS_MOUNT(mp, SCARG(uap, path), SCARG(uap, data), &nd, p); if (!error) { microtime(&tv); mp->mnt_stat.f_ctime = tv.tv_sec; } if (mp->mnt_flag & MNT_UPDATE) { vrele(vp); if (mp->mnt_flag & MNT_WANTRDWR) mp->mnt_flag &= ~MNT_RDONLY; mp->mnt_flag &=~ (MNT_UPDATE | MNT_RELOAD | MNT_FORCE | MNT_WANTRDWR); if (error) mp->mnt_flag = flag; if ((mp->mnt_flag & MNT_RDONLY) == 0) { if (mp->mnt_syncer == NULL) error = vfs_allocate_syncvnode(mp); } else { if (mp->mnt_syncer != NULL) vgone(mp->mnt_syncer); mp->mnt_syncer = NULL; } vfs_unbusy(mp, p); return (error); } vp->v_mountedhere = mp; /* * Put the new filesystem on the mount list after root. */ cache_purge(vp); if (!error) { vfsp->vfc_refcount++; simple_lock(&mountlist_slock); TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list); simple_unlock(&mountlist_slock); checkdirs(vp); VOP_UNLOCK(vp, 0, p); if ((mp->mnt_flag & MNT_RDONLY) == 0) error = vfs_allocate_syncvnode(mp); vfs_unbusy(mp, p); (void) VFS_STATFS(mp, &mp->mnt_stat, p); if ((error = VFS_START(mp, 0, p)) != 0) vrele(vp); } else { mp->mnt_vnodecovered->v_mountedhere = (struct mount *)0; vfs_unbusy(mp, p); free((caddr_t)mp, M_MOUNT); vput(vp); } return (error);} /* * Scan all active processes to see if any of them have a current * or root directory onto which the new filesystem has just been * mounted. If so, replace them with the new mount point. */voidcheckdirs(olddp) struct vnode *olddp;{ struct filedesc *fdp; struct vnode *newdp; struct proc *p; if (olddp->v_usecount == 1) return; if (VFS_ROOT(olddp->v_mountedhere, &newdp)) panic("mount: lost mount"); for (p = LIST_FIRST(&allproc); p != 0; p = LIST_NEXT(p, p_list)) { fdp = p->p_fd; if (fdp->fd_cdir == olddp) { vrele(fdp->fd_cdir); VREF(newdp); fdp->fd_cdir = newdp; } if (fdp->fd_rdir == olddp) { vrele(fdp->fd_rdir); VREF(newdp); fdp->fd_rdir = newdp; } } if (rootvnode == olddp) { vrele(rootvnode); VREF(newdp); rootvnode = newdp; } vput(newdp);} /* * Unmount a file system. * * Note: unmount takes a path to the vnode mounted on as argument, * not special file (as before). *//* ARGSUSED */intsys_unmount(p, v, retval) struct proc *p; void *v; register_t *retval;{ register struct sys_unmount_args /* { syscallarg(char *) path; syscallarg(int) flags; } */ *uap = v; register struct vnode *vp; struct mount *mp; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; 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 != p->p_ucred->cr_uid) && (error = suser(p->p_ucred, &p->p_acflag))) { vput(vp); return (error); } /* * Don't allow unmounting the root file system. */ 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); if (vfs_busy(mp, LK_EXCLUSIVE, NULL, p)) return (EBUSY); return (dounmount(mp, SCARG(uap, flags), p, vp));} /* * Do the actual file system unmount. */intdounmount(struct mount *mp, int flags, struct proc *p, struct vnode *olddp){ struct vnode *coveredvp; struct proc *p2; int error; int hadsyncer = 0; mp->mnt_flag &=~ MNT_ASYNC; cache_purgevfs(mp); /* remove cache entries for this file sys */ if (mp->mnt_syncer != NULL) { hadsyncer = 1; vgone(mp->mnt_syncer); mp->mnt_syncer = NULL; } if (((mp->mnt_flag & MNT_RDONLY) || (error = VFS_SYNC(mp, MNT_WAIT, p->p_ucred, p)) == 0) || (flags & MNT_FORCE)) error = VFS_UNMOUNT(mp, flags, p); simple_lock(&mountlist_slock); if (error) { if ((mp->mnt_flag & MNT_RDONLY) == 0 && hadsyncer) (void) vfs_allocate_syncvnode(mp); lockmgr(&mp->mnt_lock, LK_RELEASE | LK_INTERLOCK, &mountlist_slock, p); return (error); } TAILQ_REMOVE(&mountlist, mp, mnt_list); if ((coveredvp = mp->mnt_vnodecovered) != NULLVP) { if (olddp) { /* * Try to put processes back in a real directory * after a forced unmount. * XXX We're not holding a ref on olddp, which may * change, so compare id numbers. */ LIST_FOREACH(p2, &allproc, p_list) { struct filedesc *fdp = p2->p_fd; if (fdp->fd_cdir && fdp->fd_cdir->v_id == olddp->v_id) { vrele(fdp->fd_cdir); vref(coveredvp); fdp->fd_cdir = coveredvp; } if (fdp->fd_rdir && fdp->fd_rdir->v_id == olddp->v_id) { vrele(fdp->fd_rdir); vref(coveredvp); fdp->fd_rdir = coveredvp; } } } coveredvp->v_mountedhere = NULL; vrele(coveredvp); } mp->mnt_vfc->vfc_refcount--; if (mp->mnt_vnodelist.lh_first != NULL) panic("unmount: dangling vnode"); lockmgr(&mp->mnt_lock, LK_RELEASE | LK_INTERLOCK, &mountlist_slock, p); free((caddr_t)mp, M_MOUNT); return (0);} /* * Sync each mounted filesystem. */#ifdef DEBUGint syncprt = 0;struct ctldebug debug0 = { "syncprt", &syncprt };#endifLines 1–500 of 3216. Load more
/* $OpenBSD: t9.2,v 1.2 2013/12/01 16:40:56 krw Exp $ */ /* $NetBSD: vfs_syscalls.c,v 1.71 1996/04/23 10:29:02 mycroft Exp $ */ /* * 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. 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. * * @(#)vfs_syscalls.c 8.28 (Berkeley) 12/10/94 */ #include <sys/param.h> #include <sys/systm.h> #include <sys/namei.h> #include <sys/filedesc.h> #include <sys/kernel.h> #include <sys/file.h> #include <sys/stat.h> #include <sys/vnode.h> #include <sys/mount.h> #include <sys/proc.h> #include <sys/uio.h> #include <sys/malloc.h> #include <sys/dirent.h> #include <sys/extattr.h> #include <sys/syscallargs.h> #include <uvm/uvm_extern.h> #include <sys/sysctl.h> extern int suid_clear; int usermount = 0; /* sysctl: by default, users may not mount */ static int change_dir(struct nameidata *, struct proc *); void checkdirs(struct vnode *); /* * Redirection info so we don't have to include the union fs routines in * the kernel directly. This way, we can build unionfs as an LKM. The * pointer gets filled in later, when we modload the LKM, or when the * compiled-in unionfs code gets initialized. For now, we just set * it to a stub routine. */ int (*union_check_p)(struct proc *, struct vnode **, struct file *, struct uio, int *) = NULL; /* * Virtual File System System Calls */ /* * Mount a file system. */ /* ARGSUSED */ int sys_mount(p, v, retval) struct proc *p; void *v; register_t *retval; { register struct sys_mount_args /* { syscallarg(char *) type; syscallarg(char *) path; syscallarg(int) flags; syscallarg(void *) data; } */ *uap = v; register struct vnode *vp; register struct mount *mp; int error, flag = 0; #ifdef COMPAT_43 u_long fstypenum = 0; #endif char fstypename[MFSNAMELEN]; char fspath[MNAMELEN]; struct vattr va; struct nameidata nd; struct vfsconf *vfsp; struct timeval tv; if (usermount == 0 && (error = suser(p->p_ucred, &p->p_acflag))) return (error); /* * Mount points must fit in MNAMELEN, not MAXPATHLEN. */ error = copyinstr(SCARG(uap, path), fspath, MNAMELEN, NULL); if (error) return(error); /* * Get vnode to be covered */ NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, fspath, p); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; if (SCARG(uap, flags) & MNT_UPDATE) { if ((vp->v_flag & VROOT) == 0) { vput(vp); return (EINVAL); } mp = vp->v_mount; flag = mp->mnt_flag; /* * We only allow the filesystem to be reloaded if it * is currently mounted read-only. */ if ((SCARG(uap, flags) & MNT_RELOAD) && ((mp->mnt_flag & MNT_RDONLY) == 0)) { vput(vp); return (EOPNOTSUPP); /* Needs translation */ } mp->mnt_flag |= SCARG(uap, flags) & (MNT_RELOAD | MNT_UPDATE); /* * Only root, or the user that did the original mount is * permitted to update it. */ if (mp->mnt_stat.f_owner != p->p_ucred->cr_uid && (error = suser(p->p_ucred, &p->p_acflag))) { vput(vp); return (error); } /* * Do not allow NFS export by non-root users. Silently * enforce MNT_NOSUID and MNT_NODEV for non-root users. */ if (p->p_ucred->cr_uid != 0) { if (SCARG(uap, flags) & MNT_EXPORTED) { vput(vp); return (EPERM); } SCARG(uap, flags) |= MNT_NOSUID | MNT_NODEV; } if ((error = vfs_busy(mp, LK_NOWAIT, 0, p)) != 0) { vput(vp); return (error); } VOP_UNLOCK(vp, 0, p); goto update; } /* * If the user is not root, ensure that they own the directory * onto which we are attempting to mount. */ if ((error = VOP_GETATTR(vp, &va, p->p_ucred, p)) || (va.va_uid != p->p_ucred->cr_uid && (error = suser(p->p_ucred, &p->p_acflag)))) { vput(vp); return (error); } /* * Do not allow NFS export by non-root users. Silently * enforce MNT_NOSUID and MNT_NODEV for non-root users. */ if (p->p_ucred->cr_uid != 0) { if (SCARG(uap, flags) & MNT_EXPORTED) { vput(vp); return (EPERM); } SCARG(uap, flags) |= MNT_NOSUID | MNT_NODEV; } if ((error = vinvalbuf(vp, V_SAVE, p->p_ucred, p, 0, 0)) != 0) return (error); if (vp->v_type != VDIR) { vput(vp); return (ENOTDIR); } error = copyinstr(SCARG(uap, type), fstypename, MFSNAMELEN, NULL); if (error) { #ifdef COMPAT_43 /* * Historically filesystem types were identified by number. * If we get an integer for the filesystem type instead of a * string, we check to see if it matches one of the historic * filesystem types. */ fstypenum = (u_long)SCARG(uap, type); for (vfsp = vfsconf; vfsp; vfsp = vfsp->vfc_next) if (vfsp->vfc_typenum == fstypenum) break; if (vfsp == NULL) { vput(vp); return (ENODEV); } strncpy(fstypename, vfsp->vfc_name, MFSNAMELEN); #else vput(vp); return (error); #endif } for (vfsp = vfsconf; vfsp; vfsp = vfsp->vfc_next) { if (!strcmp(vfsp->vfc_name, fstypename)) break; } if (vfsp == NULL) { vput(vp); return (EOPNOTSUPP); } if (vp->v_mountedhere != NULL) { vput(vp); return (EBUSY); } /* * Allocate and initialize the file system. */ mp = (struct mount *)malloc((u_long)sizeof(struct mount), M_MOUNT, M_WAITOK); bzero((char *)mp, (u_long)sizeof(struct mount)); lockinit(&mp->mnt_lock, PVFS, "vfslock", 0, 0); /* This error never happens, but it makes auditing easier */ if ((error = vfs_busy(mp, LK_NOWAIT, 0, p))) return (error); mp->mnt_op = vfsp->vfc_vfsops; mp->mnt_vfc = vfsp; mp->mnt_flag |= (vfsp->vfc_flags & MNT_VISFLAGMASK); strncpy(mp->mnt_stat.f_fstypename, vfsp->vfc_name, MFSNAMELEN); mp->mnt_vnodecovered = vp; mp->mnt_stat.f_owner = p->p_ucred->cr_uid; update: /* * Set the mount level flags. */ if (SCARG(uap, flags) & MNT_RDONLY) mp->mnt_flag |= MNT_RDONLY; else if (mp->mnt_flag & MNT_RDONLY) mp->mnt_flag |= MNT_WANTRDWR; mp->mnt_flag &=~ (MNT_NOSUID | MNT_NOEXEC | MNT_NODEV | MNT_SYNCHRONOUS | MNT_UNION | MNT_ASYNC | MNT_SOFTDEP | MNT_NOATIME | MNT_FORCE); mp->mnt_flag |= SCARG(uap, flags) & (MNT_NOSUID | MNT_NOEXEC | MNT_NODEV | MNT_SYNCHRONOUS | MNT_UNION | MNT_ASYNC | MNT_SOFTDEP | MNT_NOATIME | MNT_FORCE); /* * Mount the filesystem. */ error = VFS_MOUNT(mp, SCARG(uap, path), SCARG(uap, data), &nd, p); if (!error) { microtime(&tv); mp->mnt_stat.f_ctime = tv.tv_sec; } if (mp->mnt_flag & MNT_UPDATE) { vrele(vp); if (mp->mnt_flag & MNT_WANTRDWR) mp->mnt_flag &= ~MNT_RDONLY; mp->mnt_flag &=~ (MNT_UPDATE | MNT_RELOAD | MNT_FORCE | MNT_WANTRDWR); if (error) mp->mnt_flag = flag; if ((mp->mnt_flag & MNT_RDONLY) == 0) { if (mp->mnt_syncer == NULL) error = vfs_allocate_syncvnode(mp); } else { if (mp->mnt_syncer != NULL) vgone(mp->mnt_syncer); mp->mnt_syncer = NULL; } vfs_unbusy(mp, p); return (error); } vp->v_mountedhere = mp; /* * Put the new filesystem on the mount list after root. */ cache_purge(vp); if (!error) { vfsp->vfc_refcount++; simple_lock(&mountlist_slock); TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list); simple_unlock(&mountlist_slock); checkdirs(vp); VOP_UNLOCK(vp, 0, p); if ((mp->mnt_flag & MNT_RDONLY) == 0) error = vfs_allocate_syncvnode(mp); vfs_unbusy(mp, p); (void) VFS_STATFS(mp, &mp->mnt_stat, p); if ((error = VFS_START(mp, 0, p)) != 0) vrele(vp); } else { mp->mnt_vnodecovered->v_mountedhere = (struct mount *)0; vfs_unbusy(mp, p); free((caddr_t)mp, M_MOUNT); vput(vp); } return (error); } /* * Scan all active processes to see if any of them have a current * or root directory onto which the new filesystem has just been * mounted. If so, replace them with the new mount point. */ void checkdirs(olddp) struct vnode *olddp; { struct filedesc *fdp; struct vnode *newdp; struct proc *p; if (olddp->v_usecount == 1) return; if (VFS_ROOT(olddp->v_mountedhere, &newdp)) panic("mount: lost mount"); for (p = LIST_FIRST(&allproc); p != 0; p = LIST_NEXT(p, p_list)) { fdp = p->p_fd; if (fdp->fd_cdir == olddp) { vrele(fdp->fd_cdir); VREF(newdp); fdp->fd_cdir = newdp; } if (fdp->fd_rdir == olddp) { vrele(fdp->fd_rdir); VREF(newdp); fdp->fd_rdir = newdp; } } if (rootvnode == olddp) { vrele(rootvnode); VREF(newdp); rootvnode = newdp; } vput(newdp); } /* * Unmount a file system. * * Note: unmount takes a path to the vnode mounted on as argument, * not special file (as before). */ /* ARGSUSED */ int sys_unmount(p, v, retval) struct proc *p; void *v; register_t *retval; { register struct sys_unmount_args /* { syscallarg(char *) path; syscallarg(int) flags; } */ *uap = v; register struct vnode *vp; struct mount *mp; int error; struct nameidata nd; NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, SCARG(uap, path), p); if ((error = namei(&nd)) != 0) return (error); vp = nd.ni_vp; 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 != p->p_ucred->cr_uid) && (error = suser(p->p_ucred, &p->p_acflag))) { vput(vp); return (error); } /* * Don't allow unmounting the root file system. */ 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); if (vfs_busy(mp, LK_EXCLUSIVE, NULL, p)) return (EBUSY); return (dounmount(mp, SCARG(uap, flags), p, vp)); } /* * Do the actual file system unmount. */ int dounmount(struct mount *mp, int flags, struct proc *p, struct vnode *olddp) { struct vnode *coveredvp; struct proc *p2; int error; int hadsyncer = 0; mp->mnt_flag &=~ MNT_ASYNC; cache_purgevfs(mp); /* remove cache entries for this file sys */ if (mp->mnt_syncer != NULL) { hadsyncer = 1; vgone(mp->mnt_syncer); mp->mnt_syncer = NULL; } if (((mp->mnt_flag & MNT_RDONLY) || (error = VFS_SYNC(mp, MNT_WAIT, p->p_ucred, p)) == 0) || (flags & MNT_FORCE)) error = VFS_UNMOUNT(mp, flags, p); simple_lock(&mountlist_slock); if (error) { if ((mp->mnt_flag & MNT_RDONLY) == 0 && hadsyncer) (void) vfs_allocate_syncvnode(mp); lockmgr(&mp->mnt_lock, LK_RELEASE | LK_INTERLOCK, &mountlist_slock, p); return (error); } TAILQ_REMOVE(&mountlist, mp, mnt_list); if ((coveredvp = mp->mnt_vnodecovered) != NULLVP) { if (olddp) { /* * Try to put processes back in a real directory * after a forced unmount. * XXX We're not holding a ref on olddp, which may * change, so compare id numbers. */ LIST_FOREACH(p2, &allproc, p_list) { struct filedesc *fdp = p2->p_fd; if (fdp->fd_cdir && fdp->fd_cdir->v_id == olddp->v_id) { vrele(fdp->fd_cdir); vref(coveredvp); fdp->fd_cdir = coveredvp; } if (fdp->fd_rdir && fdp->fd_rdir->v_id == olddp->v_id) { vrele(fdp->fd_rdir); vref(coveredvp); fdp->fd_rdir = coveredvp; } } } coveredvp->v_mountedhere = NULL; vrele(coveredvp); } mp->mnt_vfc->vfc_refcount--; if (mp->mnt_vnodelist.lh_first != NULL) panic("unmount: dangling vnode"); lockmgr(&mp->mnt_lock, LK_RELEASE | LK_INTERLOCK, &mountlist_slock, p); free((caddr_t)mp, M_MOUNT); return (0); } /* * Sync each mounted filesystem. */ #ifdef DEBUG int syncprt = 0; struct ctldebug debug0 = { "syncprt", &syncprt }; #endif