Skip to content

Commit 8b58d6c

Browse files
kbleesGit for Windows Build Agent
authored andcommitted
Win32: mingw_chdir: change to symlink-resolved directory
If symlinks are enabled, resolve all symlinks when changing directories, as required by POSIX. Note: Git's real_path() function bases its link resolution algorithm on this property of chdir(). Unfortunately, the current directory on Windows is limited to only MAX_PATH (260) characters. Therefore using symlinks and long paths in combination may be problematic. Note: GetFinalPathNameByHandleW() was introduced with symlink support in Windows Vista. Thus, for compatibility with Windows XP, we need to load it dynamically and behave gracefully if it isnt's available. Signed-off-by: Karsten Blees <[email protected]>
1 parent 25a9cee commit 8b58d6c

File tree

1 file changed

+48
-1
lines changed

1 file changed

+48
-1
lines changed

compat/mingw.c

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,34 @@ static int retry_ask_yes_no(int *tries, const char *format, ...)
227227
return result;
228228
}
229229

230+
/* Normalizes NT paths as returned by some low-level APIs. */
231+
static wchar_t *normalize_ntpath(wchar_t *wbuf)
232+
{
233+
int i;
234+
/* fix absolute path prefixes */
235+
if (wbuf[0] == '\\') {
236+
/* strip NT namespace prefixes */
237+
if (!wcsncmp(wbuf, L"\\??\\", 4) ||
238+
!wcsncmp(wbuf, L"\\\\?\\", 4))
239+
wbuf += 4;
240+
else if (!wcsnicmp(wbuf, L"\\DosDevices\\", 12))
241+
wbuf += 12;
242+
/* replace remaining '...UNC\' with '\\' */
243+
if (!wcsnicmp(wbuf, L"UNC\\", 4)) {
244+
wbuf += 2;
245+
*wbuf = '\\';
246+
}
247+
}
248+
/* convert backslashes to slashes */
249+
for (i = 0; wbuf[i]; i++)
250+
if (wbuf[i] == '\\')
251+
wbuf[i] = '/';
252+
/* remove potential trailing slashes */
253+
while (i && wbuf[i - 1] == '/')
254+
wbuf[--i] = 0;
255+
return wbuf;
256+
}
257+
230258
int mingw_unlink(const char *pathname)
231259
{
232260
int tries = 0;
@@ -517,10 +545,29 @@ static int current_directory_len = 0;
517545
int mingw_chdir(const char *dirname)
518546
{
519547
int result;
548+
DECLARE_PROC_ADDR(kernel32.dll, DWORD, GetFinalPathNameByHandleW,
549+
HANDLE, LPWSTR, DWORD, DWORD);
520550
wchar_t wdirname[MAX_LONG_PATH];
521551
if (xutftowcs_long_path(wdirname, dirname) < 0)
522552
return -1;
523-
result = _wchdir(wdirname);
553+
554+
if (has_symlinks && INIT_PROC_ADDR(GetFinalPathNameByHandleW)) {
555+
HANDLE hnd = CreateFileW(wdirname, 0,
556+
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
557+
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
558+
if (hnd == INVALID_HANDLE_VALUE) {
559+
errno = err_win_to_posix(GetLastError());
560+
return -1;
561+
}
562+
if (!GetFinalPathNameByHandleW(hnd, wdirname, ARRAY_SIZE(wdirname), 0)) {
563+
errno = err_win_to_posix(GetLastError());
564+
CloseHandle(hnd);
565+
return -1;
566+
}
567+
CloseHandle(hnd);
568+
}
569+
570+
result = _wchdir(normalize_ntpath(wdirname));
524571
current_directory_len = GetCurrentDirectoryW(0, NULL);
525572
return result;
526573
}

0 commit comments

Comments
 (0)