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);
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to