#!/usr/bin/env python3 """ This file contains all code required to generate the boot image for live-bootstrap """ # SPDX-License-Identifier: GPL-3.0-or-later # SPDX-FileCopyrightText: 2022-2023 Dor Askayo # SPDX-FileCopyrightText: 2021 Andrius Štikonas # SPDX-FileCopyrightText: 2021 Melg Eight # SPDX-FileCopyrightText: 2021-23 Samuel Tyler import hashlib import os import random import shutil import struct import tarfile import traceback import requests # pylint: disable=too-many-instance-attributes class Generator(): """ Class responsible for generating the basic media to be consumed. """ git_dir = os.path.join(os.path.dirname(os.path.join(__file__)), '..') distfiles_dir = os.path.join(git_dir, 'distfiles') payload_magic = b'LBPAYLD1' # pylint: disable=too-many-arguments,too-many-positional-arguments def __init__(self, arch, external_sources, early_preseed, repo_path, mirrors, build_guix_also=False): self.arch = arch self.early_preseed = early_preseed self.external_sources = external_sources self.repo_path = repo_path self.mirrors = mirrors self.build_guix_also = build_guix_also self.source_manifest = self.get_source_manifest(not self.external_sources, build_guix_also=self.build_guix_also) self.early_source_manifest = self.get_source_manifest(True, build_guix_also=self.build_guix_also) self.bootstrap_source_manifest = self.source_manifest self.payload_source_manifest = [] self.payload_image = None self.target_dir = None self.external_dir = None def reuse(self, target): """ Reuse a previously prepared bwrap environment for further stages. """ self.target_dir = target.path self.external_dir = os.path.join(self.target_dir, 'external') self.distfiles() def _prepare_kernel_bootstrap_payload_manifests(self): """ Split early source payload from full offline payload. """ # Keep the early builder payload small enough to avoid overrunning # builder-hex0 memory file allocation before we can jump into Fiwix. self.bootstrap_source_manifest = self.get_source_manifest(True, build_guix_also=False) full_manifest = self.get_source_manifest(False, build_guix_also=self.build_guix_also) bootstrap_set = set(self.bootstrap_source_manifest) self.payload_source_manifest = [entry for entry in full_manifest if entry not in bootstrap_set] def _copy_manifest_distfiles(self, out_dir, manifest): os.makedirs(out_dir, exist_ok=True) for entry in manifest: file_name = entry[3].strip() shutil.copy2(os.path.join(self.distfiles_dir, file_name), os.path.join(out_dir, file_name)) def _ensure_manifest_distfiles(self, manifest): for entry in manifest: checksum, directory, url, file_name = entry distfile_path = os.path.join(directory, file_name) if not os.path.isfile(distfile_path): self.download_file(url, directory, file_name) self.check_file(distfile_path, checksum) def _create_raw_payload_image(self, target_path, manifest): if not manifest: return None # Guarantee all payload distfiles exist and match checksums. self._ensure_manifest_distfiles(manifest) files_by_name = {} for checksum, _, _, file_name in manifest: if file_name in files_by_name and files_by_name[file_name] != checksum: raise ValueError(f"Conflicting payload file with same name but different hash: {file_name}") files_by_name[file_name] = checksum payload_path = os.path.join(target_path, "payload.img") ordered_names = sorted(files_by_name.keys()) with open(payload_path, "wb") as payload: payload.write(self.payload_magic) payload.write(struct.pack(" 0xFFFFFFFF: raise ValueError(f"Payload file name too long: {file_name}") src_path = os.path.join(self.distfiles_dir, file_name) file_size = os.path.getsize(src_path) if file_size > 0xFFFFFFFF: raise ValueError(f"Payload file too large for raw container format: {file_name}") payload.write(struct.pack(" 3: file_name = source[3] else: # Automatically determine file name based on URL. file_name = os.path.basename(source[1]) entry = (source[2], directory, source[1], file_name) if entry not in entries: entries.append(entry) return entries stage0_arch_map = { "amd64": "AMD64", }