bug-gnulib
[Top][All Lists]
Advanced

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

Re: gnulib-tests with dup_cloexec() failing on old Linux kernel


From: Eric Blake
Subject: Re: gnulib-tests with dup_cloexec() failing on old Linux kernel
Date: Wed, 16 Dec 2009 17:34:52 +0000 (UTC)
User-agent: Loom/3.14 (http://gmane.org/)

Eric Blake <ebb9 <at> byu.net> writes:

> > http://koji.fedoraproject.org/koji/taskinfo?taskID=1872609
> > gnulib-tests (from coreutils-8.2 tarball) with dup_cloexec() function
> > are failing in koji build system (with old RHEL-5 xen kernel) - errno
> > EINVAL . I temporarily skipped those tests to get coreutils 8.2 (and
> > again working rm) into Fedora rawhide, but I would prefer other way. 
> 
> Yep - my proposed rpl_fcntl is able to work around the bug, so I need to
> teach cloexec the same trick.  Thanks for the heads up; I'll have a patch
> later today.  I'm considering making cloexec depend on fcntl (so far, my
> approach has been that cloexec was standalone, and that fcntl was
> optional; but adding the dependency would make use of cloexec drag in
> fcntl and allow for code simplification in cloexec).

Here's the current state of my fcntl patch, rewritten to take Bruno's review 
and your bug report into consideration.  I'll probably apply it later today.

>From 6524764bb566a392d4b18ec7240b7af697e850ed Mon Sep 17 00:00:00 2001
From: Eric Blake <address@hidden>
Date: Wed, 16 Dec 2009 09:24:23 -0700
Subject: [PATCH 1/5] binary-io: avoid potential compilation warning

* lib/binary-io.h [__DJGPP__]: Avoid null preprocessor
directives.

Signed-off-by: Eric Blake <address@hidden>
---
 ChangeLog       |    4 ++++
 lib/binary-io.h |    9 +++++----
 2 files changed, 9 insertions(+), 4 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index fe1b6f8..013360d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,9 @@
 2009-12-16  Eric Blake  <address@hidden>

+       binary-io: avoid potential compilation warning
+       * lib/binary-io.h [__DJGPP__]: Avoid null preprocessor
+       directives.
+
        fflush: avoid compilation error on NetBSD
        * lib/fflush.c (update_fpos_cache): Use a union to safely convert
        between off_t and fpos_t, since the latter is sometimes a struct.
diff --git a/lib/binary-io.h b/lib/binary-io.h
index b1dc629..9851511 100644
--- a/lib/binary-io.h
+++ b/lib/binary-io.h
@@ -1,5 +1,6 @@
 /* Binary mode I/O.
-   Copyright (C) 2001, 2003, 2005, 2008 Free Software Foundation, Inc.
+   Copyright (C) 2001, 2003, 2005, 2008, 2009 Free Software
+   Foundation, Inc.

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -45,9 +46,9 @@
 # endif
 # ifdef __DJGPP__
 #  include <unistd.h> /* declares isatty() */
-#  /* Avoid putting stdin/stdout in binary mode if it is connected to the
-#     console, because that would make it impossible for the user to
-#     interrupt the program through Ctrl-C or Ctrl-Break.  */
+   /* Avoid putting stdin/stdout in binary mode if it is connected to
+      the console, because that would make it impossible for the user
+      to interrupt the program through Ctrl-C or Ctrl-Break.  */
 #  define SET_BINARY(fd) (!isatty (fd) ? (setmode (fd, O_BINARY), 0) : 0)
 # else
 #  define SET_BINARY(fd) setmode (fd, O_BINARY)
-- 
1.6.4.2


>From 00a7e50f34cd54bc068933f59509774a0f62cc88 Mon Sep 17 00:00:00 2001
From: Eric Blake <address@hidden>
Date: Mon, 7 Dec 2009 11:50:59 -0700
Subject: [PATCH 2/5] fcntl: support F_DUPFD_CLOEXEC on systems with fcntl

Implement F_DUPFD_CLOEXEC.  The unit test still fails on systems
with other fcntl bugs (such as cygwin 1.5 mishandling F_DUPFD,
or mingw lacking fcntl altogether).  Passes on Linux, both with
and without kernel support, and on cygwin 1.7.

* modules/fcntl (Files): List new files.
(configure.ac): Run a test.
* m4/fcntl.m4 (gl_FUNC_FCNTL): New file.
* lib/fcntl.c (rpl_fcntl): Likewise.
* m4/fcntl_h.m4 (gl_FCNTL_H_DEFAULTS): Add witness defaults.
(gl_FCNTL_H): Always replace fcntl.h.
* modules/fcntl-h (Makefile.am): Substitute witnesses.
* lib/fcntl.in.h (fcntl): Declare replacement.
(F_DUPFD_CLOEXEC, GNULIB_defined_F_DUPFD_CLOEXEC): New macro when
needed, plus a witness.
* doc/posix-functions/fcntl.texi (fcntl): Document this.
* doc/posix-headers/fcntl.texi (fcntl.h): Likewise.
* tests/test-fcntl.c: New file.
* modules/fcntl-tests: Likewise.

Signed-off-by: Eric Blake <address@hidden>
---
 ChangeLog                      |   16 ++
 doc/posix-functions/fcntl.texi |    9 +-
 doc/posix-headers/fcntl.texi   |   18 ++-
 lib/fcntl.c                    |  104 +++++++++++++
 lib/fcntl.in.h                 |   25 +++
 m4/fcntl.m4                    |   49 ++++++
 m4/fcntl_h.m4                  |    6 +-
 modules/fcntl                  |    6 +-
 modules/fcntl-h                |    6 +-
 modules/fcntl-tests            |   13 ++
 tests/test-fcntl.c             |  328 ++++++++++++++++++++++++++++++++++++++++
 11 files changed, 567 insertions(+), 13 deletions(-)
 create mode 100644 lib/fcntl.c
 create mode 100644 m4/fcntl.m4
 create mode 100644 modules/fcntl-tests
 create mode 100644 tests/test-fcntl.c

diff --git a/ChangeLog b/ChangeLog
index 013360d..94e8228 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,21 @@
 2009-12-16  Eric Blake  <address@hidden>

+       fcntl: support F_DUPFD_CLOEXEC on systems with fcntl
+       * modules/fcntl (Files): List new files.
+       (configure.ac): Run a test.
+       * m4/fcntl.m4 (gl_FUNC_FCNTL): New file.
+       * lib/fcntl.c (rpl_fcntl): Likewise.
+       * m4/fcntl_h.m4 (gl_FCNTL_H_DEFAULTS): Add witness defaults.
+       (gl_FCNTL_H): Always replace fcntl.h.
+       * modules/fcntl-h (Makefile.am): Substitute witnesses.
+       * lib/fcntl.in.h (fcntl): Declare replacement.
+       (F_DUPFD_CLOEXEC, GNULIB_defined_F_DUPFD_CLOEXEC): New macro when
+       needed, plus a witness.
+       * doc/posix-functions/fcntl.texi (fcntl): Document this.
+       * doc/posix-headers/fcntl.texi (fcntl.h): Likewise.
+       * tests/test-fcntl.c: New file.
+       * modules/fcntl-tests: Likewise.
+
        binary-io: avoid potential compilation warning
        * lib/binary-io.h [__DJGPP__]: Avoid null preprocessor
        directives.
