bootc-docs/composefs-finalize-staged/bootc-composefs-finalize-staged-technical-guide.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

14 KiB

bootc composefs-finalize-staged - Technical Guide

Overview

bootc composefs-finalize-staged is a hidden command specific to the composefs-backend branch that finalizes staged composefs deployments. This command is executed early in the boot process by a systemd service to complete the deployment of staged composefs images.

Purpose

The composefs-finalize-staged command serves several critical functions:

  1. Staged Deployment Finalization: Completes the deployment of staged composefs images
  2. Boot Process Integration: Executes early in the boot process via systemd service
  3. Filesystem Operations: Performs EROFS mounting and /etc merging operations
  4. Bootloader Management: Updates bootloader entries for staged deployments
  5. System State Management: Manages composefs deployment state transitions

Command Structure

#[cfg(feature = "composefs-backend")]
ComposefsFinalizeStaged,

The command is conditionally compiled only when the composefs-backend feature is enabled.

Core Functionality

Purpose

The command finalizes staged composefs deployments by:

  • Mounting the booted EROFS image to access pristine /etc configuration
  • Performing 3-way merge of /etc configurations (pristine, current, new)
  • Updating bootloader entries for staged deployments
  • Managing bootloader-specific operations (GRUB vs systemd-boot)

Usage

bootc composefs-finalize-staged

Implementation

pub(crate) async fn composefs_native_finalize() -> Result<()> {
    let host = composefs_deployment_status().await?;

    let booted_composefs = host.require_composefs_booted()?;

    let Some(staged_depl) = host.status.staged.as_ref() else {
        tracing::debug!("No staged deployment found");
        return Ok(());
    };

    let staged_composefs = staged_depl.composefs.as_ref().ok_or(anyhow::anyhow!(
        "Staged deployment is not a composefs deployment"
    ))?;

    // Mount the booted EROFS image to get pristine etc
    let sysroot = open_dir(CWD, "/sysroot")?;
    let composefs_fd = mount_composefs_image(&sysroot, &booted_composefs.verity, false)?;

    let erofs_tmp_mnt = TempMount::mount_fd(&composefs_fd)?;

    // Perform the /etc merge
    let pristine_etc =
        Dir::open_ambient_dir(erofs_tmp_mnt.dir.path().join("etc"), ambient_authority())?;
    let current_etc = Dir::open_ambient_dir("/etc", ambient_authority())?;

    let new_etc_path = Path::new(STATE_DIR_ABS)
        .join(&staged_composefs.verity)
        .join("etc");

    let new_etc = Dir::open_ambient_dir(new_etc_path, ambient_authority())?;

    let (pristine_files, current_files, new_files) =
        traverse_etc(&pristine_etc, &current_etc, &new_etc)?;

    let diff = compute_diff(&pristine_files, &current_files)?;
    merge(&current_etc, &current_files, &new_etc, &new_files, diff)?;

    // Unmount EROFS
    drop(erofs_tmp_mnt);

    let sysroot_parent = get_sysroot_parent_dev()?;
    // NOTE: Assumption here that ESP will always be present
    let (esp_part, ..) = get_esp_partition(&sysroot_parent)?;

    let esp_mount = TempMount::mount_dev(&esp_part)?;
    let boot_dir = Dir::open_ambient_dir("/sysroot/boot", ambient_authority())
        .context("Opening sysroot/boot")?;

    // NOTE: Assuming here we won't have two bootloaders at the same time
    match booted_composefs.bootloader {
        Bootloader::Grub => match staged_composefs.boot_type {
            BootType::Bls => {
                let entries_dir = boot_dir.open_dir("loader")?;
                rename_exchange_bls_entries(&entries_dir)?;
            }
            BootType::Uki => finalize_staged_grub_uki(&esp_mount.fd, &boot_dir)?,
        },

        Bootloader::Systemd => match staged_composefs.boot_type {
            BootType::Bls => {
                let entries_dir = esp_mount.fd.open_dir("loader")?;
                rename_exchange_bls_entries(&entries_dir)?;
            }
            BootType::Uki => rename_staged_uki_entries(&esp_mount.fd)?,
        },
    };

    Ok(())
}

Technical Details

1. Deployment Status Retrieval

The command first retrieves the current composefs deployment status:

let host = composefs_deployment_status().await?;
let booted_composefs = host.require_composefs_booted()?;

2. Staged Deployment Validation

The command validates that a staged deployment exists and is a composefs deployment:

let Some(staged_depl) = host.status.staged.as_ref() else {
    tracing::debug!("No staged deployment found");
    return Ok(());
};

let staged_composefs = staged_depl.composefs.as_ref().ok_or(anyhow::anyhow!(
    "Staged deployment is not a composefs deployment"
))?;

3. EROFS Image Mounting

The command mounts the booted EROFS image to access pristine /etc configuration:

let sysroot = open_dir(CWD, "/sysroot")?;
let composefs_fd = mount_composefs_image(&sysroot, &booted_composefs.verity, false)?;
let erofs_tmp_mnt = TempMount::mount_fd(&composefs_fd)?;

4. /etc Configuration Merging

The command performs a 3-way merge of /etc configurations:

let pristine_etc =
    Dir::open_ambient_dir(erofs_tmp_mnt.dir.path().join("etc"), ambient_authority())?;
let current_etc = Dir::open_ambient_dir("/etc", ambient_authority())?;

let new_etc_path = Path::new(STATE_DIR_ABS)
    .join(&staged_composefs.verity)
    .join("etc");

let new_etc = Dir::open_ambient_dir(new_etc_path, ambient_authority())?;

let (pristine_files, current_files, new_files) =
    traverse_etc(&pristine_etc, &current_etc, &new_etc)?;

let diff = compute_diff(&pristine_files, &current_files)?;
merge(&current_etc, &current_files, &new_etc, &new_files, diff)?;

5. Bootloader Management

The command updates bootloader entries based on the bootloader type and boot type:

match booted_composefs.bootloader {
    Bootloader::Grub => match staged_composefs.boot_type {
        BootType::Bls => {
            let entries_dir = boot_dir.open_dir("loader")?;
            rename_exchange_bls_entries(&entries_dir)?;
        }
        BootType::Uki => finalize_staged_grub_uki(&esp_mount.fd, &boot_dir)?,
    },

    Bootloader::Systemd => match staged_composefs.boot_type {
        BootType::Bls => {
            let entries_dir = esp_mount.fd.open_dir("loader")?;
            rename_exchange_bls_entries(&entries_dir)?;
        }
        BootType::Uki => rename_staged_uki_entries(&esp_mount.fd)?,
    },
}

Command Routing

The command is routed through the main CLI dispatcher:

#[cfg(feature = "composefs-backend")]
Opt::ComposefsFinalizeStaged => composefs_native_finalize().await,

Systemd Integration

Service Definition

The command is executed by a systemd service:

[Unit]
Description=Composefs Finalize Staged Deployment
Documentation=man:bootc(1)
DefaultDependencies=no

RequiresMountsFor=/sysroot
After=local-fs.target
Before=basic.target final.target
After=systemd-journal-flush.service
Conflicts=final.target

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStop=/usr/bin/bootc composefs-finalize-staged
TimeoutStopSec=5m
ProtectHome=yes
ReadOnlyPaths=/etc

Service Characteristics

  • Type: oneshot - runs once and exits
  • RemainAfterExit: yes - service remains active after completion
  • ExecStop: executes the composefs-finalize-staged command
  • TimeoutStopSec: 5 minutes - long timeout for slow media
  • ProtectHome: yes - sandboxing for security
  • ReadOnlyPaths: /etc - prevents modification of current /etc

Bootloader Operations

GRUB Bootloader

BLS (Boot Loader Specification) Entries

BootType::Bls => {
    let entries_dir = boot_dir.open_dir("loader")?;
    rename_exchange_bls_entries(&entries_dir)?;
}

UKI (Unified Kernel Image) Entries

BootType::Uki => finalize_staged_grub_uki(&esp_mount.fd, &boot_dir)?,

systemd-boot Bootloader

BLS Entries

BootType::Bls => {
    let entries_dir = esp_mount.fd.open_dir("loader")?;
    rename_exchange_bls_entries(&entries_dir)?;
}

UKI Entries

BootType::Uki => rename_staged_uki_entries(&esp_mount.fd)?,

Filesystem Operations

1. EROFS Mounting

The command mounts EROFS images for access to pristine configuration:

let composefs_fd = mount_composefs_image(&sysroot, &booted_composefs.verity, false)?;
let erofs_tmp_mnt = TempMount::mount_fd(&composefs_fd)?;

2. ESP Partition Mounting

The command mounts the ESP partition for bootloader operations:

let sysroot_parent = get_sysroot_parent_dev()?;
let (esp_part, ..) = get_esp_partition(&sysroot_parent)?;
let esp_mount = TempMount::mount_dev(&esp_part)?;

3. /etc Configuration Merging

The command performs 3-way merge of /etc configurations:

  • Pristine: Original configuration from EROFS image
  • Current: Current system configuration
  • New: New configuration from staged deployment

Error Handling

1. Common Error Scenarios

No Staged Deployment

let Some(staged_depl) = host.status.staged.as_ref() else {
    tracing::debug!("No staged deployment found");
    return Ok(());
};

Non-Composefs Deployment

let staged_composefs = staged_depl.composefs.as_ref().ok_or(anyhow::anyhow!(
    "Staged deployment is not a composefs deployment"
))?;

Filesystem Access Errors

