builder_peppermint_void/builder/core/initramfs_builder.py
2025-04-29 13:07:25 +00:00

193 lines
8.3 KiB
Python

# -*- coding: utf-8 -*-
"""
SPDX-FileCopyrightText: 2023-2025 PeppermintOS Team
(peppermintosteam@proton.me)
SPDX-License-Identifier: GPL-3.0-or-later
This module provides functions to build an initramfs for a Linux system.
It includes functions for:
- Copying the kernel image to the boot directory.
- Generating the initramfs using dracut.
- Retrieving kernel version information from within a chroot environment.
Credits:
- PeppermintOS Team (peppermintosteam@proton.me) - Development and maintenance of the project.
License:
This code is distributed under the GNU General Public License version 3 or later (GPL-3.0-or-later).
For more details, please refer to the LICENSE file included in the project or visit:
https://www.gnu.org/licenses/gpl-3.0.html
"""
import os
import subprocess
import shutil
import logging
import sys
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, BASE_DIR)
try:
from builder.core.bootstrap.paths import paths
from builder.core.xbps_commands import xbps_commands
from builder.configs import logger_config
logger = logger_config.setup_logger('initramfs_builder')
except ImportError as e:
print(f"Error importing necessary modules: {e}. Ensure your environment is set up correctly.")
sys.exit(1)
def execute_xbps_query_chroot(rootfs_path, xbps_query_args, iso_build_config):
"""Executes xbps-query inside the chroot and returns the output."""
xbps_arch = iso_build_config.get('target_arch', 'x86_64')
command_xbps_query = ["chroot", rootfs_path, f"XBPS_ARCH={xbps_arch}", xbps_commands["XBPS_QUERY_CMD"]] + list(xbps_query_args)
completed_process = subprocess.run(command_xbps_query, capture_output=True, text=True)
return completed_process.stdout.strip()
def execute_xbps_uhelper_chroot(rootfs_path, xbps_uhelper_args, iso_build_config):
"""Executes xbps-uhelper inside the chroot and returns the output."""
command_xbps_uhelper = ["chroot", rootfs_path, xbps_commands["XBPS_UHELPER_CMD"]] + list(xbps_uhelper_args)
completed_process = subprocess.run(command_xbps_uhelper, capture_output=True, text=True)
return completed_process.stdout.strip()
def copy_kernel_image(rootfs_path, boot_path, iso_build_config):
"""
Copies the kernel image file (vmlinuz or vmlinux) from the ROOTFS to the ISO's BOOT directory,
renaming it to 'vmlinuz' (x86_64, i686) or 'vmlinux' (aarch64) and using the correct kernel version.
"""
logger.info("=> Copying kernel image to the ISO BOOT directory...")
kernel_versions = get_kernel_version_in_chroot(rootfs_path, iso_build_config)
if not kernel_versions or 'version_final_part' not in kernel_versions:
logger.error("=> ERROR: Could not obtain kernel version to copy the image.")
raise ValueError("Could not obtain kernel version.")
kernel_file_suffix = kernel_versions['version_final_part']
target_arch = iso_build_config.get('target_arch', 'x86_64').lower()
if target_arch.startswith('i686') or target_arch == 'x86_64':
kernel_filename_in_boot = "vmlinuz"
elif target_arch == 'aarch64':
kernel_filename_in_boot = "vmlinux"
else:
logger.error(f"=> Unknown target architecture: '{target_arch}'. Cannot copy kernel.")
raise ValueError(f"Unsupported target architecture: '{target_arch}'")
boot_dir = os.path.join(rootfs_path, 'boot')
kernel_filename_in_rootfs = None
try:
boot_files = os.listdir(boot_dir)
for filename in boot_files:
if target_arch.startswith('i686') or target_arch == 'x86_64':
if filename.startswith(f'vmlinuz-{kernel_file_suffix}'):
kernel_filename_in_rootfs = filename
break
elif target_arch == 'aarch64':
if filename.startswith(f'vmlinux-{kernel_file_suffix}'):
kernel_filename_in_rootfs = filename
break
except FileNotFoundError:
logger.error(f"=> ERROR: /boot directory not found at: {boot_dir}")
raise FileNotFoundError(f"/boot directory not found: {boot_dir}")
if not kernel_filename_in_rootfs:
logger.error(f"=> ERROR: Kernel file with suffix '{kernel_file_suffix}' not found in: {boot_dir}")
raise FileNotFoundError(f"Kernel file not found with suffix: '{kernel_file_suffix}'")
kernel_path_in_rootfs = os.path.join(rootfs_path, "boot", kernel_filename_in_rootfs)
kernel_path_in_boot = os.path.join(boot_path, kernel_filename_in_boot)
logger.info(f"=> Copying kernel from: {kernel_path_in_rootfs} to: {kernel_path_in_boot}...")
shutil.copy2(kernel_path_in_rootfs, kernel_path_in_boot)
logger.info(f"=> Kernel copied successfully to: {kernel_path_in_boot}")
return kernel_path_in_boot
def get_kernel_version_in_chroot(rootfs_path, target_architecture, iso_build_config):
target_arch = iso_build_config.get('rootfs', {}).get('target_arch', 'x86_64').lower()
boot_dir = os.path.join(rootfs_path, 'boot')
try:
boot_files = os.listdir(boot_dir)
except FileNotFoundError:
logger.error(f"=> ERROR: /boot directory not found at: {boot_dir}")
return None
kernel_version = None
for filename in boot_files:
if target_arch.startswith('i686') or target_arch == 'x86_64':
if filename.startswith('vmlinuz-'):
kernel_version = filename.replace('vmlinuz-', '')
break
elif target_arch == 'aarch64':
if filename.startswith('vmlinux-'):
kernel_version = filename.replace('vmlinux-', '')
break
if not kernel_version:
logger.error(f"=> ERROR: Kernel image not found in: {boot_dir}")
return None
logger.info(f"=> Kernel version found in /boot: {kernel_version}")
kernel_versions_dict = {
'version_final_part': kernel_version,
'version_major_minor': ".".join(kernel_version.split('.')[:2])
}
return kernel_versions_dict
def get_kernel_module_dir_in_chroot(rootfs_path, kernel_version):
"""
Gets the path of the kernel modules directory in the chroot using the provided version.
"""
kernel_modules_dir = f"/lib/modules/{kernel_version}"
return kernel_modules_dir
def create_initramfs(rootfs_path, boot_path, target_architecture, xbps_commands, iso_build_config):
logger.info("=> Starting initramfs creation...")
logger.info("=> Creating initramfs using dracut...")
kernel_versions = get_kernel_version_in_chroot(rootfs_path, iso_build_config)
if not kernel_versions or 'version_final_part' not in kernel_versions:
logger.error("=> ERROR: Could not obtain kernel version to create the initramfs.")
raise ValueError("Could not obtain kernel version.")
kernel_version_final_part = kernel_versions["version_final_part"]
kernel_modules_dir = get_kernel_module_dir_in_chroot(rootfs_path, kernel_version_final_part)
KERNELVERSION = execute_xbps_uhelper_chroot(rootfs_path, ["getpkgversion", kernel_version_final_part], iso_build_config)
dracut_command = [
"chroot",
rootfs_path,
xbps_commands["DRACUT_CMD"],
"-N",
"--force",
f"--kmoddir={kernel_modules_dir}",
"--add-drivers", "ahci",
"--force-add", "vmklive",
"--omit", "systemd",
"/boot/initrd",
kernel_version_final_part,
]
logger.info("=> *** EXTRA CHECK: EXECUTING CORRECTED ISO_BUILDER.PY SCRIPT VERSION (BEFORE DRACUT!) ***")
try:
subprocess.run(dracut_command, check=True)
except subprocess.CalledProcessError as e:
logger.error(f"=> Error executing dracut: Exit code: {e.returncode}")
logger.error(f"=> Failed command: {e.cmd}")
logger.error(f"=> Stderr: {e.stderr.decode('utf-8', errors='ignore') if e.stderr else '<empty>'}")
raise
else:
logger.info("=> Initramfs created successfully using dracut!")
initrd_path_in_rootfs = os.path.join(rootfs_path, "boot", "initrd")
initrd_path_in_boot = os.path.join(boot_path, "initrd")
logger.info(f"=> Moving initramfs from: {initrd_path_in_rootfs} to: {initrd_path_in_boot}...")
shutil.move(initrd_path_in_rootfs, initrd_path_in_boot)
logger.info(f"=> Initramfs moved successfully to: {initrd_path_in_boot}")
return initrd_path_in_boot, kernel_versions