diff --git a/Makefile b/Makefile index 6443534c4..965b7d712 100644 --- a/Makefile +++ b/Makefile @@ -25,6 +25,9 @@ install: fi; \ done install -D -m 0644 -t $(DESTDIR)/$(prefix)/lib/systemd/system systemd/*.service systemd/*.timer systemd/*.path systemd/*.target + install -D -m 0644 -t $(DESTDIR)/$(prefix)/share/doc/bootc/baseimage/base/usr/lib/ostree/ baseimage/base/usr/lib/ostree/prepare-root.conf + install -d -m 755 $(DESTDIR)/$(prefix)/share/doc/bootc/baseimage/base/sysroot + cp -PfT baseimage/base/ostree $(DESTDIR)/$(prefix)/share/doc/bootc/baseimage/base/ostree # Run this to also take over the functionality of `ostree container` for example. # Only needed for OS/distros that have callers invoking `ostree container` and not bootc. diff --git a/baseimage/README.md b/baseimage/README.md new file mode 100644 index 000000000..1a759763a --- /dev/null +++ b/baseimage/README.md @@ -0,0 +1,10 @@ +# Recommended image content + +The subdirectories here are recommended to be installed alongside +bootc in `/usr/share/doc/bootc/baseimage` - they act as reference +sources of content. + +- [base](base): At the current time the content here is effectively + a hard requirement. It's not much, just an ostree configuration + enabling composefs, plus the default `sysroot` directory (which + may go away in the future) and the `ostree` symlink into `sysroot`. diff --git a/baseimage/base/ostree b/baseimage/base/ostree new file mode 120000 index 000000000..99bd5a25e --- /dev/null +++ b/baseimage/base/ostree @@ -0,0 +1 @@ +sysroot/ostree \ No newline at end of file diff --git a/baseimage/base/sysroot/.gitignore b/baseimage/base/sysroot/.gitignore new file mode 100644 index 000000000..ffead3ebb --- /dev/null +++ b/baseimage/base/sysroot/.gitignore @@ -0,0 +1,3 @@ +# A trick to keep an empty directory in git +* +!.gitignore diff --git a/baseimage/base/usr/lib/ostree/prepare-root.conf b/baseimage/base/usr/lib/ostree/prepare-root.conf new file mode 100644 index 000000000..2faae22bc --- /dev/null +++ b/baseimage/base/usr/lib/ostree/prepare-root.conf @@ -0,0 +1,2 @@ +[composefs] +enabled = true diff --git a/contrib/packaging/bootc.spec b/contrib/packaging/bootc.spec index 7a5bb798e..5ef2dd063 100644 --- a/contrib/packaging/bootc.spec +++ b/contrib/packaging/bootc.spec @@ -32,6 +32,7 @@ BuildRequires: libzstd-devel %{_prefix}/lib/systemd/system-generators/* %{_prefix}/lib/bootc %{_unitdir}/* +%{_docdir}/bootc/* %{_mandir}/man*/bootc* %prep diff --git a/lib/src/lints.rs b/lib/src/lints.rs index 6fbea7adc..654b6d837 100644 --- a/lib/src/lints.rs +++ b/lib/src/lints.rs @@ -3,8 +3,9 @@ //! This module implements `bootc container lint`. use std::env::consts::ARCH; +use std::os::unix::ffi::OsStrExt; -use anyhow::{bail, ensure, Result}; +use anyhow::{bail, ensure, Context, Result}; use cap_std::fs::Dir; use cap_std_ext::cap_std; use cap_std_ext::dirext::CapStdExtDirExt as _; @@ -12,6 +13,9 @@ use fn_error_context::context; use crate::utils::openat2_with_retry; +/// Reference to embedded default baseimage content that should exist. +const BASEIMAGE_REF: &str = "usr/share/doc/bootc/baseimage/base"; + /// check for the existence of the /var/run directory /// if it exists we need to check that it links to /run if not error /// if it does not exist error. @@ -23,6 +27,7 @@ pub(crate) fn lint(root: &Dir) -> Result<()> { check_parse_kargs, check_usretc, check_utf8, + check_baseimage_root, ]; for lint in lints { lint(&root)?; @@ -116,6 +121,57 @@ fn check_utf8(dir: &Dir) -> Result<()> { Ok(()) } +/// Check for a few files and directories we expect in the base image. +fn check_baseimage_root_norecurse(dir: &Dir) -> Result<()> { + // Check /sysroot + let meta = dir.symlink_metadata_optional("sysroot")?; + match meta { + Some(meta) if !meta.is_dir() => { + anyhow::bail!("Expected a directory for /sysroot") + } + None => anyhow::bail!("Missing /sysroot"), + _ => {} + } + + // Check /ostree -> sysroot/ostree + let Some(meta) = dir.symlink_metadata_optional("ostree")? else { + anyhow::bail!("Missing ostree -> sysroot/ostree link") + }; + if !meta.is_symlink() { + anyhow::bail!("/ostree should be a symlink"); + } + let link = dir.read_link_contents("ostree")?; + let expected = "sysroot/ostree"; + if link.as_os_str().as_bytes() != expected.as_bytes() { + anyhow::bail!("Expected /ostree -> {expected}, not {link:?}"); + } + + // Check the prepare-root config + let prepareroot_path = "usr/lib/ostree/prepare-root.conf"; + let config_data = dir + .read_to_string(prepareroot_path) + .context(prepareroot_path)?; + let config = ostree_ext::glib::KeyFile::new(); + config.load_from_data(&config_data, ostree_ext::glib::KeyFileFlags::empty())?; + + if !ostree_ext::ostree_prepareroot::overlayfs_enabled_in_config(&config)? { + anyhow::bail!("{prepareroot_path} does not have composefs enabled") + } + + Ok(()) +} + +/// Check ostree-related base image content. +fn check_baseimage_root(dir: &Dir) -> Result<()> { + check_baseimage_root_norecurse(dir)?; + // If we have our own documentation with the expected root contents + // embedded, then check that too! Mostly just because recursion is fun. + if let Some(dir) = dir.open_dir_optional(BASEIMAGE_REF)? { + check_baseimage_root_norecurse(&dir)?; + } + Ok(()) +} + #[cfg(test)] mod tests { use super::*; @@ -260,4 +316,34 @@ mod tests { root.remove_file(badfile).unwrap(); // Get rid of the problem check_utf8(root).unwrap(); // Check it } + + #[test] + fn test_baseimage_root() -> Result<()> { + use bootc_utils::CommandRunExt; + use cap_std_ext::cmdext::CapStdExtCommandExt; + use std::path::Path; + + let td = fixture()?; + + // An empty root should fail our test + assert!(check_baseimage_root(&td).is_err()); + + // Copy our reference base image content from the source dir + let manifest = std::env::var_os("CARGO_MANIFEST_PATH").unwrap(); + let srcdir = Path::new(&manifest) + .parent() + .unwrap() + .join("../baseimage/base"); + for ent in std::fs::read_dir(srcdir)? { + let ent = ent?; + std::process::Command::new("cp") + .cwd_dir(td.try_clone()?) + .arg("-pr") + .arg(ent.path()) + .arg(".") + .run()?; + } + check_baseimage_root(&td).unwrap(); + Ok(()) + } }