diff --git a/doc/posix-functions/fcntl.texi b/doc/posix-functions/fcntl.texi
index 143ac63..5e1aea5 100644
--- a/doc/posix-functions/fcntl.texi
+++ b/doc/posix-functions/fcntl.texi
@@ -4,10 +4,17 @@ fcntl

 POSIX specification: @url
{http://www.opengroup.org/onlinepubs/9699919799/functions/fcntl.html}

-Gnulib module: ---
+Gnulib module: fcntl

 Portability problems fixed by Gnulib:
 @itemize
address@hidden
+This function does not support @code{F_DUPFD_CLOEXEC} on some
+platforms:
+MacOS X 10.3, FreeBSD 6.0, NetBSD 3.0, OpenBSD 3.8, AIX 5.1, HP-UX 11,
+IRIX 6.5, OSF/1 5.1, Solaris 10, Cygwin 1.7.1, mingw, Interix 3.5,
+BeOS.
+Note that the gnulib replacement code is functional but not atomic.
 @end itemize

 Portability problems not fixed by Gnulib:
diff --git a/doc/posix-headers/fcntl.texi b/doc/posix-headers/fcntl.texi
index 340cf28..299f1b4 100644
--- a/doc/posix-headers/fcntl.texi
+++ b/doc/posix-headers/fcntl.texi
@@ -27,6 +27,12 @@ fcntl.h
 mingw.

 @item
address@hidden is not defined on some platforms:
+MacOS X 10.3, FreeBSD 6.0, NetBSD 3.0, OpenBSD 3.8, AIX 5.1, HP-UX 11,
+IRIX 6.5, OSF/1 5.1, Solaris 10, Cygwin 1.7.1, mingw, Interix 3.5,
+BeOS.
+
address@hidden
 @samp{AT_FDCWD}, @samp{AT_EACCESS}, @samp{AT_SYMLINK_NOFOLLOW},
 @samp{AT_SYMLINK_FOLLOW}, and @samp{AT_REMOVEDIR}
 are not defined on many platforms:
@@ -50,14 +56,14 @@ fcntl.h
 on some platforms.

 @item
address@hidden, @samp{F_DUPFD_CLOEXEC}, @samp{F_GETFD}, and
address@hidden are not defined on some platforms:
-mingw
address@hidden and @samp{F_GETFD} are not defined on some platforms:
+mingw.

 @item
address@hidden, @samp{F_SETFL}, @samp{F_GETLK}, @samp{F_SETLK},
address@hidden, @samp{F_GETOWN}, and @samp{F_SETOWN} are not defined
-on some platforms.
address@hidden, @samp{F_GETFL}, @samp{F_SETFL}, @samp{F_GETLK},
address@hidden, @samp{F_SETLOKW}, @samp{F_GETOWN}, and @samp{F_SETOWN}
+are not defined on some platforms:
+mingw.

 @item
 @samp{POSIX_FADV_DONTNEED}, @samp{POSIX_FADV_NOREUSE},
diff --git a/lib/fcntl.c b/lib/fcntl.c
new file mode 100644
index 0000000..3c05d31
--- /dev/null
+++ b/lib/fcntl.c
@@ -0,0 +1,104 @@
+/* Provide file descriptor control.
+
+   Copyright (C) 2009 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* Written by Eric Blake <address@hidden>.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include <fcntl.h>
+
+#include <errno.h>
+#include <stdarg.h>
+
+#if !HAVE_FCNTL
+# error not ported to mingw yet
+#endif
+#undef fcntl
+
+/* Perform the specified ACTION on the file descriptor FD, possibly
+   using the argument ARG further described below.  This replacement
+   handles the following actions, and forwards all others on to the
+   native fcntl.
+
+   F_DUPFD_CLOEXEC - duplicate FD, with int ARG being the minimum
+   target fd.  If successful, return the duplicate, which will not be
+   inheritable; otherwise return -1 and set errno.  */
+
+int
+rpl_fcntl (int fd, int action, /* arg */...)
+{
+  va_list arg;
+  int result = -1;
+  va_start (arg, action);
+  switch (action)
+    {
+    case F_DUPFD_CLOEXEC:
+      {
+        int target = va_arg (arg, int);
+
+        /* Try the system call first, if the headers claim it exists
+           (that is, if GNULIB_defined_F_DUPFD_CLOEXEC is 0), since we
+           may be running with a glibc that has the macro but with an
+           older kernel that does not support it.  Cache the
+           information on whether the system call really works, but
+           avoid caching failure if the corresponding F_DUPFD fails
+           for any reason.  0 = unknown, 1 = yes, -1 = no.  */
+        static int have_dupfd_cloexec = GNULIB_defined_F_DUPFD_CLOEXEC ? -1 : 
0;
+        if (0 <= have_dupfd_cloexec)
+          {
+            result = fcntl (fd, action, target);
+            if (0 <= result || errno != EINVAL)
+              have_dupfd_cloexec = 1;
+            else
+              {
+                result = fcntl (fd, F_DUPFD, target);
+                if (result < 0)
+                  break;
+                have_dupfd_cloexec = -1;
+              }
+          }
+        else
+          result = fcntl (fd, F_DUPFD, target);
+        if (0 <= result && have_dupfd_cloexec == -1)
+          {
+            int flags = fcntl (result, F_GETFD);
+            if (flags < 0 || fcntl (result, F_SETFD, flags | FD_CLOEXEC) == -1)
+              {
+                int saved_errno = errno;
+                close (result);
+                errno = saved_errno;
+                result = -1;
+              }
+          }
+#if REPLACE_FCHDIR
+        if (0 <= result)
+          result = _gl_register_dup (fd, result);
+#endif
+        break;
+      } /* F_DUPFD_CLOEXEC */
+
+    default:
+      {
+        void *p = va_arg (arg, void *);
+        result = fcntl (fd, action, p);
+        break;
+      }
+    }
+  va_end (arg);
+  return result;
+}
diff --git a/lib/fcntl.in.h b/lib/fcntl.in.h
index 953e065..34ea1cf 100644
--- a/lib/fcntl.in.h
+++ b/lib/fcntl.in.h
@@ -55,6 +55,20 @@
 extern "C" {
 #endif

+#if @GNULIB_FCNTL@
+# if @REPLACE_FCNTL@
+#  undef fcntl
+#  define fcntl rpl_fcntl
+extern int fcntl (int fd, int action, ...);
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef fcntl
+# define fcntl \
+    (GL_LINK_WARNING ("fcntl is not always POSIX compliant - " \
+                      "use gnulib module fcntl for portability"), \
+     fcntl)
+#endif
+
 #if @GNULIB_OPEN@
 # if @REPLACE_OPEN@
 #  undef open
@@ -96,6 +110,17 @@ extern int openat (int fd, char const *file, int flags, /* 
mode_t mode */ ...)
 # define FD_CLOEXEC 1
 #endif

+/* Fix up the supported F_* macros.  Intentionally leave other F_*
+   macros undefined.  */
+
+#ifndef F_DUPFD_CLOEXEC
+# define F_DUPFD_CLOEXEC 0x40000000
+/* Witness variable: 1 if gnulib defined F_DUPFD_CLOEXEC, 0 otherwise.  */
+# define GNULIB_defined_F_DUPFD_CLOEXEC 1
+#else
+# define GNULIB_defined_F_DUPFD_CLOEXEC 0
+#endif
+
 /* Fix up the O_* macros.  */

 #if !defined O_DIRECT && defined O_DIRECTIO
diff --git a/m4/fcntl.m4 b/m4/fcntl.m4
new file mode 100644
index 0000000..f361b7d
--- /dev/null
+++ b/m4/fcntl.m4
@@ -0,0 +1,49 @@
+# fcntl.m4 serial 1
+dnl Copyright (C) 2009 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+# For now, this module ensures that fcntl()
+# - supports or emulates F_DUPFD_CLOEXEC
+# Still to be ported to various platforms:
+# - supports F_DUPFD correctly
+# Still to be ported to mingw:
+# - F_GETFD, F_SETFD, F_DUPFD
+# - F_DUPFD_CLOEXEC
+# - F_GETFL, F_SETFL
+# - F_GETOWN, F_SETOWN
+# - F_GETLK, F_SETLK, F_SETLKW
+AC_DEFUN([gl_FUNC_FCNTL],
+[
+  dnl Persuade glibc to expose F_DUPFD_CLOEXEC.
+  AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
+  AC_REQUIRE([gl_FCNTL_H_DEFAULTS])
+  AC_CHECK_FUNCS_ONCE([fcntl])
+  if test $ac_cv_func_fcntl = no; then
+    HAVE_FCNTL=0
+  else
+    AC_CACHE_CHECK([whether fcntl understands F_DUPFD_CLOEXEC],
+      [gl_cv_func_fcntl_f_dupfd_cloexec],
+      [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+#include <fcntl.h>
+#ifndef F_DUPFD_CLOEXEC
+choke me
+#endif
+         ]])],
+         [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+#ifdef __linux__
+/* The Linux kernel only added F_DUPFD_CLOEXEC in 2.6.24, so we always replace
+   it to support the semantics on older kernels that failed with EINVAL.  */
+choke me
+#endif
+           ]])],
+           [gl_cv_func_fcntl_f_dupfd_cloexec=yes],
+           [gl_cv_func_fcntl_f_dupfd_cloexec="needs runtime check"])],
+         [gl_cv_func_fcntl_f_dupfd_cloexec=no])])
+    if test "$gl_cv_func_fcntl_f_dupfd_cloexec" != yes; then
+      REPLACE_FCNTL=1
+      AC_LIBOBJ([fcntl])
+    fi
+  fi
+])
diff --git a/m4/fcntl_h.m4 b/m4/fcntl_h.m4
index 40a1803..3825adf 100644
--- a/m4/fcntl_h.m4
+++ b/m4/fcntl_h.m4
@@ -1,4 +1,4 @@
-# serial 6
+# serial 7
 # Configure fcntl.h.
 dnl Copyright (C) 2006, 2007, 2009 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
@@ -12,8 +12,6 @@ AC_DEFUN([gl_FCNTL_H],
   AC_REQUIRE([gl_FCNTL_H_DEFAULTS])
   AC_REQUIRE([gl_FCNTL_O_FLAGS])
   gl_CHECK_NEXT_HEADERS([fcntl.h])
-  FCNTL_H='fcntl.h'
-  AC_SUBST([FCNTL_H])
 ])

 # Test whether the flags O_NOATIME and O_NOFOLLOW actually work.
