Skip to content

Commit a13ab5e

Browse files
committed
directory: Add _opt helpers
This is like https://docs.rs/cap-std-ext/latest/cap_std_ext/dirext/trait.CapStdExtDirExt.html#tymethod.open_dir_optional etc. The rationale here is that it's really common for filesystem operations to want to handle "not found" and that maps most cleanly to `Option`. We only have one use right now but there could easily be more. Signed-off-by: Colin Walters <[email protected]>
1 parent b1bd9c1 commit a13ab5e

File tree

2 files changed

+35
-14
lines changed

2 files changed

+35
-14
lines changed

src/selabel.rs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use regex_automata::{hybrid::dfa, util::syntax, Anchored, Input};
1313
use crate::{
1414
fsverity::FsVerityHashValue,
1515
repository::Repository,
16-
tree::{Directory, FileSystem, ImageError, Inode, Leaf, LeafContent, RegularFile, Stat},
16+
tree::{Directory, FileSystem, Inode, Leaf, LeafContent, RegularFile, Stat},
1717
};
1818

1919
/* We build the entire SELinux policy into a single "lazy DFA" such that:
@@ -106,16 +106,18 @@ struct Policy {
106106
contexts: Vec<String>,
107107
}
108108

109+
/// Open a file in the composefs store, handling inline vs external files.
109110
pub fn openat<'a, H: FsVerityHashValue>(
110111
dir: &'a Directory<H>,
111112
filename: impl AsRef<OsStr>,
112113
repo: &Repository<H>,
113114
) -> Result<Option<Box<dyn Read + 'a>>> {
114-
match dir.get_file(filename.as_ref()) {
115-
Ok(RegularFile::Inline(data)) => Ok(Some(Box::new(&**data))),
116-
Ok(RegularFile::External(id, ..)) => Ok(Some(Box::new(File::from(repo.open_object(id)?)))),
117-
Err(ImageError::NotFound(..)) => Ok(None),
118-
Err(other) => Err(other)?,
115+
match dir.get_file_opt(filename.as_ref())? {
116+
Some(file) => match file {
117+
RegularFile::Inline(data) => Ok(Some(Box::new(&**data))),
118+
RegularFile::External(id, ..) => Ok(Some(Box::new(File::from(repo.open_object(id)?)))),
119+
},
120+
None => Ok(None),
119121
}
120122
}
121123

@@ -248,10 +250,9 @@ fn parse_config(file: impl Read) -> Result<Option<String>> {
248250

249251
pub fn selabel<H: FsVerityHashValue>(fs: &mut FileSystem<H>, repo: &Repository<H>) -> Result<()> {
250252
// if /etc/selinux/config doesn't exist then it's not an error
251-
let etc_selinux = match fs.root.get_directory("etc/selinux".as_ref()) {
252-
Err(ImageError::NotFound(..)) => return Ok(()),
253-
other => other,
254-
}?;
253+
let Some(etc_selinux) = fs.root.get_directory_opt("etc/selinux".as_ref())? else {
254+
return Ok(());
255+
};
255256

256257
let Some(etc_selinux_config) = openat(etc_selinux, "config", repo)? else {
257258
return Ok(());

src/tree.rs

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,17 @@ impl<ObjectID: FsVerityHashValue> Directory<ObjectID> {
152152
///
153153
/// On failure, can return any number of errors from ImageError.
154154
pub fn get_directory(&self, pathname: &OsStr) -> Result<&Directory<ObjectID>, ImageError> {
155+
match self.get_directory_opt(pathname)? {
156+
Some(r) => Ok(r),
157+
None => Err(ImageError::NotFound(Box::from(pathname))),
158+
}
159+
}
160+
161+
/// Like [`Self::get_directory()`] but maps [`ImageError::NotFound`] to [`Option`].
162+
pub fn get_directory_opt(
163+
&self,
164+
pathname: &OsStr,
165+
) -> Result<Option<&Directory<ObjectID>>, ImageError> {
155166
let path = Path::new(pathname);
156167
let mut dir = self;
157168

@@ -164,12 +175,12 @@ impl<ObjectID: FsVerityHashValue> Directory<ObjectID> {
164175
Component::Normal(filename) => match dir.entries.get(filename) {
165176
Some(Inode::Directory(subdir)) => subdir,
166177
Some(_) => return Err(ImageError::NotADirectory(filename.into())),
167-
None => return Err(ImageError::NotFound(filename.into())),
178+
None => return Ok(None),
168179
},
169180
}
170181
}
171182

172-
Ok(dir)
183+
Ok(Some(dir))
173184
}
174185

175186
/// Gets a mutable reference to a subdirectory of this directory.
@@ -301,13 +312,22 @@ impl<ObjectID: FsVerityHashValue> Directory<ObjectID> {
301312
&'a self,
302313
filename: &OsStr,
303314
) -> Result<&'a RegularFile<ObjectID>, ImageError> {
315+
self.get_file_opt(filename)?
316+
.ok_or_else(|| ImageError::NotFound(Box::from(filename)))
317+
}
318+
319+
/// Like [`Self::get_file()`] but maps [`ImageError::NotFound`] to [`Option`].
320+
pub fn get_file_opt<'a>(
321+
&'a self,
322+
filename: &OsStr,
323+
) -> Result<Option<&'a RegularFile<ObjectID>>, ImageError> {
304324
match self.entries.get(filename) {
305325
Some(Inode::Leaf(leaf)) => match &leaf.content {
306-
LeafContent::Regular(file) => Ok(file),
326+
LeafContent::Regular(file) => Ok(Some(file)),
307327
_ => Err(ImageError::IsNotRegular(filename.into())),
308328
},
309329
Some(Inode::Directory(..)) => Err(ImageError::IsADirectory(filename.into())),
310-
None => Err(ImageError::NotFound(filename.into())),
330+
None => Ok(None),
311331
}
312332
}
313333

0 commit comments

Comments
 (0)