vfs_syscalls.c

来自「基于组件方式开发操作系统的OSKIT源代码」· C语言 代码 · 共 2,115 行 · 第 1/4 页

C
2,115
字号
/*	$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. 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. * *	@(#)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/syscallargs.h>#include <vm/vm.h>#include <sys/sysctl.h>static int change_dir __P((struct nameidata *, struct proc *));void checkdirs __P((struct vnode *));int dounmount __P((struct mount *, int, struct proc *));/* * 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(caddr_t) data;	} */ *uap = v;	register struct vnode *vp;	register struct mount *mp;	int error, flag = 0;	u_long fsindex = 0;	char fstypename[MFSNAMELEN];	struct vattr va;	struct nameidata nd;	/*	 * Get vnode to be covered	 */	NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE,	    SCARG(uap, path), 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_FORCE | 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;		}		VOP_UNLOCK(vp);		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) {#if defined(COMPAT_09) || defined(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.		 */     		fsindex = (u_long)SCARG(uap, type);		if (fsindex >= nvfssw || vfssw[fsindex] == NULL) {			vput(vp);			return (ENODEV);		}		strncpy(fstypename, vfssw[fsindex]->vfs_name, MFSNAMELEN);#else		vput(vp);		return (error);#endif	}#ifdef	COMPAT_10	/* Accept `ufs' as an alias for `ffs'. */	if (!strncmp(fstypename, "ufs", MFSNAMELEN))		strncpy(fstypename, "ffs", MFSNAMELEN);#endif	for (fsindex = 0; fsindex < nvfssw; fsindex++)		if (vfssw[fsindex] != NULL &&		    !strncmp(vfssw[fsindex]->vfs_name, fstypename, MFSNAMELEN))			break;	if (fsindex >= nvfssw) {		vput(vp);		return (ENODEV);	}	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));	mp->mnt_op = vfssw[fsindex];	if ((error = vfs_lock(mp)) != 0) {		free((caddr_t)mp, M_MOUNT);		vput(vp);		return (error);	}	/* Do this early in case we block later. */	vfssw[fsindex]->vfs_refcount++;	vp->v_mountedhere = mp;	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);	mp->mnt_flag |= SCARG(uap, flags) & (MNT_NOSUID | MNT_NOEXEC |	    MNT_NODEV | MNT_SYNCHRONOUS | MNT_UNION | MNT_ASYNC);	/*	 * Mount the filesystem.	 */	error = VFS_MOUNT(mp, SCARG(uap, path), SCARG(uap, data), &nd, p);	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;		return (error);	}	/*	 * Put the new filesystem on the mount list after root.	 */	cache_purge(vp);	if (!error) {		CIRCLEQ_INSERT_TAIL(&mountlist, mp, mnt_list);		checkdirs(vp);		VOP_UNLOCK(vp);		vfs_unlock(mp);		(void) VFS_STATFS(mp, &mp->mnt_stat, p);		error = VFS_START(mp, 0, p);	} else {		mp->mnt_vnodecovered->v_mountedhere = (struct mount *)0;		vfssw[fsindex]->vfs_refcount--;		vfs_unlock(mp);		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 = allproc.lh_first; p != 0; p = p->p_list.le_next) {		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);	return (dounmount(mp, SCARG(uap, flags), p));}/* * Do the actual file system unmount. */intdounmount(mp, flags, p)	register struct mount *mp;	int flags;	struct proc *p;{	struct vnode *coveredvp;	int error;	coveredvp = mp->mnt_vnodecovered;	if (vfs_busy(mp))		return (EBUSY);	mp->mnt_flag |= MNT_UNMOUNT;	if ((error = vfs_lock(mp)) != 0)		return (error);	mp->mnt_flag &=~ MNT_ASYNC;	vnode_pager_umount(mp);	/* release cached vnodes */	cache_purgevfs(mp);	/* remove cache entries for this file sys */	if ((error = VFS_SYNC(mp, MNT_WAIT, p->p_ucred, p)) == 0 ||	    (flags & MNT_FORCE))		error = VFS_UNMOUNT(mp, flags, p);	mp->mnt_flag &= ~MNT_UNMOUNT;	vfs_unbusy(mp);	if (error) {		vfs_unlock(mp);	} else {		CIRCLEQ_REMOVE(&mountlist, mp, mnt_list);		if (coveredvp != NULLVP) {			vrele(coveredvp);			coveredvp->v_mountedhere = (struct mount *)0;		}		mp->mnt_op->vfs_refcount--;		vfs_unlock(mp);		if (mp->mnt_vnodelist.lh_first != NULL)			panic("unmount: dangling vnode");		free((caddr_t)mp, M_MOUNT);	}	return (error);}/* * Sync each mounted filesystem. */#ifdef DEBUGint syncprt = 0;struct ctldebug debug0 = { "syncprt", &syncprt };#endif/* ARGSUSED */intsys_sync(p, v, retval)	struct proc *p;	void *v;	register_t *retval;{	register struct mount *mp, *nmp;	int asyncflag;	for (mp = mountlist.cqh_first; mp != (void *)&mountlist; mp = nmp) {		/*		 * Get the next pointer in case we hang on vfs_busy		 * while we are being unmounted.		 */		nmp = mp->mnt_list.cqe_next;		/*		 * The lock check below is to avoid races with mount		 * and unmount.		 */		if ((mp->mnt_flag & (MNT_MLOCK|MNT_RDONLY|MNT_MPBUSY)) == 0 &&		    !vfs_busy(mp)) {			asyncflag = mp->mnt_flag & MNT_ASYNC;			mp->mnt_flag &= ~MNT_ASYNC;			VFS_SYNC(mp, MNT_NOWAIT, p->p_ucred, p);			if (asyncflag)				mp->mnt_flag |= MNT_ASYNC;			/*			 * Get the next pointer again, as the next filesystem			 * might have been unmounted while we were sync'ing.			 */			nmp = mp->mnt_list.cqe_next;			vfs_unbusy(mp);		}	}#ifdef DEBUG	if (syncprt)		vfs_bufstats();#endif /* DEBUG */	return (0);}/* * Change filesystem quotas. *//* ARGSUSED */intsys_quotactl(p, v, retval)	struct proc *p;	void *v;	register_t *retval;{	register struct sys_quotactl_args /* {		syscallarg(char *) path;		syscallarg(int) cmd;		syscallarg(int) uid;		syscallarg(caddr_t) arg;	} */ *uap = v;	register struct mount *mp;	int error;	struct nameidata nd;	NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p);	if ((error = namei(&nd)) != 0)		return (error);	mp = nd.ni_vp->v_mount;	vrele(nd.ni_vp);	return (VFS_QUOTACTL(mp, SCARG(uap, cmd), SCARG(uap, uid),	    SCARG(uap, arg), p));}/* * Get filesystem statistics. *//* ARGSUSED */intsys_statfs(p, v, retval)	struct proc *p;	void *v;	register_t *retval;{	register struct sys_statfs_args /* {		syscallarg(char *) path;		syscallarg(struct statfs *) buf;	} */ *uap = v;	register struct mount *mp;	register struct statfs *sp;	int error;	struct nameidata nd;	NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p);	if ((error = namei(&nd)) != 0)		return (error);	mp = nd.ni_vp->v_mount;	sp = &mp->mnt_stat;	vrele(nd.ni_vp);	if ((error = VFS_STATFS(mp, sp, p)) != 0)		return (error);	sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK;	return (copyout((caddr_t)sp, (caddr_t)SCARG(uap, buf), sizeof(*sp)));}/* * Get filesystem statistics. *//* ARGSUSED */intsys_fstatfs(p, v, retval)	struct proc *p;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?