Unified getcwd() implementation

Bruce M Simpson bms at spc.org
Thu Aug 14 12:52:19 PDT 2003


Hello all,

I've had the attached patch in my tree for a 1 month period without
any panics or other adverse effects that I could see.
This is from one of phk's JKH todo list items.

It merges the Linux in-kernel scanning-based getcwd() with our namei
cache based implementation, in a way which tries to use the cache
efficiently, but will use directory scanning upon a cache miss.
This also has the advantage that scandir() games are no longer needed in libc.
                                                                              
Please let me know your thoughts on this. This is my first real bit of vfs
related work, so I have no immediate plans to commit it until I can get
concrete review going forward.

Kind regards
BMS 
-------------- next part --------------

This is a revised patch against 2003.05.08.00.00.00 which implements
getcwd() in a more efficient way. Instead of discarding the entire result
if each successive component can't be found in the cache, it checks
the cache on a component-by-component basis.

diff -uN sys/kern/vfs_cache.c.orig sys/kern/vfs_cache.c
--- sys/kern/vfs_cache.c.orig	Wed May 21 23:37:47 2003
+++ sys/kern/vfs_cache.c	Sat May 24 14:12:44 2003
@@ -36,6 +36,7 @@
  *	@(#)vfs_cache.c	8.5 (Berkeley) 3/22/95
  * $FreeBSD: src/sys/kern/vfs_cache.c,v 1.82 2003/03/20 10:40:45 phk Exp $
  */
+#include "opt_mac.h"
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -53,6 +54,11 @@
 #include <sys/filedesc.h>
 #include <sys/fnv_hash.h>
 
+#include <sys/file.h>
+#include <sys/uio.h>
+#include <sys/mac.h>
+#include <sys/dirent.h>
+#include <ufs/ufs/dir.h>	/* XXX only for DIRBLKSIZ */
 /*
  * This structure describes the elements in the cache of recent
  * names looked up by namei.
@@ -695,6 +701,13 @@
 };
 #endif
 
+static int getcwd_impl(struct vnode *lvp, struct vnode *rvp, char **bpp,
+	char *bufp, struct thread *td);
+static int getcwd_scandir(struct vnode **, struct vnode **, char **, char *,
+	struct thread *);
+
+#define DIRENT_MINSIZE (sizeof(struct dirent) - (MAXNAMLEN+1) + 4)
+
 /*
  * XXX All of these sysctls would probably be more productive dead.
  */
@@ -708,6 +721,7 @@
 static u_long numcwdfail2; STATNODE(CTLFLAG_RD, numcwdfail2, &numcwdfail2);
 static u_long numcwdfail3; STATNODE(CTLFLAG_RD, numcwdfail3, &numcwdfail3);
 static u_long numcwdfail4; STATNODE(CTLFLAG_RD, numcwdfail4, &numcwdfail4);
+static u_long numcwdfail5; STATNODE(CTLFLAG_RD, numcwdfail5, &numcwdfail5);
 static u_long numcwdfound; STATNODE(CTLFLAG_RD, numcwdfound, &numcwdfound);
 
 /* Implementation of the getcwd syscall */
@@ -720,14 +734,20 @@
 	return (kern___getcwd(td, uap->buf, UIO_USERSPACE, uap->buflen));
 }
 
