/*
- * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
*/
#include <sys/param.h>
+#include <sys/systm.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/vnode_internal.h>
+#include <sys/mount_internal.h>
#include <sys/errno.h>
#include <sys/malloc.h>
#include <sys/filedesc.h>
-#include <sys/proc.h>
+#include <sys/proc_internal.h>
#include <sys/kdebug.h>
#include <sys/unistd.h> /* For _PC_NAME_MAX */
+#include <sys/uio_internal.h>
+#include <sys/kauth.h>
+
+#include <bsm/audit_kernel.h>
#if KTRACE
#include <sys/ktrace.h>
#endif
+
+static void kdebug_lookup(struct vnode *dp, struct componentname *cnp);
+
/*
* Convert a pathname into a pointer to a locked inode.
*
* if symbolic link, massage name in buffer and continue
* }
*/
+
int
namei(ndp)
register struct nameidata *ndp;
register struct filedesc *fdp; /* pointer to file descriptor state */
register char *cp; /* pointer into pathname argument */
register struct vnode *dp; /* the directory we are searching */
- struct iovec aiov; /* uio for reading symbolic links */
- struct uio auio;
- int error, linklen;
+ uio_t auio;
+ int error;
struct componentname *cnp = &ndp->ni_cnd;
- struct proc *p = cnp->cn_proc;
+ vfs_context_t ctx = cnp->cn_context;
+ struct proc *p = vfs_context_proc(ctx);
+ char *tmppn;
+ char uio_buf[ UIO_SIZEOF(1) ];
- ndp->ni_cnd.cn_cred = ndp->ni_cnd.cn_proc->p_ucred;
#if DIAGNOSTIC
- if (!cnp->cn_cred || !cnp->cn_proc)
+ if (!vfs_context_ucred(ctx) || !p)
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;
+ fdp = p->p_fd;
/*
* Get a buffer for the name to be translated, and copy the
* name into the buffer.
*/
if ((cnp->cn_flags & HASBUF) == 0) {
- MALLOC_ZONE(cnp->cn_pnbuf, caddr_t,
- MAXPATHLEN, M_NAMEI, M_WAITOK);
- cnp->cn_pnlen = MAXPATHLEN;
+ cnp->cn_pnbuf = &ndp->ni_pathbuf;
+ cnp->cn_pnlen = PATHBUFLEN;
}
- if (ndp->ni_segflg == UIO_SYSSPACE)
- error = copystr(ndp->ni_dirp, cnp->cn_pnbuf,
- MAXPATHLEN, &ndp->ni_pathlen);
- else
+#if LP64_DEBUG
+ if (IS_VALID_UIO_SEGFLG(ndp->ni_segflg) == 0) {
+ panic("%s :%d - invalid ni_segflg\n", __FILE__, __LINE__);
+ }
+#endif /* LP64_DEBUG */
+
+retry_copy:
+ if (UIO_SEG_IS_USER_SPACE(ndp->ni_segflg))
error = copyinstr(ndp->ni_dirp, cnp->cn_pnbuf,
- MAXPATHLEN, &ndp->ni_pathlen);
+ cnp->cn_pnlen, (size_t *)&ndp->ni_pathlen);
+ else
+ error = copystr(CAST_DOWN(void *, ndp->ni_dirp), cnp->cn_pnbuf,
+ cnp->cn_pnlen, (size_t *)&ndp->ni_pathlen);
+
+ if (error == ENAMETOOLONG && !(cnp->cn_flags & HASBUF)) {
+ MALLOC_ZONE(cnp->cn_pnbuf, caddr_t,
+ MAXPATHLEN, M_NAMEI, M_WAITOK);
+
+ cnp->cn_flags |= HASBUF;
+ cnp->cn_pnlen = MAXPATHLEN;
+
+ goto retry_copy;
+ }
+ if (error)
+ goto error_out;
+
+ /* If we are auditing the kernel pathname, save the user pathname */
+ if (cnp->cn_flags & AUDITVNPATH1)
+ AUDIT_ARG(upath, p, cnp->cn_pnbuf, ARG_UPATH1);
+ if (cnp->cn_flags & AUDITVNPATH2)
+ AUDIT_ARG(upath, p, cnp->cn_pnbuf, ARG_UPATH2);
+
/*
* Do not allow empty pathnames
*/
- if (!error && *cnp->cn_pnbuf == '\0')
+ if (*cnp->cn_pnbuf == '\0') {
error = ENOENT;
-
- if (error) {
- _FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
- ndp->ni_vp = NULL;
- return (error);
+ goto error_out;
}
ndp->ni_loopcnt = 0;
#if KTRACE
- if (KTRPOINT(cnp->cn_proc, KTR_NAMEI))
- ktrnamei(cnp->cn_proc->p_tracep, cnp->cn_pnbuf);
+ if (KTRPOINT(p, KTR_NAMEI))
+ ktrnamei(p->p_tracep, cnp->cn_pnbuf);
#endif
/*
- * Get starting point for the translation.
+ * determine the starting point for the translation.
*/
- if ((ndp->ni_rootdir = fdp->fd_rdir) == NULL)
- ndp->ni_rootdir = rootvnode;
- dp = fdp->fd_cdir;
- VREF(dp);
- for (;;) {
- /*
- * Check if root directory should replace current directory.
- * Done at start of translation and after symbolic link.
- */
- 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);
+ if ((ndp->ni_rootdir = fdp->fd_rdir) == NULLVP) {
+ if ( !(fdp->fd_flags & FD_CHROOT))
+ ndp->ni_rootdir = rootvnode;
+ }
+ cnp->cn_nameptr = cnp->cn_pnbuf;
+
+ ndp->ni_usedvp = NULLVP;
+
+ if (*(cnp->cn_nameptr) == '/') {
+ while (*(cnp->cn_nameptr) == '/') {
+ cnp->cn_nameptr++;
+ ndp->ni_pathlen--;
}
+ dp = ndp->ni_rootdir;
+ } else if (cnp->cn_flags & USEDVP) {
+ dp = ndp->ni_dvp;
+ ndp->ni_usedvp = dp;
+ } else
+ dp = fdp->fd_cdir;
+
+ if (dp == NULLVP) {
+ error = ENOENT;
+ goto error_out;
+ }
+ ndp->ni_dvp = NULLVP;
+ ndp->ni_vp = NULLVP;
+
+ for (;;) {
+ int need_newpathbuf;
+ int linklen;
+
ndp->ni_startdir = dp;
- if (error = lookup(ndp)) {
- FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
- return (error);
+
+ if ( (error = lookup(ndp)) ) {
+ goto error_out;
}
/*
* Check for symbolic link
*/
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 ((cnp->cn_flags & FSNODELOCKHELD)) {
+ cnp->cn_flags &= ~FSNODELOCKHELD;
+ unlock_fsnode(ndp->ni_dvp, NULL);
+ }
if (ndp->ni_loopcnt++ >= MAXSYMLINKS) {
error = ELOOP;
break;
}
- if (ndp->ni_pathlen > 1) {
+ if (ndp->ni_pathlen > 1 || !(cnp->cn_flags & HASBUF))
+ need_newpathbuf = 1;
+ else
+ need_newpathbuf = 0;
+
+ if (need_newpathbuf) {
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);
+ auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ, &uio_buf[0], sizeof(uio_buf));
+
+ uio_addiov(auio, CAST_USER_ADDR_T(cp), MAXPATHLEN);
+
+ error = VNOP_READLINK(ndp->ni_vp, auio, ctx);
+ if (error) {
+ if (need_newpathbuf)
+ 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);
+ // LP64todo - fix this
+ linklen = MAXPATHLEN - uio_resid(auio);
+ if (linklen + ndp->ni_pathlen > MAXPATHLEN) {
+ if (need_newpathbuf)
+ FREE_ZONE(cp, MAXPATHLEN, M_NAMEI);
+
error = ENAMETOOLONG;
break;
}
- if (ndp->ni_pathlen > 1) {
+ if (need_newpathbuf) {
+ long len = cnp->cn_pnlen;
+
+ tmppn = cnp->cn_pnbuf;
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;
+
+ if ( (cnp->cn_flags & HASBUF) )
+ FREE_ZONE(tmppn, len, M_NAMEI);
+ else
+ cnp->cn_flags |= HASBUF;
} else
cnp->cn_pnbuf[linklen] = '\0';
+
ndp->ni_pathlen += linklen;
- vput(ndp->ni_vp);
+ cnp->cn_nameptr = cnp->cn_pnbuf;
+
+ /*
+ * starting point for 'relative'
+ * symbolic link path
+ */
dp = ndp->ni_dvp;
+ /*
+ * get rid of references returned via 'lookup'
+ */
+ vnode_put(ndp->ni_vp);
+ vnode_put(ndp->ni_dvp);
+
+ ndp->ni_vp = NULLVP;
+ ndp->ni_dvp = NULLVP;
+
+ /*
+ * Check if symbolic link restarts us at the root
+ */
+ if (*(cnp->cn_nameptr) == '/') {
+ while (*(cnp->cn_nameptr) == '/') {
+ cnp->cn_nameptr++;
+ ndp->ni_pathlen--;
+ }
+ if ((dp = ndp->ni_rootdir) == NULLVP) {
+ error = ENOENT;
+ goto error_out;
+ }
+ }
}
- FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
- vrele(ndp->ni_dvp);
- vput(ndp->ni_vp);
- ndp->ni_vp = NULL;
+ /*
+ * only come here if we fail to handle a SYMLINK...
+ * if either ni_dvp or ni_vp is non-NULL, then
+ * we need to drop the iocount that was picked
+ * up in the lookup routine
+ */
+ if (ndp->ni_dvp)
+ vnode_put(ndp->ni_dvp);
+ if (ndp->ni_vp)
+ vnode_put(ndp->ni_vp);
+ error_out:
+ if ( (cnp->cn_flags & HASBUF) ) {
+ cnp->cn_flags &= ~HASBUF;
+ FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
+ }
+ cnp->cn_pnbuf = NULL;
+ ndp->ni_vp = NULLVP;
+
return (error);
}
+
/*
* Search a pathname.
* This is a very central and rather complicated routine.
* identify next component of name at ndp->ni_ptr
* handle degenerate case where name is null string
* if .. and crossing mount points and on mounted filesys, find parent
- * call VOP_LOOKUP routine for next component name
+ * call VNOP_LOOKUP routine for next component name
* directory vnode returned in ni_dvp, unlocked unless LOCKPARENT set
* component vnode returned in ni_vp (if it exists), locked.
* if result vnode is mounted on and crossing mount points,
lookup(ndp)
register struct nameidata *ndp;
{
- register char *cp; /* pointer into pathname argument */
- register struct vnode *dp = 0; /* the directory we are searching */
- struct vnode *tdp; /* saved dp */
- struct mount *mp; /* mount table entry */
- int namemax = 0; /* maximun number of bytes for filename returned by pathconf() */
- int docache; /* == 0 do not cache last component */
+ register char *cp; /* pointer into pathname argument */
+ vnode_t tdp; /* saved dp */
+ vnode_t dp; /* the directory we are searching */
+ mount_t mp; /* mount table entry */
+ int docache = 1; /* == 0 do not cache last component */
int wantparent; /* 1 => wantparent or lockparent flag */
int rdonly; /* lookup read-only flag bit */
+ int trailing_slash = 0;
+ int dp_authorized = 0;
int error = 0;
struct componentname *cnp = &ndp->ni_cnd;
- struct proc *p = cnp->cn_proc;
- int i;
+ vfs_context_t ctx = cnp->cn_context;
/*
* Setup: break out flag bits into variables.
*/
+ if (cnp->cn_flags & (NOCACHE | DOWHITEOUT)) {
+ if ((cnp->cn_flags & NOCACHE) || (cnp->cn_nameiop == DELETE))
+ docache = 0;
+ }
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;
+ cnp->cn_consume = 0;
+
dp = ndp->ni_startdir;
ndp->ni_startdir = NULLVP;
- vn_lock(dp, LK_EXCLUSIVE | LK_RETRY, p);
-
-dirloop:
- /*
- * Search a new directory.
- *
- * The cn_hash value is for use by vfs_cache.
- * Check pathconf for maximun length of name
- * The last component of the filename is left accessible via
- * cnp->cn_nameptr for callers that need the name. Callers needing
- * the name set the SAVENAME flag. When done, they assume
- * responsibility for freeing the pathname buffer.
- */
- 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;
+ cp = cnp->cn_nameptr;
- /*
- * Check for degenerate name (e.g. / or "")
- * which is a way of talking about a directory,
- * e.g. like "/." or ".".
- */
- if (cnp->cn_nameptr[0] == '\0') {
- if (dp->v_type != VDIR) {
- error = ENOTDIR;
- goto bad;
- }
- if (cnp->cn_nameiop != LOOKUP) {
- error = EISDIR;
+ if (*cp == '\0') {
+ if ( (vnode_getwithref(dp)) ) {
+ dp = NULLVP;
+ error = ENOENT;
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);
+ goto emptyname;
+ }
+dirloop:
+ ndp->ni_vp = NULLVP;
+
+ if ( (error = cache_lookup_path(ndp, cnp, dp, ctx, &trailing_slash, &dp_authorized)) ) {
+ dp = NULLVP;
+ goto bad;
+ }
+ if ((cnp->cn_flags & ISLASTCN)) {
+ if (docache)
+ cnp->cn_flags |= MAKEENTRY;
+ } else
+ cnp->cn_flags |= MAKEENTRY;
+
+ dp = ndp->ni_dvp;
+
+ if (ndp->ni_vp != NULLVP) {
+ /*
+ * cache_lookup_path returned a non-NULL ni_vp then,
+ * we're guaranteed that the dp is a VDIR, it's
+ * been authorized, and vp is not ".."
+ */
+ goto returned_from_lookup_path;
}
/*
* vnode which was mounted on so we take the
* .. in the other file system.
*/
- if (cnp->cn_flags & ISDOTDOT) {
+ if ( (cnp->cn_flags & ISDOTDOT) ) {
for (;;) {
- if (dp == ndp->ni_rootdir || dp == rootvnode) {
- ndp->ni_dvp = dp;
+ if (dp == ndp->ni_rootdir || dp == rootvnode) {
+ ndp->ni_dvp = dp;
ndp->ni_vp = dp;
- VREF(dp);
+ /*
+ * we're pinned at the root
+ * we've already got one reference on 'dp'
+ * courtesy of cache_lookup_path... take
+ * another one for the ".."
+ * if we fail to get the new reference, we'll
+ * drop our original down in 'bad'
+ */
+ if ( (vnode_get(dp)) ) {
+ error = ENOENT;
+ goto bad;
+ }
goto nextname;
}
if ((dp->v_flag & VROOT) == 0 ||
(cnp->cn_flags & NOCROSSMOUNT))
- break;
+ break;
if (dp->v_mount == NULL) { /* forced umount */
- error = EBADF;
+ error = EBADF;
goto bad;
}
-
tdp = dp;
- dp = dp->v_mount->mnt_vnodecovered;
- vput(tdp);
- VREF(dp);
- vn_lock(dp, LK_EXCLUSIVE | LK_RETRY, p);
+ dp = tdp->v_mount->mnt_vnodecovered;
+
+ vnode_put(tdp);
+
+ if ( (vnode_getwithref(dp)) ) {
+ dp = NULLVP;
+ error = ENOENT;
+ goto bad;
+ }
+ ndp->ni_dvp = dp;
+ dp_authorized = 0;
}
}
* We now have a segment name to search for, and a directory to search.
*/
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
+ ndp->ni_vp = NULLVP;
+
+ if (dp->v_type != VDIR) {
+ error = ENOTDIR;
+ goto lookup_error;
+ }
+ if ( !(dp_authorized || (cnp->cn_flags & DONOTAUTH)) ) {
+ if ( (error = vnode_authorize(dp, NULL, KAUTH_VNODE_SEARCH, ctx)) )
+ goto lookup_error;
+ }
+ if ( (error = VNOP_LOOKUP(dp, &ndp->ni_vp, cnp, ctx)) ) {
+lookup_error:
if ((error == ENOENT) &&
(dp->v_flag & VROOT) && (dp->v_mount != NULL) &&
(dp->v_mount->mnt_flag & MNT_UNION)) {
+ if ((cnp->cn_flags & FSNODELOCKHELD)) {
+ cnp->cn_flags &= ~FSNODELOCKHELD;
+ unlock_fsnode(dp, NULL);
+ }
tdp = dp;
- dp = dp->v_mount->mnt_vnodecovered;
- vput(tdp);
- VREF(dp);
- vn_lock(dp, LK_EXCLUSIVE | LK_RETRY, p);
+ dp = tdp->v_mount->mnt_vnodecovered;
+
+ vnode_put(tdp);
+
+ if ( (vnode_getwithref(dp)) ) {
+ dp = NULLVP;
+ error = ENOENT;
+ goto bad;
+ }
+ ndp->ni_dvp = dp;
+ dp_authorized = 0;
goto unionlookup;
}
if (error != EJUSTRETURN)
goto bad;
+
+ if (ndp->ni_vp != NULLVP)
+ panic("leaf should be empty");
+
/*
* If creating and at end of pathname, then can consider
* allowing file to be created.
error = EROFS;
goto bad;
}
+ if ((cnp->cn_flags & ISLASTCN) && trailing_slash && !(cnp->cn_flags & WILLBEDIR)) {
+ error = ENOENT;
+ goto bad;
+ }
/*
* We return with ni_vp NULL to indicate that the entry
* doesn't currently exist, leaving a pointer to the
- * (possibly locked) directory inode in ndp->ni_dvp.
+ * referenced directory vnode in ndp->ni_dvp.
*/
if (cnp->cn_flags & SAVESTART) {
+ if ( (vnode_get(ndp->ni_dvp)) ) {
+ error = ENOENT;
+ goto bad;
+ }
ndp->ni_startdir = ndp->ni_dvp;
- VREF(ndp->ni_startdir);
}
+ if (!wantparent)
+ vnode_put(ndp->ni_dvp);
+
if (kdebug_enable)
kdebug_lookup(ndp->ni_dvp, cnp);
return (0);
}
-#ifdef NAMEI_DIAGNOSTIC
- printf("found\n");
-#endif
+returned_from_lookup_path:
+ dp = ndp->ni_vp;
/*
* Take into account any additional components consumed by
ndp->ni_next += cnp->cn_consume;
ndp->ni_pathlen -= cnp->cn_consume;
cnp->cn_consume = 0;
+ } else {
+ if (dp->v_name == NULL || dp->v_parent == NULLVP) {
+ int isdot_or_dotdot;
+ int update_flags = 0;
+
+ isdot_or_dotdot = (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') || (cnp->cn_flags & ISDOTDOT);
+
+ if (isdot_or_dotdot == 0) {
+ if (dp->v_name == NULL)
+ update_flags |= VNODE_UPDATE_NAME;
+ if (ndp->ni_dvp != NULLVP && dp->v_parent == NULLVP)
+ update_flags |= VNODE_UPDATE_PARENT;
+
+ if (update_flags)
+ vnode_update_identity(dp, ndp->ni_dvp, cnp->cn_nameptr, cnp->cn_namelen, cnp->cn_hash, update_flags);
+ }
+ }
+
+ if ( (cnp->cn_flags & MAKEENTRY) && (dp->v_flag & VNCACHEABLE) && LIST_FIRST(&dp->v_nclinks) == NULL) {
+ /*
+ * missing from name cache, but should
+ * be in it... this can happen if volfs
+ * causes the vnode to be created or the
+ * name cache entry got recycled but the
+ * vnode didn't...
+ * check to make sure that ni_dvp is valid
+ * cache_lookup_path may return a NULL
+ */
+ if (ndp->ni_dvp != NULL)
+ cache_enter(ndp->ni_dvp, dp, cnp);
+ }
}
- dp = ndp->ni_vp;
/*
- * Check to see if the vnode has been mounted on;
+ * Check to see if the vnode has been mounted on...
* if so find the root of the mounted file system.
*/
- 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;
+check_mounted_on:
+ if ((dp->v_type == VDIR) && dp->v_mountedhere &&
+ ((cnp->cn_flags & NOCROSSMOUNT) == 0)) {
+
+ vnode_lock(dp);
+
+ if ((dp->v_type == VDIR) && (mp = dp->v_mountedhere)) {
+
+ mp->mnt_crossref++;
+ vnode_unlock(dp);
+
+ if (vfs_busy(mp, 0)) {
+ mount_dropcrossref(mp, dp, 0);
+ goto check_mounted_on;
+ }
+ error = VFS_ROOT(mp, &tdp, ctx);
+ /*
+ * mount_dropcrossref does a vnode_put
+ * on dp if the 3rd arg is non-zero
+ */
+ mount_dropcrossref(mp, dp, 1);
+ dp = NULL;
+ vfs_unbusy(mp);
+
+ if (error) {
+ goto bad2;
+ }
+ ndp->ni_vp = dp = tdp;
+
+ goto check_mounted_on;
+ }
+ vnode_unlock(dp);
}
/*
* Check for symbolic link
*/
if ((dp->v_type == VLNK) &&
- ((cnp->cn_flags & FOLLOW) || *ndp->ni_next == '/')) {
+ ((cnp->cn_flags & FOLLOW) || trailing_slash || *ndp->ni_next == '/')) {
cnp->cn_flags |= ISSYMLINK;
return (0);
}
+ /*
+ * Check for bogus trailing slashes.
+ */
+ if (trailing_slash) {
+ if (dp->v_type != VDIR) {
+ error = ENOTDIR;
+ goto bad2;
+ }
+ trailing_slash = 0;
+ }
+
nextname:
/*
* Not a symbolic link. If more pathname,
* continue at next component, else return.
*/
if (*ndp->ni_next == '/') {
- cnp->cn_nameptr = ndp->ni_next;
+ cnp->cn_nameptr = ndp->ni_next + 1;
+ ndp->ni_pathlen--;
while (*cnp->cn_nameptr == '/') {
cnp->cn_nameptr++;
ndp->ni_pathlen--;
}
- vrele(ndp->ni_dvp);
+ vnode_put(ndp->ni_dvp);
+
+ cp = cnp->cn_nameptr;
+
+ if (*cp == '\0')
+ goto emptyname;
+
+ vnode_put(dp);
goto dirloop;
}
goto bad2;
}
if (cnp->cn_flags & SAVESTART) {
+ /*
+ * note that we already hold a reference
+ * on both dp and ni_dvp, but for some reason
+ * can't get another one... in this case we
+ * need to do vnode_put on dp in 'bad2'
+ */
+ if ( (vnode_get(ndp->ni_dvp)) ) {
+ error = ENOENT;
+ goto bad2;
+ }
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 (!wantparent && ndp->ni_dvp)
+ vnode_put(ndp->ni_dvp);
+
+ if (cnp->cn_flags & AUDITVNPATH1)
+ AUDIT_ARG(vnpath, dp, ARG_VNODE1);
+ else if (cnp->cn_flags & AUDITVNPATH2)
+ AUDIT_ARG(vnpath, dp, ARG_VNODE2);
+
if (kdebug_enable)
kdebug_lookup(dp, cnp);
return (0);
+emptyname:
+ cnp->cn_namelen = 0;
+ /*
+ * A degenerate name (e.g. / or "") which is a way of
+ * talking about a directory, e.g. like "/." or ".".
+ */
+ if (dp->v_type != VDIR) {
+ error = ENOTDIR;
+ goto bad;
+ }
+ if (cnp->cn_nameiop != LOOKUP) {
+ error = EISDIR;
+ goto bad;
+ }
+ if (wantparent) {
+ /*
+ * note that we already hold a reference
+ * on dp, but for some reason can't
+ * get another one... in this case we
+ * need to do vnode_put on dp in 'bad'
+ */
+ if ( (vnode_get(dp)) ) {
+ error = ENOENT;
+ goto bad;
+ }
+ ndp->ni_dvp = dp;
+ }
+ cnp->cn_flags &= ~ISDOTDOT;
+ cnp->cn_flags |= ISLASTCN;
+ ndp->ni_next = cp;
+ ndp->ni_vp = dp;
+
+ if (cnp->cn_flags & AUDITVNPATH1)
+ AUDIT_ARG(vnpath, dp, ARG_VNODE1);
+ else if (cnp->cn_flags & AUDITVNPATH2)
+ AUDIT_ARG(vnpath, dp, ARG_VNODE2);
+ if (cnp->cn_flags & SAVESTART)
+ panic("lookup: SAVESTART");
+ return (0);
+
bad2:
- if ((cnp->cn_flags & LOCKPARENT) && *ndp->ni_next == '\0')
- VOP_UNLOCK(ndp->ni_dvp, 0, p);
- vrele(ndp->ni_dvp);
+ if ((cnp->cn_flags & FSNODELOCKHELD)) {
+ cnp->cn_flags &= ~FSNODELOCKHELD;
+ unlock_fsnode(ndp->ni_dvp, NULL);
+ }
+ if (ndp->ni_dvp)
+ vnode_put(ndp->ni_dvp);
+ if (dp)
+ vnode_put(dp);
+ ndp->ni_vp = NULLVP;
+
+ if (kdebug_enable)
+ kdebug_lookup(dp, cnp);
+ return (error);
+
bad:
- vput(dp);
- ndp->ni_vp = NULL;
+ if ((cnp->cn_flags & FSNODELOCKHELD)) {
+ cnp->cn_flags &= ~FSNODELOCKHELD;
+ unlock_fsnode(ndp->ni_dvp, NULL);
+ }
+ if (dp)
+ vnode_put(dp);
+ ndp->ni_vp = NULLVP;
+
if (kdebug_enable)
kdebug_lookup(dp, cnp);
return (error);
struct vnode *dvp, **vpp;
struct componentname *cnp;
{
- struct proc *p = cnp->cn_proc;
struct vnode *dp = 0; /* the directory we are searching */
- int docache; /* == 0 do not cache last component */
int wantparent; /* 1 => wantparent or lockparent flag */
int rdonly; /* lookup read-only flag bit */
int error = 0;
#ifdef NAMEI_DIAGNOSTIC
- int newhash; /* DEBUG: check name hash */
+ int i, newhash; /* DEBUG: check name hash */
char *cp; /* DEBUG: check name ptr/len */
#endif
+ vfs_context_t ctx = cnp->cn_context;;
/*
* Setup: break out flag bits into variables.
*/
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);
-/* dirloop: */
- /*
- * Search a new directory.
- *
- * The cn_hash value is for use by vfs_cache.
- * The last component of the filename is left accessible via
- * cnp->cn_nameptr for callers that need the name. Callers needing
- * the name set the SAVENAME flag. When done, they assume
- * responsibility for freeing the pathname buffer.
- */
-#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_flags & NOCACHE)
+ cnp->cn_flags &= ~MAKEENTRY;
+ else
+ cnp->cn_flags |= MAKEENTRY;
+
+ dp = dvp;
/*
* Check for degenerate name (e.g. / or "")
error = ENOTDIR;
goto bad;
}
- if (!(cnp->cn_flags & LOCKLEAF))
- VOP_UNLOCK(dp, 0, p);
+ if ( (vnode_get(dp)) ) {
+ error = ENOENT;
+ goto bad;
+ }
*vpp = dp;
+
if (cnp->cn_flags & SAVESTART)
panic("lookup: SAVESTART");
return (0);
}
-
- if (cnp->cn_flags & ISDOTDOT)
- panic ("relookup: lookup on dot-dot");
-
/*
* We now have a segment name to search for, and a directory to search.
*/
- if (error = VOP_LOOKUP(dp, vpp, cnp)) {
+ if ( (error = VNOP_LOOKUP(dp, vpp, cnp, ctx)) ) {
+ if (error != EJUSTRETURN)
+ goto bad;
#if DIAGNOSTIC
if (*vpp != NULL)
panic("leaf should be empty");
#endif
- if (error != EJUSTRETURN)
- goto bad;
/*
* If creating and at end of pathname, then can consider
* allowing file to be created.
error = EROFS;
goto bad;
}
- /* ASSERT(dvp == ndp->ni_startdir) */
- if (cnp->cn_flags & SAVESTART)
- VREF(dvp);
/*
* We return with ni_vp NULL to indicate that the entry
* doesn't currently exist, leaving a pointer to the
goto bad2;
}
/* ASSERT(dvp == ndp->ni_startdir) */
- 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);
+ vnode_put(dp);
+bad:
*vpp = NULL;
+
return (error);
}
+/*
+ * Free pathname buffer
+ */
+void
+nameidone(struct nameidata *ndp)
+{
+ if ((ndp->ni_cnd.cn_flags & FSNODELOCKHELD)) {
+ ndp->ni_cnd.cn_flags &= ~FSNODELOCKHELD;
+ unlock_fsnode(ndp->ni_dvp, NULL);
+ }
+ if (ndp->ni_cnd.cn_flags & HASBUF) {
+ char *tmp = ndp->ni_cnd.cn_pnbuf;
+
+ ndp->ni_cnd.cn_pnbuf = NULL;
+ ndp->ni_cnd.cn_flags &= ~HASBUF;
+ FREE_ZONE(tmp, ndp->ni_cnd.cn_pnlen, M_NAMEI);
+ }
+}
+
#define NUMPARMS 23
+static void
kdebug_lookup(dp, cnp)
- struct vnode *dp;
+ struct vnode *dp;
struct componentname *cnp;
{
- register int i, n;
+ register unsigned int i, n;
register int dbg_namelen;
register int save_dbg_namelen;
register char *dbg_nameptr;
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);
+ /*
+ In the event that we collect multiple, consecutive pathname
+ entries, we must mark the start of the path's string.
+ */
+ KERNEL_DEBUG_CONSTANT((FSDBG_CODE(DBG_FSRW,36)) | DBG_FUNC_START,
+ (unsigned int)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)))
+ dbg_namelen -=(4 * sizeof(long)), i+= 4)
{
KERNEL_DEBUG_CONSTANT((FSDBG_CODE(DBG_FSRW,36)) | DBG_FUNC_NONE,
- dbg_parms[i++], dbg_parms[i++], dbg_parms[i++], dbg_parms[i++], 0);
+ dbg_parms[i], dbg_parms[i+1], dbg_parms[i+2], dbg_parms[i+3], 0);
}
}