--- //depot/projects/trustedbsd/base/sys/kern/vfs_cache.c	2002/06/28 20:54:03
+++ //depot/projects/trustedbsd/mac/sys/kern/vfs_cache.c	2002/06/28 21:44:36
@@ -49,6 +49,7 @@
 #include <sys/malloc.h>
 #include <sys/sysproto.h>
 #include <sys/proc.h>
+#include <sys/dirent.h>
 #include <sys/filedesc.h>
 #include <sys/fnv_hash.h>
 
@@ -793,106 +794,202 @@
  * Thus begins the fullpath magic.
  */
 
-#undef STATNODE
-#define STATNODE(name)							\
-	static u_int name;						\
-	SYSCTL_UINT(_vfs_cache, OID_AUTO, name, CTLFLAG_RD, &name, 0, "")
-
 static int disablefullpath;
 SYSCTL_INT(_debug, OID_AUTO, disablefullpath, CTLFLAG_RW, &disablefullpath, 0,
 	"Disable the vn_fullpath function");
 
-STATNODE(numfullpathcalls);
-STATNODE(numfullpathfail1);
-STATNODE(numfullpathfail2);
-STATNODE(numfullpathfail3);
-STATNODE(numfullpathfail4);
-STATNODE(numfullpathfound);
+static int
+vn_fullpath_dirents_searchbyid(struct thread *td, struct vnode *dvp,
+    struct dirent *dp, struct dirent *enddp, const struct vattr *vap,
+    struct dirent **retdp)
+{
+	struct vattr pvattr;
+	struct componentname cnp;
+	struct vnode *vp;
+	struct ucred *ucred = td->td_ucred;
+	int error;
+
+	*retdp = NULL;
+	for (; dp != enddp; dp = (struct dirent *)((char *)dp + dp->d_reclen)) {
+		if (dp->d_name[0] == '.' && (dp->d_namlen == 1 ||
+		    (dp->d_namlen == 2 && dp->d_name[1] == '.')))
+			continue;
+		cnp.cn_nameiop = LOOKUP;
+		cnp.cn_flags = LOCKPARENT | ISLASTCN | NOFOLLOW;
+		cnp.cn_thread = td;
+		cnp.cn_cred = ucred;
+		cnp.cn_nameptr = dp->d_name;
+		cnp.cn_namelen = dp->d_namlen;
+		error = VOP_LOOKUP(dvp, &vp, &cnp);
+		if (error)
+			return (error);
+		error = VOP_GETATTR(vp, &pvattr, ucred, td);
+		if (vp != dvp)
+			(void)vput(vp);
+		else
+			vrele(vp);	/* if looking up "." */
+		if (error)
+			return (error);
+		if (pvattr.va_fsid == vap->va_fsid &&
+		    pvattr.va_fileid == vap->va_fileid) {
+			*retdp = dp;
+			break;
+		}
+	}
+	return (0);
+}
 
-/*
- * Retrieve the full filesystem path that correspond to a vnode from the name
- * cache (if available)
- */
 int
