[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: Make 3.99.91/92 - Output-Synchronization fails with UAC or Non-Admin
From: |
Eli Zaretskii |
Subject: |
Re: Make 3.99.91/92 - Output-Synchronization fails with UAC or Non-Admins (under Windows) |
Date: |
Mon, 23 Sep 2013 23:04:01 +0300 |
> Date: Mon, 23 Sep 2013 16:21:30 +0200
> From: Markus Eisenmann <address@hidden>
>
> I have built a windows-make version of the release-canditate versions
> 3.99.91 and 3.99.92,
> using Visual Studio and MinGW. The result, I.e. make fails if I'm using
> parallel builds and the feature 'output-synchronization' with an error
> like 'could not create tmpfile'.
>
> After spending some time for debugging, I have detected the "problem":
> - A temporary file is used to buffer the outputs (of course)
> - this file is created with calling 'tmpfile()'.
>
> As written in
> http://msdn.microsoft.com/en-us/library/x8x7sakw%28v=vs.90%29.aspx,
> tmpfile() does create a temporary file in the root directory; But in many
> cases - e.g. inhibited by NTFS-security or if UAC is active - a creation of
> files in the root-directory is forbidden.
I don't think this has anything to do with UAC, it's just that the
root of a system disk is write-protected from casual writes. How
silly of Microsoft not to do something about this on Windows 7 and
later!
> IMHO, I think, this type of temporary files should be created like other
> needed temporary (batch-) files in the TMP/TEMP-folder (using the
> associated environment variable).
I agree. Can you please test with the following patch? I'm also
testing it; if it will give good results, I will commit it to the
repository.
Thanks.
--- output.c~0 2013-09-23 00:10:34.000000000 +0300
+++ output.c 2013-09-23 22:58:51.963375000 +0300
@@ -72,6 +72,103 @@ msc_vsnprintf (char *str, size_t size, c
}
#endif
+#ifdef WINDOWS32
+/* 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[] = "make_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))
+ {
+ sprintf (temp_path + path_size,
+ "%s%s%u-%x.tmp",
+ temp_path[path_size - 1] == '\\' ? "" : "\\",
+ base, pid, uniq);
+ /* O_CREAT | O_EXCL | O_RDWR | O_BINARY | O_TEMPORARY
+ SH_DENYNO */
+ HANDLE h = CreateFile (temp_path, /* file name */
+ GENERIC_READ | GENERIC_WRITE | DELETE, /* desired
access */
+ 0, /* no 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 */
+ else if (path_is_dot == 0)
+ {
+ path_size = GetCurrentDirectory (sizeof temp_path, temp_path);
+ path_is_dot = 1;
+ }
+
+ else
+ break;
+ }
+ else
+ {
+ int fd = _open_osfhandle ((intptr_t)h, 0);
+
+ return _fdopen (fd, "w+b");
+ }
+ }
+
+ return NULL;
+}
+#endif
+
/* Write a string to the current STDOUT or STDERR. */
static void
_outputs (struct output *out, int is_err, const char *msg)