bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

fdopendir: Fix fd leak and test failure on native Windows


From: Bruno Haible
Subject: fdopendir: Fix fd leak and test failure on native Windows
Date: Thu, 27 Apr 2023 01:44:10 +0200

On native Windows, I see a test failure:

FAIL: test-fdopendir
====================

../../gltests/test-fdopendir.c:76: assertion 'dup2 (fd, fd) == -1' failed
FAIL test-fdopendir.exe (exit status: 3)

The cause of the problem is that POSIX [1] says:
  "Upon calling closedir() the file descriptor shall be closed."

That is, the fdopendir() call has to associate the argument fd with the
DIR object in such a way that fd gets closed when closedir() gets called
on the DIR object, but no earlier! — see the example in [1]:
  closedir(d); // note this implicitly closes dfd

So far, on mingw and MSVC, the DIR types (provided by mingw and gnulib,
respectively) don't contain a field for the file descriptor. For
implementing
  opendir
  readdir
  rewinddir
  closedir
this was sufficient. dirfd() always returned -1 in such an implementation.
But for fdopendir() it is not sufficient.

It's not only a test failure; it's also a file descriptor leak: For
every pair of fdopendir/closedir calls, a file descriptor gets allocated
and not closed.

This patch fixes it, by adding a 'fd_to_close' member.

For kLIBC, KO Myung-Hun had added a similar mechanism, by keeping a list
of assocations between DIR objects and file descriptors. I believe
overriding the DIR type is the more efficient way to handle this, but
I have left the kLIBC code as it is. They can adapt the kLIBC code to
be like the mingw code, if they like to.

[1] https://pubs.opengroup.org/onlinepubs/9699919799/functions/fdopendir.html


2023-04-26  Bruno Haible  <bruno@clisp.org>

        fdopendir: Fix fd leak and test failure on native Windows.
        --
        * lib/dirent-private.h: On mingw, define 'struct gl_directory' as a
        wrapper around the original DIR. On MSVC, add an 'fd_to_close' field to
        'struct gl_directory'.
        * lib/dirent.in.h (DIR): Define when DIR_HAS_FD_MEMBER is 0, i.e. on
        both mingw and MSVC.
        (GNULIB_defined_DIR): New macro.
        (opendir): Avoid incompatible redeclaration.
        (readdir): Consider REPLACE_READDIR.
        (rewinddir): Consider REPLACE_REWINDDIR.
        * m4/dirent_h.m4 (gl_DIRENT_DIR): New macro.
        (gl_DIRENT_H): Invoke it.
        (gl_DIRENT_H_DEFAULTS): Initialize REPLACE_READDIR, REPLACE_REWINDDIR.
        * modules/dirent (Makefile.am): Substitute DIR_HAS_FD_MEMBER,
        REPLACE_READDIR, REPLACE_REWINDDIR.
        --
        * lib/dirfd.c (dirfd): If GNULIB_defined_DIR, just use the
        'fd_to_close' field.
        * m4/dirfd.m4 (gl_FUNC_DIRFD): Set HAVE_DIRFD. Don't set REPLACE_DIRFD
        to 1 if HAVE_DIRFD is 0. If DIR_HAS_FD_MEMBER is 0, ensure dirfd.c gets
        compiled.
        * modules/dirfd (Files): Add lib/dirent-private.h.
        (Depends-on, configure.ac): Simplify conditions.
        --
        * lib/closedir.c: Include <stdlib.h> always, for free().
        (closedir): If GNULIB_defined_DIR, arrange to call close(dirfd(dirp)) at
        the end. On mingw, call free() of dirp. Prefer testing HAVE_DIRENT_H,
        for consistency with dirent.h.
        * m4/closedir.m4 (gl_FUNC_CLOSEDIR): Don't set REPLACE_CLOSEDIR to 1 if
        HAVE_CLOSEDIR is 0. If DIR_HAS_FD_MEMBER is 0, ensure closedir.c gets
        compiled.
        --
        * lib/opendir.c: Include <stdlib.h> always. Include <string.h>.
        (opendir): On mingw, allocate the 'struct gl_directory' through malloc.
        If GNULIB_defined_DIR, set the 'fd_to_close' field to -1. Prefer
        testing HAVE_DIRENT_H, for consistency with dirent.h.
        * m4/opendir.m4 (gl_FUNC_OPENDIR): Don't set REPLACE_OPENDIR to 1 if
        HAVE_OPENDIR is 0. If DIR_HAS_FD_MEMBER is 0, ensure opendir.c gets
        compiled.
        --
        * lib/fdopendir.c (fdopendir): If GNULIB_defined_DIR, use a simple
        implementation based on opendir and the fchdir module. If __KLIBC__,
        don't define unused auxiliary functions.
        * modules/fdopendir (Files): Add lib/dirent-private.h.
        --
        * lib/readdir.c (readdir): On mingw, redirect to the original readdir
        function. Prefer testing HAVE_DIRENT_H, for consistency with dirent.h.
        * m4/readdir.m4 (gl_FUNC_READDIR): If DIR_HAS_FD_MEMBER is 0, ensure
        readdir.c gets compiled.
        * modules/readdir (configure.ac): Consider REPLACE_READDIR.
        --
        * lib/rewinddir.c (rewinddir): On mingw, redirect to the original
        rewinddir function. Prefer testing HAVE_DIRENT_H, for consistency with
        dirent.h.
        * m4/rewinddir.m4 (gl_FUNC_REWINDDIR): If DIR_HAS_FD_MEMBER is 0, ensure
        rewinddir.c gets compiled.
        * modules/rewinddir (configure.ac): Consider REPLACE_REWINDDIR.
        --
        * lib/fchdir.c (dir_info_t): Remove a FIXME.

