1
0
mirror of https://github.com/containers/bootc.git synced 2026-02-05 15:45:53 +01:00

install feature is always on, add install-to-disk

I consider `bootc install to-filesystem` support a key feature of bootc.
In theory today one can still set up a system directly with `ostree`
and we will continue to support that.

But things like logically bound images we do want to be initialized
from the start and that's with `bootc install to-filesystem`.

Maintaining the feature just has a logistical annoyance any
time one touches the install code as we often end up needing
to carefully `#[cfg(feature = "install")]` in many places
in an infectious way.

Also as we head towards enabling factory reset
https://github.com/containers/bootc/issues/404
we really do want some of the install code enabled there.

However, `to-disk` is much more of a "demo". I don't want
bootc to grow too much knowledge around block devices. Complex
setups (LVM, LUKS) etc. are the domain of external installers
and provisioning tools.

So the feature gate is now on that (which is still on by default).

We ended up with more `#[cfg(feature = "install-to-disk")]` than
I'd have liked, but some of that can be fixed subsequently.

Signed-off-by: Colin Walters <walters@verbum.org>
This commit is contained in:
Colin Walters
2024-12-19 13:22:27 -05:00
parent 7bea5efb80
commit 3e6ea489b7
15 changed files with 71 additions and 80 deletions

View File

@@ -53,9 +53,11 @@ similar-asserts = { workspace = true }
static_assertions = { workspace = true }
[features]
default = ["install"]
# This feature enables `bootc install`. Disable if you always want to use an external installer.
install = []
default = ["install-to-disk"]
# This feature enables `bootc install to-disk`, which is considered just a "demo"
# or reference installer; we expect most nontrivial use cases to be using
# `bootc install to-filesystem`.
install-to-disk = []
# This featuares enables `bootc internals publish-rhsm-facts` to integrate with
# Red Hat Subscription Manager
rhsm = []

View File

