4.3BSD-UWisc/src/sys/sys/vfs_lookup.c
/* @(#)vfs_lookup.c 1.1 86/02/03 SMI */
/* NFSSRC @(#)vfs_lookup.c 2.1 86/04/15 */
#include "param.h"
#include "user.h"
#include "uio.h"
#include "vfs.h"
#include "vnode.h"
#include "dir.h"
#include "pathname.h"
/*
* lookup the user file name,
* Handle allocation and freeing of pathname buffer, return error.
*/
lookupname(fnamep, seg, followlink, dirvpp, compvpp)
char *fnamep; /* user pathname */
int seg; /* addr space that name is in */
enum symfollow followlink; /* follow sym links */
struct vnode **dirvpp; /* ret for ptr to parent dir vnode */
struct vnode **compvpp; /* ret for ptr to component vnode */
{
struct pathname lookpn;
register int error;
error = pn_get(fnamep, seg, &lookpn);
if (error)
return (error);
error = lookuppn(&lookpn, followlink, dirvpp, compvpp);
pn_free(&lookpn);
return (error);
}
/*
* Starting at current directory, translate pathname pnp to end.
* Leave pathname of final component in pnp, return the vnode
* for the final component in *compvpp, and return the vnode
* for the parent of the final component in dirvpp.
*
* This is the central routine in pathname translation and handles
* multiple components in pathnames, separating them at /'s. It also
* implements mounted file systems and processes symbolic links.
*/
lookuppn(pnp, followlink, dirvpp, compvpp)
register struct pathname *pnp; /* pathaname to lookup */
enum symfollow followlink; /* (don't) follow sym links */
struct vnode **dirvpp; /* ptr for parent vnode */
struct vnode **compvpp; /* ptr for entry vnode */
{
register struct vnode *vp; /* current directory vp */
register struct vnode *cvp; /* current component vp */
struct vnode *tvp; /* non-reg temp ptr */
register struct vfs *vfsp; /* ptr to vfs for mount indir */
char component[MAXNAMLEN+1]; /* buffer for component */
register int error;
register int nlink;
nlink = 0;
cvp = (struct vnode *)0;
/*
* start at current directory.
*/
vp = u.u_cdir;
VN_HOLD(vp);
begin:
/*
* Each time we begin a new name interpretation (e.g.
* when first called and after each symbolic link is
* substituted), we allow the search to start at the
* root directory if the name starts with a '/', otherwise
* continuing from the current directory.
*/
component[0] = 0;
if (pn_peekchar(pnp) == '/') {
VN_RELE(vp);
pn_skipslash(pnp);
if (u.u_rdir)
vp = u.u_rdir;
else
vp = rootdir;
VN_HOLD(vp);
}
next:
/*
* Make sure we have a directory.
*/
if (vp->v_type != VDIR) {
error = ENOTDIR;
goto bad;
}
/*
* Process the next component of the pathname.
*/
error = pn_getcomponent(pnp, component);
if (error)
goto bad;
/*
* Check for degenerate name (e.g. / or "")
* which is a way of talking about a directory,
* e.g. "/." or ".".
*/
if (component[0] == 0) {
/*
* If the caller was interested in the parent then
* return an error since we don't have the real parent
*/
if (dirvpp != (struct vnode **)0) {
VN_RELE(vp);
return(EINVAL);
}
(void) pn_set(pnp, ".");
if (compvpp != (struct vnode **)0) {
*compvpp = vp;
} else {
VN_RELE(vp);
}
return(0);
}
/*
* Handle "..": two special cases.
* 1. If at root directory (e.g. after chroot)
* then ignore it so can't get out.
* 2. If this vnode is the root of a mounted
* file system, then replace it with the
* vnode which was mounted on so we take the
* .. in the other file system.
*/
if (strcmp(component, "..") == 0) {
checkforroot:
if ((vp == u.u_rdir) || (vp == rootdir)) {
cvp = vp;
VN_HOLD(cvp);
goto skip;
}
if (vp->v_flag & VROOT) {
cvp = vp;
vp = vp->v_vfsp->vfs_vnodecovered;
VN_HOLD(vp);
VN_RELE(cvp);
cvp = (struct vnode *)0;
goto checkforroot;
}
}
/*
* Perform a lookup in the current directory.
*/
error = VOP_LOOKUP(vp, component, &tvp, u.u_cred);
cvp = tvp;
if (error) {
cvp = (struct vnode *)0;
/*
* On error, if more pathname or if caller was not interested
* in the parent directory then hard error.
*/
if (pn_pathleft(pnp) || dirvpp == (struct vnode **)0
|| error == EACCES)
goto bad;
(void) pn_set(pnp, component);
*dirvpp = vp;
if (compvpp != (struct vnode **)0) {
*compvpp = (struct vnode *)0;
}
return (0);
}
/*
* If we hit a symbolic link and there is more path to be
* translated or this operation does not wish to apply
* to a link, then place the contents of the link at the
* front of the remaining pathname.
*/
if (cvp->v_type == VLNK &&
((followlink == FOLLOW_LINK) || pn_pathleft(pnp))) {
struct pathname linkpath;
nlink++;
if (nlink > 20) {
error = ELOOP;
goto bad;
}
error = getsymlink(cvp, &linkpath);
if (error)
goto bad;
if (pn_pathleft(&linkpath) == 0)
(void) pn_set(&linkpath, ".");
error = pn_combine(pnp, &linkpath); /* linkpath before pn */
pn_free(&linkpath);
if (error)
goto bad;
VN_RELE(cvp);
cvp = (struct vnode *)0;
goto begin;
}
/*
* If this vnode is mounted on, then we
* transparently indirect to the vnode which
* is the root of the mounted file system.
* Before we do this we must check that an unmount is not
* in progress on this vnode. This maintains the fs status
* quo while a possibly lengthy unmount is going on.
*/
mloop:
while (vfsp = cvp->v_vfsmountedhere) {
while (vfsp->vfs_flag & VFS_MLOCK) {
vfsp->vfs_flag |= VFS_MWAIT;
sleep((caddr_t)vfsp, PVFS);
goto mloop;
}
error = VFS_ROOT(cvp->v_vfsmountedhere, &tvp);
if (error)
goto bad;
VN_RELE(cvp);
cvp = tvp;
}
skip:
/*
* Skip to next component of the pathname.
* If no more components, return last directory (if wanted) and
* last component (if wanted).
*/
if (pn_pathleft(pnp) == 0) {
(void) pn_set(pnp, component);
if (dirvpp != (struct vnode **)0) {
/*
* check that we have the real parent and not
* an alias of the last component
*/
if (vp == cvp) {
VN_RELE(vp);
VN_RELE(cvp);
return(EINVAL);
}
*dirvpp = vp;
} else {
VN_RELE(vp);
}
if (compvpp != (struct vnode **)0) {
*compvpp = cvp;
} else {
VN_RELE(cvp);
}
return (0);
}
/*
* skip over slashes from end of last component
*/
pn_skipslash(pnp);
/*
* Searched through another level of directory:
* release previous directory handle and save new (result
* of lookup) as current directory.
*/
VN_RELE(vp);
vp = cvp;
cvp = (struct vnode *)0;
goto next;
bad:
/*
* Error. Release vnodes and return.
*/
if (cvp)
VN_RELE(cvp);
VN_RELE(vp);
return (error);
}
/*
* Gets symbolic link into pathname.
*/
static int
getsymlink(vp, pnp)
struct vnode *vp;
struct pathname *pnp;
{
struct iovec aiov;
struct uio auio;
register int error;
pn_alloc(pnp);
aiov.iov_base = pnp->pn_buf;
aiov.iov_len = MAXPATHLEN;
auio.uio_iov = &aiov;
auio.uio_iovcnt = 1;
auio.uio_offset = 0;
auio.uio_segflg = UIO_SYSSPACE;
auio.uio_resid = MAXPATHLEN;
error = VOP_READLINK(vp, &auio, u.u_cred);
if (error)
pn_free(pnp);
pnp->pn_pathlen = MAXPATHLEN - auio.uio_resid;
return (error);
}