ostree_ext/
bootabletree.rs1use std::path::Path;
4
5use anyhow::Result;
6use camino::Utf8Path;
7use camino::Utf8PathBuf;
8use cap_std::fs::Dir;
9use cap_std_ext::cap_std;
10use ostree::gio;
11use ostree::prelude::*;
12
13const MODULES: &str = "usr/lib/modules";
14const VMLINUZ: &str = "vmlinuz";
15
16pub fn find_kernel_dir(
19 root: &gio::File,
20 cancellable: Option<&gio::Cancellable>,
21) -> Result<Option<gio::File>> {
22 let moddir = root.resolve_relative_path(MODULES);
23 let e = moddir.enumerate_children(
24 "standard::name",
25 gio::FileQueryInfoFlags::NOFOLLOW_SYMLINKS,
26 cancellable,
27 )?;
28 let mut r = None;
29 for child in e.clone() {
30 let child = &child?;
31 if child.file_type() != gio::FileType::Directory {
32 continue;
33 }
34 let childpath = e.child(child);
35 let vmlinuz = childpath.child(VMLINUZ);
36 if !vmlinuz.query_exists(cancellable) {
37 continue;
38 }
39 if r.replace(childpath).is_some() {
40 anyhow::bail!("Found multiple subdirectories in {}", MODULES);
41 }
42 }
43 Ok(r)
44}
45
46fn read_dir_optional(
47 d: &Dir,
48 p: impl AsRef<Path>,
49) -> std::io::Result<Option<cap_std::fs::ReadDir>> {
50 match d.read_dir(p.as_ref()) {
51 Ok(r) => Ok(Some(r)),
52 Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(None),
53 Err(e) => Err(e),
54 }
55}
56
57pub fn find_kernel_dir_fs(root: &Dir) -> Result<Option<Utf8PathBuf>> {
60 let mut r = None;
61 let entries = if let Some(entries) = read_dir_optional(root, MODULES)? {
62 entries
63 } else {
64 return Ok(None);
65 };
66 for child in entries {
67 let child = &child?;
68 if !child.file_type()?.is_dir() {
69 continue;
70 }
71 let name = child.file_name();
72 let name = if let Some(n) = name.to_str() {
73 n
74 } else {
75 continue;
76 };
77 let mut pbuf = Utf8Path::new(MODULES).to_owned();
78 pbuf.push(name);
79 pbuf.push(VMLINUZ);
80 if !root.try_exists(&pbuf)? {
81 continue;
82 }
83 pbuf.pop();
84 if r.replace(pbuf).is_some() {
85 anyhow::bail!("Found multiple subdirectories in {}", MODULES);
86 }
87 }
88 Ok(r)
89}
90
91#[cfg(test)]
92mod test {
93 use super::*;
94 use cap_std_ext::{cap_std, cap_tempfile};
95
96 #[test]
97 fn test_find_kernel_dir_fs() -> Result<()> {
98 let td = cap_tempfile::tempdir(cap_std::ambient_authority())?;
99
100 assert!(find_kernel_dir_fs(&td).unwrap().is_none());
102 let moddir = Utf8Path::new("usr/lib/modules");
103 td.create_dir_all(moddir)?;
104 assert!(find_kernel_dir_fs(&td).unwrap().is_none());
105
106 let kpath = moddir.join("5.12.8-32.aarch64");
107 td.create_dir_all(&kpath)?;
108 td.write(kpath.join("vmlinuz"), "some kernel")?;
109 let kpath2 = moddir.join("5.13.7-44.aarch64");
110 td.create_dir_all(&kpath2)?;
111 td.write(kpath2.join("foo.ko"), "some kmod")?;
112
113 assert_eq!(
114 find_kernel_dir_fs(&td)
115 .unwrap()
116 .unwrap()
117 .file_name()
118 .unwrap(),
119 kpath.file_name().unwrap()
120 );
121
122 Ok(())
123 }
124}