On Thu, Jul 14, 2011 at 05:12:08PM -0700, Matthew Dempsky wrote:
> (It would be possible to change things slightly to avoid the two
> fcntl(2) system calls in fdopendir(3) when called from opendir(3), but
> I don't think it's worth the complexity.)
guenther@ thought this would be a good idea to do now, and oga@
pointed out that POSIX says fdopendir(3) is supposed to check the
current file offset of the passed file descriptor. (I have *no* idea
how to portably utilize this feature, but I've tested that the diff
below behaves as expected.)
How's this look?
Index: include/dirent.h
===================================================================
RCS file: /home/mdempsky/anoncvs/cvs/src/include/dirent.h,v
retrieving revision 1.23
diff -u -p -r1.23 dirent.h
--- include/dirent.h 14 Jul 2011 02:16:00 -0000 1.23
+++ include/dirent.h 14 Jul 2011 03:05:54 -0000
@@ -97,6 +97,9 @@ typedef void * DIR;
#ifndef _KERNEL
__BEGIN_DECLS
DIR *opendir(const char *);
+#if __POSIX_VISIBLE >= 200809
+DIR *fdopendir(int);
+#endif
struct dirent *readdir(DIR *);
void rewinddir(DIR *);
int closedir(DIR *);
Index: lib/libc/gen/opendir.c
===================================================================
RCS file: /home/mdempsky/anoncvs/cvs/src/lib/libc/gen/opendir.c,v
retrieving revision 1.21
diff -u -p -r1.21 opendir.c
--- lib/libc/gen/opendir.c 14 Jul 2011 02:16:00 -0000 1.21
+++ lib/libc/gen/opendir.c 16 Jul 2011 19:39:00 -0000
@@ -28,53 +28,80 @@
* SUCH DAMAGE.
*/
-#include <sys/param.h>
-#include <sys/mount.h>
+#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
-#include <limits.h>
#include <stdlib.h>
-#include <string.h>
#include <unistd.h>
#include "telldir.h"
+static DIR *__fdopendir(int fd, off_t offset);
+
/*
- * Open a directory.
+ * Open a directory specified by name.
*/
DIR *
opendir(const char *name)
{
DIR *dirp;
int fd;
- struct stat sb;
- int pageoffset;
- if ((fd = open(name, O_RDONLY | O_NONBLOCK)) == -1)
+ if ((fd = open(name, O_RDONLY | O_DIRECTORY | O_CLOEXEC)) == -1)
return (NULL);
- if (fstat(fd, &sb)) {
+ dirp = __fdopendir(fd, 0);
+ if (dirp == NULL)
close(fd);
+ return (dirp);
+}
+
+/*
+ * Open a directory specified by file descriptor.
+ */
+DIR *
+fdopendir(int fd)
+{
+ DIR *dirp;
+ int flags;
+ off_t offset;
+
+ if ((flags = fcntl(fd, F_GETFL)) == -1)
return (NULL);
+ if ((flags & O_ACCMODE) != O_RDONLY && (flags & O_ACCMODE) != O_RDWR) {
+ errno = EBADF;
+ return (NULL);
+ }
+ if ((offset = lseek(fd, 0, SEEK_CUR)) == -1)
+ return (NULL);
+ dirp = __fdopendir(fd, offset);
+ if (dirp != NULL) {
+ /*
+ * POSIX doesn't require fdopendir() to set
+ * FD_CLOEXEC, so it's okay for this to fail.
+ */
+ (void)fcntl(fd, F_SETFD, FD_CLOEXEC);
}
+ return (dirp);
+}
+
+static DIR *
+__fdopendir(int fd, off_t offset)
+{
+ DIR *dirp;
+ struct stat sb;
+ int pageoffset;
+
+ if (fstat(fd, &sb))
+ return (NULL);
if (!S_ISDIR(sb.st_mode)) {
- close(fd);
errno = ENOTDIR;
return (NULL);
}
- if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1 ||
- (dirp = malloc(sizeof(DIR) + sizeof(struct _telldir))) == NULL) {
- close(fd);
+ if ((dirp = malloc(sizeof(DIR) + sizeof(struct _telldir))) == NULL)
return (NULL);
- }
-
- dirp->dd_td = (struct _telldir *)((char *)dirp + sizeof(DIR));
- dirp->dd_td->td_locs = NULL;
- dirp->dd_td->td_sz = 0;
- dirp->dd_td->td_loccnt = 0;
- dirp->dd_td->td_last = 0;
/*
* Use a buffer that is page aligned.
@@ -86,10 +113,15 @@ opendir(const char *name)
dirp->dd_buf = malloc((size_t)dirp->dd_len);
if (dirp->dd_buf == NULL) {
free(dirp);
- close(fd);
return (NULL);
}
+ dirp->dd_td = (struct _telldir *)((char *)dirp + sizeof(DIR));
+ dirp->dd_td->td_locs = NULL;
+ dirp->dd_td->td_sz = 0;
+ dirp->dd_td->td_loccnt = 0;
+ dirp->dd_td->td_last = 0;
+
dirp->dd_seek = 0;
dirp->dd_loc = 0;
dirp->dd_fd = fd;
@@ -100,6 +132,12 @@ opendir(const char *name)
* Set up seek point for rewinddir.
*/
dirp->dd_rewind = telldir(dirp);
+
+ /*
+ * Store our actual seek offset. Must do this *after* setting
+ * dd_rewind = telldir() so that rewinddir() works correctly.
+ */
+ dirp->dd_seek = offset;
return (dirp);
}