From 2f46c0235b5740e4a8c3b1c864e6d609535c58ce Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Tue, 6 Feb 2024 16:28:12 -0500 Subject: [PATCH 1/3] Add tests that stat symlinks --- tests/by-util/test_touch.rs | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/tests/by-util/test_touch.rs b/tests/by-util/test_touch.rs index 7b659fc5155..cf0b28045a2 100644 --- a/tests/by-util/test_touch.rs +++ b/tests/by-util/test_touch.rs @@ -5,7 +5,7 @@ // spell-checker:ignore (formats) cymdhm cymdhms mdhm mdhms ymdhm ymdhms datetime mktime use crate::common::util::{AtPath, TestScenario}; -use filetime::{self, FileTime}; +use filetime::{self, set_symlink_file_times, FileTime}; use std::fs::remove_file; use std::path::PathBuf; @@ -856,3 +856,38 @@ fn test_touch_invalid_date_format() { .fails() .stderr_contains("touch: invalid date format ‘+1000000000000 years’"); } + +#[test] +fn test_touch_symlink_with_no_deref() { + let (at, mut ucmd) = at_and_ucmd!(); + let target = "foo.txt"; + let symlink = "bar.txt"; + let time = FileTime::from_unix_time(123, 0); + + at.touch(target); + at.relative_symlink_file(target, symlink); + set_symlink_file_times(at.plus(symlink), time, time).unwrap(); + + ucmd.args(&["-a", "--no-dereference", symlink]).succeeds(); + // Modification time shouldn't be set to the destination's modification time + assert_eq!(time, get_symlink_times(&at, symlink).1); +} + +#[test] +fn test_touch_reference_symlink_with_no_deref() { + let (at, mut ucmd) = at_and_ucmd!(); + let target = "foo.txt"; + let symlink = "bar.txt"; + let arg = "baz.txt"; + let time = FileTime::from_unix_time(123, 0); + + at.touch(target); + at.relative_symlink_file(target, symlink); + set_symlink_file_times(at.plus(symlink), time, time).unwrap(); + at.touch(arg); + + ucmd.args(&["--reference", symlink, "--no-dereference", arg]) + .succeeds(); + // Times should be taken from the symlink, not the destination + assert_eq!((time, time), get_symlink_times(&at, arg)); +} From 203c536512edbfa98530ace5bad059edc746ec56 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Tue, 6 Feb 2024 16:28:34 -0500 Subject: [PATCH 2/3] Check follow first in stat --- src/uu/touch/src/touch.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/uu/touch/src/touch.rs b/src/uu/touch/src/touch.rs index ebdff8d2116..9112d5e07c2 100644 --- a/src/uu/touch/src/touch.rs +++ b/src/uu/touch/src/touch.rs @@ -326,12 +326,12 @@ fn update_times( // 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 = match fs::metadata(path) { - Ok(metadata) => metadata, - 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()))?, - Err(e) => return Err(e.into()), - }; + let metadata = if follow { + fs::metadata(path).or_else(|_| fs::symlink_metadata(path)) + } else { + fs::symlink_metadata(path) + } + .map_err_context(|| format!("failed to get attributes of {}", path.quote()))?; Ok(( FileTime::from_last_access_time(&metadata), From 1e066e7ed10eb9dd79db1174438424bb25e806e8 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Tue, 6 Feb 2024 16:46:01 -0500 Subject: [PATCH 3/3] Don't run tests on FreeBSD It would be possible to get them to run on FreeBSD by avoiding get_symlink_times, but the behavior we're testing is not platform-specific, so it's fine to not test it on FreeBSD. --- tests/by-util/test_touch.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/by-util/test_touch.rs b/tests/by-util/test_touch.rs index cf0b28045a2..2e486add9a6 100644 --- a/tests/by-util/test_touch.rs +++ b/tests/by-util/test_touch.rs @@ -858,6 +858,7 @@ fn test_touch_invalid_date_format() { } #[test] +#[cfg(not(target_os = "freebsd"))] fn test_touch_symlink_with_no_deref() { let (at, mut ucmd) = at_and_ucmd!(); let target = "foo.txt"; @@ -874,6 +875,7 @@ fn test_touch_symlink_with_no_deref() { } #[test] +#[cfg(not(target_os = "freebsd"))] fn test_touch_reference_symlink_with_no_deref() { let (at, mut ucmd) = at_and_ucmd!(); let target = "foo.txt";