[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
fcntl for mingw
From: |
Eric Blake |
Subject: |
fcntl for mingw |
Date: |
Thu, 10 Dec 2009 23:24:04 +0000 (UTC) |
User-agent: |
Loom/3.14 (http://gmane.org/) |
Here's what I'm currently playing with. I'll wait another day or two for any
comments, first; but it passes for me on Linux, cygwin 1.5, cygwin 1.7, and
mingw.
Eric Blake (4):
[1/4] fcntl: support F_DUPFD_CLOEXEC on systems with fcntl
Provide POSIX semantics (minus the atomicity factor) in most systems.
[2/4] fcntl: work around cygwin bug in F_DUPFD
Work around a cygwin 1.5 bug uncovered in the previous patch.
[3/4] fcntl: port portions of fcntl to mingw
Port portions of fcntl to mingw.
[4/4] dup3, fchdir: rely more on fcntl
Directly use fcntl where it makes more sense.
>From 1823f51e958ee55c0d10ea21b973249ef533d02e Mon Sep 17 00:00:00 2001
From: Eric Blake <address@hidden>
Date: Mon, 7 Dec 2009 11:50:59 -0700
Subject: [PATCH 1/4] 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.
(Depends-on): Add cloexec.
(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.
(openat) [GNULIB_POSIXCHECK]: Avoid problematic macro.
* 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 | 8 +-
doc/posix-headers/fcntl.texi | 4 +
lib/fcntl.c | 110 ++++++++++++++
lib/fcntl.in.h | 28 +++-
m4/fcntl.m4 | 43 ++++++
m4/fcntl_h.m4 | 6 +-
modules/fcntl | 7 +-
modules/fcntl-h | 6 +-
modules/fcntl-tests | 12 ++
tests/test-fcntl.c | 328 ++++++++++++++++++++++++++++++++++++++++
11 files changed, 555 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 fec55df..af24729 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,21 @@
2009-12-10 Eric Blake <address@hidden>
+ fcntl: support F_DUPFD_CLOEXEC on systems with fcntl
+ * modules/fcntl (Files): List new files.
+ (Depends-on): Add cloexec.
+ (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.
+ (openat) [GNULIB_POSIXCHECK]: Avoid problematic macro.
+ * 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.
+
setenv: relax requirement in light of POSIX ruling
* m4/setenv.m4 (gl_FUNC_SETENV_SEPARATE): Test handling of "" but
not NULL.
diff --git a/doc/posix-functions/fcntl.texi b/doc/posix-functions/fcntl.texi
index 143ac63..31dca10 100644
--- a/doc/posix-functions/fcntl.texi
+++ b/doc/posix-functions/fcntl.texi
@@ -4,10 +4,16 @@ 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, although the replacement is not atomic:
+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.
@end itemize
Portability problems not fixed by Gnulib:
diff --git a/doc/posix-headers/fcntl.texi b/doc/posix-headers/fcntl.texi
index 340cf28..2475c9b 100644
--- a/doc/posix-headers/fcntl.texi
+++ b/doc/posix-headers/fcntl.texi
@@ -27,6 +27,10 @@ fcntl.h
mingw.
@item
address@hidden is not defined on some platforms (but for now,
+the replacement is only defined on platforms where @code{fcntl} exists).
+
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:
diff --git a/lib/fcntl.c b/lib/fcntl.c
new file mode 100644
index 0000000..d8442cc
--- /dev/null
+++ b/lib/fcntl.c
@@ -0,0 +1,110 @@
+/* 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>
+
+#include "cloexec.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 - duplicate FD, with ACTION 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 ACTION 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, ...)
+{
+ 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 it exists (that is, if
+ gl_F_DUPFD_CLOEXEC is 0 but F_DUPFD_CLOEXEC is defined).
+ (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
+ don't cache failure if F_DUPFD gets the same EINVAL
+ failure. 0 = unknown, 1 = yes, -1 = no. */
+ static int have_dupfd_cloexec = -gl_F_DUPFD_CLOEXEC;
+ 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)
+ {
+ if (set_cloexec_flag (result, true) < 0)
+ {
+ 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;
+ }
+
+ 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 0ae8213..11c64e3 100644
--- a/lib/fcntl.in.h
+++ b/lib/fcntl.in.h
@@ -53,12 +53,32 @@
extern "C" {
#endif
+#if @GNULIB_FCNTL@
+# if @REPLACE_FCNTL@
+# undef fcntl
+# define fcntl rpl_fcntl
+extern int fcntl (int fd, int action, ...);
+# ifndef F_DUPFD_CLOEXEC
+# define F_DUPFD_CLOEXEC 0x40000000
+# define gl_F_DUPFD_CLOEXEC 1
+# endif
+# endif
+/* Witness variable: 1 if gnulib defined F_DUPFD_CLOEXEC, 0 otherwise. */
+# ifndef gl_F_DUPFD_CLOEXEC
+# define gl_F_DUPFD_CLOEXEC 0
+# endif
+#elif defined GNULIB_POSIXCHECK
+/* Can't provide link warning without support for C99 variadic macros. */
+#endif
+
#if @GNULIB_OPEN@
# if @REPLACE_OPEN@
# undef open
# define open rpl_open
extern int open (const char *filename, int flags, ...);
# endif
+#elif defined GNULIB_POSIXCHECK
+/* Can't provide link warning without support for C99 variadic macros. */
#endif
#if @GNULIB_OPENAT@
@@ -67,14 +87,10 @@ extern int open (const char *filename, int flags, ...);
# define openat rpl_openat
# endif
# if address@hidden@ || @REPLACE_OPENAT@
-int openat (int fd, char const *file, int flags, /* mode_t mode */ ...);
+extern int openat (int fd, char const *file, int flags, /* mode_t mode */ ...);
# endif
#elif defined GNULIB_POSIXCHECK
-# undef openat
-# define openat(f,u,g) \
- (GL_LINK_WARNING ("openat is not portable - " \
- "use gnulib module openat for portability"), \
- openat)
+/* Can't provide link warning without support for C99 variadic macros. */
#endif
#ifdef __cplusplus
diff --git a/m4/fcntl.m4 b/m4/fcntl.m4
new file mode 100644
index 0000000..1c33540
--- /dev/null
+++ b/m4/fcntl.m4
@@ -0,0 +1,43 @@
+# 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>
+#if defined __linux__ || !defined F_DUPFD_CLOEXEC
+/* The Linux kernel added F_DUPFD_CLOEXEC in 2.6.24, but 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"])])
+ 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..961026e 100644
--- a/modules/fcntl
+++ b/modules/fcntl
@@ -1,12 +1,17 @@
Description:
-Placeholder for eventual fcntl() replacement.
+Support for fcntl() action F_DUPFD_CLOEXEC.
Files:
+m4/fcntl.m4
+lib/fcntl.c
Depends-on:
+cloexec
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 eb5cce1..60e078e 100644
--- a/modules/fcntl-h
+++ b/modules/fcntl-h
@@ -15,7 +15,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.
@@ -25,11 +25,13 @@ fcntl.h: fcntl.in.h $(LINK_WARNING_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)' \
< $(srcdir)/fcntl.in.h; \
} > address@hidden && \
diff --git a/modules/fcntl-tests b/modules/fcntl-tests
new file mode 100644
index 0000000..4f9e75d
--- /dev/null
+++ b/modules/fcntl-tests
@@ -0,0 +1,12 @@
+Files:
+tests/test-fcntl.c
+
+Depends-on:
+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 7cbb7482507b7f8581bd48c6e38105f213b39c78 Mon Sep 17 00:00:00 2001
From: Eric Blake <address@hidden>
Date: Tue, 8 Dec 2009 12:10:52 -0700
Subject: [PATCH 2/4] 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.
* doc/posix-functions/fcntl.texi (fcntl): Document it.
Signed-off-by: Eric Blake <address@hidden>
---
ChangeLog | 6 ++++++
doc/posix-functions/fcntl.texi | 4 ++++
lib/fcntl.c | 24 ++++++++++++++++++++++--
m4/fcntl.m4 | 36 +++++++++++++++++++++++++++++++-----
4 files changed, 63 insertions(+), 7 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index af24729..635a19b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,11 @@
2009-12-10 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.
+ * doc/posix-functions/fcntl.texi (fcntl): Document it.
+
fcntl: support F_DUPFD_CLOEXEC on systems with fcntl
* modules/fcntl (Files): List new files.
(Depends-on): Add cloexec.
diff --git a/doc/posix-functions/fcntl.texi b/doc/posix-functions/fcntl.texi
index 31dca10..c59f603 100644
--- a/doc/posix-functions/fcntl.texi
+++ b/doc/posix-functions/fcntl.texi
@@ -14,6 +14,10 @@ fcntl
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
+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 d8442cc..eb3736f 100644
--- a/lib/fcntl.c
+++ b/lib/fcntl.c
@@ -54,6 +54,26 @@ rpl_fcntl (int fd, int action, ...)
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);
@@ -80,7 +100,7 @@ rpl_fcntl (int fd, int action, ...)
}
}
else
- result = fcntl (fd, F_DUPFD, target);
+ result = rpl_fcntl (fd, F_DUPFD, target);
if (0 <= result && have_dupfd_cloexec == -1)
{
if (set_cloexec_flag (result, true) < 0)
@@ -96,7 +116,7 @@ rpl_fcntl (int fd, int action, ...)
result = _gl_register_dup (fd, result);
#endif
break;
- }
+ } /* F_DUPFD_CLOEXEC */
default:
{
diff --git a/m4/fcntl.m4 b/m4/fcntl.m4
index 1c33540..40f369e 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
@@ -23,6 +22,23 @@ AC_DEFUN([gl_FUNC_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],
+ [gl_cv_func_fcntl_f_dupfd_works="guessing no"])])
+ if test "$gl_cv_func_fcntl_f_dupfd_works" != yes; then
+ gl_REPLACE_FCNTL
+ AC_DEFINE([FCNTL_DUPFD_BUGGY], [1], [Define this to 1 if F_DUPFD
+ behavior does not match POSIX])
+ fi
+
+ 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([[
@@ -36,8 +52,18 @@ choke me
[gl_cv_func_fcntl_f_dupfd_cloexec=yes],
[gl_cv_func_fcntl_f_dupfd_cloexec="needs runtime check"])])
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 8a90eba1e82d51894b30a0e75a2d0428aaf2bae8 Mon Sep 17 00:00:00 2001
From: Eric Blake <address@hidden>
Date: Fri, 21 Aug 2009 15:19:25 -0600
Subject: [PATCH 3/4] 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.
* 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 | 14 ++++
doc/posix-functions/fcntl.texi | 10 ++-
doc/posix-headers/fcntl.texi | 18 ++---
lib/fcntl.c | 170 ++++++++++++++++++++++++++++++++++++++-
lib/fcntl.in.h | 17 ++++-
m4/fcntl.m4 | 14 ++--
m4/fcntl_h.m4 | 3 +-
modules/fcntl | 2 +-
modules/fcntl-h | 1 +
tests/test-fcntl.c | 37 ++++++---
10 files changed, 246 insertions(+), 40 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index 635a19b..fd96734 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,19 @@
2009-12-10 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.
+ * 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 c59f603..2280460 100644
--- a/doc/posix-functions/fcntl.texi
+++ b/doc/posix-functions/fcntl.texi
@@ -18,11 +18,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, although the replacement
+fails with @code{EINVAL} for unimplemented actions:
+mingw.
@end itemize
Portability problems not fixed by Gnulib:
@itemize
@item
-This function is missing on some platforms:
-mingw.
+This function does not support @code{F_SETFD}, @code{F_GETFL},
address@hidden, @code{F_GETOWN}, @code{F_SETOWN}, @code{F_GETLK},
address@hidden, 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 2475c9b..3237e5f 100644
--- a/doc/posix-headers/fcntl.texi
+++ b/doc/posix-headers/fcntl.texi
@@ -23,12 +23,12 @@ 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
address@hidden is not defined on some platforms (but for now,
-the replacement is only defined on platforms where @code{fcntl} exists).
address@hidden is not defined on many platforms.
@item
@samp{AT_FDCWD}, @samp{AT_EACCESS}, @samp{AT_SYMLINK_NOFOLLOW},
@@ -54,14 +54,10 @@ 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
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
index eb3736f..4f0b6cb 100644
--- a/lib/fcntl.c
+++ b/lib/fcntl.c
@@ -23,15 +23,127 @@
#include <fcntl.h>
#include <errno.h>
+#include <limits.h>
#include <stdarg.h>
#include "cloexec.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 (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 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
@@ -44,7 +156,10 @@
F_DUPFD_CLOEXEC - duplicate FD, with ACTION being the minimum
target fd. If successful, return the duplicate, which will not be
inheritable; otherwise return -1 and set errno.
-*/
+
+ F_GETFD - 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, ...)
@@ -55,7 +170,14 @@ rpl_fcntl (int fd, int action, ...)
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);
@@ -78,6 +200,10 @@ rpl_fcntl (int fd, int action, ...)
{
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 it exists (that is, if
gl_F_DUPFD_CLOEXEC is 0 but F_DUPFD_CLOEXEC is defined).
(We may be running with a glibc that has the macro but with
@@ -111,17 +237,51 @@ rpl_fcntl (int fd, int action, ...)
result = -1;
}
}
-#if REPLACE_FCHDIR
+# if REPLACE_FCHDIR
if (0 <= result)
result = _gl_register_dup (fd, result);
-#endif
+# endif
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 11c64e3..e4ef3b5 100644
--- a/lib/fcntl.in.h
+++ b/lib/fcntl.in.h
@@ -57,12 +57,27 @@ extern "C" {
# if @REPLACE_FCNTL@
# undef fcntl
# define fcntl rpl_fcntl
+# endif
+# if address@hidden@ || @REPLACE_FCNTL@
extern int fcntl (int fd, int action, ...);
+
+/* Provide supported F_* macros. Macros not listed here, and not
+ provided by the implementation, are intentionally left
+ undefined. */
# ifndef F_DUPFD_CLOEXEC
# define F_DUPFD_CLOEXEC 0x40000000
# define gl_F_DUPFD_CLOEXEC 1
# endif
+
+# ifndef F_DUPFD
+# define F_DUPFD 1
+# endif
+
+# ifndef F_GETFD
+# define F_GETFD 2
+# endif
# endif
+
/* Witness variable: 1 if gnulib defined F_DUPFD_CLOEXEC, 0 otherwise. */
# ifndef gl_F_DUPFD_CLOEXEC
# define gl_F_DUPFD_CLOEXEC 0
@@ -97,7 +112,7 @@ extern int openat (int fd, char const *file, int flags, /*
mode_t mode */ ...);
}
#endif
-/* Fix up the FD_* macros. */
+/* Fix up the FD_* macros, missing only on mingw. */
#ifndef FD_CLOEXEC
# define FD_CLOEXEC 1
diff --git a/m4/fcntl.m4 b/m4/fcntl.m4
index 40f369e..eb43317 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
@@ -20,7 +20,7 @@ AC_DEFUN([gl_FUNC_FCNTL],
AC_REQUIRE([gl_FCNTL_H_DEFAULTS])
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],
@@ -62,8 +62,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 961026e..4182747 100644
--- a/modules/fcntl
+++ b/modules/fcntl
@@ -1,5 +1,5 @@
Description:
-Support for fcntl() action F_DUPFD_CLOEXEC.
+Support for fcntl() action F_DUPFD, F_DUPFD_CLOEXEC, F_GETFD.
Files:
m4/fcntl.m4
diff --git a/modules/fcntl-h b/modules/fcntl-h
index 60e078e..6df77dc 100644
--- a/modules/fcntl-h
+++ b/modules/fcntl-h
@@ -28,6 +28,7 @@ fcntl.h: fcntl.in.h $(LINK_WARNING_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 35dc344339de7a6e3ba64a2f0d644bd9200d409b Mon Sep 17 00:00:00 2001
From: Eric Blake <address@hidden>
Date: Thu, 10 Dec 2009 15:42:56 -0700
Subject: [PATCH 4/4] dup3, fchdir: rely more on fcntl
Only call _gl_register_dup at the lowest layers, by making
an fchdir replacement require an fcntl replacement. Let
fcntl do the work, instead of copying code in dup3.
* modules/fchdir (Depends-on): Add fcntl.
* modules/accept4 (Depends-on): Use fcntl-h, not fcntl.
* 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, since REPLACE_FCHDIR
implies that fcntl will do the registration.
* lib/dup3.c (dup3): Likewise. Let fcntl do more work.
* tests/test-fchdir.c (main): Enhance test.
Signed-off-by: Eric Blake <address@hidden>
---
ChangeLog | 12 +++++
lib/cloexec.c | 5 --
lib/dup3.c | 128 ++++-----------------------------------------------
lib/fchdir.c | 5 +--
m4/fchdir.m4 | 3 +-
modules/accept4 | 2 +-
modules/dup3 | 1 +
modules/fchdir | 1 +
tests/test-fchdir.c | 9 ++++
9 files changed, 36 insertions(+), 130 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index fd96734..ee17e1a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,17 @@
2009-12-10 Eric Blake <address@hidden>
+ dup3, fchdir: rely more on fcntl
+ * modules/fchdir (Depends-on): Add fcntl.
+ * modules/accept4 (Depends-on): Use fcntl-h, not fcntl.
+ * 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, since REPLACE_FCHDIR
+ implies that fcntl will do the registration.
+ * lib/dup3.c (dup3): Likewise. Let fcntl do more work.
+ * tests/test-fchdir.c (main): Enhance test.
+
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..96ade8f 100644
--- a/lib/cloexec.c
+++ b/lib/cloexec.c
@@ -142,11 +142,6 @@ dup_cloexec (int fd)
# 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)
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/accept4 b/modules/accept4
index d13127e..6f26071 100644
--- a/modules/accept4
+++ b/modules/accept4
@@ -9,7 +9,7 @@ m4/accept4.m4
Depends-on:
sys_socket
accept
-fcntl
+fcntl-h
binary-io
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/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
- fcntl for mingw,
Eric Blake <=