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