Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

mkstemp() implementation for win32

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

like image 721
Andrei Avatar asked May 17 '11 19:05

Andrei


3 Answers

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.

like image 104
Dmitry Kuzmin Avatar answered Oct 07 '22 10:10

Dmitry Kuzmin


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.

like image 34
Keith Marshall Avatar answered Oct 07 '22 08:10

Keith Marshall


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.

like image 36
ismail Avatar answered Oct 07 '22 09:10

ismail