diff options
Diffstat (limited to 'gl/fcntl.c')
-rw-r--r-- | gl/fcntl.c | 294 |
1 files changed, 294 insertions, 0 deletions
diff --git a/gl/fcntl.c b/gl/fcntl.c new file mode 100644 index 00000000..c51e8ded --- /dev/null +++ b/gl/fcntl.c | |||
@@ -0,0 +1,294 @@ | |||
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 | int result = -1; | ||
170 | va_start (arg, action); | ||
171 | 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 | 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 | if (0 <= have_dupfd_cloexec) | ||
217 | { | ||
218 | result = fcntl (fd, action, target); | ||
219 | if (0 <= result || errno != EINVAL) | ||
220 | { | ||
221 | 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 | result = rpl_fcntl (fd, F_DUPFD, target); | ||
230 | if (result < 0) | ||
231 | break; | ||
232 | have_dupfd_cloexec = -1; | ||
233 | } | ||
234 | } | ||
235 | else | ||
236 | result = rpl_fcntl (fd, F_DUPFD, target); | ||
237 | if (0 <= result && have_dupfd_cloexec == -1) | ||
238 | { | ||
239 | int flags = fcntl (result, F_GETFD); | ||
240 | if (flags < 0 || fcntl (result, F_SETFD, flags | FD_CLOEXEC) == -1) | ||
241 | { | ||
242 | int saved_errno = errno; | ||
243 | close (result); | ||
244 | errno = saved_errno; | ||
245 | result = -1; | ||
246 | } | ||
247 | } | ||
248 | 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 | void *p = va_arg (arg, void *); | ||
285 | result = fcntl (fd, action, p); | ||
286 | #else | ||
287 | errno = EINVAL; | ||
288 | #endif | ||
289 | break; | ||
290 | } | ||
291 | } | ||
292 | va_end (arg); | ||
293 | return result; | ||
294 | } | ||