From 6fa50cc4a1979fb8a2f77a026e307d6336a09172 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Wed, 17 Dec 2025 14:18:39 +0000 Subject: mingw: special-case `open(symlink, O_CREAT | O_EXCL)` The `_wopen()` function would gladly follow a symbolic link to a non-existent file and create it when given above-mentioned flags. Git expects the `open()` call to fail, though. So let's add yet another work-around to pretend that Windows behaves according to POSIX, see: https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html#:~:text=If%20O_CREAT%20and%20O_EXCL%20are,set%2C%20the%20result%20is%20undefined. This is required to let t4115.8(--reject removes .rej symlink if it exists) pass on Windows when enabling the MSYS2 runtime's symbolic link support. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- compat/mingw.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'compat') diff --git a/compat/mingw.c b/compat/mingw.c index 90ba5cea9d..ba1b7b6dd1 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -629,6 +629,7 @@ int mingw_open (const char *filename, int oflags, ...) int fd, create = (oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL); wchar_t wfilename[MAX_PATH]; open_fn_t open_fn; + WIN32_FILE_ATTRIBUTE_DATA fdata; DECLARE_PROC_ADDR(ntdll.dll, NTSTATUS, NTAPI, RtlGetLastNtStatus, void); @@ -653,6 +654,19 @@ int mingw_open (const char *filename, int oflags, ...) else if (xutftowcs_path(wfilename, filename) < 0) return -1; + /* + * When `symlink` exists and is a symbolic link pointing to a + * non-existing file, `_wopen(symlink, O_CREAT | O_EXCL)` would + * create that file. Not what we want: Linux would say `EEXIST` + * in that instance, which is therefore what Git expects. + */ + if (create && + GetFileAttributesExW(wfilename, GetFileExInfoStandard, &fdata) && + (fdata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) { + errno = EEXIST; + return -1; + } + fd = open_fn(wfilename, oflags, mode); /* -- cgit v1.3 From f0af8b4aae3397d6bbe68a3c09f558cab983eaff Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Fri, 9 Jan 2026 20:05:05 +0000 Subject: mingw: do resolve symlinks in `getcwd()` As pointed out in https://github.com/git-for-windows/git/issues/1676, the `git rev-parse --is-inside-work-tree` command currently fails when the current directory's path contains symbolic links. The underlying reason for this bug is that `getcwd()` is supposed to resolve symbolic links, but our `mingw_getcwd()` implementation did not. We do have all the building blocks for that, though: the `GetFinalPathByHandleW()` function will resolve symbolic links. However, we only called that function if `GetLongPathNameW()` failed, for historical reasons: the latter function was supported for a long time, but the former API function was introduced only with Windows Vista, and we used to support also Windows XP. With that support having been dropped, we are free to call the symbolic link-resolving function right away. Signed-off-by: Johannes Schindelin Signed-off-by: Junio C Hamano --- compat/mingw.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) (limited to 'compat') diff --git a/compat/mingw.c b/compat/mingw.c index f09b49ff21..cf4f3c92e7 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -1239,18 +1239,16 @@ char *mingw_getcwd(char *pointer, int len) { wchar_t cwd[MAX_PATH], wpointer[MAX_PATH]; DWORD ret = GetCurrentDirectoryW(ARRAY_SIZE(cwd), cwd); + HANDLE hnd; 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 && 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; + 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) { ret = GetFinalPathNameByHandleW(hnd, wpointer, ARRAY_SIZE(wpointer), 0); CloseHandle(hnd); if (!ret || ret >= ARRAY_SIZE(wpointer)) @@ -1259,13 +1257,11 @@ char *mingw_getcwd(char *pointer, int len) return NULL; return pointer; } - if (!ret || ret >= ARRAY_SIZE(wpointer)) - return NULL; - if (GetFileAttributesW(wpointer) == INVALID_FILE_ATTRIBUTES) { + if (GetFileAttributesW(cwd) == INVALID_FILE_ATTRIBUTES) { errno = ENOENT; return NULL; } - if (xwcstoutf(pointer, wpointer, len) < 0) + if (xwcstoutf(pointer, cwd, len) < 0) return NULL; convert_slashes(pointer); return pointer; -- cgit v1.3