-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Expand file tree
/
Copy pathvalidation.rs
More file actions
150 lines (134 loc) · 4.81 KB
/
validation.rs
File metadata and controls
150 lines (134 loc) · 4.81 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
// This file is part of the uutils coreutils package.
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
// spell-checker:ignore memfd_create prefixcat rsplit testcat
use std::ffi::{OsStr, OsString};
use std::io::{Write, stderr};
use std::path::{Path, PathBuf};
use std::process;
use uucore::Args;
use uucore::display::Quotable;
use uucore::locale;
/// Gets all available utilities including "coreutils"
#[allow(clippy::type_complexity)]
pub fn get_all_utilities<T: Args>(
util_map: &phf::OrderedMap<&'static str, (fn(T) -> i32, fn() -> clap::Command)>,
) -> Vec<&'static str> {
std::iter::once("coreutils")
.chain(util_map.keys().copied())
.collect()
}
/// Prints a "utility not found" error and exits
pub fn not_found(util: &OsStr) -> ! {
let _ = writeln!(
stderr(),
"coreutils: unknown program '{}'",
util.maybe_quote()
);
process::exit(1);
}
/// Prints an "unrecognized option" error and exits
pub fn unrecognized_option(binary_name: &str, option: &OsStr) -> ! {
let _ = writeln!(
stderr(),
"{binary_name}: unrecognized option '{}'",
option.to_string_lossy()
);
process::exit(1);
}
/// Sets up localization for a utility with proper error handling
pub fn setup_localization_or_exit(util_name: &str) {
let util_name = get_canonical_util_name(util_name);
locale::setup_localization(util_name).unwrap_or_else(|err| {
match err {
locale::LocalizationError::ParseResource {
error: err_msg,
snippet,
} => eprintln!("Localization parse error at {snippet}: {err_msg}"),
other => eprintln!("Could not init the localization system: {other}"),
}
process::exit(99)
});
}
/// Gets the canonical utility name, resolving aliases
fn get_canonical_util_name(util_name: &str) -> &str {
match util_name {
// uu_test aliases - '[' is an alias for test
"[" => "test",
"dir" => "ls", // dir is an alias for ls
"vdir" => "ls", // vdir is an alias for ls
// Default case - return the util name as is
_ => util_name,
}
}
/// Gets the binary path from command line arguments
/// Panics if the binary path cannot be determined
#[cfg(not(any(target_os = "linux", target_os = "android")))]
pub fn binary_path(args: &mut impl Iterator<Item = OsString>) -> PathBuf {
match args.next() {
Some(ref s) if !s.is_empty() => PathBuf::from(s),
// the fallback is valid only for hardlinks
_ => std::env::current_exe().unwrap(),
}
}
/// Get actual binary path from kernel, not argv0, to prevent `env -a` from bypassing
/// AppArmor, SELinux policies on hard-linked binaries
#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn binary_path(args: &mut impl Iterator<Item = OsString>) -> PathBuf {
use std::fs::File;
use std::io::Read;
use std::os::unix::ffi::OsStrExt;
let execfn = rustix::param::linux_execfn();
let execfn_bytes = execfn.to_bytes();
let exec_path = Path::new(OsStr::from_bytes(execfn_bytes));
let argv0 = args.next().unwrap();
let mut shebang_buf = [0u8; 2];
if execfn_bytes.rsplit(|&b| b == b'/').next() == argv0.as_bytes().rsplit(|&b| b == b'/').next()
{
// argv0 is not full-path when called from PATH
exec_path.into()
} else if execfn_bytes.starts_with(b"/proc/")
|| (File::open(Path::new(exec_path))
.and_then(|mut f| f.read_exact(&mut shebang_buf))
.is_ok()
&& &shebang_buf == b"#!")
{
// exec_path is wrong when called from shebang or memfd_create (/proc/self/fd/*)
argv0.into()
} else {
exec_path.into()
}
}
/// Extracts the binary name from a path
pub fn name(binary_path: &Path) -> Option<&str> {
binary_path.file_stem()?.to_str()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_get_canonical_util_name() {
// Test a few key aliases
assert_eq!(get_canonical_util_name("["), "test");
assert_eq!(get_canonical_util_name("dir"), "ls");
// Test passthrough case
assert_eq!(get_canonical_util_name("cat"), "cat");
}
#[test]
fn test_name() {
// Test normal executable name
assert_eq!(name(Path::new("/usr/bin/ls")), Some("ls"));
assert_eq!(name(Path::new("cat")), Some("cat"));
assert_eq!(
name(Path::new("./target/debug/coreutils")),
Some("coreutils")
);
// Test with extensions
assert_eq!(name(Path::new("program.exe")), Some("program"));
assert_eq!(name(Path::new("/path/to/utility.bin")), Some("utility"));
// Test edge cases
assert_eq!(name(Path::new("")), None);
assert_eq!(name(Path::new("/")), None);
}
}