bootc-docs/install/source-code-analysis.md
robojerk 526f1c1afd Initial commit: Comprehensive Debian bootc documentation
- Complete documentation for all bootc commands and subcommands
- Debian-specific adaptations and workarounds
- Manual installation methods to bypass bootc reliability issues
- Technical guides with Rust source code analysis
- Flowcharts and external command references
- Hidden command documentation (bootc internals, state, etc.)
- Composefs integration analysis
- Base image creation guides (with and without bootc binary)
- Management scripts and automation
- Comprehensive troubleshooting and examples
2025-09-15 14:02:28 -07:00

13 KiB

bootc Installation Source Code Analysis

Overview

This document provides a detailed analysis of the bootc installation process based on the source code examination. The analysis covers the key components, data structures, and execution flow of the installation system.

Core Data Structures

1. State Management

// From install.rs:373-390
pub(crate) struct State {
    pub(crate) source: SourceInfo,
    pub(crate) selinux_state: SELinuxFinalState,
    pub(crate) config_opts: InstallConfigOpts,
    pub(crate) target_imgref: ostree_container::OstreeImageReference,
    pub(crate) prepareroot_config: HashMap<String, String>,
    pub(crate) install_config: Option<config::InstallConfiguration>,
    pub(crate) root_ssh_authorized_keys: Option<String>,
    pub(crate) host_is_container: bool,
    pub(crate) container_root: Dir,
    pub(crate) tempdir: TempDir,
}

Key Components:

  • source: Container image information and metadata
  • selinux_state: SELinux configuration state
  • target_imgref: Target image reference for updates
  • prepareroot_config: OSTree prepareroot configuration
  • install_config: Installation-specific configuration

2. Source Information

// From install.rs:360-370
pub(crate) struct SourceInfo {
    pub(crate) imageref: ostree_container::ImageReference,
    pub(crate) digest: Option<String>,
    pub(crate) selinux: bool,
    pub(crate) in_host_mountns: bool,
}

Purpose: Encapsulates information about the source container image Key Fields:

  • imageref: Container image reference
  • digest: Image digest for verification
  • selinux: SELinux policy presence
  • in_host_mountns: Host mount namespace access

3. Root Setup

// From install.rs:928-942
pub(crate) struct RootSetup {
    #[cfg(feature = "install-to-disk")]
    luks_device: Option<String>,
    device_info: bootc_blockdev::PartitionTable,
    physical_root_path: Utf8PathBuf,
    physical_root: Dir,
    rootfs_uuid: Option<String>,
    skip_finalize: bool,
    boot: Option<MountSpec>,
    kargs: Vec<String>,
}

Purpose: Manages target filesystem setup and configuration Key Fields:

  • device_info: Partition table information
  • physical_root: Target filesystem directory
  • rootfs_uuid: Root filesystem UUID
  • boot: Boot filesystem mount specification
  • kargs: Kernel arguments

Installation Flow Analysis

1. Preparation Phase

Container Environment Detection

// From install.rs:520-538
pub(crate) fn from_container(
    root: &Dir,
    container_info: &ContainerExecutionInfo,
) -> Result<Self> {
    if !container_info.engine.starts_with("podman") {
        anyhow::bail!("Currently this command only supports being executed via podman");
    }
    if container_info.imageid.is_empty() {
        anyhow::bail!("Invalid empty imageid");
    }
    let imageref = ostree_container::ImageReference {
        transport: ostree_container::Transport::ContainerStorage,
        name: container_info.image.clone(),
    };
    let digest = crate::podman::imageid_to_digest(&container_info.imageid)?;
    Self::new(imageref, Some(digest), root, true)
}

Key Operations:

  1. Validate podman runtime
  2. Extract image reference from container environment
  3. Resolve image digest
  4. Detect SELinux policy presence

Privilege Validation

// From install.rs:1061-1082
fn require_host_pidns() -> Result<()> {
    if rustix::process::getpid().is_init() {
        anyhow::bail!("This command must be run with the podman --pid=host flag")
    }
    tracing::trace!("OK: we're not pid 1");
    Ok(())
}

fn require_host_userns() -> Result<()> {
    let proc1 = "/proc/1";
    let pid1_uid = Path::new(proc1)
        .metadata()
        .with_context(|| format!("Querying {proc1}"))?
        .uid();
    ensure!(pid1_uid == 0, "{proc1} is owned by {pid1_uid}, not zero; this command must be run in the root user namespace");
    tracing::trace!("OK: we're in a matching user namespace with pid1");
    Ok(())
}

Validation Steps:

  1. Check process ID (not PID 1)
  2. Verify host user namespace access
  3. Validate root privileges