@@ -99,10 +97,12 @@ AC_DEFUN([gl_FCNTL_MODULE_INDICATOR],

 AC_DEFUN([gl_FCNTL_H_DEFAULTS],
 [
+  GNULIB_FCNTL=0;   AC_SUBST([GNULIB_FCNTL])
   GNULIB_OPEN=0;    AC_SUBST([GNULIB_OPEN])
   GNULIB_OPENAT=0;  AC_SUBST([GNULIB_OPENAT])
   dnl Assume proper GNU behavior unless another module says otherwise.
   HAVE_OPENAT=1;    AC_SUBST([HAVE_OPENAT])
+  REPLACE_FCNTL=0;  AC_SUBST([REPLACE_FCNTL])
   REPLACE_OPEN=0;   AC_SUBST([REPLACE_OPEN])
   REPLACE_OPENAT=0; AC_SUBST([REPLACE_OPENAT])
 ])
diff --git a/modules/fcntl b/modules/fcntl
index 1107cac..b4b2ae4 100644
--- a/modules/fcntl
+++ b/modules/fcntl
@@ -1,12 +1,16 @@
 Description:
-Placeholder for eventual fcntl() replacement.
+Support for fcntl() action F_DUPFD_CLOEXEC.

 Files:
+m4/fcntl.m4
+lib/fcntl.c

 Depends-on:
 fcntl-h

 configure.ac:
+gl_FUNC_FCNTL
+gl_FCNTL_MODULE_INDICATOR([fcntl])

 Makefile.am:

diff --git a/modules/fcntl-h b/modules/fcntl-h
index 6eaec52..322920a 100644
--- a/modules/fcntl-h
+++ b/modules/fcntl-h
@@ -16,7 +16,7 @@ configure.ac:
 gl_FCNTL_H

 Makefile.am:
-BUILT_SOURCES += $(FCNTL_H)
+BUILT_SOURCES += fcntl.h

 # We need the following in order to create <fcntl.h> when the system
 # doesn't have one that works with the given compiler.
@@ -26,11 +26,13 @@ fcntl.h: fcntl.in.h $(LINK_WARNING_H) $(ARG_NONNULL_H)
          sed -e 's|@''INCLUDE_NEXT''@|$(INCLUDE_NEXT)|g' \
              -e 's|@''PRAGMA_SYSTEM_HEADER''@|@PRAGMA_SYSTEM_HEADER@|g' \
              -e 's|@''NEXT_FCNTL_H''@|$(NEXT_FCNTL_H)|g' \
+             -e 's|@''GNULIB_FCNTL''@|$(GNULIB_FCNTL)|g' \
              -e 's|@''GNULIB_OPEN''@|$(GNULIB_OPEN)|g' \
              -e 's|@''GNULIB_OPENAT''@|$(GNULIB_OPENAT)|g' \
+             -e 's|@''HAVE_OPENAT''@|$(HAVE_OPENAT)|g' \
+             -e 's|@''REPLACE_FCNTL''@|$(REPLACE_FCNTL)|g' \
              -e 's|@''REPLACE_OPEN''@|$(REPLACE_OPEN)|g' \
              -e 's|@''REPLACE_OPENAT''@|$(REPLACE_OPENAT)|g' \
-             -e 's|@''HAVE_OPENAT''@|$(HAVE_OPENAT)|g' \
              -e '/definition of GL_LINK_WARNING/r $(LINK_WARNING_H)' \
              -e '/definition of _GL_ARG_NONNULL/r $(ARG_NONNULL_H)' \
              < $(srcdir)/fcntl.in.h; \
diff --git a/modules/fcntl-tests b/modules/fcntl-tests
new file mode 100644
index 0000000..e81a5e9
--- /dev/null
+++ b/modules/fcntl-tests
@@ -0,0 +1,13 @@
+Files:
+tests/test-fcntl.c
+
+Depends-on:
+binary-io
+getdtablesize
+stdbool
+
+configure.ac:
+
+Makefile.am:
+TESTS += test-fcntl
+check_PROGRAMS += test-fcntl
diff --git a/tests/test-fcntl.c b/tests/test-fcntl.c
new file mode 100644
index 0000000..4dd1995
--- /dev/null
+++ b/tests/test-fcntl.c
@@ -0,0 +1,328 @@
+/* Test of fcntl(2).
+   Copyright (C) 2009 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* Written by Eric Blake <address@hidden>, 2009.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include <fcntl.h>
+
+/* Helpers.  */
+#include <errno.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+/* Get declarations of the Win32 API functions.  */
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+#endif
+
+#include "binary-io.h"
+
+/* Use O_CLOEXEC if available, but test works without it.  */
+#ifndef O_CLOEXEC
+# define O_CLOEXEC 0
+#endif
+
+#if !O_BINARY
+# define setmode(f,m) zero ()
+static int zero (void) { return 0; }
+#endif
+
+#define ASSERT(expr) \
+  do                                                                         \
+    {                                                                        \
+      if (!(expr))                                                           \
+        {                                                                    \
+          fprintf (stderr, "%s:%d: assertion failed\n", __FILE__, __LINE__); \
+          fflush (stderr);                                                   \
+          abort ();                                                          \
+        }                                                                    \
+    }                                                                        \
+  while (0)
+
+/* Return true if FD is open.  */
+static bool
+is_open (int fd)
+{
+#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+  /* On Win32, the initial state of unassigned standard file
+     descriptors is that they are open but point to an
+     INVALID_HANDLE_VALUE, and there is no fcntl.  */
+  return (HANDLE) _get_osfhandle (fd) != INVALID_HANDLE_VALUE;
+#else
+# ifndef F_GETFL
+#  error Please port fcntl to your platform
+# endif
+  return 0 <= fcntl (fd, F_GETFL);
+#endif
+}
+
+/* Return true if FD is open and inheritable across exec/spawn.  */
+static bool
+is_inheritable (int fd)
+{
+#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+  /* On Win32, the initial state of unassigned standard file
+     descriptors is that they are open but point to an
+     INVALID_HANDLE_VALUE, and there is no fcntl.  */
+  HANDLE h = (HANDLE) _get_osfhandle (fd);
+  DWORD flags;
+  if (h == INVALID_HANDLE_VALUE || GetHandleInformation (h, &flags) == 0)
+    return false;
+  return (flags & HANDLE_FLAG_INHERIT) != 0;
+#else
+# ifndef F_GETFD
+#  error Please port fcntl to your platform
+# endif
+  int i = fcntl (fd, F_GETFD);
+  return 0 <= i && (i & FD_CLOEXEC) == 0;
+#endif
+}
+
+/* Return non-zero if FD is open in the given MODE, which is either
+   O_TEXT or O_BINARY.  */
+static bool
+is_mode (int fd, int mode)
+{
+  int value = setmode (fd, O_BINARY);
+  setmode (fd, value);
+  return mode == value;
+}
+
+/* Since native fcntl can have more supported operations than our
+   replacement is aware of, and since various operations assign
+   different types to the vararg argument, a wrapper around fcntl must
+   be able to pass a vararg of unknown type on through to the original
+   fcntl.  Make sure that this works properly: func1 behaves like the
+   original fcntl interpreting the vararg as an int, and func2 behaves
+   like rpl_fcntl that doesn't know what type to forward.  */
+static int
+func1 (int a, ...)
+{
+  va_list arg;
+  int i;
+  va_start (arg, a);
+  i = va_arg (arg, int);
+  va_end (arg);
+  return i;
+}
+static int
+func2 (int a, ...)
+{
+  va_list arg;
+  void *p;
+  va_start (arg, a);
+  p = va_arg (arg, void *);
+  va_end (arg);
+  return func1 (a, p);
+}
+
+/* Ensure that all supported fcntl actions are distinct, and
+   usable in preprocessor expressions.  */
+static void
+check_flags (void)
+{
+  switch (0)
+    {
+#ifdef F_DUPFD
+    case F_DUPFD:
+# if F_DUPFD
+# endif
+#endif
+
+#ifdef F_DUPFD_CLOEXEC
+    case F_DUPFD_CLOEXEC:
+# if F_DUPFD_CLOEXEC
+# endif
+#endif
+
+#ifdef F_GETFD
+    case F_GETFD:
+# if F_GETFD
+# endif
+#endif
+
+#ifdef F_SETFD
+    case F_SETFD:
+# if F_SETFD
+# endif
+#endif
+
+#ifdef F_GETFL
+    case F_GETFL:
+# if F_GETFL
+# endif
+#endif
+
+#ifdef F_SETFL
+    case F_SETFL:
+# if F_SETFL
+# endif
+#endif
+
+#ifdef F_GETOWN
+    case F_GETOWN:
+# if F_GETOWN
+# endif
+#endif
+
+#ifdef F_SETOWN
+    case F_SETOWN:
+# if F_SETOWN
+# endif
+#endif
+
+#ifdef F_GETLK
+    case F_GETLK:
+# if F_GETLK
+# endif
+#endif
+
+#ifdef F_SETLK
+    case F_SETLK:
+# if F_SETLK
+# endif
+#endif
+
+#ifdef F_SETLKW
+    case F_SETLKW:
+# if F_SETLKW
+# endif
+#endif
+
+      ;
+    }
+}
+
+int
+main (int argc, char **argv)
+{
+  const char *file = "test-fcntl.tmp";
+  int fd;
+
+  ASSERT (func2 (1, 2) == 2);
+  ASSERT (func2 (2, -2) == -2);
+  ASSERT (func2 (3, 0x80000000) == 0x80000000);
+  check_flags ();
+
+#if HAVE_FCNTL
+
+  /* Assume std descriptors were provided by invoker, and ignore fds
+     that might have been inherited.  */
+  fd = creat (file, 0600);
+  ASSERT (STDERR_FILENO < fd);
+  close (fd + 1);
+  close (fd + 2);
+
+  /* For F_DUPFD*, the source must be valid.  */
+  errno = 0;
+  ASSERT (fcntl (-1, F_DUPFD, 0) == -1);
+  ASSERT (errno == EBADF);
+  errno = 0;
+  ASSERT (fcntl (fd + 1, F_DUPFD, 0) == -1);
+  ASSERT (errno == EBADF);
+  errno = 0;
+  ASSERT (fcntl (10000000, F_DUPFD, 0) == -1);
+  ASSERT (errno == EBADF);
+  errno = 0;
+  ASSERT (fcntl (-1, F_DUPFD_CLOEXEC, 0) == -1);
+  ASSERT (errno == EBADF);
+  errno = 0;
+  ASSERT (fcntl (fd + 1, F_DUPFD_CLOEXEC, 0) == -1);
+  ASSERT (errno == EBADF);
+  errno = 0;
+  ASSERT (fcntl (10000000, F_DUPFD_CLOEXEC, 0) == -1);
+  ASSERT (errno == EBADF);
+
+  /* For F_DUPFD*, the destination must be valid.  */
+  ASSERT (getdtablesize () < 10000000);
+  errno = 0;
+  ASSERT (fcntl (fd, F_DUPFD, -1) == -1);
+  ASSERT (errno == EINVAL);
+  errno = 0;
+  ASSERT (fcntl (fd, F_DUPFD, 10000000) == -1);
+  ASSERT (errno == EINVAL);
+  ASSERT (getdtablesize () < 10000000);
+  errno = 0;
+  ASSERT (fcntl (fd, F_DUPFD_CLOEXEC, -1) == -1);
+  ASSERT (errno == EINVAL);
+  errno = 0;
+  ASSERT (fcntl (fd, F_DUPFD_CLOEXEC, 10000000) == -1);
+  ASSERT (errno == EINVAL);
+
+  /* For F_DUPFD*, check for correct inheritance, as well as
+     preservation of text vs. binary.  */
+  setmode (fd, O_BINARY);
+  ASSERT (is_open (fd));
+  ASSERT (!is_open (fd + 1));
+  ASSERT (!is_open (fd + 2));
+  ASSERT (is_inheritable (fd));
+  ASSERT (is_mode (fd, O_BINARY));
+
+  ASSERT (fcntl (fd, F_DUPFD, fd) == fd + 1);
+  ASSERT (is_open (fd));
+  ASSERT (is_open (fd + 1));
+  ASSERT (!is_open (fd + 2));
+  ASSERT (is_inheritable (fd + 1));
+  ASSERT (is_mode (fd, O_BINARY));
+  ASSERT (is_mode (fd + 1, O_BINARY));
+  ASSERT (close (fd + 1) == 0);
+
+  ASSERT (fcntl (fd, F_DUPFD_CLOEXEC, fd + 2) == fd + 2);
+  ASSERT (is_open (fd));
+  ASSERT (!is_open (fd + 1));
+  ASSERT (is_open (fd + 2));
+  ASSERT (is_inheritable (fd));
+  ASSERT (!is_inheritable (fd + 2));
+  ASSERT (is_mode (fd, O_BINARY));
+  ASSERT (is_mode (fd + 2, O_BINARY));
+  ASSERT (close (fd) == 0);
+
+  setmode (fd + 2, O_TEXT);
+  ASSERT (fcntl (fd + 2, F_DUPFD, fd + 1) == fd + 1);
+  ASSERT (!is_open (fd));
+  ASSERT (is_open (fd + 1));
+  ASSERT (is_open (fd + 2));
+  ASSERT (is_inheritable (fd + 1));
+  ASSERT (!is_inheritable (fd + 2));
+  ASSERT (is_mode (fd + 1, O_TEXT));
+  ASSERT (is_mode (fd + 2, O_TEXT));
+  ASSERT (close (fd + 1) == 0);
+
+  ASSERT (fcntl (fd + 2, F_DUPFD_CLOEXEC, 0) == fd);
+  ASSERT (is_open (fd));
+  ASSERT (!is_open (fd + 1));
+  ASSERT (is_open (fd + 2));
+  ASSERT (!is_inheritable (fd));
+  ASSERT (!is_inheritable (fd + 2));
+  ASSERT (is_mode (fd, O_TEXT));
+  ASSERT (is_mode (fd + 2, O_TEXT));
+  ASSERT (close (fd + 2) == 0);
+
+  /* Cleanup.  */
+  ASSERT (close (fd) == 0);
+  ASSERT (unlink (file) == 0);
+
+#endif /* HAVE_FCNTL */
+
+  return 0;
+}
-- 
1.6.4.2


>From c1f8b1675b5b878ec0df8eb069ce03fe29a49e7c Mon Sep 17 00:00:00 2001
From: Eric Blake <address@hidden>
Date: Tue, 8 Dec 2009 12:10:52 -0700
Subject: [PATCH 3/5] fcntl: work around cygwin bug in F_DUPFD

fcntl(0,F_DUPFD,10000000) mistakenly failed with EMFILE
instead of EINVAL, and fcntl(0,F_DUPFD,-1) mistakenly passed.

* m4/fcntl.m4 (gl_REPLACE_FCNTL): New macro.
(gl_FUNC_FCNTL): Use it.  Test for F_DUPFD bug.
* lib/fcntl.c (rpl_fcntl) <F_DUPFD>: Work around it.
<F_DUPFD_CLOEXEC>: Reduce calls to _gl_register_dup.
* doc/posix-functions/fcntl.texi (fcntl): Document it.

Signed-off-by: Eric Blake <address@hidden>
---
 ChangeLog                      |    7 ++++++
 doc/posix-functions/fcntl.texi |    5 ++++
 lib/fcntl.c                    |   40 +++++++++++++++++++++++++++++++------
 m4/fcntl.m4                    |   42 +++++++++++++++++++++++++++++++++++----
 4 files changed, 82 insertions(+), 12 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 94e8228..01e54a9 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,12 @@
 2009-12-16  Eric Blake  <address@hidden>

+       fcntl: work around cygwin bug in F_DUPFD
+       * m4/fcntl.m4 (gl_REPLACE_FCNTL): New macro.
+       (gl_FUNC_FCNTL): Use it.  Test for F_DUPFD bug.
+       * lib/fcntl.c (rpl_fcntl) <F_DUPFD>: Work around it.
+       <F_DUPFD_CLOEXEC>: Reduce calls to _gl_register_dup.
+       * doc/posix-functions/fcntl.texi (fcntl): Document it.
+
        fcntl: support F_DUPFD_CLOEXEC on systems with fcntl
        * modules/fcntl (Files): List new files.
        (configure.ac): Run a test.
diff --git a/doc/posix-functions/fcntl.texi b/doc/posix-functions/fcntl.texi
index 5e1aea5..ce38e42 100644
--- a/doc/posix-functions/fcntl.texi
+++ b/doc/posix-functions/fcntl.texi
@@ -15,6 +15,11 @@ fcntl
 IRIX 6.5, OSF/1 5.1, Solaris 10, Cygwin 1.7.1, mingw, Interix 3.5,
 BeOS.
 Note that the gnulib replacement code is functional but not atomic.
+
address@hidden
+The @code{F_DUPFD} action of this function does not reject
+out-of-range targets properly on some platforms:
+Cygwin 1.5.x.
 @end itemize

 Portability problems not fixed by Gnulib:
diff --git a/lib/fcntl.c b/lib/fcntl.c
index 3c05d31..894b478 100644
--- a/lib/fcntl.c
+++ b/lib/fcntl.c
@@ -35,6 +35,10 @@
    handles the following actions, and forwards all others on to the
    native fcntl.

+   F_DUPFD - duplicate FD, with int ARG being the minimum target fd.
+   If successful, return the duplicate, which will be inheritable;
+   otherwise return -1 and set errno.
+
    F_DUPFD_CLOEXEC - duplicate FD, with int ARG being the minimum
    target fd.  If successful, return the duplicate, which will not be
    inheritable; otherwise return -1 and set errno.  */
@@ -47,6 +51,26 @@ rpl_fcntl (int fd, int action, /* arg */...)
   va_start (arg, action);
   switch (action)
     {
+
+#if FCNTL_DUPFD_BUGGY || REPLACE_FCHDIR
+    case F_DUPFD:
+      {
+        int target = va_arg (arg, int);
+        /* Detect invalid target; needed for cygwin 1.5.x.  */
+        if (target < 0 || getdtablesize () <= target)
+          errno = EINVAL;
+        else
+          {
+            result = fcntl (fd, action, target);
+# if REPLACE_FCHDIR
+            if (0 <= result)
+              result = _gl_register_dup (fd, result);
+# endif
+          }
+        break;
+      } /* F_DUPFD */
+#endif /* FCNTL_DUPFD_BUGGY || REPLACE_FCHDIR */
+
     case F_DUPFD_CLOEXEC:
       {
         int target = va_arg (arg, int);
@@ -63,17 +87,23 @@ rpl_fcntl (int fd, int action, /* arg */...)
           {
             result = fcntl (fd, action, target);
             if (0 <= result || errno != EINVAL)
-              have_dupfd_cloexec = 1;
+              {
+                have_dupfd_cloexec = 1;
+#if REPLACE_FCHDIR
+                if (0 <= result)
+                  result = _gl_register_dup (fd, result);
+#endif
+              }
             else
               {
-                result = fcntl (fd, F_DUPFD, target);
+                result = rpl_fcntl (fd, F_DUPFD, target);
                 if (result < 0)
                   break;
                 have_dupfd_cloexec = -1;
               }
           }
         else
-          result = fcntl (fd, F_DUPFD, target);
+          result = rpl_fcntl (fd, F_DUPFD, target);
         if (0 <= result && have_dupfd_cloexec == -1)
           {
             int flags = fcntl (result, F_GETFD);
@@ -85,10 +115,6 @@ rpl_fcntl (int fd, int action, /* arg */...)
                 result = -1;
               }
           }
-#if REPLACE_FCHDIR
-        if (0 <= result)
-          result = _gl_register_dup (fd, result);
-#endif
         break;
       } /* F_DUPFD_CLOEXEC */

diff --git a/m4/fcntl.m4 b/m4/fcntl.m4
index f361b7d..b2a7e6b 100644
--- a/m4/fcntl.m4
+++ b/m4/fcntl.m4
@@ -1,13 +1,12 @@
-# fcntl.m4 serial 1
+# fcntl.m4 serial 2
 dnl Copyright (C) 2009 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
 dnl with or without modifications, as long as this notice is preserved.

 # For now, this module ensures that fcntl()
-# - supports or emulates F_DUPFD_CLOEXEC
-# Still to be ported to various platforms:
 # - supports F_DUPFD correctly
+# - supports or emulates F_DUPFD_CLOEXEC
 # Still to be ported to mingw:
 # - F_GETFD, F_SETFD, F_DUPFD
 # - F_DUPFD_CLOEXEC
@@ -19,10 +18,33 @@ AC_DEFUN([gl_FUNC_FCNTL],
   dnl Persuade glibc to expose F_DUPFD_CLOEXEC.
   AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
   AC_REQUIRE([gl_FCNTL_H_DEFAULTS])
+  AC_REQUIRE([AC_CANONICAL_HOST])
   AC_CHECK_FUNCS_ONCE([fcntl])
   if test $ac_cv_func_fcntl = no; then
     HAVE_FCNTL=0
   else
+    dnl cygwin 1.5.x F_DUPFD has wrong errno, and allows negative target
+    AC_CACHE_CHECK([whether fcntl handles F_DUPFD correctly],
+      [gl_cv_func_fcntl_f_dupfd_works],
+      [AC_RUN_IFELSE([AC_LANG_PROGRAM([[
+#include <fcntl.h>
+]], [[return fcntl (0, F_DUPFD, -1) != -1;
+         ]])],
+         [gl_cv_func_fcntl_f_dupfd_works=yes],
+         [gl_cv_func_fcntl_f_dupfd_works=no],
+         [# Guess that it works on glibc systems
+          case $host_os in #((
+            *-gnu*) gl_cv_func_fcntl_f_dupfd_works="guessing yes";;
+            *)      gl_cv_func_fcntl_f_dupfd_works="guessing no";;
+          esac])])
+    case $gl_cv_func_fcntl_f_dupfd_works in
+      *yes) ;;
+      *) gl_REPLACE_FCNTL
+        AC_DEFINE([FCNTL_DUPFD_BUGGY], [1], [Define this to 1 if F_DUPFD
+          behavior does not match POSIX]) ;;
+    esac
+
+    dnl Many systems lack F_DUPFD_CLOEXEC
     AC_CACHE_CHECK([whether fcntl understands F_DUPFD_CLOEXEC],
       [gl_cv_func_fcntl_f_dupfd_cloexec],
       [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
@@ -42,8 +64,18 @@ choke me
            [gl_cv_func_fcntl_f_dupfd_cloexec="needs runtime check"])],
          [gl_cv_func_fcntl_f_dupfd_cloexec=no])])
     if test "$gl_cv_func_fcntl_f_dupfd_cloexec" != yes; then
