PSEUDOCODE
int
vop_lookup(struct vnode *dvp,
struct vnode **vpp,
struct componentname *cnp)
{
int error;
int nameiop = cnp->cn_nameiop;
int flags = cnp->cn_flags;
int lockparent = flags & LOCKPARENT;
int islastcn = flags & ISLASTCN;
struct vnode *vp = NULL;
/*
* Check accessibility of directory.
*/
if (dvp->v_type != VDIR)
return ENOTDIR;
error = VOP_ACCESS(dvp, VEXEC, cred, cnp->cn_thread);
if (error)
return (error);
if (islastcn && (dvp->v_mount->mnt_flag & MNT_RDONLY) &&
(cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME))
return (EROFS);
/*
* Check name cache for directory/name pair. This returns ENOENT
* if the name is known not to exist, -1 if the name was found, or
* zero if not.
*/
error = cache_lookup(dvp, vpp, cnp);
if (error) {
int vpid;
if (error = ENOENT)
return error;
vp = *vpp;
if (dvp == vp) { /* lookup on "." */
VREF(vp);
error = 0;
} else if (flags & ISDOTDOT) {
/*
* We need to unlock the directory before getting
* the locked vnode for ".." to avoid deadlocks.
*/
VOP_UNLOCK(dvp);
error = vget(vp, 1);
if (!error) {
if (lockparent && islastcn)
error = VOP_LOCK(dvp);
}
} else {
error = vget(vp, 1);
if (error || !(lockparent && islastcn)) {
VOP_UNLOCK(dvp);
}
}
/*
* Check that the capability number did not change
* while we were waiting for the lock.
*/
if (!error) {
if (vpid == vp->v_id) {
/*
* dvp is locked if lockparent && islastcn.
* vp is locked.
*/
return (0);
}
vput(vp);
if (dvp != vp && lockparent && islastcn)
VOP_UNLOCK(pdp);
}
/*
* Re-lock dvp for the directory search below.
*/
error = VOP_LOCK(dvp);
if (error) {
return (error);
}
*vpp = NULL;
}
/*
* Search dvp for the component cnp->cn_nameptr.
*/
...;
if (!found) {
if ((nameiop == CREATE || nameiop == RENAME)
&& islastcn
&& directory dvp has not been removed) {
/*
* Check for write access on directory.
*/
/*
* Possibly record the position of a slot in the directory
* large enough for the new component name. This can be
* recorded in the vnode private data for dvp.
* Set the SAVENAME flag to hold onto the pathname for use
* later in VOP_CREATE or VOP_RENAME.
*/
cnp->cn_flags |= SAVENAME;
if (!lockparent)
/*
* Note that the extra data recorded above is only
* useful if lockparent is specified.
*/
VOP_UNLOCK(dvp);
return EJUSTRETURN;
}
/*
* Consider inserting name into cache.
*/
if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE)
cache_enter(dvp, NULL, cnp);
return ENOENT;
} else {
/*
* If deleting, and at end of pathname, return parameters
* which can be used to remove file. If the wantparent flag
* isnt set, we return only the directory, otherwise we go on
* and lock the inode, being careful with ".".
*/
if (nameiop == DELETE && islastcn) {
/*
* Check for write access on directory.
*/
error = VOP_ACCESS(dvp, VWRITE, cred, cnp->cn_thread);
if (error)
return (error);
if (found entry is same as dvp) {
VREF(dvp);
*vpp = dvp;
return 0;
}
error = VFS_VGET(dvp->v_mount, ..., &vp);
if (error)
return error;
if (directory is sticky
&& cred->cr_uid != 0
&& cred->cr_uid != owner of dvp
&& owner of vp != cred->cr_uid) {
vput(vp);
return EPERM;
}
*vpp = vp;
if (!lockparent)
VOP_UNLOCK(dvp);
return 0;
}
/*
* If rewriting (RENAME), return the inode and the
* information required to rewrite the present directory
* Must get inode of directory entry to verify its a
* regular file, or empty directory.
*/
if (nameiop == RENAME && wantparent && islastcn) {
error = VOP_ACCESS(dvp, VWRITE, cred, cnp->cn_thread);
if (error)
return (error);
/*
* Check for "."
*/
if (found entry is same as dvp)
return EISDIR;
error = VFS_VGET(dvp->v_mount, ..., &vp);
if (error)
return error;
*vpp = vp;
/*
* Save the name for use in VOP_RENAME later.
*/
cnp->cn_flags |= SAVENAME;
if (!lockparent)
VOP_UNLOCK(dvp);
return 0;
}
/*
* Step through the translation in the name. We do not vput the
* directory because we may need it again if a symbolic link
* is relative to the current directory. Instead we save it
* unlocked as "pdp". We must get the target inode before unlocking
* the directory to insure that the inode will not be removed
* before we get it. We prevent deadlock by always fetching
* inodes from the root, moving down the directory tree. Thus
* when following backward pointers ".." we must unlock the
* parent directory before getting the requested directory.
* There is a potential race condition here if both the current
* and parent directories are removed before the VFS_VGET for the
* inode associated with ".." returns. We hope that this occurs
* infrequently since we cannot avoid this race condition without
* implementing a sophisticated deadlock detection algorithm.
* Note also that this simple deadlock detection scheme will not
* work if the file system has any hard links other than ".."
* that point backwards in the directory structure.
*/
if (flags & ISDOTDOT) {
VOP_UNLOCK(dvp); /* race to get the inode */
error = VFS_VGET(dvp->v_mount, ..., &vp);
if (error) {
VOP_LOCK(dvp);
return (error);
}
if (lockparent && islastcn) {
error = VOP_LOCK(dvp);
if (error) {
vput(vp);
return error;
}
}
*vpp = vp;
} else if (found entry is same as dvp) {
VREF(dvp); /* we want ourself, ie "." */
*vpp = dvp;
} else {
error = VFS_VGET(dvp->v_mount, ..., &vp);
if (error)
return (error);
if (!lockparent || !islastcn)
VOP_UNLOCK(dvp);
*vpp = vp;
}
/*
* Insert name into cache if appropriate.
*/
if (cnp->cn_flags & MAKEENTRY)
cache_enter(dvp, *vpp, cnp);
return (0);
}
}
ERRORS