let sysroot = open_dir(CWD, "/sysroot")?;
let composefs_fd = mount_composefs_image(&sysroot, &booted_composefs.verity, false)?;

2. Error Recovery

The command provides comprehensive error context:

#[context("Getting composefs deployment status")]
pub(crate) async fn composefs_deployment_status() -> Result<Host> {
    // Implementation with automatic error context
}

Performance Characteristics

1. Execution Time

  • Fast: Optimized for early boot execution
  • Efficient: Minimal overhead for staging operations
  • Atomic: Single atomic operation

2. Resource Usage

  • Low CPU: Minimal CPU usage
  • Low Memory: Minimal memory usage
  • Disk I/O: Efficient filesystem operations

3. System Impact

  • Early Boot: Executes early in boot process
  • Filesystem Access: Accesses EROFS and ESP partitions
  • Bootloader Updates: Updates bootloader entries

Security Considerations

1. Privilege Requirements

The command requires appropriate privileges for:

  • Filesystem mounting operations
  • Bootloader entry modifications
  • System state management

2. Sandboxing

The systemd service provides sandboxing:

  • ProtectHome: yes - restricts home directory access
  • ReadOnlyPaths: /etc - prevents /etc modification

3. Access Control

The command accesses:

  • EROFS images: For pristine configuration
  • ESP partition: For bootloader operations
  • System state: For deployment management

Integration Points

1. Composefs Backend

The command integrates with the composefs backend:

#[cfg(feature = "composefs-backend")]
use crate::bootc_composefs::{
    finalize::composefs_native_finalize, rollback::composefs_rollback, status::composefs_booted,
    switch::switch_composefs, update::upgrade_composefs,
};

2. Systemd Service

The command integrates with systemd for early boot execution:

ExecStop=/usr/bin/bootc composefs-finalize-staged

3. Bootloader Management

The command integrates with bootloader management:

match booted_composefs.bootloader {
    Bootloader::Grub => { /* GRUB operations */ }
    Bootloader::Systemd => { /* systemd-boot operations */ }
}

Use Cases

1. Staged Deployment Finalization

The primary use case is finalizing staged composefs deployments:

# Executed by systemd service during boot
systemctl start composefs-finalize-staged.service

2. Boot Process Integration

The command integrates with the boot process:

# Service runs early in boot process
systemctl enable composefs-finalize-staged.service

3. Configuration Management

The command manages /etc configuration merging:

# Performs 3-way merge of /etc configurations
# - Pristine: Original from EROFS
# - Current: Current system
# - New: Staged deployment

4. Bootloader Updates

The command updates bootloader entries:

# Updates GRUB or systemd-boot entries
# - BLS entries for Boot Loader Specification
# - UKI entries for Unified Kernel Image

Testing and Validation

1. Unit Tests

#[cfg(test)]
mod tests {
    use super::*;

    #[tokio::test]
    async fn test_composefs_native_finalize() {
        // Test finalization process
        let result = composefs_native_finalize().await;
        assert!(result.is_ok());
    }
}

2. Integration Tests

#[tokio::test]
async fn test_composefs_finalize_integration() {
    // Test with staged deployment
    let test_env = TestEnvironment::new().await.unwrap();
    
    // Create staged deployment
    test_env.create_staged_deployment().await.unwrap();
    
    // Test finalization
    let result = composefs_native_finalize().await;
    assert!(result.is_ok());
}

Best Practices

1. Usage Guidelines

  • Systemd Integration: Use systemd service for execution
  • Early Boot: Execute early in boot process
  • Error Handling: Implement proper error handling
  • Logging: Use appropriate logging levels

2. Security Considerations

  • Privilege Requirements: Ensure appropriate privileges
  • Sandboxing: Use systemd sandboxing features
  • Access Control: Limit filesystem access
  • Error Recovery: Implement proper error recovery

3. Performance Optimization

  • Efficient Operations: Use efficient filesystem operations
  • Resource Management: Manage resources appropriately
  • Timeout Handling: Handle timeouts gracefully
  • Error Recovery: Implement proper error recovery

Future Enhancements

1. Enhanced Error Handling

Enhanced error handling and recovery:

pub(crate) struct FinalizeOptions {
    pub retry_attempts: u32,
    pub timeout_seconds: u64,
    pub rollback_on_failure: bool,
}

2. Performance Monitoring

Performance monitoring and metrics:

pub(crate) struct FinalizeMetrics {
    pub execution_time: Duration,
    pub files_processed: u64,
    pub merge_operations: u64,
}

3. Configuration Options

Configuration options for finalization:

pub(crate) struct FinalizeConfig {
    pub enable_etc_merge: bool,
    pub enable_bootloader_update: bool,
    pub backup_original: bool,
}

This technical guide provides comprehensive understanding of the bootc composefs-finalize-staged system's functionality, implementation, and usage patterns.