Commit | Line | Data |
---|---|---|
067428c1 PE |
1 | /* Provide file descriptor control. |
2 | ||
ba318903 | 3 | Copyright (C) 2009-2014 Free Software Foundation, Inc. |
067428c1 PE |
4 | |
5 | This program is free software: you can redistribute it and/or modify | |
6 | it under the terms of the GNU General Public License as published by | |
7 | the Free Software Foundation; either version 3 of the License, or | |
8 | (at your option) any later version. | |
9 | ||
10 | This program is distributed in the hope that it will be useful, | |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | GNU General Public License for more details. | |
14 | ||
15 | You should have received a copy of the GNU General Public License | |
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ | |
17 | ||
18 | /* Written by Eric Blake <ebb9@byu.net>. */ | |
19 | ||
20 | #include <config.h> | |
21 | ||
22 | /* Specification. */ | |
23 | #include <fcntl.h> | |
24 | ||
25 | #include <errno.h> | |
26 | #include <limits.h> | |
27 | #include <stdarg.h> | |
28 | #include <unistd.h> | |
29 | ||
30 | #if !HAVE_FCNTL | |
31 | # define rpl_fcntl fcntl | |
32 | #endif | |
33 | #undef fcntl | |
34 | ||
35 | #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ | |
36 | /* Get declarations of the native Windows API functions. */ | |
37 | # define WIN32_LEAN_AND_MEAN | |
38 | # include <windows.h> | |
39 | ||
40 | /* Get _get_osfhandle. */ | |
41 | # include "msvc-nothrow.h" | |
42 | ||
43 | /* Upper bound on getdtablesize(). See lib/getdtablesize.c. */ | |
44 | # define OPEN_MAX_MAX 0x10000 | |
45 | ||
46 | /* Duplicate OLDFD into the first available slot of at least NEWFD, | |
47 | which must be positive, with FLAGS determining whether the duplicate | |
48 | will be inheritable. */ | |
49 | static int | |
50 | dupfd (int oldfd, int newfd, int flags) | |
51 | { | |
52 | /* Mingw has no way to create an arbitrary fd. Iterate until all | |
53 | file descriptors less than newfd are filled up. */ | |
54 | HANDLE curr_process = GetCurrentProcess (); | |
55 | HANDLE old_handle = (HANDLE) _get_osfhandle (oldfd); | |
56 | unsigned char fds_to_close[OPEN_MAX_MAX / CHAR_BIT]; | |
57 | unsigned int fds_to_close_bound = 0; | |
58 | int result; | |
59 | BOOL inherit = flags & O_CLOEXEC ? FALSE : TRUE; | |
60 | int mode; | |
61 | ||
62 | if (newfd < 0 || getdtablesize () <= newfd) | |
63 | { | |
64 | errno = EINVAL; | |
65 | return -1; | |
66 | } | |
67 | if (old_handle == INVALID_HANDLE_VALUE | |
68 | || (mode = setmode (oldfd, O_BINARY)) == -1) | |
69 | { | |
70 | /* oldfd is not open, or is an unassigned standard file | |
71 | descriptor. */ | |
72 | errno = EBADF; | |
73 | return -1; | |
74 | } | |
75 | setmode (oldfd, mode); | |
76 | flags |= mode; | |
77 | ||
78 | for (;;) | |
79 | { | |
80 | HANDLE new_handle; | |
81 | int duplicated_fd; | |
82 | unsigned int index; | |
83 | ||
84 | if (!DuplicateHandle (curr_process, /* SourceProcessHandle */ | |
85 | old_handle, /* SourceHandle */ | |
86 | curr_process, /* TargetProcessHandle */ | |
87 | (PHANDLE) &new_handle, /* TargetHandle */ | |
88 | (DWORD) 0, /* DesiredAccess */ | |
89 | inherit, /* InheritHandle */ | |
90 | DUPLICATE_SAME_ACCESS)) /* Options */ | |
91 | { | |
92 | /* TODO: Translate GetLastError () into errno. */ | |
93 | errno = EMFILE; | |
94 | result = -1; | |
95 | break; | |
96 | } | |
97 | duplicated_fd = _open_osfhandle ((intptr_t) new_handle, flags); | |
98 | if (duplicated_fd < 0) | |
99 | { | |
100 | CloseHandle (new_handle); | |
101 | errno = EMFILE; | |
102 | result = -1; | |
103 | break; | |
104 | } | |
105 | if (newfd <= duplicated_fd) | |
106 | { | |
107 | result = duplicated_fd; | |
108 | break; | |
109 | } | |
110 | ||
111 | /* Set the bit duplicated_fd in fds_to_close[]. */ | |
112 | index = (unsigned int) duplicated_fd / CHAR_BIT; | |
113 | if (fds_to_close_bound <= index) | |
114 | { | |
115 | if (sizeof fds_to_close <= index) | |
116 | /* Need to increase OPEN_MAX_MAX. */ | |
117 | abort (); | |
118 | memset (fds_to_close + fds_to_close_bound, '\0', | |
119 | index + 1 - fds_to_close_bound); | |
120 | fds_to_close_bound = index + 1; | |
121 | } | |
122 | fds_to_close[index] |= 1 << ((unsigned int) duplicated_fd % CHAR_BIT); | |
123 | } | |
124 | ||
125 | /* Close the previous fds that turned out to be too small. */ | |
126 | { | |
127 | int saved_errno = errno; | |
128 | unsigned int duplicated_fd; | |
129 | ||
130 | for (duplicated_fd = 0; | |
131 | duplicated_fd < fds_to_close_bound * CHAR_BIT; | |
132 | duplicated_fd++) | |
133 | if ((fds_to_close[duplicated_fd / CHAR_BIT] | |
134 | >> (duplicated_fd % CHAR_BIT)) | |
135 | & 1) | |
136 | close (duplicated_fd); | |
137 | ||
138 | errno = saved_errno; | |
139 | } | |
140 | ||
141 | # if REPLACE_FCHDIR | |
142 | if (0 <= result) | |
143 | result = _gl_register_dup (oldfd, result); | |
144 | # endif | |
145 | return result; | |
146 | } | |
147 | #endif /* W32 */ | |
148 | ||
149 | /* Perform the specified ACTION on the file descriptor FD, possibly | |
150 | using the argument ARG further described below. This replacement | |
151 | handles the following actions, and forwards all others on to the | |
152 | native fcntl. An unrecognized ACTION returns -1 with errno set to | |
153 | EINVAL. | |
154 | ||
155 | F_DUPFD - duplicate FD, with int ARG being the minimum target fd. | |
156 | If successful, return the duplicate, which will be inheritable; | |
157 | otherwise return -1 and set errno. | |
158 | ||
159 | F_DUPFD_CLOEXEC - duplicate FD, with int ARG being the minimum | |
160 | target fd. If successful, return the duplicate, which will not be | |
161 | inheritable; otherwise return -1 and set errno. | |
162 | ||
163 | F_GETFD - ARG need not be present. If successful, return a | |
164 | non-negative value containing the descriptor flags of FD (only | |
165 | FD_CLOEXEC is portable, but other flags may be present); otherwise | |
166 | return -1 and set errno. */ | |
167 | ||
168 | int | |
169 | rpl_fcntl (int fd, int action, /* arg */...) | |
170 | { | |
171 | va_list arg; | |
172 | int result = -1; | |
173 | va_start (arg, action); | |
174 | switch (action) | |
175 | { | |
176 | ||
177 | #if !HAVE_FCNTL | |
178 | case F_DUPFD: | |
179 | { | |
180 | int target = va_arg (arg, int); | |
181 | result = dupfd (fd, target, 0); | |
182 | break; | |
183 | } | |
184 | #elif FCNTL_DUPFD_BUGGY || REPLACE_FCHDIR | |
185 | case F_DUPFD: | |
186 | { | |
187 | int target = va_arg (arg, int); | |
188 | /* Detect invalid target; needed for cygwin 1.5.x. */ | |
189 | if (target < 0 || getdtablesize () <= target) | |
190 | errno = EINVAL; | |
191 | else | |
192 | { | |
193 | /* Haiku alpha 2 loses fd flags on original. */ | |
194 | int flags = fcntl (fd, F_GETFD); | |
195 | if (flags < 0) | |
196 | { | |
197 | result = -1; | |
198 | break; | |
199 | } | |
200 | result = fcntl (fd, action, target); | |
201 | if (0 <= result && fcntl (fd, F_SETFD, flags) == -1) | |
202 | { | |
203 | int saved_errno = errno; | |
204 | close (result); | |
205 | result = -1; | |
206 | errno = saved_errno; | |
207 | } | |
208 | # if REPLACE_FCHDIR | |
209 | if (0 <= result) | |
210 | result = _gl_register_dup (fd, result); | |
211 | # endif | |
212 | } | |
213 | break; | |
214 | } /* F_DUPFD */ | |
215 | #endif /* FCNTL_DUPFD_BUGGY || REPLACE_FCHDIR */ | |
216 | ||
217 | case F_DUPFD_CLOEXEC: | |
218 | { | |
219 | int target = va_arg (arg, int); | |
220 | ||
221 | #if !HAVE_FCNTL | |
222 | result = dupfd (fd, target, O_CLOEXEC); | |
223 | break; | |
224 | #else /* HAVE_FCNTL */ | |
225 | /* Try the system call first, if the headers claim it exists | |
226 | (that is, if GNULIB_defined_F_DUPFD_CLOEXEC is 0), since we | |
227 | may be running with a glibc that has the macro but with an | |
228 | older kernel that does not support it. Cache the | |
229 | information on whether the system call really works, but | |
230 | avoid caching failure if the corresponding F_DUPFD fails | |
231 | for any reason. 0 = unknown, 1 = yes, -1 = no. */ | |
232 | static int have_dupfd_cloexec = GNULIB_defined_F_DUPFD_CLOEXEC ? -1 : 0; | |
233 | if (0 <= have_dupfd_cloexec) | |
234 | { | |
235 | result = fcntl (fd, action, target); | |
236 | if (0 <= result || errno != EINVAL) | |
237 | { | |
238 | have_dupfd_cloexec = 1; | |
239 | # if REPLACE_FCHDIR | |
240 | if (0 <= result) | |
241 | result = _gl_register_dup (fd, result); | |
242 | # endif | |
243 | } | |
244 | else | |
245 | { | |
246 | result = rpl_fcntl (fd, F_DUPFD, target); | |
247 | if (result < 0) | |
248 | break; | |
249 | have_dupfd_cloexec = -1; | |
250 | } | |
251 | } | |
252 | else | |
253 | result = rpl_fcntl (fd, F_DUPFD, target); | |
254 | if (0 <= result && have_dupfd_cloexec == -1) | |
255 | { | |
256 | int flags = fcntl (result, F_GETFD); | |
257 | if (flags < 0 || fcntl (result, F_SETFD, flags | FD_CLOEXEC) == -1) | |
258 | { | |
259 | int saved_errno = errno; | |
260 | close (result); | |
261 | errno = saved_errno; | |
262 | result = -1; | |
263 | } | |
264 | } | |
265 | break; | |
266 | #endif /* HAVE_FCNTL */ | |
267 | } /* F_DUPFD_CLOEXEC */ | |
268 | ||
269 | #if !HAVE_FCNTL | |
270 | case F_GETFD: | |
271 | { | |
272 | # if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ | |
273 | HANDLE handle = (HANDLE) _get_osfhandle (fd); | |
274 | DWORD flags; | |
275 | if (handle == INVALID_HANDLE_VALUE | |
276 | || GetHandleInformation (handle, &flags) == 0) | |
277 | errno = EBADF; | |
278 | else | |
279 | result = (flags & HANDLE_FLAG_INHERIT) ? 0 : FD_CLOEXEC; | |
280 | # else /* !W32 */ | |
281 | /* Use dup2 to reject invalid file descriptors. No way to | |
282 | access this information, so punt. */ | |
283 | if (0 <= dup2 (fd, fd)) | |
284 | result = 0; | |
285 | # endif /* !W32 */ | |
286 | break; | |
287 | } /* F_GETFD */ | |
288 | #endif /* !HAVE_FCNTL */ | |
289 | ||
290 | /* Implementing F_SETFD on mingw is not trivial - there is no | |
291 | API for changing the O_NOINHERIT bit on an fd, and merely | |
292 | changing the HANDLE_FLAG_INHERIT bit on the underlying handle | |
293 | can lead to odd state. It may be possible by duplicating the | |
294 | handle, using _open_osfhandle with the right flags, then | |
295 | using dup2 to move the duplicate onto the original, but that | |
296 | is not supported for now. */ | |
297 | ||
298 | default: | |
299 | { | |
300 | #if HAVE_FCNTL | |
301 | void *p = va_arg (arg, void *); | |
302 | result = fcntl (fd, action, p); | |
303 | #else | |
304 | errno = EINVAL; | |
305 | #endif | |
306 | break; | |
307 | } | |
308 | } | |
309 | va_end (arg); | |
310 | return result; | |
311 | } |