From c246263698fcd636f1333b0d12c80d7ed581e980 Mon Sep 17 00:00:00 2001 From: Bruno Haible Date: Mon, 24 Aug 2009 00:17:47 +0200 Subject: [PATCH] New module 'dup3'. --- ChangeLog | 12 +++ doc/glibc-functions/dup3.texi | 8 +- lib/dup3.c | 200 ++++++++++++++++++++++++++++++++++++++++++ lib/unistd.in.h | 21 +++++ m4/dup3.m4 | 19 ++++ m4/unistd_h.m4 | 4 +- modules/dup3 | 27 ++++++ modules/unistd | 2 + 8 files changed, 288 insertions(+), 5 deletions(-) create mode 100644 lib/dup3.c create mode 100644 m4/dup3.m4 create mode 100644 modules/dup3 diff --git a/ChangeLog b/ChangeLog index 748419faf..80a2e9687 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,17 @@ 2009-08-23 Bruno Haible + New module 'dup3'. + * lib/unistd.in.h (dup3): New declaration. + * lib/dup3.c: New file. + * m4/dup3.m4: New file. + * m4/unistd_h.m4 (gl_UNISTD_H_DEFAULTS): Initialize GNULIB_DUP3 and + HAVE_DUP3. + * modules/unistd (Makefile.am): Substitute GNULIB_DUP3 and HAVE_DUP3. + * modules/dup3: New file. + * doc/glibc-functions/dup3.texi: Mention the new module. + +2009-08-23 Bruno Haible + Tweak the dup2 test. * tests/test-dup2.c (main): Create the test file empty. Verify that an out-of-range fd yields EBADF. Verify that after writing to /dev/null, diff --git a/doc/glibc-functions/dup3.texi b/doc/glibc-functions/dup3.texi index 01dee9b27..c48d76f00 100644 --- a/doc/glibc-functions/dup3.texi +++ b/doc/glibc-functions/dup3.texi @@ -2,15 +2,15 @@ @subsection @code{dup3} @findex dup3 -Gnulib module: --- +Gnulib module: dup3 Portability problems fixed by Gnulib: @itemize +@item +This function is missing on all non-glibc 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, mingw, Interix 3.5, BeOS. @end itemize Portability problems not fixed by Gnulib: @itemize -@item -This function is missing on all non-glibc 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, mingw, Interix 3.5, BeOS. @end itemize diff --git a/lib/dup3.c b/lib/dup3.c new file mode 100644 index 000000000..b9fb341f3 --- /dev/null +++ b/lib/dup3.c @@ -0,0 +1,200 @@ +/* Copy a file descriptor, applying specific flags. + 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 2, 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, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#include + +/* Specification. */ +#include + +#include +#include +#include + +#include "binary-io.h" + +#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ +/* Native Woe32 API. */ + +/* Get declarations of the Win32 API functions. */ +# define WIN32_LEAN_AND_MEAN +# include + +/* Upper bound on getdtablesize(). See lib/getdtablesize.c. */ +# define OPEN_MAX_MAX 0x10000 + +#else +/* Unix API. */ + +# ifndef O_CLOEXEC +# define O_CLOEXEC 0 +# endif + +#endif + +int +dup3 (int oldfd, int newfd, int flags) +{ + if (oldfd < 0 || newfd < 0 || newfd >= getdtablesize ()) + { + errno = EBADF; + return -1; + } + + if (newfd == oldfd) + { + errno = EINVAL; + return -1; + } + + /* Check the supported flags. + Note that O_NONBLOCK is not supported, because setting it on newfd + would implicitly also set it on oldfd. */ + if ((flags & ~(O_CLOEXEC | O_BINARY | O_TEXT)) != 0) + { + errno = EINVAL; + 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 (;;) + { + 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 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; + } + + return result; + } + + if (dup2 (oldfd, newfd) < 0) + return -1; + +#else +/* Unix API. */ + + if (dup2 (oldfd, newfd) < 0) + return -1; + + /* POSIX + 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); + else if (flags & O_TEXT) + setmode (newfd, O_TEXT); +#endif + + return newfd; +} diff --git a/lib/unistd.in.h b/lib/unistd.in.h index a86935ba1..7dd98346c 100644 --- a/lib/unistd.in.h +++ b/lib/unistd.in.h @@ -178,6 +178,27 @@ extern int dup2 (int oldfd, int newfd); #endif +#if @GNULIB_DUP3@ +# if !@HAVE_DUP3@ +/* Copy the file descriptor OLDFD into file descriptor NEWFD, with the + specified flags. + The flags are a bitmask, possibly including O_CLOEXEC (defined in ) + and O_TEXT, O_BINARY (defined in "binary-io.h"). + Close NEWFD first if it is open. + Return newfd if successful, otherwise -1 and errno set. + See the Linux man page at + . */ +extern int dup3 (int oldfd, int newfd, int flags); +# endif +#elif defined GNULIB_POSIXCHECK +# undef dup3 +# define dup3(o,n,f) \ + (GL_LINK_WARNING ("dup3 is unportable - " \ + "use gnulib module dup3 for portability"), \ + dup3 (o, n, f)) +#endif + + #if @GNULIB_ENVIRON@ # if !@HAVE_DECL_ENVIRON@ /* Set of environment variables and values. An array of strings of the form diff --git a/m4/dup3.m4 b/m4/dup3.m4 new file mode 100644 index 000000000..e4fe7a6ab --- /dev/null +++ b/m4/dup3.m4 @@ -0,0 +1,19 @@ +# dup3.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. + +AC_DEFUN([gl_FUNC_DUP3], +[ + AC_REQUIRE([gl_UNISTD_H_DEFAULTS]) + + dnl Persuade glibc to declare dup3(). + AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS]) + + AC_CHECK_FUNCS_ONCE([dup3]) + if test $ac_cv_func_dup3 != yes; then + HAVE_DUP3=0 + AC_LIBOBJ([dup3]) + fi +]) diff --git a/m4/unistd_h.m4 b/m4/unistd_h.m4 index bff2f283d..53fc330be 100644 --- a/m4/unistd_h.m4 +++ b/m4/unistd_h.m4 @@ -1,4 +1,4 @@ -# unistd_h.m4 serial 20 +# unistd_h.m4 serial 21 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, @@ -35,6 +35,7 @@ AC_DEFUN([gl_UNISTD_H_DEFAULTS], GNULIB_CHOWN=0; AC_SUBST([GNULIB_CHOWN]) GNULIB_CLOSE=0; AC_SUBST([GNULIB_CLOSE]) GNULIB_DUP2=0; AC_SUBST([GNULIB_DUP2]) + GNULIB_DUP3=0; AC_SUBST([GNULIB_DUP3]) GNULIB_ENVIRON=0; AC_SUBST([GNULIB_ENVIRON]) GNULIB_EUIDACCESS=0; AC_SUBST([GNULIB_EUIDACCESS]) GNULIB_FCHDIR=0; AC_SUBST([GNULIB_FCHDIR]) @@ -58,6 +59,7 @@ AC_DEFUN([gl_UNISTD_H_DEFAULTS], GNULIB_WRITE=0; AC_SUBST([GNULIB_WRITE]) dnl Assume proper GNU behavior unless another module says otherwise. HAVE_DUP2=1; AC_SUBST([HAVE_DUP2]) + HAVE_DUP3=1; AC_SUBST([HAVE_DUP3]) HAVE_EUIDACCESS=1; AC_SUBST([HAVE_EUIDACCESS]) HAVE_FSYNC=1; AC_SUBST([HAVE_FSYNC]) HAVE_FTRUNCATE=1; AC_SUBST([HAVE_FTRUNCATE]) diff --git a/modules/dup3 b/modules/dup3 new file mode 100644 index 000000000..38bdcf039 --- /dev/null +++ b/modules/dup3 @@ -0,0 +1,27 @@ +Description: +dup3() function: copy a file descriptor, applying specific flags. + +Files: +lib/dup3.c +m4/dup3.m4 + +Depends-on: +unistd +fcntl +binary-io +getdtablesize + +configure.ac: +gl_FUNC_DUP3 +gl_UNISTD_MODULE_INDICATOR([dup3]) + +Makefile.am: + +Include: + + +License: +LGPL + +Maintainer: +Bruno Haible, Jim Meyering diff --git a/modules/unistd b/modules/unistd index 7fed07d4a..66fc1a32a 100644 --- a/modules/unistd +++ b/modules/unistd @@ -28,6 +28,7 @@ unistd.h: unistd.in.h -e 's|@''GNULIB_CHOWN''@|$(GNULIB_CHOWN)|g' \ -e 's|@''GNULIB_CLOSE''@|$(GNULIB_CLOSE)|g' \ -e 's|@''GNULIB_DUP2''@|$(GNULIB_DUP2)|g' \ + -e 's|@''GNULIB_DUP3''@|$(GNULIB_DUP3)|g' \ -e 's|@''GNULIB_ENVIRON''@|$(GNULIB_ENVIRON)|g' \ -e 's|@''GNULIB_EUIDACCESS''@|$(GNULIB_EUIDACCESS)|g' \ -e 's|@''GNULIB_FCHDIR''@|$(GNULIB_FCHDIR)|g' \ @@ -50,6 +51,7 @@ unistd.h: unistd.in.h -e 's|@''GNULIB_UNISTD_H_SIGPIPE''@|$(GNULIB_UNISTD_H_SIGPIPE)|g' \ -e 's|@''GNULIB_WRITE''@|$(GNULIB_WRITE)|g' \ -e 's|@''HAVE_DUP2''@|$(HAVE_DUP2)|g' \ + -e 's|@''HAVE_DUP3''@|$(HAVE_DUP3)|g' \ -e 's|@''HAVE_EUIDACCESS''@|$(HAVE_EUIDACCESS)|g' \ -e 's|@''HAVE_FSYNC''@|$(HAVE_FSYNC)|g' \ -e 's|@''HAVE_FTRUNCATE''@|$(HAVE_FTRUNCATE)|g' \ -- 2.11.0