From 267e75dda2b6b7ecf9501764a42416122a4f0ff5 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Thu, 2 Feb 2012 16:23:57 +0100 Subject: [PATCH] Windows: better stat implementation --- src/win/fs.c | 186 ++++++++----------------------------------------- test/test-fs.c | 13 ++++ 2 files changed, 41 insertions(+), 158 deletions(-) diff --git a/src/win/fs.c b/src/win/fs.c index 5248ac2a..12ad514c 100644 --- a/src/win/fs.c +++ b/src/win/fs.c @@ -489,191 +489,61 @@ 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 == 1 && 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; +static void fs__stat(uv_fs_t* req, const wchar_t* path) { + HANDLE handle; int result; + BY_HANDLE_FILE_INFORMATION info; 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); - - if (file == INVALID_HANDLE_VALUE) { + handle = CreateFileW(path, + FILE_READ_ATTRIBUTES, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, + NULL); + if (handle == INVALID_HANDLE_VALUE) { SET_REQ_RESULT_WIN32_ERROR(req, GetLastError()); return; } - FindClose(file); - - if (ent.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT && - ent.dwReserved0 == IO_REPARSE_TAG_SYMLINK) { - fs__open(req, path, _O_RDONLY, 0); - if (req->result != -1) { - result = _fstati64(req->result, &req->stat); - _close(req->result); - - if (result != -1) { - req->ptr = &req->stat; - } - - SET_REQ_RESULT(req, result); - } - + if (!GetFileInformationByHandle(handle, &info)) { + SET_REQ_RESULT_WIN32_ERROR(req, GetLastError()); + CloseHandle(handle); return; } - req->stat.st_ino = 0; - req->stat.st_uid = 0; - req->stat.st_gid = 0; - req->stat.st_mode = 0; - req->stat.st_rdev = 0; - req->stat.st_dev = 0; - req->stat.st_nlink = 1; + memset(&req->stat, 0, sizeof req->stat); - if (ent.dwFileAttributes & FILE_ATTRIBUTE_READONLY ) { + /* TODO: set st_dev and st_ino? */ + + if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) { req->stat.st_mode |= (_S_IREAD + (_S_IREAD >> 3) + (_S_IREAD >> 6)); } else { req->stat.st_mode |= ((_S_IREAD|_S_IWRITE) + ((_S_IREAD|_S_IWRITE) >> 3) + ((_S_IREAD|_S_IWRITE) >> 6)); } - if (ent.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { req->stat.st_mode |= _S_IFDIR; } else { req->stat.st_mode |= _S_IFREG; } - uv_filetime_to_time_t(&ent.ftLastWriteTime, &(req->stat.st_mtime)); - uv_filetime_to_time_t(&ent.ftLastAccessTime, &(req->stat.st_atime)); - uv_filetime_to_time_t(&ent.ftCreationTime, &(req->stat.st_ctime)); + uv_filetime_to_time_t(&info.ftLastWriteTime, &(req->stat.st_mtime)); + uv_filetime_to_time_t(&info.ftLastAccessTime, &(req->stat.st_atime)); + uv_filetime_to_time_t(&info.ftCreationTime, &(req->stat.st_ctime)); - req->stat.st_size = ((int64_t)ent.nFileSizeHigh << 32) + - (int64_t)ent.nFileSizeLow; + req->stat.st_size = ((int64_t) info.nFileSizeHigh << 32) + + (int64_t) info.nFileSizeLow; + + req->stat.st_nlink = info.nNumberOfLinks; req->ptr = &req->stat; req->result = 0; + + CloseHandle(handle); } diff --git a/test/test-fs.c b/test/test-fs.c index dbeb2a19..109ae014 100644 --- a/test/test-fs.c +++ b/test/test-fs.c @@ -1309,6 +1309,19 @@ TEST_IMPL(fs_stat_root) { r = uv_fs_stat(loop, &stat_req, "\\", NULL); ASSERT(r == 0); + r = uv_fs_stat(loop, &stat_req, "..\\..\\..\\..\\..\\..\\..", NULL); + ASSERT(r == 0); + + r = uv_fs_stat(loop, &stat_req, "..", NULL); + ASSERT(r == 0); + + r = uv_fs_stat(loop, &stat_req, "..\\", NULL); + ASSERT(r == 0); + + /* stats the current directory on c: */ + r = uv_fs_stat(loop, &stat_req, "c:", NULL); + ASSERT(r == 0); + r = uv_fs_stat(loop, &stat_req, "c:\\", NULL); ASSERT(r == 0);