Skip to content

Commit 5f50d68

Browse files
skvoboodscho
authored andcommitted
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 <[email protected]>
1 parent 42e080f commit 5f50d68

File tree

1 file changed

+17
-0
lines changed

1 file changed

+17
-0
lines changed

compat/mingw.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -834,11 +834,28 @@ struct tm *localtime_r(const time_t *timep, struct tm *result)
834834
char *mingw_getcwd(char *pointer, int len)
835835
{
836836
wchar_t cwd[MAX_PATH], wpointer[MAX_PATH];
837+
DECLARE_PROC_ADDR(kernel32.dll, DWORD, GetFinalPathNameByHandleW,
838+
HANDLE, LPWSTR, DWORD, DWORD);
837839
DWORD ret = GetCurrentDirectoryW(ARRAY_SIZE(cwd), cwd);
838840

839841
if (ret < 0 || ret >= ARRAY_SIZE(cwd))
840842
return NULL;
841843
ret = GetLongPathNameW(cwd, wpointer, ARRAY_SIZE(wpointer));
844+
if (!ret && GetLastError() == ERROR_ACCESS_DENIED &&
845+
INIT_PROC_ADDR(GetFinalPathNameByHandleW)) {
846+
HANDLE hnd = CreateFileW(cwd, 0,
847+
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
848+
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
849+
if (hnd == INVALID_HANDLE_VALUE)
850+
return NULL;
851+
ret = GetFinalPathNameByHandleW(hnd, wpointer, ARRAY_SIZE(wpointer), 0);
852+
CloseHandle(hnd);
853+
if (!ret || ret >= ARRAY_SIZE(wpointer))
854+
return NULL;
855+
if (xwcstoutf(pointer, normalize_ntpath(wpointer), len) < 0)
856+
return NULL;
857+
return pointer;
858+
}
842859
if (!ret || ret >= ARRAY_SIZE(wpointer))
843860
return NULL;
844861
if (xwcstoutf(pointer, wpointer, len) < 0)

0 commit comments

Comments
 (0)