Skip to content

Commit 94025b1

Browse files
src: add new cmdline file
Fix a longstanding TODO in composefs-setup-root for properly dealing with quoting in cmdline arguments by adding a helper function in a separate file (we'll use that elsewhere soon, as well). Also: start using String a bit more. I think I've been avoiding it as inefficient but now that I understand more about how it works (particularly how the length and indices are in byte offsets, not character offsets), I have a lot less reason to avoid it. Signed-off-by: Allison Karlitskaya <[email protected]>
1 parent d417205 commit 94025b1

File tree

3 files changed

+40
-18
lines changed

3 files changed

+40
-18
lines changed

src/bin/composefs-setup-root.rs

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
use std::{
22
ffi::OsString,
33
io::ErrorKind,
4-
os::{
5-
fd::{AsFd, OwnedFd},
6-
unix::ffi::OsStrExt,
7-
},
4+
os::fd::{AsFd, OwnedFd},
85
path::{Path, PathBuf},
96
};
107

@@ -21,6 +18,7 @@ use rustix::{
2118
use serde::Deserialize;
2219

2320
use composefs::{
21+
cmdline::get_cmdline_value,
2422
fsverity::{FsVerityHashValue, Sha256HashValue},
2523
mount::{composefs_fsmount, mount_at, FsHandle},
2624
mountcompat::{overlayfs_set_fd, overlayfs_set_lower_and_data_fds, prepare_mount},
@@ -86,7 +84,7 @@ struct Args {
8684
root_fs: Option<PathBuf>,
8785

8886
#[arg(long, help = "Kernel commandline args (for testing)")]
89-
cmdline: Option<OsString>,
87+
cmdline: Option<String>,
9088

9189
#[arg(long, help = "Mountpoint (don't replace sysroot, for testing)")]
9290
target: Option<PathBuf>,
@@ -200,14 +198,12 @@ fn mount_subdir(
200198
}
201199

202200
// Implementation
203-
fn parse_composefs_cmdline<H: FsVerityHashValue>(cmdline: &[u8]) -> Result<H> {
204-
// TODO?: officially we need to understand quoting with double-quotes...
205-
for part in cmdline.split(|c| c.is_ascii_whitespace()) {
206-
if let Some(digest) = part.strip_prefix(b"composefs=") {
207-
return H::from_hex(digest).context("Parsing composefs=");
208-
}
209-
}
210-
bail!("Unable to find composefs= cmdline parameter");
201+
fn parse_composefs_cmdline<H: FsVerityHashValue>(cmdline: &str) -> Result<H> {
202+
let Some(digest) = get_cmdline_value(cmdline, "composefs=") else {
203+
bail!("Unable to find composefs= cmdline parameter");
204+
};
205+
206+
H::from_hex(digest).context("Parsing composefs=")
211207
}
212208

213209
fn gpt_workaround() -> Result<()> {
@@ -233,8 +229,8 @@ fn setup_root(args: Args) -> Result<()> {
233229
.with_context(|| format!("Failed to open sysroot {:?}", args.sysroot))?;
234230

235231
let cmdline = match &args.cmdline {
236-
Some(cmdline) => cmdline.as_bytes(),
237-
None => &std::fs::read("/proc/cmdline")?,
232+
Some(cmdline) => cmdline,
233+
None => &std::fs::read_to_string("/proc/cmdline")?,
238234
};
239235
let image = parse_composefs_cmdline::<Sha256HashValue>(cmdline)?.to_hex();
240236

@@ -290,12 +286,11 @@ mod test {
290286
fn test_parse() {
291287
let failing = ["", "foo", "composefs", "composefs=foo"];
292288
for case in failing {
293-
assert!(parse_composefs_cmdline::<Sha256HashValue>(case.as_bytes()).is_err());
289+
assert!(parse_composefs_cmdline::<Sha256HashValue>(case).is_err());
294290
}
295291
let digest = "8b7df143d91c716ecfa5fc1730022f6b421b05cedee8fd52b1fc65a96030ad52";
296292
similar_asserts::assert_eq!(
297-
parse_composefs_cmdline::<Sha256HashValue>(format!("composefs={digest}").as_bytes())
298-
.unwrap(),
293+
parse_composefs_cmdline::<Sha256HashValue>(&format!("composefs={digest}")).unwrap(),
299294
Sha256HashValue::from_hex(digest).unwrap()
300295
);
301296
}

src/cmdline.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/// Perform kernel command line splitting.
2+
///
3+
/// The way this works in the kernel is to split on whitespace with an extremely simple quoting
4+
/// mechanism: whitespace inside of double quotes is literal, but there is no escaping mechanism.
5+
/// That means that having a literal double quote in the cmdline is effectively impossible.
6+
pub(crate) fn split_cmdline(cmdline: &str) -> impl Iterator<Item = &str> {
7+
let mut in_quotes = false;
8+
9+
cmdline.split(move |c: char| {
10+
if c == '"' {
11+
in_quotes = !in_quotes;
12+
}
13+
!in_quotes && c.is_ascii_whitespace()
14+
})
15+
}
16+
17+
/// Gets the value of an entry from the kernel cmdline.
18+
///
19+
/// The prefix should be something like "composefs=".
20+
///
21+
/// This iterates the entries in the provided cmdline string searching for an entry that starts
22+
/// with the provided prefix. This will successfully handle quoting of other items in the cmdline,
23+
/// but the value of the searched entry is returned verbatim (ie: not dequoted).
24+
pub fn get_cmdline_value<'a>(cmdline: &'a str, prefix: &str) -> Option<&'a str> {
25+
split_cmdline(cmdline).find_map(|item| item.strip_prefix(prefix))
26+
}

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#![deny(missing_debug_implementations)]
22

3+
pub mod cmdline;
34
pub mod dumpfile;
45
pub mod dumpfile_parse;
56
pub mod erofs;

0 commit comments

Comments
 (0)