-      REPLACE_FCNTL=1
-      AC_LIBOBJ([fcntl])
+      gl_REPLACE_FCNTL
+      dnl No witness macro needed for this bug.
     fi
   fi
 ])
+
+AC_DEFUN([gl_REPLACE_FCNTL],
+[
+  AC_REQUIRE([gl_FCNTL_H_DEFAULTS])
+  AC_CHECK_FUNCS_ONCE([fcntl])
+  if test $ac_cv_func_fcntl = yes; then
+    REPLACE_FCNTL=1
+    AC_LIBOBJ([fcntl])
+  fi
+])
-- 
1.6.4.2


>From f2f55ed95a72788cbb455c734caa5b86a08d4bd3 Mon Sep 17 00:00:00 2001
From: Eric Blake <address@hidden>
Date: Wed, 16 Dec 2009 09:11:32 -0700
Subject: [PATCH 4/5] fcntl: port portions of fcntl to mingw

Borrow ideas from dup_cloexec and dup3 to implement F_DUPFD and
F_DUPFD_CLOEXEC.  Support querying the inheritance status via
F_GETFD, but for now, no support for changing with F_SETFD.
The remaining portions of fcntl fail with EINVAL.

* m4/fcntl.m4 (gl_FUNC_FCNTL): Also build fcntl.c on mingw.
* lib/fcntl.c (fcntl) <F_DUPFD, F_DUPFD_CLOEXEC, F_GETFD>: Provide
replacement for mingw.
* modules/fcntl (Description): Update.
(Depends-on): Add dup2.
* m4/fcntl_h.m4 (gl_FCNTL_H_DEFAULTS): Add witness.
* modules/fcntl-h (Makefile.am): Substitute it.
* lib/fcntl.in.h (fcntl): Update declaration.
(F_DUPFD, F_GETFD): New macros, when needed.
* doc/posix-headers/fcntl.texi (fcntl.h): Update documentation.
* doc/posix-functions/fcntl.texi (fcntl): Likewise.
* tests/test-fcntl.c (check_flags, main): Enhance test for items
we now guarantee.

