Skip to content

Commit 3db492f

Browse files
committed
lints: Rework to use linkme
Two goals: - The global static LINTS array is a conflict point - It's easier to lay out the lint info when it's next to each function; prep for extending the lint data more. Signed-off-by: Colin Walters <[email protected]>
1 parent c23212a commit 3db492f

File tree

3 files changed

+129
-76
lines changed

3 files changed

+129
-76
lines changed

Cargo.lock

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

lib/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ indoc = { workspace = true }
3333
libc = { workspace = true }
3434
liboverdrop = "0.1.0"
3535
libsystemd = "0.7"
36+
linkme = "0.3"
3637
openssl = { workspace = true }
3738
regex = "1.10.4"
3839
rustix = { workspace = true }

lib/src/lints.rs

Lines changed: 107 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
//!
33
//! This module implements `bootc container lint`.
44
5+
// Unfortunately needed here to work with linkme
6+
#![allow(unsafe_code)]
7+
58
use std::collections::BTreeSet;
69
use std::env::consts::ARCH;
710
use std::os::unix::ffi::OsStrExt;
@@ -14,6 +17,7 @@ use cap_std_ext::cap_std::fs::MetadataExt;
1417
use cap_std_ext::dirext::CapStdExtDirExt as _;
1518
use fn_error_context::context;
1619
use indoc::indoc;
20+
use linkme::distributed_slice;
1721
use ostree_ext::ostree_prepareroot;
1822
use serde::Serialize;
1923

@@ -52,6 +56,8 @@ impl LintError {
5256
}
5357

5458
type LintFn = fn(&Dir) -> LintResult;
59+
#[distributed_slice]
60+
pub(crate) static LINTS: [Lint];
5561

5662
/// The classification of a lint type.
5763
#[derive(Debug, Serialize)]
@@ -81,85 +87,37 @@ struct Lint {
8187
description: &'static str,
8288
}
8389

