#include <sys/param.h>
#include <sys/syslimits.h>
#include <sys/time.h>
#include <sys/namei.h>
#include <sys/vm.h>
#include <sys/vnode.h>
#include <sys/mount.h>
#include <sys/errno.h>
#include <sys/malloc.h>
#include <sys/filedesc.h>
#include <sys/proc.h>
#include <sys/kdebug.h>
#include <sys/unistd.h>
#if KTRACE
#include <sys/ktrace.h>
#endif
int
namei(ndp)
register struct nameidata *ndp;
{
register struct filedesc *fdp;
register char *cp;
register struct vnode *dp;
struct iovec aiov;
struct uio auio;
int error, linklen;
struct componentname *cnp = &ndp->ni_cnd;
struct proc *p = cnp->cn_proc;
ndp->ni_cnd.cn_cred = ndp->ni_cnd.cn_proc->p_ucred;
#if DIAGNOSTIC
if (!cnp->cn_cred || !cnp->cn_proc)
panic ("namei: bad cred/proc");
if (cnp->cn_nameiop & (~OPMASK))
panic ("namei: nameiop contaminated with flags");
if (cnp->cn_flags & OPMASK)
panic ("namei: flags contaminated with nameiops");
#endif
fdp = cnp->cn_proc->p_fd;
if ((cnp->cn_flags & HASBUF) == 0) {
MALLOC_ZONE(cnp->cn_pnbuf, caddr_t,
MAXPATHLEN, M_NAMEI, M_WAITOK);
cnp->cn_pnlen = MAXPATHLEN;
}
if (ndp->ni_segflg == UIO_SYSSPACE)
error = copystr(ndp->ni_dirp, cnp->cn_pnbuf,
MAXPATHLEN, &ndp->ni_pathlen);
else
error = copyinstr(ndp->ni_dirp, cnp->cn_pnbuf,
MAXPATHLEN, &ndp->ni_pathlen);
if (!error && *cnp->cn_pnbuf == '\0')
error = ENOENT;
if (error) {
_FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
ndp->ni_vp = NULL;
return (error);
}
ndp->ni_loopcnt = 0;
#if KTRACE
if (KTRPOINT(cnp->cn_proc, KTR_NAMEI))
ktrnamei(cnp->cn_proc->p_tracep, cnp->cn_pnbuf);
#endif
if ((ndp->ni_rootdir = fdp->fd_rdir) == NULL)
ndp->ni_rootdir = rootvnode;
dp = fdp->fd_cdir;
VREF(dp);
for (;;) {
cnp->cn_nameptr = cnp->cn_pnbuf;
if (*(cnp->cn_nameptr) == '/') {
vrele(dp);
while (*(cnp->cn_nameptr) == '/') {
cnp->cn_nameptr++;
ndp->ni_pathlen--;
}
dp = ndp->ni_rootdir;
VREF(dp);
}
ndp->ni_startdir = dp;
if (error = lookup(ndp)) {
FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
return (error);
}
if ((cnp->cn_flags & ISSYMLINK) == 0) {
if ((cnp->cn_flags & (SAVENAME | SAVESTART)) == 0) {
FREE_ZONE(cnp->cn_pnbuf,
cnp->cn_pnlen, M_NAMEI);
} else {
cnp->cn_flags |= HASBUF;
}
return (0);
}
if ((cnp->cn_flags & LOCKPARENT) && ndp->ni_pathlen == 1)
VOP_UNLOCK(ndp->ni_dvp, 0, p);
if (ndp->ni_loopcnt++ >= MAXSYMLINKS) {
error = ELOOP;
break;
}
if (ndp->ni_pathlen > 1) {
MALLOC_ZONE(cp, char *, MAXPATHLEN, M_NAMEI, M_WAITOK);
} else {
cp = cnp->cn_pnbuf;
}
aiov.iov_base = cp;
aiov.iov_len = MAXPATHLEN;
auio.uio_iov = &aiov;
auio.uio_iovcnt = 1;
auio.uio_offset = 0;
auio.uio_rw = UIO_READ;
auio.uio_segflg = UIO_SYSSPACE;
auio.uio_procp = (struct proc *)0;
auio.uio_resid = MAXPATHLEN;
if (error = VOP_READLINK(ndp->ni_vp, &auio, cnp->cn_cred)) {
if (ndp->ni_pathlen > 1)
_FREE_ZONE(cp, MAXPATHLEN, M_NAMEI);
break;
}
linklen = MAXPATHLEN - auio.uio_resid;
if (linklen + ndp->ni_pathlen >= MAXPATHLEN) {
if (ndp->ni_pathlen > 1)
_FREE_ZONE(cp, MAXPATHLEN, M_NAMEI);
error = ENAMETOOLONG;
break;
}
if (ndp->ni_pathlen > 1) {
bcopy(ndp->ni_next, cp + linklen, ndp->ni_pathlen);
FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
cnp->cn_pnbuf = cp;
cnp->cn_pnlen = MAXPATHLEN;
} else
cnp->cn_pnbuf[linklen] = '\0';
ndp->ni_pathlen += linklen;
vput(ndp->ni_vp);
dp = ndp->ni_dvp;
}
FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
vrele(ndp->ni_dvp);
vput(ndp->ni_vp);
ndp->ni_vp = NULL;
return (error);
}
int
lookup(ndp)
register struct nameidata *ndp;
{
register char *cp;
register struct vnode *dp = 0;
struct vnode *tdp;
struct mount *mp;
int namemax = 0;
int docache;
int wantparent;
int rdonly;
int error = 0;
struct componentname *cnp = &ndp->ni_cnd;
struct proc *p = cnp->cn_proc;
int i;
wantparent = cnp->cn_flags & (LOCKPARENT | WANTPARENT);
docache = (cnp->cn_flags & NOCACHE) ^ NOCACHE;
if (cnp->cn_nameiop == DELETE ||
(wantparent && cnp->cn_nameiop != CREATE &&
cnp->cn_nameiop != LOOKUP))
docache = 0;
rdonly = cnp->cn_flags & RDONLY;
ndp->ni_dvp = NULL;
cnp->cn_flags &= ~ISSYMLINK;
dp = ndp->ni_startdir;
ndp->ni_startdir = NULLVP;
vn_lock(dp, LK_EXCLUSIVE | LK_RETRY, p);
dirloop:
cnp->cn_consume = 0;
cnp->cn_hash = 0;
for (cp = cnp->cn_nameptr, i=1; *cp != 0 && *cp != '/'; i++, cp++)
cnp->cn_hash += (unsigned char)*cp * i;
cnp->cn_namelen = cp - cnp->cn_nameptr;
if (VOP_PATHCONF(dp, _PC_NAME_MAX, &namemax))
namemax = NAME_MAX;
if (cnp->cn_namelen > namemax) {
error = ENAMETOOLONG;
goto bad;
}
#ifdef NAMEI_DIAGNOSTIC
{ char c = *cp;
*cp = '\0';
printf("{%s}: ", cnp->cn_nameptr);
*cp = c; }
#endif
ndp->ni_pathlen -= cnp->cn_namelen;
ndp->ni_next = cp;
cnp->cn_flags |= MAKEENTRY;
if (*cp == '\0' && docache == 0)
cnp->cn_flags &= ~MAKEENTRY;
if (cnp->cn_namelen == 2 &&
cnp->cn_nameptr[1] == '.' && cnp->cn_nameptr[0] == '.')
cnp->cn_flags |= ISDOTDOT;
else
cnp->cn_flags &= ~ISDOTDOT;
if (*ndp->ni_next == 0)
cnp->cn_flags |= ISLASTCN;
else
cnp->cn_flags &= ~ISLASTCN;
if (cnp->cn_nameptr[0] == '\0') {
if (dp->v_type != VDIR) {
error = ENOTDIR;
goto bad;
}
if (cnp->cn_nameiop != LOOKUP) {
error = EISDIR;
goto bad;
}
if (wantparent) {
ndp->ni_dvp = dp;
VREF(dp);
}
ndp->ni_vp = dp;
if (!(cnp->cn_flags & (LOCKPARENT | LOCKLEAF)))
VOP_UNLOCK(dp, 0, p);
if (cnp->cn_flags & SAVESTART)
panic("lookup: SAVESTART");
return (0);
}
if (cnp->cn_flags & ISDOTDOT) {
for (;;) {
if (dp == ndp->ni_rootdir || dp == rootvnode) {
ndp->ni_dvp = dp;
ndp->ni_vp = dp;
VREF(dp);
goto nextname;
}
if ((dp->v_flag & VROOT) == 0 ||
(cnp->cn_flags & NOCROSSMOUNT))
break;
if (dp->v_mount == NULL) {
error = EBADF;
goto bad;
}
tdp = dp;
dp = dp->v_mount->mnt_vnodecovered;
vput(tdp);
VREF(dp);
vn_lock(dp, LK_EXCLUSIVE | LK_RETRY, p);
}
}
unionlookup:
ndp->ni_dvp = dp;
ndp->ni_vp = NULL;
if (error = VOP_LOOKUP(dp, &ndp->ni_vp, cnp)) {
#if DIAGNOSTIC
if (ndp->ni_vp != NULL)
panic("leaf should be empty");
#endif
#ifdef NAMEI_DIAGNOSTIC
printf("not found\n");
#endif
if ((error == ENOENT) &&
(dp->v_flag & VROOT) && (dp->v_mount != NULL) &&
(dp->v_mount->mnt_flag & MNT_UNION)) {
tdp = dp;
dp = dp->v_mount->mnt_vnodecovered;
vput(tdp);
VREF(dp);
vn_lock(dp, LK_EXCLUSIVE | LK_RETRY, p);
goto unionlookup;
}
if (error != EJUSTRETURN)
goto bad;
if (rdonly) {
error = EROFS;
goto bad;
}
if (cnp->cn_flags & SAVESTART) {
ndp->ni_startdir = ndp->ni_dvp;
VREF(ndp->ni_startdir);
}
if (kdebug_enable)
kdebug_lookup(ndp->ni_dvp, cnp);
return (0);
}
#ifdef NAMEI_DIAGNOSTIC
printf("found\n");
#endif
if (cnp->cn_consume > 0) {
cnp->cn_nameptr += cnp->cn_consume;
ndp->ni_next += cnp->cn_consume;
ndp->ni_pathlen -= cnp->cn_consume;
cnp->cn_consume = 0;
}
dp = ndp->ni_vp;
while (dp->v_type == VDIR && (mp = dp->v_mountedhere) &&
(cnp->cn_flags & NOCROSSMOUNT) == 0) {
if (vfs_busy(mp, 0, 0, p))
continue;
error = VFS_ROOT(mp, &tdp);
vfs_unbusy(mp, p);
if (error)
goto bad2;
vput(dp);
ndp->ni_vp = dp = tdp;
}
if ((dp->v_type == VLNK) &&
((cnp->cn_flags & FOLLOW) || *ndp->ni_next == '/')) {
cnp->cn_flags |= ISSYMLINK;
return (0);
}
nextname:
if (*ndp->ni_next == '/') {
cnp->cn_nameptr = ndp->ni_next;
while (*cnp->cn_nameptr == '/') {
cnp->cn_nameptr++;
ndp->ni_pathlen--;
}
vrele(ndp->ni_dvp);
goto dirloop;
}
if (rdonly &&
(cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) {
error = EROFS;
goto bad2;
}
if (cnp->cn_flags & SAVESTART) {
ndp->ni_startdir = ndp->ni_dvp;
VREF(ndp->ni_startdir);
}
if (!wantparent)
vrele(ndp->ni_dvp);
if ((cnp->cn_flags & LOCKLEAF) == 0)
VOP_UNLOCK(dp, 0, p);
if (kdebug_enable)
kdebug_lookup(dp, cnp);
return (0);
bad2:
if ((cnp->cn_flags & LOCKPARENT) && *ndp->ni_next == '\0')
VOP_UNLOCK(ndp->ni_dvp, 0, p);
vrele(ndp->ni_dvp);
bad:
vput(dp);
ndp->ni_vp = NULL;
if (kdebug_enable)
kdebug_lookup(dp, cnp);
return (error);
}
int
relookup(dvp, vpp, cnp)
struct vnode *dvp, **vpp;
struct componentname *cnp;
{
struct proc *p = cnp->cn_proc;
struct vnode *dp = 0;
int docache;
int wantparent;
int rdonly;
int error = 0;
#ifdef NAMEI_DIAGNOSTIC
int newhash;
char *cp;
#endif
wantparent = cnp->cn_flags & (LOCKPARENT|WANTPARENT);
docache = (cnp->cn_flags & NOCACHE) ^ NOCACHE;
if (cnp->cn_nameiop == DELETE ||
(wantparent && cnp->cn_nameiop != CREATE))
docache = 0;
rdonly = cnp->cn_flags & RDONLY;
cnp->cn_flags &= ~ISSYMLINK;
dp = dvp;
vn_lock(dp, LK_EXCLUSIVE | LK_RETRY, p);
#ifdef NAMEI_DIAGNOSTIC
for (newhash = 0, cp = cnp->cn_nameptr; *cp != 0 && *cp != '/'; cp++)
newhash += (unsigned char)*cp;
if (newhash != cnp->cn_hash)
panic("relookup: bad hash");
if (cnp->cn_namelen != cp - cnp->cn_nameptr)
panic ("relookup: bad len");
if (*cp != 0)
panic("relookup: not last component");
printf("{%s}: ", cnp->cn_nameptr);
#endif
if (cnp->cn_nameptr[0] == '\0') {
if (cnp->cn_nameiop != LOOKUP || wantparent) {
error = EISDIR;
goto bad;
}
if (dp->v_type != VDIR) {
error = ENOTDIR;
goto bad;
}
if (!(cnp->cn_flags & LOCKLEAF))
VOP_UNLOCK(dp, 0, p);
*vpp = dp;
if (cnp->cn_flags & SAVESTART)
panic("lookup: SAVESTART");
return (0);
}
if (cnp->cn_flags & ISDOTDOT)
panic ("relookup: lookup on dot-dot");
if (error = VOP_LOOKUP(dp, vpp, cnp)) {
#if DIAGNOSTIC
if (*vpp != NULL)
panic("leaf should be empty");
#endif
if (error != EJUSTRETURN)
goto bad;
if (rdonly) {
error = EROFS;
goto bad;
}
if (cnp->cn_flags & SAVESTART)
VREF(dvp);
return (0);
}
dp = *vpp;
#if DIAGNOSTIC
if (dp->v_type == VLNK && (cnp->cn_flags & FOLLOW))
panic ("relookup: symlink found.\n");
#endif
if (rdonly &&
(cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)) {
error = EROFS;
goto bad2;
}
if (cnp->cn_flags & SAVESTART)
VREF(dvp);
if (!wantparent)
vrele(dvp);
if ((cnp->cn_flags & LOCKLEAF) == 0)
VOP_UNLOCK(dp, 0, p);
return (0);
bad2:
if ((cnp->cn_flags & LOCKPARENT) && (cnp->cn_flags & ISLASTCN))
VOP_UNLOCK(dvp, 0, p);
vrele(dvp);
bad:
vput(dp);
*vpp = NULL;
return (error);
}
#define NUMPARMS 23
kdebug_lookup(dp, cnp)
struct vnode *dp;
struct componentname *cnp;
{
register int i, n;
register int dbg_namelen;
register int save_dbg_namelen;
register char *dbg_nameptr;
long dbg_parms[NUMPARMS];
char dbg_buf[4];
static char *dbg_filler = ">>>>";
dbg_namelen = (cnp->cn_nameptr - cnp->cn_pnbuf) + cnp->cn_namelen;
dbg_nameptr = cnp->cn_nameptr + cnp->cn_namelen;
if (dbg_namelen > sizeof(dbg_parms))
dbg_namelen = sizeof(dbg_parms);
dbg_nameptr -= dbg_namelen;
save_dbg_namelen = dbg_namelen;
i = 0;
while (dbg_namelen > 0) {
if (dbg_namelen >= 4) {
dbg_parms[i++] = *(long *)dbg_nameptr;
dbg_nameptr += sizeof(long);
dbg_namelen -= sizeof(long);
} else {
for (n = 0; n < dbg_namelen; n++)
dbg_buf[n] = *dbg_nameptr++;
while (n <= 3) {
if (*dbg_nameptr)
dbg_buf[n++] = '>';
else
dbg_buf[n++] = 0;
}
dbg_parms[i++] = *(long *)&dbg_buf[0];
break;
}
}
while (i < NUMPARMS) {
if (*dbg_nameptr)
dbg_parms[i++] = *(long *)dbg_filler;
else
dbg_parms[i++] = 0;
}
KERNEL_DEBUG_CONSTANT((FSDBG_CODE(DBG_FSRW,36)) | DBG_FUNC_NONE,
dp, dbg_parms[0], dbg_parms[1], dbg_parms[2], 0);
for (dbg_namelen = save_dbg_namelen-12, i=3;
dbg_namelen > 0;
dbg_namelen -=(4 * sizeof(long)))
{
KERNEL_DEBUG_CONSTANT((FSDBG_CODE(DBG_FSRW,36)) | DBG_FUNC_NONE,
dbg_parms[i++], dbg_parms[i++], dbg_parms[i++], dbg_parms[i++], 0);
}
}