Skip to content

Commit 4c599fd

Browse files
authored
Fix Windows Sys.rename regression from ocaml#12184 (ocaml#12320)
1 parent 829e32d commit 4c599fd

File tree

4 files changed

+29
-15
lines changed

4 files changed

+29
-15
lines changed

Changes

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ OCaml 5.1.0
292292
(Daniel Bünzli, review by Jeremy Yallop, Gabriel Scherer, Wiktor Kuchta,
293293
Nicolás Ojeda Bär)
294294

295-
- #12184: Sys.rename Windows fixes on directory corner cases.
295+
- #12184, #12320: Sys.rename Windows fixes on directory corner cases.
296296
(Jan Midtgaard, review by Anil Madhavapeddy)
297297

298298
* #11565: Enable -strict-formats by default. Some incorrect format

runtime/win32.c

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -779,26 +779,17 @@ CAMLexport wchar_t *caml_win32_getenv(wchar_t const *lpName)
779779

780780
int caml_win32_rename(const wchar_t * oldpath, const wchar_t * newpath)
781781
{
782-
/* First handle corner-cases not handled by MoveFileEx:
783-
- dir to empty dir - positive - should succeed
782+
/* First handle corner-case not handled by MoveFileEx:
784783
- dir to existing file - should fail */
784+
DWORD new_attribs;
785785
DWORD old_attribs = GetFileAttributes(oldpath);
786786
if ((old_attribs != INVALID_FILE_ATTRIBUTES) &&
787-
(old_attribs & FILE_ATTRIBUTE_DIRECTORY) != 0 &&
788-
(old_attribs & FILE_ATTRIBUTE_HIDDEN) == 0 &&
789-
(old_attribs & FILE_ATTRIBUTE_SYSTEM) == 0) {
790-
DWORD new_attribs = GetFileAttributes(newpath);
787+
(old_attribs & FILE_ATTRIBUTE_DIRECTORY) != 0) {
788+
new_attribs = GetFileAttributes(newpath);
791789
if ((new_attribs != INVALID_FILE_ATTRIBUTES) &&
792-
(new_attribs & FILE_ATTRIBUTE_HIDDEN) == 0 &&
793-
(new_attribs & FILE_ATTRIBUTE_SYSTEM) == 0) {
794-
if ((new_attribs & FILE_ATTRIBUTE_DIRECTORY) != 0) {
795-
/* Try to delete and fall though.
796-
RemoveDirectoryW fails on non-empty dirs as intended. */
797-
RemoveDirectoryW(newpath);
798-
} else {
790+
(new_attribs & FILE_ATTRIBUTE_DIRECTORY) == 0) {
799791
errno = ENOTDIR;
800792
return -1;
801-
}
802793
}
803794
}
804795
/* MOVEFILE_REPLACE_EXISTING: to be closer to POSIX
@@ -812,6 +803,23 @@ int caml_win32_rename(const wchar_t * oldpath, const wchar_t * newpath)
812803
MOVEFILE_COPY_ALLOWED)) {
813804
return 0;
814805
}
806+
807+
/* Another cornercase not handled by MoveFileEx:
808+
- dir to empty dir - positive - should succeed */
809+
if ((old_attribs != INVALID_FILE_ATTRIBUTES) &&
810+
(old_attribs & FILE_ATTRIBUTE_DIRECTORY) != 0 &&
811+
(new_attribs != INVALID_FILE_ATTRIBUTES) &&
812+
(new_attribs & FILE_ATTRIBUTE_DIRECTORY) != 0) {
813+
/* Try to delete: RemoveDirectoryW fails on non-empty dirs as intended.
814+
Then try again. */
815+
RemoveDirectoryW(newpath);
816+
if (MoveFileEx(oldpath, newpath,
817+
MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH |
818+
MOVEFILE_COPY_ALLOWED)) {
819+
return 0;
820+
}
821+
}
822+
815823
/* Modest attempt at mapping Win32 error codes to POSIX error codes.
816824
The __dosmaperr() function from the CRT does a better job but is
817825
generally not accessible. */

testsuite/tests/lib-sys/rename.ml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,11 @@ let _ =
8989
print_newline();
9090
safe_remove_dir "foo";
9191
safe_remove_dir "bar";
92+
print_string "Rename existing empty directory to itself: ";
93+
Sys.mkdir "foo" 0o755;
94+
testrenamedir "foo" "foo";
95+
print_newline();
96+
safe_remove_dir "foo";
9297
print_string "Rename directory to existing file: ";
9398
Sys.mkdir "foo" 0o755;
9499
writefile f2 "xyz";

testsuite/tests/lib-sys/rename.reference

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ Rename directory to a nonexisting directory: passed
66
Rename a nonexisting directory: fails as expected
77
Rename directory to a non-empty directory: fails as expected
88
Rename directory to existing empty directory: passed
9+
Rename existing empty directory to itself: source directory still exists!
910
Rename directory to existing file: fails as expected

0 commit comments

Comments
 (0)