From d6c610d55f8c78ef1904c2a0213b643f40a2d36b Mon Sep 17 00:00:00 2001 From: David Lomas Date: Fri, 28 Jul 2023 15:20:43 +0100 Subject: [PATCH] mingw: work around rename() failing on a read-only file At least on _some_ APFS network shares, Git fails to rename the object files because they are marked as read-only, because that has the effect of setting the uchg flag on APFS, which then means the file can't be renamed or deleted. To work around that, when a rename failed, and the read-only flag is set, try to turn it off and on again. This fixes https://github.com/git-for-windows/git/issues/4482 Signed-off-by: David Lomas Signed-off-by: Johannes Schindelin --- compat/mingw.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/compat/mingw.c b/compat/mingw.c index 74203f0ac73296..7e89fd7c74e593 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -2651,7 +2651,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 = INVALID_FILE_ATTRIBUTES, gle; + DWORD attrs = INVALID_FILE_ATTRIBUTES, gle, attrsold; int tries = 0; wchar_t wpold[MAX_LONG_PATH], wpnew[MAX_LONG_PATH]; int wpnew_len; @@ -2739,11 +2739,24 @@ int mingw_rename(const char *pold, const char *pnew) gle = GetLastError(); } - if (gle == ERROR_ACCESS_DENIED && is_inside_windows_container()) { - /* Fall back to copy to destination & remove source */ - if (CopyFileW(wpold, wpnew, FALSE) && !mingw_unlink(pold)) - return 0; - gle = GetLastError(); + if (gle == ERROR_ACCESS_DENIED) { + if (is_inside_windows_container()) { + /* Fall back to copy to destination & remove source */ + if (CopyFileW(wpold, wpnew, FALSE) && !mingw_unlink(pold)) + return 0; + gle = GetLastError(); + } else if ((attrsold = GetFileAttributesW(wpold)) & FILE_ATTRIBUTE_READONLY) { + /* if file is read-only, change and retry */ + SetFileAttributesW(wpold, attrsold & ~FILE_ATTRIBUTE_READONLY); + if (MoveFileExW(wpold, wpnew, + MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED)) { + SetFileAttributesW(wpnew, attrsold); + return 0; + } + gle = GetLastError(); + /* revert attribute change on failure */ + SetFileAttributesW(wpold, attrsold); + } } /* revert file attributes on failure */