1 | /* Replacements for Posix functions and Posix functionality for MS-Windows.
|
---|
2 |
|
---|
3 | Copyright (C) 2013-2016 Free Software Foundation, Inc.
|
---|
4 | This file is part of GNU Make.
|
---|
5 |
|
---|
6 | GNU Make is free software; you can redistribute it and/or modify it under the
|
---|
7 | terms of the GNU General Public License as published by the Free Software
|
---|
8 | Foundation; either version 3 of the License, or (at your option) any later
|
---|
9 | version.
|
---|
10 |
|
---|
11 | GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
|
---|
12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
---|
13 | A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
---|
14 |
|
---|
15 | You should have received a copy of the GNU General Public License along with
|
---|
16 | this program. If not, see <http://www.gnu.org/licenses/>. */
|
---|
17 |
|
---|
18 | #include <string.h>
|
---|
19 | #include <io.h>
|
---|
20 | #include <stdarg.h>
|
---|
21 | #include <errno.h>
|
---|
22 | #include <windows.h>
|
---|
23 |
|
---|
24 | #include "dlfcn.h"
|
---|
25 |
|
---|
26 | #include "makeint.h"
|
---|
27 | #include "job.h"
|
---|
28 |
|
---|
29 | #ifndef NO_OUTPUT_SYNC
|
---|
30 | /* Support for OUTPUT_SYNC and related functionality. */
|
---|
31 |
|
---|
32 | /* Emulation of fcntl that supports only F_GETFD and F_SETLKW. */
|
---|
33 | int
|
---|
34 | fcntl (intptr_t fd, int cmd, ...)
|
---|
35 | {
|
---|
36 | va_list ap;
|
---|
37 |
|
---|
38 | va_start (ap, cmd);
|
---|
39 |
|
---|
40 | switch (cmd)
|
---|
41 | {
|
---|
42 | case F_GETFD:
|
---|
43 | va_end (ap);
|
---|
44 | /* Could have used GetHandleInformation, but that isn't
|
---|
45 | supported on Windows 9X. */
|
---|
46 | if (_get_osfhandle (fd) == -1)
|
---|
47 | return -1;
|
---|
48 | return 0;
|
---|
49 | case F_SETLKW:
|
---|
50 | {
|
---|
51 | void *buf = va_arg (ap, void *);
|
---|
52 | struct flock *fl = (struct flock *)buf;
|
---|
53 | HANDLE hmutex = (HANDLE)fd;
|
---|
54 | static struct flock last_fl;
|
---|
55 | short last_type = last_fl.l_type;
|
---|
56 |
|
---|
57 | va_end (ap);
|
---|
58 |
|
---|
59 | if (hmutex == INVALID_HANDLE_VALUE || !hmutex)
|
---|
60 | return -1;
|
---|
61 |
|
---|
62 | last_fl = *fl;
|
---|
63 |
|
---|
64 | switch (fl->l_type)
|
---|
65 | {
|
---|
66 |
|
---|
67 | case F_WRLCK:
|
---|
68 | {
|
---|
69 | DWORD result;
|
---|
70 |
|
---|
71 | if (last_type == F_WRLCK)
|
---|
72 | {
|
---|
73 | /* Don't call WaitForSingleObject if we already
|
---|
74 | own the mutex, because doing so will require
|
---|
75 | us to call ReleaseMutex an equal number of
|
---|
76 | times, before the mutex is actually
|
---|
77 | released. */
|
---|
78 | return 0;
|
---|
79 | }
|
---|
80 |
|
---|
81 | result = WaitForSingleObject (hmutex, INFINITE);
|
---|
82 | switch (result)
|
---|
83 | {
|
---|
84 | case WAIT_OBJECT_0:
|
---|
85 | /* We don't care if the mutex owner crashed or
|
---|
86 | exited. */
|
---|
87 | case WAIT_ABANDONED:
|
---|
88 | return 0;
|
---|
89 | case WAIT_FAILED:
|
---|
90 | case WAIT_TIMEOUT: /* cannot happen, really */
|
---|
91 | {
|
---|
92 | DWORD err = GetLastError ();
|
---|
93 |
|
---|
94 | /* Invalidate the last command. */
|
---|
95 | memset (&last_fl, 0, sizeof (last_fl));
|
---|
96 |
|
---|
97 | switch (err)
|
---|
98 | {
|
---|
99 | case ERROR_INVALID_HANDLE:
|
---|
100 | case ERROR_INVALID_FUNCTION:
|
---|
101 | errno = EINVAL;
|
---|
102 | return -1;
|
---|
103 | default:
|
---|
104 | errno = EDEADLOCK;
|
---|
105 | return -1;
|
---|
106 | }
|
---|
107 | }
|
---|
108 | }
|
---|
109 | }
|
---|
110 | case F_UNLCK:
|
---|
111 | {
|
---|
112 | /* FIXME: Perhaps we should call ReleaseMutex
|
---|
113 | repatedly until it errors out, to make sure the
|
---|
114 | mutext is released even if we somehow managed to
|
---|
115 | to take ownership multiple times? */
|
---|
116 | BOOL status = ReleaseMutex (hmutex);
|
---|
117 |
|
---|
118 | if (status)
|
---|
119 | return 0;
|
---|
120 | else
|
---|
121 | {
|
---|
122 | DWORD err = GetLastError ();
|
---|
123 |
|
---|
124 | if (err == ERROR_NOT_OWNER)
|
---|
125 | errno = EPERM;
|
---|
126 | else
|
---|
127 | {
|
---|
128 | memset (&last_fl, 0, sizeof (last_fl));
|
---|
129 | errno = EINVAL;
|
---|
130 | }
|
---|
131 | return -1;
|
---|
132 | }
|
---|
133 | }
|
---|
134 | default:
|
---|
135 | errno = ENOSYS;
|
---|
136 | return -1;
|
---|
137 | }
|
---|
138 | }
|
---|
139 | default:
|
---|
140 | errno = ENOSYS;
|
---|
141 | va_end (ap);
|
---|
142 | return -1;
|
---|
143 | }
|
---|
144 | }
|
---|
145 |
|
---|
146 | static intptr_t mutex_handle = -1;
|
---|
147 |
|
---|
148 | /* Record in a static variable the mutex handle we were requested to
|
---|
149 | use. That nameless mutex was created by the top-level Make, and
|
---|
150 | its handle was passed to us via inheritance. The value of that
|
---|
151 | handle is passed via the command-line arguments, so that we know
|
---|
152 | which handle to use. */
|
---|
153 | void
|
---|
154 | record_sync_mutex (const char *str)
|
---|
155 | {
|
---|
156 | char *endp;
|
---|
157 | intptr_t hmutex = strtol (str, &endp, 16);
|
---|
158 |
|
---|
159 | if (*endp == '\0')
|
---|
160 | mutex_handle = hmutex;
|
---|
161 | else
|
---|
162 | {
|
---|
163 | mutex_handle = -1;
|
---|
164 | errno = EINVAL;
|
---|
165 | }
|
---|
166 | }
|
---|
167 |
|
---|
168 | /* Create a new mutex or reuse one created by our parent. */
|
---|
169 | intptr_t
|
---|
170 | create_mutex (void)
|
---|
171 | {
|
---|
172 | SECURITY_ATTRIBUTES secattr;
|
---|
173 | intptr_t hmutex = -1;
|
---|
174 |
|
---|
175 | /* If we have a mutex handle passed from the parent Make, just use
|
---|
176 | that. */
|
---|
177 | if (mutex_handle > 0)
|
---|
178 | return mutex_handle;
|
---|
179 |
|
---|
180 | /* We are the top-level Make, and we want the handle to be inherited
|
---|
181 | by our child processes. */
|
---|
182 | secattr.nLength = sizeof (secattr);
|
---|
183 | secattr.lpSecurityDescriptor = NULL; /* use default security descriptor */
|
---|
184 | secattr.bInheritHandle = TRUE;
|
---|
185 |
|
---|
186 | hmutex = (intptr_t)CreateMutex (&secattr, FALSE, NULL);
|
---|
187 | if (!hmutex)
|
---|
188 | {
|
---|
189 | DWORD err = GetLastError ();
|
---|
190 |
|
---|
191 | fprintf (stderr, "CreateMutex: error %lu\n", err);
|
---|
192 | errno = ENOLCK;
|
---|
193 | hmutex = -1;
|
---|
194 | }
|
---|
195 |
|
---|
196 | mutex_handle = hmutex;
|
---|
197 | return hmutex;
|
---|
198 | }
|
---|
199 |
|
---|
200 | /* Return non-zero if F1 and F2 are 2 streams representing the same
|
---|
201 | file or pipe or device. */
|
---|
202 | int
|
---|
203 | same_stream (FILE *f1, FILE *f2)
|
---|
204 | {
|
---|
205 | HANDLE fh1 = (HANDLE)_get_osfhandle (fileno (f1));
|
---|
206 | HANDLE fh2 = (HANDLE)_get_osfhandle (fileno (f2));
|
---|
207 |
|
---|
208 | /* Invalid file descriptors get treated as different streams. */
|
---|
209 | if (fh1 && fh1 != INVALID_HANDLE_VALUE
|
---|
210 | && fh2 && fh2 != INVALID_HANDLE_VALUE)
|
---|
211 | {
|
---|
212 | if (fh1 == fh2)
|
---|
213 | return 1;
|
---|
214 | else
|
---|
215 | {
|
---|
216 | DWORD ftyp1 = GetFileType (fh1), ftyp2 = GetFileType (fh2);
|
---|
217 |
|
---|
218 | if (ftyp1 != ftyp2
|
---|
219 | || ftyp1 == FILE_TYPE_UNKNOWN || ftyp2 == FILE_TYPE_UNKNOWN)
|
---|
220 | return 0;
|
---|
221 | else if (ftyp1 == FILE_TYPE_CHAR)
|
---|
222 | {
|
---|
223 | /* For character devices, check if they both refer to a
|
---|
224 | console. This loses if both handles refer to the
|
---|
225 | null device (FIXME!), but in that case we don't care
|
---|
226 | in the context of Make. */
|
---|
227 | DWORD conmode1, conmode2;
|
---|
228 |
|
---|
229 | /* Each process on Windows can have at most 1 console,
|
---|
230 | so if both handles are for the console device, they
|
---|
231 | are the same. We also compare the console mode to
|
---|
232 | distinguish between stdin and stdout/stderr. */
|
---|
233 | if (GetConsoleMode (fh1, &conmode1)
|
---|
234 | && GetConsoleMode (fh2, &conmode2)
|
---|
235 | && conmode1 == conmode2)
|
---|
236 | return 1;
|
---|
237 | }
|
---|
238 | else
|
---|
239 | {
|
---|
240 | /* For disk files and pipes, compare their unique
|
---|
241 | attributes. */
|
---|
242 | BY_HANDLE_FILE_INFORMATION bhfi1, bhfi2;
|
---|
243 |
|
---|
244 | /* Pipes get zero in the volume serial number, but do
|
---|
245 | appear to have meaningful information in file index
|
---|
246 | attributes. We test file attributes as well, for a
|
---|
247 | good measure. */
|
---|
248 | if (GetFileInformationByHandle (fh1, &bhfi1)
|
---|
249 | && GetFileInformationByHandle (fh2, &bhfi2))
|
---|
250 | return (bhfi1.dwVolumeSerialNumber == bhfi2.dwVolumeSerialNumber
|
---|
251 | && bhfi1.nFileIndexLow == bhfi2.nFileIndexLow
|
---|
252 | && bhfi1.nFileIndexHigh == bhfi2.nFileIndexHigh
|
---|
253 | && bhfi1.dwFileAttributes == bhfi2.dwFileAttributes);
|
---|
254 | }
|
---|
255 | }
|
---|
256 | }
|
---|
257 | return 0;
|
---|
258 | }
|
---|
259 |
|
---|
260 | /* A replacement for tmpfile, since the MSVCRT implementation creates
|
---|
261 | the file in the root directory of the current drive, which might
|
---|
262 | not be writable by our user. Most of the code borrowed from
|
---|
263 | create_batch_file, see job.c. */
|
---|
264 | FILE *
|
---|
265 | tmpfile (void)
|
---|
266 | {
|
---|
267 | char temp_path[MAXPATHLEN];
|
---|
268 | unsigned path_size = GetTempPath (sizeof temp_path, temp_path);
|
---|
269 | int path_is_dot = 0;
|
---|
270 | /* The following variable is static so we won't try to reuse a name
|
---|
271 | that was generated a little while ago, because that file might
|
---|
272 | not be on disk yet, since we use FILE_ATTRIBUTE_TEMPORARY below,
|
---|
273 | which tells the OS it doesn't need to flush the cache to disk.
|
---|
274 | If the file is not yet on disk, we might think the name is
|
---|
275 | available, while it really isn't. This happens in parallel
|
---|
276 | builds, where Make doesn't wait for one job to finish before it
|
---|
277 | launches the next one. */
|
---|
278 | static unsigned uniq = 0;
|
---|
279 | static int second_loop = 0;
|
---|
280 | const char base[] = "gmake_tmpf";
|
---|
281 | const unsigned sizemax = sizeof base - 1 + 4 + 10 + 10;
|
---|
282 | unsigned pid = GetCurrentProcessId ();
|
---|
283 |
|
---|
284 | if (path_size == 0)
|
---|
285 | {
|
---|
286 | path_size = GetCurrentDirectory (sizeof temp_path, temp_path);
|
---|
287 | path_is_dot = 1;
|
---|
288 | }
|
---|
289 |
|
---|
290 | ++uniq;
|
---|
291 | if (uniq >= 0x10000 && !second_loop)
|
---|
292 | {
|
---|
293 | /* If we already had 64K batch files in this
|
---|
294 | process, make a second loop through the numbers,
|
---|
295 | looking for free slots, i.e. files that were
|
---|
296 | deleted in the meantime. */
|
---|
297 | second_loop = 1;
|
---|
298 | uniq = 1;
|
---|
299 | }
|
---|
300 | while (path_size > 0 &&
|
---|
301 | path_size + sizemax < sizeof temp_path &&
|
---|
302 | !(uniq >= 0x10000 && second_loop))
|
---|
303 | {
|
---|
304 | HANDLE h;
|
---|
305 |
|
---|
306 | sprintf (temp_path + path_size,
|
---|
307 | "%s%s%u-%x.tmp",
|
---|
308 | temp_path[path_size - 1] == '\\' ? "" : "\\",
|
---|
309 | base, pid, uniq);
|
---|
310 | h = CreateFile (temp_path, /* file name */
|
---|
311 | GENERIC_READ | GENERIC_WRITE | DELETE, /* desired access */
|
---|
312 | FILE_SHARE_READ | FILE_SHARE_WRITE, /* share mode */
|
---|
313 | NULL, /* default security attributes */
|
---|
314 | CREATE_NEW, /* creation disposition */
|
---|
315 | FILE_ATTRIBUTE_NORMAL | /* flags and attributes */
|
---|
316 | FILE_ATTRIBUTE_TEMPORARY |
|
---|
317 | FILE_FLAG_DELETE_ON_CLOSE,
|
---|
318 | NULL); /* no template file */
|
---|
319 |
|
---|
320 | if (h == INVALID_HANDLE_VALUE)
|
---|
321 | {
|
---|
322 | const DWORD er = GetLastError ();
|
---|
323 |
|
---|
324 | if (er == ERROR_FILE_EXISTS || er == ERROR_ALREADY_EXISTS)
|
---|
325 | {
|
---|
326 | ++uniq;
|
---|
327 | if (uniq == 0x10000 && !second_loop)
|
---|
328 | {
|
---|
329 | second_loop = 1;
|
---|
330 | uniq = 1;
|
---|
331 | }
|
---|
332 | }
|
---|
333 |
|
---|
334 | /* The temporary path is not guaranteed to exist, or might
|
---|
335 | not be writable by user. Use the current directory as
|
---|
336 | fallback. */
|
---|
337 | else if (path_is_dot == 0)
|
---|
338 | {
|
---|
339 | path_size = GetCurrentDirectory (sizeof temp_path, temp_path);
|
---|
340 | path_is_dot = 1;
|
---|
341 | }
|
---|
342 |
|
---|
343 | else
|
---|
344 | {
|
---|
345 | errno = EACCES;
|
---|
346 | break;
|
---|
347 | }
|
---|
348 | }
|
---|
349 | else
|
---|
350 | {
|
---|
351 | int fd = _open_osfhandle ((intptr_t)h, 0);
|
---|
352 |
|
---|
353 | return _fdopen (fd, "w+b");
|
---|
354 | }
|
---|
355 | }
|
---|
356 |
|
---|
357 | if (uniq >= 0x10000)
|
---|
358 | errno = EEXIST;
|
---|
359 | return NULL;
|
---|
360 | }
|
---|
361 |
|
---|
362 | #endif /* !NO_OUTPUT_SYNC */
|
---|
363 |
|
---|
364 | #if MAKE_LOAD
|
---|
365 |
|
---|
366 | /* Support for dynamic loading of objects. */
|
---|
367 |
|
---|
368 |
|
---|
369 | static DWORD last_err;
|
---|
370 |
|
---|
371 | void *
|
---|
372 | dlopen (const char *file, int mode)
|
---|
373 | {
|
---|
374 | char dllfn[MAX_PATH], *p;
|
---|
375 | HANDLE dllhandle;
|
---|
376 |
|
---|
377 | if ((mode & ~(RTLD_LAZY | RTLD_NOW | RTLD_GLOBAL)) != 0)
|
---|
378 | {
|
---|
379 | errno = EINVAL;
|
---|
380 | last_err = ERROR_INVALID_PARAMETER;
|
---|
381 | return NULL;
|
---|
382 | }
|
---|
383 |
|
---|
384 | if (!file)
|
---|
385 | dllhandle = GetModuleHandle (NULL);
|
---|
386 | else
|
---|
387 | {
|
---|
388 | /* MSDN says to be sure to use backslashes in the DLL file name. */
|
---|
389 | strcpy (dllfn, file);
|
---|
390 | for (p = dllfn; *p; p++)
|
---|
391 | if (*p == '/')
|
---|
392 | *p = '\\';
|
---|
393 |
|
---|
394 | dllhandle = LoadLibrary (dllfn);
|
---|
395 | }
|
---|
396 | if (!dllhandle)
|
---|
397 | last_err = GetLastError ();
|
---|
398 |
|
---|
399 | return dllhandle;
|
---|
400 | }
|
---|
401 |
|
---|
402 | char *
|
---|
403 | dlerror (void)
|
---|
404 | {
|
---|
405 | static char errbuf[1024];
|
---|
406 | DWORD ret;
|
---|
407 |
|
---|
408 | if (!last_err)
|
---|
409 | return NULL;
|
---|
410 |
|
---|
411 | ret = FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM
|
---|
412 | | FORMAT_MESSAGE_IGNORE_INSERTS,
|
---|
413 | NULL, last_err, 0, errbuf, sizeof (errbuf), NULL);
|
---|
414 | while (ret > 0 && (errbuf[ret - 1] == '\n' || errbuf[ret - 1] == '\r'))
|
---|
415 | --ret;
|
---|
416 |
|
---|
417 | errbuf[ret] = '\0';
|
---|
418 | if (!ret)
|
---|
419 | sprintf (errbuf, "Error code %lu", last_err);
|
---|
420 |
|
---|
421 | last_err = 0;
|
---|
422 | return errbuf;
|
---|
423 | }
|
---|
424 |
|
---|
425 | void *
|
---|
426 | dlsym (void *handle, const char *name)
|
---|
427 | {
|
---|
428 | FARPROC addr = NULL;
|
---|
429 |
|
---|
430 | if (!handle || handle == INVALID_HANDLE_VALUE)
|
---|
431 | {
|
---|
432 | last_err = ERROR_INVALID_PARAMETER;
|
---|
433 | return NULL;
|
---|
434 | }
|
---|
435 |
|
---|
436 | addr = GetProcAddress (handle, name);
|
---|
437 | if (!addr)
|
---|
438 | last_err = GetLastError ();
|
---|
439 |
|
---|
440 | return (void *)addr;
|
---|
441 | }
|
---|
442 |
|
---|
443 | int
|
---|
444 | dlclose (void *handle)
|
---|
445 | {
|
---|
446 | if (!handle || handle == INVALID_HANDLE_VALUE)
|
---|
447 | return -1;
|
---|
448 | if (!FreeLibrary (handle))
|
---|
449 | return -1;
|
---|
450 |
|
---|
451 | return 0;
|
---|
452 | }
|
---|
453 |
|
---|
454 |
|
---|
455 | #endif /* MAKE_LOAD */
|
---|
456 |
|
---|
457 |
|
---|
458 | /* MS runtime's isatty returns non-zero for any character device,
|
---|
459 | including the null device, which is not what we want. */
|
---|
460 | int
|
---|
461 | isatty (int fd)
|
---|
462 | {
|
---|
463 | HANDLE fh = (HANDLE) _get_osfhandle (fd);
|
---|
464 | DWORD con_mode;
|
---|
465 |
|
---|
466 | if (fh == INVALID_HANDLE_VALUE)
|
---|
467 | {
|
---|
468 | errno = EBADF;
|
---|
469 | return 0;
|
---|
470 | }
|
---|
471 | if (GetConsoleMode (fh, &con_mode))
|
---|
472 | return 1;
|
---|
473 |
|
---|
474 | errno = ENOTTY;
|
---|
475 | return 0;
|
---|
476 | }
|
---|
477 |
|
---|
478 | char *
|
---|
479 | ttyname (int fd)
|
---|
480 | {
|
---|
481 | /* This "knows" that Make only asks about stdout and stderr. A more
|
---|
482 | sophisticated implementation should test whether FD is open for
|
---|
483 | input or output. We can do that by looking at the mode returned
|
---|
484 | by GetConsoleMode. */
|
---|
485 | return "CONOUT$";
|
---|
486 | }
|
---|