unix, windows: add uv_fs_mkdtemp
This commit is contained in:
parent
beb54fe72d
commit
e99b89f2ad
@ -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);
|
||||
|
||||
|
||||
@ -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,
|
||||
|
||||
93
src/win/fs.c
93
src/win/fs.c
@ -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;
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user