composefs_boot/
cmdline.rs

1//! Kernel command line parsing and manipulation.
2//!
3//! This module provides utilities for parsing and generating kernel command line arguments,
4//! with specific support for composefs parameters. It handles the kernel's simple quoting
5//! mechanism and provides functions to extract and create composefs= arguments with optional
6//! insecure mode indicators.
7
8use anyhow::{Context, Result};
9use composefs::fsverity::FsVerityHashValue;
10
11/// Perform kernel command line splitting.
12///
13/// The way this works in the kernel is to split on whitespace with an extremely simple quoting
14/// mechanism: whitespace inside of double quotes is literal, but there is no escaping mechanism.
15/// That means that having a literal double quote in the cmdline is effectively impossible.
16pub(crate) fn split_cmdline(cmdline: &str) -> impl Iterator<Item = &str> {
17    let mut in_quotes = false;
18
19    cmdline.split(move |c: char| {
20        if c == '"' {
21            in_quotes = !in_quotes;
22        }
23        !in_quotes && c.is_ascii_whitespace()
24    })
25}
26
27/// Gets the value of an entry from the kernel cmdline.
28///
29/// The prefix should be something like "composefs=".
30///
31/// This iterates the entries in the provided cmdline string searching for an entry that starts
32/// with the provided prefix.  This will successfully handle quoting of other items in the cmdline,
33/// but the value of the searched entry is returned verbatim (ie: not dequoted).
34pub fn get_cmdline_value<'a>(cmdline: &'a str, prefix: &str) -> Option<&'a str> {
35    split_cmdline(cmdline).find_map(|item| item.strip_prefix(prefix))
36}
37
38/// Extracts and parses the composefs= parameter from a kernel command line.
39///
40/// # Arguments
41///
42/// * `cmdline` - The kernel command line string
43///
44/// # Returns
45///
46/// A tuple of (hash, insecure_flag) where the hash is the composefs object ID
47/// and insecure_flag indicates whether the '?' prefix was present (making verification optional)
48pub fn get_cmdline_composefs<ObjectID: FsVerityHashValue>(
49    cmdline: &str,
50) -> Result<(ObjectID, bool)> {
51    let id = get_cmdline_value(cmdline, "composefs=").context("composefs= value not found")?;
52    if let Some(stripped) = id.strip_prefix('?') {
53        Ok((ObjectID::from_hex(stripped)?, true))
54    } else {
55        Ok((ObjectID::from_hex(id)?, false))
56    }
57}
58
59/// Creates a composefs= kernel command line argument.
60///
61/// # Arguments
62///
63/// * `id` - The composefs object ID as a hex string
64/// * `insecure` - If true, prepends '?' to make fs-verity verification optional
65///
66/// # Returns
67///
68/// A string like "composefs=abc123" or "composefs=?abc123" (if insecure)
69pub fn make_cmdline_composefs(id: &str, insecure: bool) -> String {
70    match insecure {
71        true => format!("composefs=?{id}"),
72        false => format!("composefs={id}"),
73    }
74}