Skip to content

Commit 0bd1956

Browse files
authored
Merge pull request #645 from cgwalters/kargs-cleanup
kargs: Various cleanups
2 parents 5e9279d + 27d955c commit 0bd1956

File tree

4 files changed

+172
-61
lines changed

4 files changed

+172
-61
lines changed

Cargo.lock

Lines changed: 38 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

hack/Containerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ RUN /tmp/provision-derived.sh "$variant" && rm -f /tmp/*.sh
2525
COPY hack/install-test-configs/* /usr/lib/bootc/install/
2626
# Inject our built code
2727
COPY --from=build /out/bootc.tar.zst /tmp
28-
RUN tar -C / --zstd -xvf /tmp/bootc.tar.zst && rm -vf /tmp/*
28+
RUN tar -C / --zstd -xvf /tmp/bootc.tar.zst && rm -vrf /tmp/*
2929
# Also copy over arbitrary bits from the target root
3030
COPY --from=build /build/target/dev-rootfs/ /
3131
# Test our own linting

lib/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ toml = "0.8.12"
4646
xshell = { version = "0.2.6", optional = true }
4747
uuid = { version = "1.8.0", features = ["v4"] }
4848

49+
[dev-dependencies]
50+
similar-asserts = { version = "1.5.0" }
51+
4952
[features]
5053
default = ["install"]
5154
# This feature enables `bootc install`. Disable if you always want to use an external installer.

lib/src/kargs.rs

Lines changed: 130 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1-
use anyhow::Ok;
2-
use anyhow::Result;
1+
use anyhow::{Context, Result};
2+
use camino::Utf8Path;
3+
use cap_std_ext::cap_std;
4+
use cap_std_ext::cap_std::fs::Dir;
5+
use cap_std_ext::dirext::CapStdExtDirExt;
36
use ostree::gio;
47
use ostree_ext::ostree;
58
use ostree_ext::ostree::Deployment;
@@ -17,6 +20,35 @@ struct Config {
1720
match_architectures: Option<Vec<String>>,
1821
}
1922

23+
/// Load and parse all bootc kargs.d files in the specified root, returning
24+
/// a combined list.
25+
fn get_kargs_in_root(d: &Dir, sys_arch: &str) -> Result<Vec<String>> {
26+
// If the directory doesn't exist, that's OK.
27+
let d = if let Some(d) = d.open_dir_optional("usr/lib/bootc/kargs.d")? {
28+
d
29+
} else {
30+
return Ok(Default::default());
31+
};
32+
let mut ret = Vec::new();
33+
// Read all the entries
34+
let mut entries = d.entries()?.collect::<std::io::Result<Vec<_>>>()?;
35+
// cc https://github.com/rust-lang/rust/issues/85573 re the allocation-per-comparison here
36+
entries.sort_by(|a, b| a.file_name().cmp(&b.file_name()));
37+
for ent in entries {
38+
let name = ent.file_name();
39+
let name = name
40+
.to_str()
41+
.ok_or_else(|| anyhow::anyhow!("Invalid non-UTF8 filename: {name:?}"))?;
42+
if !matches!(Utf8Path::new(name).extension(), Some("toml")) {
43+
continue;
44+
}
45+
let buf = d.read_to_string(name)?;
46+
let kargs = parse_kargs_toml(&buf, sys_arch).with_context(|| format!("Parsing {name}"))?;
47+
ret.extend(kargs)
48+
}
49+
Ok(ret)
50+
}
51+
2052
/// Compute the kernel arguments for the new deployment. This starts from the booted
2153
/// karg, but applies the diff between the bootc karg files in /usr/lib/bootc/kargs.d
2254
/// between the booted deployment and the new one.
@@ -27,38 +59,34 @@ pub(crate) fn get_kargs(
2759
) -> Result<Vec<String>> {
2860
let cancellable = gio::Cancellable::NONE;
2961
let mut kargs: Vec<String> = vec![];
30-
let sys_arch = std::env::consts::ARCH.to_string();
62+
let sys_arch = std::env::consts::ARCH;
3163

3264
// Get the running kargs of the booted system
3365
if let Some(bootconfig) = ostree::Deployment::bootconfig(booted_deployment) {
3466
if let Some(options) = ostree::BootconfigParser::get(&bootconfig, "options") {
35-
let options: Vec<&str> = options.split_whitespace().collect();
36-
let mut options: Vec<String> = options.into_iter().map(|s| s.to_string()).collect();
37-
kargs.append(&mut options);
67+
let options = options.split_whitespace().map(|s| s.to_owned());
68+
kargs.extend(options);
3869
}
3970
};
4071

4172
// Get the kargs in kargs.d of the booted system
42-
let mut existing_kargs: Vec<String> = vec![];
43-
let fragments = liboverdrop::scan(&["/usr/lib"], "bootc/kargs.d", &["toml"], true);
44-
for (_name, path) in fragments {
45-
let s = std::fs::read_to_string(&path)?;
46-
let mut parsed_kargs = parse_file(s.clone(), sys_arch.clone())?;
47-
existing_kargs.append(&mut parsed_kargs);
48-
}
73+
let root = &cap_std::fs::Dir::open_ambient_dir("/", cap_std::ambient_authority())?;
74+
let existing_kargs: Vec<String> = get_kargs_in_root(root, sys_arch)?;
4975

5076
// Get the kargs in kargs.d of the pending image
51-
let mut remote_kargs: Vec<String> = vec![];
5277
let (fetched_tree, _) = repo.read_commit(fetched.ostree_commit.as_str(), cancellable)?;
5378
let fetched_tree = fetched_tree.resolve_relative_path("/usr/lib/bootc/kargs.d");
5479
let fetched_tree = fetched_tree
5580
.downcast::<ostree::RepoFile>()
5681
.expect("downcast");
82+
// A special case: if there's no kargs.d directory in the pending (fetched) image,
83+
// then we can just use the combined current kargs + kargs from booted
5784
if !fetched_tree.query_exists(cancellable) {
58-
// if the kargs.d directory does not exist in the fetched image, return the existing kargs
59-
kargs.append(&mut existing_kargs);
85+
kargs.extend(existing_kargs);
6086
return Ok(kargs);
6187
}
88+
89+
let mut remote_kargs: Vec<String> = vec![];
6290
let queryattrs = "standard::name,standard::type";
6391
let queryflags = gio::FileQueryInfoFlags::NOFOLLOW_SYMLINKS;
6492
let fetched_iter = fetched_tree.enumerate_children(queryattrs, queryflags, cancellable)?;
@@ -79,7 +107,8 @@ pub(crate) fn get_kargs(
79107
let mut reader =
80108
ostree_ext::prelude::InputStreamExtManual::into_read(file_content.unwrap());
81109
let s = std::io::read_to_string(&mut reader)?;
82-
let mut parsed_kargs = parse_file(s.clone(), sys_arch.clone())?;
110+
let mut parsed_kargs =
111+
parse_kargs_toml(&s, sys_arch).with_context(|| format!("Parsing {name}"))?;
83112
remote_kargs.append(&mut parsed_kargs);
84113
}
85114
}
@@ -110,58 +139,99 @@ pub(crate) fn get_kargs(
110139
Ok(kargs)
111140
}
112141

113-
pub fn parse_file(file_content: String, sys_arch: String) -> Result<Vec<String>> {
114-
let mut de: Config = toml::from_str(&file_content)?;
115-
let mut parsed_kargs: Vec<String> = vec![];
142+
/// This parses a bootc kargs.d toml file, returning the resulting
143+
/// vector of kernel arguments. Architecture matching is performed using
144+
/// `sys_arch`.
145+
fn parse_kargs_toml(contents: &str, sys_arch: &str) -> Result<Vec<String>> {
146+
let de: Config = toml::from_str(contents)?;
116147
// if arch specified, apply kargs only if the arch matches
117148
// if arch not specified, apply kargs unconditionally
118-
match de.match_architectures {
119-
None => parsed_kargs = de.kargs,
120-
Some(match_architectures) => {
121-
if match_architectures.contains(&sys_arch) {
122-
parsed_kargs.append(&mut de.kargs);
123-
}
124-
}
125-
}
126-
Ok(parsed_kargs)
149+
let matched = de
150+
.match_architectures
151+
.map(|arches| arches.iter().any(|s| s == sys_arch))
152+
.unwrap_or(true);
153+
let r = if matched { de.kargs } else { Vec::new() };
154+
Ok(r)
127155
}
128156

129-
#[test]
130-
/// Verify that kargs are only applied to supported architectures
131-
fn test_arch() {
132-
// no arch specified, kargs ensure that kargs are applied unconditionally
133-
let sys_arch = "x86_64".to_string();
134-
let file_content = r##"kargs = ["console=tty0", "nosmt"]"##.to_string();
135-
let parsed_kargs = parse_file(file_content.clone(), sys_arch.clone()).unwrap();
136-
assert_eq!(parsed_kargs, ["console=tty0", "nosmt"]);
137-
let sys_arch = "aarch64".to_string();
138-
let parsed_kargs = parse_file(file_content.clone(), sys_arch.clone()).unwrap();
139-
assert_eq!(parsed_kargs, ["console=tty0", "nosmt"]);
140-
141-
// one arch matches and one doesn't, ensure that kargs are only applied for the matching arch
142-
let sys_arch = "aarch64".to_string();
143-
let file_content = r##"kargs = ["console=tty0", "nosmt"]
157+
#[cfg(test)]
158+
mod tests {
159+
use super::*;
160+
161+
#[test]
162+
/// Verify that kargs are only applied to supported architectures
163+
fn test_arch() {
164+
// no arch specified, kargs ensure that kargs are applied unconditionally
165+
let sys_arch = "x86_64";
166+
let file_content = r##"kargs = ["console=tty0", "nosmt"]"##.to_string();
167+
let parsed_kargs = parse_kargs_toml(&file_content, sys_arch).unwrap();
168+
assert_eq!(parsed_kargs, ["console=tty0", "nosmt"]);
169+
let sys_arch = "aarch64";
170+
let parsed_kargs = parse_kargs_toml(&file_content, sys_arch).unwrap();
171+
assert_eq!(parsed_kargs, ["console=tty0", "nosmt"]);
172+
173+
// one arch matches and one doesn't, ensure that kargs are only applied for the matching arch
174+
let sys_arch = "aarch64";
175+
let file_content = r##"kargs = ["console=tty0", "nosmt"]
144176
match-architectures = ["x86_64"]
145177
"##
146-
.to_string();
147-
let parsed_kargs = parse_file(file_content.clone(), sys_arch.clone()).unwrap();
148-
assert_eq!(parsed_kargs, [] as [String; 0]);
149-
let file_content = r##"kargs = ["console=tty0", "nosmt"]
178+
.to_string();
179+
let parsed_kargs = parse_kargs_toml(&file_content, sys_arch).unwrap();
180+
assert_eq!(parsed_kargs, [] as [String; 0]);
181+
let file_content = r##"kargs = ["console=tty0", "nosmt"]
150182
match-architectures = ["aarch64"]
151183
"##
152-
.to_string();
153-
let parsed_kargs = parse_file(file_content.clone(), sys_arch.clone()).unwrap();
154-
assert_eq!(parsed_kargs, ["console=tty0", "nosmt"]);
184+
.to_string();
185+
let parsed_kargs = parse_kargs_toml(&file_content, sys_arch).unwrap();
186+
assert_eq!(parsed_kargs, ["console=tty0", "nosmt"]);
155187

156-
// multiple arch specified, ensure that kargs are applied to both archs
157-
let sys_arch = "x86_64".to_string();
158-
let file_content = r##"kargs = ["console=tty0", "nosmt"]
188+
// multiple arch specified, ensure that kargs are applied to both archs
189+
let sys_arch = "x86_64";
190+
let file_content = r##"kargs = ["console=tty0", "nosmt"]
159191
match-architectures = ["x86_64", "aarch64"]
160192
"##
161-
.to_string();
162-
let parsed_kargs = parse_file(file_content.clone(), sys_arch.clone()).unwrap();
163-
assert_eq!(parsed_kargs, ["console=tty0", "nosmt"]);
164-
std::env::set_var("ARCH", "aarch64");
165-
let parsed_kargs = parse_file(file_content.clone(), sys_arch.clone()).unwrap();
166-
assert_eq!(parsed_kargs, ["console=tty0", "nosmt"]);
193+
.to_string();
194+
let parsed_kargs = parse_kargs_toml(&file_content, sys_arch).unwrap();
195+
assert_eq!(parsed_kargs, ["console=tty0", "nosmt"]);
196+
std::env::set_var("ARCH", "aarch64");
197+
let parsed_kargs = parse_kargs_toml(&file_content, sys_arch).unwrap();
198+
assert_eq!(parsed_kargs, ["console=tty0", "nosmt"]);
199+
}
200+
201+
#[test]
202+
/// Verify some error cases
203+
fn test_invalid() {
204+
let test_invalid_extra = r#"kargs = ["console=tty0", "nosmt"]\nfoo=bar"#;
205+
assert!(parse_kargs_toml(test_invalid_extra, "x86_64").is_err());
206+
207+
let test_missing = r#"foo=bar"#;
208+
assert!(parse_kargs_toml(test_missing, "x86_64").is_err());
209+
}
210+
211+
#[test]
212+
fn test_get_kargs_in_root() -> Result<()> {
213+
let td = cap_std_ext::cap_tempfile::TempDir::new(cap_std::ambient_authority())?;
214+
215+
// No directory
216+
assert_eq!(get_kargs_in_root(&td, "x86_64").unwrap().len(), 0);
217+
// Empty directory
218+
td.create_dir_all("usr/lib/bootc/kargs.d")?;
219+
assert_eq!(get_kargs_in_root(&td, "x86_64").unwrap().len(), 0);
220+
// Non-toml file
221+
td.write("usr/lib/bootc/kargs.d/somegarbage", "garbage")?;
222+
assert_eq!(get_kargs_in_root(&td, "x86_64").unwrap().len(), 0);
223+
td.write(
224+
"usr/lib/bootc/kargs.d/01-foo.toml",
225+
r##"kargs = ["console=tty0", "nosmt"]"##,
226+
)?;
227+
td.write(
228+
"usr/lib/bootc/kargs.d/02-bar.toml",
229+
r##"kargs = ["console=ttyS1"]"##,
230+
)?;
231+
232+
let args = get_kargs_in_root(&td, "x86_64").unwrap();
233+
similar_asserts::assert_eq!(args, ["console=tty0", "nosmt", "console=ttyS1"]);
234+
235+
Ok(())
236+
}
167237
}

0 commit comments

Comments
 (0)