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 + -
显示快捷键?