changeset 12455:dbc90a3fc4ed

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 <ebb9@byu.net>
author Eric Blake <ebb9@byu.net>
date Tue, 08 Dec 2009 12:10:52 -0700
parents f7624052e60d
children f3aceada3c52
files ChangeLog doc/posix-functions/fcntl.texi lib/fcntl.c m4/fcntl.m4
diffstat 4 files changed, 82 insertions(+), 12 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,12 @@
 2009-12-16  Eric Blake  <ebb9@byu.net>
 
+	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.
--- a/doc/posix-functions/fcntl.texi
+++ b/doc/posix-functions/fcntl.texi
@@ -15,6 +15,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.
+
+@item
+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:
--- 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 @@
   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 @@
           {
             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 @@
                 result = -1;
               }
           }
-#if REPLACE_FCHDIR
-        if (0 <= result)
-          result = _gl_register_dup (fd, result);
-#endif
         break;
       } /* F_DUPFD_CLOEXEC */
 
--- 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 F_DUPFD correctly
 # - 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
@@ -19,10 +18,33 @@
   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 @@
            [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
+])