From ff954cf11bd6cd03d100924f4d3c77488201f83e Mon Sep 17 00:00:00 2001 From: Po Lu Date: Fri, 14 Jun 2024 10:44:20 +0800 Subject: [PATCH] Restore file's original name after a failed rename_replace on Windows 9X * src/w32.c (sys_rename_replace): If the rename operation fails and an intermediate name was generated and applied to the input file, restore the original. (cherry picked from commit a458dc5dc828cfbe5d74226d6ef45a3d205cb7d8) --- src/w32.c | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/src/w32.c b/src/w32.c index 6d0b178e978..1c6a56bcbd9 100644 --- a/src/w32.c +++ b/src/w32.c @@ -4740,10 +4740,11 @@ int sys_rename_replace (const char *oldname, const char *newname, BOOL force) { BOOL result; - char temp[MAX_UTF8_PATH], temp_a[MAX_PATH];; + char temp[MAX_UTF8_PATH], temp_a[MAX_PATH]; int newname_dev; int oldname_dev; bool have_temp_a = false; + char oldname_a[MAX_PATH]; /* MoveFile on Windows 95 doesn't correctly change the short file name alias in a number of circumstances (it is not easy to predict when @@ -4768,7 +4769,6 @@ sys_rename_replace (const char *oldname, const char *newname, BOOL force) char * o; char * p; int i = 0; - char oldname_a[MAX_PATH]; oldname = map_w32_filename (oldname, NULL); filename_to_ansi (oldname, oldname_a); @@ -4844,7 +4844,7 @@ sys_rename_replace (const char *oldname, const char *newname, BOOL force) DWORD attributes_new; if (_wchmod (newname_w, 0666) != 0) - return result; + goto return_result; attributes_old = GetFileAttributesW (temp_w); attributes_new = GetFileAttributesW (newname_w); if (attributes_old != -1 && attributes_new != -1 @@ -4855,15 +4855,16 @@ sys_rename_replace (const char *oldname, const char *newname, BOOL force) errno = ENOTDIR; else errno = EISDIR; - return -1; + result = -1; + goto return_result; } if ((attributes_new & FILE_ATTRIBUTE_DIRECTORY) != 0) { if (_wrmdir (newname_w) != 0) - return result; + goto return_result; } else if (_wunlink (newname_w) != 0) - return result; + goto return_result; result = _wrename (temp_w, newname_w); } else if (w32err == ERROR_PRIVILEGE_NOT_HELD @@ -4902,7 +4903,7 @@ sys_rename_replace (const char *oldname, const char *newname, BOOL force) DWORD attributes_new; if (_chmod (newname_a, 0666) != 0) - return result; + goto return_result; attributes_old = GetFileAttributesA (temp_a); attributes_new = GetFileAttributesA (newname_a); if (attributes_old != -1 && attributes_new != -1 @@ -4913,15 +4914,16 @@ sys_rename_replace (const char *oldname, const char *newname, BOOL force) errno = ENOTDIR; else errno = EISDIR; - return -1; + result = -1; + goto return_result; } if ((attributes_new & FILE_ATTRIBUTE_DIRECTORY) != 0) { if (_rmdir (newname_a) != 0) - return result; + goto return_result; } else if (_unlink (newname_a) != 0) - return result; + goto return_result; result = rename (temp_a, newname_a); } else if (w32err == ERROR_PRIVILEGE_NOT_HELD @@ -4930,6 +4932,23 @@ sys_rename_replace (const char *oldname, const char *newname, BOOL force) } } + return_result: + /* This label is also invoked on failure, and on Windows 9X, restores + the initial name of files that will have been renamed in + preparation for being moved. It ought to be valid to rename temp_a + to its previous name, just as it would, but for the failure, have + been renamed to the target. */ + + if (have_temp_a && result) + { + int save_errno = errno; + + /* XXX: what if a new file has replaced oldname_a in the + meantime? */ + rename (temp_a, oldname_a); + errno = save_errno; + } + return result; } -- 2.39.2