diff --git a/src/uu/touch/src/touch.rs b/src/uu/touch/src/touch.rs index eeb1dd13a..8d96b1b16 100644 --- a/src/uu/touch/src/touch.rs +++ b/src/uu/touch/src/touch.rs @@ -305,13 +305,21 @@ pub fn uu_app() -> Command { ) } +// Function to get metadata of the provided path +// If `follow` is true, the function will try to follow symlinks +// If `follow` is false or the symlink is broken, the function will return metadata of the symlink itself fn stat(path: &Path, follow: bool) -> UResult<(FileTime, FileTime)> { - let metadata = if follow { - fs::symlink_metadata(path) - } else { - fs::metadata(path) - } - .map_err_context(|| format!("failed to get attributes of {}", path.quote()))?; + // Try to get metadata using fs::metadata + // If the path is a symlink and `follow` is true, fs::metadata will follow the symlink + let metadata = match fs::metadata(path) { + // If successful, use the metadata + Ok(metadata) => metadata, + // If there's a NotFound error and `follow` is false, try to get metadata of the symlink itself + Err(e) if e.kind() == std::io::ErrorKind::NotFound && !follow => fs::symlink_metadata(path) + .map_err_context(|| format!("failed to get attributes of {}", path.quote()))?, + // If it's any other error, return the error + Err(e) => return Err(e.into()), + }; Ok(( FileTime::from_last_access_time(&metadata), diff --git a/tests/by-util/test_touch.rs b/tests/by-util/test_touch.rs index ae3a28e49..89a7cacaf 100644 --- a/tests/by-util/test_touch.rs +++ b/tests/by-util/test_touch.rs @@ -816,3 +816,12 @@ fn test_touch_trailing_slash_no_create() { at.relative_symlink_dir("dir2", "link2"); ucmd.args(&["-c", "link2/"]).succeeds(); } + +#[test] +fn test_touch_no_dereference_ref_dangling() { + let (at, mut ucmd) = at_and_ucmd!(); + at.touch("file"); + at.relative_symlink_file("nowhere", "dangling"); + + ucmd.args(&["-h", "-r", "dangling", "file"]).succeeds(); +}