diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/sysdir.c | 20 | ||||
-rw-r--r-- | src/win32/findfile.c | 316 | ||||
-rw-r--r-- | src/win32/findfile.h | 6 |
3 files changed, 162 insertions, 180 deletions
diff --git a/src/sysdir.c b/src/sysdir.c index c0e14f551..450cb509b 100644 --- a/src/sysdir.c +++ b/src/sysdir.c @@ -31,7 +31,7 @@ static int git_sysdir_guess_programdata_dirs(git_str *out) static int git_sysdir_guess_system_dirs(git_str *out) { #ifdef GIT_WIN32 - return git_win32__find_system_dirs(out, L"etc\\"); + return git_win32__find_system_dirs(out, "etc"); #else return git_str_sets(out, "/etc"); #endif @@ -154,7 +154,7 @@ static int git_sysdir_guess_xdg_dirs(git_str *out) static int git_sysdir_guess_template_dirs(git_str *out) { #ifdef GIT_WIN32 - return git_win32__find_system_dirs(out, L"share\\git-core\\templates"); + return git_win32__find_system_dirs(out, "share/git-core/templates"); #else return git_str_sets(out, "/usr/share/git-core/templates"); #endif @@ -190,22 +190,22 @@ int git_sysdir_global_init(void) error = git_sysdir__dirs[i].guess(&git_sysdir__dirs[i].buf); if (error) - return error; + return error; return git_runtime_shutdown_register(git_sysdir_global_shutdown); } int git_sysdir_reset(void) { - size_t i; - int error = 0; + size_t i; + int error = 0; - for (i = 0; !error && i < ARRAY_SIZE(git_sysdir__dirs); ++i) { - git_str_dispose(&git_sysdir__dirs[i].buf); - error = git_sysdir__dirs[i].guess(&git_sysdir__dirs[i].buf); - } + for (i = 0; !error && i < ARRAY_SIZE(git_sysdir__dirs); ++i) { + git_str_dispose(&git_sysdir__dirs[i].buf); + error = git_sysdir__dirs[i].guess(&git_sysdir__dirs[i].buf); + } - return error; + return error; } static int git_sysdir_check_selector(git_sysdir_t which) diff --git a/src/win32/findfile.c b/src/win32/findfile.c index b82347fc6..516391d7f 100644 --- a/src/win32/findfile.c +++ b/src/win32/findfile.c @@ -11,13 +11,8 @@ #include "utf-conv.h" #include "fs_path.h" -#define REG_MSYSGIT_INSTALL_LOCAL L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Git_is1" - -#ifndef _WIN64 -#define REG_MSYSGIT_INSTALL REG_MSYSGIT_INSTALL_LOCAL -#else -#define REG_MSYSGIT_INSTALL L"SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Git_is1" -#endif +#define REG_GITFORWINDOWS_KEY L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Git_is1" +#define REG_GITFORWINDOWS_KEY_WOW64 L"SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Git_is1" static int git_win32__expand_path(git_win32_path dest, const wchar_t *src) { @@ -44,164 +39,130 @@ static int win32_path_to_8(git_str *dest, const wchar_t *src) return git_str_sets(dest, utf8_path); } -static wchar_t *win32_walkpath(wchar_t *path, wchar_t *buf, size_t buflen) -{ - wchar_t term, *base = path; - - GIT_ASSERT_ARG_WITH_RETVAL(path, NULL); - GIT_ASSERT_ARG_WITH_RETVAL(buf, NULL); - GIT_ASSERT_ARG_WITH_RETVAL(buflen, NULL); - - term = (*path == L'"') ? *path++ : L';'; - - for (buflen--; *path && *path != term && buflen; buflen--) - *buf++ = *path++; - - *buf = L'\0'; /* reserved a byte via initial subtract */ +static git_win32_path mock_registry; +static bool mock_registry_set; - while (*path == term || *path == L';') - path++; - - return (path != base) ? path : NULL; -} - -static int win32_find_git_for_windows_architecture_root(git_win32_path root_path, const wchar_t *subdir) +extern int git_win32__set_registry_system_dir(const wchar_t *mock_sysdir) { - /* Git for Windows >= 2 comes with a special architecture root (mingw64 and mingw32) - * under which the "share" folder is located, check which we need (none is also ok) */ - - static const wchar_t *architecture_roots[4] = { - L"", // starting with Git 2.24 the etc folder is directly in the root folder - L"mingw64\\", - L"mingw32\\", - NULL, - }; - - const wchar_t **roots = architecture_roots; - size_t root_path_len = wcslen(root_path); - - for (; *roots != NULL; ++roots) { - git_win32_path tmp_root; - DWORD subdir_len; - if (wcscpy(tmp_root, root_path) && - root_path_len + wcslen(*roots) <= MAX_PATH && - wcscat(tmp_root, *roots) && - !_waccess(tmp_root, F_OK)) { - wcscpy(root_path, tmp_root); - root_path_len += (DWORD)wcslen(*roots); - - subdir_len = (DWORD)wcslen(subdir); - if (root_path_len + subdir_len >= MAX_PATH) - break; - - // append subdir and check whether it exists for the Git installation - wcscat(tmp_root, subdir); - if (!_waccess(tmp_root, F_OK)) { - wcscpy(root_path, tmp_root); - root_path_len += subdir_len; - break; - } + if (!mock_sysdir) { + mock_registry[0] = L'\0'; + mock_registry_set = false; + } else { + size_t len = wcslen(mock_sysdir); + + if (len > GIT_WIN_PATH_MAX) { + git_error_set(GIT_ERROR_INVALID, "mock path too long"); + return -1; } + + wcscpy(mock_registry, mock_sysdir); + mock_registry_set = true; } return 0; } -static int win32_find_git_in_path(git_str *buf, const wchar_t *gitexe, const wchar_t *subdir) +static int lookup_registry_key( + git_win32_path out, + const HKEY hive, + const wchar_t* key, + const wchar_t *value) { - wchar_t *path, *env, lastch; - git_win32_path root; - size_t gitexe_len = wcslen(gitexe); - DWORD len; - bool found = false; - - len = GetEnvironmentVariableW(L"PATH", NULL, 0); - - if (len < 0) - return -1; - - path = git__malloc(len * sizeof(wchar_t)); - GIT_ERROR_CHECK_ALLOC(path); + HKEY hkey; + DWORD type, size; + int error = GIT_ENOTFOUND; - len = GetEnvironmentVariableW(L"PATH", path, len); + /* + * Registry data may not be NUL terminated, provide room to do + * it ourselves. + */ + size = (DWORD)((sizeof(git_win32_path) - 1) * sizeof(wchar_t)); + + if (RegOpenKeyExW(hive, key, 0, KEY_READ, &hkey) != 0) + return GIT_ENOTFOUND; + + if (RegQueryValueExW(hkey, value, NULL, &type, (LPBYTE)out, &size) == 0 && + type == REG_SZ && + size > 0 && + size < sizeof(git_win32_path)) { + size_t wsize = size / sizeof(wchar_t); + size_t len = wsize - 1; + + if (out[wsize - 1] != L'\0') { + len = wsize; + out[wsize] = L'\0'; + } - if (len < 0) - return -1; + if (out[len - 1] == L'\\') + out[len - 1] = L'\0'; - env = path; + if (_waccess(out, F_OK) == 0) + error = 0; + } - while ((env = win32_walkpath(env, root, MAX_PATH-1)) && *root) { - size_t root_len = wcslen(root); - lastch = root[root_len - 1]; + RegCloseKey(hkey); + return error; +} - /* ensure trailing slash (MAX_PATH-1 to walkpath guarantees space) */ - if (lastch != L'/' && lastch != L'\\') { - root[root_len++] = L'\\'; - root[root_len] = L'\0'; - } +static int find_sysdir_in_registry(git_win32_path out) +{ + if (mock_registry_set) { + if (mock_registry[0] == L'\0') + return GIT_ENOTFOUND; - if (root_len + gitexe_len >= MAX_PATH) - continue; - - if (!_waccess(root, F_OK)) { - /* check whether we found a Git for Windows installation and do some path adjustments OR just append subdir */ - if ((root_len > 5 && wcscmp(root - 4, L"cmd\\")) || wcscmp(root - 4, L"bin\\")) { - /* strip "bin" or "cmd" and try to find architecture root for appending subdir */ - root_len -= 4; - root[root_len] = L'\0'; - if (win32_find_git_for_windows_architecture_root(root, subdir)) - continue; - } else { - if (root_len + wcslen(subdir) >= MAX_PATH) - continue; - wcscat(root, subdir); - } - - win32_path_to_8(buf, root); - found = true; - break; - } + wcscpy(out, mock_registry); + return 0; } - git__free(path); - return found ? 0 : GIT_ENOTFOUND; + if (lookup_registry_key(out, HKEY_CURRENT_USER, REG_GITFORWINDOWS_KEY, L"InstallLocation") == 0 || + lookup_registry_key(out, HKEY_CURRENT_USER, REG_GITFORWINDOWS_KEY_WOW64, L"InstallLocation") == 0 || + lookup_registry_key(out, HKEY_LOCAL_MACHINE, REG_GITFORWINDOWS_KEY, L"InstallLocation") == 0 || + lookup_registry_key(out, HKEY_LOCAL_MACHINE, REG_GITFORWINDOWS_KEY_WOW64, L"InstallLocation") == 0) + return 0; + + return GIT_ENOTFOUND; } -static int win32_find_git_in_registry( - git_str *buf, const HKEY hive, const wchar_t *key, const wchar_t *subdir) +static int find_sysdir_in_path(git_win32_path out) { - HKEY hKey; - int error = GIT_ENOTFOUND; + size_t out_len; - GIT_ASSERT_ARG(buf); + if (git_win32_path_find_executable(out, L"git.exe") < 0 && + git_win32_path_find_executable(out, L"git.cmd") < 0) + return GIT_ENOTFOUND; - if (!RegOpenKeyExW(hive, key, 0, KEY_READ, &hKey)) { - DWORD dwType, cbData; - git_win32_path path; + out_len = wcslen(out); - /* Ensure that the buffer is big enough to have the suffix attached - * after we receive the result. */ - cbData = (DWORD)(sizeof(path) - wcslen(subdir) * sizeof(wchar_t)); + /* Trim the file name */ + if (out_len <= CONST_STRLEN(L"git.exe")) + return GIT_ENOTFOUND; - /* InstallLocation points to the root of the git directory */ - if (!RegQueryValueExW(hKey, L"InstallLocation", NULL, &dwType, (LPBYTE)path, &cbData) && - dwType == REG_SZ) { + out_len -= CONST_STRLEN(L"git.exe"); - /* Convert to UTF-8, with forward slashes, and output the path - * to the provided buffer */ - if (!win32_find_git_for_windows_architecture_root(path, subdir) && - !win32_path_to_8(buf, path)) - error = 0; - } + if (out_len && out[out_len - 1] == L'\\') + out_len--; - RegCloseKey(hKey); - } + /* + * Git for Windows usually places the command in a 'bin' or + * 'cmd' directory, trim that. + */ + if (out_len >= CONST_STRLEN(L"\\bin") && + wcsncmp(&out[out_len - CONST_STRLEN(L"\\bin")], L"\\bin", CONST_STRLEN(L"\\bin")) == 0) + out_len -= CONST_STRLEN(L"\\bin"); + else if (out_len >= CONST_STRLEN(L"\\cmd") && + wcsncmp(&out[out_len - CONST_STRLEN(L"\\cmd")], L"\\cmd", CONST_STRLEN(L"\\cmd")) == 0) + out_len -= CONST_STRLEN(L"\\cmd"); - return error; + if (!out_len) + return GIT_ENOTFOUND; + + out[out_len] = L'\0'; + return 0; } static int win32_find_existing_dirs( - git_str *out, const wchar_t *tmpl[]) + git_str* out, + const wchar_t* tmpl[]) { git_win32_path path16; git_str buf = GIT_STR_INIT; @@ -210,9 +171,8 @@ static int win32_find_existing_dirs( for (; *tmpl != NULL; tmpl++) { if (!git_win32__expand_path(path16, *tmpl) && - path16[0] != L'%' && - !_waccess(path16, F_OK)) - { + path16[0] != L'%' && + !_waccess(path16, F_OK)) { win32_path_to_8(&buf, path16); if (buf.size) @@ -225,47 +185,67 @@ static int win32_find_existing_dirs( return (git_str_oom(out) ? -1 : 0); } -int git_win32__find_system_dir_in_path(git_str *out, const wchar_t *subdir) -{ - /* directories where git.exe & git.cmd are found */ - if (win32_find_git_in_path(out, L"git.exe", subdir) == 0) - return 0; - - return win32_find_git_in_path(out, L"git.cmd", subdir); -} - -static int git_win32__find_system_dir_in_registry(git_str *out, const wchar_t *subdir) +static int append_subdir(git_str *out, git_str *path, const char *subdir) { - git_str buf = GIT_STR_INIT; - - /* directories where git is installed according to registry */ - if (!win32_find_git_in_registry( - &buf, HKEY_CURRENT_USER, REG_MSYSGIT_INSTALL_LOCAL, subdir) && buf.size) - git_str_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr); + static const char* architecture_roots[] = { + "", + "mingw64", + "mingw32", + NULL + }; + const char **root; + size_t orig_path_len = path->size; -#ifdef GIT_ARCH_64 - if (!win32_find_git_in_registry( - &buf, HKEY_LOCAL_MACHINE, REG_MSYSGIT_INSTALL_LOCAL, subdir) && buf.size) - git_str_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr); -#endif + for (root = architecture_roots; *root; root++) { + if ((*root[0] && git_str_joinpath(path, path->ptr, *root) < 0) || + git_str_joinpath(path, path->ptr, subdir) < 0) + return -1; - if (!win32_find_git_in_registry( - &buf, HKEY_LOCAL_MACHINE, REG_MSYSGIT_INSTALL, subdir) && buf.size) - git_str_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr); + if (git_fs_path_exists(path->ptr) && + git_str_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, path->ptr) < 0) + return -1; - git_str_dispose(&buf); + git_str_truncate(path, orig_path_len); + } - return (git_str_oom(out) ? -1 : 0); + return 0; } -int git_win32__find_system_dirs(git_str *out, const wchar_t *subdir) +int git_win32__find_system_dirs(git_str *out, const char *subdir) { + git_win32_path pathdir, regdir; + git_str path8 = GIT_STR_INIT; + bool has_pathdir, has_regdir; int error; - if ((error = git_win32__find_system_dir_in_path(out, subdir)) == 0) - error = git_win32__find_system_dir_in_registry(out, subdir); + has_pathdir = (find_sysdir_in_path(pathdir) == 0); + has_regdir = (find_sysdir_in_registry(regdir) == 0); - return error; + if (!has_pathdir && !has_regdir) + return GIT_ENOTFOUND; + + /* + * Usually the git in the path is the same git in the registry, + * in this case there's no need to duplicate the paths. + */ + if (has_pathdir && has_regdir && wcscmp(pathdir, regdir) == 0) + has_regdir = false; + + if (has_pathdir) { + if ((error = win32_path_to_8(&path8, pathdir)) < 0 || + (error = append_subdir(out, &path8, subdir)) < 0) + goto done; + } + + if (has_regdir) { + if ((error = win32_path_to_8(&path8, regdir)) < 0 || + (error = append_subdir(out, &path8, subdir)) < 0) + goto done; + } + +done: + git_str_dispose(&path8); + return error; } int git_win32__find_global_dirs(git_str *out) diff --git a/src/win32/findfile.h b/src/win32/findfile.h index 7f7691ce1..61fb7dbad 100644 --- a/src/win32/findfile.h +++ b/src/win32/findfile.h @@ -10,8 +10,10 @@ #include "common.h" -extern int git_win32__find_system_dir_in_path(git_str* out, const wchar_t* subdir); -extern int git_win32__find_system_dirs(git_str *out, const wchar_t *subpath); +/** Sets the mock registry root for Git for Windows for testing. */ +extern int git_win32__set_registry_system_dir(const wchar_t *mock_sysdir); + +extern int git_win32__find_system_dirs(git_str *out, const char *subpath); extern int git_win32__find_global_dirs(git_str *out); extern int git_win32__find_xdg_dirs(git_str *out); extern int git_win32__find_programdata_dirs(git_str *out); |