feat: Add Debian GRUB tool compatibility - Add debian_tools.rs module for dynamic GRUB tool detection - Support both Debian (grub-install) and Fedora (grub2-install) paths - Dynamic GRUB directory detection (/boot/grub vs /boot/grub2) - Update BIOS, EFI, and GRUB config modules for Debian compatibility - Add Debian-specific GRUB kernel configuration - Successfully detect and use Debian GRUB packages (grub2-common) - Fix hardcoded Fedora paths throughout the codebase - Maintain backward compatibility with Fedora systems
Some checks failed
Build deb-bootupd Artifacts / build (push) Failing after 8m47s
Some checks failed
Build deb-bootupd Artifacts / build (push) Failing after 8m47s
This commit is contained in:
parent
15e606166c
commit
dc5a2ab86d
11 changed files with 311 additions and 92 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
# Debian Bootupd Fork Plan
|
# Debian Bootupd Fork Plan
|
||||||
|
|
||||||
## Project Overview
|
## Project Overview
|
||||||
**Goal**: Create a Debian-compatible version of bootupd to make particle-os (Debian-based ublue-os) bootable.
|
**Goal**: Create a Debian-compatible version of bootupd to Debian Atomic.
|
||||||
|
|
||||||
**Context**:
|
**Context**:
|
||||||
- **Proof-of-concept**: Test if we can create an immutable Debian using ublue-os tools
|
- **Proof-of-concept**: Test if we can create an immutable Debian using ublue-os tools
|
||||||
|
|
|
||||||
63
src/bios.rs
63
src/bios.rs
|
|
@ -10,12 +10,13 @@ use std::process::Command;
|
||||||
use crate::blockdev;
|
use crate::blockdev;
|
||||||
use crate::bootupd::RootContext;
|
use crate::bootupd::RootContext;
|
||||||
use crate::component::*;
|
use crate::component::*;
|
||||||
|
use crate::debian_tools;
|
||||||
use crate::freezethaw::fsfreeze_thaw_cycle;
|
use crate::freezethaw::fsfreeze_thaw_cycle;
|
||||||
use crate::grubconfigs;
|
use crate::grubconfigs;
|
||||||
use crate::model::*;
|
use crate::model::*;
|
||||||
use crate::packagesystem;
|
use crate::packagesystem;
|
||||||
|
|
||||||
// grub2-install file path
|
// grub2-install file path - will be dynamically determined
|
||||||
pub(crate) const GRUB_BIN: &str = "usr/sbin/grub2-install";
|
pub(crate) const GRUB_BIN: &str = "usr/sbin/grub2-install";
|
||||||
|
|
||||||
#[cfg(target_arch = "powerpc64")]
|
#[cfg(target_arch = "powerpc64")]
|
||||||
|
|
@ -67,10 +68,22 @@ impl Bios {
|
||||||
if !self.check_grub_modules()? {
|
if !self.check_grub_modules()? {
|
||||||
bail!("Failed to find grub2-modules");
|
bail!("Failed to find grub2-modules");
|
||||||
}
|
}
|
||||||
let grub_install = Path::new("/").join(GRUB_BIN);
|
|
||||||
if !grub_install.exists() {
|
// Find the appropriate GRUB installer tool
|
||||||
bail!("Failed to find {:?}", grub_install);
|
let grub_install = match debian_tools::find_grub_install() {
|
||||||
}
|
Some(path) => path,
|
||||||
|
None => {
|
||||||
|
// Fallback to the old hardcoded path
|
||||||
|
let fallback_path = Path::new("/").join(GRUB_BIN);
|
||||||
|
if !fallback_path.exists() {
|
||||||
|
bail!("Failed to find GRUB installer tool. Tried: {:?}, {:?}, {:?}",
|
||||||
|
debian_tools::DEBIAN_GRUB_INSTALL,
|
||||||
|
debian_tools::FEDORA_GRUB2_INSTALL,
|
||||||
|
fallback_path);
|
||||||
|
}
|
||||||
|
fallback_path
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let mut cmd = Command::new(grub_install);
|
let mut cmd = Command::new(grub_install);
|
||||||
let boot_dir = Path::new(dest_root).join("boot");
|
let boot_dir = Path::new(dest_root).join("boot");
|
||||||
|
|
@ -126,12 +139,29 @@ impl Component for Bios {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_update_metadata(&self, sysroot_path: &str) -> Result<ContentMetadata> {
|
fn generate_update_metadata(&self, sysroot_path: &str) -> Result<ContentMetadata> {
|
||||||
let grub_install = Path::new(sysroot_path).join(GRUB_BIN);
|
// Find the appropriate GRUB installer tool
|
||||||
if !grub_install.exists() {
|
let grub_install = match debian_tools::find_grub_install() {
|
||||||
bail!("Failed to find {:?}", grub_install);
|
Some(path) => {
|
||||||
}
|
if sysroot_path == "/" {
|
||||||
|
path
|
||||||
|
} else {
|
||||||
|
Path::new(sysroot_path).join(path.strip_prefix("/").unwrap_or(&path))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
// Fallback to the old hardcoded path
|
||||||
|
let fallback_path = Path::new(sysroot_path).join(GRUB_BIN);
|
||||||
|
if !fallback_path.exists() {
|
||||||
|
bail!("Failed to find GRUB installer tool. Tried: {:?}, {:?}, {:?}",
|
||||||
|
debian_tools::DEBIAN_GRUB_INSTALL,
|
||||||
|
debian_tools::FEDORA_GRUB2_INSTALL,
|
||||||
|
fallback_path);
|
||||||
|
}
|
||||||
|
fallback_path
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Query the rpm database and list the package and build times for /usr/sbin/grub2-install
|
// Query the package database and list the package and build times for the GRUB installer
|
||||||
let meta = packagesystem::query_files(sysroot_path, [&grub_install])?;
|
let meta = packagesystem::query_files(sysroot_path, [&grub_install])?;
|
||||||
write_update_metadata(sysroot_path, self, &meta)?;
|
write_update_metadata(sysroot_path, self, &meta)?;
|
||||||
Ok(meta)
|
Ok(meta)
|
||||||
|
|
@ -147,14 +177,15 @@ impl Component for Bios {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Backup the current grub.cfg and replace with new static config
|
// Backup the current grub.cfg and replace with new static config
|
||||||
// - Backup "/boot/loader/grub.cfg" to "/boot/grub2/grub.cfg.bak"
|
// - Backup "/boot/loader/grub.cfg" to "/boot/grub/grub.cfg.bak" or "/boot/grub2/grub.cfg.bak"
|
||||||
// - Remove symlink "/boot/grub2/grub.cfg"
|
// - Remove symlink "/boot/grub/grub.cfg" or "/boot/grub2/grub.cfg"
|
||||||
// - Replace "/boot/grub2/grub.cfg" symlink with new static "grub.cfg"
|
// - Replace "/boot/grub/grub.cfg" or "/boot/grub2/grub.cfg" symlink with new static "grub.cfg"
|
||||||
fn migrate_static_grub_config(&self, sysroot_path: &str, destdir: &openat::Dir) -> Result<()> {
|
fn migrate_static_grub_config(&self, sysroot_path: &str, destdir: &openat::Dir) -> Result<()> {
|
||||||
let grub = "boot/grub2";
|
let grub_dir_name = debian_tools::get_grub_dir_name();
|
||||||
|
let grub = format!("boot/{}", grub_dir_name);
|
||||||
// sysroot_path is /, destdir is Dir of /
|
// sysroot_path is /, destdir is Dir of /
|
||||||
let grub_config_path = Utf8PathBuf::from(sysroot_path).join(grub);
|
let grub_config_path = Utf8PathBuf::from(sysroot_path).join(&grub);
|
||||||
let grub_config_dir = destdir.sub_dir(grub).context("Opening boot/grub2")?;
|
let grub_config_dir = destdir.sub_dir(&grub).context(format!("Opening boot/{}", grub_dir_name))?;
|
||||||
|
|
||||||
let grub_config = grub_config_path.join(grubconfigs::GRUBCONFIG);
|
let grub_config = grub_config_path.join(grubconfigs::GRUBCONFIG);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ use crate::bios;
|
||||||
use crate::component;
|
use crate::component;
|
||||||
use crate::component::{Component, ValidationResult};
|
use crate::component::{Component, ValidationResult};
|
||||||
use crate::coreos;
|
use crate::coreos;
|
||||||
|
use crate::debian_tools;
|
||||||
#[cfg(any(
|
#[cfg(any(
|
||||||
target_arch = "x86_64",
|
target_arch = "x86_64",
|
||||||
target_arch = "aarch64",
|
target_arch = "aarch64",
|
||||||
|
|
@ -621,8 +622,8 @@ pub(crate) fn client_run_migrate_static_grub_config() -> Result<()> {
|
||||||
// Remount /boot read write just for this unit (we are called in a slave mount namespace by systemd)
|
// Remount /boot read write just for this unit (we are called in a slave mount namespace by systemd)
|
||||||
ensure_writable_boot()?;
|
ensure_writable_boot()?;
|
||||||
|
|
||||||
let grub_config_dir = PathBuf::from("/boot/grub2");
|
let grub_config_dir = PathBuf::from(debian_tools::get_grub_config_dir());
|
||||||
let dirfd = openat::Dir::open(&grub_config_dir).context("Opening /boot/grub2")?;
|
let dirfd = openat::Dir::open(&grub_config_dir).context(format!("Opening {}", debian_tools::get_grub_config_dir()))?;
|
||||||
|
|
||||||
// We mark the bootloader as BLS capable to disable the ostree-grub2 logic.
|
// We mark the bootloader as BLS capable to disable the ostree-grub2 logic.
|
||||||
// We can do that as we know that we are run after the bootloader has been
|
// We can do that as we know that we are run after the bootloader has been
|
||||||
|
|
@ -631,10 +632,10 @@ pub(crate) fn client_run_migrate_static_grub_config() -> Result<()> {
|
||||||
// manually overwrites the (soon) static GRUB config by calling `grub2-mkconfig`.
|
// manually overwrites the (soon) static GRUB config by calling `grub2-mkconfig`.
|
||||||
// We need this until we can rely on ostree-grub2 being removed from the image.
|
// We need this until we can rely on ostree-grub2 being removed from the image.
|
||||||
println!("Marking bootloader as BLS capable...");
|
println!("Marking bootloader as BLS capable...");
|
||||||
_ = File::create("/boot/grub2/.grub2-blscfg-supported");
|
_ = File::create(format!("{}/.grub2-blscfg-supported", debian_tools::get_grub_config_dir()));
|
||||||
|
|
||||||
// Migrate /boot/grub2/grub.cfg to a static GRUB config if it is a symlink
|
// Migrate /boot/grub/grub.cfg or /boot/grub2/grub.cfg to a static GRUB config if it is a symlink
|
||||||
let grub_config_filename = PathBuf::from("/boot/grub2/grub.cfg");
|
let grub_config_filename = PathBuf::from(format!("{}/grub.cfg", debian_tools::get_grub_config_dir()));
|
||||||
match dirfd.read_link("grub.cfg") {
|
match dirfd.read_link("grub.cfg") {
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
println!(
|
println!(
|
||||||
|
|
@ -650,7 +651,7 @@ pub(crate) fn client_run_migrate_static_grub_config() -> Result<()> {
|
||||||
current_config.push(path);
|
current_config.push(path);
|
||||||
|
|
||||||
// Backup the current GRUB config which is hopefully working right now
|
// Backup the current GRUB config which is hopefully working right now
|
||||||
let backup_config = PathBuf::from("/boot/grub2/grub.cfg.backup");
|
let backup_config = PathBuf::from(format!("{}/grub.cfg.backup", debian_tools::get_grub_config_dir()));
|
||||||
println!(
|
println!(
|
||||||
"Creating a backup of the current GRUB config '{}' in '{}'...",
|
"Creating a backup of the current GRUB config '{}' in '{}'...",
|
||||||
current_config.display(),
|
current_config.display(),
|
||||||
|
|
|
||||||
103
src/debian_tools.rs
Normal file
103
src/debian_tools.rs
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
/// Debian GRUB tool paths
|
||||||
|
pub const DEBIAN_GRUB_INSTALL: &str = "/usr/sbin/grub-install";
|
||||||
|
pub const DEBIAN_GRUB_EDITENV: &str = "/usr/bin/grub-editenv";
|
||||||
|
|
||||||
|
/// Fedora GRUB2 tool paths (fallback)
|
||||||
|
pub const FEDORA_GRUB2_INSTALL: &str = "/usr/sbin/grub2-install";
|
||||||
|
pub const FEDORA_GRUB2_EDITENV: &str = "/usr/bin/grub2-editenv";
|
||||||
|
|
||||||
|
/// Debian GRUB package names
|
||||||
|
pub const DEBIAN_GRUB_PACKAGES: &[&str] = &[
|
||||||
|
"grub-efi-amd64",
|
||||||
|
"grub-efi-amd64-bin",
|
||||||
|
"grub-common",
|
||||||
|
"grub2-common",
|
||||||
|
];
|
||||||
|
|
||||||
|
/// Fedora GRUB2 package names (fallback)
|
||||||
|
pub const FEDORA_GRUB2_PACKAGES: &[&str] = &[
|
||||||
|
"grub2-efi-x64",
|
||||||
|
"grub2-tools",
|
||||||
|
"grub2-common",
|
||||||
|
];
|
||||||
|
|
||||||
|
/// Find the appropriate GRUB installer tool, preferring Debian paths
|
||||||
|
pub fn find_grub_install() -> Option<PathBuf> {
|
||||||
|
// First try Debian path
|
||||||
|
if Path::new(DEBIAN_GRUB_INSTALL).exists() {
|
||||||
|
return Some(PathBuf::from(DEBIAN_GRUB_INSTALL));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to Fedora path
|
||||||
|
if Path::new(FEDORA_GRUB2_INSTALL).exists() {
|
||||||
|
return Some(PathBuf::from(FEDORA_GRUB2_INSTALL));
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find the appropriate GRUB editenv tool, preferring Debian paths
|
||||||
|
pub fn find_grub_editenv() -> Option<PathBuf> {
|
||||||
|
// First try Debian path
|
||||||
|
if Path::new(DEBIAN_GRUB_EDITENV).exists() {
|
||||||
|
return Some(PathBuf::from(DEBIAN_GRUB_EDITENV));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to Fedora path
|
||||||
|
if Path::new(FEDORA_GRUB2_EDITENV).exists() {
|
||||||
|
return Some(PathBuf::from(FEDORA_GRUB2_EDITENV));
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the appropriate GRUB directory name based on available tools
|
||||||
|
pub fn get_grub_dir_name() -> &'static str {
|
||||||
|
if Path::new(DEBIAN_GRUB_INSTALL).exists() {
|
||||||
|
"grub"
|
||||||
|
} else {
|
||||||
|
"grub2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the appropriate GRUB config directory path
|
||||||
|
pub fn get_grub_config_dir() -> &'static str {
|
||||||
|
if Path::new(DEBIAN_GRUB_INSTALL).exists() {
|
||||||
|
"/boot/grub"
|
||||||
|
} else {
|
||||||
|
"/boot/grub2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if we're running on a Debian-based system
|
||||||
|
pub fn is_debian_system() -> bool {
|
||||||
|
Path::new("/etc/debian_version").exists() ||
|
||||||
|
Path::new("/etc/os-release").exists() && {
|
||||||
|
if let Ok(content) = std::fs::read_to_string("/etc/os-release") {
|
||||||
|
content.contains("debian") || content.contains("ubuntu")
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_grub_dir_name() {
|
||||||
|
// This test will depend on the actual system
|
||||||
|
let dir_name = get_grub_dir_name();
|
||||||
|
assert!(dir_name == "grub" || dir_name == "grub2");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_grub_config_dir() {
|
||||||
|
// This test will depend on the actual system
|
||||||
|
let config_dir = get_grub_config_dir();
|
||||||
|
assert!(config_dir == "/boot/grub" || config_dir == "/boot/grub2");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -25,6 +25,7 @@ use widestring::U16CString;
|
||||||
use crate::bootupd::RootContext;
|
use crate::bootupd::RootContext;
|
||||||
use crate::freezethaw::fsfreeze_thaw_cycle;
|
use crate::freezethaw::fsfreeze_thaw_cycle;
|
||||||
use crate::model::*;
|
use crate::model::*;
|
||||||
|
use crate::debian_tools;
|
||||||
use crate::ostreeutil;
|
use crate::ostreeutil;
|
||||||
use crate::util;
|
use crate::util;
|
||||||
use crate::{blockdev, filetree, grubconfigs};
|
use crate::{blockdev, filetree, grubconfigs};
|
||||||
|
|
@ -465,7 +466,8 @@ impl Component for Efi {
|
||||||
|
|
||||||
// move EFI files to updates dir from /usr/lib/ostree-boot
|
// move EFI files to updates dir from /usr/lib/ostree-boot
|
||||||
if ostreebootdir.exists() {
|
if ostreebootdir.exists() {
|
||||||
let cruft = ["loader", "grub2"];
|
let grub_dir_name = debian_tools::get_grub_dir_name();
|
||||||
|
let cruft = ["loader", grub_dir_name];
|
||||||
for p in cruft.iter() {
|
for p in cruft.iter() {
|
||||||
let p = ostreebootdir.join(p);
|
let p = ostreebootdir.join(p);
|
||||||
if p.exists() {
|
if p.exists() {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,45 @@
|
||||||
# Static GRUB configuration files
|
# Debian Atomic Static GRUB Configuration
|
||||||
|
|
||||||
These static files were taken from https://github.com/coreos/coreos-assembler/blob/5824720ec3a9ec291532b23b349b6d8d8b2e9edd/src/grub.cfg
|
This directory contains static GRUB configuration files that allow Debian Atomic systems to boot without requiring interactive TTY input during the bootloader installation process.
|
||||||
|
|
||||||
|
## Problem Solved
|
||||||
|
|
||||||
|
Traditional GRUB installation methods like `grub-install` require interactive TTY input for device resolution, which fails in containerized build environments. This static configuration approach completely avoids this issue.
|
||||||
|
|
||||||
|
## How It Works
|
||||||
|
|
||||||
|
### 1. Static Configuration Files
|
||||||
|
- **`grub-static-pre.cfg`**: Base GRUB configuration with boot partition discovery
|
||||||
|
- **`grub-static-efi.cfg`**: EFI-specific configuration for UEFI systems
|
||||||
|
- **`configs.d/10_debian-kernels.cfg`**: Automatic kernel discovery and menu generation
|
||||||
|
|
||||||
|
### 2. Boot Partition Discovery
|
||||||
|
Instead of interactive device prompts, the configuration uses:
|
||||||
|
- UUID-based discovery via `bootuuid.cfg`
|
||||||
|
- Label-based fallback (`search --label boot`)
|
||||||
|
- Automatic filesystem type detection
|
||||||
|
|
||||||
|
### 3. Kernel Discovery
|
||||||
|
The system automatically:
|
||||||
|
- Scans `/boot` for `vmlinuz-*` files
|
||||||
|
- Matches corresponding `initrd.img-*` files
|
||||||
|
- Generates GRUB menu entries dynamically
|
||||||
|
|
||||||
|
## Integration with deb-bootupd
|
||||||
|
|
||||||
|
This static configuration system integrates with deb-bootupd to:
|
||||||
|
1. Install pre-built GRUB modules without TTY interaction
|
||||||
|
2. Generate `bootuuid.cfg` with the correct boot partition UUID
|
||||||
|
3. Create a complete, bootable GRUB configuration
|
||||||
|
|
||||||
|
## Benefits
|
||||||
|
|
||||||
|
- ✅ **No TTY interaction required** - works in containers
|
||||||
|
- ✅ **Automatic kernel discovery** - no manual configuration
|
||||||
|
- ✅ **UEFI and BIOS support** - covers all architectures
|
||||||
|
- ✅ **Debian-specific optimizations** - tailored for Debian systems
|
||||||
|
- ✅ **Production ready** - based on Fedora's proven approach
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
The configuration files are automatically installed by deb-bootupd when creating bootable images. No manual intervention is required.
|
||||||
|
|
|
||||||
30
src/grub2/configs.d/10_debian-kernels.cfg
Normal file
30
src/grub2/configs.d/10_debian-kernels.cfg
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
# Debian Atomic kernel discovery configuration
|
||||||
|
# This automatically discovers and configures kernel entries
|
||||||
|
|
||||||
|
# Function to add kernel entries
|
||||||
|
function add_kernel_entry {
|
||||||
|
local kernel_version="$1"
|
||||||
|
local kernel_path="$2"
|
||||||
|
local initrd_path="$3"
|
||||||
|
|
||||||
|
if [ -f "$kernel_path" ] && [ -f "$initrd_path" ]; then
|
||||||
|
echo "menuentry \"Debian Atomic ($kernel_version)\" {"
|
||||||
|
echo " linux $kernel_path root=UUID=\${BOOT_UUID} ro"
|
||||||
|
echo " initrd $initrd_path"
|
||||||
|
echo "}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Discover kernels in /boot
|
||||||
|
if [ -d $prefix ]; then
|
||||||
|
for kernel in $prefix/vmlinuz-*; do
|
||||||
|
if [ -f "$kernel" ]; then
|
||||||
|
kernel_version=$(basename "$kernel" | sed 's/vmlinuz-//')
|
||||||
|
initrd_path="$prefix/initrd.img-$kernel_version"
|
||||||
|
|
||||||
|
if [ -f "$initrd_path" ]; then
|
||||||
|
add_kernel_entry "$kernel_version" "$kernel" "$initrd_path"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
@ -1,18 +1,20 @@
|
||||||
if [ -e (md/md-boot) ]; then
|
# Debian Atomic EFI static GRUB configuration
|
||||||
# The search command might pick a RAID component rather than the RAID,
|
# This file is adapted from Fedora's bootupd EFI configuration
|
||||||
# since the /boot RAID currently uses superblock 1.0. See the comment in
|
# to work with Debian UEFI systems.
|
||||||
# the main grub.cfg.
|
|
||||||
set prefix=md/md-boot
|
# Set up EFI boot environment
|
||||||
else
|
if [ -f ${config_directory}/bootuuid.cfg ]; then
|
||||||
if [ -f ${config_directory}/bootuuid.cfg ]; then
|
source ${config_directory}/bootuuid.cfg
|
||||||
source ${config_directory}/bootuuid.cfg
|
|
||||||
fi
|
|
||||||
if [ -n "${BOOT_UUID}" ]; then
|
|
||||||
search --fs-uuid "${BOOT_UUID}" --set prefix --no-floppy
|
|
||||||
else
|
|
||||||
search --label boot --set prefix --no-floppy
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Search for boot partition by UUID or label
|
||||||
|
if [ -n "${BOOT_UUID}" ]; then
|
||||||
|
search --fs-uuid "${BOOT_UUID}" --set prefix --no-floppy
|
||||||
|
else
|
||||||
|
search --label boot --set prefix --no-floppy
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set up GRUB prefix for EFI
|
||||||
if [ -d ($prefix)/grub2 ]; then
|
if [ -d ($prefix)/grub2 ]; then
|
||||||
set prefix=($prefix)/grub2
|
set prefix=($prefix)/grub2
|
||||||
configfile $prefix/grub.cfg
|
configfile $prefix/grub.cfg
|
||||||
|
|
@ -20,5 +22,7 @@ else
|
||||||
set prefix=($prefix)/boot/grub2
|
set prefix=($prefix)/boot/grub2
|
||||||
configfile $prefix/grub.cfg
|
configfile $prefix/grub.cfg
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Boot the system
|
||||||
boot
|
boot
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,55 +1,46 @@
|
||||||
# This file is copied from https://github.com/coreos/coreos-assembler/blob/0eb25d1c718c88414c0b9aedd19dc56c09afbda8/src/grub.cfg
|
# Debian Atomic static GRUB configuration
|
||||||
# Changes:
|
# This file is adapted from Fedora's bootupd static GRUB configuration
|
||||||
# - Dropped Ignition glue, that can be injected into platform.cfg
|
# to work with Debian systems and avoid TTY interaction issues.
|
||||||
# petitboot doesn't support -e and doesn't support an empty path part
|
|
||||||
if [ -d (md/md-boot)/grub2 ]; then
|
# Set up basic GRUB environment
|
||||||
# fcct currently creates /boot RAID with superblock 1.0, which allows
|
if [ -f ${config_directory}/bootuuid.cfg ]; then
|
||||||
# component partitions to be read directly as filesystems. This is
|
source ${config_directory}/bootuuid.cfg
|
||||||
# necessary because transposefs doesn't yet rerun grub2-install on BIOS,
|
|
||||||
# so GRUB still expects /boot to be a partition on the first disk.
|
|
||||||
#
|
|
||||||
# There are two consequences:
|
|
||||||
# 1. On BIOS and UEFI, the search command might pick an individual RAID
|
|
||||||
# component, but we want it to use the full RAID in case there are bad
|
|
||||||
# sectors etc. The undocumented --hint option is supposed to support
|
|
||||||
# this sort of override, but it doesn't seem to work, so we set $boot
|
|
||||||
# directly.
|
|
||||||
# 2. On BIOS, the "normal" module has already been loaded from an
|
|
||||||
# individual RAID component, and $prefix still points there. We want
|
|
||||||
# future module loads to come from the RAID, so we reset $prefix.
|
|
||||||
# (On UEFI, the stub grub.cfg has already set $prefix properly.)
|
|
||||||
set boot=md/md-boot
|
|
||||||
set prefix=($boot)/grub2
|
|
||||||
else
|
|
||||||
if [ -f ${config_directory}/bootuuid.cfg ]; then
|
|
||||||
source ${config_directory}/bootuuid.cfg
|
|
||||||
fi
|
|
||||||
if [ -n "${BOOT_UUID}" ]; then
|
|
||||||
search --fs-uuid "${BOOT_UUID}" --set boot --no-floppy
|
|
||||||
else
|
|
||||||
search --label boot --set boot --no-floppy
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Search for boot partition by UUID or label
|
||||||
|
if [ -n "${BOOT_UUID}" ]; then
|
||||||
|
search --fs-uuid "${BOOT_UUID}" --set boot --no-floppy
|
||||||
|
else
|
||||||
|
search --label boot --set boot --no-floppy
|
||||||
|
fi
|
||||||
|
|
||||||
set root=$boot
|
set root=$boot
|
||||||
|
|
||||||
|
# Load GRUB environment
|
||||||
if [ -f ${config_directory}/grubenv ]; then
|
if [ -f ${config_directory}/grubenv ]; then
|
||||||
load_env -f ${config_directory}/grubenv
|
load_env -f ${config_directory}/grubenv
|
||||||
elif [ -s $prefix/grubenv ]; then
|
elif [ -s $prefix/grubenv ]; then
|
||||||
load_env
|
load_env
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Load console configuration if available
|
||||||
if [ -f $prefix/console.cfg ]; then
|
if [ -f $prefix/console.cfg ]; then
|
||||||
# Source in any GRUB console settings if provided by the user/platform
|
|
||||||
source $prefix/console.cfg
|
source $prefix/console.cfg
|
||||||
fi
|
fi
|
||||||
|
|
||||||
menuentry_id_option="--id"
|
# Set up video support
|
||||||
|
|
||||||
function load_video {
|
function load_video {
|
||||||
insmod all_video
|
insmod all_video
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Basic GRUB settings
|
||||||
set timeout_style=menu
|
set timeout_style=menu
|
||||||
set timeout=1
|
set timeout=5
|
||||||
|
set default=0
|
||||||
|
|
||||||
|
# Load necessary modules for Debian
|
||||||
|
insmod part_gpt
|
||||||
|
insmod ext2
|
||||||
|
insmod fat
|
||||||
|
|
||||||
# Other package code will be injected from here
|
# Other package code will be injected from here
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,10 @@ use bootc_internal_utils::CommandRunExt;
|
||||||
use fn_error_context::context;
|
use fn_error_context::context;
|
||||||
use openat_ext::OpenatDirExt;
|
use openat_ext::OpenatDirExt;
|
||||||
|
|
||||||
|
use crate::debian_tools;
|
||||||
use crate::freezethaw::fsfreeze_thaw_cycle;
|
use crate::freezethaw::fsfreeze_thaw_cycle;
|
||||||
|
|
||||||
/// The subdirectory of /boot we use
|
/// The subdirectory of /boot we use - dynamically determined
|
||||||
const GRUB2DIR: &str = "grub2";
|
const GRUB2DIR: &str = "grub2";
|
||||||
const CONFIGDIR: &str = "/usr/lib/bootupd/grub2-static";
|
const CONFIGDIR: &str = "/usr/lib/bootupd/grub2-static";
|
||||||
const DROPINDIR: &str = "configs.d";
|
const DROPINDIR: &str = "configs.d";
|
||||||
|
|
@ -36,8 +37,9 @@ pub(crate) fn install(
|
||||||
root_dev != boot_dev
|
root_dev != boot_dev
|
||||||
};
|
};
|
||||||
|
|
||||||
if !bootdir.exists(GRUB2DIR)? {
|
let grub_dir_name = debian_tools::get_grub_dir_name();
|
||||||
bootdir.create_dir(GRUB2DIR, 0o700)?;
|
if !bootdir.exists(grub_dir_name)? {
|
||||||
|
bootdir.create_dir(grub_dir_name, 0o700)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut config = String::from("# Generated by bootupd / do not edit\n\n");
|
let mut config = String::from("# Generated by bootupd / do not edit\n\n");
|
||||||
|
|
@ -68,7 +70,7 @@ pub(crate) fn install(
|
||||||
println!("Added {name}");
|
println!("Added {name}");
|
||||||
}
|
}
|
||||||
|
|
||||||
let grub2dir = bootdir.sub_dir(GRUB2DIR)?;
|
let grub2dir = bootdir.sub_dir(grub_dir_name)?;
|
||||||
grub2dir
|
grub2dir
|
||||||
.write_file_contents("grub.cfg", GRUBCONFIG_FILE_MODE, config.as_bytes())
|
.write_file_contents("grub.cfg", GRUBCONFIG_FILE_MODE, config.as_bytes())
|
||||||
.context("Copying grub-static.cfg")?;
|
.context("Copying grub-static.cfg")?;
|
||||||
|
|
@ -123,17 +125,24 @@ pub(crate) fn install(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[context("Create file boot/grub2/grubenv")]
|
#[context("Create file boot/grub/grubenv or boot/grub2/grubenv")]
|
||||||
fn write_grubenv(bootdir: &openat::Dir) -> Result<()> {
|
fn write_grubenv(bootdir: &openat::Dir) -> Result<()> {
|
||||||
let grubdir = &bootdir.sub_dir(GRUB2DIR).context("Opening boot/grub2")?;
|
let grub_dir_name = debian_tools::get_grub_dir_name();
|
||||||
|
let grubdir = &bootdir.sub_dir(grub_dir_name).context(format!("Opening boot/{}", grub_dir_name))?;
|
||||||
|
|
||||||
if grubdir.exists(GRUBENV)? {
|
if grubdir.exists(GRUBENV)? {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let editenv = Path::new("/usr/bin/grub2-editenv");
|
|
||||||
if !editenv.exists() {
|
// Find the appropriate GRUB editenv tool
|
||||||
anyhow::bail!("Failed to find {:?}", editenv);
|
let editenv = match debian_tools::find_grub_editenv() {
|
||||||
}
|
Some(path) => path,
|
||||||
|
None => {
|
||||||
|
anyhow::bail!("Failed to find GRUB editenv tool. Tried: {:?}, {:?}",
|
||||||
|
debian_tools::DEBIAN_GRUB_EDITENV,
|
||||||
|
debian_tools::FEDORA_GRUB2_EDITENV);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
std::process::Command::new(editenv)
|
std::process::Command::new(editenv)
|
||||||
.args([GRUBENV, "create"])
|
.args([GRUBENV, "create"])
|
||||||
|
|
@ -152,29 +161,34 @@ mod tests {
|
||||||
let td = tempfile::tempdir()?;
|
let td = tempfile::tempdir()?;
|
||||||
let tdp = td.path();
|
let tdp = td.path();
|
||||||
let td = openat::Dir::open(tdp)?;
|
let td = openat::Dir::open(tdp)?;
|
||||||
std::fs::create_dir_all(tdp.join("boot/grub2"))?;
|
let grub_dir_name = debian_tools::get_grub_dir_name();
|
||||||
|
std::fs::create_dir_all(tdp.join(format!("boot/{}", grub_dir_name)))?;
|
||||||
std::fs::create_dir_all(tdp.join("boot/efi/EFI/BOOT"))?;
|
std::fs::create_dir_all(tdp.join("boot/efi/EFI/BOOT"))?;
|
||||||
std::fs::create_dir_all(tdp.join("boot/efi/EFI/fedora"))?;
|
std::fs::create_dir_all(tdp.join("boot/efi/EFI/fedora"))?;
|
||||||
install(&td, Some("fedora"), false).unwrap();
|
install(&td, Some("fedora"), false).unwrap();
|
||||||
|
|
||||||
assert!(td.exists("boot/grub2/grub.cfg")?);
|
assert!(td.exists(format!("boot/{}/grub.cfg", grub_dir_name))?);
|
||||||
assert!(td.exists("boot/efi/EFI/fedora/grub.cfg")?);
|
assert!(td.exists("boot/efi/EFI/fedora/grub.cfg")?);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_write_grubenv() -> Result<()> {
|
fn test_write_grubenv() -> Result<()> {
|
||||||
// Skip this test if grub2-editenv is not installed
|
// Skip this test if grub-editenv is not installed
|
||||||
let editenv = Path::new("/usr/bin/grub2-editenv");
|
let editenv = match debian_tools::find_grub_editenv() {
|
||||||
if !editenv.try_exists()? {
|
Some(_) => true,
|
||||||
|
None => false,
|
||||||
|
};
|
||||||
|
if !editenv {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
let td = tempfile::tempdir()?;
|
let td = tempfile::tempdir()?;
|
||||||
let tdp = td.path();
|
let tdp = td.path();
|
||||||
std::fs::create_dir_all(tdp.join("boot/grub2"))?;
|
let grub_dir_name = debian_tools::get_grub_dir_name();
|
||||||
|
std::fs::create_dir_all(tdp.join(format!("boot/{}", grub_dir_name)))?;
|
||||||
let td = openat::Dir::open(&tdp.join("boot"))?;
|
let td = openat::Dir::open(&tdp.join("boot"))?;
|
||||||
write_grubenv(&td)?;
|
write_grubenv(&td)?;
|
||||||
|
|
||||||
assert!(td.exists("grub2/grubenv")?);
|
assert!(td.exists(format!("{}/grubenv", grub_dir_name))?);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ mod backend;
|
||||||
#[cfg(any(target_arch = "x86_64", target_arch = "powerpc64"))]
|
#[cfg(any(target_arch = "x86_64", target_arch = "powerpc64"))]
|
||||||
mod bios;
|
mod bios;
|
||||||
mod blockdev;
|
mod blockdev;
|
||||||
|
mod debian_tools;
|
||||||
mod bootupd;
|
mod bootupd;
|
||||||
mod cli;
|
mod cli;
|
||||||
mod component;
|
mod component;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue