From a0f1e34d101045c1f5c9df5ab861ed626f2b0b52 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Fri, 8 Aug 2025 10:32:45 +0200 Subject: [PATCH] rm: fix handling of non-UTF-8 filenames --- src/uu/rm/src/rm.rs | 20 ++++++++++---------- tests/by-util/test_rm.rs | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 10 deletions(-) diff --git a/src/uu/rm/src/rm.rs b/src/uu/rm/src/rm.rs index 9e3fa9b39..c00a94639 100644 --- a/src/uu/rm/src/rm.rs +++ b/src/uu/rm/src/rm.rs @@ -31,17 +31,17 @@ enum RmError { #[error("{}", translate!("rm-error-missing-operand", "util_name" => uucore::execution_phrase()))] MissingOperand, #[error("{}", translate!("rm-error-cannot-remove-no-such-file", "file" => _0.quote()))] - CannotRemoveNoSuchFile(String), + CannotRemoveNoSuchFile(OsString), #[error("{}", translate!("rm-error-cannot-remove-permission-denied", "file" => _0.quote()))] - CannotRemovePermissionDenied(String), + CannotRemovePermissionDenied(OsString), #[error("{}", translate!("rm-error-cannot-remove-is-directory", "file" => _0.quote()))] - CannotRemoveIsDirectory(String), + CannotRemoveIsDirectory(OsString), #[error("{}", translate!("rm-error-dangerous-recursive-operation"))] DangerousRecursiveOperation, #[error("{}", translate!("rm-error-use-no-preserve-root"))] UseNoPreserveRoot, - #[error("{}", translate!("rm-error-refusing-to-remove-directory", "path" => _0))] - RefusingToRemoveDirectory(String), + #[error("{}", translate!("rm-error-refusing-to-remove-directory", "path" => _0.to_string_lossy()))] + RefusingToRemoveDirectory(OsString), } impl UError for RmError {} @@ -366,7 +366,7 @@ pub fn remove(files: &[&OsStr], options: &Options) -> bool { } else { show_error!( "{}", - RmError::CannotRemoveNoSuchFile(filename.to_string_lossy().to_string()) + RmError::CannotRemoveNoSuchFile(filename.to_os_string()) ); true } @@ -542,7 +542,7 @@ fn handle_dir(path: &Path, options: &Options) -> bool { if path_is_current_or_parent_directory(path) { show_error!( "{}", - RmError::RefusingToRemoveDirectory(path.display().to_string()) + RmError::RefusingToRemoveDirectory(path.as_os_str().to_os_string()) ); return true; } @@ -559,7 +559,7 @@ fn handle_dir(path: &Path, options: &Options) -> bool { } else { show_error!( "{}", - RmError::CannotRemoveIsDirectory(path.to_string_lossy().to_string()) + RmError::CannotRemoveIsDirectory(path.as_os_str().to_os_string()) ); had_err = true; } @@ -580,7 +580,7 @@ fn remove_dir(path: &Path, options: &Options) -> bool { if !options.dir && !options.recursive { show_error!( "{}", - RmError::CannotRemoveIsDirectory(path.to_string_lossy().to_string()) + RmError::CannotRemoveIsDirectory(path.as_os_str().to_os_string()) ); return true; } @@ -621,7 +621,7 @@ fn remove_file(path: &Path, options: &Options) -> bool { // GNU compatibility (rm/fail-eacces.sh) show_error!( "{}", - RmError::CannotRemovePermissionDenied(path.to_string_lossy().to_string()) + RmError::CannotRemovePermissionDenied(path.as_os_str().to_os_string()) ); } else { show_error!("cannot remove {}: {e}", path.quote()); diff --git a/tests/by-util/test_rm.rs b/tests/by-util/test_rm.rs index ec7de9136..18dbec8fa 100644 --- a/tests/by-util/test_rm.rs +++ b/tests/by-util/test_rm.rs @@ -1037,3 +1037,43 @@ fn test_inaccessible_dir_recursive() { assert!(!at.dir_exists("a/unreadable")); assert!(!at.dir_exists("a")); } + +#[test] +#[cfg(target_os = "linux")] +fn test_rm_non_utf8_paths() { + use std::ffi::OsStr; + use std::os::unix::ffi::OsStrExt; + + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + + // Create a test file with non-UTF-8 bytes in the name + let non_utf8_bytes = b"test_\xFF\xFE.txt"; + let non_utf8_name = OsStr::from_bytes(non_utf8_bytes); + + // Create the actual file + at.touch(non_utf8_name); + assert!(at.file_exists(non_utf8_name)); + + // Test that rm handles non-UTF-8 file names without crashing + scene.ucmd() + .arg(non_utf8_name) + .succeeds(); + + // The file should be removed + assert!(!at.file_exists(non_utf8_name)); + + // Test with directory + let non_utf8_dir_bytes = b"test_dir_\xFF\xFE"; + let non_utf8_dir_name = OsStr::from_bytes(non_utf8_dir_bytes); + + at.mkdir(non_utf8_dir_name); + assert!(at.dir_exists(non_utf8_dir_name)); + + scene.ucmd() + .args(&["-r"]) + .arg(non_utf8_dir_name) + .succeeds(); + + assert!(!at.dir_exists(non_utf8_dir_name)); +}