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
This commit is contained in:
robojerk 2025-09-15 14:02:28 -07:00
commit 526f1c1afd
67 changed files with 34174 additions and 0 deletions

View file

@ -0,0 +1,462 @@
# 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
```rust
// 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
```rust
// 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
```rust
// 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
```rust
// 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
```rust
// 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
```rust
// 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
```rust
// 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
```rust
// 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
```rust
// 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
```rust
// 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
```rust
// 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
```rust
// 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
```rust
// 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
```rust
// 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
```rust
// 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
```rust
// 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
```rust
// 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.