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);
 }

Reply via email to