From 9815615cbafe01786bd9183f1e76e83d8850b192 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Fri, 18 Sep 2015 16:57:33 +0200 Subject: [PATCH 1/2] mingw: ensure `getcwd()` reports the correct case When switching the current working directory, say, in PowerShell, it is quite possible to use a different capitalization than the one that is recorded on disk. While doing the same in `cmd.exe` adjusts the capitalization magically, that does not happen in PowerShell so that `getcwd()` returns the current directory in a different way than is recorded on disk. Typically this creates no problems except when you call git log . in a subdirectory called, say, "GIT/" but you switched to "Git/" and your `getcwd()` reports the latter, then Git won't understand that you wanted to see the history as per the `GIT/` subdirectory but it thinks you wanted to see the history of some directory that may have existed in the past (but actually never did). So let's be extra careful to adjust the capitalization of the current directory before working with it. Reported by a few PowerShell power users ;-) Signed-off-by: Johannes Schindelin --- compat/mingw.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/compat/mingw.c b/compat/mingw.c index 858ca14a57..8e6bf79a1a 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -887,8 +887,15 @@ struct tm *localtime_r(const time_t *timep, struct tm *result) char *mingw_getcwd(char *pointer, int len) { - wchar_t wpointer[MAX_PATH]; - if (!_wgetcwd(wpointer, ARRAY_SIZE(wpointer))) + wchar_t cwd[MAX_PATH], wpointer[MAX_PATH]; + DWORD ret = GetCurrentDirectoryW(ARRAY_SIZE(cwd), cwd); + + if (!ret || ret >= ARRAY_SIZE(cwd)) { + errno = ret ? ENAMETOOLONG : err_win_to_posix(GetLastError()); + return NULL; + } + ret = GetLongPathNameW(cwd, wpointer, ARRAY_SIZE(wpointer)); + if (!ret || ret >= ARRAY_SIZE(wpointer)) return NULL; if (xwcstoutf(pointer, wpointer, len) < 0) return NULL; From f60b0c24a8bc40f5bafcc0fb2cbad22352092666 Mon Sep 17 00:00:00 2001 From: Anton Serbulov Date: Fri, 15 Jan 2016 12:25:02 +0600 Subject: [PATCH 2/2] mingw: fix getcwd when the parent directory cannot be queried `GetLongPathName()` function may fail when it is unable to query the parent directory of a path component to determine the long name for that component. It happens, because of it uses `FindFirstFile()` function for each next short part of path. The `FindFirstFile()` requires `List Directory` and `Synchronize` desired access for a calling process. In case of lacking such permission for some part of path, the `GetLongPathName()` returns 0 as result and `GetLastError()` returns ERROR_ACCESS_DENIED. `GetFinalPathNameByHandle()` function can help in such cases, because it requires `Read Attributes` and `Synchronize` desired access to the target path only. The `GetFinalPathNameByHandle()` function was introduced on `Windows Server 2008/Windows Vista`. So we need to load it dynamically. `CreateFile()` parameters: `lpFileName` = path to the current directory `dwDesiredAccess` = 0 (it means `Read Attributes` and `Synchronize`) `dwShareMode` = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE (it prevents `Sharing Violation`) `lpSecurityAttributes` = NULL (default security attributes) `dwCreationDisposition` = OPEN_EXISTING (required to obtain a directory handle) `dwFlagsAndAttributes` = FILE_FLAG_BACKUP_SEMANTICS (required to obtain a directory handle) `hTemplateFile` = NULL (when opening an existing file or directory, `CreateFile` ignores this parameter) The string that is returned by `GetFinalPathNameByHandle()` function uses the \\?\ syntax. To skip the prefix and convert backslashes to slashes, the `normalize_ntpath()` mingw function will be used. Note: `GetFinalPathNameByHandle()` function returns a final path. It is the path that is returned when a path is fully resolved. For example, for a symbolic link named "C:\tmp\mydir" that points to "D:\yourdir", the final path would be "D:\yourdir". Signed-off-by: Anton Serbulov --- compat/mingw.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/compat/mingw.c b/compat/mingw.c index 8e6bf79a1a..cc73c0d642 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -888,6 +888,8 @@ struct tm *localtime_r(const time_t *timep, struct tm *result) char *mingw_getcwd(char *pointer, int len) { wchar_t cwd[MAX_PATH], wpointer[MAX_PATH]; + DECLARE_PROC_ADDR(kernel32.dll, DWORD, GetFinalPathNameByHandleW, + HANDLE, LPWSTR, DWORD, DWORD); DWORD ret = GetCurrentDirectoryW(ARRAY_SIZE(cwd), cwd); if (!ret || ret >= ARRAY_SIZE(cwd)) { @@ -895,6 +897,21 @@ char *mingw_getcwd(char *pointer, int len) return NULL; } ret = GetLongPathNameW(cwd, wpointer, ARRAY_SIZE(wpointer)); + if (!ret && GetLastError() == ERROR_ACCESS_DENIED && + INIT_PROC_ADDR(GetFinalPathNameByHandleW)) { + HANDLE hnd = CreateFileW(cwd, 0, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + if (hnd == INVALID_HANDLE_VALUE) + return NULL; + ret = GetFinalPathNameByHandleW(hnd, wpointer, ARRAY_SIZE(wpointer), 0); + CloseHandle(hnd); + if (!ret || ret >= ARRAY_SIZE(wpointer)) + return NULL; + if (xwcstoutf(pointer, normalize_ntpath(wpointer), len) < 0) + return NULL; + return pointer; + } if (!ret || ret >= ARRAY_SIZE(wpointer)) return NULL; if (xwcstoutf(pointer, wpointer, len) < 0)