mirror of
https://github.com/git/git.git
synced 2026-01-19 07:04:49 +00:00
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 <blees@dcon.de>
This commit is contained in:
committed by
Jameson Miller
parent
ab6b1c007a
commit
eebf2dab42
@@ -269,6 +269,34 @@ int mingw_core_config(const char *var, const char *value, void *cb)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 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] = '/';
|
||||
/* remove potential trailing slashes */
|
||||
while (i && wbuf[i - 1] == '/')
|
||||
wbuf[--i] = 0;
|
||||
return wbuf;
|
||||
}
|
||||
|
||||
int mingw_unlink(const char *pathname)
|
||||
{
|
||||
int tries = 0;
|
||||
@@ -598,10 +626,29 @@ static int current_directory_len = 0;
|
||||
int mingw_chdir(const char *dirname)
|
||||
{
|
||||
int result;
|
||||
DECLARE_PROC_ADDR(kernel32.dll, DWORD, GetFinalPathNameByHandleW,
|
||||
HANDLE, LPWSTR, DWORD, DWORD);
|
||||
wchar_t wdirname[MAX_LONG_PATH];
|
||||
if (xutftowcs_long_path(wdirname, dirname) < 0)
|
||||
return -1;
|
||||
result = _wchdir(wdirname);
|
||||
|
||||
if (has_symlinks && INIT_PROC_ADDR(GetFinalPathNameByHandleW)) {
|
||||
HANDLE hnd = CreateFileW(wdirname, 0,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
|
||||
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
||||
if (hnd == INVALID_HANDLE_VALUE) {
|
||||
errno = err_win_to_posix(GetLastError());
|
||||
return -1;
|
||||
}
|
||||
if (!GetFinalPathNameByHandleW(hnd, wdirname, ARRAY_SIZE(wdirname), 0)) {
|
||||
errno = err_win_to_posix(GetLastError());
|
||||
CloseHandle(hnd);
|
||||
return -1;
|
||||
}
|
||||
CloseHandle(hnd);
|
||||
}
|
||||
|
||||
result = _wchdir(normalize_ntpath(wdirname));
|
||||
current_directory_len = GetCurrentDirectoryW(0, NULL);
|
||||
return result;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user