union_vnops.c

来自「早期freebsd实现」· C语言 代码 · 共 1,496 行 · 第 1/3 页

C
1,496
字号
/* * Copyright (c) 1992, 1993, 1994 The Regents of the University of California. * Copyright (c) 1992, 1993, 1994 Jan-Simon Pendry. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Jan-Simon Pendry. * * 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. * *	@(#)union_vnops.c	8.6 (Berkeley) 2/17/94 */#include <sys/param.h>#include <sys/systm.h>#include <sys/proc.h>#include <sys/file.h>#include <sys/time.h>#include <sys/types.h>#include <sys/vnode.h>#include <sys/mount.h>#include <sys/namei.h>#include <sys/malloc.h>#include <sys/buf.h>#include <sys/queue.h>#include <miscfs/union/union.h>#define FIXUP(un) { \	if (((un)->un_flags & UN_ULOCK) == 0) { \		union_fixup(un); \	} \}static voidunion_fixup(un)	struct union_node *un;{	VOP_LOCK(un->un_uppervp);	un->un_flags |= UN_ULOCK;}static intunion_lookup1(udvp, dvp, vpp, cnp)	struct vnode *udvp;	struct vnode *dvp;	struct vnode **vpp;	struct componentname *cnp;{	int error;	struct vnode *tdvp;	struct mount *mp;	/*	 * If stepping up the directory tree, check for going	 * back across the mount point, in which case do what	 * lookup would do by stepping back down the mount	 * hierarchy.	 */	if (cnp->cn_flags & ISDOTDOT) {		for (;;) {			/*			 * Don't do the NOCROSSMOUNT check			 * at this level.  By definition,			 * union fs deals with namespaces, not			 * filesystems.			 */			if ((dvp->v_flag & VROOT) == 0)				break;			tdvp = dvp;			dvp = dvp->v_mount->mnt_vnodecovered;			vput(tdvp);			VREF(dvp);			VOP_LOCK(dvp);		}	}        error = VOP_LOOKUP(dvp, &tdvp, cnp);	if (error)		return (error);	/*	 * The parent directory will have been unlocked, unless lookup	 * found the last component.  In which case, re-lock the node	 * here to allow it to be unlocked again (phew) in union_lookup.	 */	if (dvp != tdvp && !(cnp->cn_flags & ISLASTCN))		VOP_LOCK(dvp);	dvp = tdvp;	/*	 * Lastly check if the current node is a mount point in	 * which case walk up the mount hierarchy making sure not to	 * bump into the root of the mount tree (ie. dvp != udvp).	 */	while (dvp != udvp && (dvp->v_type == VDIR) &&	       (mp = dvp->v_mountedhere)) {		if (mp->mnt_flag & MNT_MLOCK) {			mp->mnt_flag |= MNT_MWAIT;			sleep((caddr_t) mp, PVFS);			continue;		}		if (error = VFS_ROOT(mp, &tdvp)) {			vput(dvp);			return (error);		}		vput(dvp);		dvp = tdvp;	}	*vpp = dvp;	return (0);}intunion_lookup(ap)	struct vop_lookup_args /* {		struct vnodeop_desc *a_desc;		struct vnode *a_dvp;		struct vnode **a_vpp;		struct componentname *a_cnp;	} */ *ap;{	int error;	int uerror, lerror;	struct vnode *uppervp, *lowervp;	struct vnode *upperdvp, *lowerdvp;	struct vnode *dvp = ap->a_dvp;	struct union_node *dun = VTOUNION(dvp);	struct componentname *cnp = ap->a_cnp;	int lockparent = cnp->cn_flags & LOCKPARENT;	int rdonly = cnp->cn_flags & RDONLY;	struct union_mount *um = MOUNTTOUNIONMOUNT(dvp->v_mount);	struct ucred *saved_cred;	cnp->cn_flags |= LOCKPARENT;	upperdvp = dun->un_uppervp;	lowerdvp = dun->un_lowervp;	uppervp = NULLVP;	lowervp = NULLVP;	/*	 * do the lookup in the upper level.	 * if that level comsumes additional pathnames,	 * then assume that something special is going	 * on and just return that vnode.	 */	if (upperdvp) {		FIXUP(dun);		uerror = union_lookup1(um->um_uppervp, upperdvp,					&uppervp, cnp);		/*if (uppervp == upperdvp)			dun->un_flags |= UN_KLOCK;*/		if (cnp->cn_consume != 0) {			*ap->a_vpp = uppervp;			if (!lockparent)				cnp->cn_flags &= ~LOCKPARENT;			return (uerror);		}	} else {		uerror = ENOENT;	}	/*	 * in a similar way to the upper layer, do the lookup	 * in the lower layer.   this time, if there is some	 * component magic going on, then vput whatever we got	 * back from the upper layer and return the lower vnode	 * instead.	 */	if (lowerdvp) {		int nameiop;		VOP_LOCK(lowerdvp);		/*		 * Only do a LOOKUP on the bottom node, since		 * we won't be making changes to it anyway.		 */		nameiop = cnp->cn_nameiop;		cnp->cn_nameiop = LOOKUP;		if (um->um_op == UNMNT_BELOW) {			saved_cred = cnp->cn_cred;			cnp->cn_cred = um->um_cred;		}		lerror = union_lookup1(um->um_lowervp, lowerdvp,				&lowervp, cnp);		if (um->um_op == UNMNT_BELOW)			cnp->cn_cred = saved_cred;		cnp->cn_nameiop = nameiop;		if (lowervp != lowerdvp)			VOP_UNLOCK(lowerdvp);		if (cnp->cn_consume != 0) {			if (uppervp) {				if (uppervp == upperdvp)					vrele(uppervp);				else					vput(uppervp);				uppervp = NULLVP;			}			*ap->a_vpp = lowervp;			if (!lockparent)				cnp->cn_flags &= ~LOCKPARENT;			return (lerror);		}	} else {		lerror = ENOENT;	}	if (!lockparent)		cnp->cn_flags &= ~LOCKPARENT;	/*	 * at this point, we have uerror and lerror indicating	 * possible errors with the lookups in the upper and lower	 * layers.  additionally, uppervp and lowervp are (locked)	 * references to existing vnodes in the upper and lower layers.	 *	 * there are now three cases to consider.	 * 1. if both layers returned an error, then return whatever	 *    error the upper layer generated.	 *	 * 2. if the top layer failed and the bottom layer succeeded	 *    then two subcases occur.	 *    a.  the bottom vnode is not a directory, in which	 *	  case just return a new union vnode referencing	 *	  an empty top layer and the existing bottom layer.	 *    b.  the bottom vnode is a directory, in which case	 *	  create a new directory in the top-level and	 *	  continue as in case 3.	 *	 * 3. if the top layer succeeded then return a new union	 *    vnode referencing whatever the new top layer and	 *    whatever the bottom layer returned.	 */	*ap->a_vpp = NULLVP;	/* case 1. */	if ((uerror != 0) && (lerror != 0)) {		return (uerror);	}	/* case 2. */	if (uerror != 0 /* && (lerror == 0) */ ) {		if (lowervp->v_type == VDIR) { /* case 2b. */			dun->un_flags &= ~UN_ULOCK;			VOP_UNLOCK(upperdvp);			uerror = union_mkshadow(um, upperdvp, cnp, &uppervp);			VOP_LOCK(upperdvp);			dun->un_flags |= UN_ULOCK;			if (uerror) {				if (lowervp) {					vput(lowervp);					lowervp = NULLVP;				}				return (uerror);			}		}	}	if (lowervp)		VOP_UNLOCK(lowervp);	error = union_allocvp(ap->a_vpp, dvp->v_mount, dvp, upperdvp, cnp,			      uppervp, lowervp);	if (error) {		if (uppervp)			vput(uppervp);		if (lowervp)			vrele(lowervp);	} else {		if (*ap->a_vpp != dvp)			if (!lockparent || !(cnp->cn_flags & ISLASTCN))				VOP_UNLOCK(dvp);	}	return (error);}intunion_create(ap)	struct vop_create_args /* {		struct vnode *a_dvp;		struct vnode **a_vpp;		struct componentname *a_cnp;		struct vattr *a_vap;	} */ *ap;{	struct union_node *un = VTOUNION(ap->a_dvp);	struct vnode *dvp = un->un_uppervp;	if (dvp) {		int error;		struct vnode *vp;		FIXUP(un);		VREF(dvp);		un->un_flags |= UN_KLOCK;		vput(ap->a_dvp);		error = VOP_CREATE(dvp, &vp, ap->a_cnp, ap->a_vap);		if (error)			return (error);		error = union_allocvp(				ap->a_vpp,				ap->a_dvp->v_mount,				ap->a_dvp,				NULLVP,				ap->a_cnp,				vp,				NULLVP);		if (error)			vput(vp);		return (error);	}	vput(ap->a_dvp);	return (EROFS);}intunion_mknod(ap)	struct vop_mknod_args /* {		struct vnode *a_dvp;		struct vnode **a_vpp;		struct componentname *a_cnp;		struct vattr *a_vap;	} */ *ap;{	struct union_node *un = VTOUNION(ap->a_dvp);	struct vnode *dvp = un->un_uppervp;	if (dvp) {		int error;		struct vnode *vp;		FIXUP(un);		VREF(dvp);		un->un_flags |= UN_KLOCK;		vput(ap->a_dvp);		error = VOP_MKNOD(dvp, &vp, ap->a_cnp, ap->a_vap);		if (error)			return (error);		if (vp) {			error = union_allocvp(					ap->a_vpp,					ap->a_dvp->v_mount,					ap->a_dvp,					NULLVP,					ap->a_cnp,					vp,					NULLVP);			if (error)				vput(vp);		}		return (error);	}	vput(ap->a_dvp);	return (EROFS);}intunion_open(ap)	struct vop_open_args /* {		struct vnodeop_desc *a_desc;		struct vnode *a_vp;		int a_mode;		struct ucred *a_cred;		struct proc *a_p;	} */ *ap;{	struct union_node *un = VTOUNION(ap->a_vp);	struct vnode *tvp;	int mode = ap->a_mode;	struct ucred *cred = ap->a_cred;	struct proc *p = ap->a_p;	int error;	/*	 * If there is an existing upper vp then simply open that.	 */	tvp = un->un_uppervp;	if (tvp == NULLVP) {		/*		 * If the lower vnode is being opened for writing, then		 * copy the file contents to the upper vnode and open that,		 * otherwise can simply open the lower vnode.		 */		tvp = un->un_lowervp;		if ((ap->a_mode & FWRITE) && (tvp->v_type == VREG)) {			struct vnode *vp;			int i;			/*			 * Open the named file in the upper layer.  Note that			 * the file may have come into existence *since* the			 * lookup was done, since the upper layer may really			 * be a loopback mount of some other filesystem...			 * so open the file with exclusive create and barf if			 * it already exists.			 * XXX - perhaps should re-lookup the node (once more			 * with feeling) and simply open that.  Who knows.			 */			error = union_vn_create(&vp, un, p);			if (error)				return (error);			/* at this point, uppervp is locked */			union_newupper(un, vp);			un->un_flags |= UN_ULOCK;			/*			 * Now, if the file is being opened with truncation,			 * then the (new) upper vnode is ready to fly,			 * otherwise the data from the lower vnode must be			 * copied to the upper layer first.  This only works			 * for regular files (check is made above).			 */			if ((mode & O_TRUNC) == 0) {				/*				 * XXX - should not ignore errors				 * from VOP_CLOSE				 */				VOP_LOCK(tvp);				error = VOP_OPEN(tvp, FREAD, cred, p);				if (error == 0) {					error = union_copyfile(p, cred,						       tvp, un->un_uppervp);					VOP_UNLOCK(tvp);					(void) VOP_CLOSE(tvp, FREAD);				} else {					VOP_UNLOCK(tvp);				}#ifdef UNION_DIAGNOSTIC				if (!error)					uprintf("union: copied up %s\n",								un->un_path);#endif			}			un->un_flags &= ~UN_ULOCK;			VOP_UNLOCK(un->un_uppervp);			union_vn_close(un->un_uppervp, FWRITE, cred, p);			VOP_LOCK(un->un_uppervp);			un->un_flags |= UN_ULOCK;			/*			 * Subsequent IOs will go to the top layer, so			 * call close on the lower vnode and open on the			 * upper vnode to ensure that the filesystem keeps			 * its references counts right.  This doesn't do			 * the right thing with (cred) and (FREAD) though.			 * Ignoring error returns is not righ, either.			 */			for (i = 0; i < un->un_openl; i++) {				(void) VOP_CLOSE(tvp, FREAD);

⌨️ 快捷键说明

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