Skip to content

Commit 5460b37

Browse files
committed
fix: preserve symlink targets in copy_file
Previously, copy_file created a symlink pointing to the source path rather than preserving the original symlink's target. Now it reads the link target and creates a symlink with that same target. Adds regression test copy_file_preserves_symlinks.
1 parent 7308dd3 commit 5460b37

File tree

2 files changed

+56
-1
lines changed

2 files changed

+56
-1
lines changed

src/dist/component/tests.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,3 +567,53 @@ fn copy_dir_preserves_symlinks() {
567567
"Dir symlink target should be preserved"
568568
);
569569
}
570+
571+
#[test]
572+
#[cfg(unix)]
573+
fn copy_file_preserves_symlinks() {
574+
// copy_file must preserve symlink targets, not create new symlinks to source
575+
use std::os::unix::fs::symlink;
576+
577+
let cx = DistContext::new(None).unwrap();
578+
let mut tx = cx.transaction();
579+
580+
let src_dir = cx.pkg_dir.path();
581+
let real_file = src_dir.join("real_file.txt");
582+
utils::write_file("", &real_file, "content").unwrap();
583+
584+
let link_file = src_dir.join("link.txt");
585+
symlink("real_file.txt", &link_file).unwrap();
586+
587+
assert!(
588+
fs::symlink_metadata(&link_file)
589+
.unwrap()
590+
.file_type()
591+
.is_symlink()
592+
);
593+
assert_eq!(
594+
fs::read_link(&link_file).unwrap().to_str().unwrap(),
595+
"real_file.txt"
596+
);
597+
598+
tx.copy_file(
599+
"test-component",
600+
PathBuf::from("copied_link.txt"),
601+
&link_file,
602+
)
603+
.unwrap();
604+
tx.commit();
605+
606+
let dest_link = cx.prefix.path().join("copied_link.txt");
607+
assert!(
608+
fs::symlink_metadata(&dest_link)
609+
.unwrap()
610+
.file_type()
611+
.is_symlink(),
612+
"Copied file should be a symlink"
613+
);
614+
assert_eq!(
615+
fs::read_link(&dest_link).unwrap().to_str().unwrap(),
616+
"real_file.txt",
617+
"Symlink target should be preserved, not point to original source"
618+
);
619+
}

src/utils/mod.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,12 @@ pub(crate) fn copy_file(src: &Path, dest: &Path) -> Result<()> {
232232
path: PathBuf::from(src),
233233
})?;
234234
if metadata.file_type().is_symlink() {
235-
symlink_file(src, dest).map(|_| ())
235+
// Read the symlink target and create a new symlink with the same target
236+
let link_target = fs::read_link(src).with_context(|| RustupError::ReadingFile {
237+
name: "symlink target for",
238+
path: PathBuf::from(src),
239+
})?;
240+
symlink_file(&link_target, dest).map(|_| ())
236241
} else {
237242
fs::copy(src, dest)
238243
.with_context(|| {

0 commit comments

Comments
 (0)