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