mingw: support renaming symlinks
Older MSVCRT's `_wrename()` function cannot rename symlinks over existing files: it returns success without doing anything. Newer MSVCR*.dll versions probably do not share this problem: according to CRT sources, they just call `MoveFileEx()` with the `MOVEFILE_COPY_ALLOWED` flag. Avoid the `_wrename()` call, and go with directly calling `MoveFileEx()`, with proper error handling of course. Signed-off-by: Karsten Blees <karsten.blees@gmail.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Karsten Blees committed
Jan 9, 2026 at 20:05 UTC
5e88e98c04267b44e4f822f297c60e1e8c852411
1 file changed
+16
-22
compat/mingw.c
+16
-22
index b1cc30d0f1..55f0bb478e 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -2275,7 +2275,7 @@ int mingw_accept(int sockfd1, struct sockaddr *sa, socklen_t *sz)
int mingw_rename(const char *pold, const char *pnew)
{
static int supports_file_rename_info_ex = 1;
- DWORD attrs, gle;
+ DWORD attrs = INVALID_FILE_ATTRIBUTES, gle;
int tries = 0;
wchar_t wpold[MAX_PATH], wpnew[MAX_PATH];
int wpnew_len;
@@ -2286,15 +2286,6 @@ int mingw_rename(const char *pold, const char *pnew)
if (wpnew_len < 0)
return -1;
- /*
- * Try native rename() first to get errno right.
- * It is based on MoveFile(), which cannot overwrite existing files.
- */
- if (!_wrename(wpold, wpnew))
- return 0;
- if (errno != EEXIST)
- return -1;
-
repeat:
if (supports_file_rename_info_ex) {
/*
@@ -2370,13 +2361,22 @@ repeat:
* to retry.
*/
} else {
- if (MoveFileExW(wpold, wpnew, MOVEFILE_REPLACE_EXISTING))
+ if (MoveFileExW(wpold, wpnew,
+ MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED))
return 0;
gle = GetLastError();
}
- /* TODO: translate more errors */
- if (gle == ERROR_ACCESS_DENIED &&
+ /* revert file attributes on failure */
+ if (attrs != INVALID_FILE_ATTRIBUTES)
+ SetFileAttributesW(wpnew, attrs);
+
+ if (!is_file_in_use_error(gle)) {
+ errno = err_win_to_posix(gle);
+ return -1;
+ }
+
+ if (attrs == INVALID_FILE_ATTRIBUTES &&
(attrs = GetFileAttributesW(wpnew)) != INVALID_FILE_ATTRIBUTES) {
if (attrs & FILE_ATTRIBUTE_DIRECTORY) {
DWORD attrsold = GetFileAttributesW(wpold);
@@ -2388,16 +2388,10 @@ repeat:
return -1;
}
if ((attrs & FILE_ATTRIBUTE_READONLY) &&
- SetFileAttributesW(wpnew, attrs & ~FILE_ATTRIBUTE_READONLY)) {
- if (MoveFileExW(wpold, wpnew, MOVEFILE_REPLACE_EXISTING))
- return 0;
- gle = GetLastError();
- /* revert file attributes on failure */
- SetFileAttributesW(wpnew, attrs);
- }
+ SetFileAttributesW(wpnew, attrs & ~FILE_ATTRIBUTE_READONLY))
+ goto repeat;
}
- if (gle == ERROR_ACCESS_DENIED &&
- retry_ask_yes_no(&tries, "Rename from '%s' to '%s' failed. "
+ if (retry_ask_yes_no(&tries, "Rename from '%s' to '%s' failed. "
"Should I try again?", pold, pnew))
goto repeat;