diff --git a/lib/closedir.c b/lib/closedir.c
index 0e004afeca..3777e9f70d 100644
--- a/lib/closedir.c
+++ b/lib/closedir.c
@@ -23,31 +23,37 @@
 # include <unistd.h>
 #endif
 
+#include <stdlib.h>
+
 #if HAVE_CLOSEDIR
 
 /* Override closedir(), to keep track of the open file descriptors.
    Needed because there is a function dirfd().  */
 
-#else
-
-# include <stdlib.h>
+#endif
 
+#if GNULIB_defined_DIR
 # include "dirent-private.h"
-
 #endif
 
 int
 closedir (DIR *dirp)
+#undef closedir
 {
-# if REPLACE_FCHDIR || REPLACE_DIRFD
+#if GNULIB_defined_DIR || REPLACE_FCHDIR || defined __KLIBC__
   int fd = dirfd (dirp);
-# endif
+#endif
   int retval;
 
-#if HAVE_CLOSEDIR
-# undef closedir
+#if HAVE_DIRENT_H                       /* equivalent to HAVE_CLOSEDIR */
 
+# if GNULIB_defined_DIR
+  retval = closedir (dirp->real_dirp);
+  if (retval >= 0)
+    free (dirp);
+# else
   retval = closedir (dirp);
+# endif
 
 # ifdef __KLIBC__
   if (!retval)
@@ -63,9 +69,13 @@ closedir (DIR *dirp)
 
 #endif
 
-#if REPLACE_FCHDIR
+#if GNULIB_defined_DIR
+  if (retval >= 0)
+    close (fd);
+#elif REPLACE_FCHDIR
   if (retval >= 0)
     _gl_unregister_fd (fd);
 #endif
+
   return retval;
 }
diff --git a/lib/dirent-private.h b/lib/dirent-private.h
index f454e0e39a..012b683719 100644
--- a/lib/dirent-private.h
+++ b/lib/dirent-private.h
@@ -17,15 +17,36 @@
 #ifndef _DIRENT_PRIVATE_H
 #define _DIRENT_PRIVATE_H 1
 
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
+#if HAVE_DIRENT_H                       /* mingw */
+
+# undef DIR
+
+struct gl_directory
+{
+  /* File descriptor to close during closedir().
+     Needed for implementing fdopendir().  */
+  int fd_to_close;
+  /* Pointer to the real DIR.  */
+  DIR *real_dirp;
+};
+
+/* Restore definition from dirent.h.  */
+# define DIR struct gl_directory
+
+#else                                   /* MSVC */
+
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
 
 /* Don't assume that UNICODE is not defined.  */
