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