[PATCH 2/2] mingw: fix getcwd when the parent directory cannot be queried
- From
- Anton Serbulov via GitGitGadget <gitgitgadget@gmail.com>
- Date
- Oct 23, 2018, 10:52 UTC
- Message-ID
- <3e3b1c3b1ff3363b216574f09d2c9cf22d25ad30.1540291965.git.gitgitgadget@gmail.com>
- In-Reply-To
- <pull.54.git.gitgitgadget@gmail.com>
From: Anton Serbulov <aserbulov@plesk.com>
`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 <aserbulov@plesk.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> --- compat/mingw.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+)
diff --git a/compat/mingw.c b/compat/mingw.c index 2c3e27ce9..19addfa5d 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -202,6 +202,31 @@ static int ask_yes_no_if_possible(const char *format, ...) } } +/* Normalizes NT paths as returned by some low-level APIs. */ +static wchar_t *normalize_ntpath(wchar_t *wbuf) +{ + int i; + /* fix absolute path prefixes */ + if (wbuf[0] == '\\') { + /* strip NT namespace prefixes */ + if (!wcsncmp(wbuf, L"\\??\\", 4) || + !wcsncmp(wbuf, L"\\\\?\\", 4)) + wbuf += 4; + else if (!wcsnicmp(wbuf, L"\\DosDevices\\", 12)) + wbuf += 12; + /* replace remaining '...UNC\' with '\\' */ + if (!wcsnicmp(wbuf, L"UNC\\", 4)) { + wbuf += 2; + *wbuf = '\\'; + } + } + /* convert backslashes to slashes */ + for (i = 0; wbuf[i]; i++) + if (wbuf[i] == '\\') + wbuf[i] = '/'; + return wbuf; +} + int mingw_unlink(const char *pathname) { int ret, tries = 0; @@ -925,6 +950,20 @@ char *mingw_getcwd(char *pointer, int len) return NULL; } ret = GetLongPathNameW(cwd, wpointer, ARRAY_SIZE(wpointer)); + if (!ret && GetLastError() == ERROR_ACCESS_DENIED) { + 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)
-- gitgitgadget