Signed-off-by: Eric Blake <address@hidden>
---
 ChangeLog                      |   15 ++++
 doc/posix-functions/fcntl.texi |   10 ++-
 doc/posix-headers/fcntl.texi   |    7 +-
 lib/fcntl.c                    |  175 ++++++++++++++++++++++++++++++++++++++--
 lib/fcntl.in.h                 |   14 +++-
 m4/fcntl.m4                    |   14 ++--
 m4/fcntl_h.m4                  |    3 +-
 modules/fcntl                  |    3 +-
 modules/fcntl-h                |    1 +
 tests/test-fcntl.c             |   37 ++++++---
 10 files changed, 243 insertions(+), 36 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 01e54a9..7117e5a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,20 @@
 2009-12-16  Eric Blake  <address@hidden>

+       fcntl: port portions of fcntl to mingw
+       * m4/fcntl.m4 (gl_FUNC_FCNTL): Also build fcntl.c on mingw.
+       * lib/fcntl.c (fcntl) <F_DUPFD, F_DUPFD_CLOEXEC, F_GETFD>: Provide
+       replacement for mingw.
+       * modules/fcntl (Description): Update.
+       (Depends-on): Add dup2.
+       * m4/fcntl_h.m4 (gl_FCNTL_H_DEFAULTS): Add witness.
+       * modules/fcntl-h (Makefile.am): Substitute it.
+       * lib/fcntl.in.h (fcntl): Update declaration.
+       (F_DUPFD, F_GETFD): New macros, when needed.
+       * doc/posix-headers/fcntl.texi (fcntl.h): Update documentation.
+       * doc/posix-functions/fcntl.texi (fcntl): Likewise.
+       * tests/test-fcntl.c (check_flags, main): Enhance test for items
+       we now guarantee.
+
        fcntl: work around cygwin bug in F_DUPFD
        * m4/fcntl.m4 (gl_REPLACE_FCNTL): New macro.
        (gl_FUNC_FCNTL): Use it.  Test for F_DUPFD bug.
diff --git a/doc/posix-functions/fcntl.texi b/doc/posix-functions/fcntl.texi
index ce38e42..2a41ad1 100644
--- a/doc/posix-functions/fcntl.texi
+++ b/doc/posix-functions/fcntl.texi
@@ -20,11 +20,17 @@ fcntl
 The @code{F_DUPFD} action of this function does not reject
 out-of-range targets properly on some platforms:
 Cygwin 1.5.x.
+
address@hidden
+This function is missing on some platforms:
+mingw.
 @end itemize

 Portability problems not fixed by Gnulib:
 @itemize
 @item
-This function is missing on some platforms:
-mingw.
+The replacement function does not support @code{F_SETFD},
address@hidden, @code{F_SETFL}, @code{F_GETOWN}, @code{F_SETOWN},
address@hidden, @code{F_SETLK}, and @code{F_SETLKW} on some platforms:
+mingw
 @end itemize
diff --git a/doc/posix-headers/fcntl.texi b/doc/posix-headers/fcntl.texi
index 299f1b4..97cd12a 100644
--- a/doc/posix-headers/fcntl.texi
+++ b/doc/posix-headers/fcntl.texi
@@ -23,7 +23,8 @@ fcntl.h
 on some platforms but not on others.

 @item
address@hidden is not defined on some platforms:
address@hidden, @samp{F_DUPFD}, and @samp{F_GETFD} are not defined
+on some platforms:
 mingw.

 @item
@@ -56,10 +57,6 @@ fcntl.h
 on some platforms.

 @item
address@hidden and @samp{F_GETFD} are not defined on some platforms:
-mingw.
-
address@hidden
 @samp{F_SETFD}, @samp{F_GETFL}, @samp{F_SETFL}, @samp{F_GETLK},
 @samp{F_SETLK}, @samp{F_SETLOKW}, @samp{F_GETOWN}, and @samp{F_SETOWN}
 are not defined on some platforms:
diff --git a/lib/fcntl.c b/lib/fcntl.c
index 894b478..05d73a9 100644
--- a/lib/fcntl.c
+++ b/lib/fcntl.c
@@ -23,17 +23,130 @@
 #include <fcntl.h>

 #include <errno.h>
+#include <limits.h>
 #include <stdarg.h>

 #if !HAVE_FCNTL
-# error not ported to mingw yet
+# define rpl_fcntl fcntl
 #endif
 #undef fcntl

+#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+/* Get declarations of the Win32 API functions.  */
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+
+/* Upper bound on getdtablesize().  See lib/getdtablesize.c.  */
+# define OPEN_MAX_MAX 0x10000
+
+/* Duplicate OLDFD into the first available slot of at least NEWFD,
+   which must be positive, with FLAGS determining whether the duplicate
+   will be inheritable.  */
+static int
+dupfd (int oldfd, int newfd, int flags)
+{
+  /* Mingw has no way to create an arbitrary fd.  Iterate until all
+     file descriptors less than newfd are filled up.  */
+  HANDLE curr_process = GetCurrentProcess ();
+  HANDLE old_handle = (HANDLE) _get_osfhandle (oldfd);
+  unsigned char fds_to_close[OPEN_MAX_MAX / CHAR_BIT];
+  unsigned int fds_to_close_bound = 0;
+  int result;
+  BOOL inherit = flags & O_CLOEXEC ? FALSE : TRUE;
+  int mode;
+
+  if (newfd < 0 || getdtablesize () <= newfd)
+    {
+      errno = EINVAL;
+      return -1;
+    }
+  if (old_handle == INVALID_HANDLE_VALUE
+      || (mode = setmode (oldfd, O_BINARY)) == -1)
+    {
+      /* oldfd is not open, or is an unassigned standard file
+         descriptor.  */
+      errno = EBADF;
+      return -1;
+    }
+  setmode (oldfd, mode);
+  flags |= mode;
+
+  for (;;)
+    {
+      HANDLE new_handle;
+      int duplicated_fd;
+      unsigned int index;
+
+      if (!DuplicateHandle (curr_process,           /* SourceProcessHandle */
+                            old_handle,             /* SourceHandle */
+                            curr_process,           /* TargetProcessHandle */
+                            (PHANDLE) &new_handle,  /* TargetHandle */
+                            (DWORD) 0,              /* DesiredAccess */
+                            inherit,                /* InheritHandle */
+                            DUPLICATE_SAME_ACCESS)) /* Options */
+        {
+          /* TODO: Translate GetLastError () into errno.  */
+          errno = EMFILE;
+          result = -1;
+          break;
+        }
+      duplicated_fd = _open_osfhandle ((long) new_handle, flags);
+      if (duplicated_fd < 0)
+        {
+          CloseHandle (new_handle);
+          errno = EMFILE;
+          result = -1;
+          break;
+        }
+      if (newfd <= duplicated_fd)
+        {
+          result = duplicated_fd;
+          break;
+        }
+
+      /* Set the bit duplicated_fd in fds_to_close[].  */
+      index = (unsigned int) duplicated_fd / CHAR_BIT;
+      if (fds_to_close_bound <= index)
+        {
+          if (sizeof fds_to_close <= index)
+            /* Need to increase OPEN_MAX_MAX.  */
+            abort ();
+          memset (fds_to_close + fds_to_close_bound, '\0',
+                  index + 1 - fds_to_close_bound);
+          fds_to_close_bound = index + 1;
+        }
+      fds_to_close[index] |= 1 << ((unsigned int) duplicated_fd % CHAR_BIT);
+    }
+
+  /* Close the previous fds that turned out to be too small.  */
+  {
+    int saved_errno = errno;
+    unsigned int duplicated_fd;
+
+    for (duplicated_fd = 0;
+         duplicated_fd < fds_to_close_bound * CHAR_BIT;
+         duplicated_fd++)
+      if ((fds_to_close[duplicated_fd / CHAR_BIT]
+           >> (duplicated_fd % CHAR_BIT))
+          & 1)
+        close (duplicated_fd);
+
+    errno = saved_errno;
+  }
+
+# if REPLACE_FCHDIR
+  if (0 <= result)
+    result = _gl_register_dup (oldfd, result);
+# endif
+  return result;
+}
+#endif /* W32 */
+
 /* Perform the specified ACTION on the file descriptor FD, possibly
    using the argument ARG further described below.  This replacement
    handles the following actions, and forwards all others on to the
-   native fcntl.
+   native fcntl.  An unrecognized ACTION returns -1 with errno set to
+   EINVAL.

    F_DUPFD - duplicate FD, with int ARG being the minimum target fd.
    If successful, return the duplicate, which will be inheritable;
@@ -41,7 +154,12 @@

    F_DUPFD_CLOEXEC - duplicate FD, with int ARG being the minimum
    target fd.  If successful, return the duplicate, which will not be
-   inheritable; otherwise return -1 and set errno.  */
+   inheritable; otherwise return -1 and set errno.
+
+   F_GETFD - ARG need not be present.  If successful, return a
+   non-negative value containing the descriptor flags of FD (only
+   FD_CLOEXEC is portable, but other flags may be present); otherwise
+   return -1 and set errno.  */

 int
 rpl_fcntl (int fd, int action, /* arg */...)
