3216 lines · 71.5 KB · R
RawDownload
/*	$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 };#endif

Lines 1–500 of 3216.