From fc12f35ab10cca4dc698ddee31b5e2bbeeb54167 Mon Sep 17 00:00:00 2001
From: Eric Blake <eblake@redhat.com>
Date: Tue, 25 Jan 2011 22:06:16 -0700
Subject: [PATCH] fcntl: work around Haiku F_DUPFD bugs

* m4/fcntl.m4 (gl_FUNC_FCNTL): Also catch Haiku errno bug.
* lib/fcntl.c (rpl_fcntl) [F_DUPFD]: Work around Haiku losing
cloexec bit on duplication.
* doc/posix-functions/fcntl.texi (fcntl): Document the bug.

Signed-off-by: Eric Blake <eblake@redhat.com>
---
 ChangeLog                      |  8 ++++++++
 doc/posix-functions/fcntl.texi |  7 ++++++-
 lib/fcntl.c                    | 14 ++++++++++++++
 m4/fcntl.m4                    |  9 +++++++--
 4 files changed, 35 insertions(+), 3 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index c6700ecf9e..4528062279 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2011-01-26  Eric Blake  <eblake@redhat.com>
+
+	fcntl: work around Haiku F_DUPFD bugs
+	* m4/fcntl.m4 (gl_FUNC_FCNTL): Also catch Haiku errno bug.
+	* lib/fcntl.c (rpl_fcntl) [F_DUPFD]: Work around Haiku losing
+	cloexec bit on duplication.
+	* doc/posix-functions/fcntl.texi (fcntl): Document the bug.
+
 2011-01-26  Bruno Haible  <bruno@clisp.org>
 
 	Enable memory leak tests on AIX.
diff --git a/doc/posix-functions/fcntl.texi b/doc/posix-functions/fcntl.texi
index a0511ec370..161af1e796 100644
--- a/doc/posix-functions/fcntl.texi
+++ b/doc/posix-functions/fcntl.texi
@@ -19,7 +19,12 @@ 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.
+Cygwin 1.5.x, Haiku.
+
+@item
+The @code{F_DUPFD} action of this function mistakenly clears
+FD_CLOEXEC on the source descriptor on some platforms:
+Haiku.
 
 @item
 This function is missing on some platforms:
diff --git a/lib/fcntl.c b/lib/fcntl.c
index 25a79c5441..d6a328c1c0 100644
--- a/lib/fcntl.c
+++ b/lib/fcntl.c
@@ -187,7 +187,21 @@ rpl_fcntl (int fd, int action, /* arg */...)
           errno = EINVAL;
         else
           {
+            /* Haiku alpha 2 loses fd flags on original.  */
+            int flags = fcntl (fd, F_GETFD);
+            if (flags < 0)
+              {
+                result = -1;
+                break;
+              }
             result = fcntl (fd, action, target);
+            if (0 <= result && fcntl (fd, F_SETFD, flags) == -1)
+              {
+                int saved_errno = errno;
+                close (result);
+                result = -1;
+                errno = saved_errno;
+              }
 # if REPLACE_FCHDIR
             if (0 <= result)
               result = _gl_register_dup (fd, result);
diff --git a/m4/fcntl.m4 b/m4/fcntl.m4
index 3a86f2ca49..a93ed85a36 100644
--- a/m4/fcntl.m4
+++ b/m4/fcntl.m4
@@ -1,4 +1,4 @@
-# fcntl.m4 serial 3
+# fcntl.m4 serial 4
 dnl Copyright (C) 2009-2011 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -24,11 +24,16 @@ AC_DEFUN([gl_FUNC_FCNTL],
     gl_REPLACE_FCNTL
   else
     dnl cygwin 1.5.x F_DUPFD has wrong errno, and allows negative target
+    dnl haiku alpha 2 F_DUPFD has wrong errno
     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;
+#include <errno.h>
+]], [[int result = 0;
+      if (fcntl (0, F_DUPFD, -1) != -1) result |= 1;
+      if (errno != EINVAL) result |= 2;
+      return result;
          ]])],
          [gl_cv_func_fcntl_f_dupfd_works=yes],
          [gl_cv_func_fcntl_f_dupfd_works=no],
-- 
2.39.5