Skip to content

Commit 54b130a

Browse files
committed
Fix renaming symlinks on Windows
Previously we only detected mount points and not other types of links when determining reparse point behaviour.
1 parent a25032c commit 54b130a

File tree

2 files changed

+39
-7
lines changed

2 files changed

+39
-7
lines changed

library/std/src/fs/tests.rs

+29
Original file line numberDiff line numberDiff line change
@@ -1953,3 +1953,32 @@ fn test_rename_directory_to_non_empty_directory() {
19531953

19541954
error!(fs::rename(source_path, target_path), 145); // ERROR_DIR_NOT_EMPTY
19551955
}
1956+
1957+
#[test]
1958+
fn test_rename_symlink() {
1959+
let tmpdir = tmpdir();
1960+
let original = tmpdir.join("original");
1961+
let dest = tmpdir.join("dest");
1962+
let not_exist = Path::new("does not exist");
1963+
1964+
symlink_file(not_exist, &original).unwrap();
1965+
fs::rename(&original, &dest).unwrap();
1966+
// Make sure that renaming `original` to `dest` preserves the symlink.
1967+
assert_eq!(fs::read_link(&dest).unwrap().as_path(), not_exist);
1968+
}
1969+
1970+
#[test]
1971+
#[cfg(windows)]
1972+
fn test_rename_junction() {
1973+
let tmpdir = tmpdir();
1974+
let original = tmpdir.join("original");
1975+
let dest = tmpdir.join("dest");
1976+
let not_exist = Path::new("does not exist");
1977+
1978+
junction_point(&not_exist, &original).unwrap();
1979+
fs::rename(&original, &dest).unwrap();
1980+
1981+
// Make sure that renaming `original` to `dest` preserves the junction point.
1982+
// Junction links are always absolute so we just check the file name is correct.
1983+
assert_eq!(fs::read_link(&dest).unwrap().file_name(), Some(not_exist.as_os_str()));
1984+
}

library/std/src/sys/pal/windows/fs.rs

+10-7
Original file line numberDiff line numberDiff line change
@@ -1295,15 +1295,18 @@ pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
12951295
} else {
12961296
// SAFETY: The struct has been initialized by GetFileInformationByHandleEx
12971297
let file_attribute_tag_info = unsafe { file_attribute_tag_info.assume_init() };
1298+
let file_type = FileType::new(
1299+
file_attribute_tag_info.FileAttributes,
1300+
file_attribute_tag_info.ReparseTag,
1301+
);
12981302

1299-
if file_attribute_tag_info.FileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0
1300-
&& file_attribute_tag_info.ReparseTag != c::IO_REPARSE_TAG_MOUNT_POINT
1301-
{
1302-
// The file is not a mount point: Reopen the file without inhibiting reparse point behavior.
1303-
None
1304-
} else {
1305-
// The file is a mount point: Don't reopen the file so that the mount point gets renamed.
1303+
if file_type.is_symlink() {
1304+
// The file is a mount point, junction point or symlink so
1305+
// don't reopen the file so that the link gets renamed.
13061306
Some(Ok(handle))
1307+
} else {
1308+
// Otherwise reopen the file without inhibiting reparse point behavior.
1309+
None
13071310
}
13081311
}
13091312
}

0 commit comments

Comments
 (0)