-#undef WIN32_FIND_DATA
-#define WIN32_FIND_DATA WIN32_FIND_DATAA
+# undef WIN32_FIND_DATA
+# define WIN32_FIND_DATA WIN32_FIND_DATAA
 
 struct gl_directory
 {
+  /* File descriptor to close during closedir().
+     Needed for implementing fdopendir().  */
+  int fd_to_close;
   /* Status, or error code to produce in next readdir() call.
      -2 means the end of the directory is already reached,
      -1 means the entry was already filled by FindFirstFile,
@@ -41,4 +62,6 @@ struct gl_directory
   char dir_name_mask[1];
 };
 
+#endif
+
 #endif /* _DIRENT_PRIVATE_H */
diff --git a/lib/dirent.in.h b/lib/dirent.in.h
index 40dea1a80e..d409a031ec 100644
--- a/lib/dirent.in.h
+++ b/lib/dirent.in.h
@@ -56,11 +56,24 @@ struct dirent
 #  define DT_LNK    10          /* symbolic link */
 #  define DT_SOCK   12          /* socket */
 #  define DT_WHT    14          /* whiteout */
-typedef struct gl_directory DIR;
 #  define GNULIB_defined_struct_dirent 1
 # endif
 #endif
 
+#if !@DIR_HAS_FD_MEMBER@
+# if !GNULIB_defined_DIR
+/* struct gl_directory is a type with a field 'int fd_to_close'.
+   It is needed for implementing fdopendir().  */
+struct gl_directory;
+#  if @HAVE_DIRENT_H@
+#   define DIR struct gl_directory
+#  else
+typedef struct gl_directory DIR;
+#  endif
+#  define GNULIB_defined_DIR 1
+# endif
+#endif
+
 /* _GL_ATTRIBUTE_DEALLOC (F, I) declares that the function returns pointers
    that can be freed by passing them as the Ith argument to the
    function F.  */
@@ -149,7 +162,7 @@ _GL_CXXALIAS_SYS (opendir, DIR *, (const char *dir_name));
 # endif
 _GL_CXXALIASWARN (opendir);
 #else
-# if @GNULIB_CLOSEDIR@ && __GNUC__ >= 11 && !defined opendir
+# if @GNULIB_CLOSEDIR@ && !GNULIB_defined_DIR && __GNUC__ >= 11 && !defined 
opendir
 /* For -Wmismatched-dealloc: Associate opendir with closedir or
    rpl_closedir.  */
 _GL_FUNCDECL_SYS (opendir, DIR *,
@@ -167,10 +180,19 @@ _GL_WARN_ON_USE (opendir, "opendir is not portable - "
 #endif
 
 #if @GNULIB_READDIR@
-# if !@HAVE_READDIR@
+# if @REPLACE_READDIR@
+#  if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+#   undef readdir
+#   define readdir rpl_readdir
+#  endif
+_GL_FUNCDECL_RPL (readdir, struct dirent *, (DIR *dirp) _GL_ARG_NONNULL ((1)));
+_GL_CXXALIAS_RPL (readdir, struct dirent *, (DIR *dirp));
+# else
+#  if !@HAVE_READDIR@
 _GL_FUNCDECL_SYS (readdir, struct dirent *, (DIR *dirp) _GL_ARG_NONNULL ((1)));
-# endif
+#  endif
 _GL_CXXALIAS_SYS (readdir, struct dirent *, (DIR *dirp));
+# endif
 _GL_CXXALIASWARN (readdir);
 #elif defined GNULIB_POSIXCHECK
 # undef readdir
@@ -181,10 +203,19 @@ _GL_WARN_ON_USE (readdir, "readdir is not portable - "
 #endif
 
 #if @GNULIB_REWINDDIR@
-# if !@HAVE_REWINDDIR@
+# if @REPLACE_REWINDDIR@
+#  if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+#   undef rewinddir
+#   define rewinddir rpl_rewinddir
+#  endif
+_GL_FUNCDECL_RPL (rewinddir, void, (DIR *dirp) _GL_ARG_NONNULL ((1)));
+_GL_CXXALIAS_RPL (rewinddir, void, (DIR *dirp));
+# else
+#  if !@HAVE_REWINDDIR@
 _GL_FUNCDECL_SYS (rewinddir, void, (DIR *dirp) _GL_ARG_NONNULL ((1)));
-# endif
+#  endif
 _GL_CXXALIAS_SYS (rewinddir, void, (DIR *dirp));
+# endif
 _GL_CXXALIASWARN (rewinddir);
 #elif defined GNULIB_POSIXCHECK
 # undef rewinddir
diff --git a/lib/dirfd.c b/lib/dirfd.c
index b2b1d25cdb..75b2163c35 100644
--- a/lib/dirfd.c
+++ b/lib/dirfd.c
@@ -22,6 +22,10 @@
 #include <dirent.h>
 #include <errno.h>
 
+#if GNULIB_defined_DIR
+# include "dirent-private.h"
+#endif
+
 #ifdef __KLIBC__
 # include <stdlib.h>
 # include <io.h>
@@ -78,11 +82,17 @@ _gl_unregister_dirp_fd (int fd)
 int
 dirfd (DIR *dir_p)
 {
+#if GNULIB_defined_DIR
+  int fd = dir_p->fd_to_close;
+  if (fd == -1)
+    errno = EINVAL;
+  return fd;
+#else
   int fd = DIR_TO_FD (dir_p);
   if (fd == -1)
-#ifndef __KLIBC__
+# ifndef __KLIBC__
     errno = ENOTSUP;
-#else
+# else
     {
       struct dirp_fd_list *dirp_fd;
 
@@ -92,7 +102,8 @@ dirfd (DIR *dir_p)
 
       errno = EINVAL;
     }
-#endif
+# endif
 
   return fd;
+#endif
 }
diff --git a/lib/fchdir.c b/lib/fchdir.c
index 4e62091605..880b137ec6 100644
--- a/lib/fchdir.c
+++ b/lib/fchdir.c
@@ -48,7 +48,6 @@
 typedef struct
 {
   char *name;       /* Absolute name of the directory, or NULL.  */
-  /* FIXME - add a DIR* member to make dirfd possible on mingw?  */
 } dir_info_t;
 static dir_info_t *dirs;
 static size_t dirs_allocated;
diff --git a/lib/fdopendir.c b/lib/fdopendir.c
index aa841e3e81..0f43d6ff34 100644
--- a/lib/fdopendir.c
+++ b/lib/fdopendir.c
@@ -25,44 +25,27 @@
 
 #if !HAVE_FDOPENDIR
 
-# include "openat.h"
-# include "openat-priv.h"
-# include "save-cwd.h"
+# if GNULIB_defined_DIR
+/* We are in control of the file descriptor of a DIR.  */
 
-# if GNULIB_DIRENT_SAFER
-#  include "dirent--.h"
-# endif
-
-# ifndef REPLACE_FCHDIR
-#  define REPLACE_FCHDIR 0
-# endif
-
-static DIR *fdopendir_with_dup (int, int, struct saved_cwd const *);
-static DIR *fd_clone_opendir (int, struct saved_cwd const *);
-
-/* Replacement for POSIX fdopendir.
+#  include "dirent-private.h"
 
-   First, try to simulate it via opendir ("/proc/self/fd/...").  Failing
-   that, simulate it by using fchdir metadata, or by doing
-   save_cwd/fchdir/opendir(".")/restore_cwd.
-   If either the save_cwd or the restore_cwd fails (relatively unlikely),
-   then give a diagnostic and exit nonzero.
-
-   If successful, the resulting stream is based on FD in
-   implementations where streams are based on file descriptors and in
-   applications where no other thread or signal handler allocates or
-   frees file descriptors.  In other cases, consult dirfd on the result
-   to find out whether FD is still being used.
+#  if !REPLACE_FCHDIR
+#   error "unexpected configuration: GNULIB_defined_DIR but fchdir not 
replaced"
+#  endif
 
-   Otherwise, this function works just like POSIX fdopendir.
+DIR *
+fdopendir (int fd)
+{
+  char const *name = _gl_directory_name (fd);
+  DIR *dirp = name ? opendir (name) : NULL;
+  if (dirp != NULL)
+    dirp->fd_to_close = fd;
+  return dirp;
+}
 
-   W A R N I N G:
+# elif defined __KLIBC__
 
-   Unlike other fd-related functions, this one places constraints on FD.
-   If this function returns successfully, FD is under control of the
-   dirent.h system, and the caller should not close or modify the state of
-   FD other than by the dirent.h functions.  */
-# ifdef __KLIBC__
 #  include <InnoTekLIBC/backend.h>
 
 DIR *
@@ -96,7 +79,48 @@ fdopendir (int fd)
 
   return dirp;
 }
+
 # else
+/* We are not in control of the file descriptor of a DIR, and therefore have to
+   play tricks with file descriptors before and after a call to opendir().  */
+
+#  include "openat.h"
+#  include "openat-priv.h"
+#  include "save-cwd.h"
+
+#  if GNULIB_DIRENT_SAFER
+#   include "dirent--.h"
+#  endif
+
+#  ifndef REPLACE_FCHDIR
+#   define REPLACE_FCHDIR 0
+#  endif
+
+static DIR *fdopendir_with_dup (int, int, struct saved_cwd const *);
+static DIR *fd_clone_opendir (int, struct saved_cwd const *);
+
+/* Replacement for POSIX fdopendir.
+
+   First, try to simulate it via opendir ("/proc/self/fd/...").  Failing
+   that, simulate it by using fchdir metadata, or by doing
+   save_cwd/fchdir/opendir(".")/restore_cwd.
+   If either the save_cwd or the restore_cwd fails (relatively unlikely),
+   then give a diagnostic and exit nonzero.
+
+   If successful, the resulting stream is based on FD in
+   implementations where streams are based on file descriptors and in
+   applications where no other thread or signal handler allocates or
+   frees file descriptors.  In other cases, consult dirfd on the result
+   to find out whether FD is still being used.
+
+   Otherwise, this function works just like POSIX fdopendir.
+
+   W A R N I N G:
+
+   Unlike other fd-related functions, this one places constraints on FD.
+   If this function returns successfully, FD is under control of the
+   dirent.h system, and the caller should not close or modify the state of
+   FD other than by the dirent.h functions.  */
 DIR *
 fdopendir (int fd)
 {
@@ -119,7 +143,6 @@ fdopendir (int fd)
 
   return dir;
 }
-# endif
 
 /* Like fdopendir, except that if OLDER_DUPFD is not -1, it is known
    to be a dup of FD which is less than FD - 1 and which will be
@@ -188,7 +211,7 @@ fd_clone_opendir (int fd, struct saved_cwd const *cwd)
           if (proc_file != buf)
             free (proc_file);
         }
-# if REPLACE_FCHDIR
+#  if REPLACE_FCHDIR
       if (! dir && EXPECTED_ERRNO (saved_errno))
         {
           char const *name = _gl_directory_name (fd);
@@ -203,7 +226,7 @@ fd_clone_opendir (int fd, struct saved_cwd const *cwd)
 
           return dp;
         }
-# endif
+#  endif
       errno = saved_errno;
       return dir;
     }
@@ -223,6 +246,8 @@ fd_clone_opendir (int fd, struct saved_cwd const *cwd)
     }
 }
 
+# endif
+
 #else /* HAVE_FDOPENDIR */
 
 # include <errno.h>
diff --git a/lib/opendir.c b/lib/opendir.c
index bbe2c1dbcd..ceb0e2829f 100644
--- a/lib/opendir.c
+++ b/lib/opendir.c
@@ -29,13 +29,17 @@
 
 #else
 
-# include <stdlib.h>
-
-# include "dirent-private.h"
 # include "filename.h"
 
 #endif
 
+#include <stdlib.h>
+#include <string.h>
+
+#if GNULIB_defined_DIR
+# include "dirent-private.h"
+#endif
+
 #if REPLACE_FCHDIR
 # include <unistd.h>
 #endif
@@ -57,14 +61,37 @@
 
 DIR *
 opendir (const char *dir_name)
+#undef opendir
 {
-#if HAVE_OPENDIR
-# undef opendir
+#if HAVE_DIRENT_H                       /* equivalent to HAVE_OPENDIR */
   DIR *dirp;
 
+# if GNULIB_defined_DIR
+#  undef DIR
+
+  dirp = (struct gl_directory *) malloc (sizeof (struct gl_directory));
+  if (dirp == NULL)
+    {
+      errno = ENOMEM;
+      return NULL;
+    }
+
+  DIR *real_dirp = opendir (dir_name);
+  if (real_dirp == NULL)
+    {
+      int saved_errno = errno;
+      free (dirp);
+      errno = saved_errno;
+      return NULL;
+    }
+
+  dirp->fd_to_close = -1;
+  dirp->real_dirp = real_dirp;
+# else
   dirp = opendir (dir_name);
   if (dirp == NULL)
     return NULL;
+# endif
 
 # ifdef __KLIBC__
   {
@@ -82,6 +109,7 @@ opendir (const char *dir_name)
       }
   }
 # endif
+
 #else
 
   char dir_name_mask[MAX_PATH + 1 + 1 + 1];
@@ -154,6 +182,7 @@ opendir (const char *dir_name)
       errno = ENOMEM;
       return NULL;
     }
+  dirp->fd_to_close = -1;
   dirp->status = status;
   dirp->current = current;
   if (status == -1)
diff --git a/lib/readdir.c b/lib/readdir.c
index 36eac8afbb..04dbba21bc 100644
--- a/lib/readdir.c
+++ b/lib/readdir.c
@@ -22,7 +22,9 @@
 #include <errno.h>
 #include <stddef.h>
 
-#include "dirent-private.h"
+#if GNULIB_defined_DIR
+# include "dirent-private.h"
+#endif
 
 /* Don't assume that UNICODE is not defined.  */
 #undef FindNextFile
@@ -30,7 +32,11 @@
 
 struct dirent *
 readdir (DIR *dirp)
+#undef readdir
 {
+#if HAVE_DIRENT_H                       /* equivalent to HAVE_READDIR */
+  return readdir (dirp->real_dirp);
+#else
   char type;
   struct dirent *result;
 
@@ -99,4 +105,5 @@ readdir (DIR *dirp)
   result->d_type = type;
 
   return result;
+#endif
 }
diff --git a/lib/rewinddir.c b/lib/rewinddir.c
index 9988dc7be3..f200cc53c0 100644
--- a/lib/rewinddir.c
+++ b/lib/rewinddir.c
@@ -21,7 +21,9 @@
 
 #include <errno.h>
 
-#include "dirent-private.h"
+#if GNULIB_defined_DIR
+# include "dirent-private.h"
+#endif
 
 /* Don't assume that UNICODE is not defined.  */
 #undef FindFirstFile
@@ -29,7 +31,11 @@
 
 void
 rewinddir (DIR *dirp)
+#undef rewinddir
 {
+#if HAVE_DIRENT_H                       /* equivalent to HAVE_REWINDDIR */
+  rewinddir (dirp->real_dirp);
+#else
   /* Like in closedir().  */
   if (dirp->current != INVALID_HANDLE_VALUE)
     FindClose (dirp->current);
@@ -50,4 +56,5 @@ rewinddir (DIR *dirp)
           break;
         }
     }
+#endif
 }
