/* Replacements for Posix functions and Posix functionality for MS-Windows.
-Copyright (C) 2013-2020 Free Software Foundation, Inc.
+Copyright (C) 2013-2022 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
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/>. */
+this program. If not, see <https://www.gnu.org/licenses/>. */
+
+#include "makeint.h"
#include <string.h>
#include <io.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 *