xref: /aosp_15_r20/external/crosvm/cros_fdt/src/path.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1 // Copyright 2023 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 //! This module implements DT path handling.
6 
7 use std::fmt;
8 use std::str::FromStr;
9 
10 use crate::fdt::Error;
11 use crate::fdt::Result;
12 
13 pub(crate) const PATH_SEP: &str = "/";
14 
15 // Property name and offset containing a phandle value.
16 #[derive(Debug, PartialEq)]
17 pub(crate) struct PhandlePin(pub String, pub u32);
18 
19 /// Device tree path.
20 #[derive(Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Hash)]
21 pub struct Path(String);
22 
23 impl Path {
24     // Verify path and strip unneeded characters.
sanitize(path: &str) -> Result<String>25     fn sanitize(path: &str) -> Result<String> {
26         if path.is_empty() || !path.starts_with(PATH_SEP) {
27             return Err(Error::InvalidPath(format!("{path} is not absolute")));
28         } else if path == PATH_SEP {
29             return Ok(path.into());
30         }
31         let path = path.trim_end_matches(PATH_SEP);
32         if path.is_empty() || path.split(PATH_SEP).skip(1).any(|c| c.is_empty()) {
33             Err(Error::InvalidPath("empty component in path".into()))
34         } else {
35             assert!(path.starts_with(PATH_SEP));
36             Ok(path.into())
37         }
38     }
39 
40     // Create a new Path.
new(path: &str) -> Result<Self>41     pub(crate) fn new(path: &str) -> Result<Self> {
42         Ok(Self(Self::sanitize(path)?))
43     }
44 
45     // Push a new path segment, creating a new path.
push(&self, subpath: &str) -> Result<Self>46     pub(crate) fn push(&self, subpath: &str) -> Result<Self> {
47         let mut new_path = self.0.clone();
48         if !new_path.ends_with(PATH_SEP) {
49             new_path.push_str(PATH_SEP);
50         }
51         new_path.push_str(
52             subpath
53                 .trim_start_matches(PATH_SEP)
54                 .trim_end_matches(PATH_SEP),
55         );
56         Ok(Self(Self::sanitize(&new_path)?))
57     }
58 
59     // Iterate path segments.
iter(&self) -> impl Iterator<Item = &str>60     pub(crate) fn iter(&self) -> impl Iterator<Item = &str> {
61         self.0
62             .split(PATH_SEP)
63             .skip(if self.0 == PATH_SEP { 2 } else { 1 }) // Skip empty segments at start
64     }
65 
66     // Return `true` if the path points to a child of `other`.
is_child_of(&self, other: &Path) -> bool67     pub(crate) fn is_child_of(&self, other: &Path) -> bool {
68         let mut self_iter = self.iter();
69         for elem in other.iter() {
70             if self_iter.next() != Some(elem) {
71                 return false;
72             }
73         }
74         true
75     }
76 }
77 
78 impl FromStr for Path {
79     type Err = Error;
80 
from_str(value: &str) -> Result<Self>81     fn from_str(value: &str) -> Result<Self> {
82         Path::new(value)
83     }
84 }
85 
86 impl TryFrom<&str> for Path {
87     type Error = Error;
88 
try_from(value: &str) -> Result<Path>89     fn try_from(value: &str) -> Result<Path> {
90         value.parse()
91     }
92 }
93 
94 impl TryFrom<String> for Path {
95     type Error = Error;
96 
try_from(value: String) -> Result<Path>97     fn try_from(value: String) -> Result<Path> {
98         value.parse()
99     }
100 }
101 
102 impl From<Path> for String {
from(val: Path) -> Self103     fn from(val: Path) -> Self {
104         val.0 // Return path
105     }
106 }
107 
108 impl AsRef<str> for Path {
as_ref(&self) -> &str109     fn as_ref(&self) -> &str {
110         self.0.as_str()
111     }
112 }
113 
114 impl fmt::Display for Path {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result115     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
116         write!(f, "{}", self.0)
117     }
118 }
119 
120 // Parse a DT path string containing a node path and a property location (name and offset),
121 // eg '/path/to/node:prop1:4'.
parse_path_with_prop(value: &str) -> Result<(Path, PhandlePin)>122 pub(crate) fn parse_path_with_prop(value: &str) -> Result<(Path, PhandlePin)> {
123     const PROP_SEP: char = ':';
124     let mut elements = value.split(PROP_SEP);
125     let path: Path = elements.next().unwrap().parse()?; // There will always be at least one.
126     let prop = elements
127         .next()
128         .ok_or_else(|| Error::InvalidPath("missing property part".into()))?
129         .to_owned();
130     let off: u32 = elements
131         .next()
132         .ok_or_else(|| Error::InvalidPath("missing offset part".into()))?
133         .parse()
134         .map_err(|_| Error::InvalidPath("cannot parse offset as u32".into()))?;
135     Ok((path, PhandlePin(prop, off)))
136 }
137 
138 #[cfg(test)]
139 mod tests {
140     use super::*;
141 
142     #[test]
fdt_parse_path()143     fn fdt_parse_path() {
144         let l: Path = "/".parse().unwrap();
145         assert!(l.iter().next().is_none());
146 
147         let l: Path = "/a/b/c".parse().unwrap();
148         assert!(l.iter().eq(["a", "b", "c"]));
149 
150         let (path, prop) = parse_path_with_prop("/:a:0").unwrap();
151         assert!(path.iter().next().is_none());
152         assert_eq!(prop.0, "a");
153         assert_eq!(prop.1, 0);
154 
155         let (path, prop) = parse_path_with_prop("/a/b/c:defg:1").unwrap();
156         assert!(path.iter().eq(["a", "b", "c"]));
157         assert_eq!(prop.0, "defg");
158         assert_eq!(prop.1, 1);
159     }
160 
161     #[test]
fdt_path_parse_invalid()162     fn fdt_path_parse_invalid() {
163         assert!(Path::from_str("").is_err());
164         assert!(Path::from_str("/a/b//c").is_err());
165         assert!(Path::from_str("a/b").is_err());
166         assert!(Path::from_str("a").is_err());
167         parse_path_with_prop("a").expect_err("parse error");
168         parse_path_with_prop("a::").expect_err("parse error");
169         parse_path_with_prop("/a/b:c:").expect_err("parse error");
170         parse_path_with_prop("/a/b:c:p:w").expect_err("parse error");
171     }
172 
173     #[test]
fdt_path_from_empty()174     fn fdt_path_from_empty() {
175         let mut path = Path::new("/").unwrap();
176         assert!(path.iter().next().is_none());
177         path = path.push("abc").unwrap();
178         assert!(path.iter().eq(["abc",]));
179         path = Path::new("/").unwrap();
180         path = path.push("a/b/c").unwrap();
181         assert!(path.iter().eq(["a", "b", "c"]));
182     }
183 
184     #[test]
fdt_path_create()185     fn fdt_path_create() {
186         let mut path = Path::new("/a/b/c").unwrap();
187         path = path.push("de").unwrap();
188         assert!(path.iter().eq(["a", "b", "c", "de"]));
189         path = path.push("f/g/h").unwrap();
190         assert!(path.iter().eq(["a", "b", "c", "de", "f", "g", "h"]));
191     }
192 
193     #[test]
fdt_path_childof()194     fn fdt_path_childof() {
195         let path = Path::new("/aaa/bbb/ccc").unwrap();
196         assert!(path.is_child_of(&Path::new("/aaa").unwrap()));
197         assert!(path.is_child_of(&Path::new("/aaa/bbb").unwrap()));
198         assert!(path.is_child_of(&Path::new("/aaa/bbb/ccc").unwrap()));
199         assert!(!path.is_child_of(&Path::new("/aaa/bbb/ccc/ddd").unwrap()));
200         assert!(!path.is_child_of(&Path::new("/aa").unwrap()));
201         assert!(!path.is_child_of(&Path::new("/aaa/bb").unwrap()));
202         assert!(!path.is_child_of(&Path::new("/d").unwrap()));
203         assert!(!path.is_child_of(&Path::new("/d/e").unwrap()));
204     }
205 }
206