@@ -52,7 +170,14 @@ rpl_fcntl (int fd, int action, /* arg */...)
   switch (action)
     {

-#if FCNTL_DUPFD_BUGGY || REPLACE_FCHDIR
+#if !HAVE_FCNTL
+    case F_DUPFD:
+      {
+        int target = va_arg (arg, int);
+        result = dupfd (fd, target, 0);
+        break;
+      }
+#elif FCNTL_DUPFD_BUGGY || REPLACE_FCHDIR
     case F_DUPFD:
       {
         int target = va_arg (arg, int);
@@ -75,6 +200,10 @@ rpl_fcntl (int fd, int action, /* arg */...)
       {
         int target = va_arg (arg, int);

+#if !HAVE_FCNTL
+        result = dupfd (fd, target, O_CLOEXEC);
+        break;
+#else /* HAVE_FCNTL */
         /* Try the system call first, if the headers claim it exists
            (that is, if GNULIB_defined_F_DUPFD_CLOEXEC is 0), since we
            may be running with a glibc that has the macro but with an
@@ -89,10 +218,10 @@ rpl_fcntl (int fd, int action, /* arg */...)
             if (0 <= result || errno != EINVAL)
               {
                 have_dupfd_cloexec = 1;
-#if REPLACE_FCHDIR
+# if REPLACE_FCHDIR
                 if (0 <= result)
                   result = _gl_register_dup (fd, result);
-#endif
+# endif
               }
             else
               {
@@ -116,12 +245,46 @@ rpl_fcntl (int fd, int action, /* arg */...)
               }
           }
         break;
+#endif /* HAVE_FCNTL */
       } /* F_DUPFD_CLOEXEC */

+#if !HAVE_FCNTL
+    case F_GETFD:
+      {
+# if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+        HANDLE handle = (HANDLE) _get_osfhandle (fd);
+        DWORD flags;
+        if (handle == INVALID_HANDLE_VALUE
+            || GetHandleInformation (handle, &flags) == 0)
+          errno = EBADF;
+        else
+          result = (flags & HANDLE_FLAG_INHERIT) ? 0 : FD_CLOEXEC;
+# else /* !W32 */
+        /* Use dup2 to reject invalid file descriptors.  No way to
+           access this information, so punt.  */
+        if (0 <= dup2 (fd, fd))
+          result = 0;
+# endif /* !W32 */
+        break;
+      } /* F_GETFD */
+#endif /* !HAVE_FCNTL */
+
+      /* Implementing F_SETFD on mingw is not trivial - there is no
+         API for changing the O_NOINHERIT bit on an fd, and merely
+         changing the HANDLE_FLAG_INHERIT bit on the underlying handle
+         can lead to odd state.  It may be possible by duplicating the
+         handle, using _open_osfhandle with the right flags, then
+         using dup2 to move the duplicate onto the original, but that
+         is not supported for now.  */
+
     default:
       {
+#if HAVE_FCNTL
         void *p = va_arg (arg, void *);
         result = fcntl (fd, action, p);
+#else
+        errno = EINVAL;
+#endif
         break;
       }
     }
diff --git a/lib/fcntl.in.h b/lib/fcntl.in.h
index 34ea1cf..1a4602b 100644
--- a/lib/fcntl.in.h
+++ b/lib/fcntl.in.h
@@ -59,6 +59,8 @@ extern "C" {
 # if @REPLACE_FCNTL@
 #  undef fcntl
 #  define fcntl rpl_fcntl
+# endif
+# if address@hidden@ || @REPLACE_FCNTL@
 extern int fcntl (int fd, int action, ...);
 # endif
 #elif defined GNULIB_POSIXCHECK
@@ -104,14 +106,14 @@ extern int openat (int fd, char const *file, int 
flags, /* mode_t mode */ ...)
 }
 #endif

-/* Fix up the FD_* macros.  */
+/* Fix up the FD_* macros, only known to be missing on mingw.  */

 #ifndef FD_CLOEXEC
 # define FD_CLOEXEC 1
 #endif

 /* Fix up the supported F_* macros.  Intentionally leave other F_*
-   macros undefined.  */
+   macros undefined.  Only known to be missing on mingw.  */

 #ifndef F_DUPFD_CLOEXEC
 # define F_DUPFD_CLOEXEC 0x40000000
@@ -121,6 +123,14 @@ extern int openat (int fd, char const *file, int flags, /* 
mode_t mode */ ...)
 # define GNULIB_defined_F_DUPFD_CLOEXEC 0
 #endif

+#ifndef F_DUPFD
+# define F_DUPFD 1
+#endif
+
+#ifndef F_GETFD
+# define F_GETFD 2
+#endif
+
 /* Fix up the O_* macros.  */

 #if !defined O_DIRECT && defined O_DIRECTIO
diff --git a/m4/fcntl.m4 b/m4/fcntl.m4
index b2a7e6b..4fc5f6b 100644
--- a/m4/fcntl.m4
+++ b/m4/fcntl.m4
@@ -1,4 +1,4 @@
-# fcntl.m4 serial 2
+# fcntl.m4 serial 3
 dnl Copyright (C) 2009 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -7,9 +7,9 @@ dnl with or without modifications, as long as this notice is 
preserved.
 # For now, this module ensures that fcntl()
 # - supports F_DUPFD correctly
 # - supports or emulates F_DUPFD_CLOEXEC
+# - supports F_GETFD
 # Still to be ported to mingw:
-# - F_GETFD, F_SETFD, F_DUPFD
-# - F_DUPFD_CLOEXEC
+# - F_SETFD
 # - F_GETFL, F_SETFL
 # - F_GETOWN, F_SETOWN
 # - F_GETLK, F_SETLK, F_SETLKW
