From 2a867e2571adceaf8d67665b45970155811ef412 Mon Sep 17 00:00:00 2001 From: Patrick Steinhardt Date: Fri, 4 Oct 2024 12:09:54 +0200 Subject: [PATCH] mingw: try harder to rename `tables.list` It is not just `tables.list`, of course, but this hack allows t0610.47 to pass, finally. Signed-off-by: Patrick Steinhardt Signed-off-by: Johannes Schindelin --- compat/mingw.c | 66 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 57 insertions(+), 9 deletions(-) diff --git a/compat/mingw.c b/compat/mingw.c index 56db193d13602d..8865c0d5ffc3c1 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -2723,6 +2723,26 @@ int mingw_accept(int sockfd1, struct sockaddr *sa, socklen_t *sz) return sockfd2; } +/* + * This is only available on Windows 10 build 1607 or later, and mingw-w64's + * headers only define this if NTDDI_VERSION >= 0x0A000002. + * + * We use it here optimistically. + */ +typedef struct _FILE_RENAME_INFORMATION { + ULONG Flags; + HANDLE RootDirectory; + ULONG FileNameLength; + WCHAR FileName[1]; +} FILE_RENAME_INFORMATION, *PFILE_RENAME_INFORMATION; + +enum { + FILE_RENAME_REPLACE_IF_EXISTS = 0x00000001, + FILE_RENAME_POSIX_SEMANTICS = 0x00000002, + FILE_RENAME_IGNORE_READONLY_ATTRIBUTE = 0x00000040, +}; +#define FileRenameInfoEx 22 + #undef rename int mingw_rename(const char *pold, const char *pnew) { @@ -2733,7 +2753,6 @@ int mingw_rename(const char *pold, const char *pnew) xutftowcs_long_path(wpnew, pnew) < 0) return -1; -repeat: if (MoveFileExW(wpold, wpnew, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED)) return 0; @@ -2775,17 +2794,46 @@ int mingw_rename(const char *pold, const char *pnew) if (attrsold == INVALID_FILE_ATTRIBUTES || !(attrsold & FILE_ATTRIBUTE_DIRECTORY)) errno = EISDIR; - else if (!_wrmdir(wpnew)) - goto repeat; + else if (_wrmdir(wpnew)) + return -1; + } + if (attrs & FILE_ATTRIBUTE_READONLY) + SetFileAttributesW(wpnew, attrs & ~FILE_ATTRIBUTE_READONLY); + } + + { + PFILE_RENAME_INFORMATION fri = NULL; + size_t wpnew_size; + HANDLE handle = CreateFileW(wpold, DELETE, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (handle == INVALID_HANDLE_VALUE) { + errno = ENOENT; return -1; } - if ((attrs & FILE_ATTRIBUTE_READONLY) && - SetFileAttributesW(wpnew, attrs & ~FILE_ATTRIBUTE_READONLY)) - goto repeat; + + wpnew_size = sizeof(wchar_t) * (1 + wcslen(wpnew)); + fri = xcalloc(1, sizeof(*fri) + wpnew_size); + fri->Flags = FILE_RENAME_REPLACE_IF_EXISTS | FILE_RENAME_POSIX_SEMANTICS | + FILE_RENAME_IGNORE_READONLY_ATTRIBUTE; + fri->FileNameLength = wpnew_size - sizeof(wchar_t); + memcpy(fri->FileName, wpnew, wpnew_size); + + do { + if (SetFileInformationByHandle(handle, FileRenameInfoEx, + fri, sizeof(*fri) + wpnew_size) || + MoveFileExW(wpold, wpnew, + MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED)) { + CloseHandle(handle); + free(fri); + return 0; + } + } while (retry_ask_yes_no(&tries, "Rename from '%s' to '%s' failed. " + "Should I try again?", pold, pnew)); + CloseHandle(handle); + free(fri); } - if (retry_ask_yes_no(&tries, "Rename from '%s' to '%s' failed. " - "Should I try again?", pold, pnew)) - goto repeat; errno = EACCES; return -1;