@@ -1,15 +1,20 @@
use std::collections::HashMap;
#[cfg(feature = "install-to-disk")]
use std::env;
#[cfg(feature = "install-to-disk")]
use std::path::Path;
use std::process::Command;
use std::sync::OnceLock;
use anyhow::{anyhow, Context, Result};
use camino::{Utf8Path, Utf8PathBuf};
use camino::Utf8Path;
#[cfg(feature = "install-to-disk")]
use camino::Utf8PathBuf;
use fn_error_context::context;
use regex::Regex;
use serde::Deserialize;
#[cfg(feature = "install-to-disk")]
use crate::install::run_in_host_mountns;
use crate::task::Task;
use bootc_utils::CommandRunExt;
@@ -47,6 +52,7 @@ impl Device {
self.path.clone().unwrap_or(format!("/dev/{}", &self.name))
}
#[allow(dead_code)]
pub(crate) fn has_children(&self) -> bool {
self.children.as_ref().map_or(false, |v| !v.is_empty())
}
@@ -86,6 +92,7 @@ impl Device {
}
#[context("Failed to wipe {dev}")]
#[cfg(feature = "install-to-disk")]
pub(crate) fn wipefs(dev: &Utf8Path) -> Result<()> {
Task::new_and_run(
format!("Wiping device {dev}"),
@@ -161,6 +168,7 @@ impl PartitionTable {
}
// Find the partition with the given offset (starting at 1)
#[allow(dead_code)]
pub(crate) fn find_partno(&self, partno: u32) -> Result<&Partition> {
let r = self
.partitions
@@ -171,6 +179,7 @@ impl PartitionTable {
}
impl Partition {
#[allow(dead_code)]
pub(crate) fn path(&self) -> &Utf8Path {
self.node.as_str().into()
}
@@ -185,10 +194,12 @@ pub(crate) fn partitions_of(dev: &Utf8Path) -> Result<PartitionTable> {
Ok(o.partitiontable)
}
#[cfg(feature = "install-to-disk")]
pub(crate) struct LoopbackDevice {
pub(crate) dev: Option<Utf8PathBuf>,
}
#[cfg(feature = "install-to-disk")]
impl LoopbackDevice {
// Create a new loopback block device targeting the provided file path.
pub(crate) fn new(path: &Path) -> Result<Self> {
@@ -243,6 +254,7 @@ impl LoopbackDevice {
}
}
#[cfg(feature = "install-to-disk")]
impl Drop for LoopbackDevice {
fn drop(&mut self) {
// Best effort to unmount if we're dropped without invoking `close`
@@ -250,6 +262,7 @@ impl Drop for LoopbackDevice {
}
}
#[cfg(feature = "install-to-disk")]
pub(crate) fn udev_settle() -> Result<()> {
// There's a potential window after rereading the partition table where
// udevd hasn't yet received updates from the kernel, settle will return
@@ -311,6 +324,7 @@ pub(crate) fn find_parent_devices(device: &str) -> Result<Vec<String>> {
}
/// Parse a string into mibibytes
#[cfg(feature = "install-to-disk")]
pub(crate) fn parse_size_mib(mut s: &str) -> Result<u64> {
let suffixes = [
("MiB", 1u64),

View File

@@ -7,8 +7,11 @@ use crate::task::Task;
/// The name of the mountpoint for efi (as a subdirectory of /boot, or at the toplevel)
pub(crate) const EFI_DIR: &str = "efi";
#[cfg(feature = "install-to-disk")]
pub(crate) const ESP_GUID: &str = "C12A7328-F81F-11D2-BA4B-00A0C93EC93B";
#[cfg(feature = "install-to-disk")]
pub(crate) const PREPBOOT_GUID: &str = "9E1A2D38-C612-4316-AA26-8B49521E5A8B";
#[cfg(feature = "install-to-disk")]
pub(crate) const PREPBOOT_LABEL: &str = "PowerPC-PReP-boot";
#[cfg(target_arch = "powerpc64")]
/// We make a best-effort to support MBR partitioning too.

View File

@@ -10,7 +10,6 @@ use camino::Utf8Path;
use cap_std_ext::cap_std::fs::Dir;
use cap_std_ext::dirext::CapStdExtDirExt;
use fn_error_context::context;
#[cfg(feature = "install")]
use ostree_ext::containers_image_proxy;
use ostree_ext::ostree::Deployment;
@@ -33,7 +32,6 @@ pub(crate) struct BoundImage {
}
#[derive(Debug, PartialEq, Eq)]
#[cfg(feature = "install")]
pub(crate) struct ResolvedBoundImage {
pub(crate) image: String,
pub(crate) digest: String,
@@ -104,7 +102,6 @@ pub(crate) fn query_bound_images(root: &Dir) -> Result<Vec<BoundImage>> {
Ok(bound_images)
}
#[cfg(feature = "install")]
impl ResolvedBoundImage {
#[context("resolving bound image {}", src.image)]
pub(crate) async fn from_image(src: &BoundImage) -> Result<Self> {

View File

@@ -184,7 +184,6 @@ pub(crate) struct StatusOpts {
pub(crate) booted: bool,
}
#[cfg(feature = "install")]
#[derive(Debug, clap::Subcommand, PartialEq, Eq)]
pub(crate) enum InstallOpts {
/// Install to the target block device.
@@ -197,6 +196,7 @@ pub(crate) enum InstallOpts {
/// in the container image, alongside any required system partitions such as
/// the EFI system partition. Use `install to-filesystem` for anything more
/// complex such as RAID, LVM, LUKS etc.
#[cfg(feature = "install-to-disk")]
ToDisk(crate::install::InstallToDiskOpts),
/// Install to an externally created filesystem structure.
///
@@ -384,7 +384,6 @@ pub(crate) enum InternalsOpts {
#[clap(allow_hyphen_values = true)]
args: Vec<OsString>,
},
#[cfg(feature = "install")]
/// Invoked from ostree-ext to complete an installation.
BootcInstallCompletion {
/// Path to the sysroot
@@ -525,7 +524,6 @@ pub(crate) enum Opt {
/// An installation is not simply a copy of the container filesystem, but includes
/// other setup and metadata.
#[clap(subcommand)]
#[cfg(feature = "install")]
Install(InstallOpts),
/// Operations which can be executed as part of a container build.
#[clap(subcommand)]
@@ -537,7 +535,6 @@ pub(crate) enum Opt {
#[clap(subcommand, hide = true)]
Image(ImageOpts),
/// Execute the given command in the host mount namespace
#[cfg(feature = "install")]
#[clap(hide = true)]
ExecInHostMountNamespace {
#[clap(trailing_var_arg = true, allow_hyphen_values = true)]
@@ -1053,8 +1050,8 @@ async fn run_from_opt(opt: Opt) -> Result<()> {
}
}
},
#[cfg(feature = "install")]
Opt::Install(opts) => match opts {
#[cfg(feature = "install-to-disk")]
InstallOpts::ToDisk(opts) => crate::install::install_to_disk(opts).await,
InstallOpts::ToFilesystem(opts) => {
crate::install::install_to_filesystem(opts, false).await
@@ -1068,7 +1065,6 @@ async fn run_from_opt(opt: Opt) -> Result<()> {
crate::install::completion::run_from_anaconda(rootfs).await
}
},
#[cfg(feature = "install")]
Opt::ExecInHostMountNamespace { args } => {
crate::install::exec_in_host_mountns(args.as_slice())
}
@@ -1104,7 +1100,6 @@ async fn run_from_opt(opt: Opt) -> Result<()> {
let sysroot = get_storage().await?;
crate::deploy::cleanup(&sysroot).await
}
#[cfg(feature = "install")]
InternalsOpts::BootcInstallCompletion { sysroot, stateroot } => {
let rootfs = &Dir::open_ambient_dir("/", cap_std::ambient_authority())?;
crate::install::completion::run_from_ostree(rootfs, &sysroot, &stateroot).await

View File

@@ -6,6 +6,7 @@
// This sub-module is the "basic" installer that handles creating basic block device
// and filesystem setup.
#[cfg(feature = "install-to-disk")]
pub(crate) mod baseline;
pub(crate) mod completion;
pub(crate) mod config;
@@ -40,9 +41,12 @@ use ostree_ext::oci_spec;
use ostree_ext::ostree;
use ostree_ext::prelude::Cast;
use ostree_ext::sysroot::SysrootLock;
use rustix::fs::{FileTypeExt, MetadataExt as _};
#[cfg(feature = "install-to-disk")]
use rustix::fs::FileTypeExt;
use rustix::fs::MetadataExt as _;
use serde::{Deserialize, Serialize};
#[cfg(feature = "install-to-disk")]
use self::baseline::InstallBlockDeviceOpts;
use crate::boundimage::{BoundImage, ResolvedBoundImage};
use crate::containerenv::ContainerExecutionInfo;
@@ -57,6 +61,7 @@ use crate::utils::sigpolicy_from_opts;
/// The toplevel boot directory
const BOOT: &str = "boot";
/// Directory for transient runtime state
#[cfg(feature = "install-to-disk")]
const RUN_BOOTC: &str = "/run/bootc";
/// The default path for the host rootfs
const ALONGSIDE_ROOT_MOUNT: &str = "/target";
@@ -65,10 +70,8 @@ const LOST_AND_FOUND: &str = "lost+found";
/// The filename of the composefs EROFS superblock; TODO move this into ostree
const OSTREE_COMPOSEFS_SUPER: &str = ".ostree.cfs";
/// The mount path for selinux
#[cfg(feature = "install")]
const SELINUXFS: &str = "/sys/fs/selinux";
/// The mount path for uefi
#[cfg(feature = "install")]
const EFIVARFS: &str = "/sys/firmware/efi/efivars";
pub(crate) const ARCH_USES_EFI: bool = cfg!(any(target_arch = "x86_64", target_arch = "aarch64"));
@@ -202,6 +205,7 @@ pub(crate) struct InstallConfigOpts {
pub(crate) stateroot: Option<String>,
}
#[cfg(feature = "install-to-disk")]
#[derive(Debug, Clone, clap::Parser, Serialize, Deserialize, PartialEq, Eq)]
pub(crate) struct InstallToDiskOpts {
#[clap(flatten)]
@@ -375,6 +379,7 @@ impl State {
}
#[context("Finalizing state")]
#[allow(dead_code)]
pub(crate) fn consume(self) -> Result<()> {
self.tempdir.close()?;
// If we had invoked `setenforce 0`, then let's re-enable it.
@@ -880,6 +885,7 @@ pub(crate) fn exec_in_host_mountns(args: &[std::ffi::OsString]) -> Result<()> {
}
pub(crate) struct RootSetup {
#[cfg(feature = "install-to-disk")]
luks_device: Option<String>,
device_info: crate::blockdev::PartitionTable,
/// Absolute path to the location where we've mounted the physical
@@ -907,12 +913,14 @@ impl RootSetup {
}
// Drop any open file descriptors and return just the mount path and backing luks device, if any
#[cfg(feature = "install-to-disk")]
fn into_storage(self) -> (Utf8PathBuf, Option<String>) {
(self.physical_root_path, self.luks_device)
}
}
#[derive(Debug)]
#[allow(dead_code)]
pub(crate) enum SELinuxFinalState {
/// Host and target both have SELinux, but user forced it off for target
ForceTargetDisabled,
@@ -1449,6 +1457,7 @@ fn installation_complete() {
/// Implementation of the `bootc install to-disk` CLI command.
#[context("Installing to disk")]
#[cfg(feature = "install-to-disk")]
pub(crate) async fn install_to_disk(mut opts: InstallToDiskOpts) -> Result<()> {
let mut block_opts = opts.block_opts;
let target_blockdev_meta = block_opts
@@ -1845,6 +1854,7 @@ pub(crate) async fn install_to_filesystem(
let skip_finalize =
matches!(fsopts.replace, Some(ReplaceMode::Alongside)) || fsopts.skip_finalize;
let mut rootfs = RootSetup {
#[cfg(feature = "install-to-disk")]
luks_device: None,
device_info,
physical_root_path: fsopts.root_path,

View File

@@ -21,12 +21,14 @@ use clap::ValueEnum;
use fn_error_context::context;
use serde::{Deserialize, Serialize};
use super::config::Filesystem;
use super::MountSpec;
use super::RootSetup;
use super::State;
use super::RUN_BOOTC;
use super::RW_KARG;
use crate::mount;
#[cfg(feature = "install-to-disk")]
use crate::mount::is_mounted_in_pid1_mountns;
use crate::task::Task;
@@ -36,20 +38,6 @@ pub(crate) const EFIPN_SIZE_MB: u32 = 512;
/// The GPT type for "linux"
pub(crate) const LINUX_PARTTYPE: &str = "0FC63DAF-8483-4772-8E79-3D69D8477DE4";
#[derive(clap::ValueEnum, Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub(crate) enum Filesystem {
Xfs,
Ext4,
Btrfs,
}
impl Display for Filesystem {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.to_possible_value().unwrap().get_name().fmt(f)
}
}
#[derive(clap::ValueEnum, Default, Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub(crate) enum BlockSetup {
@@ -104,6 +92,7 @@ impl BlockSetup {
}
}
#[cfg(feature = "install-to-disk")]
fn mkfs<'a>(
dev: &str,
fs: Filesystem,
@@ -143,6 +132,7 @@ fn mkfs<'a>(
}
#[context("Creating rootfs")]
#[cfg(feature = "install-to-disk")]
pub(crate) fn install_create_rootfs(
state: &State,
opts: InstallBlockDeviceOpts,

View File

@@ -3,9 +3,11 @@
//! This module handles the TOML configuration file for `bootc install`.
use anyhow::{Context, Result};
use clap::ValueEnum;
use fn_error_context::context;
use serde::{Deserialize, Serialize};
#[cfg(feature = "install-to-disk")]
use super::baseline::BlockSetup;
/// Properties of the environment, such as the system architecture
@@ -14,6 +16,21 @@ pub(crate) struct EnvProperties {
pub(crate) sys_arch: String,
}
/// A well known filesystem type.
#[derive(clap::ValueEnum, Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub(crate) enum Filesystem {
Xfs,
Ext4,
Btrfs,
}
impl std::fmt::Display for Filesystem {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.to_possible_value().unwrap().get_name().fmt(f)
}
}
/// The toplevel config entry for installation configs stored
/// in bootc/install (e.g. /etc/bootc/install/05-custom.toml)
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
@@ -27,7 +44,7 @@ pub(crate) struct InstallConfigurationToplevel {
#[serde(deny_unknown_fields)]
pub(crate) struct RootFS {
#[serde(rename = "type")]
pub(crate) fstype: Option<super::baseline::Filesystem>,
pub(crate) fstype: Option<Filesystem>,
}
/// This structure should only define "system" or "basic" filesystems; we are
@@ -46,8 +63,9 @@ pub(crate) struct BasicFilesystems {
#[serde(rename = "install", rename_all = "kebab-case", deny_unknown_fields)]
pub(crate) struct InstallConfiguration {
/// Root filesystem type
pub(crate) root_fs_type: Option<super::baseline::Filesystem>,
pub(crate) root_fs_type: Option<Filesystem>,
/// Enabled block storage configurations
#[cfg(feature = "install-to-disk")]
pub(crate) block: Option<Vec<BlockSetup>>,
pub(crate) filesystem: Option<BasicFilesystems>,
/// Kernel arguments, applied at installation time
@@ -112,6 +130,7 @@ impl Mergeable for InstallConfiguration {
.unwrap_or(true)
{
merge_basic(&mut self.root_fs_type, other.root_fs_type, env);
#[cfg(feature = "install-to-disk")]
merge_basic(&mut self.block, other.block, env);
self.filesystem.merge(other.filesystem, env);
if let Some(other_kargs) = other.kargs {
@@ -139,6 +158,7 @@ impl InstallConfiguration {
root.fstype = Some(*rootfs);
}
#[cfg(feature = "install-to-disk")]
if self.block.is_none() {
self.block = Some(vec![BlockSetup::Direct]);
}
@@ -154,6 +174,7 @@ impl InstallConfiguration {
self.kargs.take();
}
#[cfg(feature = "install-to-disk")]
pub(crate) fn get_block_setup(&self, default: Option<BlockSetup>) -> Result<BlockSetup> {
let valid_block_setups = self.block.as_deref().unwrap_or_default();
let default_block = valid_block_setups.iter().next().ok_or_else(|| {
@@ -217,8 +238,6 @@ pub(crate) fn load_config() -> Result<Option<InstallConfiguration>> {
mod tests {
use super::*;
use super::super::baseline::Filesystem;
#[test]
/// Verify that we can parse our default config file
fn test_parse_config() {

View File

@@ -53,7 +53,6 @@ pub(crate) fn get_kargs_in_root(d: &Dir, sys_arch: &str) -> Result<Vec<String>>
}
/// Load kargs.d files from the target ostree commit root
#[cfg(feature = "install")]
pub(crate) fn get_kargs_from_ostree_root(
repo: &ostree::Repo,
root: &ostree::RepoFile,

View File

@@ -30,17 +30,11 @@ mod utils;
#[cfg(feature = "docgen")]
mod docgen;
#[cfg(feature = "install")]
mod blockdev;
#[cfg(feature = "install")]
mod bootloader;
#[cfg(feature = "install")]
mod containerenv;
#[cfg(feature = "install")]
mod install;
#[cfg(feature = "install")]
mod kernel;
#[cfg(feature = "install")]
pub(crate) mod mount;
#[cfg(feature = "rhsm")]

View File

@@ -1,4 +1,3 @@
#[cfg(feature = "install")]
use std::io::Write;
use std::os::fd::AsRawFd;
use std::os::unix::process::CommandExt;
@@ -9,14 +8,10 @@ use anyhow::{Context, Result};
use bootc_utils::CommandRunExt;
use camino::{Utf8Path, Utf8PathBuf};
use cap_std::fs::Dir;
#[cfg(feature = "install")]
use cap_std::fs::{DirBuilder, OpenOptions};
#[cfg(feature = "install")]
use cap_std::io_lifetimes::AsFilelike;
use cap_std_ext::cap_std;
#[cfg(feature = "install")]
use cap_std_ext::cap_std::fs::{Metadata, MetadataExt};
#[cfg(feature = "install")]
use cap_std_ext::dirext::CapStdExtDirExt;
use fn_error_context::context;
use ostree_ext::gio;
@@ -24,10 +19,8 @@ use ostree_ext::ostree;
use rustix::fd::AsFd;
/// The mount path for selinux
#[cfg(feature = "install")]
const SELINUXFS: &str = "/sys/fs/selinux";
/// The SELinux xattr
#[cfg(feature = "install")]
const SELINUX_XATTR: &[u8] = b"security.selinux\0";
const SELF_CURRENT: &str = "/proc/self/attr/current";
@@ -99,7 +92,6 @@ pub(crate) fn selinux_ensure_install() -> Result<bool> {
}
/// Query whether SELinux is apparently enabled in the target root
#[cfg(feature = "install")]
pub(crate) fn have_selinux_policy(root: &Dir) -> Result<bool> {
// TODO use ostree::SePolicy and query policy name
root.try_exists("etc/selinux/config").map_err(Into::into)
@@ -108,17 +100,17 @@ pub(crate) fn have_selinux_policy(root: &Dir) -> Result<bool> {
/// A type which will reset SELinux back to enforcing mode when dropped.
/// This is a workaround for the deep difficulties in trying to reliably
/// gain the `mac_admin` permission (install_t).
#[cfg(feature = "install")]
#[must_use]
#[derive(Debug)]
#[allow(dead_code)]
pub(crate) struct SetEnforceGuard(Option<()>);
#[cfg(feature = "install")]
impl SetEnforceGuard {
pub(crate) fn new() -> Self {
SetEnforceGuard(Some(()))
}
#[allow(dead_code)]
pub(crate) fn consume(mut self) -> Result<()> {
// SAFETY: The option cannot have been consumed until now
self.0.take().unwrap();
@@ -127,7 +119,6 @@ impl SetEnforceGuard {
}
}
#[cfg(feature = "install")]
impl Drop for SetEnforceGuard {
fn drop(&mut self) {
// A best-effort attempt to re-enable enforcement on drop (installation failure)
@@ -140,7 +131,6 @@ impl Drop for SetEnforceGuard {
/// Try to enter the install_t domain, but if we can't do that, then
/// just setenforce 0.
#[context("Ensuring selinux install_t type")]
#[cfg(feature = "install")]
pub(crate) fn selinux_ensure_install_or_setenforce() -> Result<Option<SetEnforceGuard>> {
// If the process already has install_t, exit early
// Note that this may re-exec the entire process
@@ -160,7 +150,6 @@ pub(crate) fn selinux_ensure_install_or_setenforce() -> Result<Option<SetEnforce
#[context("Setting SELinux permissive mode")]
#[allow(dead_code)]
#[cfg(feature = "install")]
pub(crate) fn selinux_set_permissive(permissive: bool) -> Result<()> {
let enforce_path = &Utf8Path::new(SELINUXFS).join("enforce");
if !enforce_path.exists() {
@@ -179,7 +168,6 @@ pub(crate) fn selinux_set_permissive(permissive: bool) -> Result<()> {
Ok(())
}
#[cfg(feature = "install")]
/// Check if the ostree-formatted extended attributes include a security.selinux value.
pub(crate) fn xattrs_have_selinux(xattrs: &ostree::glib::Variant) -> bool {
let n = xattrs.n_children();
@@ -223,7 +211,6 @@ pub(crate) fn set_security_selinux(fd: std::os::fd::BorrowedFd, label: &[u8]) ->
/// The labeling state; "unsupported" is distinct as we need to handle
/// cases like the ESP which don't support labeling.
#[cfg(feature = "install")]
pub(crate) enum SELinuxLabelState {
Unlabeled,
Unsupported,
@@ -231,7 +218,6 @@ pub(crate) enum SELinuxLabelState {
}
/// Query the SELinux labeling for a particular path
#[cfg(feature = "install")]
pub(crate) fn has_security_selinux(root: &Dir, path: &Utf8Path) -> Result<SELinuxLabelState> {
// TODO: avoid hardcoding a max size here
let mut buf = [0u8; 2048];
@@ -244,7 +230,6 @@ pub(crate) fn has_security_selinux(root: &Dir, path: &Utf8Path) -> Result<SELinu
}
}
#[cfg(feature = "install")]
pub(crate) fn set_security_selinux_path(root: &Dir, path: &Utf8Path, label: &[u8]) -> Result<()> {
// TODO: avoid hardcoding a max size here
let fdpath = format!("/proc/self/fd/{}/", root.as_raw_fd());
@@ -258,7 +243,6 @@ pub(crate) fn set_security_selinux_path(root: &Dir, path: &Utf8Path, label: &[u8
Ok(())
}
#[cfg(feature = "install")]
pub(crate) fn ensure_labeled(
root: &Dir,
path: &Utf8Path,
@@ -277,7 +261,6 @@ pub(crate) fn ensure_labeled(
/// A wrapper for creating a directory, also optionally setting a SELinux label.
/// The provided `skip` parameter is a device/inode that we will ignore (and not traverse).
#[cfg(feature = "install")]
pub(crate) fn ensure_dir_labeled_recurse(
root: &Dir,
path: &mut Utf8PathBuf,
@@ -340,7 +323,6 @@ pub(crate) fn ensure_dir_labeled_recurse(
}
/// A wrapper for creating a directory, also optionally setting a SELinux label.
#[cfg(feature = "install")]
pub(crate) fn ensure_dir_labeled(
root: &Dir,
destname: impl AsRef<Utf8Path>,
@@ -387,7 +369,6 @@ pub(crate) fn ensure_dir_labeled(
}
/// A wrapper for atomically writing a file, also optionally setting a SELinux label.
#[cfg(feature = "install")]
pub(crate) fn atomic_replace_labeled<F>(
root: &Dir,
destname: impl AsRef<Utf8Path>,

View File

@@ -21,6 +21,7 @@ use rustix::{
};
use serde::Deserialize;
#[cfg(feature = "install-to-disk")]
use crate::task::Task;
/// Well known identifier for pid 1
@@ -90,6 +91,7 @@ pub(crate) fn inspect_filesystem_by_uuid(uuid: &str) -> Result<Filesystem> {
// Check if a specified device contains an already mounted filesystem
// in the root mount namespace
#[cfg(feature = "install-to-disk")]
pub(crate) fn is_mounted_in_pid1_mountns(path: &str) -> Result<bool> {
let o = run_findmnt(&["-N"], "1")?;
@@ -99,6 +101,7 @@ pub(crate) fn is_mounted_in_pid1_mountns(path: &str) -> Result<bool> {
}
// Recursively check a given filesystem to see if it contains an already mounted source
#[cfg(feature = "install-to-disk")]
pub(crate) fn is_source_mounted(path: &str, mounted_fs: &Filesystem) -> bool {
if mounted_fs.source.contains(path) {
return true;
@@ -116,6 +119,7 @@ pub(crate) fn is_source_mounted(path: &str, mounted_fs: &Filesystem) -> bool {
}
/// Mount a device to the target path.
#[cfg(feature = "install-to-disk")]
pub(crate) fn mount(dev: &str, target: &Utf8Path) -> Result<()> {
Task::new_and_run(
format!("Mounting {target}"),

View File

@@ -1,19 +1,14 @@
#[cfg(feature = "install")]
use anyhow::Result;
#[cfg(feature = "install")]
use camino::Utf8Path;
#[cfg(feature = "install")]
use cap_std_ext::cap_std::fs::Dir;
use serde::Deserialize;
/// Where we look inside our container to find our own image
/// for use with `bootc install`.
#[cfg(feature = "install")]
pub(crate) const CONTAINER_STORAGE: &str = "/var/lib/containers";
#[derive(Deserialize)]
#[serde(rename_all = "PascalCase")]
#[cfg(feature = "install")]
pub(crate) struct Inspect {
pub(crate) digest: String,
}
@@ -27,7 +22,6 @@ pub(crate) struct ImageListEntry {
}
/// Given an image ID, return its manifest digest
#[cfg(feature = "install")]
pub(crate) fn imageid_to_digest(imgid: &str) -> Result<String> {
use bootc_utils::CommandRunExt;
let o: Vec<Inspect> = crate::install::run_in_host_mountns("podman")
@@ -41,7 +35,6 @@ pub(crate) fn imageid_to_digest(imgid: &str) -> Result<String> {
}
/// Return true if there is apparently an active container store at the target path.
#[cfg(feature = "install")]
pub(crate) fn storage_exists(root: &Dir, path: impl AsRef<Utf8Path>) -> Result<bool> {
fn impl_storage_exists(root: &Dir, path: &Utf8Path) -> Result<bool> {
let lock = "storage.lock";
@@ -55,7 +48,6 @@ pub(crate) fn storage_exists(root: &Dir, path: impl AsRef<Utf8Path>) -> Result<b
///
/// Note this does not attempt to parse the root filesystem's container storage configuration,
/// this uses a hardcoded default path.
#[cfg(feature = "install")]
pub(crate) fn storage_exists_default(root: &Dir) -> Result<bool> {
storage_exists(root, CONTAINER_STORAGE.trim_start_matches('/'))
}

View File

@@ -102,7 +102,6 @@ pub(crate) struct Deployments {
pub(crate) other: VecDeque<ostree::Deployment>,
}
#[cfg(feature = "install")]
pub(crate) fn try_deserialize_timestamp(t: &str) -> Option<chrono::DateTime<chrono::Utc>> {
match chrono::DateTime::parse_from_rfc3339(t).context("Parsing timestamp") {
Ok(t) => Some(t.into()),

View File

@@ -7,14 +7,10 @@ use std::time::Duration;
use anyhow::{Context, Result};
use bootc_utils::CommandRunExt;
#[cfg(feature = "install")]
use camino::Utf8Path;
use cap_std_ext::cap_std::fs::Dir;
#[cfg(feature = "install")]
use cap_std_ext::dirext::CapStdExtDirExt;
#[cfg(feature = "install")]
use cap_std_ext::prelude::CapStdExtCommandExt;
#[cfg(feature = "install")]
use fn_error_context::context;
use indicatif::HumanDuration;
use libsystemd::logging::journal_print;
@@ -88,7 +84,6 @@ pub fn openat2_with_retry(
/// Given an mount option string list like foo,bar=baz,something=else,ro parse it and find
/// the first entry like $optname=
/// This will not match a bare `optname` without an equals.
#[cfg(feature = "install")]
pub(crate) fn find_mount_option<'a>(
option_string_list: &'a str,
optname: &'_ str,
@@ -102,7 +97,6 @@ pub(crate) fn find_mount_option<'a>(
/// Given a target directory, if it's a read-only mount, then remount it writable
#[context("Opening {target} with writable mount")]
#[cfg(feature = "install")]
pub(crate) fn open_dir_remount_rw(root: &Dir, target: &Utf8Path) -> Result<Dir> {
if matches!(root.is_mountpoint(target), Ok(Some(true))) {
tracing::debug!("Target {target} is a mountpoint, remounting rw");
@@ -118,7 +112,6 @@ pub(crate) fn open_dir_remount_rw(root: &Dir, target: &Utf8Path) -> Result<Dir>
/// Given a target path, remove its immutability if present
#[context("Removing immutable flag from {target}")]
#[cfg(feature = "install")]
pub(crate) fn remove_immutability(root: &Dir, target: &Utf8Path) -> Result<()> {
use anyhow::ensure;
@@ -175,7 +168,6 @@ pub(crate) fn sigpolicy_from_opts(
/// Output a warning message that we want to be quite visible.
/// The process (thread) execution will be delayed for a short time.
#[cfg(feature = "install")]
pub(crate) fn medium_visibility_warning(s: &str) {
anstream::eprintln!(
"{}{s}{}",