From a669f21bf88fdd06f445e5507ed1f834c0ba6432 Mon Sep 17 00:00:00 2001 From: Pavel Platto Date: Thu, 7 Aug 2014 09:47:54 +0300 Subject: [PATCH] windows: use OpenBSD implementation for uv_fs_mkdtemp --- include/uv.h | 6 +++ src/win/fs.c | 104 +++++++++++++++++++++++---------------------------- 2 files changed, 52 insertions(+), 58 deletions(-) diff --git a/include/uv.h b/include/uv.h index 649758de2..ff62df9bf 100644 --- a/include/uv.h +++ b/include/uv.h @@ -1916,6 +1916,12 @@ UV_EXTERN int uv_fs_write(uv_loop_t* loop, uv_fs_t* req, uv_file file, UV_EXTERN int uv_fs_mkdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, uv_fs_cb cb); +/* + * Generates a uniquely named temporary directory from tpl. The last six + * characters of tpl must be XXXXXX and these are replaced with a string that + * makes the directory name unique. On success the name of created directory + * will be stored in req->path. + */ UV_EXTERN int uv_fs_mkdtemp(uv_loop_t* loop, uv_fs_t* req, const char* tpl, uv_fs_cb cb); diff --git a/src/win/fs.c b/src/win/fs.c index 8b52e610f..3d3c4fdd3 100644 --- a/src/win/fs.c +++ b/src/win/fs.c @@ -36,6 +36,8 @@ #include "req-inl.h" #include "handle-inl.h" +#include + #define UV_FS_FREE_PATHS 0x0002 #define UV_FS_FREE_PTR 0x0008 @@ -721,75 +723,61 @@ void fs__mkdir(uv_fs_t* req) { } -/* Some parts of the implementation were borrowed from glibc. */ +/* OpenBSD original: lib/libc/stdio/mktemp.c */ void fs__mkdtemp(uv_fs_t* req) { - static const WCHAR letters[] = + static const WCHAR *tempchars = L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + static const size_t num_chars = 62; + static const size_t num_x = 6; + WCHAR *cp, *ep; + unsigned int tries, i; size_t len; - WCHAR* template_part; - static uint64_t value; - unsigned int count; - int fd; - - /* 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 + HCRYPTPROV h_crypt_prov; + uint64_t v; + BOOL released; len = wcslen(req->pathw); - if (len < 6 || wcsncmp(&req->pathw[len - 6], L"XXXXXX", 6)) { + ep = req->pathw + len; + if (len < num_x || wcsncmp(ep - num_x, L"XXXXXX", num_x)) { SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER); return; } - /* This is where the Xs start. */ - template_part = &req->pathw[len - 6]; - - /* Get some random data. */ - value += uv_hrtime() ^ _getpid(); - - for (count = 0; count < attempts; value += 7777, ++count) { - uint64_t v = value; - - /* Fill in the random bits. */ - template_part[0] = letters[v % 62]; - v /= 62; - template_part[1] = letters[v % 62]; - v /= 62; - template_part[2] = letters[v % 62]; - v /= 62; - template_part[3] = letters[v % 62]; - v /= 62; - template_part[4] = letters[v % 62]; - v /= 62; - template_part[5] = letters[v % 62]; - - fd = _wmkdir(req->pathw); - - if (fd >= 0) { - len = strlen(req->path); - wcstombs((char*) req->path + len - 6, template_part, 6); - SET_REQ_RESULT(req, 0); - return; - } else if (errno != EEXIST) { - SET_REQ_RESULT(req, -1); - return; - } + if (!CryptAcquireContext(&h_crypt_prov, NULL, NULL, PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT)) { + SET_REQ_WIN32_ERROR(req, GetLastError()); + return; } - /* We got out of the loop because we ran out of combinations to try. */ - SET_REQ_RESULT(req, -1); + tries = TMP_MAX; + do { + if (!CryptGenRandom(h_crypt_prov, sizeof(v), (BYTE*) &v)) { + SET_REQ_WIN32_ERROR(req, GetLastError()); + break; + } + + cp = ep - num_x; + for (i = 0; i < num_x; i++) { + *cp++ = tempchars[v % num_chars]; + v /= num_chars; + } + + if (_wmkdir(req->pathw) == 0) { + len = strlen(req->path); + wcstombs((char*) req->path + len - num_x, ep - num_x, num_x); + SET_REQ_RESULT(req, 0); + break; + } else if (errno != EEXIST) { + SET_REQ_RESULT(req, -1); + break; + } + } while (--tries); + + released = CryptReleaseContext(h_crypt_prov, 0); + assert(released); + if (tries == 0) { + SET_REQ_RESULT(req, -1); + } }