| /* Replacements for Posix functions and Posix functionality for MS-Windows. |
| |
| Copyright (C) 2013-2016 Free Software Foundation, Inc. |
| This file is part of GNU Make. |
| |
| GNU Make is free software; you can redistribute it and/or modify it under the |
| terms of the GNU General Public License as published by the Free Software |
| Foundation; either version 3 of the License, or (at your option) any later |
| version. |
| |
| GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY |
| WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR |
| A PARTICULAR PURPOSE. See the GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License along with |
| this program. If not, see <http://www.gnu.org/licenses/>. */ |
| |
| #include <string.h> |
| #include <io.h> |
| #include <stdarg.h> |
| #include <errno.h> |
| #include <windows.h> |
| |
| #include "dlfcn.h" |
| |
| #include "makeint.h" |
| #include "job.h" |
| |
| #ifndef NO_OUTPUT_SYNC |
| /* Support for OUTPUT_SYNC and related functionality. */ |
| |
| /* Emulation of fcntl that supports only F_GETFD and F_SETLKW. */ |
| int |
| fcntl (intptr_t fd, int cmd, ...) |
| { |
| va_list ap; |
| |
| va_start (ap, cmd); |
| |
| switch (cmd) |
| { |
| case F_GETFD: |
| va_end (ap); |
| /* Could have used GetHandleInformation, but that isn't |
| supported on Windows 9X. */ |
| if (_get_osfhandle (fd) == -1) |
| return -1; |
| return 0; |
| case F_SETLKW: |
| { |
| void *buf = va_arg (ap, void *); |
| struct flock *fl = (struct flock *)buf; |
| HANDLE hmutex = (HANDLE)fd; |
| static struct flock last_fl; |
| short last_type = last_fl.l_type; |
| |
| va_end (ap); |
| |
| if (hmutex == INVALID_HANDLE_VALUE || !hmutex) |
| return -1; |
| |
| last_fl = *fl; |
| |
| switch (fl->l_type) |
| { |
| |
| case F_WRLCK: |
| { |
| DWORD result; |
| |
| if (last_type == F_WRLCK) |
| { |
| /* Don't call WaitForSingleObject if we already |
| own the mutex, because doing so will require |
| us to call ReleaseMutex an equal number of |
| times, before the mutex is actually |
| released. */ |
| return 0; |
| } |
| |
| result = WaitForSingleObject (hmutex, INFINITE); |
| switch (result) |
| { |
| case WAIT_OBJECT_0: |
| /* We don't care if the mutex owner crashed or |
| exited. */ |
| case WAIT_ABANDONED: |
| return 0; |
| case WAIT_FAILED: |
| case WAIT_TIMEOUT: /* cannot happen, really */ |
| { |
| DWORD err = GetLastError (); |
| |
| /* Invalidate the last command. */ |
| memset (&last_fl, 0, sizeof (last_fl)); |
| |
| switch (err) |
| { |
| case ERROR_INVALID_HANDLE: |
| case ERROR_INVALID_FUNCTION: |
| errno = EINVAL; |
| return -1; |
| default: |
| errno = EDEADLOCK; |
| return -1; |
| } |
| } |
| } |
| } |
| case F_UNLCK: |
| { |
| /* FIXME: Perhaps we should call ReleaseMutex |
| repatedly until it errors out, to make sure the |
| mutext is released even if we somehow managed to |
| to take ownership multiple times? */ |
| BOOL status = ReleaseMutex (hmutex); |
| |
| if (status) |
| return 0; |
| else |
| { |
| DWORD err = GetLastError (); |
| |
| if (err == ERROR_NOT_OWNER) |
| errno = EPERM; |
| else |
| { |
| memset (&last_fl, 0, sizeof (last_fl)); |
| errno = EINVAL; |
| } |
| return -1; |
| } |
| } |
| default: |
| errno = ENOSYS; |
| return -1; |
| } |
| } |
| default: |
| errno = ENOSYS; |
| va_end (ap); |
| return -1; |
| } |
| } |
| |
| static intptr_t mutex_handle = -1; |
| |
| /* Record in a static variable the mutex handle we were requested to |
| use. That nameless mutex was created by the top-level Make, and |
| its handle was passed to us via inheritance. The value of that |
| handle is passed via the command-line arguments, so that we know |
| which handle to use. */ |
| void |
| record_sync_mutex (const char *str) |
| { |
| char *endp; |
| intptr_t hmutex = strtol (str, &endp, 16); |
| |
| if (*endp == '\0') |
| mutex_handle = hmutex; |
| else |
| { |
| mutex_handle = -1; |
| errno = EINVAL; |
| } |
| } |
| |
| /* Create a new mutex or reuse one created by our parent. */ |
| intptr_t |
| create_mutex (void) |
| { |
| SECURITY_ATTRIBUTES secattr; |
| intptr_t hmutex = -1; |
| |
| /* If we have a mutex handle passed from the parent Make, just use |
| that. */ |
| if (mutex_handle > 0) |
| return mutex_handle; |
| |
| /* We are the top-level Make, and we want the handle to be inherited |
| by our child processes. */ |
| secattr.nLength = sizeof (secattr); |
| secattr.lpSecurityDescriptor = NULL; /* use default security descriptor */ |
| secattr.bInheritHandle = TRUE; |
| |
| hmutex = (intptr_t)CreateMutex (&secattr, FALSE, NULL); |
| if (!hmutex) |
| { |
| DWORD err = GetLastError (); |
| |
| fprintf (stderr, "CreateMutex: error %lu\n", err); |
| errno = ENOLCK; |
| hmutex = -1; |
| } |
| |
| mutex_handle = hmutex; |
| return hmutex; |
| } |
| |
| /* Return non-zero if F1 and F2 are 2 streams representing the same |
| file or pipe or device. */ |
| int |
| same_stream (FILE *f1, FILE *f2) |
| { |
| HANDLE fh1 = (HANDLE)_get_osfhandle (fileno (f1)); |
| HANDLE fh2 = (HANDLE)_get_osfhandle (fileno (f2)); |
| |
| /* Invalid file descriptors get treated as different streams. */ |
| if (fh1 && fh1 != INVALID_HANDLE_VALUE |
| && fh2 && fh2 != INVALID_HANDLE_VALUE) |
| { |
| if (fh1 == fh2) |
| return 1; |
| else |
| { |
| DWORD ftyp1 = GetFileType (fh1), ftyp2 = GetFileType (fh2); |
| |
| if (ftyp1 != ftyp2 |
| || ftyp1 == FILE_TYPE_UNKNOWN || ftyp2 == FILE_TYPE_UNKNOWN) |
| return 0; |
| else if (ftyp1 == FILE_TYPE_CHAR) |
| { |
| /* For character devices, check if they both refer to a |
| console. This loses if both handles refer to the |
| null device (FIXME!), but in that case we don't care |
| in the context of Make. */ |
| DWORD conmode1, conmode2; |
| |
| /* Each process on Windows can have at most 1 console, |
| so if both handles are for the console device, they |
| are the same. We also compare the console mode to |
| distinguish between stdin and stdout/stderr. */ |
| if (GetConsoleMode (fh1, &conmode1) |
| && GetConsoleMode (fh2, &conmode2) |
| && conmode1 == conmode2) |
| return 1; |
| } |
| else |
| { |
| /* For disk files and pipes, compare their unique |
| attributes. */ |
| BY_HANDLE_FILE_INFORMATION bhfi1, bhfi2; |
| |
| /* Pipes get zero in the volume serial number, but do |
| appear to have meaningful information in file index |
| attributes. We test file attributes as well, for a |
| good measure. */ |
| if (GetFileInformationByHandle (fh1, &bhfi1) |
| && GetFileInformationByHandle (fh2, &bhfi2)) |
| return (bhfi1.dwVolumeSerialNumber == bhfi2.dwVolumeSerialNumber |
| && bhfi1.nFileIndexLow == bhfi2.nFileIndexLow |
| && bhfi1.nFileIndexHigh == bhfi2.nFileIndexHigh |
| && bhfi1.dwFileAttributes == bhfi2.dwFileAttributes); |
| } |
| } |
| } |
| return 0; |
| } |
| |
| /* A replacement for tmpfile, since the MSVCRT implementation creates |
| the file in the root directory of the current drive, which might |
| not be writable by our user. Most of the code borrowed from |
| create_batch_file, see job.c. */ |
| FILE * |
| tmpfile (void) |
| { |
| char temp_path[MAXPATHLEN]; |
| unsigned path_size = GetTempPath (sizeof temp_path, temp_path); |
| int path_is_dot = 0; |
| /* The following variable is static so we won't try to reuse a name |
| that was generated a little while ago, because that file might |
| not be on disk yet, since we use FILE_ATTRIBUTE_TEMPORARY below, |
| which tells the OS it doesn't need to flush the cache to disk. |
| If the file is not yet on disk, we might think the name is |
| available, while it really isn't. This happens in parallel |
| builds, where Make doesn't wait for one job to finish before it |
| launches the next one. */ |
| static unsigned uniq = 0; |
| static int second_loop = 0; |
| const char base[] = "gmake_tmpf"; |
| const unsigned sizemax = sizeof base - 1 + 4 + 10 + 10; |
| unsigned pid = GetCurrentProcessId (); |
| |
| if (path_size == 0) |
| { |
| path_size = GetCurrentDirectory (sizeof temp_path, temp_path); |
| path_is_dot = 1; |
| } |
| |
| ++uniq; |
| if (uniq >= 0x10000 && !second_loop) |
| { |
| /* If we already had 64K batch files in this |
| process, make a second loop through the numbers, |
| looking for free slots, i.e. files that were |
| deleted in the meantime. */ |
| second_loop = 1; |
| uniq = 1; |
| } |
| while (path_size > 0 && |
| path_size + sizemax < sizeof temp_path && |
| !(uniq >= 0x10000 && second_loop)) |
| { |
| HANDLE h; |
| |
| sprintf (temp_path + path_size, |
| "%s%s%u-%x.tmp", |
| temp_path[path_size - 1] == '\\' ? "" : "\\", |
| base, pid, uniq); |
| h = CreateFile (temp_path, /* file name */ |
| GENERIC_READ | GENERIC_WRITE | DELETE, /* desired access */ |
| FILE_SHARE_READ | FILE_SHARE_WRITE, /* share mode */ |
| NULL, /* default security attributes */ |
| CREATE_NEW, /* creation disposition */ |
| FILE_ATTRIBUTE_NORMAL | /* flags and attributes */ |
| FILE_ATTRIBUTE_TEMPORARY | |
| FILE_FLAG_DELETE_ON_CLOSE, |
| NULL); /* no template file */ |
| |
| if (h == INVALID_HANDLE_VALUE) |
| { |
| const DWORD er = GetLastError (); |
| |
| if (er == ERROR_FILE_EXISTS || er == ERROR_ALREADY_EXISTS) |
| { |
| ++uniq; |
| if (uniq == 0x10000 && !second_loop) |
| { |
| second_loop = 1; |
| uniq = 1; |
| } |
| } |
| |
| /* The temporary path is not guaranteed to exist, or might |
| not be writable by user. Use the current directory as |
| fallback. */ |
| else if (path_is_dot == 0) |
| { |
| path_size = GetCurrentDirectory (sizeof temp_path, temp_path); |
| path_is_dot = 1; |
| } |
| |
| else |
| { |
| errno = EACCES; |
| break; |
| } |
| } |
| else |
| { |
| int fd = _open_osfhandle ((intptr_t)h, 0); |
| |
| return _fdopen (fd, "w+b"); |
| } |
| } |
| |
| if (uniq >= 0x10000) |
| errno = EEXIST; |
| return NULL; |
| } |
| |
| #endif /* !NO_OUTPUT_SYNC */ |
| |
| #if MAKE_LOAD |
| |
| /* Support for dynamic loading of objects. */ |
| |
| |
| static DWORD last_err; |
| |
| void * |
| dlopen (const char *file, int mode) |
| { |
| char dllfn[MAX_PATH], *p; |
| HANDLE dllhandle; |
| |
| if ((mode & ~(RTLD_LAZY | RTLD_NOW | RTLD_GLOBAL)) != 0) |
| { |
| errno = EINVAL; |
| last_err = ERROR_INVALID_PARAMETER; |
| return NULL; |
| } |
| |
| if (!file) |
| dllhandle = GetModuleHandle (NULL); |
| else |
| { |
| /* MSDN says to be sure to use backslashes in the DLL file name. */ |
| strcpy (dllfn, file); |
| for (p = dllfn; *p; p++) |
| if (*p == '/') |
| *p = '\\'; |
| |
| dllhandle = LoadLibrary (dllfn); |
| } |
| if (!dllhandle) |
| last_err = GetLastError (); |
| |
| return dllhandle; |
| } |
| |
| char * |
| dlerror (void) |
| { |
| static char errbuf[1024]; |
| DWORD ret; |
| |
| if (!last_err) |
| return NULL; |
| |
| ret = FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM |
| | FORMAT_MESSAGE_IGNORE_INSERTS, |
| NULL, last_err, 0, errbuf, sizeof (errbuf), NULL); |
| while (ret > 0 && (errbuf[ret - 1] == '\n' || errbuf[ret - 1] == '\r')) |
| --ret; |
| |
| errbuf[ret] = '\0'; |
| if (!ret) |
| sprintf (errbuf, "Error code %lu", last_err); |
| |
| last_err = 0; |
| return errbuf; |
| } |
| |
| void * |
| dlsym (void *handle, const char *name) |
| { |
| FARPROC addr = NULL; |
| |
| if (!handle || handle == INVALID_HANDLE_VALUE) |
| { |
| last_err = ERROR_INVALID_PARAMETER; |
| return NULL; |
| } |
| |
| addr = GetProcAddress (handle, name); |
| if (!addr) |
| last_err = GetLastError (); |
| |
| return (void *)addr; |
| } |
| |
| int |
| dlclose (void *handle) |
| { |
| if (!handle || handle == INVALID_HANDLE_VALUE) |
| return -1; |
| if (!FreeLibrary (handle)) |
| return -1; |
| |
| return 0; |
| } |
| |
| |
| #endif /* MAKE_LOAD */ |
| |
| |
| /* MS runtime's isatty returns non-zero for any character device, |
| including the null device, which is not what we want. */ |
| int |
| isatty (int fd) |
| { |
| HANDLE fh = (HANDLE) _get_osfhandle (fd); |
| DWORD con_mode; |
| |
| if (fh == INVALID_HANDLE_VALUE) |
| { |
| errno = EBADF; |
| return 0; |
| } |
| if (GetConsoleMode (fh, &con_mode)) |
| return 1; |
| |
| errno = ENOTTY; |
| return 0; |
| } |
| |
| char * |
| ttyname (int fd) |
| { |
| /* This "knows" that Make only asks about stdout and stderr. A more |
| sophisticated implementation should test whether FD is open for |
| input or output. We can do that by looking at the mode returned |
| by GetConsoleMode. */ |
| return "CONOUT$"; |
| } |