Include string.h, for memset.
[gnulib.git] / lib / dup3.c
1 /* Copy a file descriptor, applying specific flags.
2    Copyright (C) 2009 Free Software Foundation, Inc.
3
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2, or (at your option)
7    any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License along
15    with this program; if not, write to the Free Software Foundation,
16    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
17
18 #include <config.h>
19
20 /* Specification.  */
21 #include <unistd.h>
22
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <limits.h>
26
27 #include "binary-io.h"
28
29 #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
30 /* Native Woe32 API.  */
31
32 # include <string.h>
33
34 /* Get declarations of the Win32 API functions.  */
35 # define WIN32_LEAN_AND_MEAN
36 # include <windows.h>
37
38 /* Upper bound on getdtablesize().  See lib/getdtablesize.c.  */
39 # define OPEN_MAX_MAX 0x10000
40
41 #else
42 /* Unix API.  */
43
44 # ifndef O_CLOEXEC
45 #  define O_CLOEXEC 0
46 # endif
47
48 #endif
49
50 int
51 dup3 (int oldfd, int newfd, int flags)
52 {
53 #if HAVE_DUP3
54 # undef dup3
55   /* Try the system call first, if it exists.  (We may be running with a glibc
56      that has the function but with an older kernel that lacks it.)  */
57   {
58     /* Cache the information whether the system call really exists.  */
59     static int have_dup3_really; /* 0 = unknown, 1 = yes, -1 = no */
60     if (have_dup3_really >= 0)
61       {
62         int result = dup3 (oldfd, newfd, flags);
63         if (!(result < 0 && errno == ENOSYS))
64           {
65             have_dup3_really = 1;
66             return result;
67           }
68         have_dup3_really = -1;
69       }
70   }
71 #endif
72
73   if (oldfd < 0 || newfd < 0 || newfd >= getdtablesize ())
74     {
75       errno = EBADF;
76       return -1;
77     }
78
79   if (newfd == oldfd)
80     {
81       errno = EINVAL;
82       return -1;
83     }
84
85   /* Check the supported flags.
86      Note that O_NONBLOCK is not supported, because setting it on newfd
87      would implicitly also set it on oldfd.  */
88   if ((flags & ~(O_CLOEXEC | O_BINARY | O_TEXT)) != 0)
89     {
90       errno = EINVAL;
91       return -1;
92     }
93
94 #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
95 /* Native Woe32 API.  */
96
97   if (flags & O_CLOEXEC)
98     {
99       /* Neither dup() nor dup2() can create a file descriptor with
100          O_CLOEXEC = O_NOINHERIT set.  We need to use the low-level function
101          _open_osfhandle for this.  Iterate until all file descriptors less
102          than newfd are filled up.  */
103       HANDLE curr_process = GetCurrentProcess ();
104       HANDLE old_handle = (HANDLE) _get_osfhandle (oldfd);
105       unsigned char fds_to_close[OPEN_MAX_MAX / CHAR_BIT];
106       unsigned int fds_to_close_bound = 0;
107       int result;
108
109       if (old_handle == INVALID_HANDLE_VALUE)
110         {
111           /* oldfd is not open, or is an unassigned standard file
112              descriptor.  */
113           errno = EBADF;
114           return -1;
115         }
116
117       close (newfd);
118
119       for (;;)
120         {
121           HANDLE new_handle;
122           int duplicated_fd;
123           unsigned int index;
124
125           if (!DuplicateHandle (curr_process,         /* SourceProcessHandle */
126                                 old_handle,           /* SourceHandle */
127                                 curr_process,         /* TargetProcessHandle */
128                                 (PHANDLE) &new_handle, /* TargetHandle */
129                                 (DWORD) 0,            /* DesiredAccess */
130                                 FALSE,                /* InheritHandle */
131                                 DUPLICATE_SAME_ACCESS)) /* Options */
132             {
133               errno = EBADF; /* arbitrary */
134               result = -1;
135               break;
136             }
137           duplicated_fd = _open_osfhandle ((long) new_handle, flags);
138           if (duplicated_fd < 0)
139             {
140               CloseHandle (new_handle);
141               result = -1;
142               break;
143             }
144           if (duplicated_fd > newfd)
145             /* Shouldn't happen, since newfd is still closed.  */
146             abort ();
147           if (duplicated_fd == newfd)
148             {
149               result = newfd;
150               break;
151             }
152
153           /* Set the bit duplicated_fd in fds_to_close[].  */
154           index = (unsigned int) duplicated_fd / CHAR_BIT;
155           if (index >= fds_to_close_bound)
156             {
157               if (index >= sizeof (fds_to_close))
158                 /* Need to increase OPEN_MAX_MAX.  */
159                 abort ();
160               memset (fds_to_close + fds_to_close_bound, '\0',
161                       index + 1 - fds_to_close_bound);
162               fds_to_close_bound = index + 1;
163             }
164           fds_to_close[index] |= 1 << ((unsigned int) duplicated_fd % CHAR_BIT);
165         }
166
167       /* Close the previous fds that turned out to be too small.  */
168       {
169         int saved_errno = errno;
170         unsigned int duplicated_fd;
171
172         for (duplicated_fd = 0;
173              duplicated_fd < fds_to_close_bound * CHAR_BIT;
174              duplicated_fd++)
175           if ((fds_to_close[duplicated_fd / CHAR_BIT]
176                >> (duplicated_fd % CHAR_BIT))
177               & 1)
178             close (duplicated_fd);
179
180         errno = saved_errno;
181       }
182
183       return result;
184     }
185
186   if (dup2 (oldfd, newfd) < 0)
187     return -1;
188
189 #else
190 /* Unix API.  */
191
192   if (dup2 (oldfd, newfd) < 0)
193     return -1;
194
195   /* POSIX <http://www.opengroup.org/onlinepubs/9699919799/functions/dup.html>
196      says that initially, the FD_CLOEXEC flag is cleared on newfd.  */
197
198   if (flags & O_CLOEXEC)
199     {
200       int fcntl_flags;
201
202       if ((fcntl_flags = fcntl (newfd, F_GETFD, 0)) < 0
203           || fcntl (newfd, F_SETFD, fcntl_flags | FD_CLOEXEC) == -1)
204         {
205           int saved_errno = errno;
206           close (newfd);
207           errno = saved_errno;
208           return -1;
209         }
210     }
211
212 #endif
213
214 #if O_BINARY
215   if (flags & O_BINARY)
216     setmode (newfd, O_BINARY);
217   else if (flags & O_TEXT)
218     setmode (newfd, O_TEXT);
219 #endif
220
221   return newfd;
222 }