+/*
+ * Find pathname of process's current directory.
+ *
+ * Use vfs vnode-to-name reverse cache; if that fails, fall back
+ * to reading directory contents.
+ */
 int
 kern___getcwd(struct thread *td, u_char *buf, enum uio_seg bufseg, u_int buflen)
 {
-	char *bp, *tmpbuf;
-	int error, i, slash_prefixed;
+	char *bp, *bufp;
+	int error;
 	struct filedesc *fdp;
-	struct namecache *ncp;
-	struct vnode *vp;
+	struct vnode *rvp;
+	struct vnode *lvp;
 
 	numcwdcalls++;
 	if (disablecwd)
@@ -736,77 +756,414 @@
 		return (EINVAL);
 	if (buflen > MAXPATHLEN)
 		buflen = MAXPATHLEN;
+
+	buflen *= 4; /* XXX */
+
 	error = 0;
-	tmpbuf = bp = malloc(buflen, M_TEMP, M_WAITOK);
+	bufp = bp = malloc(buflen, M_TEMP, M_WAITOK);
 	bp += buflen - 1;
 	*bp = '\0';
+
 	fdp = td->td_proc->p_fd;
-	slash_prefixed = 0;
 	FILEDESC_LOCK(fdp);
-	mp_fixme("No vnode locking done!");
-	for (vp = fdp->fd_cdir; vp != fdp->fd_rdir && vp != rootvnode;) {
-		if (vp->v_vflag & VV_ROOT) {
-			if (vp->v_mount == NULL) {	/* forced unmount */
-				FILEDESC_UNLOCK(fdp);
-				free(tmpbuf, M_TEMP);
-				return (EBADF);
-			}
-			vp = vp->v_mount->mnt_vnodecovered;
-			continue;
-		}
-		if (vp->v_dd->v_id != vp->v_ddid) {
-			FILEDESC_UNLOCK(fdp);
+
+	rvp = fdp->fd_rdir;
+	if (rvp == NULL)
+		rvp = rootvnode;
+	VREF(rvp);
+
+	lvp = fdp->fd_cdir;
+
+	error = getcwd_impl(lvp, rvp, &bp, bufp, td);
+	if (!error) {
+		numcwdfound++;
+		if (bufseg == UIO_SYSSPACE)
+			bcopy(bp, buf, strlen(bp) + 1);
+		else
+			error = copyout(bp, buf, strlen(bp) + 1);
+	} else {
+#if DIAGNOSTIC
+		printf("getcwd: error %d\n", error);
+#endif
+	}
+
+	vrele(rvp);
+	FILEDESC_UNLOCK(fdp);
+	free(bufp, M_TEMP);
+	return (error);
+}
+
+/*
+ * Recursively advance up the directory hierarchy from lvp to rvp.
+ * Look in the namecache first for each path component; if it cannot
+ * be found, use scandir to find it. Copy the result into the buffer
+ * pointed to by bufp from *bpp onwards.
+ */
+
+static int
+getcwd_impl(lvp, rvp, bpp, bufp, td)
+	struct vnode *lvp;
+	struct vnode *rvp;
+	char **bpp;
+	char *bufp;
+	struct thread *td;
+{
+	int i, error, slash_prefixed;
+	struct namecache *ncp;
+	struct vnode *uvp;
+	struct vnode *tvp;
+	char *bp;
+
+	error = slash_prefixed = 0;
+	uvp = NULL;
+
+	VREF(lvp);
+	error = vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY, td);
+	if (error) {
+		numcwdfail5++;
+		vrele(lvp);
+		lvp = NULL;
+		goto out;
+	}
+	if (bufp)
+		bp = *bpp;
+
+	while (lvp != rvp) {
+#if 1
+		if (lvp->v_type != VDIR) {
 			numcwdfail1++;
-			free(tmpbuf, M_TEMP);
-			return (ENOTDIR);
+			error = ENOTDIR;
+			goto out;
 		}
-		ncp = TAILQ_FIRST(&vp->v_cache_dst);
-		if (!ncp) {
-			FILEDESC_UNLOCK(fdp);
-			numcwdfail2++;
-			free(tmpbuf, M_TEMP);
-			return (ENOENT);
+#else
+		if (lvp->v_dd->v_id != lvp->v_ddid) {
+			numcwdfail1++;
+			error = ENOTDIR;
+			goto out;
 		}
-		if (ncp->nc_dvp != vp->v_dd) {
-			FILEDESC_UNLOCK(fdp);
-			numcwdfail3++;
-			free(tmpbuf, M_TEMP);
-			return (EBADF);
-		}
-		for (i = ncp->nc_nlen - 1; i >= 0; i--) {
-			if (bp == tmpbuf) {
-				FILEDESC_UNLOCK(fdp);
-				numcwdfail4++;
-				free(tmpbuf, M_TEMP);
-				return (ENOMEM);
+#endif
+		/*
+		 * step up if we're a covered vnode.
+		 */
+		while (lvp->v_vflag & VV_ROOT) {
+			if (lvp == rvp)
+				goto out;
+			tvp = lvp;
+			lvp = lvp->v_mount->mnt_vnodecovered;
+			vput(tvp);
+			if (lvp == NULL) {
+				numcwdfail3++;
+				error = ENOENT;
+				goto out;
+			}
+			VREF(lvp);
+			error = vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY, td);
+			if (error != 0) {
+				numcwdfail5++;
+				vrele(lvp);
+				lvp = NULL;
+				goto out;
 			}
-			*--bp = ncp->nc_name[i];
 		}
-		if (bp == tmpbuf) {
-			FILEDESC_UNLOCK(fdp);
-			numcwdfail4++;
-			free(tmpbuf, M_TEMP);
-			return (ENOMEM);
+		/*
+		 * look in the cache first
+		 */
+		ncp = TAILQ_FIRST(&lvp->v_cache_dst);
+#if DIAGNOSTIC
+		/* XXX simulate cache failure every 10 lookups */
+		if ((numcwdcalls % 10) == 0)
+			ncp = NULL;
+#endif
+		if (ncp) {
+			if (ncp->nc_dvp != lvp->v_dd) {
+				numcwdfail3++;
+				error = EBADF;
+				goto out;
+			}
+			for (i = ncp->nc_nlen - 1; i >= 0; i--) {
+				if (bp == bufp) {
+					numcwdfail4++;
+					error = ENOMEM;
+					goto out;
+				}
+				*--bp = ncp->nc_name[i];
+			}
+			/*
+			 * must ensure lvp is always locked and ref'd
+			 */
+			tvp = lvp;			
+			lvp = lvp->v_dd;
+			vput(tvp);
+			if (lvp == NULL) {
+				numcwdfail3++;
+				error = ENOENT;
+				goto out;
+			}
+			VREF(lvp);
+			error = vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY, td);
+			if (error != 0) {
+				numcwdfail5++;
+				vrele(lvp);
+				lvp = NULL;
+				goto out;
+			}
+		} else {
+			numcwdfail2++;
+#if DIAGNOSTIC
+			printf("getcwd: using scandir\n");
+#endif
+			error = getcwd_scandir(&lvp, &uvp, &bp, bufp, td);
+			if (error) {
+#if DIAGNOSTIC
+				printf("getcwd_scandir returned %d\n", error);
+#endif
+				goto out;
+			}
+#if DIAGNOSTIC
+			if (lvp != NULL)
+				panic("getcwd: oops, forgot to null lvp");
+#endif
+			lvp = uvp;
+			uvp = NULL;
 		}
+#if DIAGNOSTIC
+		if (bufp && (bp <= bufp))
+			panic("getcwd: oops, went back too far");
+#endif
 		*--bp = '/';
 		slash_prefixed = 1;
-		vp = vp->v_dd;
 	}
-	FILEDESC_UNLOCK(fdp);
+
 	if (!slash_prefixed) {
-		if (bp == tmpbuf) {
+		if (bp == bufp) {
 			numcwdfail4++;
-			free(tmpbuf, M_TEMP);
-			return (ENOMEM);
+			error = ENOMEM;
+			goto out;
 		}
 		*--bp = '/';
 	}
-	numcwdfound++;
-	if (bufseg == UIO_SYSSPACE)
-		bcopy(bp, buf, strlen(bp) + 1);
-	else
-		error = copyout(bp, buf, strlen(bp) + 1);
-	free(tmpbuf, M_TEMP);
+out:
+	if (bpp)
+		*bpp = bp;
+	if (uvp)
+		vput(uvp);
+	if (lvp)
+		vput(lvp);
+	return (error);
+}
+
+/*
+ * Vnode variable naming conventions in this file:
+ *
+ * rvp: the current root we're aiming towards.
+ * lvp, *lvpp: the "lower" vnode
+ * uvp, *uvpp: the "upper" vnode.
+ *
+ * Since all the vnodes we're dealing with are directories, and the
+ * lookups are going *up* in the filesystem rather than *down*, the
+ * usual "pvp" (parent) or "dvp" (directory) naming conventions are
+ * too confusing.
+ */
+
+/*
+ * XXX Will infinite loop in certain cases if a directory read reliably
+ *	returns EINVAL on last block.
+ * XXX is EINVAL the right thing to return if a directory is malformed?
+ */
+
+/*
+ * XXX Untested vs. mount -o union; probably does the wrong thing.
+ */
+
+/*
+ * Find parent vnode of *lvpp, return in *uvpp
+ *
+ * If we care about the name, scan it looking for name of directory
+ * entry pointing at lvp.
+ *
+ * Place the name in the buffer which starts at bufp, immediately
+ * before *bpp, and move bpp backwards to point at the start of it.
+ *
+ * On entry, *lvpp is a locked vnode reference; on exit, it is vput and NULL'ed
+ * On exit, *uvpp is either NULL or is a locked vnode reference.
+ */
+
+static int
+getcwd_scandir(lvpp, uvpp, bpp, bufp, td)
+	struct vnode **lvpp;
+	struct vnode **uvpp;
+	char **bpp;
+	char *bufp;
+	struct thread *td;
+{
+	int	error = 0;
+	int	eofflag;
+	off_t	off;
+	int	tries;
+	struct uio uio;
+	struct iovec iov;
+	char	*dirbuf = NULL;
+	int	dirbuflen;
+	ino_t   fileno;
+	struct vattr va;
+	struct vnode *uvp = NULL;
+	struct vnode *lvp = *lvpp;
+	struct componentname cn;
+	int len, reclen;
+	tries = 0;
+
+	/*
+	 * If we want the filename, get some info we need while the
+	 * current directory is still locked.
+	 */
+	if (bufp != NULL) {
+		error = VOP_GETATTR(lvp, &va, td->td_ucred, td);
+		if (error) {
+			vput(lvp);
+			*lvpp = NULL;
+			*uvpp = NULL;
+#if DIAGNOSTICS
+			printf("VOP_GETATTR returned %d", error);
+#endif
+			return error;
+		}
+	}
+
+	/*
+	 * Ok, we have to do it the hard way..
+	 * Next, get parent vnode using lookup of ..
+	 */
+	cn.cn_nameiop = LOOKUP;
+	cn.cn_flags = ISLASTCN | ISDOTDOT | RDONLY;
+	cn.cn_thread = td;
+	cn.cn_cred = td->td_ucred;
+	cn.cn_pnbuf = NULL;
+	cn.cn_nameptr = "..";
+	cn.cn_namelen = 2;
+	cn.cn_consume = 0;
+
+	/*
+	 * At this point, lvp is locked and will be unlocked by the lookup.
+	 * On successful return, *uvpp will be locked
+	 */
+	error = VOP_LOOKUP(lvp, uvpp, &cn);
+	if (error) {
+		vput(lvp);
+		*lvpp = NULL;
+		*uvpp = NULL;
+		return error;
+	}
+	uvp = *uvpp;
+
+	/* If we don't care about the pathname, we're done */
+	if (bufp == NULL) {
+		vrele(lvp);
+		*lvpp = NULL;
+		return 0;
+	}
+
+	fileno = va.va_fileid;
+
+	dirbuflen = DIRBLKSIZ;
+	if (dirbuflen < va.va_blocksize)
+		dirbuflen = va.va_blocksize;
+	dirbuf = (char *)malloc(dirbuflen, M_TEMP, M_WAITOK);
+
+#if 0
+unionread:
+#endif
+	off = 0;
+	do {
+		/* call VOP_READDIR of parent */
+		iov.iov_base = dirbuf;
+		iov.iov_len = dirbuflen;
+
+		uio.uio_iov = &iov;
+		uio.uio_iovcnt = 1;
+		uio.uio_offset = off;
+		uio.uio_resid = dirbuflen;
+		uio.uio_segflg = UIO_SYSSPACE;
+		uio.uio_rw = UIO_READ;
+		uio.uio_td = td;
+
+		eofflag = 0;
+
+#ifdef MAC
+		error = mac_check_vnode_readdir(td->td_ucred, uvp);
+		if (error == 0)
+#endif /* MAC */
+			error = VOP_READDIR(uvp, &uio, td->td_ucred, &eofflag,
+			    0, 0);
+
+		off = uio.uio_offset;
+
+		/*
+		 * Try again if NFS tosses its cookies.
+		 * XXX this can still loop forever if the directory is busted
+		 * such that the second or subsequent page of it always
+		 * returns EINVAL
+		 */
+		if ((error == EINVAL) && (tries < 3)) {
+			off = 0;
+			tries++;
+			continue;	/* once more, with feeling */
+		}
+
+		if (!error) {
+			char   *cpos;
+			struct dirent *dp;
+			
+			cpos = dirbuf;
+			tries = 0;
+				
+			/* scan directory page looking for matching vnode */ 
+			for (len = (dirbuflen - uio.uio_resid);
+			     len > 0;
+			     len -= reclen)
+			{
+				dp = (struct dirent *) cpos;
+				reclen = dp->d_reclen;
+
+				/* check for malformed directory.. */
+				if (reclen < DIRENT_MINSIZE) {
+					error = EINVAL;
+					goto out;
+				}
+				/*
+				 * XXX should perhaps do VOP_LOOKUP to
+				 * check that we got back to the right place,
+				 * but getting the locking games for that
+				 * right would be heinous.
+				 */
+				if ((dp->d_type != DT_WHT) &&
+				    (dp->d_fileno == fileno)) {
+					char *bp = *bpp;
+					bp -= dp->d_namlen;
+#if DIAGNOSTIC
+					printf("bp: %p bufp: %p dp->d_name: %s"
+					       " (%d)\n",
+					       bp, bufp,
+					       dp->d_name, dp->d_namlen);
+#endif
+					if (bp <= bufp) {
+						error = ERANGE;
+						goto out;
+					}
+					bcopy(dp->d_name, bp, dp->d_namlen);
+					error = 0;
+					*bpp = bp;
+					goto out;
+				}
+				cpos += reclen;
+			}
+		}
+	} while (!eofflag);
+	error = ENOENT;
+
+out:
+	vrele(lvp);
+	*lvpp = NULL;
+	free(dirbuf, M_TEMP);
 	return (error);
 }
 
diff -uN sys/compat/linux/linux_getcwd.c.orig sys/compat/linux/linux_getcwd.c
--- sys/compat/linux/linux_getcwd.c.orig	Wed May 21 23:38:05 2003
+++ sys/compat/linux/linux_getcwd.c	Wed May 21 23:38:15 2003
@@ -38,431 +38,27 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 #include "opt_compat.h"
-#include "opt_mac.h"
 
 #include <sys/param.h>
 #include <sys/systm.h>
-#include <sys/namei.h>
-#include <sys/filedesc.h>
-#include <sys/kernel.h>
-#include <sys/file.h>
-#include <sys/stat.h>
-#include <sys/syscallsubr.h>
-#include <sys/vnode.h>
-#include <sys/mount.h>
 #include <sys/proc.h>
-#include <sys/uio.h>
-#include <sys/mac.h>
-#include <sys/malloc.h>
-#include <sys/dirent.h>
-#include <ufs/ufs/dir.h>	/* XXX only for DIRBLKSIZ */
+#include <sys/syscallsubr.h>
 
 #include <machine/../linux/linux.h>
 #include <machine/../linux/linux_proto.h>
 #include <compat/linux/linux_util.h>
 
-static int
-linux_getcwd_scandir(struct vnode **, struct vnode **,
-    char **, char *, struct thread *);
-static int
-linux_getcwd_common(struct vnode *, struct vnode *,
-		   char **, char *, int, int, struct thread *);
-
-#define DIRENT_MINSIZE (sizeof(struct dirent) - (MAXNAMLEN+1) + 4)
-
-/*
- * Vnode variable naming conventions in this file:
- *
- * rvp: the current root we're aiming towards.
- * lvp, *lvpp: the "lower" vnode
- * uvp, *uvpp: the "upper" vnode.
- *
- * Since all the vnodes we're dealing with are directories, and the
- * lookups are going *up* in the filesystem rather than *down*, the
- * usual "pvp" (parent) or "dvp" (directory) naming conventions are
- * too confusing.
- */
-
-/*
- * XXX Will infinite loop in certain cases if a directory read reliably
- *	returns EINVAL on last block.
- * XXX is EINVAL the right thing to return if a directory is malformed?
- */
-
-/*
- * XXX Untested vs. mount -o union; probably does the wrong thing.
- */
-
-/*
- * Find parent vnode of *lvpp, return in *uvpp
- *
- * If we care about the name, scan it looking for name of directory
- * entry pointing at lvp.
- *
- * Place the name in the buffer which starts at bufp, immediately
- * before *bpp, and move bpp backwards to point at the start of it.
- *
- * On entry, *lvpp is a locked vnode reference; on exit, it is vput and NULL'ed
- * On exit, *uvpp is either NULL or is a locked vnode reference.
- */
-static int
-linux_getcwd_scandir(lvpp, uvpp, bpp, bufp, td)
-	struct vnode **lvpp;
-	struct vnode **uvpp;
-	char **bpp;
-	char *bufp;
-	struct thread *td;
-{
-	int     error = 0;
-	int     eofflag;
-	off_t   off;
-	int     tries;
-	struct uio uio;
-	struct iovec iov;
-	char   *dirbuf = NULL;
-	int	dirbuflen;
-	ino_t   fileno;
-	struct vattr va;
-	struct vnode *uvp = NULL;
-	struct vnode *lvp = *lvpp;	
-	struct componentname cn;
-	int len, reclen;
-	tries = 0;
-
-	/*
-	 * If we want the filename, get some info we need while the
-	 * current directory is still locked.
-	 */
-	if (bufp != NULL) {
-		error = VOP_GETATTR(lvp, &va, td->td_ucred, td);
-		if (error) {
-			vput(lvp);
-			*lvpp = NULL;
-			*uvpp = NULL;
-			return error;
-		}
-	}
-
-	/*
-	 * Ok, we have to do it the hard way..
-	 * Next, get parent vnode using lookup of ..
-	 */
-	cn.cn_nameiop = LOOKUP;
-	cn.cn_flags = ISLASTCN | ISDOTDOT | RDONLY;
-	cn.cn_thread = td;
-	cn.cn_cred = td->td_ucred;
-	cn.cn_pnbuf = NULL;
-	cn.cn_nameptr = "..";
-	cn.cn_namelen = 2;
-	cn.cn_consume = 0;
-	
-	/*
-	 * At this point, lvp is locked and will be unlocked by the lookup.
-	 * On successful return, *uvpp will be locked
-	 */
-	error = VOP_LOOKUP(lvp, uvpp, &cn);
-	if (error) {
-		vput(lvp);
-		*lvpp = NULL;
-		*uvpp = NULL;
-		return error;
-	}
-	uvp = *uvpp;
-
-	/* If we don't care about the pathname, we're done */
-	if (bufp == NULL) {
-		vrele(lvp);
-		*lvpp = NULL;
-		return 0;
-	}
-	
-	fileno = va.va_fileid;
-
-	dirbuflen = DIRBLKSIZ;
-	if (dirbuflen < va.va_blocksize)
-		dirbuflen = va.va_blocksize;
-	dirbuf = (char *)malloc(dirbuflen, M_TEMP, M_WAITOK);
-
-#if 0
-unionread:
-#endif
-	off = 0;
-	do {
-		/* call VOP_READDIR of parent */
-		iov.iov_base = dirbuf;
-		iov.iov_len = dirbuflen;
-
-		uio.uio_iov = &iov;
-		uio.uio_iovcnt = 1;
-		uio.uio_offset = off;
-		uio.uio_resid = dirbuflen;
-		uio.uio_segflg = UIO_SYSSPACE;
-		uio.uio_rw = UIO_READ;
-		uio.uio_td = td;
-
-		eofflag = 0;
-
-#ifdef MAC
-		error = mac_check_vnode_readdir(td->td_ucred, uvp);
-		if (error == 0)
-#endif /* MAC */
-			error = VOP_READDIR(uvp, &uio, td->td_ucred, &eofflag,
-			    0, 0);
-
-		off = uio.uio_offset;
-
-		/*
-		 * Try again if NFS tosses its cookies.
-		 * XXX this can still loop forever if the directory is busted
-		 * such that the second or subsequent page of it always
-		 * returns EINVAL
-		 */
-		if ((error == EINVAL) && (tries < 3)) {
-			off = 0;
-			tries++;
-			continue;	/* once more, with feeling */
-		}
-
-		if (!error) {
-			char   *cpos;
-			struct dirent *dp;
-			
-			cpos = dirbuf;
-			tries = 0;
-				
-			/* scan directory page looking for matching vnode */ 
-			for (len = (dirbuflen - uio.uio_resid); len > 0; len -= reclen) {
-				dp = (struct dirent *) cpos;
-				reclen = dp->d_reclen;
-
-				/* check for malformed directory.. */
-				if (reclen < DIRENT_MINSIZE) {
-					error = EINVAL;
-					goto out;
-				}
-				/*
-				 * XXX should perhaps do VOP_LOOKUP to
-				 * check that we got back to the right place,
-				 * but getting the locking games for that
-				 * right would be heinous.
-				 */
-				if ((dp->d_type != DT_WHT) &&
-				    (dp->d_fileno == fileno)) {
-					char *bp = *bpp;
-					bp -= dp->d_namlen;
-					
-					if (bp <= bufp) {
-						error = ERANGE;
-						goto out;
-					}
-					bcopy(dp->d_name, bp, dp->d_namlen);
-					error = 0;
-					*bpp = bp;
-					goto out;
-				}
-				cpos += reclen;
-			}
-		}
-	} while (!eofflag);
-	error = ENOENT;
-		
-out:
-	vrele(lvp);
-	*lvpp = NULL;
-	free(dirbuf, M_TEMP);
-	return error;
-}
-
-
-/*
- * common routine shared by sys___getcwd() and linux_vn_isunder()
- */
-
-#define GETCWD_CHECK_ACCESS 0x0001
-
-static int
-linux_getcwd_common (lvp, rvp, bpp, bufp, limit, flags, td)
-	struct vnode *lvp;
-	struct vnode *rvp;
-	char **bpp;
-	char *bufp;
-	int limit;
-	int flags;
-	struct thread *td;
-{
-	struct filedesc *fdp = td->td_proc->p_fd;
-	struct vnode *uvp = NULL;
-	char *bp = NULL;
-	int error;
-	int perms = VEXEC;
-
-	if (rvp == NULL) {
-		rvp = fdp->fd_rdir;
-		if (rvp == NULL)
-			rvp = rootvnode;
-	}
-	
-	VREF(rvp);
-	VREF(lvp);
-
-	/*
-	 * Error handling invariant:
-	 * Before a `goto out':
-	 *	lvp is either NULL, or locked and held.
-	 *	uvp is either NULL, or locked and held.
-	 */
-
-	error = vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY, td);
-	if (error) {
-		vrele(lvp);
-		lvp = NULL;
-		goto out;
-	}
-	if (bufp)
-		bp = *bpp;
-	/*
-	 * this loop will terminate when one of the following happens:
-	 *	- we hit the root
-	 *	- getdirentries or lookup fails
-	 *	- we run out of space in the buffer.
-	 */
-	if (lvp == rvp) {
-		if (bp)
-			*(--bp) = '/';
-		goto out;
-	}
-	do {
-		if (lvp->v_type != VDIR) {
-			error = ENOTDIR;
-			goto out;
-		}
-		
-		/*
-		 * access check here is optional, depending on
-		 * whether or not caller cares.
-		 */
-		if (flags & GETCWD_CHECK_ACCESS) {
-			error = VOP_ACCESS(lvp, perms, td->td_ucred, td);
-			if (error)
-				goto out;
-			perms = VEXEC|VREAD;
-		}
-		
-		/*
-		 * step up if we're a covered vnode..
-		 */
-		while (lvp->v_vflag & VV_ROOT) {
-			struct vnode *tvp;
-
-			if (lvp == rvp)
-				goto out;
-			
-			tvp = lvp;
-			lvp = lvp->v_mount->mnt_vnodecovered;
-			vput(tvp);
-			/*
-			 * hodie natus est radici frater
-			 */
-			if (lvp == NULL) {
-				error = ENOENT;
-				goto out;
-			}
-			VREF(lvp);
-			error = vn_lock(lvp, LK_EXCLUSIVE | LK_RETRY, td);
-			if (error != 0) {
-				vrele(lvp);
-				lvp = NULL;
-				goto out;
-			}
-		}
-		error = linux_getcwd_scandir(&lvp, &uvp, &bp, bufp, td);
-		if (error)
-			goto out;
-#if DIAGNOSTIC		
-		if (lvp != NULL)
-			panic("getcwd: oops, forgot to null lvp");
-		if (bufp && (bp <= bufp)) {
-			panic("getcwd: oops, went back too far");
-		}
-#endif		
-		if (bp) 
-			*(--bp) = '/';
-		lvp = uvp;
-		uvp = NULL;
-		limit--;
-	} while ((lvp != rvp) && (limit > 0)); 
-
-out:
-	if (bpp)
-		*bpp = bp;
-	if (uvp)
-		vput(uvp);
-	if (lvp)
-		vput(lvp);
-	vrele(rvp);
-	return error;
-}
-
-
 /*
  * Find pathname of process's current directory.
- *
- * Use vfs vnode-to-name reverse cache; if that fails, fall back
- * to reading directory contents.
+ * Simply use the new, complete getcwd() implementation.
  */
 
 int
 linux_getcwd(struct thread *td, struct linux_getcwd_args *args)
 {
-	caddr_t bp, bend, path;
-	int error, len, lenused;
-
 #ifdef DEBUG
 	printf("Linux-emul(%ld): getcwd(%p, %ld)\n", (long)td->td_proc->p_pid,
 	       args->buf, (long)args->bufsize);
 #endif
-
-	len = args->bufsize;
-
-	if (len > MAXPATHLEN*4)
-		len = MAXPATHLEN*4;
-	else if (len < 2)
-		return ERANGE;
-
-	path = (char *)malloc(len, M_TEMP, M_WAITOK);
-
-	error = kern___getcwd(td, path, UIO_SYSSPACE, len);
-	if (!error) {
-		lenused = strlen(path) + 1;
-		if (lenused <= args->bufsize) {
-			td->td_retval[0] = lenused;
-			error = copyout(path, args->buf, lenused);
-		}
-		else
-			error = ERANGE;
-	} else {
-		bp = &path[len];
-		bend = bp;
-		*(--bp) = '\0';
-
-		/*
-		 * 5th argument here is "max number of vnodes to traverse".
-		 * Since each entry takes up at least 2 bytes in the output buffer,
-		 * limit it to N/2 vnodes for an N byte buffer.
-		 */
-
-		error = linux_getcwd_common (td->td_proc->p_fd->fd_cdir, NULL,
-		    &bp, path, len/2, GETCWD_CHECK_ACCESS, td);
-
-		if (error)
-			goto out;
-		lenused = bend - bp;
-		td->td_retval[0] = lenused;
-		/* put the result into user buffer */
-		error = copyout(bp, args->buf, lenused);
-	}
-out:
-	free(path, M_TEMP);
-	return (error);
+	return (kern___getcwd(td, args->buf, UIO_USERSPACE, args->bufsize));
 }
-


More information about the freebsd-arch mailing list