Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/cros_fdt/src/path.rs
5392 views
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.
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.
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.
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.
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`.
67
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
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
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
97
fn try_from(value: String) -> Result<Path> {
98
value.parse()
99
}
100
}
101
102
impl From<Path> for String {
103
fn from(val: Path) -> Self {
104
val.0 // Return path
105
}
106
}
107
108
impl AsRef<str> for Path {
109
fn as_ref(&self) -> &str {
110
self.0.as_str()
111
}
112
}
113
114
impl fmt::Display for Path {
115
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'.
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]
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]
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]
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]
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]
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
207