+ * while (!done && !error) {
+ * call lookup to search path.
+ * if symbolic link, massage name in buffer and continue
+ * }
+ */
+namei(ndp, p)
+ register struct nameidata *ndp;
+ struct proc *p;
+{
+ 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;
+
+ ndp->ni_cred = p->p_ucred;
+ fdp = p->p_fd;
+
+ /*
+ * Get a buffer for the name to be translated, and copy the
+ * name into the buffer.
+ */
+ if ((ndp->ni_nameiop & HASBUF) == 0)
+ MALLOC(ndp->ni_pnbuf, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
+ if (ndp->ni_segflg == UIO_SYSSPACE)
+ error = copystr(ndp->ni_dirp, ndp->ni_pnbuf,
+ MAXPATHLEN, &ndp->ni_pathlen);
+ else
+ error = copyinstr(ndp->ni_dirp, ndp->ni_pnbuf,
+ MAXPATHLEN, &ndp->ni_pathlen);
+ if (error) {
+ free(ndp->ni_pnbuf, M_NAMEI);
+ ndp->ni_vp = NULL;
+ return (error);
+ }
+ ndp->ni_loopcnt = 0;
+#ifdef KTRACE
+ if (KTRPOINT(p, KTR_NAMEI))
+ ktrnamei(p->p_tracep, ndp->ni_pnbuf);
+#endif
+
+ /*
+ * Get starting point for the translation.
+ */
+ if ((ndp->ni_rootdir = fdp->fd_rdir) == NULL)
+ ndp->ni_rootdir = rootdir;
+ dp = fdp->fd_cdir;
+ VREF(dp);
+ for (;;) {
+ /*
+ * Check if root directory should replace current directory.
+ * Done at start of translation and after symbolic link.
+ */
+ ndp->ni_ptr = ndp->ni_pnbuf;
+ if (*ndp->ni_ptr == '/') {
+ vrele(dp);
+ while (*ndp->ni_ptr == '/') {
+ ndp->ni_ptr++;
+ ndp->ni_pathlen--;
+ }
+ dp = ndp->ni_rootdir;
+ VREF(dp);
+ }
+ ndp->ni_startdir = dp;
+ if (error = lookup(ndp, p)) {
+ FREE(ndp->ni_pnbuf, M_NAMEI);
+ return (error);
+ }
+ /*
+ * Check for symbolic link
+ */
+ if (ndp->ni_more == 0) {
+ if ((ndp->ni_nameiop & (SAVENAME | SAVESTART)) == 0)
+ FREE(ndp->ni_pnbuf, M_NAMEI);
+ else
+ ndp->ni_nameiop |= HASBUF;
+ return (0);
+ }
+ if ((ndp->ni_nameiop & LOCKPARENT) && ndp->ni_pathlen == 1)
+ VOP_UNLOCK(ndp->ni_dvp);
+ if (ndp->ni_loopcnt++ >= MAXSYMLINKS) {
+ error = ELOOP;
+ break;
+ }
+ if (ndp->ni_pathlen > 1)
+ MALLOC(cp, char *, MAXPATHLEN, M_NAMEI, M_WAITOK);
+ else
+ cp = ndp->ni_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, p->p_ucred)) {
+ if (ndp->ni_pathlen > 1)
+ free(cp, M_NAMEI);
+ break;
+ }
+ linklen = MAXPATHLEN - auio.uio_resid;
+ if (linklen + ndp->ni_pathlen >= MAXPATHLEN) {
+ if (ndp->ni_pathlen > 1)
+ free(cp, M_NAMEI);
+ error = ENAMETOOLONG;
+ break;
+ }
+ if (ndp->ni_pathlen > 1) {
+ bcopy(ndp->ni_next, cp + linklen, ndp->ni_pathlen);
+ FREE(ndp->ni_pnbuf, M_NAMEI);
+ ndp->ni_pnbuf = cp;
+ } else
+ ndp->ni_pnbuf[linklen] = '\0';
+ ndp->ni_pathlen += linklen;
+ vput(ndp->ni_vp);
+ dp = ndp->ni_dvp;
+ }
+ FREE(ndp->ni_pnbuf, M_NAMEI);
+ vrele(ndp->ni_dvp);
+ vput(ndp->ni_vp);
+ ndp->ni_vp = NULL;
+ return (error);
+}
+
+/*
+ * Search a pathname.
+ * This is a very central and rather complicated routine.
+ *
+ * The pathname is pointed to by ni_ptr and is of length ni_pathlen.
+ * The starting directory is taken from ni_startdir. The pathname is
+ * descended until done, or a symbolic link is encountered. The variable
+ * ni_more is clear if the path is completed; it is set to one if a
+ * symbolic link needing interpretation is encountered.
+ *
+ * The flag argument is LOOKUP, CREATE, RENAME, or DELETE depending on
+ * whether the name is to be looked up, created, renamed, or deleted.
+ * When CREATE, RENAME, or DELETE is specified, information usable in
+ * creating, renaming, or deleting a directory entry may be calculated.
+ * If flag has LOCKPARENT or'ed into it, the parent directory is returned
+ * locked. If flag has WANTPARENT or'ed into it, the parent directory is
+ * returned unlocked. Otherwise the parent directory is not returned. If
+ * the target of the pathname exists and LOCKLEAF is or'ed into the flag
+ * the target is returned locked, otherwise it is returned unlocked.
+ * When creating or renaming and LOCKPARENT is specified, the target may not
+ * be ".". When deleting and LOCKPARENT is specified, the target may be ".".
+ * NOTE: (LOOKUP | LOCKPARENT) currently returns the parent vnode unlocked.
+ *
+ * Overall outline of lookup:
+ *