vop_lookup(9)
NAME
VOP_LOOKUP - lookup a component of a pathname
SYNOPSIS
#include <sys/param.h> #include <sys/vnode.h> #include <sys/namei.h> int VOP_LOOKUP(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp);
DESCRIPTION
- This entry point looks up a single pathname component in a
- given directory.
- Its arguments are:
- dvp The locked vnode of the directory to search.
- vpp The address of a variable where the resulting locked
- vnode should be
- stored.
- cnp The pathname component to be searched for.
- Cnp is a pointer to a componentname structure defined as
- follows:
- struct componentname {
- /*
* Arguments to lookup. */
- u_long cn_nameiop; /* namei operation */ u_long cn_flags; /* flags to namei */ struct thread *cn_thread; /* thread requesting
- lookup */
struct ucred *cn_cred; /* credentials */
/*
* Shared between lookup and commit routines. */
- char *cn_pnbuf; /* pathname buffer */ char *cn_nameptr; /* pointer to looked up name
- */ long cn_namelen; /* length of looked up com
- ponent */ u_long cn_hash; /* hash value of looked up
- name */ long cn_consume; /* chars to consume in
- lookup() */
- };
- Convert a component of a pathname into a pointer to a locked
- vnode. This is a very central and rather complicated routine. If the
- file system is not maintained in a strict tree hierarchy, this can result
- in a deadlock situation.
- The cnp->cn_nameiop argument is LOOKUP, CREATE, RENAME, or
- DELETE depending on the intended use of the object. When CREATE, RENAME,
- or DELETE is specified, information usable in creating, renaming, or
- deleting a directory entry may be calculated.
- Overall outline of VOP_LOOKUP:
- Check accessibility of directory. Look for name in
- cache, if found, then return name. Search for name in directo
- ry, goto to found or notfound as appropriate.
- notfound:
- If creating or renaming and at end of pathname, return
- EJUSTRETURN, leaving info on available slots else return ENOENT.
- found:
- If at end of path and deleting, return information to
- allow delete. If at end of path and renaming, lock target inode and
- return info to allow rename. If not at end, add name to cache; if
- at end and neither creating nor deleting, add name to cache.
LOCKS
- The directory, dvp should be locked on entry. If an error
- (note: the return value EJUSTRETURN is not considered an error) is de
- tected, it will be returned locked. Otherwise, it will be unlocked unless
- both LOCKPARENT and ISLASTCN are specified in cnp->cn_flags. If
- an entry is found in the directory, it will be returned locked.
RETURN VALUES
- Zero is returned with *vpp set to the locked vnode of the
- file if the component is found. If the component being searched for is
- ".", then the vnode just has an extra reference added to it with vref(9).
- The caller must take care to release the locks appropriately in this
- case.
- If the component is not found and the operation is CREATE or
- RENAME, the flag ISLASTCN is specified and the operation would succeed,
- the special return value EJUSTRETURN is returned. Otherwise, an appro
- priate error code is returned.
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 wasfound, 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 thedirectory * 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 aboveis 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 * isn't 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 it'sa * 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 donot `vput' the * directory because we may need it again if a symbolic link * is relative to the current directory. Instead wesave it * unlocked as "pdp". We must get the target inodebefore unlocking * the directory to insure that the inode will notbe removed * before we get it. We prevent deadlock by alwaysfetching * inodes from the root, moving down the directorytree. Thus * when following backward pointers ".." we must unlock the * parent directory before getting the requested directory. * There is a potential race condition here if boththe current * and parent directories are removed before theVFS_VGET for the * inode associated with ".." returns. We hope thatthis occurs * infrequently since we cannot avoid this race condition without * implementing a sophisticated deadlock detectionalgorithm. * Note also that this simple deadlock detectionscheme will not * work if the file system has any hard links otherthan ".." * 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
- [ENOTDIR] The vnode dvp does not represent a direc
- tory.
- [ENOENT] The component dvp was not found in this
- directory.
- [EACCES] Access for the specified operation is de
- nied.
- [EJUSTRETURN] A CREATE or RENAME operation would be
- successful.
SEE ALSO
HISTORY
The function VOP_LOOKUP appeared in 4.3BSD.
AUTHORS
- This manual page was written by Doug Rabson, with some text
- from comments in ufs_lookup.c.
- BSD November 24, 1997