From c7e1cc415cd60f989fc75b99596e3bd656d3004e Mon Sep 17 00:00:00 2001 From: KO Myung-Hun Date: Thu, 14 Jan 2016 11:23:47 +0900 Subject: [PATCH] dup, dup2, fcntl: support a directory fd on OS/2 kLIBC On OS/2 kLIBC, dup(), dup2() and fcntl() do not work on a directory fd. * lib/dup.c (dup_nothrow): New. * lib/dup2.c (klibc_dup2dirfd): New. dup2() for a directory fd. (klibc_dup2): New. * lib/fcntl.c (klibc_fcntl): New. * m4/dup.m4 (gl_FUNC_DUP): Check if dup() works on a directory fd. * m4/dup2.m4 (gl_FUNC_DUP2): Check if dup2() works on a directory fd. * m4/fcntl.m4 (gl_FUNC_FCNTL): Check if F_DUPFD works on a directory fd. --- lib/dup.c | 25 +++++++++++++++ lib/dup2.c | 51 +++++++++++++++++++++++++++++++ lib/fcntl.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++++ m4/dup.m4 | 22 +++++++++++++- m4/dup2.m4 | 14 ++++++++- m4/fcntl.m4 | 13 +++++++- 6 files changed, 209 insertions(+), 3 deletions(-) diff --git a/lib/dup.c b/lib/dup.c index f1fa5f9ab7..034fcf8d31 100644 --- a/lib/dup.c +++ b/lib/dup.c @@ -45,6 +45,31 @@ dup_nothrow (int fd) return result; } +#elif defined __KLIBC__ +# include +# include + +# include + +static int +dup_nothrow (int fd) +{ + int dupfd; + struct stat sbuf; + + dupfd = dup (fd); + if (dupfd == -1 && errno == ENOTSUP \ + && !fstat (fd, &sbuf) && S_ISDIR (sbuf.st_mode)) + { + char path[_MAX_PATH]; + + /* Get a path from fd */ + if (!__libc_Back_ioFHToPath (fd, path, sizeof (path))) + dupfd = open (path, O_RDONLY); + } + + return dupfd; +} #else # define dup_nothrow dup #endif diff --git a/lib/dup2.c b/lib/dup2.c index c913f473ad..5d026f21fa 100644 --- a/lib/dup2.c +++ b/lib/dup2.c @@ -85,6 +85,57 @@ ms_windows_dup2 (int fd, int desired_fd) # define dup2 ms_windows_dup2 +# elif defined __KLIBC__ + +# include + +static int +klibc_dup2dirfd (int fd, int desired_fd) +{ + int tempfd; + int dupfd; + + tempfd = open ("NUL", O_RDONLY); + if (tempfd == -1) + return -1; + + if (tempfd == desired_fd) + { + close (tempfd); + + char path[_MAX_PATH]; + if (__libc_Back_ioFHToPath (fd, path, sizeof (path))) + return -1; + + return open(path, O_RDONLY); + } + + dupfd = klibc_dup2dirfd (fd, desired_fd); + + close (tempfd); + + return dupfd; +} + +static int +klibc_dup2 (int fd, int desired_fd) +{ + int dupfd; + struct stat sbuf; + + dupfd = dup2 (fd, desired_fd); + if (dupfd == -1 && errno == ENOTSUP \ + && !fstat (fd, &sbuf) && S_ISDIR (sbuf.st_mode)) + { + close (desired_fd); + + return klibc_dup2dirfd (fd, desired_fd); + } + + return dupfd; +} + +# define dup2 klibc_dup2 # endif int diff --git a/lib/fcntl.c b/lib/fcntl.c index 1ccc5acea8..fd17e962f8 100644 --- a/lib/fcntl.c +++ b/lib/fcntl.c @@ -162,6 +162,93 @@ dupfd (int oldfd, int newfd, int flags) } #endif /* W32 */ +#ifdef __KLIBC__ + +# define INCL_DOS +# include + +static int +klibc_fcntl (int fd, int action, /* arg */...) +{ + va_list arg_ptr; + int arg; + struct stat sbuf; + int result = -1; + + va_start (arg_ptr, action); + arg = va_arg (arg_ptr, int); + result = fcntl (fd, action, arg); + /* EPERM for F_DUPFD, ENOTSUP for others */ + if (result == -1 && (errno == EPERM || errno == ENOTSUP) + && !fstat (fd, &sbuf) && S_ISDIR (sbuf.st_mode)) + { + ULONG ulMode; + + switch (action) + { + case F_DUPFD: + /* Find available fd */ + while (fcntl (arg, F_GETFL) != -1 || errno != EBADF) + arg++; + + result = dup2 (fd, arg); + break; + + /* Using underlying APIs is right ? */ + case F_GETFD: + if (DosQueryFHState (fd, &ulMode)) + break; + + result = (ulMode & OPEN_FLAGS_NOINHERIT) ? FD_CLOEXEC : 0; + break; + + case F_SETFD: + if (arg & ~FD_CLOEXEC) + break; + + if (DosQueryFHState (fd, &ulMode)) + break; + + if (arg & FD_CLOEXEC) + ulMode |= OPEN_FLAGS_NOINHERIT; + else + ulMode &= ~OPEN_FLAGS_NOINHERIT; + + /* Filter supported flags. */ + ulMode &= (OPEN_FLAGS_WRITE_THROUGH | OPEN_FLAGS_FAIL_ON_ERROR + | OPEN_FLAGS_NO_CACHE | OPEN_FLAGS_NOINHERIT); + + if (DosSetFHState (fd, ulMode)) + break; + + result = 0; + break; + + case F_GETFL: + result = 0; + break; + + case F_SETFL: + if (arg != 0) + break; + + result = 0; + break; + + default : + errno = EINVAL; + break; + } + } + + va_end (arg_ptr); + + return result; +} + +# define fcntl klibc_fcntl +#endif + /* 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 diff --git a/m4/dup.m4 b/m4/dup.m4 index 4fb0d30beb..e30348c8cd 100644 --- a/m4/dup.m4 +++ b/m4/dup.m4 @@ -1,4 +1,4 @@ -# dup.m4 serial 3 +# dup.m4 serial 4 dnl Copyright (C) 2011-2016 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -19,6 +19,26 @@ AC_DEFUN([gl_FUNC_DUP], REPLACE_DUP=1 fi ]) + AC_CACHE_CHECK([whether dup works], [gl_cv_func_dup_works], + [AC_RUN_IFELSE( + [AC_LANG_PROGRAM([[#include + #include + #include ]], + [[/* On OS/2 kLIBC, dup does not work on a directory fd. */ + int fd = open (".", O_RDONLY); + return fd < 0 ? 1 : dup (fd) < 0 ? 2 : 0; + ]]) + ], + [gl_cv_func_dup_works=yes], + [gl_cv_func_dup_works=no], + [gl_cv_func_dup_works='guessing yes']) + ]) + case "$gl_cv_func_dup_works" in + *yes) ;; + *) + REPLACE_DUP=1 + ;; + esac ]) # Prerequisites of lib/dup.c. diff --git a/m4/dup2.m4 b/m4/dup2.m4 index 63d6d8eb6e..5b68312b1e 100644 --- a/m4/dup2.m4 +++ b/m4/dup2.m4 @@ -1,4 +1,4 @@ -#serial 24 +#serial 25 dnl Copyright (C) 2002, 2005, 2007, 2009-2016 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -62,6 +62,16 @@ AC_DEFUN([gl_FUNC_DUP2], result |= 32; dup2 (2, 255); dup2 (2, 256); + /* On OS/2 kLIBC, dup2() does not work on a directory fd. */ + { + int fd = open (".", O_RDONLY); + if (fd == -1) + result |= 64; + else if (dup2 (fd, fd + 1) == -1) + result |= 128; + + close (fd); + } return result;]]) ], [gl_cv_func_dup2_works=yes], [gl_cv_func_dup2_works=no], @@ -78,6 +88,8 @@ AC_DEFUN([gl_FUNC_DUP2], gl_cv_func_dup2_works="guessing no" ;; *-android*) # implemented using dup3(), which fails if oldfd == newfd gl_cv_func_dup2_works="guessing no" ;; + os2*) # on OS/2 kLIBC, dup2() does not work on a directory fd. + gl_cv_func_dup2_works="guessing no" ;; *) gl_cv_func_dup2_works="guessing yes" ;; esac]) ]) diff --git a/m4/fcntl.m4 b/m4/fcntl.m4 index 0037e5fb65..bb61470b2e 100644 --- a/m4/fcntl.m4 +++ b/m4/fcntl.m4 @@ -1,4 +1,4 @@ -# fcntl.m4 serial 8 +# fcntl.m4 serial 9 dnl Copyright (C) 2009-2016 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -54,6 +54,17 @@ AC_DEFUN([gl_FUNC_FCNTL], if (errno != EINVAL) result |= 2; if (fcntl (0, F_DUPFD, bad_fd) != -1) result |= 4; if (errno != EINVAL) result |= 8; + /* On OS/2 kLIBC, F_DUPFD does not work on a directory fd */ + { + int fd; + fd = open (".", O_RDONLY); + if (fd == -1) + result |= 16; + else if (fcntl (fd, F_DUPFD, STDERR_FILENO + 1) == -1) + result |= 32; + + close (fd); + } return result;]])], [gl_cv_func_fcntl_f_dupfd_works=yes], [gl_cv_func_fcntl_f_dupfd_works=no], -- 2.39.5