Skip to content

Commit

Permalink
mktemp: add message for directory not found
Browse files Browse the repository at this point in the history
Add special handling in `mktemp` for when the directory that will
contain the temporary file is not found. This situation now produces
the message

    mktemp: failed to create file via template 'XXX': No such file or directory

to match the behavior of GNU mktemp.
  • Loading branch information
jfinkels committed Sep 17, 2022
1 parent 689e673 commit 0764fba
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 2 deletions.
28 changes: 26 additions & 2 deletions src/uu/mktemp/src/mktemp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use uucore::format_usage;
use std::env;
use std::error::Error;
use std::fmt::Display;
use std::io::ErrorKind;
use std::iter;
use std::path::{Path, PathBuf, MAIN_SEPARATOR};

Expand Down Expand Up @@ -54,6 +55,9 @@ enum MkTempError {
SuffixContainsDirSeparator(String),
InvalidTemplate(String),
TooManyTemplates,

/// When a specified temporary directory could not be found.
NotFound(String, String),
}

impl UError for MkTempError {
Expand Down Expand Up @@ -93,6 +97,12 @@ impl Display for MkTempError {
TooManyTemplates => {
write!(f, "too many templates")
}
NotFound(template_type, s) => write!(
f,
"failed to create {} via template {}: No such file or directory",
template_type,
s.quote()
),
}
}
}
Expand Down Expand Up @@ -461,7 +471,8 @@ pub fn dry_exec(tmpdir: &str, prefix: &str, rand: usize, suffix: &str) -> UResul
///
/// # Errors
///
/// If the temporary directory could not be written to disk.
/// If the temporary directory could not be written to disk or if the
/// given directory `dir` does not exist.
fn make_temp_dir(dir: &str, prefix: &str, rand: usize, suffix: &str) -> UResult<PathBuf> {
let mut builder = Builder::new();
builder.prefix(prefix).rand_bytes(rand).suffix(suffix);
Expand All @@ -473,6 +484,12 @@ fn make_temp_dir(dir: &str, prefix: &str, rand: usize, suffix: &str) -> UResult<
fs::set_permissions(&path, fs::Permissions::from_mode(0o700))?;
Ok(path)
}
Err(e) if e.kind() == ErrorKind::NotFound => {
let filename = format!("{}{}{}", prefix, "X".repeat(rand), suffix);
let path = Path::new(dir).join(filename);
let s = path.display().to_string();
Err(MkTempError::NotFound("directory".to_string(), s).into())
}
Err(e) => Err(e.into()),
}
}
Expand All @@ -486,7 +503,8 @@ fn make_temp_dir(dir: &str, prefix: &str, rand: usize, suffix: &str) -> UResult<
///
/// # Errors
///
/// If the file could not be written to disk.
/// If the file could not be written to disk or if the directory does
/// not exist.
fn make_temp_file(dir: &str, prefix: &str, rand: usize, suffix: &str) -> UResult<PathBuf> {
let mut builder = Builder::new();
builder.prefix(prefix).rand_bytes(rand).suffix(suffix);
Expand All @@ -496,6 +514,12 @@ fn make_temp_file(dir: &str, prefix: &str, rand: usize, suffix: &str) -> UResult
Ok((_, pathbuf)) => Ok(pathbuf),
Err(e) => Err(MkTempError::PersistError(e.file.path().to_path_buf()).into()),
},
Err(e) if e.kind() == ErrorKind::NotFound => {
let filename = format!("{}{}{}", prefix, "X".repeat(rand), suffix);
let path = Path::new(dir).join(filename);
let s = path.display().to_string();
Err(MkTempError::NotFound("file".to_string(), s).into())
}
Err(e) => Err(e.into()),
}
}
Expand Down
56 changes: 56 additions & 0 deletions tests/by-util/test_mktemp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -735,3 +735,59 @@ fn test_tmpdir_env_var() {
assert_matches_template!(template, filename);
assert!(at.file_exists(filename));
}

#[test]
fn test_nonexistent_tmpdir_env_var() {
let result = new_ucmd!().env(TMPDIR, "no/such/dir").fails();
result.no_stdout();
let stderr = result.stderr_str();
#[cfg(not(windows))]
assert_eq!(stderr, "mktemp: failed to create file via template 'no/such/dir/tmp.XXXXXXXXXX': No such file or directory\n");
#[cfg(windows)]
assert!(
stderr.starts_with("mktemp: failed to create file via template")
&& stderr.ends_with("'no/such/dir/tmp.XXXXXXXXXX': No such file or directory\n")
);

let result = new_ucmd!().env(TMPDIR, "no/such/dir").arg("-d").fails();
result.no_stdout();
let stderr = result.stderr_str();
#[cfg(not(windows))]
assert_eq!(stderr, "mktemp: failed to create directory via template 'no/such/dir/tmp.XXXXXXXXXX': No such file or directory\n");
#[cfg(windows)]
assert!(
stderr.starts_with("mktemp: failed to create directory via template")
&& stderr.ends_with("'no/such/dir/tmp.XXXXXXXXXX': No such file or directory\n")
);
}

#[test]
fn test_nonexistent_dir_prefix() {
let result = new_ucmd!().arg("d/XXX").fails();
result.no_stdout();
let stderr = result.stderr_str();
#[cfg(not(windows))]
assert_eq!(
stderr,
"mktemp: failed to create file via template 'd/XXX': No such file or directory\n"
);
#[cfg(windows)]
assert!(
stderr.starts_with("mktemp: failed to create file via template")
&& stderr.ends_with("d/XXX': No such file or directory\n")
);

let result = new_ucmd!().args(&["-d", "d/XXX"]).fails();
result.no_stdout();
let stderr = result.stderr_str();
#[cfg(not(windows))]
assert_eq!(
stderr,
"mktemp: failed to create directory via template 'd/XXX': No such file or directory\n"
);
#[cfg(windows)]
assert!(
stderr.starts_with("mktemp: failed to create directory via template")
&& stderr.ends_with("d/XXX': No such file or directory\n")
);
}

0 comments on commit 0764fba

Please sign in to comment.