Can anybody point me to the code that implements mkstemp() (C/C++) on Win32, or very close analog.
Must be race-free.
It's supposed to look like
#include <windows.h>
#include <io.h>
// port of mkstemp() to win32. race-free.
// behaviour as described in http://linux.die.net/man/3/mkstemp
//
int mkstemp(char *template) {
...
}
Thanks
You can use the following function which is extracted from wcecompat
library (from file src/stdlib_extras.cpp
)
/* mkstemp extracted from libc/sysdeps/posix/tempname.c. Copyright
(C) 1991-1999, 2000, 2001, 2006 Free Software Foundation, Inc.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. */
static const char letters[] =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
/* Generate a temporary file name based on TMPL. TMPL must match the
rules for mk[s]temp (i.e. end in "XXXXXX"). The name constructed
does not exist at the time of the call to mkstemp. TMPL is
overwritten with the result. */
int
mkstemp (char *tmpl)
{
int len;
char *XXXXXX;
static unsigned long long value;
unsigned long long random_time_bits;
unsigned int count;
int fd = -1;
int save_errno = errno;
/* A lower bound on the number of temporary files to attempt to
generate. The maximum total number of temporary file names that
can exist for a given template is 62**6. It should never be
necessary to try all these combinations. Instead if a reasonable
number of names is tried (we define reasonable as 62**3) fail to
give the system administrator the chance to remove the problems. */
#define ATTEMPTS_MIN (62 * 62 * 62)
/* The number of times to attempt to generate a temporary file. To
conform to POSIX, this must be no smaller than TMP_MAX. */
#if ATTEMPTS_MIN < TMP_MAX
unsigned int attempts = TMP_MAX;
#else
unsigned int attempts = ATTEMPTS_MIN;
#endif
len = strlen (tmpl);
if (len < 6 || strcmp (&tmpl[len - 6], "XXXXXX"))
{
errno = EINVAL;
return -1;
}
/* This is where the Xs start. */
XXXXXX = &tmpl[len - 6];
/* Get some more or less random data. */
{
SYSTEMTIME stNow;
FILETIME ftNow;
// get system time
GetSystemTime(&stNow);
stNow.wMilliseconds = 500;
if (!SystemTimeToFileTime(&stNow, &ftNow))
{
errno = -1;
return -1;
}
random_time_bits = (((unsigned long long)ftNow.dwHighDateTime << 32)
| (unsigned long long)ftNow.dwLowDateTime);
}
value += random_time_bits ^ (unsigned long long)GetCurrentThreadId ();
for (count = 0; count < attempts; value += 7777, ++count)
{
unsigned long long v = value;
/* Fill in the random bits. */
XXXXXX[0] = letters[v % 62];
v /= 62;
XXXXXX[1] = letters[v % 62];
v /= 62;
XXXXXX[2] = letters[v % 62];
v /= 62;
XXXXXX[3] = letters[v % 62];
v /= 62;
XXXXXX[4] = letters[v % 62];
v /= 62;
XXXXXX[5] = letters[v % 62];
fd = open (tmpl, O_RDWR | O_CREAT | O_EXCL, _S_IREAD | _S_IWRITE);
if (fd >= 0)
{
errno = save_errno;
return fd;
}
else if (errno != EEXIST)
return -1;
}
/* We got out of the loop because we ran out of combinations to try. */
errno = EEXIST;
return -1;
}
It defines O_EXCL
as;
#define _O_EXCL 0x0400
#define O_EXCL _O_EXCL
You can rip out mkstemp support out of it easily.
Actually, using _mktemp_s() is a really bad idea -- only 26 possible file name candidates in any one context, and, with this limited range to be attacked, it exposes the very race condition that mkstemp() is designed to overcome. However, the other proposed solution, while much better, is also flawed, insofar as it attributes 62 degrees of freedom in the choice of substitute file name characters, whereas the case insensitivity of the Windows file system consumes 26 of these, thus leaving only 36; this has the effect of weighting the probability of selecting any logically distinguishable alphabetic character at double that for a numeric.
With this in mind, I've posted a MinGW patch here:
https://sourceforge.net/p/mingw/bugs/2003/
If adopted, this will formally add both mkstemp() and mkdtemp() to the standard MinGW distribution.
You can use _mktemp_s() function, or any of it's variations:
errno_t _mktemp_s(
char *template,
size_t sizeInChars
);
where:
- template: File name pattern.
- sizeInChars: Size of the buffer in single-byte characters in
_mktemp_s
; wide characters in _wmktemp_s
, including the null terminator.
It returns 0 on success, and an error code on failure. Note that the function modifyes the template
argument.