From a1dea980e867ceab8a5341ec1f761c572345c73c Mon Sep 17 00:00:00 2001 From: Pavel Platto Date: Sun, 13 Jul 2014 22:42:14 +0300 Subject: [PATCH] Implement uv_fs_mkdtemp --- include/uv.h | 4 +++ src/unix/fs.c | 18 ++++++++++ src/win/fs.c | 94 ++++++++++++++++++++++++++++++++++++++++++++++++ test/test-fs.c | 61 +++++++++++++++++++++++++++++++ test/test-list.h | 2 ++ 5 files changed, 179 insertions(+) diff --git a/include/uv.h b/include/uv.h index 8909e10f24..ea52a7b638 100644 --- a/include/uv.h +++ b/include/uv.h @@ -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); diff --git a/src/unix/fs.c b/src/unix/fs.c index 2f502ed350..6e25fe3b54 100644 --- a/src/unix/fs.c +++ b/src/unix/fs.c @@ -214,6 +214,11 @@ static ssize_t uv__fs_futime(uv_fs_t* req) { } +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, diff --git a/src/win/fs.c b/src/win/fs.c index 4de2988424..8a429b9d4a 100644 --- a/src/win/fs.c +++ b/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,27 @@ 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; diff --git a/test/test-fs.c b/test/test-fs.c index 5cfbeeadc3..15c6eea97b 100644 --- a/test/test-fs.c +++ b/test/test-fs.c @@ -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,38 @@ TEST_IMPL(fs_async_sendfile) { } +TEST_IMPL(fs_mkdtemp) { + int r; + const char* path_template = "test_dir_XXXXXX"; + + loop = uv_default_loop(); + + /* async mkdtemp, store result value in buf */ + 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; diff --git a/test/test-list.h b/test/test-list.h index a62c11e436..9650f8e4b2 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -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)