windows: use OpenBSD implementation for uv_fs_mkdtemp

This commit is contained in:
Pavel Platto 2014-08-07 09:47:54 +03:00 committed by Saúl Ibarra Corretgé
parent 875814adc5
commit a669f21bf8
2 changed files with 52 additions and 58 deletions

View File

@ -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);

View File

@ -36,6 +36,8 @@
#include "req-inl.h"
#include "handle-inl.h"
#include <wincrypt.h>
#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);
if (!CryptAcquireContext(&h_crypt_prov, NULL, NULL, PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT)) {
SET_REQ_WIN32_ERROR(req, GetLastError());
return;
}
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);
return;
}
break;
}
} while (--tries);
/* We got out of the loop because we ran out of combinations to try. */
released = CryptReleaseContext(h_crypt_prov, 0);
assert(released);
if (tries == 0) {
SET_REQ_RESULT(req, -1);
}
}