diff --git a/m4/closedir.m4 b/m4/closedir.m4
index 9c15354c3d..7e702def25 100644
--- a/m4/closedir.m4
+++ b/m4/closedir.m4
@@ -1,4 +1,4 @@
-# closedir.m4 serial 6
+# closedir.m4 serial 7
 dnl Copyright (C) 2011-2023 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -12,20 +12,23 @@ AC_DEFUN([gl_FUNC_CLOSEDIR]
   AC_CHECK_FUNCS([closedir])
   if test $ac_cv_func_closedir = no; then
     HAVE_CLOSEDIR=0
-  fi
-  dnl Replace closedir() for supporting the gnulib-defined fchdir() function,
-  dnl to keep fchdir's bookkeeping up-to-date.
-  m4_ifdef([gl_FUNC_FCHDIR], [
-    gl_TEST_FCHDIR
-    if test $HAVE_FCHDIR = 0; then
-      if test $HAVE_CLOSEDIR = 1; then
+  else
+    dnl Replace closedir() on native Windows, to support fdopendir().
+    AC_REQUIRE([gl_DIRENT_DIR])
+    if test $DIR_HAS_FD_MEMBER = 0; then
+      REPLACE_CLOSEDIR=1
+    fi
+    dnl Replace closedir() for supporting the gnulib-defined dirfd() function.
+    case $host_os in
+      os2*) REPLACE_CLOSEDIR=1 ;;
+    esac
+    dnl Replace closedir() for supporting the gnulib-defined fchdir() function,
+    dnl to keep fchdir's bookkeeping up-to-date.
+    m4_ifdef([gl_FUNC_FCHDIR], [
+      gl_TEST_FCHDIR
+      if test $HAVE_FCHDIR = 0; then
         REPLACE_CLOSEDIR=1
       fi
-    fi
-  ])
-  dnl Replace closedir() for supporting the gnulib-defined dirfd() function.
-  case $host_os,$HAVE_CLOSEDIR in
-    os2*,1)
-      REPLACE_CLOSEDIR=1;;
-  esac
+    ])
+  fi
 ])