2. OSTree Initialization

Repository Setup

// From install.rs:591-694
async fn initialize_ostree_root(state: &State, root_setup: &RootSetup) -> Result<(Storage, bool)> {
    let sepolicy = state.load_policy()?;
    let sepolicy = sepolicy.as_ref();
    let rootfs_dir = &root_setup.physical_root;
    let cancellable = gio::Cancellable::NONE;
    let stateroot = state.stateroot();

    let has_ostree = rootfs_dir.try_exists("ostree/repo")?;
    if !has_ostree {
        Task::new("Initializing ostree layout", "ostree")
            .args(["admin", "init-fs", "--modern", "."])
            .cwd(rootfs_dir)?
            .run()?;
    } else {
        println!("Reusing extant ostree layout");
        let path = ".".into();
        let _ = crate::utils::open_dir_remount_rw(rootfs_dir, path)
            .context("remounting target as read-write")?;
        crate::utils::remove_immutability(rootfs_dir, path)?;
    }
    // ... repository configuration
}

Key Operations:

  1. Check for existing OSTree repository
  2. Initialize repository if needed
  3. Configure repository settings
  4. Set up stateroot
  5. Configure SELinux labeling

Repository Configuration

// From install.rs:624-629
for (k, v) in DEFAULT_REPO_CONFIG.iter() {
    Command::new("ostree")
        .args(["config", "--repo", "ostree/repo", "set", k, v])
        .cwd_dir(rootfs_dir.try_clone()?)
        .run_capture_stderr()?;
}

Default Configuration:

  • sysroot.bootloader=none
  • sysroot.bootprefix=true
  • sysroot.readonly=true

3. Container Image Deployment

Image Pulling

// From install.rs:755-769
let pulled_image = match prepare_for_pull(repo, &spec_imgref, Some(&state.target_imgref))
    .await?
{
    PreparedPullResult::AlreadyPresent(existing) => existing,
    PreparedPullResult::Ready(image_meta) => {
        check_disk_space(root_setup.physical_root.as_fd(), &image_meta, &spec_imgref)?;
        pull_from_prepared(&spec_imgref, false, ProgressWriter::default(), *image_meta).await?
    }
};

Process:

  1. Prepare image for pulling
  2. Check disk space requirements
  3. Pull image into OSTree repository
  4. Handle already present images

Deployment

// From install.rs:826-836
let mut options = ostree_container::deploy::DeployOpts::default();
options.kargs = Some(kargs.as_slice());
options.target_imgref = Some(&state.target_imgref);
options.proxy_cfg = proxy_cfg;
options.skip_completion = true;
options.no_clean = has_ostree;
let imgstate = crate::utils::async_task_with_spinner(
    "Deploying container image",
    ostree_container::deploy::deploy(&sysroot, stateroot, &src_imageref, Some(options)),
)
.await?;

Deployment Options:

  • Kernel arguments configuration
  • Target image reference
  • Proxy configuration
  • Completion skipping
  • Cleanup control

4. Bootloader Installation

Bootupd Integration

// From install.rs:1337-1343
crate::bootloader::install_via_bootupd(
    &rootfs.device_info,
    &rootfs.physical_root_path,
    &state.config_opts,
    &deployment_path.as_str(),
)?;

Architecture Support:

  • x86_64/aarch64: GRUB2 via bootupd
  • s390x: zipl implementation
  • Other: Platform-specific

5. Kernel Argument Processing

Argument Collection

// From install.rs:773-825
let kargsd = crate::bootc_kargs::get_kargs_from_ostree_root(
    &sysroot.repo(),
    merged_ostree_root.downcast_ref().unwrap(),
    std::env::consts::ARCH,
)?;
let kargsd = kargsd.iter().map(|s| s.as_str());

let install_config_kargs = state
    .install_config
    .as_ref()
    .and_then(|c| c.kargs.as_ref())
    .into_iter()
    .flatten()
    .map(|s| s.as_str());

let kargs = root_setup
    .kargs
    .iter()
    .map(|v| v.as_str())
    .chain(install_config_kargs)
    .chain(kargsd)
    .chain(state.config_opts.karg.iter().flatten().map(|v| v.as_str()))
    .collect::<Vec<_>>();

Argument Sources (Priority Order):

  1. Root filesystem kargs
  2. Install configuration kargs
  3. Container image kargs.d files
  4. CLI-specified kargs

6. Filesystem Finalization

Optimization Process

