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.
This commit is contained in:
parent
60edaad227
commit
b4ce52f66c
@ -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 void *p2)
|
||||
* 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 *name, int flags)
|
||||
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 *name, int flags)
|
||||
* 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 *name, int flags)
|
||||
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 *name, int flags)
|
||||
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 *name, int flags)
|
||||
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 *name, int flags)
|
||||
|
||||
fail:
|
||||
saved_errno = errno;
|
||||
if (fd2 != -1)
|
||||
_close(fd2);
|
||||
free(dirp);
|
||||
errno = saved_errno;
|
||||
return (NULL);
|
||||
|
Loading…
x
Reference in New Issue
Block a user