diff --git a/m4/dirent_h.m4 b/m4/dirent_h.m4
index 2a232a7622..b6c189c0d9 100644
--- a/m4/dirent_h.m4
+++ b/m4/dirent_h.m4
@@ -1,4 +1,4 @@
-# dirent_h.m4 serial 19
+# dirent_h.m4 serial 20
 dnl Copyright (C) 2008-2023 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -21,12 +21,29 @@ AC_DEFUN_ONCE([gl_DIRENT_H]
   fi
   AC_SUBST([HAVE_DIRENT_H])
 
+  gl_DIRENT_DIR
+
   dnl Check for declarations of anything we want to poison if the
   dnl corresponding gnulib module is not in use.
   gl_WARN_ON_USE_PREPARE([[#include <dirent.h>
     ]], [alphasort closedir dirfd fdopendir opendir readdir rewinddir scandir])
 ])
 
+dnl Determine whether <dirent.h> needs to override the DIR type.
+AC_DEFUN_ONCE([gl_DIRENT_DIR],
+[
+  dnl Set DIR_HAS_FD_MEMBER if dirfd() works, i.e. not always returns -1,
+  dnl or has the __KLIBC__ workaround as in lib/dirfd.c.
+  dnl We could use the findings from gl_FUNC_DIRFD and gl_PREREQ_DIRFD, but
+  dnl it's simpler since we know the affected platforms.
+  AC_REQUIRE([AC_CANONICAL_HOST])
+  case "$host_os" in
+    mingw*) DIR_HAS_FD_MEMBER=0 ;;
+    *)      DIR_HAS_FD_MEMBER=1 ;;
+  esac
+  AC_SUBST([DIR_HAS_FD_MEMBER])
+])
+
 # gl_DIRENT_MODULE_INDICATOR([modulename])
 # sets the shell variable that indicates the presence of the given module
 # to a C preprocessor expression that will evaluate to 1.
