Index: lib/libc/gen/fts.c
===================================================================
--- lib/libc/gen/fts.c	(revision 217719)
+++ lib/libc/gen/fts.c	(working copy)
@@ -141,10 +141,6 @@
 	/* Shush, GCC. */
 	tmp = NULL;
 
-	/* Logical walks turn on NOCHDIR; symbolic links are too hard. */
-	if (ISSET(FTS_LOGICAL))
-		SET(FTS_NOCHDIR);
-
 	/*
 	 * Start out with 1K of path space, and enough, in any case,
 	 * to hold the user's paths.
@@ -355,8 +351,10 @@
 		/* If skipped or crossed mount point, do post-order visit. */
 		if (instr == FTS_SKIP ||
 		    (ISSET(FTS_XDEV) && p->fts_dev != sp->fts_dev)) {
-			if (p->fts_flags & FTS_SYMFOLLOW)
+			if (p->fts_symfd != -1) {
 				(void)_close(p->fts_symfd);
+				p->fts_symfd = -1;
+			}
 			if (sp->fts_child) {
 				fts_lfree(sp->fts_child);
 				sp->fts_child = NULL;
@@ -385,6 +383,14 @@
 		 * FTS_STOP or the fts_info field of the node.
 		 */
 		if (sp->fts_child != NULL) {
+			if (p->fts_flags & FTS_DIRSYM && !ISSET(FTS_NOCHDIR) &&
+			    p->fts_symfd == -1) {
+				if ((p->fts_symfd = _open(".", O_RDONLY, 0)) < 0) {
+					p->fts_errno = errno;
+					p->fts_info = FTS_ERR;
+				} else
+					p->fts_flags |= FTS_SYMFOLLOW;
+			}
 			if (fts_safe_changedir(sp, p, -1, p->fts_accpath)) {
 				p->fts_errno = errno;
 				p->fts_flags |= FTS_DONTCHDIR;
@@ -474,15 +480,17 @@
 			SET(FTS_STOP);
 			return (NULL);
 		}
-	} else if (p->fts_flags & FTS_SYMFOLLOW) {
+	} else if (p->fts_symfd != -1) {
 		if (FCHDIR(sp, p->fts_symfd)) {
 			saved_errno = errno;
 			(void)_close(p->fts_symfd);
 			errno = saved_errno;
+			p->fts_symfd = -1;
 			SET(FTS_STOP);
 			return (NULL);
 		}
 		(void)_close(p->fts_symfd);
+		p->fts_symfd = -1;
 	} else if (!(p->fts_flags & FTS_DONTCHDIR) &&
 	    fts_safe_changedir(sp, p->fts_parent, -1, "..")) {
 		SET(FTS_STOP);
@@ -674,8 +682,8 @@
 		nlinks = 0;
 		/* Be quiet about nostat, GCC. */
 		nostat = 0;
-	} else if (ISSET(FTS_NOSTAT) && ISSET(FTS_PHYSICAL)) {
-		if (fts_ufslinks(sp, cur))
+	} else if (ISSET(FTS_NOSTAT)) {
+		if (ISSET(FTS_PHYSICAL) && fts_ufslinks(sp, cur))
 			nlinks = cur->fts_nlink - (ISSET(FTS_SEEDOT) ? 0 : 2);
 		else
 			nlinks = -1;
@@ -707,7 +715,19 @@
 	 */
 	cderrno = 0;
 	if (nlinks || type == BREAD) {
-		if (fts_safe_changedir(sp, cur, dirfd(dirp), NULL)) {
+		if (cur->fts_flags & FTS_DIRSYM && !ISSET(FTS_DONTCHDIR) &&
+		    cur->fts_symfd == -1) {
+			if ((cur->fts_symfd = _open(".", O_RDONLY, 0)) < 0) {
+				if (nlinks && type == BREAD)
+					cur->fts_errno = errno;
+				cur->fts_flags |= FTS_DONTCHDIR;
+				descend = 0;
+				cderrno = errno;
+			} else
+				cur->fts_flags |= FTS_SYMFOLLOW;
+		}
+		if (!(cur->fts_flags & FTS_DONTCHDIR) &&
+		    fts_safe_changedir(sp, cur, dirfd(dirp), NULL)) {
 			if (nlinks && type == BREAD)
 				cur->fts_errno = errno;
 			cur->fts_flags |= FTS_DONTCHDIR;
@@ -796,7 +816,8 @@
 		} else if (nlinks == 0
 #ifdef DT_DIR
 		    || (nostat &&
-		    dp->d_type != DT_DIR && dp->d_type != DT_UNKNOWN)
+		    dp->d_type != DT_DIR && dp->d_type != DT_UNKNOWN &&
+		    (dp->d_type != DT_LNK || ISSET(FTS_PHYSICAL)))
 #endif
 		    ) {
 			p->fts_accpath =
@@ -883,7 +904,7 @@
 	FTSENT *t;
 	dev_t dev;
 	ino_t ino;
-	struct stat *sbp, sb;
+	struct stat *sbp, sb, sb2;
 	int saved_errno;
 
 	/* If user needs stat info, stat buffer already allocated. */
@@ -923,6 +944,20 @@
 
 	if (S_ISDIR(sbp->st_mode)) {
 		/*
+		 * Check if this is actually a symlink to a directory.
+		 * If so, record this fact so we save a file descriptor
+		 * to get back instead of using "..".
+		 */
+		if (ISSET(FTS_LOGICAL) && !follow) {
+			if (lstat(p->fts_accpath, &sb2)) {
+				p->fts_errno = errno;
+				goto err;
+			}
+			if (S_ISLNK(sb2.st_mode))
+				p->fts_flags |= FTS_DIRSYM;
+		}
+
+		/*
 		 * Set the device/inode.  Used to find cycles and check for
 		 * crossing mount points.  Also remember the link count, used
 		 * in fts_build to limit the number of stat calls.  It is
@@ -1046,6 +1081,7 @@
 	p->fts_namelen = namelen;
 	p->fts_path = sp->fts_path;
 	p->fts_errno = 0;
+	p->fts_symfd = -1;
 	p->fts_flags = 0;
 	p->fts_instr = FTS_NOINSTR;
 	p->fts_number = 0;
Index: include/fts.h
===================================================================
--- include/fts.h	(revision 217719)
+++ include/fts.h	(working copy)
@@ -102,6 +102,7 @@
 #define	FTS_DONTCHDIR	 0x01		/* don't chdir .. to the parent */
 #define	FTS_SYMFOLLOW	 0x02		/* followed a symlink to get here */
 #define	FTS_ISW		 0x04		/* this is a whiteout object */
+#define	FTS_DIRSYM	 0x08		/* this is a symlink to a directory */
 	unsigned fts_flags;		/* private flags for FTSENT structure */
 
 #define	FTS_AGAIN	 1		/* read node again */
