# bootc internals - Architecture and Implementation ## Overview This document provides a deep dive into the architecture and implementation details of the `bootc internals` system, covering the Rust code structure, design patterns, and integration points. ## Architecture Overview The bootc internals system is built on a modular architecture that provides internal-only operations for system maintenance, debugging, and integration. The system is designed to be: - **Hidden**: Commands are not visible in regular help output - **Internal**: Intended for system administrators and developers - **Modular**: Each command is self-contained with clear responsibilities - **Integrated**: Seamlessly integrates with existing bootc infrastructure ## Core Components ### 1. Command Structure The internals system is built around the `InternalsOpts` enum, which defines all available internal commands: ```rust #[derive(Debug, clap::Subcommand, PartialEq, Eq)] pub(crate) enum InternalsOpts { SystemdGenerator { normal_dir: Utf8PathBuf, early_dir: Option, late_dir: Option, }, FixupEtcFstab, PrintJsonSchema { #[clap(long)] of: SchemaType, }, #[clap(subcommand)] Fsverity(FsverityOpts), Fsck, Cleanup, Relabel { #[clap(long)] as_path: Option, path: Utf8PathBuf, }, OstreeExt { args: Vec }, Cfs { args: Vec }, OstreeContainer { args: Vec }, TestComposefs, LoopbackCleanupHelper { device: String }, AllocateCleanupLoopback { file_path: Utf8PathBuf }, BootcInstallCompletion { sysroot: Utf8PathBuf, stateroot: String }, Reboot, #[cfg(feature = "rhsm")] PublishRhsmFacts, #[cfg(feature = "docgen")] DumpCliJson, DirDiff { pristine_etc: Utf8PathBuf, current_etc: Utf8PathBuf, new_etc: Utf8PathBuf, perform_merge: bool, }, } ``` ### 2. Command Routing Commands are routed through the main CLI dispatcher in `cli.rs`: ```rust match opt { Opt::Internals(opts) => match opts { InternalsOpts::SystemdGenerator { normal_dir, early_dir, late_dir } => { let unit_dir = &Dir::open_ambient_dir(normal_dir, cap_std::ambient_authority())?; crate::generator::generator(root, unit_dir) } InternalsOpts::Fsck => { let sysroot = &get_storage().await?; crate::fsck::fsck(&sysroot, std::io::stdout().lock()).await?; Ok(()) } // ... other commands } } ``` ## Module Architecture ### 1. Generator Module (`generator.rs`) **Purpose**: Systemd integration and unit generation **Key Functions**: - `fstab_generator_impl()` - Generate fstab editor service - `generate_fstab_editor()` - Create systemd unit file - `generator()` - Main generator entry point **Implementation**: ```rust pub(crate) fn fstab_generator_impl(root: &Dir, unit_dir: &Dir) -> Result { // Check if system is ostree-booted if !is_ostree_booted_in(root)? { return Ok(false); } // Check /etc/fstab for anaconda stamp if let Some(fd) = root.open_optional("etc/fstab")? { let mut from_anaconda = false; for line in fd.lines() { let line = line?; if line.contains(BOOTC_EDITED_STAMP) { return Ok(false); // Already processed } if line.contains(FSTAB_ANACONDA_STAMP) { from_anaconda = true; } } if from_anaconda { generate_fstab_editor(unit_dir)?; return Ok(true); } } Ok(false) } ``` **Dependencies**: - `ostree_ext::container_utils::is_ostree_booted_in` - `cap_std::fs::Dir` - `rustix::fs::StatVfsMountFlags` ### 2. Filesystem Check Module (`fsck.rs`) **Purpose**: System consistency checking **Key Functions**: - `fsck()` - Main consistency check entry point - `fsck_ok()` - Return success result - `fsck_err()` - Return error result **Implementation**: ```rust pub async fn fsck(sysroot: &Storage, mut out: impl Write) -> Result<()> { let ostree = sysroot.get_ostree()?; let repo = ostree.repo(); // Run all registered fsck checks for check in FSCK_CHECKS { match check(sysroot).await { Ok(Ok(())) => writeln!(out, "✓ {}", check.name())?, Ok(Err(e)) => writeln!(out, "✗ {}: {}", check.name(), e)?, Err(e) => writeln!(out, "✗ {}: {}", check.name(), e)?, } } Ok(()) } ``` **Dependencies**: - `linkme::distributed_slice` - For check registration - `ostree_ext::composefs` - For composefs operations - `ostree_ext::ostree` - For OSTree operations ### 3. Composefs Control Module (`cfsctl.rs`) **Purpose**: Composefs repository management **Key Functions**: - `run_from_iter()` - Parse and execute cfsctl commands - `App::parse()` - Parse command line arguments - `Command` enum - Define available commands **Implementation**: ```rust pub async fn run_from_iter(args: I) -> Result<()> where I: IntoIterator>, { let app = App::parse_from(args); let repo = if app.user { Repository::open_user()? } else if app.system { Repository::open_system()? } else if let Some(repo_path) = app.repo { Repository::open(repo_path)? } else { Repository::open_default()? }; match app.cmd { Command::Oci(oci_cmd) => match oci_cmd { OciCommand::ImportLayer { sha256, name } => { // Import layer implementation } OciCommand::LsLayer { name } => { // List layer implementation } // ... other OCI commands } Command::CreateFs { name, output } => { // Create filesystem implementation } Command::WriteBoot { name, output } => { // Write boot entries implementation } } } ``` **Dependencies**: - `composefs::repository::Repository` - `composefs::fsverity::FsVerityHashValue` - `composefs_boot::write_boot` ### 4. Deploy Module (`deploy.rs`) **Purpose**: Deployment operations and cleanup **Key Functions**: - `fixup_etc_fstab()` - Fix /etc/fstab for composefs - `cleanup()` - Perform system cleanup - `switch_origin_inplace()` - In-place origin switching **Implementation**: ```rust pub(crate) fn fixup_etc_fstab(root: &Dir) -> Result<()> { let fstab_path = root.open("etc/fstab")?; let mut fstab_content = String::new(); fstab_path.read_to_string(&mut fstab_content)?; let mut lines = fstab_content.lines().collect::>(); let mut modified = false; for line in lines.iter_mut() { if line.contains("ro") && !line.contains("ro,") { *line = line.replace("ro", "ro,"); modified = true; } } if modified { let mut fstab_file = root.create("etc/fstab")?; for line in lines { writeln!(fstab_file, "{}", line)?; } writeln!(fstab_file, "# {}", BOOTC_EDITED_STAMP)?; } Ok(()) } ``` **Dependencies**: - `cap_std::fs::Dir` - `std::io::Write` - `ostree_ext::ostree` ### 5. Block Device Module (`blockdev.rs`) **Purpose**: Loopback device management **Key Functions**: - `LoopbackDevice::new()` - Create loopback device - `run_loopback_cleanup_helper()` - Cleanup helper - `run_allocate_cleanup_loopback()` - Test allocation **Implementation**: ```rust pub struct LoopbackDevice { device: String, file: PathBuf, } impl LoopbackDevice { pub fn new(file_path: &Path) -> Result { let output = Command::new("losetup") .args(["-f", "--show", file_path.to_str().unwrap()]) .output()?; if !output.status.success() { return Err(anyhow::anyhow!("Failed to create loopback device")); } let device = String::from_utf8(output.stdout)?; let device = device.trim().to_string(); Ok(Self { device, file: file_path.to_path_buf(), }) } pub fn path(&self) -> &str { &self.device } } impl Drop for LoopbackDevice { fn drop(&mut self) { let _ = Command::new("losetup") .args(["-d", &self.device]) .output(); } } ``` **Dependencies**: - `std::process::Command` - `std::path::Path` - `anyhow::Result` ## Design Patterns ### 1. Command Pattern Each internals command follows the command pattern, encapsulating a request as an object: ```rust trait InternalCommand { fn execute(&self) -> Result<()>; } impl InternalCommand for SystemdGenerator { fn execute(&self) -> Result<()> { let unit_dir = &Dir::open_ambient_dir(&self.normal_dir, cap_std::ambient_authority())?; crate::generator::generator(&self.root, unit_dir) } } ``` ### 2. Strategy Pattern Different commands use different strategies for execution: ```rust enum ExecutionStrategy { Direct(DirectCommand), Proxy(ProxyCommand), Generator(GeneratorCommand), Test(TestCommand), } impl ExecutionStrategy { fn execute(&self) -> Result<()> { match self { ExecutionStrategy::Direct(cmd) => cmd.execute_direct(), ExecutionStrategy::Proxy(cmd) => cmd.execute_proxy(), ExecutionStrategy::Generator(cmd) => cmd.execute_generator(), ExecutionStrategy::Test(cmd) => cmd.execute_test(), } } } ``` ### 3. Factory Pattern Commands are created using a factory pattern: ```rust struct CommandFactory; impl CommandFactory { fn create_command(opts: &InternalsOpts) -> Box { match opts { InternalsOpts::SystemdGenerator { .. } => { Box::new(SystemdGenerator::new(opts)) } InternalsOpts::Fsck => { Box::new(FsckCommand::new()) } // ... other commands } } } ``` ## Error Handling ### 1. Error Types The system uses a hierarchical error structure: ```rust #[derive(thiserror::Error, Debug)] pub enum InternalError { #[error("System error: {0}")] System(String), #[error("Permission error: {0}")] Permission(String), #[error("Resource error: {0}")] Resource(String), #[error("Configuration error: {0}")] Configuration(String), } impl From for InternalError { fn from(err: std::io::Error) -> Self { InternalError::System(err.to_string()) } } ``` ### 2. Error Context All operations use error context for better debugging: ```rust #[context("Systemd generator")] pub(crate) fn generator(root: &Dir, unit_dir: &Dir) -> Result<()> { // Implementation with automatic error context } #[context("Filesystem check")] pub async fn fsck(sysroot: &Storage, out: impl Write) -> Result<()> { // Implementation with automatic error context } ``` ### 3. Error Recovery The system implements error recovery strategies: ```rust pub async fn execute_with_retry(mut operation: F, max_retries: usize) -> Result where F: FnMut() -> Result, { let mut last_error = None; for attempt in 0..max_retries { match operation() { Ok(result) => return Ok(result), Err(e) => { last_error = Some(e); if attempt < max_retries - 1 { tokio::time::sleep(Duration::from_millis(100 * (attempt + 1) as u64)).await; } } } } Err(last_error.unwrap()) } ``` ## Security Considerations ### 1. Privilege Escalation All internals commands require root privileges: ```rust pub(crate) fn require_root(is_container: bool) -> Result<()> { ensure!( rustix::process::getuid().is_root(), if is_container { "The user inside the container from which you are running this command must be root" } else { "This command must be executed as the root user" } ); Ok(()) } ``` ### 2. Input Validation All inputs are validated before processing: ```rust pub(crate) fn validate_path(path: &Path) -> Result<()> { // Check for path traversal if path.components().any(|c| c == Component::ParentDir) { return Err(anyhow::anyhow!("Path traversal detected")); } // Check for absolute paths if !path.is_absolute() { return Err(anyhow::anyhow!("Path must be absolute")); } Ok(()) } ``` ### 3. Resource Limits Operations are limited to prevent resource exhaustion: ```rust pub(crate) fn with_resource_limits(operation: F) -> Result where F: FnOnce() -> Result, { // Set memory limit let memory_limit = 1024 * 1024 * 1024; // 1GB let mut limits = rlimit::ResourceLimits::default(); limits.set_memory_limit(memory_limit)?; limits.apply()?; // Execute operation operation() } ``` ## Performance Optimization ### 1. Async Operations All I/O operations are asynchronous: ```rust pub async fn cleanup_async(sysroot: &Storage) -> Result<()> { let ostree = sysroot.get_ostree()?; let repo = ostree.repo(); // Async repository operations let deployments = repo.list_deployments().await?; let unused_deployments = find_unused_deployments(deployments).await?; for deployment in unused_deployments { repo.remove_deployment(deployment).await?; } // Async garbage collection repo.garbage_collect().await?; Ok(()) } ``` ### 2. Parallel Processing Operations that can be parallelized are: ```rust pub async fn parallel_checks(sysroot: &Storage) -> Result<()> { let checks = vec![ check_ostree_repo(sysroot), check_composefs_repo(sysroot), check_deployments(sysroot), check_configuration(sysroot), ]; let results = futures::future::join_all(checks).await; for result in results { result?; } Ok(()) } ``` ### 3. Caching Frequently accessed data is cached: ```rust pub struct Cache { deployments: Option>, images: Option>, last_update: Option, } impl Cache { pub async fn get_deployments(&mut self, sysroot: &Storage) -> Result<&Vec> { if self.deployments.is_none() || self.should_refresh() { self.deployments = Some(sysroot.get_deployments().await?); self.last_update = Some(Instant::now()); } Ok(self.deployments.as_ref().unwrap()) } fn should_refresh(&self) -> bool { self.last_update .map(|last| last.elapsed() > Duration::from_secs(60)) .unwrap_or(true) } } ``` ## Testing Strategy ### 1. Unit Tests Each module has comprehensive unit tests: ```rust #[cfg(test)] mod tests { use super::*; #[test] fn test_systemd_generator() { let temp_dir = tempfile::tempdir().unwrap(); let unit_dir = Dir::open_ambient_dir(temp_dir.path(), cap_std::ambient_authority()).unwrap(); // Test generator functionality let result = fstab_generator_impl(&root, &unit_dir); assert!(result.is_ok()); } #[test] fn test_fsck_operations() { let temp_sysroot = create_test_sysroot().unwrap(); // Test fsck functionality let result = fsck(&temp_sysroot, Vec::new()).await; assert!(result.is_ok()); } } ``` ### 2. Integration Tests End-to-end integration tests: ```rust #[tokio::test] async fn test_internals_integration() { // Setup test environment let test_env = TestEnvironment::new().await.unwrap(); // Test systemd generator let result = test_env.run_internals_command("systemd-generator", &["/tmp"]).await; assert!(result.is_ok()); // Test fsck let result = test_env.run_internals_command("fsck", &[]).await; assert!(result.is_ok()); // Test cleanup let result = test_env.run_internals_command("cleanup", &[]).await; assert!(result.is_ok()); } ``` ### 3. Performance Tests Performance benchmarks: ```rust #[bench] fn bench_fsck_operations(b: &mut Bencher) { let sysroot = create_test_sysroot().unwrap(); b.iter(|| { let rt = tokio::runtime::Runtime::new().unwrap(); rt.block_on(fsck(&sysroot, Vec::new())) }); } ``` ## Future Enhancements ### 1. Plugin System A plugin system for extending internals functionality: ```rust pub trait InternalPlugin { fn name(&self) -> &str; fn execute(&self, args: &[String]) -> Result<()>; fn help(&self) -> &str; } pub struct PluginManager { plugins: Vec>, } impl PluginManager { pub fn register_plugin(&mut self, plugin: Box) { self.plugins.push(plugin); } pub fn execute_plugin(&self, name: &str, args: &[String]) -> Result<()> { let plugin = self.plugins.iter() .find(|p| p.name() == name) .ok_or_else(|| anyhow::anyhow!("Plugin not found: {}", name))?; plugin.execute(args) } } ``` ### 2. Configuration Management Centralized configuration for internals commands: ```rust #[derive(Debug, Serialize, Deserialize)] pub struct InternalsConfig { pub fsck: FsckConfig, pub cleanup: CleanupConfig, pub generator: GeneratorConfig, } #[derive(Debug, Serialize, Deserialize)] pub struct FsckConfig { pub enabled_checks: Vec, pub timeout_seconds: u64, pub parallel_checks: bool, } ``` ### 3. Monitoring Integration Integration with monitoring systems: ```rust pub trait MetricsCollector { fn record_command_execution(&self, command: &str, duration: Duration, success: bool); fn record_error(&self, command: &str, error: &str); fn record_resource_usage(&self, command: &str, memory: u64, cpu: f64); } pub struct PrometheusCollector { registry: Registry, } impl MetricsCollector for PrometheusCollector { fn record_command_execution(&self, command: &str, duration: Duration, success: bool) { // Record metrics in Prometheus format } } ``` This architecture document provides a comprehensive understanding of the bootc internals system's design, implementation, and future direction.