diff --git a/src/util/win32/path_w32.c b/src/util/win32/path_w32.c index 2e90d2e8e..2714efbb6 100644 --- a/src/util/win32/path_w32.c +++ b/src/util/win32/path_w32.c @@ -153,6 +153,7 @@ int git_win32_path_canonicalize(git_win32_path path) static int git_win32_path_join( git_win32_path dest, + size_t *dest_len, const wchar_t *one, size_t one_len, const wchar_t *two, @@ -176,6 +177,9 @@ static int git_win32_path_join( memcpy(dest + one_len + backslash, two, two_len * sizeof(wchar_t)); dest[one_len + backslash + two_len] = L'\0'; + if (dest_len) + *dest_len = one_len + backslash + two_len; + return 0; } @@ -258,21 +262,12 @@ struct executable_suffix { size_t len; }; -int git_win32_path_find_executable(git_win32_path fullpath, wchar_t *exe) +static struct executable_suffix suffixes[] = { { NULL, 0 }, { L".exe", 4 }, { L".cmd", 4 } }; + +static bool has_executable_suffix(wchar_t *exe, size_t exe_len) { - struct win32_path_iter path_iter; - const wchar_t *dir; - size_t dir_len, exe_len = wcslen(exe); - bool found = false; - static struct executable_suffix suffixes[] = { { NULL, 0 }, { L".exe", 4 }, { L".cmd", 4 } }; - size_t skip_bare = 1, i; + size_t i; - if (win32_path_iter_init(&path_iter) < 0) - return -1; - - /* see if the given executable has an executable suffix; if so we will - * look for the explicit name directly, as well as with added suffixes. - */ for (i = 0; i < ARRAY_SIZE(suffixes); i++) { struct executable_suffix *suffix = &suffixes[i]; @@ -282,40 +277,80 @@ int git_win32_path_find_executable(git_win32_path fullpath, wchar_t *exe) if (exe_len < suffix->len) continue; - if (memcmp(&exe[exe_len - suffix->len], suffix->suffix, suffix->len) == 0) { - skip_bare = 0; - break; - } + if (memcmp(&exe[exe_len - suffix->len], suffix->suffix, suffix->len) == 0) + return true; } - while (win32_path_iter_next(&dir, &dir_len, &path_iter) != GIT_ITEROVER && !found) { - /* - * if the given name has an executable suffix, then try looking for it - * directly. in all cases, append executable extensions - * (".exe", ".cmd"...) - */ - for (i = skip_bare; i < ARRAY_SIZE(suffixes); i++) { - struct executable_suffix *suffix = &suffixes[i]; + return false; +} - if (git_win32_path_join(fullpath, dir, dir_len, exe, exe_len) < 0) +static int is_executable(git_win32_path path, size_t path_len, bool suffixed) +{ + size_t i; + + /* + * if the given name has an executable suffix, then try looking for it + * directly. in all cases, append executable extensions + * (".exe", ".cmd"...) + */ + for (i = suffixed ? 0 : 1; i < ARRAY_SIZE(suffixes); i++) { + struct executable_suffix *suffix = &suffixes[i]; + + if (suffix->len) { + if (path_len + suffix->len > MAX_PATH) continue; - if (suffix->len) { - if (dir_len + exe_len + 1 + suffix->len > MAX_PATH) - continue; + wcscat(path, suffix->suffix); + } - wcscat(fullpath, suffix->suffix); - } + if (_waccess(path, 0) == 0) + return true; - if (_waccess(fullpath, 0) == 0) { - found = true; - break; - } + path[path_len] = L'\0'; + } + + return false; +} + +int git_win32_path_find_executable(git_win32_path fullpath, wchar_t *exe) +{ + struct win32_path_iter path_iter; + const wchar_t *dir; + size_t dir_len, exe_len, fullpath_len; + bool suffixed = false, found = false; + + if ((exe_len = wcslen(exe)) > MAX_PATH) + goto done; + + /* see if the given executable has an executable suffix; if so we will + * look for the explicit name directly, as well as with added suffixes. + */ + suffixed = has_executable_suffix(exe, exe_len); + + /* For fully-qualified paths we do not look in PATH */ + if (wcschr(exe, L'\\') != NULL || wcschr(exe, L'/') != NULL) { + if ((found = is_executable(exe, exe_len, suffixed))) + wcscpy(fullpath, exe); + + goto done; + } + + if (win32_path_iter_init(&path_iter) < 0) + return -1; + + while (win32_path_iter_next(&dir, &dir_len, &path_iter) != GIT_ITEROVER && !found) { + if (git_win32_path_join(fullpath, &fullpath_len, dir, dir_len, exe, exe_len) < 0) + continue; + + if (is_executable(fullpath, fullpath_len, suffixed)) { + found = true; + break; } } win32_path_iter_dispose(&path_iter); +done: if (found) return 0; diff --git a/src/util/win32/process.c b/src/util/win32/process.c index bb5224597..ad4ae896c 100644 --- a/src/util/win32/process.c +++ b/src/util/win32/process.c @@ -11,6 +11,7 @@ #include "git2_util.h" #include "process.h" #include "strlist.h" +#include "fs_path.h" #ifndef DWORD_MAX # define DWORD_MAX INT32_MAX @@ -19,7 +20,8 @@ #define ENV_MAX 32767 struct git_process { - wchar_t *appname; + char *app_name; + wchar_t *app_path; wchar_t *cmdline; wchar_t *env; @@ -178,8 +180,7 @@ static int process_new( process = git__calloc(1, sizeof(git_process)); GIT_ERROR_CHECK_ALLOC(process); - if (appname && - git_utf8_to_16_alloc(&process->appname, appname) < 0) { + if (appname && (process->app_name = git__strdup(appname)) == NULL) { error = -1; goto done; } @@ -233,7 +234,7 @@ int git_process_new( size_t env_len, git_process_options *opts) { - git_str cmdline = GIT_STR_INIT; + git_str cmd_path = GIT_STR_INIT, cmdline = GIT_STR_INIT; int error; GIT_ASSERT_ARG(out && args && args_len > 0); @@ -244,6 +245,7 @@ int git_process_new( error = process_new(out, args[0], cmdline.ptr, env, env_len, opts); done: + git_str_dispose(&cmd_path); git_str_dispose(&cmdline); return error; } @@ -259,6 +261,19 @@ int git_process_start(git_process *process) out[2] = { NULL, NULL }, err[2] = { NULL, NULL }; + if (process->app_name) { + git_str cmd_path = GIT_STR_INIT; + int error; + + if ((error = git_fs_path_find_executable(&cmd_path, process->app_name)) == 0) + error = git_utf8_to_16_alloc(&process->app_path, cmd_path.ptr); + + git_str_dispose(&cmd_path); + + if (error < 0) + goto on_error; + } + memset(&security_attrs, 0, sizeof(SECURITY_ATTRIBUTES)); security_attrs.bInheritHandle = TRUE; @@ -303,7 +318,7 @@ int git_process_start(git_process *process) memset(&process->process_info, 0, sizeof(PROCESS_INFORMATION)); - if (!CreateProcessW(process->appname, process->cmdline, + if (!CreateProcessW(process->app_path, process->cmdline, NULL, NULL, TRUE, flags, process->env, process->cwd, &startup_info, @@ -501,6 +516,7 @@ void git_process_free(git_process *process) git__free(process->env); git__free(process->cwd); git__free(process->cmdline); - git__free(process->appname); + git__free(process->app_path); + git__free(process->app_name); git__free(process); } diff --git a/tests/util/process/start.c b/tests/util/process/start.c index 19ae5e312..febbc70c8 100644 --- a/tests/util/process/start.c +++ b/tests/util/process/start.c @@ -78,6 +78,52 @@ void test_process_start__not_found(void) git_process_free(process); } +void test_process_start__finds_in_path(void) +{ +#ifdef GIT_WIN32 + const char *args_array[] = { "cmd", "/c", "exit", "0" }; +#else + const char *args_array[] = { "true" }; +#endif + + git_process *process; + git_process_options opts = GIT_PROCESS_OPTIONS_INIT; + git_process_result result = GIT_PROCESS_RESULT_INIT; + + cl_git_pass(git_process_new(&process, args_array, ARRAY_SIZE(args_array), NULL, 0, &opts)); + cl_git_pass(git_process_start(process)); + cl_git_pass(git_process_wait(&result, process)); + + cl_assert_equal_i(GIT_PROCESS_STATUS_NORMAL, result.status); + cl_assert_equal_i(0, result.exitcode); + cl_assert_equal_i(0, result.signal); + + git_process_free(process); +} + +void test_process_start__adds_suffix(void) +{ +#ifdef GIT_WIN32 + const char *args_array[] = { "C:\\Windows\\System32\\cmd", "/c", "exit", "0" }; + + git_process *process; + git_process_options opts = GIT_PROCESS_OPTIONS_INIT; + git_process_result result = GIT_PROCESS_RESULT_INIT; + + cl_git_pass(git_process_new(&process, args_array, ARRAY_SIZE(args_array), NULL, 0, &opts)); + cl_git_pass(git_process_start(process)); + cl_git_pass(git_process_wait(&result, process)); + + cl_assert_equal_i(GIT_PROCESS_STATUS_NORMAL, result.status); + cl_assert_equal_i(0, result.exitcode); + cl_assert_equal_i(0, result.signal); + + git_process_free(process); +#else + cl_skip(); +#endif +} + static void write_all(git_process *process, char *buf) { size_t buf_len = strlen(buf);