diff --git a/CMakeLists.txt b/CMakeLists.txt index 47edda4cf..dca72dbf5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -707,7 +707,7 @@ if(LIBUV_BUILD_TESTS) test/test-uname.c test/test-walk-handles.c test/test-watcher-cross-stop.c - test/test-win32-namespaces.c) + test/test-chdir-win32.c) add_executable(uv_run_tests ${uv_test_sources} uv_win_longpath.manifest) target_compile_definitions(uv_run_tests diff --git a/Makefile.am b/Makefile.am index 321584f7b..389ce2f13 100644 --- a/Makefile.am +++ b/Makefile.am @@ -332,7 +332,7 @@ test_run_tests_SOURCES = test/blackhole-server.c \ test/test-uname.c \ test/test-walk-handles.c \ test/test-watcher-cross-stop.c \ - test/test-win32-namespaces.c + test/test-chdir-win32.c test_run_tests_LDADD = libuv.la if WINNT diff --git a/test/test-chdir-win32.c b/test/test-chdir-win32.c new file mode 100644 index 000000000..fc38dd2e0 --- /dev/null +++ b/test/test-chdir-win32.c @@ -0,0 +1,355 @@ +#ifdef _WIN32 + +#include "uv.h" +#include "task.h" +#include +#include +#include + +static char get_current_drive(void) { + char cwd[MAX_PATH]; + size_t cwd_size = sizeof(cwd); + if (uv_cwd(cwd, &cwd_size) != 0) + return 0; + if (cwd[0] >= 'A' && cwd[0] <= 'Z') + return cwd[0]; + if (cwd[0] >= 'a' && cwd[0] <= 'z') + return cwd[0] - 'a' + 'A'; + return 0; +} + +static int get_drive_env(char drive, char* buf, size_t buflen) { + char env_name[4]; + env_name[0] = '='; + env_name[1] = drive; + env_name[2] = ':'; + env_name[3] = '\0'; + + DWORD result = GetEnvironmentVariableA(env_name, buf, (DWORD)buflen); + return result > 0 && result < buflen; +} + +TEST_IMPL(chdir_sets_drive_env_for_normal_path) { + char original_cwd[1024]; + size_t size = sizeof(original_cwd); + char env_value[1024]; + char drive; + int r; + + r = uv_cwd(original_cwd, &size); + ASSERT_EQ(r, 0); + + drive = get_current_drive(); + if (drive == 0) + return 0; + + r = uv_chdir(original_cwd); + ASSERT_EQ(r, 0); + + ASSERT_EQ(get_drive_env(drive, env_value, sizeof(env_value)), 1); + ASSERT(strstr(env_value, original_cwd) != NULL); + + return 0; +} + +TEST_IMPL(chdir_normalizes_drive_letter_case) { + char original_cwd[1024]; + size_t size = sizeof(original_cwd); + char lowercase_path[1024]; + char env_value[1024]; + char drive; + int r; + + r = uv_cwd(original_cwd, &size); + ASSERT_EQ(r, 0); + + drive = get_current_drive(); + if (drive == 0) + return 0; + + strcpy(lowercase_path, original_cwd); + if (lowercase_path[0] >= 'A' && lowercase_path[0] <= 'Z') + lowercase_path[0] = lowercase_path[0] + 32; + + r = uv_chdir(lowercase_path); + ASSERT_EQ(r, 0); + + ASSERT_EQ(get_drive_env(drive, env_value, sizeof(env_value)), 1); + + return 0; +} + +TEST_IMPL(chdir_updates_env_on_path_change) { + char original_cwd[1024]; + size_t size = sizeof(original_cwd); + char parent_dir[1024]; + char env_value_1[1024]; + char env_value_2[1024]; + char drive; + char* last_slash; + int r; + + r = uv_cwd(original_cwd, &size); + ASSERT_EQ(r, 0); + + drive = get_current_drive(); + if (drive == 0) return 0; + + strcpy(parent_dir, original_cwd); + last_slash = strrchr(parent_dir, '\\'); + if (last_slash == NULL || last_slash == parent_dir + 2) + return 0; + + *last_slash = '\0'; + + r = uv_chdir(parent_dir); + ASSERT_EQ(r, 0); + ASSERT_EQ(get_drive_env(drive, env_value_1, sizeof(env_value_1)), 1); + + r = uv_chdir(original_cwd); + ASSERT_EQ(r, 0); + ASSERT_EQ(get_drive_env(drive, env_value_2, sizeof(env_value_2)), 1); + + ASSERT_STR_NE(env_value_1, env_value_2); + + return 0; +} + +TEST_IMPL(chdir_preserves_env_for_regular_unc) { + char original_cwd[1024]; + size_t size = sizeof(original_cwd); + char unc_path[256]; + char old_env[1024]; + char new_env[1024]; + char drive; + int had_env; + int has_env; + int r; + + r = uv_cwd(original_cwd, &size); + ASSERT_EQ(r, 0); + + drive = get_current_drive(); + if (drive == 0) + return 0; + + had_env = get_drive_env(drive, old_env, sizeof(old_env)); + + snprintf(unc_path, sizeof(unc_path), "\\\\localhost\\%c$", drive); + r = uv_chdir(unc_path); + + if (r != 0) + return 0; + + has_env = get_drive_env(drive, new_env, sizeof(new_env)); + + if (had_env && has_env) { + ASSERT_STR_EQ(old_env, new_env); + } else if (had_env && !has_env) { + ASSERT(0 && "Drive env should not be deleted"); + } else if (!had_env && has_env) { + ASSERT(0 && "Drive env should not be created"); + } + + uv_chdir(original_cwd); + return 0; +} + +TEST_IMPL(chdir_preserves_env_for_volume_guid) { + char original_cwd[1024]; + size_t size = sizeof(original_cwd); + char old_env[1024]; + char new_env[1024]; + char drive; + int had_env; + int has_env; + int r; + + r = uv_cwd(original_cwd, &size); + ASSERT_EQ(r, 0); + + drive = get_current_drive(); + if (drive == 0) + return 0; + + had_env = get_drive_env(drive, old_env, sizeof(old_env)); + + r = uv_chdir("\\\\?\\Volume{12345678-1234-1234-1234-123456789012}\\"); + + if (r != 0) + return 0; + + has_env = get_drive_env(drive, new_env, sizeof(new_env)); + + if (had_env && has_env) { + ASSERT_STR_EQ(old_env, new_env); + } else if (had_env && !has_env) { + ASSERT(0 && "Drive env should not be deleted for volume GUID"); + } else if (!had_env && has_env) { + ASSERT(0 && "Drive env should not be created for volume GUID"); + } + + uv_chdir(original_cwd); + + return 0; +} + +TEST_IMPL(chdir_preserves_env_for_globalroot) { + char original_cwd[1024]; + size_t size = sizeof(original_cwd); + char old_env[1024]; + char new_env[1024]; + char drive; + int had_env; + int has_env; + int r; + + r = uv_cwd(original_cwd, &size); + ASSERT_EQ(r, 0); + + drive = get_current_drive(); + if (drive == 0) return 0; + + had_env = get_drive_env(drive, old_env, sizeof(old_env)); + + r = uv_chdir("\\\\?\\GLOBALROOT\\Device\\HarddiskVolume1"); + + if (r != 0) + return 0; + + has_env = get_drive_env(drive, new_env, sizeof(new_env)); + + if (had_env && has_env) { + ASSERT_STR_EQ(old_env, new_env); + } else if (had_env && !has_env) { + ASSERT(0 && "Drive env should not be deleted for GLOBALROOT"); + } else if (!had_env && has_env) { + ASSERT(0 && "Drive env should not be created for GLOBALROOT"); + } + + uv_chdir(original_cwd); + + return 0; +} + +TEST_IMPL(chdir_invalid_input_returns_error) { + int r; + + r = uv_chdir(NULL); + ASSERT_EQ(r, UV_EINVAL); + + r = uv_chdir(""); + ASSERT_EQ(r, UV_EINVAL); + + return 0; +} + +TEST_IMPL(chdir_nonexistent_path_returns_error) { + char drive; + char nonexistent[256]; + int r; + + drive = get_current_drive(); + if (drive == 0) return 0; + + snprintf(nonexistent, sizeof(nonexistent), "%c:\\NonExistentDirectory_xyz123456789", drive); + + r = uv_chdir(nonexistent); + + ASSERT(r != 0); + ASSERT(r == UV_ENOENT || r == UV_EACCES); + + return 0; +} + +TEST_IMPL(chdir_win32_namespace_updates_env) { + char original_cwd[1024]; + size_t size = sizeof(original_cwd); + char win32_path[2048]; + char env_value[1024]; + char drive; + int r; + + r = uv_cwd(original_cwd, &size); + ASSERT_EQ(r, 0); + + drive = get_current_drive(); + if (drive == 0) return 0; + + if (strlen(original_cwd) > sizeof(win32_path) - 5) { + return 0; + } + + snprintf(win32_path, sizeof(win32_path), "\\\\?\\%s", original_cwd); + + r = uv_chdir(win32_path); + ASSERT_EQ(r, 0); + + ASSERT_EQ(get_drive_env(drive, env_value, sizeof(env_value)), 1); + + return 0; +} + +TEST_IMPL(chdir_device_namespace_with_drive) { + char original_cwd[1024]; + size_t size = sizeof(original_cwd); + char device_path[256]; + char old_env[1024]; + char drive; + int r; + + r = uv_cwd(original_cwd, &size); + ASSERT_EQ(r, 0); + + drive = get_current_drive(); + if (drive == 0) return 0; + + get_drive_env(drive, old_env, sizeof(old_env)); + + snprintf(device_path, sizeof(device_path), "\\\\.\\%c:", drive); + r = uv_chdir(device_path); + + if (r == 0) { + char new_env[1024]; + ASSERT_EQ(get_drive_env(drive, new_env, sizeof(new_env)), 1); + } + + uv_chdir(original_cwd); + return 0; +} + +TEST_IMPL(chdir_nt_namespace_with_drive) { + char original_cwd[1024]; + size_t size = sizeof(original_cwd); + char nt_path[1024]; + char env_value[1024]; + char drive; + char* path_after_drive; + int r; + + r = uv_cwd(original_cwd, &size); + ASSERT_EQ(r, 0); + + drive = get_current_drive(); + if (drive == 0) return 0; + + path_after_drive = strchr(original_cwd + 2, '\\'); + if (path_after_drive == NULL) { + return 0; + } + + snprintf(nt_path, sizeof(nt_path), "\\??\\%c:%s", drive, path_after_drive); + + r = uv_chdir(nt_path); + + if (r == 0) { + ASSERT_EQ(get_drive_env(drive, env_value, sizeof(env_value)), 1); + } else { + ASSERT(r == UV_ENOENT || r == UV_EINVAL || r == UV_EACCES); + } + + uv_chdir(original_cwd); + return 0; +} + +#endif diff --git a/test/test-list.h b/test/test-list.h index b8fe06d45..b425c9a59 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -594,14 +594,17 @@ TEST_DECLARE (metrics_idle_time_thread) TEST_DECLARE (metrics_idle_time_zero) #ifdef _WIN32 -TEST_DECLARE(chdir_win32_namespace) -TEST_DECLARE(chdir_unc_paths) -TEST_DECLARE(chdir_volume_guid_path) -TEST_DECLARE(chdir_globalroot_path) -TEST_DECLARE(chdir_nt_namespace) -TEST_DECLARE(chdir_case_insensitive) -TEST_DECLARE(chdir_drive_env_variable_update) -TEST_DECLARE(chdir_device_paths) +TEST_DECLARE(chdir_sets_drive_env_for_normal_path) +TEST_DECLARE(chdir_normalizes_drive_letter_case) +TEST_DECLARE(chdir_updates_env_on_path_change) +TEST_DECLARE(chdir_preserves_env_for_regular_unc) +TEST_DECLARE(chdir_preserves_env_for_volume_guid) +TEST_DECLARE(chdir_preserves_env_for_globalroot) +TEST_DECLARE(chdir_invalid_input_returns_error) +TEST_DECLARE(chdir_nonexistent_path_returns_error) +TEST_DECLARE(chdir_win32_namespace_updates_env) +TEST_DECLARE(chdir_device_namespace_with_drive) +TEST_DECLARE(chdir_nt_namespace_with_drive) #endif TASK_LIST_START @@ -1277,14 +1280,17 @@ TASK_LIST_START TEST_ENTRY (metrics_idle_time_zero) #ifdef _WIN32 - TEST_ENTRY(chdir_win32_namespace) - TEST_ENTRY(chdir_unc_paths) - TEST_ENTRY(chdir_volume_guid_path) - TEST_ENTRY(chdir_globalroot_path) - TEST_ENTRY(chdir_nt_namespace) - TEST_ENTRY(chdir_case_insensitive) - TEST_ENTRY(chdir_drive_env_variable_update) - TEST_ENTRY(chdir_device_paths) + TEST_ENTRY(chdir_sets_drive_env_for_normal_path) + TEST_ENTRY(chdir_normalizes_drive_letter_case) + TEST_ENTRY(chdir_updates_env_on_path_change) + TEST_ENTRY(chdir_preserves_env_for_regular_unc) + TEST_ENTRY(chdir_preserves_env_for_volume_guid) + TEST_ENTRY(chdir_preserves_env_for_globalroot) + TEST_ENTRY(chdir_invalid_input_returns_error) + TEST_ENTRY(chdir_nonexistent_path_returns_error) + TEST_ENTRY(chdir_win32_namespace_updates_env) + TEST_ENTRY(chdir_device_namespace_with_drive) + TEST_ENTRY(chdir_nt_namespace_with_drive) #endif #if 0 diff --git a/test/test-win32-namespaces.c b/test/test-win32-namespaces.c deleted file mode 100644 index fd1ea799a..000000000 --- a/test/test-win32-namespaces.c +++ /dev/null @@ -1,336 +0,0 @@ -#ifdef _WIN32 - -#include "uv.h" -#include "task.h" -#include -#include -#include - -static char get_current_drive(void) { - char cwd[MAX_PATH]; - size_t cwd_size = sizeof(cwd); - if (uv_cwd(cwd, &cwd_size) != 0) - return 0; - if (cwd[0] >= 'A' && cwd[0] <= 'Z') - return cwd[0]; - if (cwd[0] >= 'a' && cwd[0] <= 'z') - return cwd[0] - 'a' + 'A'; - return 0; -} - - -static int path_exists(const char* path) { - WCHAR wpath[32768]; - DWORD attrs; - int len; - - len = MultiByteToWideChar(CP_UTF8, 0, path, -1, wpath, 32768); - if (len == 0) - return 0; - - attrs = GetFileAttributesW(wpath); - - return attrs != INVALID_FILE_ATTRIBUTES && - (attrs & FILE_ATTRIBUTE_DIRECTORY); -} - - -static int get_drive_env(char drive, char* buf, size_t buflen) { - char env_name[4]; - env_name[0] = '='; - env_name[1] = drive; - env_name[2] = ':'; - env_name[3] = '\0'; - - DWORD result = GetEnvironmentVariableA(env_name, buf, (DWORD)buflen); - return result > 0 && result < buflen; -} - - -TEST_IMPL(chdir_win32_namespace) { - char original_cwd[1024]; - size_t original_cwd_size = sizeof(original_cwd); - char test_path[1024]; - char expected_path[1024]; - char env_value[1024]; - char drive; - int r; - - r = uv_cwd(original_cwd, &original_cwd_size); - ASSERT_EQ(r, 0); - - drive = get_current_drive(); - ASSERT_NE(drive, 0); - - snprintf(test_path, sizeof(test_path), "\\\\?\\%c:\\Windows", drive); - if (path_exists(test_path)) { - r = uv_chdir(test_path); - ASSERT_EQ(r, 0); - - ASSERT_EQ(get_drive_env(drive, env_value, sizeof(env_value)), 1); - - snprintf(expected_path, sizeof(expected_path), "\\\\?\\%c:\\Windows", drive); - ASSERT(strstr(env_value, "Windows") != NULL); - } - - snprintf(test_path, sizeof(test_path), "\\\\?\\%c:\\Windows", drive + 32); - if (path_exists(test_path)) { - r = uv_chdir(test_path); - ASSERT_EQ(r, 0); - - ASSERT_EQ(get_drive_env(drive, env_value, sizeof(env_value)), 1); - } - - snprintf(test_path, sizeof(test_path), "\\\\.\\%c:", drive); - r = uv_chdir(test_path); - if (r == 0) { - ASSERT_EQ(get_drive_env(drive, env_value, sizeof(env_value)), 1); - } - - r = uv_chdir(original_cwd); - ASSERT_EQ(r, 0); - - return 0; -} - - -TEST_IMPL(chdir_unc_paths) { - char original_cwd[1024]; - size_t original_cwd_size = sizeof(original_cwd); - char test_path[1024]; - char drive; - int r; - - r = uv_cwd(original_cwd, &original_cwd_size); - ASSERT_EQ(r, 0); - - drive = get_current_drive(); - ASSERT_NE(drive, 0); - - char old_env[1024]; - int had_env = get_drive_env(drive, old_env, sizeof(old_env)); - - snprintf(test_path, sizeof(test_path), "\\\\?\\UNC\\localhost\\%c$", drive); - r = uv_chdir(test_path); - - if (r == 0) { - char new_env[1024]; - int has_env = get_drive_env(drive, new_env, sizeof(new_env)); - - if (had_env && has_env) { - ASSERT_STR_EQ(old_env, new_env); - } - - char cwd[1024]; - size_t cwd_size = sizeof(cwd); - r = uv_cwd(cwd, &cwd_size); - ASSERT_EQ(r, 0); - - ASSERT(strncmp(cwd, "\\\\", 2) == 0 || strncmp(cwd, "//", 2) == 0); - } else { - ASSERT(r == UV_EACCES || r == UV_ENOENT || r == UV_EINVAL); - } - - snprintf(test_path, sizeof(test_path), "\\\\localhost\\%c$", drive); - r = uv_chdir(test_path); - - r = uv_chdir(original_cwd); - ASSERT_EQ(r, 0); - - return 0; -} - - -TEST_IMPL(chdir_volume_guid_path) { - char drive; - char old_env[1024]; - int had_env; - - drive = get_current_drive(); - ASSERT_NE(drive, 0); - - had_env = get_drive_env(drive, old_env, sizeof(old_env)); - - uv_chdir("\\\\?\\Volume{12345678-1234-1234-1234-123456789012}\\"); - - char new_env[1024]; - int has_env = get_drive_env(drive, new_env, sizeof(new_env)); - - if (had_env && has_env) { - ASSERT_STR_EQ(old_env, new_env); - } else if (had_env && !has_env) { - ASSERT(0 && "Drive env variable was deleted"); - } else if (!had_env && has_env) { - ASSERT(0 && "Drive env variable was created"); - } - - return 0; -} - - -TEST_IMPL(chdir_globalroot_path) { - char drive; - char old_env[1024]; - int had_env; - - drive = get_current_drive(); - ASSERT_NE(drive, 0); - - had_env = get_drive_env(drive, old_env, sizeof(old_env)); - - uv_chdir("\\\\?\\GLOBALROOT\\Device\\HarddiskVolume1"); - - char new_env[1024]; - int has_env = get_drive_env(drive, new_env, sizeof(new_env)); - - if (had_env && has_env) { - ASSERT_STR_EQ(old_env, new_env); - } else if (had_env && !has_env) { - ASSERT(0 && "Drive env variable was deleted"); - } else if (!had_env && has_env) { - ASSERT(0 && "Drive env variable was created"); - } - - return 0; -} - - -TEST_IMPL(chdir_nt_namespace) { - char original_cwd[1024]; - size_t original_cwd_size = sizeof(original_cwd); - char test_path[256]; - char env_value[1024]; - char drive; - int r; - - r = uv_cwd(original_cwd, &original_cwd_size); - ASSERT_EQ(r, 0); - - drive = get_current_drive(); - ASSERT_NE(drive, 0); - - snprintf(test_path, sizeof(test_path), "\\??\\%c:\\Windows", drive); - r = uv_chdir(test_path); - - if (r == 0) { - ASSERT_EQ(get_drive_env(drive, env_value, sizeof(env_value)), 1); - ASSERT(strstr(env_value, "Windows") != NULL); - } else { - ASSERT(r == UV_ENOENT || r == UV_EINVAL || r == UV_EACCES); - } - - r = uv_chdir(original_cwd); - ASSERT_EQ(r, 0); - - return 0; -} - - -TEST_IMPL(chdir_case_insensitive) { - char original_cwd[1024]; - size_t original_cwd_size = sizeof(original_cwd); - char test_path_upper[256]; - char test_path_lower[256]; - char test_path_mixed[256]; - char env_value[1024]; - char drive; - int r; - - r = uv_cwd(original_cwd, &original_cwd_size); - ASSERT_EQ(r, 0); - - drive = get_current_drive(); - ASSERT_NE(drive, 0); - - snprintf(test_path_upper, sizeof(test_path_upper), "%c:\\Windows", drive); - if (path_exists(test_path_upper)) { - r = uv_chdir(test_path_upper); - ASSERT_EQ(r, 0); - - ASSERT_EQ(get_drive_env(drive, env_value, sizeof(env_value)), 1); - } - - snprintf(test_path_lower, sizeof(test_path_lower), "%c:\\Windows", drive + 32); - if (path_exists(test_path_lower)) { - r = uv_chdir(test_path_lower); - ASSERT_EQ(r, 0); - - ASSERT_EQ(get_drive_env(drive, env_value, sizeof(env_value)), 1); - } - - snprintf(test_path_mixed, sizeof(test_path_mixed), "\\\\?\\%c:\\Windows", - drive + 32); - if (path_exists(test_path_mixed)) { - r = uv_chdir(test_path_mixed); - ASSERT_EQ(r, 0); - - ASSERT_EQ(get_drive_env(drive, env_value, sizeof(env_value)), 1); - } - - r = uv_chdir(original_cwd); - ASSERT_EQ(r, 0); - - return 0; -} - - -TEST_IMPL(chdir_drive_env_variable_update) { - char original_cwd[1024]; - size_t original_cwd_size = sizeof(original_cwd); - char test_path_1[256]; - char test_path_2[256]; - char env_value_1[1024]; - char env_value_2[1024]; - char drive; - int r; - - r = uv_cwd(original_cwd, &original_cwd_size); - ASSERT_EQ(r, 0); - - drive = get_current_drive(); - ASSERT_NE(drive, 0); - - snprintf(test_path_1, sizeof(test_path_1), "%c:\\Windows", drive); - if (path_exists(test_path_1)) { - r = uv_chdir(test_path_1); - ASSERT_EQ(r, 0); - - ASSERT_EQ(get_drive_env(drive, env_value_1, sizeof(env_value_1)), 1); - ASSERT(strstr(env_value_1, "Windows") != NULL); - - snprintf(test_path_2, sizeof(test_path_2), "%c:\\Windows\\System32", drive); - if (path_exists(test_path_2)) { - r = uv_chdir(test_path_2); - ASSERT_EQ(r, 0); - - ASSERT_EQ(get_drive_env(drive, env_value_2, sizeof(env_value_2)), 1); - ASSERT(strstr(env_value_2, "System32") != NULL); - - ASSERT_STR_NE(env_value_1, env_value_2); - } - } - - r = uv_chdir(original_cwd); - ASSERT_EQ(r, 0); - - return 0; -} - - -TEST_IMPL(chdir_device_paths) { - char drive = get_current_drive(); - char old_env[1024]; - int had_env = get_drive_env(drive, old_env, sizeof(old_env)); - - uv_chdir("\\\\.\\COM1"); - - char new_env[1024]; - int has_env = get_drive_env(drive, new_env, sizeof(new_env)); - - if (had_env && has_env) { - ASSERT_STR_EQ(old_env, new_env); - } - return 0; -} - -#endif