unix, windows: add uv_fs_mkdtemp

This commit is contained in:
Pavel Platto 2014-07-13 22:42:14 +03:00 committed by Saúl Ibarra Corretgé
parent beb54fe72d
commit e99b89f2ad
5 changed files with 177 additions and 0 deletions

View File

@ -1849,6 +1849,7 @@ typedef enum {
UV_FS_UNLINK,
UV_FS_RMDIR,
UV_FS_MKDIR,
UV_FS_MKDTEMP,
UV_FS_RENAME,
UV_FS_READDIR,
UV_FS_LINK,
@ -1891,6 +1892,9 @@ 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);
UV_EXTERN int uv_fs_mkdtemp(uv_loop_t* loop, uv_fs_t* req, const char* template,
uv_fs_cb cb);
UV_EXTERN int uv_fs_rmdir(uv_loop_t* loop, uv_fs_t* req, const char* path,
uv_fs_cb cb);

View File

@ -214,6 +214,11 @@ skip:
}
static ssize_t uv__fs_mkdtemp(uv_fs_t* req) {
return mkdtemp((char*) req->path) ? 0 : -1;
}
static ssize_t uv__fs_read(uv_fs_t* req) {
ssize_t result;
@ -789,6 +794,7 @@ static void uv__fs_work(struct uv__work* w) {
X(LSTAT, uv__fs_lstat(req->path, &req->statbuf));
X(LINK, link(req->path, req->new_path));
X(MKDIR, mkdir(req->path, req->mode));
X(MKDTEMP, uv__fs_mkdtemp(req));
X(READ, uv__fs_read(req));
X(READDIR, uv__fs_readdir(req));
X(READLINK, uv__fs_readlink(req));
@ -1001,6 +1007,18 @@ int uv_fs_mkdir(uv_loop_t* loop,
}
int uv_fs_mkdtemp(uv_loop_t* loop,
uv_fs_t* req,
const char* template,
uv_fs_cb cb) {
INIT(MKDTEMP);
req->path = strdup(template);
if (req->path == NULL)
return -ENOMEM;
POST;
}
int uv_fs_open(uv_loop_t* loop,
uv_fs_t* req,
const char* path,

View File

@ -721,6 +721,78 @@ void fs__mkdir(uv_fs_t* req) {
}
/* Some parts of the implementation were borrowed from glibc. */
void fs__mkdtemp(uv_fs_t* req) {
static const WCHAR letters[] =
L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
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
len = wcslen(req->pathw);
if (len < 6 || wcsncmp(&req->pathw[len - 6], L"XXXXXX", 6)) {
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;
}
}
/* We got out of the loop because we ran out of combinations to try. */
SET_REQ_RESULT(req, -1);
}
void fs__readdir(uv_fs_t* req) {
WCHAR* pathw = req->pathw;
size_t len = wcslen(pathw);
@ -1528,6 +1600,7 @@ static void uv__fs_work(struct uv__work* w) {
XX(UNLINK, unlink)
XX(RMDIR, rmdir)
XX(MKDIR, mkdir)
XX(MKDTEMP, mkdtemp)
XX(RENAME, rename)
XX(READDIR, readdir)
XX(LINK, link)
@ -1724,6 +1797,26 @@ int uv_fs_mkdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode,
}
int uv_fs_mkdtemp(uv_loop_t* loop, uv_fs_t* req, const char* template,
uv_fs_cb cb) {
int err;
uv_fs_req_init(loop, req, UV_FS_MKDTEMP, cb);
err = fs__capture_path(loop, req, template, NULL, TRUE);
if (err)
return uv_translate_sys_error(err);
if (cb) {
QUEUE_FS_TP_JOB(loop, req);
return 0;
} else {
fs__mkdtemp(req);
return req->result;
}
}
int uv_fs_rmdir(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) {
int err;

View File

@ -65,6 +65,7 @@ static int read_cb_count;
static int write_cb_count;
static int unlink_cb_count;
static int mkdir_cb_count;
static int mkdtemp_cb_count;
static int rmdir_cb_count;
static int readdir_cb_count;
static int stat_cb_count;
@ -93,6 +94,8 @@ static uv_fs_t write_req;
static uv_fs_t unlink_req;
static uv_fs_t close_req;
static uv_fs_t mkdir_req;
static uv_fs_t mkdtemp_req1;
static uv_fs_t mkdtemp_req2;
static uv_fs_t rmdir_req;
static uv_fs_t readdir_req;
static uv_fs_t stat_req;
@ -376,6 +379,32 @@ static void mkdir_cb(uv_fs_t* req) {
}
static void check_mkdtemp_result(uv_fs_t* req) {
int r;
ASSERT(req->fs_type == UV_FS_MKDTEMP);
ASSERT(req->result == 0);
ASSERT(req->path);
ASSERT(strlen(req->path) == 15);
ASSERT(memcmp(req->path, "test_dir_", 9) == 0);
ASSERT(memcmp(req->path + 9, "XXXXXX", 6) != 0);
check_permission(req->path, 0700);
/* Check if req->path is actually a directory */
r = uv_fs_stat(uv_default_loop(), &stat_req, req->path, NULL);
ASSERT(r == 0);
ASSERT(((uv_stat_t*)stat_req.ptr)->st_mode & S_IFDIR);
uv_fs_req_cleanup(&stat_req);
}
static void mkdtemp_cb(uv_fs_t* req) {
ASSERT(req == &mkdtemp_req1);
check_mkdtemp_result(req);
mkdtemp_cb_count++;
}
static void rmdir_cb(uv_fs_t* req) {
ASSERT(req == &rmdir_req);
ASSERT(req->fs_type == UV_FS_RMDIR);
@ -927,6 +956,37 @@ TEST_IMPL(fs_async_sendfile) {
}
TEST_IMPL(fs_mkdtemp) {
int r;
const char* path_template = "test_dir_XXXXXX";
loop = uv_default_loop();
r = uv_fs_mkdtemp(loop, &mkdtemp_req1, path_template, mkdtemp_cb);
ASSERT(r == 0);
uv_run(loop, UV_RUN_DEFAULT);
ASSERT(mkdtemp_cb_count == 1);
/* sync mkdtemp */
r = uv_fs_mkdtemp(loop, &mkdtemp_req2, path_template, NULL);
ASSERT(r == 0);
check_mkdtemp_result(&mkdtemp_req2);
/* mkdtemp return different values on subsequent calls */
ASSERT(strcmp(mkdtemp_req1.path, mkdtemp_req2.path) != 0);
/* Cleanup */
rmdir(mkdtemp_req1.path);
rmdir(mkdtemp_req2.path);
uv_fs_req_cleanup(&mkdtemp_req1);
uv_fs_req_cleanup(&mkdtemp_req2);
MAKE_VALGRIND_HAPPY();
return 0;
}
TEST_IMPL(fs_fstat) {
int r;
uv_fs_t req;

View File

@ -200,6 +200,7 @@ TEST_DECLARE (fs_file_sync)
TEST_DECLARE (fs_file_write_null_buffer)
TEST_DECLARE (fs_async_dir)
TEST_DECLARE (fs_async_sendfile)
TEST_DECLARE (fs_mkdtemp)
TEST_DECLARE (fs_fstat)
TEST_DECLARE (fs_chmod)
TEST_DECLARE (fs_chown)
@ -548,6 +549,7 @@ TASK_LIST_START
TEST_ENTRY (fs_file_write_null_buffer)
TEST_ENTRY (fs_async_dir)
TEST_ENTRY (fs_async_sendfile)
TEST_ENTRY (fs_mkdtemp)
TEST_ENTRY (fs_fstat)
TEST_ENTRY (fs_chmod)
TEST_ENTRY (fs_chown)