// From install.rs:1033-1058
pub(crate) fn finalize_filesystem(
    fsname: &str,
    root: &Dir,
    path: impl AsRef<Utf8Path>,
) -> Result<()> {
    let path = path.as_ref();
    // fstrim ensures the underlying block device knows about unused space
    Task::new(format!("Trimming {fsname}"), "fstrim")
        .args(["--quiet-unsupported", "-v", path.as_str()])
        .cwd(root)?
        .run()?;
    // Remounting readonly will flush outstanding writes
    Task::new(format!("Finalizing filesystem {fsname}"), "mount")
        .cwd(root)?
        .args(["-o", "remount,ro", path.as_str()])
        .run()?;
    // Finally, freezing (and thawing) the filesystem will flush the journal
    for a in ["-f", "-u"] {
        Command::new("fsfreeze")
            .cwd_dir(root.try_clone()?)
            .args([a, path.as_str()])
            .run_capture_stderr()?;
    }
    Ok(())
}

Finalization Steps:

  1. Trim filesystem (fstrim)
  2. Remount read-only
  3. Freeze and thaw filesystem
  4. Flush journal

Error Handling Patterns

1. Contextual Error Handling

// From install.rs:393-404
#[context("Loading SELinux policy")]
pub(crate) fn load_policy(&self) -> Result<Option<ostree::SePolicy>> {
    if !self.selinux_state.enabled() {
        return Ok(None);
    }
    let r = lsm::new_sepolicy_at(&self.container_root)?
        .ok_or_else(|| anyhow::anyhow!("SELinux enabled, but no policy found in root"))?;
    tracing::debug!("Loaded SELinux policy: {}", r.csum().unwrap());
    Ok(Some(r))
}

2. Resource Validation

// From install.rs:696-715
fn check_disk_space(
    repo_fd: impl AsFd,
    image_meta: &PreparedImportMeta,
    imgref: &ImageReference,
) -> Result<()> {
    let stat = rustix::fs::fstatvfs(repo_fd)?;
    let bytes_avail: u64 = stat.f_bsize * stat.f_bavail;
    
    if image_meta.bytes_to_fetch > bytes_avail {
        anyhow::bail!(
            "Insufficient free space for {image} (available: {bytes_avail} required: {bytes_to_fetch})",
            bytes_avail = ostree_ext::glib::format_size(bytes_avail),
            bytes_to_fetch = ostree_ext::glib::format_size(image_meta.bytes_to_fetch),
            image = imgref.image,
        );
    }
    Ok(())
}

3. Graceful Degradation

// From install.rs:1001-1029
pub(crate) fn reexecute_self_for_selinux_if_needed(
    srcdata: &SourceInfo,
    override_disable_selinux: bool,
) -> Result<SELinuxFinalState> {
    if srcdata.selinux {
        let host_selinux = crate::lsm::selinux_enabled()?;
        let r = if override_disable_selinux {
            println!("notice: Target has SELinux enabled, overriding to disable");
            SELinuxFinalState::ForceTargetDisabled
        } else if host_selinux {
            setup_sys_mount("selinuxfs", SELINUXFS)?;
            let g = crate::lsm::selinux_ensure_install_or_setenforce()?;
            SELinuxFinalState::Enabled(g)
        } else {
            SELinuxFinalState::HostDisabled
        };
        Ok(r)
    } else {
        Ok(SELinuxFinalState::Disabled)
    }
}

Performance Optimizations

1. Async Operations

// From install.rs:1538-1543
let rootfs = tokio::task::spawn_blocking(move || {
    baseline::install_create_rootfs(&state, block_opts)
})
.await??;

2. Parallel Processing

  • Container image pulling
  • Filesystem operations
  • Bootloader installation

3. Resource Management

  • Temporary directory cleanup
  • File descriptor management
  • Memory usage optimization

Security Considerations

1. SELinux Integration

  • Policy detection and loading
  • Context preservation
  • Labeling operations

2. Privilege Management

  • Minimal privilege requirements
  • Capability dropping
  • Namespace isolation

3. Input Validation

  • Command argument validation
  • Path sanitization
  • Resource limit enforcement

Testing and Validation

1. Unit Tests

// From install.rs:2064-2153
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn install_opts_serializable() {
        let c: InstallToDiskOpts = serde_json::from_value(serde_json::json!({
            "device": "/dev/vda"
        }))
        .unwrap();
        assert_eq!(c.block_opts.device, "/dev/vda");
    }
}

2. Integration Tests

  • End-to-end installation testing
  • Error condition testing
  • Performance testing

3. Validation Steps

  • Container image integrity
  • Filesystem compatibility
  • Bootloader support
  • Kernel compatibility

This analysis provides a comprehensive understanding of the bootc installation process from a source code perspective, covering the key components, execution flow, and implementation details.