From b5f8931b176088c7d304b0481346b4a4c02f63a8 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Fri, 8 Aug 2025 16:28:16 +0200 Subject: [PATCH] Fix stdbuf to handle non-UTF-8 filenames --- src/uu/chgrp/src/chgrp.rs | 3 +-- src/uu/mktemp/src/mktemp.rs | 21 +++++++++++++-------- src/uu/stdbuf/src/stdbuf.rs | 8 +++++--- tests/by-util/test_stdbuf.rs | 19 +++++++++++++++++++ 4 files changed, 38 insertions(+), 13 deletions(-) diff --git a/src/uu/chgrp/src/chgrp.rs b/src/uu/chgrp/src/chgrp.rs index f45cd33dd..07859d07d 100644 --- a/src/uu/chgrp/src/chgrp.rs +++ b/src/uu/chgrp/src/chgrp.rs @@ -6,8 +6,7 @@ // spell-checker:ignore (ToDO) COMFOLLOW Chowner RFILE RFILE's derefer dgid nonblank nonprint nonprinting use uucore::display::Quotable; -pub use uucore::entries; -use uucore::display::Quotable; +use uucore::entries; use uucore::error::{FromIo, UResult, USimpleError}; use uucore::format_usage; use uucore::perms::{GidUidOwnerFilter, IfFrom, chown_base, options}; diff --git a/src/uu/mktemp/src/mktemp.rs b/src/uu/mktemp/src/mktemp.rs index b9cb58289..98ffdadbf 100644 --- a/src/uu/mktemp/src/mktemp.rs +++ b/src/uu/mktemp/src/mktemp.rs @@ -201,12 +201,11 @@ fn find_last_contiguous_block_of_xs(s: &str) -> Option<(usize, usize)> { impl Params { fn from(options: Options) -> Result { // Convert OsString template to string for processing - let template_str = match options.template.to_str() { - Some(s) => s, - None => { - // For non-UTF-8 templates, return an error - return Err(MkTempError::InvalidTemplate(options.template.to_string_lossy().into_owned())); - } + let Some(template_str) = options.template.to_str() else { + // For non-UTF-8 templates, return an error + return Err(MkTempError::InvalidTemplate( + options.template.to_string_lossy().into_owned(), + )); }; // The template argument must end in 'X' if a suffix option is given. @@ -241,7 +240,9 @@ impl Params { .display() .to_string(); if options.treat_as_template && prefix_from_template.contains(MAIN_SEPARATOR) { - return Err(MkTempError::PrefixContainsDirSeparator(template_str.to_string())); + return Err(MkTempError::PrefixContainsDirSeparator( + template_str.to_string(), + )); } if tmpdir.is_some() && Path::new(prefix_from_template).is_absolute() { return Err(MkTempError::InvalidTemplate(template_str.to_string())); @@ -465,7 +466,11 @@ pub fn uu_app() -> Command { .help(translate!("mktemp-help-t")) .action(ArgAction::SetTrue), ) - .arg(Arg::new(ARG_TEMPLATE).num_args(..=1).value_parser(clap::value_parser!(OsString))) + .arg( + Arg::new(ARG_TEMPLATE) + .num_args(..=1) + .value_parser(clap::value_parser!(OsString)), + ) } fn dry_exec(tmpdir: &Path, prefix: &str, rand: usize, suffix: &str) -> UResult { diff --git a/src/uu/stdbuf/src/stdbuf.rs b/src/uu/stdbuf/src/stdbuf.rs index c2c6beab8..e8721d981 100644 --- a/src/uu/stdbuf/src/stdbuf.rs +++ b/src/uu/stdbuf/src/stdbuf.rs @@ -6,6 +6,7 @@ // spell-checker:ignore (ToDO) tempdir dyld dylib optgrps libstdbuf use clap::{Arg, ArgAction, ArgMatches, Command}; +use std::ffi::OsString; use std::os::unix::process::ExitStatusExt; use std::path::PathBuf; use std::process; @@ -183,9 +184,9 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { let options = ProgramOptions::try_from(&matches).map_err(|e| UUsageError::new(125, e.to_string()))?; - let mut command_values = matches.get_many::(options::COMMAND).unwrap(); + let mut command_values = matches.get_many::(options::COMMAND).unwrap(); let mut command = process::Command::new(command_values.next().unwrap()); - let command_params: Vec<&str> = command_values.map(|s| s.as_ref()).collect(); + let command_params: Vec<&OsString> = command_values.collect(); let tmp_dir = tempdir().unwrap(); let (preload_env, libstdbuf) = get_preload_env(&tmp_dir)?; @@ -269,6 +270,7 @@ pub fn uu_app() -> Command { .action(ArgAction::Append) .hide(true) .required(true) - .value_hint(clap::ValueHint::CommandName), + .value_hint(clap::ValueHint::CommandName) + .value_parser(clap::value_parser!(OsString)), ) } diff --git a/tests/by-util/test_stdbuf.rs b/tests/by-util/test_stdbuf.rs index 810e5df5d..71e368bf8 100644 --- a/tests/by-util/test_stdbuf.rs +++ b/tests/by-util/test_stdbuf.rs @@ -3,6 +3,8 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. // spell-checker:ignore dyld dylib setvbuf +#[cfg(target_os = "linux")] +use uutests::at_and_ucmd; use uutests::new_ucmd; #[cfg(not(target_os = "windows"))] use uutests::util::TestScenario; @@ -216,3 +218,20 @@ fn test_libstdbuf_preload() { "uutils echo should not show architecture mismatch" ); } + +#[cfg(target_os = "linux")] +#[test] +fn test_stdbuf_non_utf8_paths() { + use std::os::unix::ffi::OsStringExt; + + let (at, mut ucmd) = at_and_ucmd!(); + + let filename = std::ffi::OsString::from_vec(vec![0xFF, 0xFE]); + std::fs::write(at.plus(&filename), b"test content for stdbuf\n").unwrap(); + + ucmd.arg("-o0") + .arg("cat") + .arg(&filename) + .succeeds() + .stdout_is("test content for stdbuf\n"); +}