diff --git a/src/uu/ln/src/ln.rs b/src/uu/ln/src/ln.rs index b89a922bfe7..ad7322651ea 100644 --- a/src/uu/ln/src/ln.rs +++ b/src/uu/ln/src/ln.rs @@ -393,12 +393,16 @@ fn link_files_in_dir(files: &[PathBuf], target_dir: &Path, settings: &Settings) } fn relative_path<'a>(src: &'a Path, dst: &Path) -> Cow<'a, Path> { - if let Ok(src_abs) = canonicalize(src, MissingHandling::Missing, ResolveMode::Physical) { - if let Ok(dst_abs) = canonicalize( - dst.parent().unwrap(), - MissingHandling::Missing, - ResolveMode::Physical, - ) { + // `dst.parent()` is None for a destination with no parent (`/`, `""`, or a + // bare Windows prefix). Fall through to the non-relative `src` rather than + // unwrapping it; the caller then reports the usual error. + if let (Ok(src_abs), Some(dst_parent)) = ( + canonicalize(src, MissingHandling::Missing, ResolveMode::Physical), + dst.parent(), + ) { + if let Ok(dst_abs) = + canonicalize(dst_parent, MissingHandling::Missing, ResolveMode::Physical) + { return make_path_relative_to(src_abs, dst_abs).into(); } } diff --git a/tests/by-util/test_ln.rs b/tests/by-util/test_ln.rs index 9d8bfdf995b..fcda733f1cc 100644 --- a/tests/by-util/test_ln.rs +++ b/tests/by-util/test_ln.rs @@ -745,6 +745,13 @@ fn test_relative_requires_symbolic() { new_ucmd!().args(&["-r", "foo", "bar"]).fails(); } +#[test] +fn test_relative_target_with_no_parent() { + let (at, mut ucmd) = at_and_ucmd!(); + at.write("src", "data"); + ucmd.args(&["-rsfT", "src", ""]).fails_with_code(1); +} + #[test] fn test_relative_dst_already_symlink() { let (at, mut ucmd) = at_and_ucmd!();