Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion src/uu/touch/src/touch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use clap::{crate_version, Arg, ArgAction, ArgGroup, ArgMatches, Command};
use filetime::{set_file_times, set_symlink_file_times, FileTime};
use std::ffi::OsString;
use std::fs::{self, File};
use std::io::{Error, ErrorKind};
use std::path::{Path, PathBuf};
use uucore::display::Quotable;
use uucore::error::{FromIo, UResult, USimpleError};
Expand Down Expand Up @@ -126,7 +127,23 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
}

if let Err(e) = File::create(path) {
show!(e.map_err_context(|| format!("cannot touch {}", path.quote())));
// we need to check if the path is the path to a directory (ends with a separator)
// we can't use File::create to create a directory
// we cannot use path.is_dir() because it calls fs::metadata which we already called
// when stable, we can change to use e.kind() == std::io::ErrorKind::IsADirectory
let is_directory = if let Some(last_char) = path.to_string_lossy().chars().last() {
last_char == std::path::MAIN_SEPARATOR
} else {
false
};
if is_directory {
let custom_err = Error::new(ErrorKind::Other, "No such file or directory");
return Err(
custom_err.map_err_context(|| format!("cannot touch {}", filename.quote()))
);
} else {
show!(e.map_err_context(|| format!("cannot touch {}", path.quote())));
}
continue;
};

Expand Down
15 changes: 14 additions & 1 deletion tests/by-util/test_touch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -689,10 +689,23 @@ fn test_touch_system_fails() {
}

#[test]
#[cfg(not(target_os = "windows"))]
fn test_touch_trailing_slash() {
let (_at, mut ucmd) = at_and_ucmd!();
let file = "no-file/";
ucmd.args(&[file]).fails();
ucmd.args(&[file]).fails().stderr_only(format!(
"touch: cannot touch '{file}': No such file or directory\n"
));
}

#[test]
#[cfg(target_os = "windows")]
fn test_touch_trailing_slash_windows() {
let (_at, mut ucmd) = at_and_ucmd!();
let file = "no-file/";
ucmd.args(&[file]).fails().stderr_only(format!(
"touch: cannot touch '{file}': The filename, directory name, or volume label syntax is incorrect.\n"
));
}

#[test]
Expand Down