diff --git a/crates/lib/src/store/mod.rs b/crates/lib/src/store/mod.rs index 500216d9..ff166018 100644 --- a/crates/lib/src/store/mod.rs +++ b/crates/lib/src/store/mod.rs @@ -145,28 +145,27 @@ pub(crate) enum BootedStorageKind<'a> { Composefs(BootedComposefs), } +/// Open the physical root (/sysroot) and /run directories for a booted system. +fn get_physical_root_and_run() -> Result<(Dir, Dir)> { + let physical_root = { + let d = Dir::open_ambient_dir("/sysroot", cap_std::ambient_authority()) + .context("Opening /sysroot")?; + open_dir_remount_rw(&d, ".".into())? + }; + let run = + Dir::open_ambient_dir("/run", cap_std::ambient_authority()).context("Opening /run")?; + Ok((physical_root, run)) +} + impl BootedStorage { /// Create a new booted storage accessor for the given environment. /// /// The caller must have already called `prepare_for_write()` if /// `env.needs_mount_namespace()` is true. pub(crate) async fn new(env: Environment) -> Result> { - let physical_root = { - let d = Dir::open_ambient_dir("/sysroot", cap_std::ambient_authority()) - .context("Opening /sysroot")?; - // Remount /sysroot rw only if we are in a new mount ns - if env.needs_mount_namespace() { - open_dir_remount_rw(&d, ".".into())? - } else { - d - } - }; - - let run = - Dir::open_ambient_dir("/run", cap_std::ambient_authority()).context("Opening /run")?; - let r = match &env { Environment::ComposefsBooted(cmdline) => { + let (physical_root, run) = get_physical_root_and_run()?; let mut composefs = ComposefsRepository::open_path(&physical_root, COMPOSEFS)?; if cmdline.insecure { composefs.set_insecure(true); @@ -204,6 +203,7 @@ impl BootedStorage { // remount /sysroot as writable, and we call set_mount_namespace_in_use() // to indicate we're in a mount namespace. Without actually being in a // mount namespace, this would leave the global /sysroot writable. + let (physical_root, run) = get_physical_root_and_run()?; let sysroot = ostree::Sysroot::new_default(); sysroot.set_mount_namespace_in_use(); @@ -223,6 +223,7 @@ impl BootedStorage { Some(Self { storage }) } + // For container or non-bootc environments, there's no storage Environment::Container | Environment::Other => None, }; Ok(r) diff --git a/hack/provision-packit.sh b/hack/provision-packit.sh index 3a3abafa..7f1848bf 100755 --- a/hack/provision-packit.sh +++ b/hack/provision-packit.sh @@ -4,6 +4,9 @@ set -exuo pipefail # Check environment printenv +# This must work outside of a container too +bootc status + # temp folder to save building files and folders BOOTC_TEMPDIR=$(mktemp -d) trap 'rm -rf -- "$BOOTC_TEMPDIR"' EXIT diff --git a/tests/container/status-outside-container/run b/tests/container/status-outside-container/run new file mode 100755 index 00000000..06e98fe9 --- /dev/null +++ b/tests/container/status-outside-container/run @@ -0,0 +1,30 @@ +#!/bin/bash +# Test that `bootc status` works on a non-bootc-deployed system. +# +# This simulates a "package mode" environment where bootc is installed +# via RPM on a traditional system (not deployed via ostree/composefs). +# The key is hiding /run/.containerenv so bootc doesn't think it's in a container. +# +# xref: https://issues.redhat.com/browse/RHEL-135687 +set -euo pipefail +image=$1 + +# Run the container with: +# - A tmpfs over /run to hide .containerenv (simulates bare metal) +# - Unset the 'container' environment variable +# - No /sysroot (simulates package mode) +# Use --format=humanreadable to get consistent output +output=$(podman run --rm \ + --tmpfs /run \ + --env container= \ + "$image" \ + bootc status --format=humanreadable 2>&1) + +# Verify the output indicates this is not a bootc-deployed system +if echo "$output" | grep -q "System is not deployed via bootc"; then + echo "ok status-outside-container: correctly reports non-bootc system" +else + echo "FAIL: unexpected output from bootc status:" >&2 + echo "$output" >&2 + exit 1 +fi