From 62d033359d2889d85576765999cd5bf0b3665eb2 Mon Sep 17 00:00:00 2001 From: Kyle Edwards Date: Wed, 29 Nov 2023 13:05:43 -0500 Subject: [PATCH] win/spawn: run executables with no file extension --- .github/workflows/CI-win.yml | 2 +- CMakeLists.txt | 12 +++++++ Makefile.am | 6 ++++ src/win/process.c | 37 +++++++--------------- test/test-list.h | 4 +++ test/test-spawn.c | 61 ++++++++++++++++++++++++++++++++++++ 6 files changed, 96 insertions(+), 26 deletions(-) diff --git a/.github/workflows/CI-win.yml b/.github/workflows/CI-win.yml index e66b107d4cb..c013fc219ef 100644 --- a/.github/workflows/CI-win.yml +++ b/.github/workflows/CI-win.yml @@ -91,7 +91,7 @@ jobs: cmake --install build --prefix "`pwd`/build/usr" mkdir -p build/usr/test build/usr/bin cp -av test/fixtures build/usr/test - cp -av build/uv_run_tests_a.exe build/uv_run_tests.exe \ + cp -av build/uv_run_tests_a.exe build/uv_run_tests.exe build/uv_run_tests_a_no_ext build/uv_run_tests_no_ext \ `${{ matrix.config.arch }}-w64-mingw32-gcc -print-file-name=libgcc_s_${{ matrix.config.libgcc }}-1.dll` \ `${{ matrix.config.arch }}-w64-mingw32-gcc -print-file-name=libwinpthread-1.dll` \ `${{ matrix.config.arch }}-w64-mingw32-gcc -print-file-name=libatomic-1.dll` \ diff --git a/CMakeLists.txt b/CMakeLists.txt index f7f42773bf6..e3da51ea4d7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -703,6 +703,12 @@ if(LIBUV_BUILD_TESTS) set_tests_properties(uv_test PROPERTIES ENVIRONMENT "LIBPATH=${CMAKE_BINARY_DIR}:$ENV{LIBPATH}") endif() + if(WIN32) + add_custom_command(TARGET uv_run_tests POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy + "$" + "$/uv_run_tests_no_ext") + endif() add_executable(uv_run_tests_a ${uv_test_sources} uv_win_longpath.manifest) target_compile_definitions(uv_run_tests_a PRIVATE ${uv_defines}) target_compile_options(uv_run_tests_a PRIVATE ${uv_cflags}) @@ -719,6 +725,12 @@ if(LIBUV_BUILD_TESTS) set_target_properties(uv_run_tests PROPERTIES LINKER_LANGUAGE CXX) set_target_properties(uv_run_tests_a PROPERTIES LINKER_LANGUAGE CXX) endif() + if(WIN32) + add_custom_command(TARGET uv_run_tests_a POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy + "$" + "$/uv_run_tests_a_no_ext") + endif() endif() # Now for some gibbering horrors from beyond the stars... diff --git a/Makefile.am b/Makefile.am index ff6f1b8a6d2..a14228da3bf 100644 --- a/Makefile.am +++ b/Makefile.am @@ -136,6 +136,12 @@ TESTS = test/run-tests check_PROGRAMS = test/run-tests test_run_tests_CFLAGS = $(AM_CFLAGS) +if WINNT +check-am: test/run-tests_no_ext +test/run-tests_no_ext: test/run-tests$(EXEEXT) + cp test/run-tests$(EXEEXT) test/run-tests_no_ext +endif + if SUNOS # Can't be turned into a CC_CHECK_CFLAGS in configure.ac, it makes compilers # on other platforms complain that the argument is unused during compilation. diff --git a/src/win/process.c b/src/win/process.c index 117054d49e0..132ca2cbc30 100644 --- a/src/win/process.c +++ b/src/win/process.c @@ -249,19 +249,16 @@ static WCHAR* path_search_walk_ext(const WCHAR *dir, const WCHAR *name, size_t name_len, WCHAR *cwd, - size_t cwd_len, - int name_has_ext) { + size_t cwd_len) { WCHAR* result; - /* If the name itself has a nonempty extension, try this extension first */ - if (name_has_ext) { - result = search_path_join_test(dir, dir_len, - name, name_len, - L"", 0, - cwd, cwd_len); - if (result != NULL) { - return result; - } + /* Try the name itself first */ + result = search_path_join_test(dir, dir_len, + name, name_len, + L"", 0, + cwd, cwd_len); + if (result != NULL) { + return result; } /* Try .com extension */ @@ -304,8 +301,7 @@ static WCHAR* path_search_walk_ext(const WCHAR *dir, * - If there's really only a filename, check the current directory for file, * then search all path directories. * - * - If filename specified has *any* extension, search for the file with the - * specified extension first. + * - Search for the file exactly as specified first. * * - If the literal filename is not found in a directory, try *appending* * (not replacing) .com first and then .exe. @@ -335,10 +331,8 @@ static WCHAR* search_path(const WCHAR *file, int file_has_dir; WCHAR* result = NULL; WCHAR *file_name_start; - WCHAR *dot; const WCHAR *dir_start, *dir_end, *dir_path; size_t dir_len; - int name_has_ext; size_t file_len = wcslen(file); size_t cwd_len = wcslen(cwd); @@ -362,17 +356,12 @@ static WCHAR* search_path(const WCHAR *file, file_has_dir = file_name_start != file; - /* Check if the filename includes an extension */ - dot = wcschr(file_name_start, L'.'); - name_has_ext = (dot != NULL && dot[1] != L'\0'); - if (file_has_dir) { /* The file has a path inside, don't use path */ result = path_search_walk_ext( file, file_name_start - file, file_name_start, file_len - (file_name_start - file), - cwd, cwd_len, - name_has_ext); + cwd, cwd_len); } else { dir_end = path; @@ -381,8 +370,7 @@ static WCHAR* search_path(const WCHAR *file, /* The file is really only a name; look in cwd first, then scan path */ result = path_search_walk_ext(L"", 0, file, file_len, - cwd, cwd_len, - name_has_ext); + cwd, cwd_len); } while (result == NULL) { @@ -431,8 +419,7 @@ static WCHAR* search_path(const WCHAR *file, result = path_search_walk_ext(dir_path, dir_len, file, file_len, - cwd, cwd_len, - name_has_ext); + cwd, cwd_len); } } diff --git a/test/test-list.h b/test/test-list.h index f042bc29f7c..78e593a98c6 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -506,6 +506,8 @@ TEST_DECLARE (listen_no_simultaneous_accepts) TEST_DECLARE (fs_stat_root) TEST_DECLARE (spawn_with_an_odd_path) TEST_DECLARE (spawn_no_path) +TEST_DECLARE (spawn_no_ext) +TEST_DECLARE (spawn_path_no_ext) TEST_DECLARE (ipc_listen_after_bind_twice) TEST_DECLARE (win32_signum_number) #else @@ -1025,6 +1027,8 @@ TASK_LIST_START TEST_ENTRY (fs_stat_root) TEST_ENTRY (spawn_with_an_odd_path) TEST_ENTRY (spawn_no_path) + TEST_ENTRY (spawn_no_ext) + TEST_ENTRY (spawn_path_no_ext) TEST_ENTRY (ipc_listen_after_bind_twice) TEST_ENTRY (win32_signum_number) #else diff --git a/test/test-spawn.c b/test/test-spawn.c index bbb7cb498ba..0be06548fb6 100644 --- a/test/test-spawn.c +++ b/test/test-spawn.c @@ -1394,6 +1394,67 @@ TEST_IMPL(spawn_no_path) { MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } + + +TEST_IMPL(spawn_no_ext) { + char new_exepath[1024]; + + init_process_options("spawn_helper1", exit_cb); + memcpy(new_exepath, exepath, exepath_size - (sizeof(".exe") - sizeof(char))); + strcpy(new_exepath + exepath_size - (sizeof(".exe") / sizeof(char) - 1), "_no_ext"); + options.file = options.args[0] = new_exepath; + + ASSERT_OK(uv_spawn(uv_default_loop(), &process, &options)); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + + ASSERT_EQ(1, exit_cb_called); + ASSERT_EQ(1, close_cb_called); + + MAKE_VALGRIND_HAPPY(uv_default_loop()); + return 0; +} + + +TEST_IMPL(spawn_path_no_ext) { + int r; + int len; + int file_len; + char file[64]; + char path[1024]; + char* env[2]; + + /* Set up the process, but make sure that the file to run is relative and + * requires a lookup into PATH. */ + init_process_options("spawn_helper1", exit_cb); + + /* Set up the PATH env variable */ + for (len = strlen(exepath), file_len = 0; + exepath[len - 1] != '/' && exepath[len - 1] != '\\'; + len--, file_len++); + memcpy(file, exepath + len, file_len - (sizeof(".exe") - sizeof(char))); + strcpy(file + file_len - (sizeof(".exe") / sizeof(char) - 1), "_no_ext"); + exepath[len] = 0; + strcpy(path, "PATH="); + strcpy(path + 5, exepath); + + env[0] = path; + env[1] = NULL; + + options.file = options.args[0] = file; + options.env = env; + + r = uv_spawn(uv_default_loop(), &process, &options); + ASSERT_OK(r); + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT_OK(r); + + ASSERT_EQ(1, exit_cb_called); + ASSERT_EQ(1, close_cb_called); + + MAKE_VALGRIND_HAPPY(uv_default_loop()); + return 0; +} #endif #ifndef _WIN32