- 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
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:
- Staged Deployment Finalization: Completes the deployment of staged composefs images
- Boot Process Integration: Executes early in the boot process via systemd service
- Filesystem Operations: Performs EROFS mounting and /etc merging operations
- Bootloader Management: Updates bootloader entries for staged deployments
- 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, ¤t_etc, &new_etc)?;
let diff = compute_diff(&pristine_files, ¤t_files)?;
merge(¤t_etc, ¤t_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, ¤t_etc, &new_etc)?;
let diff = compute_diff(&pristine_files, ¤t_files)?;
merge(¤t_etc, ¤t_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.