On Wed, 29 Oct 2025, Pali Rohár wrote:
Function tmpfile() in msvcr100 and older versions (including the system os
msvcrt.dll) is trying to create a temporary file in the root of the drive
corresponding to current working directory. In most cases is the current
working directory in some user profile application directory, which means
that the directory for temporary file used by the tmpfile() is often C:\.
Writing to the C:\ is not allowed by unprivileged processes on Windows
Vista and new systems, so tmpfile() is not working at all.
This tmpfile() problem was fixed in msvcr110.dll (and hence also in UCRT)
and tmpfile() in these new versions is creating a temporary file in the
temporary directory returned by WinAPI function GetTempPath() instead of
the root directory.
For affected msvcrt import libraries, replace the msvcrt version of
tmpfile() function by mingw-w64 emulation which attempts to create a
temporary file in GetTempPath() too. For generating unique filename is used
the tmpnam() function together with open(O_CREAT | O_EXCL) in a loop to
prevent race condition in case two different processes or threads generate
same temporary name by the tmpnam() function.
Seems that tmpnam() function generates same file name pattern as the
tmpfile() function. So with this change, the file name should be same, just
located in the proper temporary directory.
Bug: https://sourceforge.net/p/mingw-w64/bugs/921/
diff --git a/mingw-w64-crt/stdio/tmpfile.c b/mingw-w64-crt/stdio/tmpfile.c
new file mode 100644
index 000000000000..f0c77f52a18a
--- /dev/null
+++ b/mingw-w64-crt/stdio/tmpfile.c
@@ -0,0 +1,71 @@
+/**
+ * This file has no copyright assigned and is placed in the Public Domain.
+ * This file is part of the mingw-w64 runtime package.
+ * No warranty is given; refer to the file DISCLAIMER.PD within this package.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <io.h>
+#include <windows.h>
+
+FILE *__cdecl tmpfile(void)
+{
+ /* GetTempPathA() requires buffer of size MAX_PATH+1
+ * and tmpnam() requires buffer of size L_tmpnam.
+ */
+ char path[MAX_PATH+1+L_tmpnam];
+ DWORD len;
+ int fd;
+ FILE *file;
+
+ len = GetTempPathA(MAX_PATH, path);
+ if (len == 0 || len > MAX_PATH) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /* Function tmpnam() generates path name starting with backslash,
+ * so ensure that the temp directory path does not contain it.
+ */
https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/tempnam-wtempnam-tmpnam-wtmpnam?view=msvc-170
says that tmpnam() returns a name in the directory GetTempPathW() - but I
presume this is new in msvcr110/UCRT or newer? As the behaviour you
describe of tmpnam() is that it only generates a string "\<random>",
right? As the old tmpnam() doesn't see the full string, only the name
subset, does it do anything to check that the returned names are unique
already? As the current docs say "Each of these functions returns the name
of a file that doesn't currently exist. " - but that can't be proven if it
doesn't even see the full path?
The patch looks ok, if old versions of tmpnam() behave in the way of just
returning a random string and not trying to check if the file is unique
(ok, we can't know what it does internally) without a full path, just
starting with a backslash.
It would be good to clarify this aspect. But I guess that's what you
already try to say in
Seems that tmpnam() function generates same file name pattern as the
tmpfile() function.
So in short - the current documentation of tmpnam() doesn't hold up at all
for the old tmpnam() functions?
// Martin
_______________________________________________
Mingw-w64-public mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/mingw-w64-public