From 6c152ffa96584b0c716e6c4234a1d16f04aa7caf Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Fri, 7 Aug 2026 23:12:00 +0200 Subject: [PATCH] mktemp: remove the temp file when its name cannot be printed Should make test mktemp/write-error.sh pass --- src/uu/mktemp/locales/en-US.ftl | 1 - src/uu/mktemp/locales/fr-FR.ftl | 1 - src/uu/mktemp/src/mktemp.rs | 18 +++++++++++++--- tests/by-util/test_mktemp.rs | 38 +++++++++++++++++++++++++++++++++ 4 files changed, 53 insertions(+), 5 deletions(-) diff --git a/src/uu/mktemp/locales/en-US.ftl b/src/uu/mktemp/locales/en-US.ftl index 7f48c4940c3..10c10f03740 100644 --- a/src/uu/mktemp/locales/en-US.ftl +++ b/src/uu/mktemp/locales/en-US.ftl @@ -19,7 +19,6 @@ mktemp-error-suffix-contains-separator = invalid suffix { $suffix }, contains di mktemp-error-invalid-template = invalid template, { $template }; with --tmpdir, it may not be absolute mktemp-error-too-many-templates = too many templates mktemp-error-not-found = failed to create { $template_type } via template { $template }: No such file or directory -mktemp-error-failed-print = failed to print directory name # Template types mktemp-template-type-directory = directory diff --git a/src/uu/mktemp/locales/fr-FR.ftl b/src/uu/mktemp/locales/fr-FR.ftl index 4bfe550a6c4..2a0eff74901 100644 --- a/src/uu/mktemp/locales/fr-FR.ftl +++ b/src/uu/mktemp/locales/fr-FR.ftl @@ -19,7 +19,6 @@ mktemp-error-suffix-contains-separator = suffixe invalide { $suffix }, contient mktemp-error-invalid-template = modèle invalide, { $template } ; avec --tmpdir, il ne peut pas être absolu mktemp-error-too-many-templates = trop de modèles mktemp-error-not-found = échec de la création de { $template_type } via le modèle { $template } : Aucun fichier ou répertoire de ce type -mktemp-error-failed-print = échec de l'affichage du nom de répertoire # Types de modèle mktemp-template-type-directory = répertoire diff --git a/src/uu/mktemp/src/mktemp.rs b/src/uu/mktemp/src/mktemp.rs index 1f872714f09..e6e1ebda5a6 100644 --- a/src/uu/mktemp/src/mktemp.rs +++ b/src/uu/mktemp/src/mktemp.rs @@ -14,12 +14,11 @@ use uucore::translate; use std::env; use std::ffi::{OsStr, OsString}; +use std::fs; use std::io::ErrorKind; use std::iter; use std::path::{MAIN_SEPARATOR, Path, PathBuf}; -#[cfg(unix)] -use std::fs; #[cfg(unix)] use std::os::unix::prelude::PermissionsExt; @@ -435,7 +434,20 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { } else { res }; - println_verbatim(res?).map_err_context(|| translate!("mktemp-error-failed-print")) + let path = res?; + if let Err(e) = println_verbatim(&path) { + // The caller never learns the name, so leaving the file behind would + // litter the temporary directory with something nothing can clean up. + if !dry_run { + let _ = if make_dir { + fs::remove_dir(&path) + } else { + fs::remove_file(&path) + }; + } + return Err(e).map_err_context(|| translate!("common-write-error")); + } + Ok(()) } pub fn uu_app() -> Command { diff --git a/tests/by-util/test_mktemp.rs b/tests/by-util/test_mktemp.rs index 0fa2896bb6f..c863556df28 100644 --- a/tests/by-util/test_mktemp.rs +++ b/tests/by-util/test_mktemp.rs @@ -1209,3 +1209,41 @@ fn test_mktemp_hidden_file_single_dot() { template_name.len() ); } + +// When the name cannot be printed the caller never learns it, so the file must +// not be left behind for nothing to clean up. +#[test] +#[cfg(target_os = "linux")] +fn test_write_error_removes_the_temporary_file() { + let (at, mut ucmd) = at_and_ucmd!(); + ucmd.arg("keep-none-XXXX") + .set_stdout(std::fs::File::create("/dev/full").unwrap()) + .fails() + .stderr_is("mktemp: write error: No space left on device\n"); + + let leftovers: Vec<_> = std::fs::read_dir(at.as_string()) + .unwrap() + .filter_map(Result::ok) + .map(|entry| entry.file_name().to_string_lossy().into_owned()) + .filter(|name| name.starts_with("keep-none-")) + .collect(); + assert!(leftovers.is_empty(), "left behind: {leftovers:?}"); +} + +#[test] +#[cfg(target_os = "linux")] +fn test_write_error_removes_the_temporary_directory() { + let (at, mut ucmd) = at_and_ucmd!(); + ucmd.args(&["-d", "keep-none-XXXX"]) + .set_stdout(std::fs::File::create("/dev/full").unwrap()) + .fails() + .stderr_is("mktemp: write error: No space left on device\n"); + + let leftovers: Vec<_> = std::fs::read_dir(at.as_string()) + .unwrap() + .filter_map(Result::ok) + .map(|entry| entry.file_name().to_string_lossy().into_owned()) + .filter(|name| name.starts_with("keep-none-")) + .collect(); + assert!(leftovers.is_empty(), "left behind: {leftovers:?}"); +}