1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 11:37:44 +00:00

touch: Respect -h when getting metadata (#5951)

* Add tests that stat symlinks

* Check follow first in stat

* 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.

---------

Co-authored-by: Sylvestre Ledru <sylvestre@debian.org>
This commit is contained in:
Yash Thakur 2024-03-10 03:05:59 -04:00 committed by GitHub
parent 8c7940260b
commit d11d595fda
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 44 additions and 7 deletions

View file

@ -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),

View file

@ -5,7 +5,7 @@
// spell-checker:ignore (formats) cymdhm cymdhms mdhm mdhms ymdhm ymdhms datetime mktime
use crate::common::util::{AtPath, TestScenario};
use filetime::FileTime;
use filetime::{self, set_symlink_file_times, FileTime};
use std::fs::remove_file;
use std::path::PathBuf;
@ -854,3 +854,40 @@ fn test_touch_invalid_date_format() {
.fails()
.stderr_contains("touch: invalid date format '+1000000000000 years'");
}
#[test]
#[cfg(not(target_os = "freebsd"))]
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]
#[cfg(not(target_os = "freebsd"))]
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));
}