-vn_fullpath(struct thread *td, struct vnode *vn, char **retbuf, char **freebuf)
-{
-	char *bp, *buf;
-	int i, slash_prefixed;
-	struct filedesc *fdp;
-	struct namecache *ncp;
-	struct vnode *vp;
+vn_fullpath(struct thread *td, struct vnode *startdvp, struct vnode *startvp,
+    char **buf, char **freebuf) {
+	struct vattr cvattr;
+	struct vnode *vp, *dvp, *fd_rdir;
+	char *bp, *allocedmem, *direntmem;
+	const int direntmem_size = (32 << 10) - MAXPATHLEN;
+	int error, i, slash_prefixed;
 
-	numfullpathcalls++;
 	if (disablefullpath)
-		return (ENODEV);
-	if (vn == NULL)
-		return (EINVAL);
-	buf = malloc(MAXPATHLEN, M_TEMP, M_WAITOK);
-	bp = buf + MAXPATHLEN - 1;
+		return (EPERM);
+	FILEDESC_LOCK(td->td_proc->p_fd);
+	fd_rdir = td->td_proc->p_fd->fd_rdir;
+	vref(fd_rdir);
+	FILEDESC_UNLOCK(td->td_proc->p_fd);
+	allocedmem = malloc(MAXPATHLEN + direntmem_size, M_TEMP, M_WAITOK);
+	bp = allocedmem;
+	bp += MAXPATHLEN - 1;
 	*bp = '\0';
-	fdp = td->td_proc->p_fd;
+	direntmem = allocedmem + MAXPATHLEN;
 	slash_prefixed = 0;
-	FILEDESC_LOCK(fdp);
-	for (vp = vn; vp != fdp->fd_rdir && vp != rootvnode;) {
+	vref(startvp);
+	for (vp = startvp;; vrele(vp), vp = dvp) {
+		struct iovec diov = {
+			direntmem,
+			direntmem_size
+		};
+		struct uio duio = {
+			&diov,
+			1,
+			0,
+			direntmem_size,
+			UIO_SYSSPACE,
+			UIO_READ,
+			td
+		};
+		struct dirent *dp;
+		int direof;
+
+		if (vp == rootvnode || vp == fd_rdir)
+			break;
 		if (vp->v_flag & VROOT) {
 			if (vp->v_mount == NULL) {	/* forced unmount */
-				FILEDESC_UNLOCK(fdp);
-				free(buf, M_TEMP);
-				return (EBADF);
+				error = EBADF;
+				goto out;
 			}
-			vp = vp->v_mount->mnt_vnodecovered;
+			dvp = vp->v_mount->mnt_vnodecovered;
+			vref(dvp);
 			continue;
 		}
-		if (vp != vn && vp->v_dd->v_id != vp->v_ddid) {
-			FILEDESC_UNLOCK(fdp);
-			numfullpathfail1++;
-			free(buf, M_TEMP);
-			return (ENOTDIR);
+		if (startdvp != NULL) {
+			dvp = startdvp;
+			vref(dvp);
+			startdvp = NULL;
+		} else {
+			if (vp->v_ddid == vp->v_dd->v_id) {
+				dvp = vp->v_dd;
+				vref(dvp);
+			} else {
+				struct componentname cnp;
+
+				if (vp->v_type != VDIR) {
+					error = EBADF;
+					goto out;
+				}
+				cnp.cn_nameiop = LOOKUP;
+				cnp.cn_flags = 0;
+				cnp.cn_thread = td;
+				cnp.cn_cred = td->td_ucred;
+				cnp.cn_nameptr = "..";
+				cnp.cn_namelen = 2;
+				vn_lock(vp, LK_EXCLUSIVE, td);
+				error = VOP_LOOKUP(vp, &dvp, &cnp);
+				if (error) { 
+					VOP_UNLOCK(vp, 0, td);
+					goto out;
+				}
+				VOP_UNLOCK(dvp, 0, td);
+			}
 		}
-		ncp = TAILQ_FIRST(&vp->v_cache_dst);
-		if (!ncp) {
-			FILEDESC_UNLOCK(fdp);
-			numfullpathfail2++;
-			free(buf, M_TEMP);
-			return (ENOENT);
+		if (vp == dvp)
+			break;
+		/*
+		 * Utilize POSIX requirement of files having same
+		 * st_dev and st_ino to be the same  file, in our
+		 * case with vattr.va_fsid and vattr.va_fileid.
+		 */
+		vn_lock(vp, LK_EXCLUSIVE, td);
+		error = VOP_GETATTR(vp, &cvattr, curthread->td_ucred,
+		    curthread);
+		VOP_UNLOCK(vp, 0, td);
+		if (error) {
+			vrele(dvp);
+			goto out;
 		}
-		if (vp != vn && ncp->nc_dvp != vp->v_dd) {
-			FILEDESC_UNLOCK(fdp);
-			numfullpathfail3++;
-			free(buf, M_TEMP);
-			return (EBADF);
-		}
-		for (i = ncp->nc_nlen - 1; i >= 0; i--) {
-			if (bp == buf) {
-				FILEDESC_UNLOCK(fdp);
-				numfullpathfail4++;
-				free(buf, M_TEMP);
-				return (ENOMEM);
+		vn_lock(dvp, LK_EXCLUSIVE, td);
+		for (direof = 0; !direof;) {
+			error = VOP_READDIR(dvp, &duio, td->td_ucred, &direof,
+			    NULL, NULL);
+			if (error)
+				break;
+			error = vn_fullpath_dirents_searchbyid(td, dvp,
+			    (struct dirent *)direntmem,
+			    (struct dirent *)(direntmem +
+			    direntmem_size - duio.uio_resid),
+			    &cvattr, &dp);
+			if (error)
+				break;
+			if (dp != NULL) {
+				for (i = dp->d_namlen - 1; i >= 0; i--) {
+					if (bp == allocedmem) {
+						error = ENOMEM;
+						vput(dvp);
+						goto out;
+					}
+					*--bp = dp->d_name[i];
+				}
+				goto nextcomp;
 			}
-			*--bp = ncp->nc_name[i];
+			diov.iov_base = direntmem;
+			diov.iov_len = direntmem_size;
+			duio.uio_resid = direntmem_size;
 		}
-		if (bp == buf) {
-			FILEDESC_UNLOCK(fdp);
-			numfullpathfail4++;
-			free(buf, M_TEMP);
-			return (ENOMEM);
+		vput(dvp);
+		if (direof)
+			error = ENOENT;
+		goto out;
+nextcomp:
+		if (bp == allocedmem) {
+			error = ENOMEM;
+			vput(dvp);
+			goto out;
 		}
 		*--bp = '/';
 		slash_prefixed = 1;
-		vp = ncp->nc_dvp;
+		VOP_UNLOCK(dvp, 0, td);
 	}
 	if (!slash_prefixed) {
-		if (bp == buf) {
-			FILEDESC_UNLOCK(fdp);
-			numfullpathfail4++;
-			free(buf, M_TEMP);
-			return (ENOMEM);
+		if (bp == allocedmem) {
+			error = ENOMEM;
+			goto out;
 		}
 		*--bp = '/';
 	}
-	FILEDESC_UNLOCK(fdp);
-	numfullpathfound++;
-	*retbuf = bp; 
-	*freebuf = buf;
-	return (0);
+	error = 0;
+	*buf = bp;
+	*freebuf = allocedmem;
+out:
+	vrele(vp);
+	vrele(fd_rdir);
+	if (error)
+		free(allocedmem, M_TEMP);
+	return (error);
 }
