diff --git a/deps/uv/src/win/fs.c b/deps/uv/src/win/fs.c index d7087b1f55..6948f7ef1c 100644 --- a/deps/uv/src/win/fs.c +++ b/deps/uv/src/win/fs.c @@ -489,6 +489,103 @@ void fs__readdir(uv_fs_t* req, const wchar_t* path, int flags) { } +#define IS_SLASH(c) \ + ((wchar_t) c == L'/' || (wchar_t) c == L'\\') +#define IS_COLON(c) \ + ((wchar_t) c == L':') +#define IS_LETTER(c) \ + ((((wchar_t) c >= L'a') && ((wchar_t) c <= L'z')) || \ + (((wchar_t) c >= L'A') && ((wchar_t) c <= L'Z'))) +#define IS_QUESTION(c) \ + ((wchar_t) c == L'?') + + +static int uv__count_slash_separated_words(const wchar_t* pos, + const wchar_t* end, + int limit) { + char last_was_slash = 1, count = 0; + + for (; pos < end; pos++) { + if (IS_SLASH(*pos)) { + /* Don't accept double slashes */ + if (last_was_slash) { + return 0; + } else { + last_was_slash = 1; + } + } else { + if (last_was_slash) { + /* Found a new word */ + count++; + if (count > limit) { + return -1; + } + last_was_slash = 0; + } + } + } + + return count; +} + +/* + * Returns true if the given path is a root directory. The following patterns + * are recognized: + * \ + * c:\ (must have trailing slash) + * \\server\share (trailing slash optional) + * \\?\c: (trailing slash optional) + * \\?\UNC\server\share (trailing slash optional) + */ +static int uv__is_root(const wchar_t* path) { + size_t len = wcslen(path); + + /* Test for \ */ + if (len == 0 && IS_SLASH(path[0])) { + return 1; + } + + if (len < 3) { + return 0; + } + + /* Test for c:\ */ + if (IS_LETTER(path[0]) && IS_COLON(path[1]) && IS_SLASH(path[2])) { + return 1; + } + + if (!IS_SLASH(path[0]) || !IS_SLASH(path[1])) { + return 0; + } + + /* Test for \\server\share */ + if (!IS_QUESTION(path[2])) { + return uv__count_slash_separated_words(path + 2, path + len, 2) == 2; + } + + if (!IS_SLASH(path[3])) { + return 0; + } + + if ((len == 6 || len == 7) && + IS_LETTER(path[4]) && IS_COLON(path[5]) && + (len == 6 || IS_SLASH(path[6]))) { + return 1; + } + + /* Test for \\?\UNC\server\share */ + if (len >= 8 && + (path[4] == L'u' || path[4] == L'U') && + (path[5] == L'n' || path[5] == L'N') && + (path[6] == L'c' || path[6] == L'C') && + IS_SLASH(path[7])) { + return uv__count_slash_separated_words(path + 8, path + len, 2) == 2; + } + + return 0; +} + + void fs__stat(uv_fs_t* req, const wchar_t* path) { HANDLE file; WIN32_FIND_DATAW ent; @@ -496,6 +593,30 @@ void fs__stat(uv_fs_t* req, const wchar_t* path) { req->ptr = NULL; + if (uv__is_root(path)) { + /* We can't stat root directories like c:\. _wstati64 can't either, but */ + /* it will make up something reasonable. */ + DWORD drive_type = GetDriveTypeW(path); + if (drive_type == DRIVE_UNKNOWN || drive_type == DRIVE_NO_ROOT_DIR) { + req->last_error = ERROR_PATH_NOT_FOUND; + req->errorno = UV_ENOENT; + req->result = -1; + return; + } + + memset(&req->stat, 0, sizeof req->stat); + + req->stat.st_nlink = 1; + req->stat.st_mode = ((_S_IREAD|_S_IWRITE) + ((_S_IREAD|_S_IWRITE) >> 3) + + ((_S_IREAD|_S_IWRITE) >> 6)) | S_IFDIR; + + req->last_error = ERROR_SUCCESS; + req->errorno = UV_OK; + req->result = 0; + req->ptr = &req->stat; + return; + } + file = FindFirstFileExW(path, FindExInfoStandard, &ent, FindExSearchNameMatch, NULL, 0); @@ -516,7 +637,7 @@ void fs__stat(uv_fs_t* req, const wchar_t* path) { if (result != -1) { req->ptr = &req->stat; } - + SET_REQ_RESULT(req, result); } diff --git a/deps/uv/test/test-fs.c b/deps/uv/test/test-fs.c index b8e0ee6567..1601ac442f 100644 --- a/deps/uv/test/test-fs.c +++ b/deps/uv/test/test-fs.c @@ -453,10 +453,10 @@ TEST_IMPL(fs_file_noent) { TEST_IMPL(fs_file_nametoolong) { uv_fs_t req; int r; + char name[TOO_LONG_NAME_LENGTH + 1]; loop = uv_default_loop(); - char name[TOO_LONG_NAME_LENGTH + 1]; memset(name, 'a', TOO_LONG_NAME_LENGTH); name[TOO_LONG_NAME_LENGTH] = 0; @@ -1288,6 +1288,22 @@ TEST_IMPL(fs_utime) { } +#ifdef _WIN32 +TEST_IMPL(fs_stat_root) { + int r; + uv_loop_t* loop = uv_default_loop(); + + r = uv_fs_stat(loop, &stat_req, "c:\\", NULL); + ASSERT(r == 0); + + r = uv_fs_stat(loop, &stat_req, "\\\\?\\C:\\", NULL); + ASSERT(r == 0); + + return 0; +} +#endif + + TEST_IMPL(fs_futime) { utime_check_t checkme; const char* path = "test_file"; diff --git a/deps/uv/test/test-list.h b/deps/uv/test/test-list.h index fa9390f9c7..acc5cea938 100644 --- a/deps/uv/test/test-list.h +++ b/deps/uv/test/test-list.h @@ -134,6 +134,7 @@ TEST_DECLARE (argument_escaping) TEST_DECLARE (environment_creation) TEST_DECLARE (listen_with_simultaneous_accepts) TEST_DECLARE (listen_no_simultaneous_accepts) +TEST_DECLARE (fs_stat_root) #endif HELPER_DECLARE (tcp4_echo_server) HELPER_DECLARE (tcp6_echo_server) @@ -267,6 +268,7 @@ TASK_LIST_START TEST_ENTRY (environment_creation) TEST_ENTRY (listen_with_simultaneous_accepts) TEST_ENTRY (listen_no_simultaneous_accepts) + TEST_ENTRY (fs_stat_root) #endif TEST_ENTRY (fs_file_noent)