@@ -73,6 +90,8 @@ AC_DEFUN([gl_DIRENT_H_DEFAULTS]
   HAVE_SCANDIR=1;       AC_SUBST([HAVE_SCANDIR])
   HAVE_ALPHASORT=1;     AC_SUBST([HAVE_ALPHASORT])
   REPLACE_OPENDIR=0;    AC_SUBST([REPLACE_OPENDIR])
+  REPLACE_READDIR=0;    AC_SUBST([REPLACE_READDIR])
+  REPLACE_REWINDDIR=0;  AC_SUBST([REPLACE_REWINDDIR])
   REPLACE_CLOSEDIR=0;   AC_SUBST([REPLACE_CLOSEDIR])
   REPLACE_DIRFD=0;      AC_SUBST([REPLACE_DIRFD])
   REPLACE_FDOPENDIR=0;  AC_SUBST([REPLACE_FDOPENDIR])
diff --git a/m4/dirfd.m4 b/m4/dirfd.m4
index 2135535042..d1ee2c7f61 100644
--- a/m4/dirfd.m4
+++ b/m4/dirfd.m4
@@ -1,4 +1,4 @@
-# serial 26   -*- Autoconf -*-
+# serial 27   -*- Autoconf -*-
 
 dnl Find out how to get the file descriptor associated with an open DIR*.
 
@@ -12,7 +12,7 @@
 AC_DEFUN([gl_FUNC_DIRFD],
 [
   AC_REQUIRE([gl_DIRENT_H_DEFAULTS])
-  AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+  AC_REQUIRE([AC_CANONICAL_HOST])
 
   dnl Persuade glibc <dirent.h> to declare dirfd().
   AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS])
@@ -36,15 +36,24 @@ AC_DEFUN([gl_FUNC_DIRFD]
        [gl_cv_func_dirfd_macro=yes],
        [gl_cv_func_dirfd_macro=no])])
 
-  # Use the replacement if we have no function or macro with that name,
-  # or if OS/2 kLIBC whose dirfd() does not work.
-  # Replace only if the system declares dirfd already.
-  case 
$ac_cv_func_dirfd,$gl_cv_func_dirfd_macro,$host_os,$ac_cv_have_decl_dirfd in
-    no,no,*,yes | *,*,os2*,yes)
+  if test $ac_cv_func_dirfd = no && test $gl_cv_func_dirfd_macro = no; then
+    HAVE_DIRFD=0
+  else
+    HAVE_DIRFD=1
+    dnl Replace only if the system declares dirfd already.
+    if test $ac_cv_have_decl_dirfd = yes; then
       REPLACE_DIRFD=1
