Imported Upstream version 4.4
[platform/upstream/make.git] / src / w32 / w32os.c
index fe74811..9c5dec2 100644 (file)
@@ -1,5 +1,5 @@
 /* Windows32-based operating system interface for GNU Make.
-Copyright (C) 2016-2020 Free Software Foundation, Inc.
+Copyright (C) 2016-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
@@ -12,7 +12,7 @@ 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/>.  */
+this program.  If not, see <https://www.gnu.org/licenses/>.  */
 
 #include "makeint.h"
 
@@ -22,22 +22,202 @@ this program.  If not, see <http://www.gnu.org/licenses/>.  */
 #include <windows.h>
 #include <process.h>
 #include <io.h>
+#if _WIN32_WINNT > 0x0601
+#include <synchapi.h>
+#endif
 #include "pathstuff.h"
 #include "sub_proc.h"
 #include "w32err.h"
 #include "os.h"
 #include "debug.h"
 
+unsigned int
+check_io_state ()
+{
+  static unsigned int state = IO_UNKNOWN;
+
+  /* We only need to compute this once per process.  */
+  if (state != IO_UNKNOWN)
+    return state;
+
+  /* Could have used GetHandleInformation, but that isn't supported
+     on Windows 9X.  */
+  HANDLE outfd = (HANDLE)_get_osfhandle (fileno (stdout));
+  HANDLE errfd = (HANDLE)_get_osfhandle (fileno (stderr));
+
+  if ((HANDLE)_get_osfhandle (fileno (stdin)) != INVALID_HANDLE_VALUE)
+    state |= IO_STDIN_OK;
+  if (outfd != INVALID_HANDLE_VALUE)
+    state |= IO_STDOUT_OK;
+  if (errfd != INVALID_HANDLE_VALUE)
+    state |= IO_STDERR_OK;
+
+  if (ALL_SET (state, IO_STDOUT_OK|IO_STDERR_OK))
+    {
+      unsigned int combined = 0;
+
+      if (outfd == errfd)
+        combined = IO_COMBINED_OUTERR;
+      else
+        {
+          DWORD outtype = GetFileType (outfd), errtype = GetFileType (errfd);
+
+          if (outtype == errtype
+              && outtype != FILE_TYPE_UNKNOWN && errtype != FILE_TYPE_UNKNOWN)
+            {
+              if (outtype == 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 outmode, errmode;
+
+                  /* 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 (outfd, &outmode)
+                      && GetConsoleMode (errfd, &errmode)
+                      && outmode == errmode)
+                    combined = IO_COMBINED_OUTERR;
+                }
+              else
+                {
+                  /* For disk files and pipes, compare their unique
+                     attributes.  */
+                  BY_HANDLE_FILE_INFORMATION outfi, errfi;
+
+                  /* 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 (outfd, &outfi)
+                      && GetFileInformationByHandle (errfd, &errfi)
+                      && outfi.dwVolumeSerialNumber == errfi.dwVolumeSerialNumber
+                      && outfi.nFileIndexLow == errfi.nFileIndexLow
+                      && outfi.nFileIndexHigh == errfi.nFileIndexHigh
+                      && outfi.dwFileAttributes == errfi.dwFileAttributes)
+                    combined = IO_COMBINED_OUTERR;
+                }
+            }
+        }
+      state |= combined;
+    }
+
+  return state;
+}
+
+/* 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, and also it returns a FILE* and we want a file
+   descriptor.  Mostly borrowed from create_batch_file, see job.c.  */
+int
+os_anontmp ()
+{
+  char temp_path[MAX_PATH+1];
+  unsigned path_size = GetTempPath (sizeof (temp_path), temp_path);
+  int using_cwd = 0;
+
+  /* These variables are 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.  */
+  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);
+      using_cwd = 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)
+        return _open_osfhandle ((intptr_t)h, 0);
+
+      {
+        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 (!using_cwd)
+          {
+            path_size = GetCurrentDirectory (sizeof (temp_path), temp_path);
+            using_cwd = 1;
+          }
+        else
+          {
+            errno = EACCES;
+            return -1;
+          }
+      }
+    }
+
+  if (uniq >= 0x10000)
+    errno = EEXIST;
+  return -1;
+}
+
+#if defined(MAKE_JOBSERVER)
+
 /* This section provides OS-specific functions to support the jobserver.  */
 
 static char jobserver_semaphore_name[MAX_PATH + 1];
 static HANDLE jobserver_semaphore = NULL;
 
 unsigned int
-jobserver_setup (int slots)
+jobserver_setup (int slots, const char *style)
 {
   /* sub_proc.c is limited in the number of objects it can wait for. */
 
+  if (style && strcmp (style, "sem") != 0)
+    OS (fatal, NILF, _("Unknown jobserver auth style '%s'"), style);
+
   if (slots > process_table_usable_size())
     {
       slots = process_table_usable_size();
@@ -90,6 +270,13 @@ jobserver_get_auth ()
   return xstrdup (jobserver_semaphore_name);
 }
 
+const char *
+jobserver_get_invalid_auth ()
+{
+  /* Because we're using a semaphore we don't need to invalidate.  */
+  return NULL;
+}
+
 unsigned int
 jobserver_enabled ()
 {
@@ -199,6 +386,111 @@ jobserver_acquire (int timeout)
     return dwEvent == WAIT_OBJECT_0;
 }
 
+#endif /* MAKE_JOBSERVER */
+
+#if !defined(NO_OUTPUT_SYNC)
+
+#define MUTEX_PREFIX    "fnm:"
+
+/* Since we're using this with CreateMutex, NULL is invalid.  */
+static HANDLE osync_handle = NULL;
+
+unsigned int
+osync_enabled ()
+{
+  return osync_handle != NULL;
+}
+
+void
+osync_setup ()
+{
+  SECURITY_ATTRIBUTES secattr;
+
+  /* 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;
+
+  osync_handle = CreateMutex (&secattr, FALSE, NULL);
+  if (!osync_handle)
+    {
+      DWORD err = GetLastError ();
+      fprintf (stderr, "CreateMutex: error %lu\n", err);
+      errno = ENOLCK;
+    }
+}
+
+char *
+osync_get_mutex ()
+{
+  char *mutex = NULL;
+
+  if (osync_enabled ())
+    {
+      /* Prepare the mutex handle string for our children.
+         2 hex digits per byte + 2 characters for "0x" + null.  */
+      mutex = xmalloc ((2 * sizeof (osync_handle)) + 2 + 1);
+      sprintf (mutex, "0x%Ix", (unsigned long long)(DWORD_PTR)osync_handle);
+    }
+
+  return mutex;
+}
+
+unsigned int
+osync_parse_mutex (const char *mutex)
+{
+  char *endp;
+  unsigned long long i;
+
+  errno = 0;
+  i = strtoull (mutex, &endp, 16);
+  if (errno != 0)
+    OSS (fatal, NILF, _("cannot parse output sync mutex %s: %s"),
+         mutex, strerror (errno));
+  if (endp[0] != '\0')
+    OS (fatal, NILF, _("invalid output sync mutex: %s"), mutex);
+
+  osync_handle = (HANDLE) (DWORD_PTR) i;
+
+  return 1;
+}
+
+void
+osync_clear ()
+{
+  if (osync_handle)
+    {
+      CloseHandle (osync_handle);
+      osync_handle = NULL;
+    }
+}
+
+unsigned int
+osync_acquire ()
+{
+  if (osync_enabled())
+    {
+      DWORD result = WaitForSingleObject (osync_handle, INFINITE);
+      if (result == WAIT_FAILED || result == WAIT_TIMEOUT)
+        return 0;
+    }
+
+  return 1;
+}
+
+void
+osync_release ()
+{
+  if (osync_enabled())
+    /* 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?  */
+    ReleaseMutex (osync_handle);
+}
+
+#endif /* NO_OUTPUT_SYNC */
+
 void
 fd_inherit(int fd)
 {
@@ -216,3 +508,7 @@ fd_noinherit(int fd)
   if (fh && fh != INVALID_HANDLE_VALUE)
         SetHandleInformation(fh, HANDLE_FLAG_INHERIT, 0);
 }
+
+void
+fd_set_append (int fd)
+{}