Skip to content

Commit 42591a4

Browse files
committed
Auto merge of rust-lang#134786 - ChrisDenton:fix-rename-symlink, r=tgross35
Fix renaming symlinks on Windows Previously we only detected mount points and not other types of links when determining reparse point behaviour. Also added some tests to avoid this regressing again in the future.
2 parents 207ed1b + 54b130a commit 42591a4

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)