-      AC_DEFINE([REPLACE_DIRFD], [1],
-        [Define to 1 if gnulib's dirfd() replacement is used.]);;
-  esac
+    fi
+    dnl Replace dirfd() on native Windows, to support fdopendir().
+    AC_REQUIRE([gl_DIRENT_DIR])
+    if test $DIR_HAS_FD_MEMBER = 0; then
+      REPLACE_DIRFD=1
+    fi
+    dnl OS/2 kLIBC dirfd() does not work.
+    case "$host_os" in
+      os2*) REPLACE_DIRFD=1 ;;
+    esac
+  fi
 ])
 
 dnl Prerequisites of lib/dirfd.c.
diff --git a/m4/opendir.m4 b/m4/opendir.m4
index 2fb90b6d57..2e9be769b5 100644
--- a/m4/opendir.m4
+++ b/m4/opendir.m4
@@ -1,4 +1,4 @@
-# opendir.m4 serial 5
+# opendir.m4 serial 6
 dnl Copyright (C) 2011-2023 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -12,21 +12,24 @@ AC_DEFUN([gl_FUNC_OPENDIR]
   AC_CHECK_FUNCS([opendir])
   if test $ac_cv_func_opendir = no; then
     HAVE_OPENDIR=0
-  fi
-  dnl Replace opendir() for supporting the gnulib-defined fchdir() function,
-  dnl to keep fchdir's bookkeeping up-to-date.
-  m4_ifdef([gl_FUNC_FCHDIR], [
-    gl_TEST_FCHDIR
-    if test $HAVE_FCHDIR = 0; then
-      if test $HAVE_OPENDIR = 1; then
+  else
+    dnl Replace opendir() on native Windows, to support fdopendir().
+    AC_REQUIRE([gl_DIRENT_DIR])
+    if test $DIR_HAS_FD_MEMBER = 0; then
+      REPLACE_OPENDIR=1
+    fi
+    dnl Replace opendir() on OS/2 kLIBC to support dirfd() function replaced
+    dnl by gnulib.
+    case $host_os in
+      os2*) REPLACE_OPENDIR=1 ;;
+    esac
+    dnl Replace opendir() for supporting the gnulib-defined fchdir() function,
+    dnl to keep fchdir's bookkeeping up-to-date.
+    m4_ifdef([gl_FUNC_FCHDIR], [
+      gl_TEST_FCHDIR
+      if test $HAVE_FCHDIR = 0; then
         REPLACE_OPENDIR=1
       fi
-    fi
-  ])
-  dnl Replace opendir() on OS/2 kLIBC to support dirfd() function replaced
-  dnl by gnulib.
-  case $host_os,$HAVE_OPENDIR in
-    os2*,1)
-      REPLACE_OPENDIR=1;;
-  esac
+    ])
+  fi
 ])
diff --git a/m4/readdir.m4 b/m4/readdir.m4
index 0a78739b3c..81337e2ffa 100644
--- a/m4/readdir.m4
+++ b/m4/readdir.m4
@@ -1,4 +1,4 @@
-# readdir.m4 serial 1
+# readdir.m4 serial 2
 dnl Copyright (C) 2011-2023 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -11,5 +11,11 @@ AC_DEFUN([gl_FUNC_READDIR]
   AC_CHECK_FUNCS([readdir])
   if test $ac_cv_func_readdir = no; then
     HAVE_READDIR=0
+  else
+    dnl Replace readdir() on native Windows, to support fdopendir().
+    AC_REQUIRE([gl_DIRENT_DIR])
+    if test $DIR_HAS_FD_MEMBER = 0; then
+      REPLACE_READDIR=1
+    fi
   fi
 ])
diff --git a/m4/rewinddir.m4 b/m4/rewinddir.m4
index dc2c37d97a..d0d24de8a6 100644
--- a/m4/rewinddir.m4
+++ b/m4/rewinddir.m4
@@ -1,4 +1,4 @@
-# rewinddir.m4 serial 1
+# rewinddir.m4 serial 2
 dnl Copyright (C) 2011-2023 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -11,5 +11,11 @@ AC_DEFUN([gl_FUNC_REWINDDIR]
   AC_CHECK_FUNCS([rewinddir])
   if test $ac_cv_func_rewinddir = no; then
     HAVE_REWINDDIR=0
+  else
+    dnl Replace rewinddir() on native Windows, to support fdopendir().
+    AC_REQUIRE([gl_DIRENT_DIR])
+    if test $DIR_HAS_FD_MEMBER = 0; then
+      REPLACE_REWINDDIR=1
+    fi
   fi
 ])