@@ -21,7 +21,7 @@ AC_DEFUN([gl_FUNC_FCNTL],
   AC_REQUIRE([AC_CANONICAL_HOST])
   AC_CHECK_FUNCS_ONCE([fcntl])
   if test $ac_cv_func_fcntl = no; then
-    HAVE_FCNTL=0
+    gl_REPLACE_FCNTL
   else
     dnl cygwin 1.5.x F_DUPFD has wrong errno, and allows negative target
     AC_CACHE_CHECK([whether fcntl handles F_DUPFD correctly],
@@ -74,8 +74,10 @@ AC_DEFUN([gl_REPLACE_FCNTL],
 [
   AC_REQUIRE([gl_FCNTL_H_DEFAULTS])
   AC_CHECK_FUNCS_ONCE([fcntl])
-  if test $ac_cv_func_fcntl = yes; then
+  if test $ac_cv_func_fcntl = no; then
+    HAVE_FCNTL=0
+  else
     REPLACE_FCNTL=1
-    AC_LIBOBJ([fcntl])
   fi
+  AC_LIBOBJ([fcntl])
 ])
diff --git a/m4/fcntl_h.m4 b/m4/fcntl_h.m4
index 3825adf..12e435e 100644
--- a/m4/fcntl_h.m4
+++ b/m4/fcntl_h.m4
@@ -1,4 +1,4 @@
-# serial 7
+# serial 8
 # Configure fcntl.h.
 dnl Copyright (C) 2006, 2007, 2009 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
@@ -101,6 +101,7 @@ AC_DEFUN([gl_FCNTL_H_DEFAULTS],
   GNULIB_OPEN=0;    AC_SUBST([GNULIB_OPEN])
   GNULIB_OPENAT=0;  AC_SUBST([GNULIB_OPENAT])
   dnl Assume proper GNU behavior unless another module says otherwise.
+  HAVE_FCNTL=1;     AC_SUBST([HAVE_FCNTL])
   HAVE_OPENAT=1;    AC_SUBST([HAVE_OPENAT])
   REPLACE_FCNTL=0;  AC_SUBST([REPLACE_FCNTL])
   REPLACE_OPEN=0;   AC_SUBST([REPLACE_OPEN])
diff --git a/modules/fcntl b/modules/fcntl
index b4b2ae4..14298b8 100644
--- a/modules/fcntl
+++ b/modules/fcntl
@@ -1,11 +1,12 @@
 Description:
-Support for fcntl() action F_DUPFD_CLOEXEC.
+Support for fcntl() action F_DUPFD, F_DUPFD_CLOEXEC, F_GETFD.

 Files:
 m4/fcntl.m4
 lib/fcntl.c

 Depends-on:
+dup2
 fcntl-h

 configure.ac:
diff --git a/modules/fcntl-h b/modules/fcntl-h
index 322920a..aa6d8a0 100644
--- a/modules/fcntl-h
+++ b/modules/fcntl-h
@@ -29,6 +29,7 @@ fcntl.h: fcntl.in.h $(LINK_WARNING_H) $(ARG_NONNULL_H)
              -e 's|@''GNULIB_FCNTL''@|$(GNULIB_FCNTL)|g' \
              -e 's|@''GNULIB_OPEN''@|$(GNULIB_OPEN)|g' \
              -e 's|@''GNULIB_OPENAT''@|$(GNULIB_OPENAT)|g' \
+             -e 's|@''HAVE_FCNTL''@|$(HAVE_FCNTL)|g' \
              -e 's|@''HAVE_OPENAT''@|$(HAVE_OPENAT)|g' \
              -e 's|@''REPLACE_FCNTL''@|$(REPLACE_FCNTL)|g' \
              -e 's|@''REPLACE_OPEN''@|$(REPLACE_OPEN)|g' \
diff --git a/tests/test-fcntl.c b/tests/test-fcntl.c
index 4dd1995..a7be425 100644
--- a/tests/test-fcntl.c
+++ b/tests/test-fcntl.c
@@ -143,22 +143,16 @@ check_flags (void)
 {
   switch (0)
     {
-#ifdef F_DUPFD
     case F_DUPFD:
-# if F_DUPFD
-# endif
+#if F_DUPFD
 #endif

-#ifdef F_DUPFD_CLOEXEC
     case F_DUPFD_CLOEXEC:
-# if F_DUPFD_CLOEXEC
-# endif
+#if F_DUPFD_CLOEXEC
 #endif

-#ifdef F_GETFD
     case F_GETFD:
-# if F_GETFD
-# endif
+#if F_GETFD
 #endif

 #ifdef F_SETFD
@@ -224,8 +218,6 @@ main (int argc, char **argv)
   ASSERT (func2 (3, 0x80000000) == 0x80000000);
   check_flags ();

-#if HAVE_FCNTL
-
   /* Assume std descriptors were provided by invoker, and ignore fds
      that might have been inherited.  */
   fd = creat (file, 0600);
@@ -318,11 +310,30 @@ main (int argc, char **argv)
   ASSERT (is_mode (fd + 2, O_TEXT));
   ASSERT (close (fd + 2) == 0);

+  /* Test F_GETFD.  */
+  errno = 0;
+  ASSERT (fcntl (-1, F_GETFD) == -1);
+  ASSERT (errno == EBADF);
+  errno = 0;
+  ASSERT (fcntl (fd + 1, F_GETFD) == -1);
+  ASSERT (errno == EBADF);
+  errno = 0;
+  ASSERT (fcntl (10000000, F_GETFD) == -1);
+  ASSERT (errno == EBADF);
+  {
+    int result = fcntl (fd, F_GETFD);
+    ASSERT (0 <= result);
+    ASSERT ((result & FD_CLOEXEC) == FD_CLOEXEC);
+    ASSERT (dup (fd) == fd + 1);
+    result = fcntl (fd + 1, F_GETFD);
+    ASSERT (0 <= result);
+    ASSERT ((result & FD_CLOEXEC) == 0);
+    ASSERT (close (fd + 1) == 0);
+  }
+
   /* Cleanup.  */
   ASSERT (close (fd) == 0);
   ASSERT (unlink (file) == 0);

-#endif /* HAVE_FCNTL */
-
   return 0;
 }
-- 
1.6.4.2


>From d630f79230de9f20e27ea512bafaacbc1cbec277 Mon Sep 17 00:00:00 2001
From: Eric Blake <address@hidden>
Date: Wed, 16 Dec 2009 10:07:13 -0700
Subject: [PATCH 5/5] fcntl: use to simplify other modules
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Let fcntl do the work, instead of copying code into other modules.

* modules/cloexec (Depends-on): Add fcntl.
* modules/fchdir (Depends-on): Likewise.
* modules/fd-safer-flag (Depends-on): Likewise.
* modules/unistd-safer (Depends-on): Likewise.
* modules/dup3 (configure.ac): Set module indicator.
* m4/fchdir.m4 (gl_FUNC_FCHDIR): Replace fcntl if fchdir is
missing.
* lib/fchdir.c (_gl_register_dup): Fix comment.
* lib/cloexec.c (dup_cloexec): Simplify, by relying on fcntl.
* lib/dup-safer.c (dup_safer): Likewise.
* lib/dup-safer-flag.c (dup_safer_flag): Likewise.
* lib/dup3.c (dup3): Likewise.
* tests/test-fchdir.c (main): Enhance test.
Fixes a dup_cloexec bug reported by Ondřej Vašík.

Signed-off-by: Eric Blake <address@hidden>
---
 ChangeLog             |   16 ++++++
 lib/cloexec.c         |   88 ++--------------------------------
 lib/dup-safer-flag.c  |   14 +-----
 lib/dup-safer.c       |    6 --
 lib/dup3.c            |  128 ++++---------------------------------------------
 lib/fchdir.c          |    5 +--
 m4/fchdir.m4          |    3 +-
 modules/cloexec       |    1 +
 modules/dup3          |    1 +
 modules/fchdir        |    1 +
 modules/fd-safer-flag |    3 +-
 modules/unistd-safer  |    1 +
 tests/test-fchdir.c   |    9 ++++
 13 files changed, 49 insertions(+), 227 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 7117e5a..3291499 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,21 @@
 2009-12-16  Eric Blake  <address@hidden>

+       fcntl: use to simplify other modules
+       * modules/cloexec (Depends-on): Add fcntl.
+       * modules/fchdir (Depends-on): Likewise.
+       * modules/fd-safer-flag (Depends-on): Likewise.
+       * modules/unistd-safer (Depends-on): Likewise.
+       * modules/dup3 (configure.ac): Set module indicator.
+       * m4/fchdir.m4 (gl_FUNC_FCHDIR): Replace fcntl if fchdir is
+       missing.
+       * lib/fchdir.c (_gl_register_dup): Fix comment.
+       * lib/cloexec.c (dup_cloexec): Simplify, by relying on fcntl.
+       * lib/dup-safer.c (dup_safer): Likewise.
+       * lib/dup-safer-flag.c (dup_safer_flag): Likewise.
+       * lib/dup3.c (dup3): Likewise.
+       * tests/test-fchdir.c (main): Enhance test.
+       Fixes a dup_cloexec bug reported by Ondřej Vašík.
+
        fcntl: port portions of fcntl to mingw
        * m4/fcntl.m4 (gl_FUNC_FCNTL): Also build fcntl.c on mingw.
        * lib/fcntl.c (fcntl) <F_DUPFD, F_DUPFD_CLOEXEC, F_GETFD>: Provide
diff --git a/lib/cloexec.c b/lib/cloexec.c
index 69b45b4..ec3cd4e 100644
--- a/lib/cloexec.c
+++ b/lib/cloexec.c
@@ -26,14 +26,6 @@
 #include <fcntl.h>
 #include <unistd.h>

-#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
-/* Native Woe32 API.  */
-# define WIN32_LEAN_AND_MEAN
-# include <windows.h>
-# include <io.h>
-#endif
-
-
 /* Set the `FD_CLOEXEC' flag of DESC if VALUE is true,
    or clear the flag if VALUE is false.
    Return 0 on success, or -1 on error with `errno' set.
@@ -47,7 +39,7 @@
 int
 set_cloexec_flag (int desc, bool value)
 {
-#if defined F_GETFD && defined F_SETFD
+#ifdef F_SETFD

   int flags = fcntl (desc, F_GETFD, 0);

@@ -62,7 +54,7 @@ set_cloexec_flag (int desc, bool value)

   return -1;

-#else
+#else /* !F_SETFD */

   /* Use dup2 to reject invalid file descriptors; the cloexec flag
      will be unaffected.  */
@@ -77,7 +69,7 @@ set_cloexec_flag (int desc, bool value)

   /* There is nothing we can do on this kind of platform.  Punt.  */
   return 0;
-#endif
+#endif /* !F_SETFD */
 }


@@ -88,77 +80,5 @@ set_cloexec_flag (int desc, bool value)
 int
 dup_cloexec (int fd)
 {
-  int nfd;
-
-#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
-
-  /* Native Woe32 API.  */
-  HANDLE curr_process = GetCurrentProcess ();
-  HANDLE old_handle = (HANDLE) _get_osfhandle (fd);
-  HANDLE new_handle;
-  int mode;
-
-  if (old_handle == INVALID_HANDLE_VALUE
-      || (mode = setmode (fd, O_BINARY)) == -1)
-    {
-      /* fd is closed, or is open to no handle at all.
-         We cannot duplicate fd in this case, because _open_osfhandle
-         fails for an INVALID_HANDLE_VALUE argument.  */
-      errno = EBADF;
-      return -1;
-    }
-  setmode (fd, mode);
-
-  if (!DuplicateHandle (curr_process,               /* SourceProcessHandle */
-                        old_handle,                 /* SourceHandle */
-                        curr_process,               /* TargetProcessHandle */
-                        (PHANDLE) &new_handle,      /* TargetHandle */
-                        (DWORD) 0,                  /* DesiredAccess */
-                        FALSE,                      /* InheritHandle */
-                        DUPLICATE_SAME_ACCESS))     /* Options */
-    {
-      /* TODO: Translate GetLastError () into errno.  */
-      errno = EMFILE;
-      return -1;
-    }
-
-  nfd = _open_osfhandle ((long) new_handle, mode | O_NOINHERIT);
-  if (nfd < 0)
-    {
-      CloseHandle (new_handle);
-      errno = EMFILE;
-      return -1;
-    }
-
-#  if REPLACE_FCHDIR
-  if (0 <= nfd)
-    nfd = _gl_register_dup (fd, nfd);
-#  endif
-  return nfd;
-
-#else /* !_WIN32 */
-
-  /* Unix API.  */
-
-# ifdef F_DUPFD_CLOEXEC
-  nfd = fcntl (fd, F_DUPFD_CLOEXEC, 0);
-#  if REPLACE_FCHDIR
-  if (0 <= nfd)
-    nfd = _gl_register_dup (fd, nfd);
-#  endif
-
-# else /* !F_DUPFD_CLOEXEC */
-  nfd = dup (fd);
-  if (0 <= nfd && set_cloexec_flag (nfd, true) < 0)
-    {
-      int saved_errno = errno;
-      close (nfd);
-      nfd = -1;
-      errno = saved_errno;
-    }
-# endif /* !F_DUPFD_CLOEXEC */
-
-  return nfd;
-
-#endif /* !_WIN32 */
+  return fcntl (fd, F_DUPFD_CLOEXEC, 0);
 }
diff --git a/lib/dup-safer-flag.c b/lib/dup-safer-flag.c
index 3549d0d..b76c6b6 100644
--- a/lib/dup-safer-flag.c
+++ b/lib/dup-safer-flag.c
@@ -39,16 +39,6 @@
 int
 dup_safer_flag (int fd, int flag)
 {
-  if (flag & O_CLOEXEC)
-    {
-#if defined F_DUPFD_CLOEXEC && !REPLACE_FCHDIR
-      return fcntl (fd, F_DUPFD_CLOEXEC, STDERR_FILENO + 1);
-#else
-      /* fd_safer_flag calls us back, but eventually the recursion
-         unwinds and does the right thing.  */
-      fd = dup_cloexec (fd);
-      return fd_safer_flag (fd, flag);
-#endif
-    }
-  return dup_safer (fd);
+  return fcntl (fd, (flag & O_CLOEXEC) ? F_DUPFD_CLOEXEC : F_DUPFD,
+                STDERR_FILENO + 1);
 }
diff --git a/lib/dup-safer.c b/lib/dup-safer.c
index bb11ba5..ee29d3d 100644
--- a/lib/dup-safer.c
+++ b/lib/dup-safer.c
@@ -31,11 +31,5 @@
 int
 dup_safer (int fd)
 {
-#if defined F_DUPFD && !REPLACE_FCHDIR
   return fcntl (fd, F_DUPFD, STDERR_FILENO + 1);
-#else
-  /* fd_safer calls us back, but eventually the recursion unwinds and
-     does the right thing.  */
-  return fd_safer (dup (fd));
-#endif
 }
diff --git a/lib/dup3.c b/lib/dup3.c
index c61b9ba..85412c7 100644
--- a/lib/dup3.c
+++ b/lib/dup3.c
@@ -74,7 +74,7 @@ dup3 (int oldfd, int newfd, int flags)
   }
 #endif

-  if (oldfd < 0 || newfd < 0 || newfd >= getdtablesize ())
+  if (newfd < 0 || newfd >= getdtablesize () || fcntl (oldfd, F_GETFD) == -1)
     {
       errno = EBADF;
       return -1;
@@ -95,130 +95,23 @@ dup3 (int oldfd, int newfd, int flags)
       return -1;
     }

-#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
-/* Native Woe32 API.  */
-
   if (flags & O_CLOEXEC)
     {
-      /* Neither dup() nor dup2() can create a file descriptor with
-         O_CLOEXEC = O_NOINHERIT set.  We need to use the low-level function
-         _open_osfhandle for this.  Iterate until all file descriptors less
-         than newfd are filled up.  */
-      HANDLE curr_process = GetCurrentProcess ();
-      HANDLE old_handle = (HANDLE) _get_osfhandle (oldfd);
-      unsigned char fds_to_close[OPEN_MAX_MAX / CHAR_BIT];
-      unsigned int fds_to_close_bound = 0;
       int result;
-
-      if (old_handle == INVALID_HANDLE_VALUE)
-        {
-          /* oldfd is not open, or is an unassigned standard file
-             descriptor.  */
-          errno = EBADF;
-          return -1;
-        }
-
       close (newfd);
-
-      for (;;)
+      result = fcntl (oldfd, F_DUPFD_CLOEXEC, newfd);
+      if (newfd < result)
         {
-          HANDLE new_handle;
-          int duplicated_fd;
-          unsigned int index;
-
-          if (!DuplicateHandle (curr_process,         /* SourceProcessHandle */
-                                old_handle,           /* SourceHandle */
-                                curr_process,         /* TargetProcessHandle */
-                                (PHANDLE) &new_handle, /* TargetHandle */
-                                (DWORD) 0,            /* DesiredAccess */
-                                FALSE,                /* InheritHandle */
-                                DUPLICATE_SAME_ACCESS)) /* Options */
-            {
-              errno = EBADF; /* arbitrary */
-              result = -1;
-              break;
-            }
-          duplicated_fd = _open_osfhandle ((long) new_handle, flags);
-          if (duplicated_fd < 0)
-            {
-              CloseHandle (new_handle);
-              result = -1;
-              break;
-            }
-          if (duplicated_fd > newfd)
-            /* Shouldn't happen, since newfd is still closed.  */
-            abort ();
-          if (duplicated_fd == newfd)
-            {
-              result = newfd;
-              break;
-            }
-
-          /* Set the bit duplicated_fd in fds_to_close[].  */
-          index = (unsigned int) duplicated_fd / CHAR_BIT;
-          if (index >= fds_to_close_bound)
-            {
-              if (index >= sizeof (fds_to_close))
-                /* Need to increase OPEN_MAX_MAX.  */
-                abort ();
-              memset (fds_to_close + fds_to_close_bound, '\0',
-                      index + 1 - fds_to_close_bound);
-              fds_to_close_bound = index + 1;
-            }
-          fds_to_close[index] |= 1 << ((unsigned int) duplicated_fd % 
CHAR_BIT);
+          close (result);
+          errno = EIO;
+          result = -1;
         }
-
-      /* Close the previous fds that turned out to be too small.  */
-      {
-        int saved_errno = errno;
-        unsigned int duplicated_fd;
-
-        for (duplicated_fd = 0;
-             duplicated_fd < fds_to_close_bound * CHAR_BIT;
-             duplicated_fd++)
-          if ((fds_to_close[duplicated_fd / CHAR_BIT]
-               >> (duplicated_fd % CHAR_BIT))
-              & 1)
-            close (duplicated_fd);
-
-        errno = saved_errno;
-      }
-
-#if REPLACE_FCHDIR
-      if (result == newfd)
-        result = _gl_register_dup (oldfd, newfd);
-#endif
-      return result;
+      if (result < 0)
+        return -1;
     }
-
-  if (dup2 (oldfd, newfd) < 0)
-    return -1;
-
-#else
-/* Unix API.  */
-
-  if (dup2 (oldfd, newfd) < 0)
+  else if (dup2 (oldfd, newfd) < 0)
     return -1;

-  /* POSIX <http://www.opengroup.org/onlinepubs/9699919799/functions/dup.html>
-     says that initially, the FD_CLOEXEC flag is cleared on newfd.  */
-
-  if (flags & O_CLOEXEC)
-    {
-      int fcntl_flags;
-
-      if ((fcntl_flags = fcntl (newfd, F_GETFD, 0)) < 0
-          || fcntl (newfd, F_SETFD, fcntl_flags | FD_CLOEXEC) == -1)
-        {
-          int saved_errno = errno;
-          close (newfd);
-          errno = saved_errno;
-          return -1;
-        }
-    }
-
-#endif
-
 #if O_BINARY
   if (flags & O_BINARY)
     setmode (newfd, O_BINARY);
@@ -226,8 +119,5 @@ dup3 (int oldfd, int newfd, int flags)
     setmode (newfd, O_TEXT);
 #endif

-#if REPLACE_FCHDIR
-  newfd = _gl_register_dup (oldfd, newfd);
-#endif
   return newfd;
 }
diff --git a/lib/fchdir.c b/lib/fchdir.c
index 5930940..8c0ff13 100644
--- a/lib/fchdir.c
+++ b/lib/fchdir.c
@@ -168,10 +168,7 @@ _gl_register_fd (int fd, const char *filename)
    and fcntl.  Both arguments must be valid and distinct file
    descriptors.  Close NEWFD and return -1 if OLDFD is tracking a
    directory, but there is insufficient memory to track the same
-   directory in NEWFD; otherwise return NEWFD.
-
-   FIXME: Need to implement rpl_fcntl in gnulib, and have it call
-   this.  */
+   directory in NEWFD; otherwise return NEWFD.  */
 int
 _gl_register_dup (int oldfd, int newfd)
 {
diff --git a/m4/fchdir.m4 b/m4/fchdir.m4
index e0240a1..fcdf62e 100644
--- a/m4/fchdir.m4
+++ b/m4/fchdir.m4
@@ -1,4 +1,4 @@
-# fchdir.m4 serial 12
+# fchdir.m4 serial 13
 dnl Copyright (C) 2006-2009 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -26,6 +26,7 @@ AC_DEFUN([gl_FUNC_FCHDIR],
     gl_REPLACE_CLOSE
     gl_REPLACE_DUP2
     dnl dup3 is already unconditionally replaced
+    gl_REPLACE_FCNTL
     gl_REPLACE_DIRENT_H
     AC_CACHE_CHECK([whether open can visit directories],
       [gl_cv_func_open_directory_works],
diff --git a/modules/cloexec b/modules/cloexec
index bb2767d..88fb6d3 100644
--- a/modules/cloexec
+++ b/modules/cloexec
@@ -8,6 +8,7 @@ m4/cloexec.m4

 Depends-on:
 dup2
+fcntl
 stdbool

 configure.ac:
diff --git a/modules/dup3 b/modules/dup3
index 4665044..07228a3 100644
--- a/modules/dup3
+++ b/modules/dup3
@@ -13,6 +13,7 @@ getdtablesize

 configure.ac:
 gl_FUNC_DUP3
+gl_MODULE_INDICATOR([dup3])
 gl_UNISTD_MODULE_INDICATOR([dup3])

 Makefile.am:
diff --git a/modules/fchdir b/modules/fchdir
index 5bae7d6..46b481f 100644
--- a/modules/fchdir
+++ b/modules/fchdir
@@ -10,6 +10,7 @@ close
 dirent
 dirfd
 dup2
+fcntl
 fcntl-h
 include_next
 malloc-posix
diff --git a/modules/fd-safer-flag b/modules/fd-safer-flag
index 887b797..0a2df83 100644
--- a/modules/fd-safer-flag
+++ b/modules/fd-safer-flag
@@ -7,8 +7,9 @@ lib/fd-safer-flag.c
 lib/dup-safer-flag.c

 Depends-on:
-unistd-safer
 cloexec
+fcntl
+unistd-safer

 configure.ac:
 gl_MODULE_INDICATOR([fd-safer-flag])
diff --git a/modules/unistd-safer b/modules/unistd-safer
index 86e23ab..dfa5672 100644
--- a/modules/unistd-safer
+++ b/modules/unistd-safer
@@ -10,6 +10,7 @@ lib/unistd-safer.h
 m4/unistd-safer.m4

 Depends-on:
+fcntl
 unistd

 configure.ac:
diff --git a/tests/test-fchdir.c b/tests/test-fchdir.c
index 1906559..75819eb 100644
--- a/tests/test-fchdir.c
+++ b/tests/test-fchdir.c
@@ -89,6 +89,15 @@ main (void)
           ASSERT (dup_cloexec (fd) == new_fd);
           ASSERT (dup2 (new_fd, fd) == fd);
           ASSERT (close (new_fd) == 0);
+          ASSERT (fcntl (fd, F_DUPFD_CLOEXEC, new_fd) == new_fd);
+          ASSERT (close (fd) == 0);
+          ASSERT (fcntl (new_fd, F_DUPFD, fd) == fd);
+          ASSERT (close (new_fd) == 0);
+#if GNULIB_DUP3
+          ASSERT (dup3 (fd, new_fd, 0) == new_fd);
+          ASSERT (dup3 (new_fd, fd, 0) == fd);
+          ASSERT (close (new_fd) == 0);
+#endif
         }
     }

-- 
1.6.4.2








reply via email to

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