diff --git a/src/uu/touch/src/touch.rs b/src/uu/touch/src/touch.rs index 8bd483712..00d23da75 100644 --- a/src/uu/touch/src/touch.rs +++ b/src/uu/touch/src/touch.rs @@ -139,7 +139,13 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { let path = pathbuf.as_path(); - if let Err(e) = path.metadata() { + let metadata_result = if matches.get_flag(options::NO_DEREF) { + path.symlink_metadata() + } else { + path.metadata() + }; + + if let Err(e) = metadata_result { if e.kind() != std::io::ErrorKind::NotFound { return Err(e.map_err_context(|| format!("setting times of {}", filename.quote()))); } @@ -198,14 +204,21 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { } } - if matches.get_flag(options::NO_DEREF) { + // sets the file access and modification times for a file or a symbolic link. + // The filename, access time (atime), and modification time (mtime) are provided as inputs. + + // If the filename is not "-", indicating a special case for touch -h -, + // the code checks if the NO_DEREF flag is set, which means the user wants to + // set the times for a symbolic link itself, rather than the file it points to. + if filename == "-" { + filetime::set_file_times(path, atime, mtime) + } else if matches.get_flag(options::NO_DEREF) { set_symlink_file_times(path, atime, mtime) } else { filetime::set_file_times(path, atime, mtime) } .map_err_context(|| format!("setting times of {}", path.quote()))?; } - Ok(()) } @@ -305,13 +318,16 @@ pub fn uu_app() -> Command { ) } +// 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()))?; + 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()), + }; Ok(( FileTime::from_last_access_time(&metadata), diff --git a/tests/by-util/test_touch.rs b/tests/by-util/test_touch.rs index d13da0bf6..0e4eade3d 100644 --- a/tests/by-util/test_touch.rs +++ b/tests/by-util/test_touch.rs @@ -829,3 +829,27 @@ 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(); +} + +#[test] +fn test_touch_no_dereference_dangling() { + let (at, mut ucmd) = at_and_ucmd!(); + at.relative_symlink_file("nowhere", "dangling"); + + ucmd.args(&["-h", "dangling"]).succeeds(); +} + +#[test] +fn test_touch_dash() { + let (_, mut ucmd) = at_and_ucmd!(); + + ucmd.args(&["-h", "-"]).succeeds().no_stderr().no_stdout(); +} diff --git a/util/build-gnu.sh b/util/build-gnu.sh index 9035fa69b..486cf3227 100755 --- a/util/build-gnu.sh +++ b/util/build-gnu.sh @@ -67,7 +67,7 @@ if test -f gnu-built; then echo "'rm -f $(pwd)/gnu-built' to force the build" echo "Note: the customization of the tests will still happen" else - ./bootstrap + ./bootstrap --skip-po ./configure --quiet --disable-gcc-warnings #Add timeout to to protect against hangs sed -i 's|^"\$@|/usr/bin/timeout 600 "\$@|' build-aux/test-driver