mirror of
https://github.com/uutils/coreutils.git
synced 2026-06-10 15:48:22 -07:00
Fix ptx to handle non-UTF-8 filenames
This commit is contained in:
@@ -17,23 +17,87 @@ use std::path::PathBuf;
|
||||
use uufuzz::{run_gnu_cmd, CommandResult};
|
||||
// Programs that typically take file/path arguments and should be tested
|
||||
static PATH_PROGRAMS: &[&str] = &[
|
||||
// Core file operations
|
||||
"cat", "cp", "mv", "rm", "ln", "link", "unlink", "touch", "truncate",
|
||||
// Directory operations
|
||||
"ls", "mkdir", "rmdir", "du", "stat", "mktemp",
|
||||
"cat",
|
||||
"cp",
|
||||
"mv",
|
||||
"rm",
|
||||
"ln",
|
||||
"link",
|
||||
"unlink",
|
||||
"touch",
|
||||
// Path operations
|
||||
"basename", "dirname", "readlink", "realpath", "pathchk",
|
||||
// File content operations
|
||||
"head", "tail", "tee", "more", "od", "wc", "cksum", "sum",
|
||||
"ls", "mkdir", "rmdir", "du", "stat", "mktemp", "df", // Path operations
|
||||
"ls",
|
||||
"mkdir",
|
||||
"rmdir",
|
||||
"du",
|
||||
"stat",
|
||||
"df",
|
||||
"df", // Path operations
|
||||
"basename",
|
||||
"dirname",
|
||||
"readlink",
|
||||
"realpath",
|
||||
"pathchk",
|
||||
// File processing
|
||||
"sort", "uniq", "split", "csplit", "cut", "tr", "shred",
|
||||
// File permissions/ownership
|
||||
"chmod", "chown", "chgrp", "install",
|
||||
"head", "tail", "tee", "more", "od", "wc", "cksum", "sum", "nl", "tac", // File processing
|
||||
"head",
|
||||
"tail",
|
||||
"tee",
|
||||
"more",
|
||||
"od",
|
||||
"wc",
|
||||
"cksum",
|
||||
"sum",
|
||||
"tac",
|
||||
"tac", // File processing
|
||||
"sort",
|
||||
"uniq",
|
||||
"split",
|
||||
"csplit",
|
||||
"cut",
|
||||
"tr",
|
||||
"shred",
|
||||
"shuf",
|
||||
"ptx",
|
||||
// Text processing with files
|
||||
"comm", "join", "paste", "pr", "fmt", "fold", "expand", "unexpand",
|
||||
// Directory listing variants
|
||||
"chmod", "chown", "chgrp", "install", "chcon", "runcon", // Text processing with files
|
||||
"chmod",
|
||||
"chown",
|
||||
"chgrp",
|
||||
"install",
|
||||
"runcon",
|
||||
"runcon", // Text processing with files
|
||||
"comm",
|
||||
"join",
|
||||
"paste",
|
||||
"pr",
|
||||
"fmt",
|
||||
"fold",
|
||||
"expand",
|
||||
"dir", "vdir",
|
||||
"dir",
|
||||
"mkfifo", "mknod",
|
||||
"mkfifo",
|
||||
"mknod",
|
||||
// File I/O utilities
|
||||
// File I/O utilities
|
||||
"dd",
|
||||
"sync",
|
||||
"stdbuf",
|
||||
"dircolors",
|
||||
"base32", "base64", "basenc",
|
||||
"base32",
|
||||
"base64",
|
||||
"stty", "tty",
|
||||
"stty",
|
||||
"env", "nohup", "nice", "timeout",
|
||||
"env",
|
||||
"nohup",
|
||||
"nice",
|
||||
];
|
||||
"timeout",
|
||||
|
||||
fn generate_non_utf8_bytes() -> Vec<u8> {
|
||||
let mut rng = rand::rng();
|
||||
@@ -94,7 +158,8 @@ fn test_program_with_non_utf8_path(program: &str, path: &PathBuf) -> CommandResu
|
||||
let path_os = path.as_os_str();
|
||||
|
||||
// Use the locally built uutils binary instead of system PATH
|
||||
let local_binary = "/home/sylvestre/dev/debian/coreutils.disable-loca/target/debug/coreutils";
|
||||
let local_binary = std::env::var("CARGO_BIN_FILE_COREUTILS")
|
||||
.unwrap_or_else(|_| "target/release/coreutils".to_string());
|
||||
|
||||
// Build appropriate arguments for each program
|
||||
let local_args = match program {
|
||||
@@ -114,6 +179,22 @@ fn test_program_with_non_utf8_path(program: &str, path: &PathBuf) -> CommandResu
|
||||
OsString::from("root"),
|
||||
path_os.to_owned(),
|
||||
],
|
||||
"chcon" => vec![
|
||||
OsString::from(program),
|
||||
OsString::from("system_u:object_r:admin_home_t:s0"),
|
||||
path_os.to_owned(),
|
||||
],
|
||||
"runcon" => {
|
||||
let coreutils_binary = std::env::var("CARGO_BIN_FILE_COREUTILS")
|
||||
.unwrap_or_else(|_| "target/release/coreutils".to_string());
|
||||
vec![
|
||||
OsString::from(program),
|
||||
OsString::from("system_u:object_r:admin_home_t:s0"),
|
||||
OsString::from(coreutils_binary),
|
||||
OsString::from("cat"),
|
||||
path_os.to_owned(),
|
||||
]
|
||||
},
|
||||
// Programs that need source and destination
|
||||
"cp" | "mv" | "ln" | "link" => {
|
||||
let dest_path = path.with_extension("dest");
|
||||
@@ -122,7 +203,7 @@ fn test_program_with_non_utf8_path(program: &str, path: &PathBuf) -> CommandResu
|
||||
path_os.to_owned(),
|
||||
dest_path.as_os_str().to_owned(),
|
||||
]
|
||||
},
|
||||
}
|
||||
"install" => {
|
||||
let dest_path = path.with_extension("dest");
|
||||
vec![
|
||||
@@ -130,7 +211,7 @@ fn test_program_with_non_utf8_path(program: &str, path: &PathBuf) -> CommandResu
|
||||
path_os.to_owned(),
|
||||
dest_path.as_os_str().to_owned(),
|
||||
]
|
||||
},
|
||||
}
|
||||
// Programs that need size/truncate operations
|
||||
"truncate" => vec![
|
||||
OsString::from(program),
|
||||
@@ -147,6 +228,104 @@ fn test_program_with_non_utf8_path(program: &str, path: &PathBuf) -> CommandResu
|
||||
path_os.to_owned(),
|
||||
OsString::from("1"),
|
||||
],
|
||||
// File creation programs
|
||||
"mkfifo" | "mknod" => {
|
||||
let new_path = path.with_extension("new");
|
||||
if program == "mknod" {
|
||||
vec![
|
||||
OsString::from(program),
|
||||
new_path.as_os_str().to_owned(),
|
||||
OsString::from("c"),
|
||||
OsString::from("1"),
|
||||
OsString::from("3"),
|
||||
]
|
||||
vec![
|
||||
}
|
||||
},
|
||||
}
|
||||
"dd" => vec![
|
||||
OsString::from(program),
|
||||
OsString::from(format!("if={}", path_os.to_string_lossy())),
|
||||
OsString::from("of=/dev/null"),
|
||||
OsString::from("bs=1"),
|
||||
OsString::from("count=1"),
|
||||
],
|
||||
// Hashsum needs algorithm
|
||||
"hashsum" => vec![
|
||||
OsString::from(program),
|
||||
OsString::from("--md5"),
|
||||
path_os.to_owned(),
|
||||
],
|
||||
"base32" | "base64" | "basenc" => vec![
|
||||
"df" => vec![
|
||||
"chroot" => {
|
||||
"df" => vec![OsString::from(program), path_os.to_owned()],
|
||||
// chroot needs a directory and command
|
||||
vec![
|
||||
OsString::from(program),
|
||||
path_os.to_owned(),
|
||||
OsString::from("true"),
|
||||
},
|
||||
}
|
||||
"stty" => vec![
|
||||
"sync" => vec![OsString::from(program), path_os.to_owned()],
|
||||
OsString::from(program),
|
||||
OsString::from("-F"),
|
||||
path_os.to_owned(),
|
||||
"tty" => vec![
|
||||
"tty" => vec![OsString::from(program)], // tty doesn't take file args, but test anyway
|
||||
"env" => {
|
||||
let coreutils_binary = std::env::var("CARGO_BIN_FILE_COREUTILS")
|
||||
.unwrap_or_else(|_| "target/release/coreutils".to_string());
|
||||
vec![
|
||||
OsString::from(program),
|
||||
OsString::from(coreutils_binary),
|
||||
OsString::from("cat"),
|
||||
path_os.to_owned(),
|
||||
]
|
||||
},
|
||||
"nohup" => {
|
||||
let coreutils_binary = std::env::var("CARGO_BIN_FILE_COREUTILS")
|
||||
.unwrap_or_else(|_| "target/release/coreutils".to_string());
|
||||
vec![
|
||||
OsString::from(program),
|
||||
OsString::from(coreutils_binary),
|
||||
OsString::from("cat"),
|
||||
path_os.to_owned(),
|
||||
]
|
||||
},
|
||||
"nice" => {
|
||||
let coreutils_binary = std::env::var("CARGO_BIN_FILE_COREUTILS")
|
||||
.unwrap_or_else(|_| "target/release/coreutils".to_string());
|
||||
vec![
|
||||
OsString::from(program),
|
||||
OsString::from(coreutils_binary),
|
||||
OsString::from("cat"),
|
||||
path_os.to_owned(),
|
||||
]
|
||||
},
|
||||
"timeout" => {
|
||||
let coreutils_binary = std::env::var("CARGO_BIN_FILE_COREUTILS")
|
||||
.unwrap_or_else(|_| "target/release/coreutils".to_string());
|
||||
vec![
|
||||
OsString::from(program),
|
||||
OsString::from("1"),
|
||||
OsString::from(coreutils_binary),
|
||||
OsString::from("cat"),
|
||||
path_os.to_owned(),
|
||||
]
|
||||
},
|
||||
"stdbuf" => {
|
||||
let coreutils_binary = std::env::var("CARGO_BIN_FILE_COREUTILS")
|
||||
.unwrap_or_else(|_| "target/release/coreutils".to_string());
|
||||
vec![
|
||||
OsString::from(program),
|
||||
OsString::from("-o0"),
|
||||
OsString::from(coreutils_binary),
|
||||
OsString::from("cat"),
|
||||
path_os.to_owned(),
|
||||
]
|
||||
},
|
||||
// Programs that work with multiple files (use just one for testing)
|
||||
"comm" | "join" => {
|
||||
// These need two files, use the same file twice for simplicity
|
||||
@@ -155,7 +334,7 @@ fn test_program_with_non_utf8_path(program: &str, path: &PathBuf) -> CommandResu
|
||||
path_os.to_owned(),
|
||||
path_os.to_owned(),
|
||||
]
|
||||
},
|
||||
}
|
||||
// Programs that typically take file input
|
||||
_ => vec![OsString::from(program), path_os.to_owned()],
|
||||
};
|
||||
@@ -238,8 +417,8 @@ fuzz_target!(|_data: &[u8]| {
|
||||
let non_utf8_dir_name = generate_non_utf8_osstring();
|
||||
let non_utf8_dir = temp_root.join(non_utf8_dir_name);
|
||||
|
||||
let local_binary =
|
||||
"/home/sylvestre/dev/debian/coreutils.disable-loca/target/debug/coreutils";
|
||||
let local_binary = std::env::var("CARGO_BIN_FILE_COREUTILS")
|
||||
.unwrap_or_else(|_| "target/release/coreutils".to_string());
|
||||
let mkdir_args = vec![OsString::from("mkdir"), non_utf8_dir.as_os_str().to_owned()];
|
||||
|
||||
let mkdir_result = run_gnu_cmd(local_binary, &mkdir_args, false, None);
|
||||
|
||||
+27
-18
@@ -7,10 +7,12 @@
|
||||
|
||||
use std::cmp;
|
||||
use std::collections::{BTreeSet, HashMap, HashSet};
|
||||
use std::ffi::OsString;
|
||||
use std::fmt::Write as FmtWrite;
|
||||
use std::fs::File;
|
||||
use std::io::{BufRead, BufReader, BufWriter, Read, Write, stdin, stdout};
|
||||
use std::num::ParseIntError;
|
||||
use std::path::Path;
|
||||
|
||||
use clap::{Arg, ArgAction, Command};
|
||||
use regex::Regex;
|
||||
@@ -66,13 +68,12 @@ fn read_word_filter_file(
|
||||
option: &str,
|
||||
) -> std::io::Result<HashSet<String>> {
|
||||
let filename = matches
|
||||
.get_one::<String>(option)
|
||||
.expect("parsing options failed!")
|
||||
.to_string();
|
||||
.get_one::<OsString>(option)
|
||||
.expect("parsing options failed!");
|
||||
let reader: BufReader<Box<dyn Read>> = BufReader::new(if filename == "-" {
|
||||
Box::new(stdin())
|
||||
} else {
|
||||
let file = File::open(filename)?;
|
||||
let file = File::open(Path::new(filename))?;
|
||||
Box::new(file)
|
||||
});
|
||||
let mut words: HashSet<String> = HashSet::new();
|
||||
@@ -88,12 +89,12 @@ fn read_char_filter_file(
|
||||
option: &str,
|
||||
) -> std::io::Result<HashSet<char>> {
|
||||
let filename = matches
|
||||
.get_one::<String>(option)
|
||||
.get_one::<OsString>(option)
|
||||
.expect("parsing options failed!");
|
||||
let mut reader: Box<dyn Read> = if filename == "-" {
|
||||
Box::new(stdin())
|
||||
} else {
|
||||
let file = File::open(filename)?;
|
||||
let file = File::open(Path::new(filename))?;
|
||||
Box::new(file)
|
||||
};
|
||||
let mut buffer = String::new();
|
||||
@@ -275,14 +276,14 @@ struct FileContent {
|
||||
|
||||
type FileMap = HashMap<String, FileContent>;
|
||||
|
||||
fn read_input(input_files: &[String]) -> std::io::Result<FileMap> {
|
||||
fn read_input(input_files: &[OsString]) -> std::io::Result<FileMap> {
|
||||
let mut file_map: FileMap = HashMap::new();
|
||||
let mut offset: usize = 0;
|
||||
for filename in input_files {
|
||||
let reader: BufReader<Box<dyn Read>> = BufReader::new(if filename == "-" {
|
||||
Box::new(stdin())
|
||||
} else {
|
||||
let file = File::open(filename)?;
|
||||
let file = File::open(Path::new(filename))?;
|
||||
Box::new(file)
|
||||
});
|
||||
let lines: Vec<String> = reader.lines().collect::<std::io::Result<Vec<String>>>()?;
|
||||
@@ -292,7 +293,7 @@ fn read_input(input_files: &[String]) -> std::io::Result<FileMap> {
|
||||
let chars_lines: Vec<Vec<char>> = lines.iter().map(|x| x.chars().collect()).collect();
|
||||
let size = lines.len();
|
||||
file_map.insert(
|
||||
filename.to_owned(),
|
||||
filename.to_string_lossy().into_owned(),
|
||||
FileContent {
|
||||
lines,
|
||||
chars_lines,
|
||||
@@ -736,7 +737,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
let output_file;
|
||||
|
||||
let mut files = matches
|
||||
.get_many::<String>(options::FILE)
|
||||
.get_many::<OsString>(options::FILE)
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.cloned();
|
||||
@@ -745,18 +746,22 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
input_files = {
|
||||
let mut files = files.collect::<Vec<_>>();
|
||||
if files.is_empty() {
|
||||
files.push("-".to_string());
|
||||
files.push(OsString::from("-"));
|
||||
}
|
||||
files
|
||||
};
|
||||
output_file = "-".to_string();
|
||||
} else {
|
||||
input_files = vec![files.next().unwrap_or("-".to_string())];
|
||||
output_file = files.next().unwrap_or("-".to_string());
|
||||
input_files = vec![files.next().unwrap_or(OsString::from("-"))];
|
||||
output_file = files
|
||||
.next()
|
||||
.unwrap_or(OsString::from("-"))
|
||||
.to_string_lossy()
|
||||
.into_owned();
|
||||
if let Some(file) = files.next() {
|
||||
return Err(UUsageError::new(
|
||||
1,
|
||||
translate!("ptx-error-extra-operand", "operand" => file.quote()),
|
||||
translate!("ptx-error-extra-operand", "operand" => file.to_string_lossy().quote()),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -778,7 +783,8 @@ pub fn uu_app() -> Command {
|
||||
Arg::new(options::FILE)
|
||||
.hide(true)
|
||||
.action(ArgAction::Append)
|
||||
.value_hint(clap::ValueHint::FilePath),
|
||||
.value_hint(clap::ValueHint::FilePath)
|
||||
.value_parser(clap::value_parser!(OsString)),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::AUTO_REFERENCE)
|
||||
@@ -856,7 +862,8 @@ pub fn uu_app() -> Command {
|
||||
.long(options::BREAK_FILE)
|
||||
.help(translate!("ptx-help-break-file"))
|
||||
.value_name("FILE")
|
||||
.value_hint(clap::ValueHint::FilePath),
|
||||
.value_hint(clap::ValueHint::FilePath)
|
||||
.value_parser(clap::value_parser!(OsString)),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::IGNORE_CASE)
|
||||
@@ -878,7 +885,8 @@ pub fn uu_app() -> Command {
|
||||
.long(options::IGNORE_FILE)
|
||||
.help(translate!("ptx-help-ignore-file"))
|
||||
.value_name("FILE")
|
||||
.value_hint(clap::ValueHint::FilePath),
|
||||
.value_hint(clap::ValueHint::FilePath)
|
||||
.value_parser(clap::value_parser!(OsString)),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::ONLY_FILE)
|
||||
@@ -886,7 +894,8 @@ pub fn uu_app() -> Command {
|
||||
.long(options::ONLY_FILE)
|
||||
.help(translate!("ptx-help-only-file"))
|
||||
.value_name("FILE")
|
||||
.value_hint(clap::ValueHint::FilePath),
|
||||
.value_hint(clap::ValueHint::FilePath)
|
||||
.value_parser(clap::value_parser!(OsString)),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::REFERENCES)
|
||||
|
||||
Reference in New Issue
Block a user