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