bootc_internal_utils/
path.rs1use std::fmt::Display;
2use std::os::unix::ffi::OsStrExt;
3use std::path::Path;
4
5#[derive(Debug)]
7pub struct PathQuotedDisplay<'a> {
8 path: &'a Path,
9}
10
11fn is_shellsafe(c: char) -> bool {
19 matches!(c, '/' | '.' | '-' | '_' | ',' | '=' | ':') || c.is_alphanumeric()
20}
21
22impl<'a> Display for PathQuotedDisplay<'a> {
23 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24 if let Some(s) = self.path.to_str() {
25 if s.chars().all(is_shellsafe) {
26 return f.write_str(s);
27 }
28 }
29 if let Ok(r) = shlex::bytes::try_quote(self.path.as_os_str().as_bytes()) {
30 let s = String::from_utf8_lossy(&r);
31 return f.write_str(&s);
32 }
33 return Err(std::fmt::Error);
35 }
36}
37
38impl<'a> PathQuotedDisplay<'a> {
39 pub fn new<P: AsRef<Path>>(path: &'a P) -> PathQuotedDisplay<'a> {
43 PathQuotedDisplay {
44 path: path.as_ref(),
45 }
46 }
47}
48
49#[cfg(test)]
50mod tests {
51 use std::ffi::OsStr;
52
53 use super::*;
54
55 #[test]
56 fn test_unquoted() {
57 for v in [
58 "",
59 "foo",
60 "/foo/bar",
61 "/foo/bar/../baz",
62 "/foo9/bar10",
63 "--foo",
64 "--virtiofs=/foo,/bar",
65 "/foo:/bar",
66 "--label=type=unconfined_t",
67 ] {
68 assert_eq!(v, format!("{}", PathQuotedDisplay::new(&v)));
69 }
70 }
71
72 #[test]
73 fn test_bash_metachars() {
74 let bash_metachars = "|&;()<>";
76 for c in bash_metachars.chars() {
77 assert!(!is_shellsafe(c));
78 }
79 }
80
81 #[test]
82 fn test_quoted() {
83 let cases = [
84 (" ", "' '"),
85 ("/some/path with spaces/", "'/some/path with spaces/'"),
86 ("/foo/!/bar&", "'/foo/!/bar&'"),
87 (r#"/path/"withquotes'"#, r#""/path/\"withquotes'""#),
88 ];
89 for (v, quoted) in cases {
90 let q = PathQuotedDisplay::new(&v).to_string();
91 assert_eq!(quoted, q.as_str());
92 let token = shlex::split(&q).unwrap();
94 assert_eq!(1, token.len());
95 assert_eq!(v, token[0]);
96 }
97 }
98
99 #[test]
100 fn test_nonutf8() {
101 let p = Path::new(OsStr::from_bytes(b"/foo/somenonutf8\xEE/bar"));
102 assert!(p.to_str().is_none());
103 let q = PathQuotedDisplay::new(&p).to_string();
104 assert_eq!(q, r#"'/foo/somenonutf8�/bar'"#);
105 }
106}