84-
const LINTS: &[Lint] = &[
85-
Lint {
86-
name: "var-run",
87-
ty: LintType::Fatal,
88-
f: check_var_run,
89-
description: "Check for /var/run being a physical directory; this is always a bug.",
90-
},
91-
Lint {
92-
name: "kernel",
93-
ty: LintType::Fatal,
94-
f: check_kernel,
95-
description: indoc! { r#"
96-
Check for multiple kernels, i.e. multiple directories of the form /usr/lib/modules/$kver.
97-
Only one kernel is supported in an image.
98-
"# },
99-
},
100-
Lint {
101-
name: "bootc-kargs",
102-
ty: LintType::Fatal,
103-
f: check_parse_kargs,
104-
description: "Verify syntax of /usr/lib/bootc/kargs.d.",
105-
},
106-
Lint {
107-
name: "etc-usretc",
108-
ty: LintType::Fatal,
109-
f: check_usretc,
110-
description: indoc! { r#"
111-
Verify that only one of /etc or /usr/etc exist. You should only have /etc
112-
in a container image. It will cause undefined behavior to have both /etc
113-
and /usr/etc.
114-
"#},
115-
},
116-
Lint {
117-
// This one can be lifted in the future, see https://github.com/containers/bootc/issues/975
118-
name: "utf8",
119-
ty: LintType::Fatal,
120-
f: check_utf8,
121-
description: indoc! { r#"
122-
Check for non-UTF8 filenames. Currently, the ostree backend of bootc only supports
123-
UTF-8 filenames. Non-UTF8 filenames will cause a fatal error.
124-
"#},
125-
},
126-
Lint {
127-
name: "baseimage-root",
128-
ty: LintType::Fatal,
129-
f: check_baseimage_root,
130-
description: indoc! { r#"
131-
Check that expected files are present in the root of the filesystem; such
132-
as /sysroot and a composefs configuration for ostree. More in
133-
<https://containers.github.io/bootc/bootc-images.html#standard-image-content>.
134-
"#},
135-
},
136-
Lint {
137-
name: "var-log",
138-
ty: LintType::Warning,
139-
f: check_varlog,
140-
description: indoc! { r#"
141-
Check for non-empty regular files in `/var/log`. It is often undesired
142-
to ship log files in container images. Log files in general are usually
143-
per-machine state in `/var`. Additionally, log files often include
144-
timestamps, causing unreproducible container images, and may contain
145-
sensitive build system information.
146-
"#},
147-
},
148-
Lint {
149-
name: "nonempty-boot",
150-
ty: LintType::Warning,
151-
f: check_boot,
152-
description: indoc! { r#"
153-
The `/boot` directory should be present, but empty. The kernel
154-
content should be in /usr/lib/modules instead in the container image.
155-
Any content here in the container image will be masked at runtime.
156-
"#},
157-
},
158-
];
90+
impl Lint {
91+
pub(crate) const fn new_fatal(
92+
name: &'static str,
93+
description: &'static str,
94+
f: LintFn,
95+
) -> Self {
96+
Lint {
97+
name: name,
98+
ty: LintType::Fatal,
99+
f: f,
100+
description: description,
101+
}
102+
}
103+
104+
pub(crate) const fn new_warning(
105+
name: &'static str,
106+
description: &'static str,
107+
f: LintFn,
108+
) -> Self {
109+
Lint {
110+
name: name,
111+
ty: LintType::Warning,
112+
f: f,
113+
description: description,
114+
}
115+
}
116+
}
159117

160118
pub(crate) fn lint_list(output: impl std::io::Write) -> Result<()> {
161119
// Dump in yaml format by default, it's readable enough
162-
serde_yaml::to_writer(output, LINTS)?;
120+
serde_yaml::to_writer(output, &*LINTS)?;
163121
Ok(())
164122
}
165123

@@ -214,6 +172,12 @@ pub(crate) fn lint(
214172
Ok(())
215173
}
216174

175+
#[distributed_slice(LINTS)]
176+
static LINT_VAR_RUN: Lint = Lint::new_fatal(
177+
"var-run",
178+
"Check for /var/run being a physical directory; this is always a bug.",
179+
check_var_run,
180+
);
217181
fn check_var_run(root: &Dir) -> LintResult {
218182
if let Some(meta) = root.symlink_metadata_optional("var/run")? {
219183
if !meta.is_symlink() {
@@ -223,6 +187,16 @@ fn check_var_run(root: &Dir) -> LintResult {
223187
lint_ok()
224188
}
225189

190+
#[distributed_slice(LINTS)]
191+
static LINT_ETC_USRUSETC: Lint = Lint::new_fatal(
192+
"etc-usretc",
193+
indoc! { r#"
194+
Verify that only one of /etc or /usr/etc exist. You should only have /etc
195+
in a container image. It will cause undefined behavior to have both /etc
196+
and /usr/etc.
197+
"# },
198+
check_usretc,
199+
);
226200
fn check_usretc(root: &Dir) -> LintResult {
227201
let etc_exists = root.symlink_metadata_optional("etc")?.is_some();
228202
// For compatibility/conservatism don't bomb out if there's no /etc.
@@ -239,18 +213,43 @@ fn check_usretc(root: &Dir) -> LintResult {
239213
}
240214

241215
/// Validate that we can parse the /usr/lib/bootc/kargs.d files.
216+
#[distributed_slice(LINTS)]
217+
static LINT_KARGS: Lint = Lint::new_fatal(
218+
"bootc-kargs",
219+
"Verify syntax of /usr/lib/bootc/kargs.d.",
220+
check_parse_kargs,
221+
);
242222
fn check_parse_kargs(root: &Dir) -> LintResult {
243223
let args = crate::kargs::get_kargs_in_root(root, ARCH)?;
244224
tracing::debug!("found kargs: {args:?}");
245225
lint_ok()
246226
}
247227

228+
#[distributed_slice(LINTS)]
229+
static LINT_KERNEL: Lint = Lint::new_fatal(
230+
"kernel",
231+
indoc! { r#"
232+
Check for multiple kernels, i.e. multiple directories of the form /usr/lib/modules/$kver.
233+
Only one kernel is supported in an image.
234+
"# },
235+
check_kernel,
236+
);
248237
fn check_kernel(root: &Dir) -> LintResult {
249238
let result = ostree_ext::bootabletree::find_kernel_dir_fs(&root)?;
250239
tracing::debug!("Found kernel: {:?}", result);
251240
lint_ok()
252241
}
253242

243+
// This one can be lifted in the future, see https://github.com/containers/bootc/issues/975
244+
#[distributed_slice(LINTS)]
245+
static LINT_UTF8: Lint = Lint::new_fatal(
246+
"utf8",
247+
indoc! { r#"
248+
Check for non-UTF8 filenames. Currently, the ostree backend of bootc only supports
249+
UTF-8 filenames. Non-UTF8 filenames will cause a fatal error.
250+
"#},
251+
check_utf8,
252+
);
254253
fn check_utf8(dir: &Dir) -> LintResult {
255254
for entry in dir.entries()? {
256255
let entry = entry?;
@@ -312,6 +311,16 @@ fn check_baseimage_root_norecurse(dir: &Dir) -> LintResult {
312311
}
313312

314313
/// Check ostree-related base image content.
314+
#[distributed_slice(LINTS)]
315+
static LINT_BASEIMAGE_ROOT: Lint = Lint::new_fatal(
316+
"baseimage-root",
317+
indoc! { r#"
318+
Check that expected files are present in the root of the filesystem; such
319+
as /sysroot and a composefs configuration for ostree. More in
320+
<https://containers.github.io/bootc/bootc-images.html#standard-image-content>.
321+
"#},
322+
check_baseimage_root,
323+
);
315324
fn check_baseimage_root(dir: &Dir) -> LintResult {
316325
if let Err(e) = check_baseimage_root_norecurse(dir)? {
317326
return Ok(Err(e));
@@ -348,6 +357,18 @@ fn collect_nonempty_regfiles(
348357
Ok(())
349358
}
350359

360+
#[distributed_slice(LINTS)]
361+
static LINT_VARLOG: Lint = Lint::new_warning(
362+
"var-log",
363+
indoc! { r#"
364+
Check for non-empty regular files in `/var/log`. It is often undesired
365+
to ship log files in container images. Log files in general are usually
366+
per-machine state in `/var`. Additionally, log files often include
367+
timestamps, causing unreproducible container images, and may contain
368+
sensitive build system information.
369+
"#},
370+
check_varlog,
371+
);
351372
fn check_varlog(root: &Dir) -> LintResult {
352373
let Some(d) = root.open_dir_optional("var/log")? else {
353374
return lint_ok();
@@ -367,6 +388,16 @@ fn check_varlog(root: &Dir) -> LintResult {
367388
lint_err(format!("Found non-empty logfile: {first}{others}"))
368389
}
369390

391+
#[distributed_slice(LINTS)]
392+
static LINT_NONEMPTY_BOOT: Lint = Lint::new_warning(
393+
"nonempty-boot",
394+
indoc! { r#"
395+
The `/boot` directory should be present, but empty. The kernel
396+
content should be in /usr/lib/modules instead in the container image.
397+
Any content here in the container image will be masked at runtime.
398+
"#},
399+
check_boot,
400+
);
370401
fn check_boot(root: &Dir) -> LintResult {
371402
let Some(d) = root.open_dir_optional("boot")? else {
372403
return lint_err(format!("Missing /boot directory"));

0 commit comments

Comments
 (0)