chmod/readlink/du: improve non-UTF-8 filename handling and code style

This commit is contained in:
Sylvestre Ledru
2025-08-14 10:52:24 +02:00
parent 581e752598
commit f15dac138e
10 changed files with 70 additions and 60 deletions
+18 -5
View File
@@ -7,9 +7,14 @@ use clap::{Arg, ArgAction, ArgMatches, Command, builder::PossibleValue};
use glob::Pattern;
use std::collections::HashSet;
use std::env;
#[cfg(unix)]
use std::ffi::OsStr;
use std::ffi::OsString;
use std::fs::Metadata;
use std::fs::{self, DirEntry, File};
use std::io::{BufRead, BufReader, stdout};
#[cfg(unix)]
use std::os::unix::ffi::OsStrExt;
#[cfg(not(windows))]
use std::os::unix::fs::MetadataExt;
#[cfg(windows)]
@@ -568,6 +573,9 @@ fn read_files_from(file_name: &str) -> Result<Vec<PathBuf>, std::io::Error> {
);
set_exit_code(1);
} else {
#[cfg(unix)]
let p = PathBuf::from(OsStr::from_bytes(&path));
#[cfg(windows)]
let p = PathBuf::from(String::from_utf8_lossy(&path).to_string());
if !paths.contains(&p) {
paths.push(p);
@@ -594,21 +602,24 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
summarize,
)?;
let files = if let Some(file_from) = matches.get_one::<String>(options::FILES0_FROM) {
if file_from == "-" && matches.get_one::<String>(options::FILE).is_some() {
let files = if let Some(file_from) = matches.get_one::<OsString>(options::FILES0_FROM) {
if file_from.to_string_lossy() == "-"
&& matches.get_one::<OsString>(options::FILE).is_some()
{
return Err(std::io::Error::other(
translate!("du-error-extra-operand-with-files0-from",
"file" => matches
.get_one::<String>(options::FILE)
.get_one::<OsString>(options::FILE)
.unwrap()
.to_string_lossy()
.quote()
),
)
.into());
}
read_files_from(file_from)?
} else if let Some(files) = matches.get_many::<String>(options::FILE) {
read_files_from(&file_from.to_string_lossy())?
} else if let Some(files) = matches.get_many::<OsString>(options::FILE) {
let files = files.map(PathBuf::from);
if count_links {
files.collect()
@@ -984,6 +995,7 @@ pub fn uu_app() -> Command {
.long("files0-from")
.value_name("FILE")
.value_hint(clap::ValueHint::FilePath)
.value_parser(clap::value_parser!(OsString))
.help(translate!("du-help-files0-from"))
.action(ArgAction::Append),
)
@@ -1010,6 +1022,7 @@ pub fn uu_app() -> Command {
Arg::new(options::FILE)
.hide(true)
.value_hint(clap::ValueHint::AnyPath)
.value_parser(clap::value_parser!(OsString))
.action(ArgAction::Append),
)
}
+2 -2
View File
@@ -81,9 +81,9 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
for f in &files {
let p = f;
let path_result = if res_mode == ResolveMode::None {
fs::read_link(&p)
fs::read_link(p)
} else {
canonicalize(&p, can_mode, res_mode)
canonicalize(p, can_mode, res_mode)
};
match path_result {
+5 -5
View File
@@ -14,9 +14,9 @@ use std::process::Stdio;
use uutests::at_and_ucmd;
use uutests::new_ucmd;
use uutests::util::TestScenario;
use uutests::util_name;
#[cfg(not(windows))]
use uutests::util::vec_of_size;
use uutests::util_name;
#[test]
fn test_output_simple() {
@@ -755,17 +755,17 @@ fn test_cat_non_utf8_paths() {
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 with some content
std::fs::write(at.plus(non_utf8_name), "Hello, non-UTF-8 world!\n").unwrap();
// Test that cat handles non-UTF-8 file names without crashing
let result = scene.ucmd().arg(non_utf8_name).succeeds();
// The result should contain the file content
let output = result.stdout_str_lossy();
assert_eq!(output, "Hello, non-UTF-8 world!\n");
+7 -5
View File
@@ -867,17 +867,17 @@ fn test_head_non_utf8_paths() {
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 with some content
std::fs::write(at.plus(non_utf8_name), "line1\nline2\nline3\n").unwrap();
// Test that head handles non-UTF-8 file names without crashing
let result = scene.ucmd().arg(non_utf8_name).succeeds();
// The result should contain the file content
let output = result.stdout_str_lossy();
assert!(output.contains("line1"));
@@ -885,10 +885,12 @@ fn test_head_non_utf8_paths() {
assert!(output.contains("line3"));
// Test with line count argument
scene.ucmd()
scene
.ucmd()
.args(&["-n", "2"])
.arg(non_utf8_name)
.succeeds()
.stdout_contains("line1")
.stdout_contains("line2");
}
// Test that head handles non-UTF-8 file names without crashing
+11 -9
View File
@@ -852,36 +852,38 @@ fn test_ln_non_utf8_paths() {
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);
let non_utf8_link_bytes = b"link_\xFF\xFE.txt";
let non_utf8_link_name = OsStr::from_bytes(non_utf8_link_bytes);
// Create the actual file
at.touch(non_utf8_name);
// Test creating a hard link with non-UTF-8 file names
scene.ucmd()
scene
.ucmd()
.arg(non_utf8_name)
.arg(non_utf8_link_name)
.succeeds();
// Both files should exist
assert!(at.file_exists(non_utf8_name));
assert!(at.file_exists(non_utf8_link_name));
// Test creating a symbolic link with non-UTF-8 file names
let symlink_bytes = b"symlink_\xFF\xFE.txt";
let symlink_name = OsStr::from_bytes(symlink_bytes);
scene.ucmd()
scene
.ucmd()
.args(&["-s"])
.arg(non_utf8_name)
.arg(symlink_name)
.succeeds();
// Check if symlink was created successfully
let symlink_path = at.plus(symlink_name);
assert!(symlink_path.is_symlink());
+4 -4
View File
@@ -382,18 +382,18 @@ fn test_readlink_non_utf8_paths() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
// Create a target file and a symlink with non-UTF-8 bytes in the name
at.touch("target_file");
let non_utf8_bytes = b"symlink_\xFF\xFE";
let non_utf8_name = OsStr::from_bytes(non_utf8_bytes);
// Create symlink using std::os::unix::fs::symlink
std::os::unix::fs::symlink(at.plus_as_string("target_file"), at.plus(non_utf8_name)).unwrap();
// Test that readlink handles non-UTF-8 symlink names without crashing
let result = scene.ucmd().arg(non_utf8_name).succeeds();
// The result should contain the target path
let output = result.stdout_str_lossy();
assert!(output.contains("target_file"));
+4 -4
View File
@@ -473,17 +473,17 @@ fn test_realpath_non_utf8_paths() {
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);
// Test that realpath handles non-UTF-8 paths without crashing
let result = scene.ucmd().arg(non_utf8_name).succeeds();
// The result should contain the non-UTF-8 bytes
let output = result.stdout_str_lossy();
assert!(output.contains("test_"));
+10 -15
View File
@@ -1046,34 +1046,29 @@ fn test_rm_non_utf8_paths() {
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();
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();
scene.ucmd().args(&["-r"]).arg(non_utf8_dir_name).succeeds();
assert!(!at.dir_exists(non_utf8_dir_name));
}
+4 -4
View File
@@ -1022,17 +1022,17 @@ fn test_touch_non_utf8_paths() {
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);
// Test that touch handles non-UTF-8 file names without crashing
let result = scene.ucmd().arg(non_utf8_name).succeeds();
// Verify no output and file was created
result.no_output();
// Check that the file was created (using the raw path)
assert!(std::fs::metadata(at.plus(non_utf8_name)).is_ok());
}
+5 -7
View File
@@ -86,20 +86,18 @@ fn test_unlink_non_utf8_paths() {
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 unlink handles non-UTF-8 file names without crashing
scene.ucmd()
.arg(non_utf8_name)
.succeeds();
scene.ucmd().arg(non_utf8_name).succeeds();
// The file should be removed
assert!(!at.file_exists(non_utf8_name));
}