diff --git a/src/uu/mktemp/src/mktemp.rs b/src/uu/mktemp/src/mktemp.rs index e145a393354..6c899c452f5 100644 --- a/src/uu/mktemp/src/mktemp.rs +++ b/src/uu/mktemp/src/mktemp.rs @@ -150,7 +150,26 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { (template, tmp) } } else { - (template, PathBuf::from(tmpdir)) + // In this case, the command was `mktemp --tmpdir=foo XXX`, so + // we need to parse out the parent directory and the filename + // from the argument `XXX`, since it may be include path + // separators. + + // The template cannot be an absolute path. For example, `mktemp + // --tmpdir=a /XXX` is not allowed. + if path.is_absolute() { + return Err(MkTempError::InvalidTemplate(template.into()).into()); + } + + // `mktemp --tmpdir=a b/cXXX` becomes `a`, `b`, and `cXXX`. + // Then the `tmpdir` is `a/b` and `template` is `cXXX`. + let subdirectory = path.parent().unwrap_or_else(|| Path::new("")); + let tmpdir = PathBuf::from(tmpdir).join(subdirectory); + let template = match path.file_name() { + None => template, + Some(s) => s.to_str().unwrap(), + }; + (template, tmpdir) }; let make_dir = matches.is_present(OPT_DIRECTORY); diff --git a/tests/by-util/test_mktemp.rs b/tests/by-util/test_mktemp.rs index 86c4ba4dbe4..8d1b5d02c0e 100644 --- a/tests/by-util/test_mktemp.rs +++ b/tests/by-util/test_mktemp.rs @@ -27,6 +27,18 @@ const TMPDIR: &str = "TMPDIR"; #[cfg(windows)] const TMPDIR: &str = "TMP"; +/// An assertion that uses [`matches_template`] and adds a helpful error message. +macro_rules! assert_matches_template { + ($template:expr, $s:expr) => {{ + assert!( + matches_template($template, $s), + "\"{}\" != \"{}\"", + $template, + $s + ); + }}; +} + #[test] fn test_mktemp_mktemp() { let scene = TestScenario::new(util_name!()); @@ -417,6 +429,21 @@ fn test_mktemp_directory_tmpdir() { assert!(PathBuf::from(result.stdout_str().trim()).is_dir()); } +/// Test for combining `--tmpdir` and a template with a subdirectory. +#[test] +fn test_tmpdir_template_has_subdirectory() { + let (at, mut ucmd) = at_and_ucmd!(); + at.mkdir("a"); + #[cfg(not(windows))] + let (template, joined) = ("a/bXXXX", "./a/bXXXX"); + #[cfg(windows)] + let (template, joined) = (r"a\bXXXX", r".\a\bXXXX"); + let result = ucmd.args(&["--tmpdir=.", template]).succeeds(); + let filename = result.no_stderr().stdout_str().trim_end(); + assert_matches_template!(joined, filename); + assert!(at.file_exists(filename)); +} + /// Test that an absolute path is disallowed when --tmpdir is provided. #[test] fn test_tmpdir_absolute_path() { @@ -466,18 +493,6 @@ fn matches_template(template: &str, s: &str) -> bool { true } -/// An assertion that uses [`matches_template`] and adds a helpful error message. -macro_rules! assert_matches_template { - ($template:expr, $s:expr) => {{ - assert!( - matches_template($template, $s), - "\"{}\" != \"{}\"", - $template, - $s - ); - }}; -} - /// Test that the file is created in the directory given by the template. #[test] fn test_respect_template() {