svn commit: r247236 - head/lib/libc/gen

Jilles Tjoelker jilles at FreeBSD.org
Sun Feb 24 20:53:33 UTC 2013


Author: jilles
Date: Sun Feb 24 20:53:32 2013
New Revision: 247236
URL: http://svnweb.freebsd.org/changeset/base/247236

Log:
  libc/opendir: Improve behaviour of union uniquifier:
  
  * Reopen the directory using openat(fd, ".", ...) instead of opening the
    pathname again. This fixes a race condition where the meaning of the
    pathname changes and allows a reopen with fdopendir().
  * Always reopen the directory for union stacks, not only when DTF_REWIND
    is passed. Applications should be able to fchdir(dirfd(dir)) and
    *at(dirfd(dir), ...). DTF_REWIND now does nothing.

Modified:
  head/lib/libc/gen/opendir.c

Modified: head/lib/libc/gen/opendir.c
==============================================================================
--- head/lib/libc/gen/opendir.c	Sun Feb 24 19:49:02 2013	(r247235)
+++ head/lib/libc/gen/opendir.c	Sun Feb 24 20:53:32 2013	(r247236)
@@ -49,7 +49,7 @@ __FBSDID("$FreeBSD$");
 #include "gen-private.h"
 #include "telldir.h"
 
-static DIR * __opendir_common(int, const char *, int);
+static DIR * __opendir_common(int, int);
 
 /*
  * Open a directory.
@@ -78,7 +78,7 @@ fdopendir(int fd)
 	}
 	if (_fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
 		return (NULL);
-	return (__opendir_common(fd, NULL, DTF_HIDEW|DTF_NODUP));
+	return (__opendir_common(fd, DTF_HIDEW|DTF_NODUP));
 }
 
 DIR *
@@ -92,7 +92,7 @@ __opendir2(const char *name, int flags)
 	    O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC)) == -1)
 		return (NULL);
 
-	dir = __opendir_common(fd, name, flags);
+	dir = __opendir_common(fd, flags);
 	if (dir == NULL) {
 		saved_errno = errno;
 		_close(fd);
@@ -113,7 +113,7 @@ opendir_compar(const void *p1, const voi
  * Common routine for opendir(3), __opendir2(3) and fdopendir(3).
  */
 static DIR *
-__opendir_common(int fd, const char *name, int flags)
+__opendir_common(int fd, int flags)
 {
 	DIR *dirp;
 	int incr;
@@ -121,6 +121,8 @@ __opendir_common(int fd, const char *nam
 	int unionstack;
 	int fd2;
 
+	fd2 = -1;
+
 	if ((dirp = malloc(sizeof(DIR) + sizeof(struct _telldir))) == NULL)
 		return (NULL);
 
@@ -165,7 +167,22 @@ __opendir_common(int fd, const char *nam
 		 * entries into a buffer, sort the buffer, and
 		 * remove duplicate entries by setting the inode
 		 * number to zero.
+		 *
+		 * We reopen the directory because _getdirentries()
+		 * on a MNT_UNION mount modifies the open directory,
+		 * making it refer to the lower directory after the
+		 * upper directory's entries are exhausted.
+		 * This would otherwise break software that uses
+		 * the directory descriptor for fchdir or *at
+		 * functions, such as fts.c.
 		 */
+		if ((fd2 = _openat(fd, ".", O_RDONLY | O_CLOEXEC)) == -1) {
+			saved_errno = errno;
+			free(buf);
+			free(dirp);
+			errno = saved_errno;
+			return (NULL);
+		}
 
 		do {
 			/*
@@ -181,7 +198,7 @@ __opendir_common(int fd, const char *nam
 				ddptr = buf + (len - space);
 			}
 
-			n = _getdirentries(fd, ddptr, space, &dirp->dd_seek);
+			n = _getdirentries(fd2, ddptr, space, &dirp->dd_seek);
 			if (n > 0) {
 				ddptr += n;
 				space -= n;
@@ -191,25 +208,8 @@ __opendir_common(int fd, const char *nam
 		ddeptr = ddptr;
 		flags |= __DTF_READALL;
 
-		/*
-		 * Re-open the directory.
-		 * This has the effect of rewinding back to the
-		 * top of the union stack and is needed by
-		 * programs which plan to fchdir to a descriptor
-		 * which has also been read -- see fts.c.
-		 */
-		if (flags & DTF_REWIND) {
-			if ((fd2 = _open(name, O_RDONLY | O_DIRECTORY |
-			    O_CLOEXEC)) == -1) {
-				saved_errno = errno;
-				free(buf);
-				free(dirp);
-				errno = saved_errno;
-				return (NULL);
-			}
-			(void)_dup2(fd2, fd);
-			_close(fd2);
-		}
+		_close(fd2);
+		fd2 = -1;
 
 		/*
 		 * There is now a buffer full of (possibly) duplicate
@@ -293,7 +293,6 @@ __opendir_common(int fd, const char *nam
 		if (dirp->dd_buf == NULL)
 			goto fail;
 		dirp->dd_seek = 0;
-		flags &= ~DTF_REWIND;
 	}
 
 	dirp->dd_loc = 0;
@@ -310,6 +309,8 @@ __opendir_common(int fd, const char *nam
 
 fail:
 	saved_errno = errno;
+	if (fd2 != -1)
+		_close(fd2);
 	free(dirp);
 	errno = saved_errno;
 	return (NULL);


More information about the svn-src-head mailing list