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