diff --git a/modules/dirent b/modules/dirent
index 8ae7bbfc4e..6f5a63ad66 100644
--- a/modules/dirent
+++ b/modules/dirent
@@ -33,6 +33,7 @@ dirent.h: dirent.in.h $(top_builddir)/config.status 
$(CXXDEFS_H) $(ARG_NONNULL_H
              -e 's|@''PRAGMA_SYSTEM_HEADER''@|@PRAGMA_SYSTEM_HEADER@|g' \
              -e 's|@''PRAGMA_COLUMNS''@|@PRAGMA_COLUMNS@|g' \
              -e 's|@''NEXT_DIRENT_H''@|$(NEXT_DIRENT_H)|g' \
+             -e 's/@''DIR_HAS_FD_MEMBER''@/$(DIR_HAS_FD_MEMBER)/g' \
              -e 's/@''GNULIB_OPENDIR''@/$(GNULIB_OPENDIR)/g' \
              -e 's/@''GNULIB_READDIR''@/$(GNULIB_READDIR)/g' \
              -e 's/@''GNULIB_REWINDDIR''@/$(GNULIB_REWINDDIR)/g' \
@@ -51,6 +52,8 @@ dirent.h: dirent.in.h $(top_builddir)/config.status 
$(CXXDEFS_H) $(ARG_NONNULL_H
              -e 's|@''HAVE_SCANDIR''@|$(HAVE_SCANDIR)|g' \
              -e 's|@''HAVE_ALPHASORT''@|$(HAVE_ALPHASORT)|g' \
              -e 's|@''REPLACE_OPENDIR''@|$(REPLACE_OPENDIR)|g' \
+             -e 's|@''REPLACE_READDIR''@|$(REPLACE_READDIR)|g' \
+             -e 's|@''REPLACE_REWINDDIR''@|$(REPLACE_REWINDDIR)|g' \
              -e 's|@''REPLACE_CLOSEDIR''@|$(REPLACE_CLOSEDIR)|g' \
              -e 's|@''REPLACE_DIRFD''@|$(REPLACE_DIRFD)|g' \
              -e 's|@''REPLACE_FDOPENDIR''@|$(REPLACE_FDOPENDIR)|g' \
diff --git a/modules/dirfd b/modules/dirfd
index 0ffcf7ebbd..a87fa8b2e7 100644
--- a/modules/dirfd
+++ b/modules/dirfd
@@ -3,17 +3,18 @@ Retrieving the file descriptor of an open directory stream.  
(Unportable.)
 
 Files:
 lib/dirfd.c
+lib/dirent-private.h
 m4/dirfd.m4
 
 Depends-on:
 dirent
 extensions
-errno           [test $ac_cv_func_dirfd = no && test $gl_cv_func_dirfd_macro = 
no || test $REPLACE_DIRFD = 1]
+errno           [test $HAVE_DIRFD = 0 || test $REPLACE_DIRFD = 1]
 
 configure.ac:
 gl_FUNC_DIRFD
 gl_CONDITIONAL([GL_COND_OBJ_DIRFD],
-               [test $ac_cv_func_dirfd = no && test $gl_cv_func_dirfd_macro = 
no || test $REPLACE_DIRFD = 1])
+               [test $HAVE_DIRFD = 0 || test $REPLACE_DIRFD = 1])
 AM_COND_IF([GL_COND_OBJ_DIRFD], [
   gl_PREREQ_DIRFD
 ])
diff --git a/modules/fdopendir b/modules/fdopendir
index 2dec790167..7412254b6e 100644
--- a/modules/fdopendir
+++ b/modules/fdopendir
@@ -3,6 +3,7 @@ Open a directory stream from a file descriptor.
 
 Files:
 lib/fdopendir.c
+lib/dirent-private.h
 m4/fdopendir.m4
 
 Depends-on:
diff --git a/modules/readdir b/modules/readdir
index cfa61c9c64..111fd307af 100644
--- a/modules/readdir
+++ b/modules/readdir
@@ -12,7 +12,8 @@ largefile
 
 configure.ac:
 gl_FUNC_READDIR
-gl_CONDITIONAL([GL_COND_OBJ_READDIR], [test $HAVE_READDIR = 0])
+gl_CONDITIONAL([GL_COND_OBJ_READDIR],
+               [test $HAVE_READDIR = 0 || test $REPLACE_READDIR = 1])
 gl_DIRENT_MODULE_INDICATOR([readdir])
 
 Makefile.am:
diff --git a/modules/rewinddir b/modules/rewinddir
index 38cf6db0bc..e4ccd2a6a5 100644
--- a/modules/rewinddir
+++ b/modules/rewinddir
@@ -13,7 +13,8 @@ largefile
 
 configure.ac:
 gl_FUNC_REWINDDIR
-gl_CONDITIONAL([GL_COND_OBJ_REWINDDIR], [test $HAVE_REWINDDIR = 0])
+gl_CONDITIONAL([GL_COND_OBJ_REWINDDIR],
+               [test $HAVE_REWINDDIR = 0 || test $REPLACE_REWINDDIR = 1])
 gl_DIRENT_MODULE_INDICATOR([rewinddir])
 
 Makefile.am:






reply via email to

[Prev in Thread] Current Thread [Next in Thread]