mirror of
https://git.hardenedbsd.org/hardenedbsd/HardenedBSD.git
synced 2024-12-30 15:38:06 +01:00
450 lines
11 KiB
Groff
450 lines
11 KiB
Groff
.\" -*- nroff -*-
|
|
.\"
|
|
.\" Copyright (c) 1996 Doug Rabson
|
|
.\"
|
|
.\" All rights reserved.
|
|
.\"
|
|
.\" This program is free software.
|
|
.\"
|
|
.\" 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.
|
|
.\"
|
|
.\" THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``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 DEVELOPERS 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.
|
|
.\"
|
|
.\" $Id: VOP_LOOKUP.9,v 1.3 1997/03/07 03:32:26 mpp Exp $
|
|
.\"
|
|
.Dd July 24, 1996
|
|
.Os FreeBSD
|
|
.Dt VOP_LOOKUP 9
|
|
.Sh NAME
|
|
.Nm VOP_LOOKUP
|
|
.Nd lookup a component of a pathname
|
|
.Sh SYNOPSIS
|
|
.Fd #include <sys/param.h>
|
|
.Fd #include <sys/vnode.h>
|
|
.Fd #include <sys/namei.h>
|
|
.Ft int
|
|
.Fn VOP_LOOKUP "struct vnode *dvp" "struct vnode **vpp" "struct componentname *cnp"
|
|
.Sh DESCRIPTION
|
|
This VFS entry point looks up a single pathname component in a given directory.
|
|
.Pp
|
|
Its arguments are:
|
|
.Bl -tag -width vpp
|
|
.It Ar dvp
|
|
the locked vnode of the directory to search
|
|
.It Ar vpp
|
|
the address of a variable where the resulting locked vnode should be stored
|
|
.It Ar cnp
|
|
the pathname component to be searched for
|
|
.El
|
|
.Pp
|
|
.Fa Cnp
|
|
is a pointer to a componentname structure defined as follows:
|
|
.Bd -literal
|
|
struct componentname {
|
|
/*
|
|
* Arguments to lookup.
|
|
*/
|
|
u_long cn_nameiop; /* namei operation */
|
|
u_long cn_flags; /* flags to namei */
|
|
struct proc *cn_proc; /* process 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 component */
|
|
u_long cn_hash; /* hash value of looked up name */
|
|
long cn_consume; /* chars to consume in lookup() */
|
|
};
|
|
.Ed
|
|
.Pp
|
|
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.
|
|
.Pp
|
|
The
|
|
.Fa cnp->cn_nameiop
|
|
argument is
|
|
.Dv LOOKUP ,
|
|
.Dv CREATE ,
|
|
.Dv RENAME ,
|
|
or
|
|
.Dv DELETE
|
|
depending on the intended use of the object.
|
|
When
|
|
.Dv CREATE ,
|
|
.Dv RENAME ,
|
|
or
|
|
.Dv DELETE
|
|
is specified, information usable in
|
|
creating, renaming, or deleting a directory entry may be calculated.
|
|
.Pp
|
|
Overall outline of VOP_LOOKUP:
|
|
.Bd -filled -offset indent
|
|
Check accessibility of directory.
|
|
Look for name in cache, if found, then return name.
|
|
Search for name in directory, goto to found or notfound as appropriate.
|
|
.Ed
|
|
.Pp
|
|
notfound:
|
|
.Bd -filled -offset indent
|
|
If creating or renaming and at end of pathname,
|
|
return
|
|
.Dv EJUSTRETURN ,
|
|
leaving info on available slots else return
|
|
.Dv ENOENT .
|
|
.Ed
|
|
.Pp
|
|
found:
|
|
.Bd -filled -offset indent
|
|
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.
|
|
.Ed
|
|
.Sh LOCKS
|
|
The directory,
|
|
.Fa dvp
|
|
should be locked on entry.
|
|
If an error (note: the return value
|
|
.Dv EJUSTRETURN
|
|
is not considered an error)
|
|
is detected, it will be returned locked.
|
|
Otherwise, it will be unlocked unless both
|
|
.Dv LOCKPARENT
|
|
and
|
|
.Dv ISLASTCN
|
|
are specified in
|
|
.Fa cnp->cn_flags .
|
|
If an entry is found in the directory, it will be returned locked.
|
|
.Sh RETURN VALUES
|
|
Zero is returned with
|
|
.Fa *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
|
|
.Xr vref 9 .
|
|
The caller must take care to release the locks appropriately in this
|
|
case.
|
|
.Pp
|
|
If the component is not found and the operation is
|
|
.Dv CREATE
|
|
or
|
|
.Dv RENAME ,
|
|
the flag
|
|
.Dv ISLASTCN
|
|
is specified and the operation would succeed, the special return value
|
|
.Dv EJUSTRETURN
|
|
is returned.
|
|
Otherwise, an appropriate error code is returned.
|
|
.Sh PSEUDOCODE
|
|
.Bd -literal
|
|
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_proc);
|
|
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
|
|
* 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_proc);
|
|
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's a
|
|
* regular file, or empty directory.
|
|
*/
|
|
if (nameiop == RENAME && wantparent && islastcn) {
|
|
error = VOP_ACCESS(dvp, VWRITE, cred, cnp->cn_proc);
|
|
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);
|
|
}
|
|
}
|
|
.Ed
|
|
.Sh ERRORS
|
|
.Bl -tag -width Er
|
|
.It Bq Er ENOTDIR
|
|
The vnode
|
|
.Fa dvp
|
|
does not represent a directory.
|
|
.It Bq Er ENOENT
|
|
The component
|
|
.Fa dvp
|
|
was not found in this directory.
|
|
.It Bq Er EACCESS
|
|
access for the specified operation is denied.
|
|
.It Bq Er EJUSTRETURN
|
|
a
|
|
.Dv CREATE
|
|
or
|
|
.Dv RENAME
|
|
operation would be successful
|
|
.El
|
|
.Sh SEE ALSO
|
|
.Xr VOP_ACCESS 9 ,
|
|
.Xr VOP_CREATE 9 ,
|
|
.Xr VOP_MKNOD 9 ,
|
|
.Xr VOP_MKDIR 9 ,
|
|
.Xr VOP_SYMLINK 9 ,
|
|
.Xr VOP_RENAME 9 ,
|
|
.Xr VOP_ABORTOP 9
|
|
.Sh HISTORY
|
|
The function
|
|
.Nm
|
|
appeared in
|
|
.Bx 4.3 .
|
|
.Sh SEE ALSO
|
|
.Xr vnode 9
|
|
.Sh AUTHORS
|
|
This man page was written by Doug Rabson, with some text from
|
|
comments in ufs_lookup.c.
|