git/list[1] front-page[2] threads[3] people[4] search[5] about
wed 2026-10-07 18:09 UTC

[PATCH v2 11/18] mingw: support renaming symlinks

From
Karsten Blees via GitGitGadget <gitgitgadget@gmail.com>
Date
Jan 9, 2026, 20:05 UTC
Message-ID
<3670a0a181f8c1198ffea6560ae81a4b4f949be2.1767989115.git.gitgitgadget@gmail.com>
In-Reply-To
<pull.2018.v2.git.1767989115.gitgitgadget@gmail.com>
From: Karsten Blees <karsten.blees@gmail.com>

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>
---
 compat/mingw.c | 38 ++++++++++++++++----------------------
 1 file changed, 16 insertions(+), 22 deletions(-)
diff --git a/compat/mingw.c b/compat/mingw.c
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;
 
-- 
gitgitgadget
Previous: Karsten Blees via GitGitGadgetNext: Karsten Blees via GitGitGadget
Message 43 of 51 in “Support symbolic links on Windows”
  1. 00/18 Support symbolic links on WindowsJohannes Schindelin via GitGitGadget, Dec 17, 2025
  2. 01/18 mingw: don't call `GetFileAttributes()` twice in `mingw_lstat()`Karsten Blees via GitGitGadget, Dec 17, 2025
  3. 02/18 mingw: implement `stat()` with symlink supportKarsten Blees via GitGitGadget, Dec 17, 2025
  4. 03/18 mingw: drop the separate `do_lstat()` functionKarsten Blees via GitGitGadget, Dec 17, 2025
  5. 04/18 mingw: let `mingw_lstat()` error early upon problems with reparse pointsKarsten Blees via GitGitGadget, Dec 17, 2025
  6. 05/18 mingw: teach dirent about symlinksKarsten Blees via GitGitGadget, Dec 17, 2025
  7. 06/18 mingw: compute the correct size for symlinks in `mingw_lstat()`Bill Zissimopoulos via GitGitGadget, Dec 17, 2025
  8. 07/18 mingw: factor out the retry logicKarsten Blees via GitGitGadget, Dec 17, 2025
  9. 08/18 mingw: change default of `core.symlinks` to falseKarsten Blees via GitGitGadget, Dec 17, 2025
  10. 09/18 mingw: add symlink-specific error codesKarsten Blees via GitGitGadget, Dec 17, 2025
  11. 10/18 mingw: handle symlinks to directories in `mingw_unlink()`Karsten Blees via GitGitGadget, Dec 17, 2025
  12. 11/18 mingw: support renaming symlinksKarsten Blees via GitGitGadget, Dec 17, 2025
  13. 12/18 mingw: allow `mingw_chdir()` to change to symlink-resolved directoriesKarsten Blees via GitGitGadget, Dec 17, 2025
  14. 13/18 mingw: implement `readlink()`Karsten Blees via GitGitGadget, Dec 17, 2025
  15. 14/18 mingw: implement basic `symlink()` functionality (file symlinks only)Karsten Blees via GitGitGadget, Dec 17, 2025
  16. 15/18 mingw: add support for symlinks to directoriesKarsten Blees via GitGitGadget, Dec 17, 2025
  17. 16/18 mingw: try to create symlinks without elevated permissionsJohannes Schindelin via GitGitGadget, Dec 17, 2025
  18. 17/18 mingw: emulate `stat()` a little more faithfullyJohannes Schindelin via GitGitGadget, Dec 17, 2025
  19. 18/18 mingw: special-case index entries for symlinks with buggy sizeJohannes Schindelin via GitGitGadget, Dec 17, 2025
  20. Junio C HamanoDec 18, 2025
  21. Ben KnobleDec 18, 2025
  22. Johannes SixtDec 18, 2025
  23. Johannes SixtDec 18, 2025
  24. Johannes SixtDec 18, 2025
  25. Johannes SixtDec 18, 2025
  26. Johannes SixtDec 18, 2025
  27. Johannes SixtDec 18, 2025
  28. Karsten BleesDec 18, 2025
  29. Johannes SchindelinJan 9, 2026
  30. Johannes SchindelinJan 9, 2026
  31. Johannes SchindelinJan 9, 2026
  32. Johannes SchindelinJan 9, 2026
  33. 00/18 Support symbolic links on WindowsJohannes Schindelin via GitGitGadget, Jan 9, 2026
  34. 01/18 mingw: don't call `GetFileAttributes()` twice in `mingw_lstat()`Karsten Blees via GitGitGadget, Jan 9, 2026
  35. 02/18 mingw: implement `stat()` with symlink supportKarsten Blees via GitGitGadget, Jan 9, 2026
  36. 03/18 mingw: drop the separate `do_lstat()` functionKarsten Blees via GitGitGadget, Jan 9, 2026
  37. 04/18 mingw: let `mingw_lstat()` error early upon problems with reparse pointsKarsten Blees via GitGitGadget, Jan 9, 2026
  38. 05/18 mingw: teach dirent about symlinksKarsten Blees via GitGitGadget, Jan 9, 2026
  39. 06/18 mingw: compute the correct size for symlinks in `mingw_lstat()`Bill Zissimopoulos via GitGitGadget, Jan 9, 2026
  40. 07/18 mingw: factor out the retry logicKarsten Blees via GitGitGadget, Jan 9, 2026
  41. 08/18 mingw: change default of `core.symlinks` to falseKarsten Blees via GitGitGadget, Jan 9, 2026
  42. 09/18 mingw: add symlink-specific error codesKarsten Blees via GitGitGadget, Jan 9, 2026
  43. 11/18 mingw: support renaming symlinksKarsten Blees via GitGitGadget, Jan 9, 2026
  44. 10/18 mingw: handle symlinks to directories in `mingw_unlink()`Karsten Blees via GitGitGadget, Jan 9, 2026
  45. 12/18 mingw: allow `mingw_chdir()` to change to symlink-resolved directoriesKarsten Blees via GitGitGadget, Jan 9, 2026
  46. 13/18 mingw: implement `readlink()`Karsten Blees via GitGitGadget, Jan 9, 2026
  47. 14/18 mingw: implement basic `symlink()` functionality (file symlinks only)Karsten Blees via GitGitGadget, Jan 9, 2026
  48. 15/18 mingw: add support for symlinks to directoriesKarsten Blees via GitGitGadget, Jan 9, 2026
  49. 16/18 mingw: try to create symlinks without elevated permissionsJohannes Schindelin via GitGitGadget, Jan 9, 2026
  50. 17/18 mingw: emulate `stat()` a little more faithfullyJohannes Schindelin via GitGitGadget, Jan 9, 2026
  51. 18/18 mingw: special-case index entries for symlinks with buggy sizeJohannes Schindelin via GitGitGadget, Jan 9, 2026

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.