diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index cb89eb56e936ffe338d4989164db54b9decdb2fd..17ac5ff58a3f5b6528a8be0298ac09a9a2ef6763 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -2,20 +2,6 @@ # # SPDX-License-Identifier: GPL-3.0-or-later -# NOTE: most functionality (apart from building) is abstracted by this include -include: - - project: 'archlinux/ci-scripts' - ref: master - file: '/prepare_archiso_vm.yml' - -variables: - BUILD_SCRIPT: ./.gitlab/ci/build_archiso.sh - PACKAGE_LIST: arch-install-scripts bash dosfstools e2fsprogs erofs-utils gnupg grub jq libarchive libisoburn mtools openssl qemu-headless squashfs-tools zsync - QEMU_BUILD_TIMEOUT: 2400 - QEMU_COPY_ARTIFACTS_TIMEOUT: 120 - QEMU_VM_MEMORY: 3072 - ARCHISO_COW_SPACE_SIZE: 2g - stages: - check - build @@ -33,6 +19,19 @@ check: stage: check interruptible: true +.build: + artifacts: + reports: + metrics: output/metrics.txt + before_script: + - pacman -Sy --needed --noconfirm archlinux-keyring + - pacman -Syu --needed --noconfirm arch-install-scripts bash dosfstools e2fsprogs erofs-utils gnupg grub jq libarchive libisoburn mtools openssl qemu-headless squashfs-tools zsync + script: + - ./.gitlab/ci/build_archiso.sh ${BUILD_SCRIPT_ARGS} + stage: build + tags: + - vm + build_short: extends: .build parallel: @@ -59,8 +58,6 @@ build_long: - BUILD_SCRIPT_ARGS: baseline netboot - BUILD_SCRIPT_ARGS: releng iso - BUILD_SCRIPT_ARGS: releng netboot - tags: - - fast-single-thread only: refs: - master diff --git a/AUTHORS.rst b/AUTHORS.rst index aa287ec408108d33a042c75aff5d3078c31a1c26..b03b91e6d1a1dd45a94325925ae1f45fcf8af062 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -35,3 +35,4 @@ Archiso Authors * nl6720 * Øyvind Heggstad * plain linen +* Pellegrino Prevete diff --git a/CHANGELOG.rst b/CHANGELOG.rst index c3029c4dbe945cc7585b71c2a8124069042c852c..db1559ae1b7feed7f0bb2516c4d892e8bf3c1131 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -5,6 +5,59 @@ Changelog [XX] - YYYY-MM-DD ================= +Added +----- +- Add support for LUKS2 image disks; + - new ``+luks`` image types. +- Insert known needed extra modules in ``mkinitcpio.conf`` if image configuration requires it. +- Add support for ``dongle`` buildmode. +- Add support for a new ISO partition intended to be "persistent" + on both ``iso`` (``persistent``) and ``dongle`` (``donglepersistent``). +- New ``mk_`` functions. +- Add support for GRUB as MBR bootloader. +- Add support for GRUB booting system from persistent partitions. +- Added known vulnerabilities warnings. +- Added functions to determine chosen file system image type. +Profiles: + - Add systemd automounts for persistent partition. + +Changed +------- +- Upgraded storage handling function functions: + - Set overwrite flag in ``mtools`` commands. + - Refactor ``run_`` functions. +- Upgraded image building functions: + - Abstract ``mkairootfs_`` as ``mk_``. +- Abstract signature and checksum functions. +- Grouped all bootloader seds in a single function. +- Profiles: + - Completely switch to GRUB. + - Reduced bootloader configuration files complexity. + - Reduced mkinitcpio.conf complexity. + +Removed +------- + +[66] - 2022-08-28 +================= + +Added +----- + +- Add ``efibootimg`` to ``mkarchiso`` to abstract the FAT image path. +- Unset ``LANGUAGE`` since ``LC_ALL=C.UTF-8``, unlike ``LC_ALL=C``, does not override ``LANGUAGE``. +- Copy all files from the ``grub`` directory to ISO9660 and the FAT image, not just only ``grub.cfg``. +- Touching ``/usr/lib/clock-epoch`` to to help ``systemd`` with screwed or broken RTC. + +Changed +------- + +- Disable GRUB's shim_lock verifier and preload more modules. This allows reusing the GRUB EFI binaries when repacking + the ISO to support Secure Boot with custom signatures. + +[65] - 2022-06-30 +================= + Added ----- diff --git a/archiso/mkarchiso b/archiso/mkarchiso index 0c6a1eaefd6ce26ba3e3386fca35a517d39c83df..6bc8652ce0cc8cd5dba24524a082d49184d8ba01 100755 --- a/archiso/mkarchiso +++ b/archiso/mkarchiso @@ -3,10 +3,16 @@ # SPDX-License-Identifier: GPL-3.0-or-later set -e -u +shopt -s extglob # Control the environment umask 0022 export LC_ALL="C.UTF-8" +if [[ -v LANGUAGE ]]; then + # LC_ALL=C.UTF-8, unlike LC_ALL=C, does not override LANGUAGE. + # See https://sourceware.org/bugzilla/show_bug.cgi?id=16621 and https://savannah.gnu.org/bugs/?62815 + unset LANGUAGE +fi [[ -v SOURCE_DATE_EPOCH ]] || printf -v SOURCE_DATE_EPOCH '%(%s)T' -1 export SOURCE_DATE_EPOCH @@ -21,25 +27,42 @@ work_dir="" out_dir="" gpg_key="" gpg_sender="" +gpg_home="" iso_name="" iso_label="" iso_publisher="" iso_application="" iso_version="" +isofs_dir="" install_dir="" arch="" pacman_conf="" packages="" bootstrap_packages="" pacstrap_dir="" +keys_dir="" buildmodes=() bootmodes=() airootfs_image_type="" airootfs_image_tool_options=() +airootfs_img_dir="" +xorriso_options=() +xorrisofs_options=() +keys_image_type="" +keys_image_tool_options=() +dongle_isofs_dir="" +dongle_xorriso_options=() +dongle_xorrisofs_options=() cert_list=() sign_netboot_artifacts="" declare -A file_permissions=() +efibootimg="" efiboot_files=() +encryption_key="" +persistent_size_kib="" +persistent_image_type="" +persistentimg="" +donglepersistentimg="" # adapted from GRUB_EARLY_INITRD_LINUX_STOCK in https://git.savannah.gnu.org/cgit/grub.git/tree/util/grub-mkconfig.in readonly ucodes=('intel-uc.img' 'intel-ucode.img' 'amd-uc.img' 'amd-ucode.img' 'early_ucode.cpio' 'microcode.cpio') @@ -95,6 +118,7 @@ usage: ${app_name} [options] Passed to gpg as the value for --default-key -G Set the PGP signer (must include an email address) Passed to gpg as the value for --sender + -H Set the gpg home directory. -h This message -m [mode ..] Build mode(s) to use (valid modes are: 'bootstrap', 'iso' and 'netboot'). Multiple build modes are provided as quoted, space delimited list. @@ -103,7 +127,7 @@ usage: ${app_name} [options] -p [package ..] Package(s) to install. Multiple packages are provided as quoted, space delimited list. -v Enable verbose output - -w Set the working directory + -w Set the working directory (can't be a bind mount). Default: '${work_dir}' profile_dir: Directory of the archiso profile to build @@ -115,7 +139,7 @@ ENDUSAGETEXT # Shows configuration options. _show_config() { local build_date - printf -v build_date '%(%FT%R%z)T' "${SOURCE_DATE_EPOCH}" + TZ=UTC printf -v build_date '%(%FT%R%z)T' "${SOURCE_DATE_EPOCH}" _msg_info "${app_name} configuration settings" _msg_info " Architecture: ${arch}" _msg_info " Working directory: ${work_dir}" @@ -126,6 +150,8 @@ _show_config() { _msg_info " Build modes: ${buildmodes[*]}" _msg_info " GPG key: ${gpg_key:-None}" _msg_info " GPG signer: ${gpg_sender:-None}" + _msg_info " GPG Home: ${gpg_home:-None}" + _msg_info " Encryption Key: ${encryption_key:-None}" _msg_info "Code signing certificates: ${cert_list[*]:-None}" _msg_info " Profile: ${profile}" _msg_info "Pacman configuration file: ${pacman_conf}" @@ -140,132 +166,603 @@ _show_config() { # Cleanup airootfs _cleanup_pacstrap_dir() { + local _images=("initramfs-*img" "vmlinuz-*" "${ucodes[@]}") + local _root="${pacstrap_dir}" + local _boot="${_root}/boot" + local _log="${pacstrap_dir}/var/log" + local _machine_id="${pacstrap_dir}/etc/machine-id" + local _pacman_db="${_root}/var/lib/pacman" + local _pacman_cache="${_root}/var/cache/pacman/pkg" + local _tmp="${pacstrap_dir}/var/tmp" + local _cp_opts=('-af' '--no-preserve=ownership,mode') _msg_info "Cleaning up in pacstrap location..." - # Delete all files in /boot - [[ -d "${pacstrap_dir}/boot" ]] && find "${pacstrap_dir}/boot" -mindepth 1 -delete + # Move kernel, initramfs and microcodes for later usage + install -d -m 0755 "${work_dir}/boot" + for _query in "${_images[@]}"; do + find "${_boot}" -iname "${_query}" -exec cp "${_cp_opts[@]}" {} "${work_dir}/boot" \; -quit + done + [[ -d "${_boot}" ]] && find "${_boot}" -maxdepth 1 -type f -delete # Delete pacman database sync cache files (*.tar.gz) - [[ -d "${pacstrap_dir}/var/lib/pacman" ]] && find "${pacstrap_dir}/var/lib/pacman" -maxdepth 1 -type f -delete + [[ -d "${_pacman_db}" ]] && find "${_pacman_db}" -maxdepth 1 -type f -delete # Delete pacman database sync cache - [[ -d "${pacstrap_dir}/var/lib/pacman/sync" ]] && find "${pacstrap_dir}/var/lib/pacman/sync" -delete - # Delete pacman package cache - [[ -d "${pacstrap_dir}/var/cache/pacman/pkg" ]] && find "${pacstrap_dir}/var/cache/pacman/pkg" -type f -delete + [[ -d "${_pacman_db}/sync" ]] && find "${_pacman_db}/sync" -delete + # Delete pacman cache files + [[ -d "${_pacman_cache}" ]] && find "${_pacman_cache}" -type f -delete # Delete all log files, keeps empty dirs. - [[ -d "${pacstrap_dir}/var/log" ]] && find "${pacstrap_dir}/var/log" -type f -delete + [[ -d "${_log}" ]] && find "${_log}" -type f -delete # Delete all temporary files and dirs - [[ -d "${pacstrap_dir}/var/tmp" ]] && find "${pacstrap_dir}/var/tmp" -mindepth 1 -delete + [[ -d "${_tmp}" ]] && find "${_tmp}" -mindepth 1 -delete # Delete package pacman related files. find "${work_dir}" \( -name '*.pacnew' -o -name '*.pacsave' -o -name '*.pacorig' \) -delete - # Create an empty /etc/machine-id - rm -f -- "${pacstrap_dir}/etc/machine-id" - printf '' > "${pacstrap_dir}/etc/machine-id" - + if [[ "${persistent_size_kib}" != "" ]]; then + dbus-uuidgen --ensure="${_machine_id}" + else + # Create an empty /etc/machine-id + rm -f -- "${_machine_id}" + printf '' > "${_machine_id}" + fi _msg_info "Done!" } -# Create a squashfs image and place it in the ISO 9660 file system. -# $@: options to pass to mksquashfs +# Create a squashfs image from a file system image or directory. _run_mksquashfs() { - local mksquashfs_options=() image_path="${isofs_dir}/${install_dir}/${arch}/airootfs.sfs" - rm -f -- "${image_path}" + local _directory_or_fs="${1}" + local _image_path="${2}" + local mksquashfs_options=("${_mkfs_options[@]}") [[ ! "${quiet}" == "y" ]] || mksquashfs_options+=('-no-progress' '-quiet') - mksquashfs "$@" "${image_path}" -noappend "${airootfs_image_tool_options[@]}" "${mksquashfs_options[@]}" + _msg_info "Creating SQUASHFS image, this may take some time..." + mksquashfs "${_directory_or_fs}" "${_image_path}" -noappend "${mksquashfs_options[@]}" +} + +# Create an ext4 image of given size (in KB) from a directory. +_run_mkext4() { + local _dir="${1}" + local _image_path="${1}.img" + local _size="${2}" + local _epoch="${3}" + local _label="${4}" + local _writable="${5}" + local ext4_hash_seed _fsuuid mkfs_ext4_options=() + _fsuuid="$(_epoch_to_uuid "${_epoch}")" + + _msg_info "Creating ext4 image" + _msg_info "Size: ${_size} KB" + _msg_info "Directory: ${_dir}" + _msg_info "Image path: ${_image_path}" + _msg_info "UUID: ${_fsuuid}" + _msg_info "Label: ${_label}" + _msg_info "this may take a while..." + + ext4_hash_seed="$(uuidgen --sha1 --namespace 93a870ff-8565-4cf3-a67b-f47299271a96 \ + --name "${_epoch} ext4 hash seed")" + mkfs_ext4_options=('-d' "${_dir}" + '-O' '^has_journal,^resize_inode' + '-E' "lazy_itable_init=0,root_owner=0:0,hash_seed=${ext4_hash_seed}" + '-m' '0' + '-F' + '-b' 4096 + '-U' 'clear') + [[ "${_label}" != "" ]] && mkfs_ext4_options+=('-L' "${_label}") + [[ ! "${quiet}" == "y" ]] || mkfs_ext4_options+=('-q') + rm -f -- "${_image_path}" + E2FSPROGS_FAKE_TIME="${_epoch}" mkfs.ext4 "${mkfs_ext4_options[@]}" -- "${_image_path}" "${_size}K" + tune2fs -c 0 -i 0 -- "${_image_path}" > /dev/null + if [ "${_writable}" != "true" ]; then + tunefs -O read-only "${_image_path}" + fi + _msg_info "Done!" } -# Create an ext4 image containing the root file system and pack it inside a squashfs image. -# Save the squashfs image on the ISO 9660 file system. -_mkairootfs_ext4+squashfs() { - local ext4_hash_seed mkfs_ext4_options=() - [[ -e "${pacstrap_dir}" ]] || _msg_error "The path '${pacstrap_dir}' does not exist" 1 +# Create an EROFS image given a directory. +_run_mkerofs() { + local _image_path="${1}" + local _dir="${2}" + local _epoch="${3}" + local fsuuid + fsuuid="$(uuidgen --sha1 --namespace 93a870ff-8565-4cf3-a67b-f47299271a96 --name "${_epoch}")" + [[ ! "${quiet}" == "y" ]] || mkfs_erofs_options+=('--quiet') + _msg_info "Creating EROFS image" + _msg_info "Directory: ${_dir}" + _msg_info "Image path: ${_image_path}" + _msg_info "UUID: ${fsuuid}" + _msg_info "this may take a while..." + mkfs_erofs_options+=('-U' "${fsuuid}" "${_mkfs_options[@]}") + mkfs.erofs "${mkfs_erofs_options[@]}" -- "${_image_path}" "${_dir}" + _msg_info "Done!" +} - _msg_info "Creating ext4 image of 32 GiB and copying '${pacstrap_dir}/' to it..." +# Create and open a LUKS image of the size of the pacstrap directory plus 200MB. +_run_mkluks() { + local _encryption_key + local _image_path="${1}" + local _size="${2}" + local _encryption_key="${3}" + local _epoch="${4}" + local _label="${5}" + local _writable="${6}" + local _grub="${7}" + local _activation_opts=() _format_opts=() _key_msg="" _mapper + local _cryptsetup_opts=('--type' 'luks2') + _mapper=$(basename "${_image_path}")".map" + image_device="/dev/mapper/${_mapper}" + _key_opts=() + _fsuuid="$(uuidgen --sha1 --namespace 93a870ff-8565-4cf3-a67b-f47299271a96 --name "${_epoch}")" + + if [[ "${_encryption_key}" != "" ]]; then + _key_opts=("--key-file=${_encryption_key}") + _cryptsetup_opts+=("${_key_opts[@]}") + _key_msg="with key $(basename "${_encryption_key}")" + fi + + _activation_opts+=("${_cryptsetup_opts[@]}") + _format_opts+=("${_cryptsetup_opts[@]}" \ + '--sector-size' '4096' \ + '--key-size' '512') + + # Remove as soon as gcrypt adds support for argon2i + # and GRUB for the dm-integrity layer + if [ "${_grub}" == "true" ]; then + _format_opts+=('--pbkdf' 'pbkdf2') + else + _format_opts+=('--pbkdf-memory' 256 + '--integrity' 'hmac-sha512') + fi - ext4_hash_seed="$(uuidgen --sha1 --namespace 93a870ff-8565-4cf3-a67b-f47299271a96 \ - --name "${SOURCE_DATE_EPOCH} ext4 hash seed")" - mkfs_ext4_options=( - '-d' "${pacstrap_dir}" - '-O' '^has_journal,^resize_inode' - '-E' "lazy_itable_init=0,root_owner=0:0,hash_seed=${ext4_hash_seed}" - '-m' '0' - '-F' - '-U' 'clear' - ) - [[ ! "${quiet}" == "y" ]] || mkfs_ext4_options+=('-q') - rm -f -- "${pacstrap_dir}.img" - E2FSPROGS_FAKE_TIME="${SOURCE_DATE_EPOCH}" mkfs.ext4 "${mkfs_ext4_options[@]}" -- "${pacstrap_dir}.img" 32G - tune2fs -c 0 -i 0 -- "${pacstrap_dir}.img" > /dev/null + if [ "${_writable}" != "true" ]; then + _msg_info "Integrity journal disabled" + _activation_opts+=('--persistent' \ + '--integrity-no-journal' \ + '--key-size' '512') + fi + + _msg_info "Creating LUKS image $(basename "${_image_path}") with cryptsetup (${_size}KB) ${_key_msg}" + fallocate -l "${_size}K" "${_image_path}" + while ! eval "cryptsetup -q -y luksFormat ${_format_opts[*]} ${_image_path}"; do + sleep 1 + done + + _msg_info "Setting label ${_label}" + while ! eval "cryptsetup config ${_cryptsetup_opts[*]} ${_image_path} --label ${_label}"; do + _close_luks_device "${image_device}" + done + + _msg_info "Setting UUID ${_fsuuid}" + while ! eval "cryptsetup -q -y luksUUID --uuid ${_fsuuid} ${_image_path}"; do + _close_luks_device "${image_device}" + done + + _close_luks_device "${image_device}" + + _msg_info "Opening device on mapper ${_mapper}" + while ! eval "cryptsetup ${_activation_opts[*]} open ${_image_path} ${_mapper}"; do + _close_luks_device "${image_device}" + done + _msg_info "Done!" +} + +# Close a LUKS device mapper +_close_luks_device() { + local _device="${1}" + local _mapper + _mapper=$(basename "${_device}") + blockdev --flushbufs "${_device}" || true + cryptsetup luksClose "${_mapper}" || true + if [ -e "${_device}" ]; then + dmsetup remove "${_device}" || true + fi + sync +} + +# Ask the user for a passphrase if not specified +_mkluks_common() { + local _name="${1}" + local _key="${2}" + local _label="${3}" + local _out="${work_dir}/${_label}.key" + if [ "${_key}" = "" ]; then + echo -n "Insert ${_name} (${_label}) password:" + read -rs password + echo "${password}" > "${_out}" + _encryption_key="${_out}" + fi +} + +# Create an ext4 fs inside a LUKS container image file. +_mkimg_ext4() { + local _dir="${2}" + local _image_path="${3}" + local _size_kib="${4}" + local _epoch="${6}" + local _label="${7}" + local _writable="${8}" + _run_mkext4 "${_dir}" "$(_get_internal_size "${_size_kib}")" \ + "${_epoch}" "${_label}" "${_writable}" + mv "${_dir}.img" "${_image_path}" + sync + _msg_info "Done!" +} + +# Creates an ext4 fs inside a LUKS container image file. +_mkimg_ext4+luks() { + local _name="${1}" + local _dir="${2}" + local _image_path="${3}" + local _size_kib="${4}" + local _encryption_key="${5}" + local _epoch="${6}" + local _label="${7}" + local _writable="${8}" + local _grub="${9}" + _mkluks_common "${_name}" "${_encryption_key}" "${_label}" + _run_mkluks "${_image_path}" "${_size_kib}" "${_encryption_key}" \ + "${_epoch}" "${_label}" "${_writable}" "${_grub}" + _run_mkext4 "${_dir}" "$(_get_internal_size "${_size_kib}")" \ + "${_epoch}" "${_name}" "${_writable}" + dd if="${_dir}.img" of="${image_device}" + sync + _close_luks_device "${image_device}" + _msg_info "Done!" + rm -- "${_dir}.img" +} + +# Creates an ext4 image and packs it inside a squashfs image. +# Packs the squash image inside a LUKS image. +_mkimg_ext4+squashfs+luks() { + local _name="${1}" + local _dir="${2}" + local _image_path="${3}" + local _size_kib="${4}" + local _encryption_key="${5}" + local _epoch="${6}" + local _label="${7}" + local _writable="${8}" + local _grub="${9}" + _mkluks_common "${_name}" "${_encryption_key}" "${_label}" + _run_mkluks "${_image_path}" "${_size_kib}" "${_encryption_key}" \ + "${_epoch}" "${_label}" "${_writable}" "${_grub}" + _run_mkext4 "${_dir}" "$(_get_internal_size "${_size_kib}")" \ + "${_epoch}" "${_name}" "${_writable}" + _run_mksquashfs "${_dir}.img" "${image_device}" + sync + _close_luks_device "${image_device}" _msg_info "Done!" + rm -- "${_dir}.img" +} + +# Creates a squashfs image and saves it on a LUKS image. +_mkimg_squashfs+luks() { + local _name="${1}" + local _dir="${2}" + local _image_path="${3}" + local _size_kib="${4}" + local _encryption_key="${5}" + local _epoch="${6}" + local _label="${7}" + local _writable="${8}" + local _grub="${9}" + _mkluks_common "${_name}" "${_encryption_key}" "${_label}" + _run_mkluks "${_image_path}" "${_size_kib}" "${_encryption_key}" \ + "${_epoch}" "${_label}" "${_writable}" "${_grub}" + _run_mksquashfs "${_dir}" "${image_device}" + sync + _close_luks_device "${image_device}" +} + +# Creates an ext4 file system inside a squashfs image. +_mkimg_ext4+squashfs() { + local _name="${1}" + local _dir="${2}" + local _image_path="${3}" + local _size_kib="${4}" + local _epoch="${5}" + local _label="${6}" + local _writable="false" + _run_mkext4 "${_dir}" "$(_get_internal_size "${_size_kib}")" \ + "${_epoch}" "${_name}" "${_writable}" + _run_mksquashfs "${_dir}.img" "${_image_path}" + _msg_info "Done!" + rm -- "${_dir}.img" +} - install -d -m 0755 -- "${isofs_dir}/${install_dir}/${arch}" - _msg_info "Creating SquashFS image, this may take some time..." - _run_mksquashfs "${pacstrap_dir}.img" +# Creates a squash fs image. +_mkimg_squashfs() { + local _name="${1}" + local _dir="${2}" + local _image_path="${3}" + _run_mksquashfs "${_dir}" "${_image_path}" +} + +# Create an EROFS image containing the root file system and saves it on the ISO 9660 file system. +_mkimg_erofs() { + local _image_path="${1}" + local _dir="${2}" + local _epoch="${3}" + _run_mkerofs "${_image_path}" "${_dir}" "${_epoch}" _msg_info "Done!" - rm -- "${pacstrap_dir}.img" +} + +# Create an EROFS image containing the root file system and saves it on the LUKS image. +# Save the LUKS image on the ISO 9660 file system. +_mkimg_erofs+luks() { + local _name="${1}" + local _dir="${2}" + local _image_path="${3}" + local _size_kib="${4}" + local _encryption_key="${5}" + local _epoch="${6}" + local _label="${7}" + local _grub="${8}" + local _writable="false" + _mkluks_common "${_name}" "${_encryption_key}" "${_label}" + _run_mkluks "${_image_path}" "${_size_kib}" "${_encryption_key}" \ + "${_epoch}" "${_label}" "${_writable}" "${_grub}" + _run_mkerofs "${image_device}" "${_dir}" "${_epoch}" + sync + _close_luks_device "${image_device}" +} + +# Steps shared by _mkairootfs_fs_type functions +_mkairootfs_common() { + _mkfs_options=("${airootfs_image_tool_options[@]}") + _name="airootfs" + _dir="${pacstrap_dir}" + _image_path="${airootfs_img_dir}/airootfs."$(_get_fs_ext "airootfs") + _size_kib="${airootfs_size}" + _epoch="$((SOURCE_DATE_EPOCH))" + _label="${iso_label}" + _writable=false + _grub=false + rm -f -- "${_image_path}" + [[ -e "${_dir}" ]] || _msg_error "The path '${_dir}' does not exist" 1 + install -d -m 0755 -- "${airootfs_img_dir}" +} + +# Create an ext4 image containing the root file system. +# Save the image on the ISO 9660 file system. +_mkairootfs_ext4() { + _mkimg_ext4 "${_dir}" "${_image_path}" "${_size_kib}" "${_epoch}" "${_label}" "${_writable}" +} + +# Create an ext4 image containing the root file system and pack it inside a squashfs image. +# Save the squashfs image on the ISO 9660 file system. +_mkairootfs_ext4+squashfs() { + _mkimg_ext4+squashfs "${_name}" "${_dir}" "${_image_path}" "${_size_kib}" \ + "${_epoch}" "${_label}" "${_writable}" +} + +# Create an ext4 image containing the root file system and pack it inside a squashfs image. +# Pack the squash image inside the LUKS image and save it on the ISO 9660 file system. +_mkairootfs_ext4+squashfs+luks() { + _mkimg_ext4+squashfs+luks "${_name}" "${_dir}" "${_image_path}" "${_size_kib}" \ + "${_encryption_key}" "${_epoch}" "${_writable}" "${_grub}" } # Create a squashfs image containing the root file system and saves it on the ISO 9660 file system. _mkairootfs_squashfs() { - [[ -e "${pacstrap_dir}" ]] || _msg_error "The path '${pacstrap_dir}' does not exist" 1 + _mkimg_squashfs "${_name}" "${_dir}" "${_image_path}" +} - install -d -m 0755 -- "${isofs_dir}/${install_dir}/${arch}" - _msg_info "Creating SquashFS image, this may take some time..." - _run_mksquashfs "${pacstrap_dir}" +# Create a squashfs image containing the root file system and saves it on the LUKS image. +# Saves the LUKS image on the ISO 9660 file system. +_mkairootfs_squashfs+luks() { + _mkimg_squashfs+luks "${_name}" "${_dir}" "${_image_path}" "${_size_kib}" \ + "${encryption_key}" "${_epoch}" "${_label}" "${_writable}" "${_grub}" } # Create an EROFS image containing the root file system and saves it on the ISO 9660 file system. _mkairootfs_erofs() { - local fsuuid mkfs_erofs_options=() - [[ -e "${pacstrap_dir}" ]] || _msg_error "The path '${pacstrap_dir}' does not exist" 1 + _mkimg_erofs "${_image_path}" "${_dir}" "${_epoch}" +} - install -d -m 0755 -- "${isofs_dir}/${install_dir}/${arch}" - local image_path="${isofs_dir}/${install_dir}/${arch}/airootfs.erofs" +# Create an EROFS image containing the root file system and saves it on the LUKS image. +# Save the LUKS image on the ISO 9660 file system. +_mkairootfs_erofs+luks() { + _mkimg_erofs+luks "${_name}" "${_dir}" "${_image_path}" "${_size_kib}" \ + "${_encryption_key}" "${_epoch}" "${_label}" "${_grub}" +} + +# Steps shared by _mkkeys+fs_type functions +_mkkeys_common() { + _mkfs_options=("${keys_image_tool_options[@]}") + _name="keys" + _dir="${keys_dir}" + _image_path="${dongle_isofs_dir}/keys."$(_get_fs_ext "keys") + _encryption_key="" + _size_kib="${keys_size}" + _epoch=$((SOURCE_DATE_EPOCH + 42 * 69)) + _label="${iso_label}_KEYS" + _writable="false" + _grub="false" + rm -f -- "${_image_path}" +} + +# Create an ext4 image containing the keys file system for the dongle. +# Save the image on the dongle ISO 9660 file system. +_mkkeys_ext4() { + _mkimg_ext4 "${_dir}" "${_image_path}" "${_size_kib}" "${_epoch}" "${_label}" "${_writable}" +} + +# Create an ext4 image containing the keys file system and pack it inside a squashfs image. +# Save the squashfs image on the dongle ISO 9660 file system. +_mkkeys_ext4+squashfs() { + _mkimg_ext4+squashfs "${_name}" "${_dir}" "${_image_path}" "${_size_kib}" \ + "${_epoch}" "${_label}" "${_writable}" +} + +# Create an ext4 image containing the keys file system and pack it inside a squashfs image. +# Pack the squash image inside the LUKS image and save it on the ISO 9660 file system. +_mkkeys_ext4+squashfs+luks() { + _mkimg_ext4+squashfs+luks "${_name}" "${_dir}" "${_image_path}" "${_size_kib}" \ + "${_encryption_key}" "${_epoch}" "${_writable}" "${_grub}" +} + +# Create a squashfs image containing the root file system and saves it on the ISO 9660 file system. +_mkkeys_squashfs() { + _mkimg_squashfs "${_name}" "${_dir}" "${_image_path}" +} + +# Create an EROFS image containing the keys file system and saves it on the ISO 9660 file system. +_mkkeys_erofs() { + _mkimg_erofs "${_image_path}" "${_dir}" "${_epoch}" +} + +# Create an EROFS image containing the keys file system and saves it on the LUKS image. +# Save the LUKS image on the ISO 9660 file system. +_mkkeys_erofs+luks() { + _mkimg_erofs+luks "${_name}" "${_dir}" "${_image_path}" "${_size_kib}" \ + "${_encryption_key}" "${_epoch}" "${_label}" "${_grub}" +} + +# Steps shared by _mkpersistent+fs_type functions +_mkpersistent_common() { + _name="persistent" + _dir="${persistent_dir}" + _image_path="${persistentimg}" + _encryption_key="${persistent_encryption_key}" + _size_kib="${persistent_size_kib}" + _epoch=$((SOURCE_DATE_EPOCH + 3 * 1415)) + _label="${iso_label}_PERSISTENT" + _writable="true" + if [[ " ${buildmodes[*]} " =~ ' dongle ' ]]; then + _grub="false" + else + _grub="true" + fi + persistent_epoch="${_epoch}" rm -f -- "${image_path}" - [[ ! "${quiet}" == "y" ]] || mkfs_erofs_options+=('--quiet') - # Generate reproducible file system UUID from SOURCE_DATE_EPOCH - fsuuid="$(uuidgen --sha1 --namespace 93a870ff-8565-4cf3-a67b-f47299271a96 --name "${SOURCE_DATE_EPOCH}")" - mkfs_erofs_options+=('-U' "${fsuuid}" "${airootfs_image_tool_options[@]}") - _msg_info "Creating EROFS image, this may take some time..." - mkfs.erofs "${mkfs_erofs_options[@]}" -- "${image_path}" "${pacstrap_dir}" - _msg_info "Done!" } -# Create checksum file for the rootfs image. +# Create an ext4 image to be used as the persistent ISO partition. +_mkpersistent_ext4() { + persistent_guid="0FC63DAF-8483-4772-8E79-3D69D8477DE4" + _mkimg_ext4 "${_dir}" "${_image_path}" "${_size_kib}" "${_epoch}" "${_label}" "${_writable}" +} + +# Create an ext4 fs inside a LUKS container to be used as the persistent ISO partition. +_mkpersistent_ext4+luks() { + persistent_guid="CA7D7CCB-63ED-4C53-861C-1742536059CC" + _mkimg_ext4+luks "${_name}" "${_dir}" "${_image_path}" "${_encryption_key}" "${_epoch}" \ + "${_writable}" "${_grub}" +} + +# Steps shared by _mkdonglepersistent+fs_type functions +_mkdonglepersistent_common() { + _name="donglepersistent" + _dir="${dongle_persistent_dir}" + _image_path="${donglepersistentimg}" + _encryption_key="" + _size_kib="${persistent_size_kib}" + _epoch=$((SOURCE_DATE_EPOCH + 42 * 69 + 3 * 1415)) + _label="${iso_label}_DONGLE_PERSISTENT" + _writable="true" + _grub="true" + rm -f -- "${image_path}" + dongle_persistent_epoch="${_epoch}" +} + +# Create an ext4 image to be used as the persistent ISO partition. +_mkdonglepersistent_ext4() { + persistent_guid="0FC63DAF-8483-4772-8E79-3D69D8477DE4" + _mkimg_ext4 "${_dir}" "${_image_path}" "${_size_kib}" "${_epoch}" "${_label}" "${_writable}" +} + +# Create an ext4 fs inside a LUKS container to be used as the persistent ISO partition. +_mkdonglepersistent_ext4+luks() { + persistent_guid="CA7D7CCB-63ED-4C53-861C-1742536059CC" + _mkimg_ext4+luks "${_name}" "${_dir}" "${_image_path}" "${_encryption_key}" "${_epoch}" \ + "${_writable}" "${_grub}" +} + +# Create checksum for a given file. _mkchecksum() { - _msg_info "Creating checksum file for self-test..." - cd -- "${isofs_dir}/${install_dir}/${arch}" - if [[ -e "${isofs_dir}/${install_dir}/${arch}/airootfs.sfs" ]]; then - sha512sum airootfs.sfs > airootfs.sha512 - elif [[ -e "${isofs_dir}/${install_dir}/${arch}/airootfs.erofs" ]]; then - sha512sum airootfs.erofs > airootfs.sha512 + local _file + local _dir + local _name + _file="${1}" + _dir=$(dirname "${_file}") + _name=$(basename "${_file%.*}") + + cd -- "${_dir}" + if [[ -e "${_file}" ]]; then + _msg_info "Creating checksum of ${_file} for self-test..." + sha512sum "$(basename "${_file}")" > "${_name}.sha512" fi cd -- "${OLDPWD}" - _msg_info "Done!" } -# GPG sign the root file system image. +# GPG sign a file. _mksignature() { - local airootfs_image_filename gpg_options=() - _msg_info "Signing rootfs image..." - if [[ -e "${isofs_dir}/${install_dir}/${arch}/airootfs.sfs" ]]; then - airootfs_image_filename="${isofs_dir}/${install_dir}/${arch}/airootfs.sfs" - elif [[ -e "${isofs_dir}/${install_dir}/${arch}/airootfs.erofs" ]]; then - airootfs_image_filename="${isofs_dir}/${install_dir}/${arch}/airootfs.erofs" - fi - rm -f -- "${airootfs_image_filename}.sig" + local _file="${1}" + local gpg_options=() + rm -f -- "${_file}.sig" # Add gpg sender option if the value is provided [[ -z "${gpg_sender}" ]] || gpg_options+=('--sender' "${gpg_sender}") - # always use the .sig file extension, as that is what mkinitcpio-archiso's hooks expect - gpg --batch --no-armor --no-include-key-block --output "${airootfs_image_filename}.sig" --detach-sign \ - --default-key "${gpg_key}" "${gpg_options[@]}" "${airootfs_image_filename}" + if [ -e "${_file}" ]; then + _msg_info "Signing $(basename "${_file}")..." + # always use the .sig file extension, as that is what + # mkinitcpio-archiso's hooks expect + _msg_info "GPG home: ${gpg_home}" + if [ "${gpg_home}" != "" ]; then + GNUPGHOME="${gpg_home}" gpg --batch --no-armor --no-include-key-block \ + --output "${_file}.sig" --detach-sign \ + --homedir "${gpg_home}" \ + --default-key "${gpg_key}" "${gpg_options[@]}" "${_file}" + else + gpg --batch --no-armor --no-include-key-block \ + --output "${_file}.sig" --detach-sign \ + --default-key "${gpg_key}" "${gpg_options[@]}" "${_file}" + fi + fi _msg_info "Done!" } +# Get image extension. +_get_fs_ext() { + local _image="${1}" + local _fs_type + _fs_type=$(_get_fs_type "${_image}") + if [ "${_fs_type}" = "squashfs" ]; then + echo "sfs" + else + echo "${_fs_type}" + fi +} + +# Get image extension. +_get_fs_type() { + local _image="${1}" + local _no_luks + _no_luks=$(eval echo \$"{${_image}_image_type%%+luks}") + echo "${_no_luks##*"+"}" +} + +_get_internal_size() { + local _size="${1}" + if [[ "${_size}" -lt 50000 ]]; then + ratio=70 + elif [[ "${_size}" -gt 50000 ]] && [[ "${_size}" -lt 200000 ]]; then + ratio=75 + elif [[ "${_size}" -gt 200000 ]] && [[ "${_size}" -lt 1000000 ]]; then + ratio=80 + elif [[ "${_size}" -gt 200000 ]] && [[ "${_size}" -lt 5000000 ]]; then + ratio=80 + else + ratio=85 + fi + echo $(((_size * ratio)/100)) +} + # Helper function to run functions only one time. # $1: function name _run_once() { if [[ ! -e "${work_dir}/${run_once_mode}.${1}" ]]; then "$1" touch "${work_dir}/${run_once_mode}.${1}" + else + if [ "${quiet}" != "y" ]; then + _msg_info "Command ${1} already run" + fi fi } @@ -291,7 +788,7 @@ _make_pacman_conf() { # see `man 8 pacman` for further info pacman-conf --config "${pacman_conf}" | \ sed "/CacheDir/d;/DBPath/d;/HookDir/d;/LogFile/d;/RootDir/d;/\[options\]/a CacheDir = ${_cache_dirs} - /\[options\]/a HookDir = ${pacstrap_dir}/etc/pacman.d/hooks/" > "${work_dir}/${buildmode}.pacman.conf" + /\[options\]/a HookDir = ${pacstrap_dir}/etc/pacman.d/hooks/" > "${work_dir}/${buildmode}.pacman.conf" } # Prepare working directory and copy custom root file system files. @@ -302,6 +799,7 @@ _make_custom_airootfs() { install -d -m 0755 -o 0 -g 0 -- "${pacstrap_dir}" if [[ -d "${profile}/airootfs" ]]; then + _build_archiso_mkinitcpio_conf _msg_info "Copying custom airootfs files..." cp -af --no-preserve=ownership,mode -- "${profile}/airootfs/." "${pacstrap_dir}" # Set ownership and mode for files and directories @@ -390,103 +888,302 @@ _make_customize_airootfs() { } # Set up boot loaders -_make_bootmodes() { +_make_bootmodes_iso() { local bootmode for bootmode in "${bootmodes[@]}"; do - _run_once "_make_bootmode_${bootmode}" + _run_once "_make_bootmode_${bootmode}_iso" + done +} + +# Set up boot loaders +_make_bootmodes_dongle() { + local bootmode + for bootmode in "${bootmodes[@]}"; do + _run_once "_make_bootmode_${bootmode}_dongle" done } # Copy kernel and initramfs to ISO 9660 _make_boot_on_iso9660() { - local ucode_image - _msg_info "Preparing kernel and initramfs for the ISO 9660 file system..." - install -d -m 0755 -- "${isofs_dir}/${install_dir}/boot/${arch}" - install -m 0644 -- "${pacstrap_dir}/boot/initramfs-"*".img" "${isofs_dir}/${install_dir}/boot/${arch}/" - install -m 0644 -- "${pacstrap_dir}/boot/vmlinuz-"* "${isofs_dir}/${install_dir}/boot/${arch}/" + isofs_dir="${work_dir}/iso" + _make_boot_on_iso "${isofs_dir}" +} + +# Copy kernel and initramfs to dongle ISO 9660 +_make_boot_on_dongle() { + if [[ " ${buildmodes[*]} " =~ ' dongle ' ]]; then + dongle_boot_dir="${work_dir}/dongle_persistent" + else + dongle_boot_dir="${work_dir}/dongle_iso" + fi + _make_boot_on_iso "${dongle_boot_dir}" +} + +# Copy kernel and initramfs to ISO 9660 +_make_boot_on_iso() { + local bootable_dir="${1}" + local _query _src ucode_image + local _cp_opts=('-af' '--no-preserve=ownership,mode') + local boot_dir="${bootable_dir}/${install_dir}/boot" + local kernel_dir="${bootable_dir}/${install_dir}/boot/${arch}" + local licenses_src="${pacstrap_dir}/usr/share/licenses" + local licenses_dest="${boot_dir}/licenses" + local images=("initramfs-*.img" "vmlinuz-*") + _msg_info "Preparing kernel and initramfs for the ISO 9660 file system in ${bootable_dir}..." + + install -d -m 0755 -- "${kernel_dir}" + for _query in "${images[@]}"; do + find "${work_dir}" -iname "${_query}" -exec cp "${_cp_opts[@]}" {} "${kernel_dir}" \; -quit + find "${kernel_dir}" -iname "${_query}" -exec chmod 0644 {} \; + done for ucode_image in "${ucodes[@]}"; do - if [[ -e "${pacstrap_dir}/boot/${ucode_image}" ]]; then - install -m 0644 -- "${pacstrap_dir}/boot/${ucode_image}" "${isofs_dir}/${install_dir}/boot/" - if [[ -e "${pacstrap_dir}/usr/share/licenses/${ucode_image%.*}/" ]]; then - install -d -m 0755 -- "${isofs_dir}/${install_dir}/boot/licenses/${ucode_image%.*}/" - install -m 0644 -- "${pacstrap_dir}/usr/share/licenses/${ucode_image%.*}/"* \ - "${isofs_dir}/${install_dir}/boot/licenses/${ucode_image%.*}/" - fi + find "${work_dir}" -iname "${ucode_image}" -exec cp "${_cp_opts[@]}" {} "${boot_dir}" \; + find "${boot_dir}" -iname "${_query}" -exec chmod 0644 {} \; + if [[ -e "${licenses_src}/${ucode_image%.*}/" ]]; then + install -d -m 0755 -- "${licenses_dest}/${ucode_image%.*}/" + install -m 0644 -- "${licenses_src}/${ucode_image%.*}/"* "${licenses_dest}/${ucode_image%.*}/" fi done _msg_info "Done!" } +# Add archiso modules to profile's mkinitcpio.conf +_build_archiso_mkinitcpio_conf() { + local _hooks=() _mkinitcpio_conf="${profile}/airootfs/etc/mkinitcpio.conf" _modules=() + if [[ "${airootfs_image_type}" == *luks ]] || [[ " ${buildmodes[*]} " =~ ' dongle ' ]]; then + _hooks+=("encrypt") + fi + _hooks+=("archiso") + + if [[ "${airootfs_image_type}" == *"ext4"* ]]; then + _modules+=("ext4") + fi + + cp "${_mkinitcpio_conf}" "${work_dir}/mkinitcpio.conf" + sed "s|%ARCHISO_HOOKS%|${_hooks[*]}|g; + s|%ARCHISO_MODULES%|${_modules[*]}|g" "${work_dir}/mkinitcpio.conf" > "${_mkinitcpio_conf}" +} + +# Get UUID for the dongle GRUB plain mount +_get_dongle_uuid() { + #shellcheck disable=SC2005 + echo "$(_epoch_to_uuid $((SOURCE_DATE_EPOCH + 42 * 69)))" +} + +# Get UUID for the GRUB plain mount +_get_archiso_uuid() { + #shellcheck disable=SC2005 + echo "$(_epoch_to_uuid "${SOURCE_DATE_EPOCH}")" +} + +# Get UUID for the GRUB cryptomount +_get_cryptomount_uuid() { + if [[ " ${buildmodes[*]} " =~ ' dongle ' ]]; then + _epoch="$((SOURCE_DATE_EPOCH + 42 * 69 + 3 * 1415))" + else + _epoch=$((SOURCE_DATE_EPOCH + 3 * 1415)) + fi + uuidgen --sha1 \ + --namespace 93a870ff-8565-4cf3-a67b-f47299271a96 \ + --name "${_epoch}" | sed "s|-||g" +} + +# Produce kernel parameters. +_get_kernel_params() { + local _kparams=() + _kparams+=("archisobasedir=${install_dir}" + "archisolabel=${iso_label}" + "archisouuid=$(_get_archiso_uuid)" + "$(_get_crypto_params)") + if [[ " ${buildmodes[*]} " =~ ' dongle ' ]]; then + _kparams+=("sigdevice=UUID=$(_get_dongle_uuid):iso9660:$(_get_airootfs_path).sig" + "verify=y") + fi + echo "${_kparams[@]}" +} + +# Produce the 'encrypt' hook's kernel parameters needed to boot an encrypted ISO. +_get_crypto_params() { + declare -a _cparams + local _keys_fs_type _keys_path + _keys_fs_type="$(_get_fs_type "keys")" + _keys_path="keys.$(_get_fs_ext "keys")" + _cparams=() + if [[ "${airootfs_image_type}" == *luks ]]; then + _cparams+=("root=/dev/mapper/root" + "cryptdevice=UUID=$(_get_archiso_uuid),$(_get_airootfs_path):root:readonly") + if [ "${encryption_key}" != "" ]; then + _cparams+=("cryptkey=UUID=$(_get_dongle_uuid):iso9660,${_keys_fs_type}:${_keys_path},airootfs.key") + fi + fi + echo "${_cparams[@]}" +} + +# Prepare syslinux for booting from MBR (isohybrid) +_make_bootmode_bios.grub.mbr() { + bootable_dir="${1}" + bootable="${2}" + _msg_info "Setting up GRUB for BIOS booting from a disk..." + install -d -m 0755 -- "${bootable_dir}/EFI/BOOT" + _run_once _make_common_bootmode_grub_cfg + + grub-mkstandalone -O i386-pc \ + --modules="$(_get_grubmodules "bios")" \ + --locales="en@quot" \ + --themes="" \ + --fonts="" \ + --install-modules="$(_get_grubmodules "bios")" \ + --disable-shim-lock \ + --compress=xz \ + -o "${work_dir}/BOOTIA32.MBR" "boot/grub/grub.cfg=${work_dir}/grub/grub.cfg" + + install -D -m 0644 "${work_dir}/BOOTIA32.MBR" "${bootable_dir}/EFI/BOOT" + + if [[ "${bootable}" == "iso" ]]; then + _run_once _make_boot_on_iso9660 + elif [[ "${bootable}" == "dongle" ]]; then + _run_once _make_boot_on_dongle + fi + + _msg_info "Done! GRUB set up for BIOS booting from a disk successfully." +} + +_make_bootmode_bios.grub.mbr_iso() { + _make_bootmode_bios.grub.mbr "${isofs_dir}" "iso" +} + +_make_bootmode_bios.grub.mbr_dongle() { + _make_bootmode_bios.grub.mbr "${dongle_isofs_dir}" "dongle" +} + +# Prepare GRUB for El-Torito booting +_make_bootmode_bios.grub.eltorito() { + local bootable_dir="${1}" + _msg_info "Setting up GRUB for BIOS booting from an optical disc..." + install -d -m 0755 -- "${bootable_dir}/EFI/BOOT" + install -m 0644 -- "${pacstrap_dir}/usr/lib/grub/i386-pc/boot_hybrid.img" "${bootable_dir}/EFI/BOOT/" + _run_once _make_common_bootmode_grub_cfg + + grub-mkstandalone -O i386-pc-eltorito \ + --modules="$(_get_grubmodules "bios")" \ + --locales="en@quot" \ + --themes="" \ + --fonts="" \ + --install-modules="$(_get_grubmodules "bios")" \ + --disable-shim-lock \ + --compress=xz \ + -o "${work_dir}/BOOTIA32.ELTORITO" "boot/grub/grub.cfg=${work_dir}/grub/grub.cfg" + + install -m 0644 -- "${work_dir}/BOOTIA32.ELTORITO" "${bootable_dir}/EFI/BOOT" + + if [[ "${bootable}" == "iso" ]]; then + _run_once _make_boot_on_iso9660 + elif [[ "${bootable}" == "dongle" ]]; then + _run_once _make_boot_on_dongle + fi +} + +_make_bootmode_bios.grub.eltorito_iso() { + _make_bootmode_bios.grub.eltorito "${isofs_dir}" + # ISOLINUX and SYSLINUX installation is shared + _run_once _make_bootmode_bios.grub.mbr_iso +} + +_make_bootmode_bios.grub.eltorito_dongle() { + _make_bootmode_bios.grub.eltorito "${dongle_isofs_dir}" + # ISOLINUX and SYSLINUX installation is shared + _run_once _make_bootmode_bios.grub.mbr_dongle +} + # Prepare syslinux for booting from MBR (isohybrid) _make_bootmode_bios.syslinux.mbr() { + bootable_dir="${1}" + bootable="${2}" _msg_info "Setting up SYSLINUX for BIOS booting from a disk..." - install -d -m 0755 -- "${isofs_dir}/syslinux" + install -d -m 0755 -- "${bootable_dir}/syslinux" for _cfg in "${profile}/syslinux/"*.cfg; do - sed "s|%ARCHISO_LABEL%|${iso_label}|g; - s|%INSTALL_DIR%|${install_dir}|g; - s|%ARCH%|${arch}|g" \ - "${_cfg}" > "${isofs_dir}/syslinux/${_cfg##*/}" + _build_bootloader_config "${_cfg}" "syslinux" > "${bootable_dir}/syslinux/${_cfg##*/}" done if [[ -e "${profile}/syslinux/splash.png" ]]; then - install -m 0644 -- "${profile}/syslinux/splash.png" "${isofs_dir}/syslinux/" + install -m 0644 -- "${profile}/syslinux/splash.png" "${bootable_dir}/syslinux/" fi - install -m 0644 -- "${pacstrap_dir}/usr/lib/syslinux/bios/"*.c32 "${isofs_dir}/syslinux/" - install -m 0644 -- "${pacstrap_dir}/usr/lib/syslinux/bios/lpxelinux.0" "${isofs_dir}/syslinux/" - install -m 0644 -- "${pacstrap_dir}/usr/lib/syslinux/bios/memdisk" "${isofs_dir}/syslinux/" + install -m 0644 -- "${pacstrap_dir}/usr/lib/syslinux/bios/"*.c32 "${bootable_dir}/syslinux/" + install -m 0644 -- "${pacstrap_dir}/usr/lib/syslinux/bios/lpxelinux.0" "${bootable_dir}/syslinux/" + install -m 0644 -- "${pacstrap_dir}/usr/lib/syslinux/bios/memdisk" "${bootable_dir}/syslinux/" - _run_once _make_boot_on_iso9660 + if [[ "${bootable}" == "iso" ]]; then + _run_once _make_boot_on_iso9660 + elif [[ "${bootable}" == "dongle" ]]; then + _run_once _make_boot_on_dongle + fi - if [[ -e "${isofs_dir}/syslinux/hdt.c32" ]]; then - install -d -m 0755 -- "${isofs_dir}/syslinux/hdt" + if [[ -e "${bootable_dir}/syslinux/hdt.c32" ]]; then + install -d -m 0755 -- "${bootable_dir}/syslinux/hdt" if [[ -e "${pacstrap_dir}/usr/share/hwdata/pci.ids" ]]; then gzip -cn9 "${pacstrap_dir}/usr/share/hwdata/pci.ids" > \ - "${isofs_dir}/syslinux/hdt/pciids.gz" + "${bootable_dir}/syslinux/hdt/pciids.gz" fi - find "${pacstrap_dir}/usr/lib/modules" -name 'modules.alias' -print -exec gzip -cn9 '{}' ';' -quit > \ - "${isofs_dir}/syslinux/hdt/modalias.gz" + find "${pacstrap_dir}/usr/lib/modules" -name 'modules.alias' -print -exec gzip -cn9 '{}' ';' > \ + "${bootable_dir}/syslinux/hdt/modalias.gz" fi # Add other aditional/extra files to ${install_dir}/boot/ if [[ -e "${pacstrap_dir}/boot/memtest86+/memtest.bin" ]]; then # rename for PXE: https://wiki.archlinux.org/title/Syslinux#Using_memtest - install -m 0644 -- "${pacstrap_dir}/boot/memtest86+/memtest.bin" "${isofs_dir}/${install_dir}/boot/memtest" - install -d -m 0755 -- "${isofs_dir}/${install_dir}/boot/licenses/memtest86+/" + install -m 0644 -- "${pacstrap_dir}/boot/memtest86+/memtest.bin" "${bootable_dir}/${install_dir}/boot/memtest" + install -d -m 0755 -- "${bootable_dir}/${install_dir}/boot/licenses/memtest86+/" install -m 0644 -- "${pacstrap_dir}/usr/share/licenses/common/GPL2/license.txt" \ - "${isofs_dir}/${install_dir}/boot/licenses/memtest86+/" + "${bootable_dir}/${install_dir}/boot/licenses/memtest86+/" fi _msg_info "Done! SYSLINUX set up for BIOS booting from a disk successfully." } +_make_bootmode_bios.syslinux.mbr_iso() { + _make_bootmode_bios.syslinux.mbr "${isofs_dir}" "iso" +} + +_make_bootmode_bios.syslinux.mbr_dongle() { + _make_bootmode_bios.syslinux.mbr "${dongle_isofs_dir}" "dongle" +} + # Prepare syslinux for El-Torito booting _make_bootmode_bios.syslinux.eltorito() { + local bootable_dir="${1}" _msg_info "Setting up SYSLINUX for BIOS booting from an optical disc..." - install -d -m 0755 -- "${isofs_dir}/syslinux" - install -m 0644 -- "${pacstrap_dir}/usr/lib/syslinux/bios/isolinux.bin" "${isofs_dir}/syslinux/" - install -m 0644 -- "${pacstrap_dir}/usr/lib/syslinux/bios/isohdpfx.bin" "${isofs_dir}/syslinux/" + install -d -m 0755 -- "${bootable_dir}/syslinux" + install -m 0644 -- "${pacstrap_dir}/usr/lib/syslinux/bios/isolinux.bin" "${bootable_dir}/syslinux/" + install -m 0644 -- "${pacstrap_dir}/usr/lib/syslinux/bios/isohdpfx.bin" "${bootable_dir}/syslinux/" + + _msg_info "Done! SYSLINUX set up for BIOS booting from an optical disc successfully." +} +_make_bootmode_bios.syslinux.eltorito_iso() { + _make_bootmode_bios.syslinux.eltorito "${isofs_dir}" # ISOLINUX and SYSLINUX installation is shared - _run_once _make_bootmode_bios.syslinux.mbr + _run_once _make_bootmode_bios.syslinux.mbr_iso - _msg_info "Done! SYSLINUX set up for BIOS booting from an optical disc successfully." +} + +_make_bootmode_bios.syslinux.eltorito_dongle() { + _make_bootmode_bios.syslinux.eltorito "${dongle_isofs_dir}" + # ISOLINUX and SYSLINUX installation is shared + _run_once _make_bootmode_bios.syslinux.mbr_dongle } # Copy kernel and initramfs to FAT image _make_boot_on_fat() { - local ucode_image all_ucode_images=() + local _images _ucode_image _msg_info "Preparing kernel and initramfs for the FAT file system..." - mmd -i "${work_dir}/efiboot.img" \ + _images=("initramfs-*.img" "vmlinuz-*") + mmd -D o -i "${efibootimg}" \ "::/${install_dir}" "::/${install_dir}/boot" "::/${install_dir}/boot/${arch}" - mcopy -i "${work_dir}/efiboot.img" "${pacstrap_dir}/boot/vmlinuz-"* \ - "${pacstrap_dir}/boot/initramfs-"*".img" "::/${install_dir}/boot/${arch}/" - for ucode_image in "${ucodes[@]}"; do - if [[ -e "${pacstrap_dir}/boot/${ucode_image}" ]]; then - all_ucode_images+=("${pacstrap_dir}/boot/${ucode_image}") - fi + for _query in "${_images[@]}"; do + find "${work_dir}" -iname "${_query}" -exec mcopy -D "o" -i "${efibootimg}" {} "::/${install_dir}/boot/${arch}/" \; -quit + done + for _ucode_image in "${ucodes[@]}"; do + find "${work_dir}" -iname "${_ucode_image}" -exec mcopy -D "o" -i "${efibootimg}" {} "::/${install_dir}/boot/" \; done - if (( ${#all_ucode_images[@]} )); then - mcopy -i "${work_dir}/efiboot.img" "${all_ucode_images[@]}" "::/${install_dir}/boot/" - fi _msg_info "Done!" } @@ -498,9 +1195,12 @@ _make_efibootimg() { if (( imgsize_bytes < 2*1024*1024 )); then _msg_info "Validating '${bootmode}': efiboot.img size is ${imgsize_bytes} bytes is less than 2 MiB! Bumping up to 2 MiB" - imgsize_bytes=$((2*1024*1024)) + imgsize_bytes=$((20*1024*1024)) fi + # Where it spills? + imgsize_bytes=$((imgsize_bytes * 10)) + # Convert from bytes to KiB and round up to the next full MiB with an additional MiB for reserved sectors. imgsize_kib="$(awk 'function ceil(x){return int(x)+(x>int(x))} function byte_to_kib(x){return x/1024} @@ -509,32 +1209,85 @@ _make_efibootimg() { )" # The FAT image must be created with mkfs.fat not mformat, as some systems have issues with mformat made images: # https://lists.gnu.org/archive/html/grub-devel/2019-04/msg00099.html - rm -f -- "${work_dir}/efiboot.img" + rm -f -- "${efibootimg}" _msg_info "Creating FAT image of size: ${imgsize_kib} KiB..." if [[ "${quiet}" == "y" ]]; then # mkfs.fat does not have a -q/--quiet option, so redirect stdout to /dev/null instead # https://github.com/dosfstools/dosfstools/issues/103 - mkfs.fat -C -n ARCHISO_EFI "${work_dir}/efiboot.img" "${imgsize_kib}" > /dev/null + mkfs.fat -C -n ARCHISO_EFI "${efibootimg}" "${imgsize_kib}" > /dev/null else - mkfs.fat -C -n ARCHISO_EFI "${work_dir}/efiboot.img" "${imgsize_kib}" + mkfs.fat -C -n ARCHISO_EFI "${efibootimg}" "${imgsize_kib}" fi # Create the default/fallback boot path in which a boot loaders will be placed later. - mmd -i "${work_dir}/efiboot.img" ::/EFI ::/EFI/BOOT + mmd -D o -i "${efibootimg}" ::/EFI ::/EFI/BOOT } -# Copy the grub.cfg file in efiboot.img which is used by both IA32 UEFI and x64 UEFI. -_make_efibootimg_grubcfg() { - mcopy -i "${work_dir}/efiboot.img" \ - "${work_dir}/grub.cfg" ::/EFI/BOOT/grub.cfg +# Copy GRUB files to efiboot.img which is used by both IA32 UEFI and x64 UEFI. +_make_common_bootmode_grub_copy_to_efibootimg() { + local files_to_copy=() + files_to_copy+=("${work_dir}/grub/"*'.cfg') + if compgen -G "${profile}/grub/!(*.cfg)" &> /dev/null; then + files_to_copy+=("${profile}/grub/"!(*.cfg)) + fi + mcopy -D o -i "${efibootimg}" "${files_to_copy[@]}" ::/EFI/BOOT/ } -_make_bootmode_uefi-ia32.grub.esp() { +_epoch_to_uuid() { + local _epoch="${1}" + local _uuid + TZ=UTC printf -v _uuid '%(%F-%H-%M-%S-00)T' "${_epoch}" + echo "${_uuid}" +} + +_get_device_select_cmdline() { + #shellcheck disable=SC2005 + if [[ "${persistent_image_type}" == *luks ]]; then + echo "cryptomount -u $(_get_cryptomount_uuid)" + else + echo "search --no-floppy --set=root --fs-uuid $(_get_bootable_uuid)" + fi +} + +_get_bootable_uuid() { + local _uuid + _uuid="$(_get_archiso_uuid)" + if [[ " ${buildmodes[*]} " =~ ' dongle ' ]]; then + _uuid=$(_get_dongle_uuid) + fi + echo "${_uuid}" +} + +# Fill a bootloader configuration template and copy the result in a file +_build_bootloader_config() { + local _template="${1}" + local _bootloader="${2}" + local _boot_disk + if [[ "${persistent_image_type}" == *luks ]] && [[ "${_bootloader}" = "grub" ]]; then + _boot_disk="(crypto0)" + else + _boot_disk="" + fi + sed "s|%DEVICE_SELECT_CMDLINE%|$(_get_device_select_cmdline)|g; + s|%ARCH%|${arch}|g; + s|%INSTALL_DIR%|${_boot_disk}/${install_dir}|g; + s|%KERNEL_PARAMS%|$(_get_kernel_params)|g; + s|%BOOTABLE_UUID%|$(_get_bootable_uuid)|g" "${_template}" +} + +# Prepare GRUB configuration files +_make_common_bootmode_grub_cfg(){ + local _cfg + + install -d -- "${work_dir}/grub" + # Fill GRUB configuration files - sed "s|%ARCHISO_LABEL%|${iso_label}|g; - s|%INSTALL_DIR%|${install_dir}|g; - s|%ARCH%|${arch}|g" \ - "${profile}/grub/grub.cfg" > "${work_dir}/grub.cfg" + for _cfg in "${profile}/grub/"*'.cfg'; do + _build_bootloader_config "${_cfg}" "grub" > "${work_dir}/grub/${_cfg##*/}" + done + # Add all GRUB files to the list of files used to calculate the required FAT image size. + efiboot_files+=("${work_dir}/grub/" + "${profile}/grub/"!(*.cfg)) IFS='' read -r -d '' grubembedcfg <<'EOF' || true if ! [ -d "$cmdpath" ]; then @@ -547,153 +1300,225 @@ fi configfile "${cmdpath}/grub.cfg" EOF printf '%s\n' "$grubembedcfg" > "${work_dir}/grub-embed.cfg" +} + +_make_common_bootmode_grub_copy_to_isofs_iso() { + isofs_dir="${work_dir}/iso" + _make_common_bootmode_grub_copy_to_isofs "${isofs_dir}" +} + +_make_common_bootmode_grub_copy_to_isofs_dongle() { + dongle_isofs_dir="${work_dir}/dongle_iso" + _make_common_bootmode_grub_copy_to_isofs "${dongle_isofs_dir}" +} + +_make_common_bootmode_grub_copy_to_isofs() { + local isofs_dir="${1}" + local files_to_copy=() + + files_to_copy+=("${work_dir}/grub/"*'.cfg') + if compgen -G "${profile}/grub/!(*.cfg)" &> /dev/null; then + files_to_copy+=("${profile}/grub/"!(*.cfg)) + fi + install -m 0644 -- "${files_to_copy[@]}" "${isofs_dir}/EFI/BOOT/" +} + +# Module list from https://bugs.archlinux.org/task/71382#comment202911 +_get_grubmodules(){ + local _mode="${1}" + _modules=(afsplitter boot bufio chain configfile cryptodisk disk ext2 gcry_rijndael + gcry_sha256 gcry_sha512 halt iso9660 linux loadenv loopback luks2 minicmd + normal ntfs part_apple part_gpt part_msdos password_pbkdf2 reboot search + search_fs_uuid usb) + if [ "${_mode}" != "bios" ]; then + _modules+=(at_keyboard all_video btrfs cat echo diskfilter echo efifwsetup f2fs + fat font gcry_crc gfxmenu gfxterm gzio hfsplus jpeg keylayouts ls + lsefi lsefimmap lzopio png read regexp search_fs_file search_label + serial sleep tpm trig usbserial_common usbserial_ftdi usbserial_pl2303 + usbserial_usbdebug video xfs zstd) + elif [ "${_mode}" == "bios" ] ; then + _modules+=(biosdisk) + fi + echo "${_modules[*]}" +} + +_make_bootmode_uefi-ia32.grub.esp() { + local bootable="${1}" + + # Prepare configuration files + _run_once _make_common_bootmode_grub_cfg # Create EFI binary grub-mkstandalone -O i386-efi \ - --modules="part_gpt part_msdos fat iso9660" \ + --modules="$(_get_grubmodules "efi")" \ --locales="en@quot" \ --themes="" \ --sbat=/usr/share/grub/sbat.csv \ + --disable-shim-lock \ -o "${work_dir}/BOOTIA32.EFI" "boot/grub/grub.cfg=${work_dir}/grub-embed.cfg" + # Add GRUB to the list of files used to calculate the required FAT image size. efiboot_files+=("${work_dir}/BOOTIA32.EFI" "${pacstrap_dir}/usr/share/edk2-shell/ia32/Shell_Full.efi") if [[ " ${bootmodes[*]} " =~ uefi-x64.systemd-boot.esp ]]; then # TODO: Remove this branch. - _run_once _make_bootmode_uefi-x64.systemd-boot.esp + _run_once "_make_bootmode_uefi-x64.systemd-boot.esp_${bootable}" elif [[ " ${bootmodes[*]} " =~ uefi-x64.grub.esp ]]; then - _run_once _make_bootmode_uefi-x64.grub.esp + _run_once "_make_bootmode_uefi-x64.grub.esp_${bootable}" else - efiboot_imgsize="$(du -bc "${efiboot_files[@]}" \ - 2>/dev/null | awk 'END { print $1 }')" + efiboot_imgsize="$(du -bcs -- "${efiboot_files[@]}" 2>/dev/null | awk 'END { print $1 }')" # Create a FAT image for the EFI system partition _make_efibootimg "$efiboot_imgsize" fi # Copy GRUB EFI binary to the default/fallback boot path - mcopy -i "${work_dir}/efiboot.img" \ - "${work_dir}/BOOTIA32.EFI" ::/EFI/BOOT/BOOTIA32.EFI + mcopy -D o -i "${efibootimg}" "${work_dir}/BOOTIA32.EFI" ::/EFI/BOOT/BOOTIA32.EFI - # Copy GRUB configuration files - _run_once _make_efibootimg_grubcfg + # Copy GRUB files + _run_once _make_common_bootmode_grub_copy_to_efibootimg if [[ -e "${pacstrap_dir}/usr/share/edk2-shell/ia32/Shell_Full.efi" ]]; then - mcopy -i "${work_dir}/efiboot.img" \ - "${pacstrap_dir}/usr/share/edk2-shell/ia32/Shell_Full.efi" ::/shellia32.efi + mcopy -D o -i "${efibootimg}" "${pacstrap_dir}/usr/share/edk2-shell/ia32/Shell_Full.efi" ::/shellia32.efi fi _msg_info "Done! GRUB set up for UEFI booting successfully." } +_make_bootmode_uefi-ia32.grub.esp_iso() { + _make_bootmode_uefi-ia32.grub.esp "iso" +} + +_make_bootmode_uefi-ia32.grub.esp_dongle() { + _make_bootmode_uefi-ia32.grub.esp "dongle" +} + # Prepare GRUB for El Torito booting _make_bootmode_uefi-ia32.grub.eltorito() { + local bootable_dir="${1}" + local bootable="${2}" # El Torito UEFI boot requires an image containing the EFI system partition. # uefi-ia32.grub.eltorito has the same requirements as uefi-ia32.grub.esp - _run_once _make_bootmode_uefi-ia32.grub.esp + _run_once "_make_bootmode_uefi-ia32.grub.esp_${bootable}" + + # Prepare configuration files + _run_once _make_common_bootmode_grub_cfg # Additionally set up systemd-boot in ISO 9660. This allows creating a medium for the live environment by using # manual partitioning and simply copying the ISO 9660 file system contents. # This is not related to El Torito booting and no firmware uses these files. _msg_info "Preparing an /EFI directory for the ISO 9660 file system..." - install -d -m 0755 -- "${isofs_dir}/EFI/BOOT" + install -d -m 0755 -- "${bootable_dir}/EFI/BOOT" # Copy GRUB EFI binary to the default/fallback boot path - install -m 0644 -- "${work_dir}/BOOTIA32.EFI" \ - "${isofs_dir}/EFI/BOOT/BOOTIA32.EFI" + install -m 0644 -- "${work_dir}/BOOTIA32.EFI" "${bootable_dir}/EFI/BOOT/BOOTIA32.EFI" # Copy GRUB configuration files - install -m 0644 -- "${work_dir}/grub.cfg" "${isofs_dir}/EFI/BOOT/grub.cfg" + _run_once "_make_common_bootmode_grub_copy_to_isofs_${bootable}" # edk2-shell based UEFI shell if [[ -e "${pacstrap_dir}/usr/share/edk2-shell/ia32/Shell_Full.efi" ]]; then - install -m 0644 -- "${pacstrap_dir}/usr/share/edk2-shell/ia32/Shell_Full.efi" \ - "${isofs_dir}/shellia32.efi" + install -m 0644 -- "${pacstrap_dir}/usr/share/edk2-shell/ia32/Shell_Full.efi" "${bootable_dir}/shellia32.efi" fi _msg_info "Done!" } -_make_bootmode_uefi-x64.grub.esp() { - # Fill Grub configuration files - sed "s|%ARCHISO_LABEL%|${iso_label}|g; - s|%INSTALL_DIR%|${install_dir}|g; - s|%ARCH%|${arch}|g" \ - "${profile}/grub/grub.cfg" > "${work_dir}/grub.cfg" +_make_bootmode_uefi-ia32.grub.eltorito_iso() { + _make_bootmode_uefi-ia32.grub.eltorito "${isofs_dir}" "iso" +} - IFS='' read -r -d '' grubembedcfg <<'EOF' || true -if ! [ -d "$cmdpath" ]; then - # On some firmware, GRUB has a wrong cmdpath when booted from an optical disc. - # https://gitlab.archlinux.org/archlinux/archiso/-/issues/183 - if regexp --set=1:isodevice '^(\([^)]+\))\/?[Ee][Ff][Ii]\/[Bb][Oo][Oo][Tt]\/?$' "$cmdpath"; then - cmdpath="${isodevice}/EFI/BOOT" - fi -fi -configfile "${cmdpath}/grub.cfg" -EOF - printf '%s\n' "$grubembedcfg" > "${work_dir}/grub-embed.cfg" +_make_bootmode_uefi-ia32.grub.eltorito_dongle() { + _make_bootmode_uefi-ia32.grub.eltorito "${dongle_isofs_dir}" "dongle" +} + +_make_bootmode_uefi-x64.grub.esp() { + # Prepare configuration files + _run_once _make_common_bootmode_grub_cfg # Create EFI binary grub-mkstandalone -O x86_64-efi \ - --modules="part_gpt part_msdos fat iso9660" \ + --modules="$(_get_grubmodules "efi")" \ --locales="en@quot" \ --themes="" \ --sbat=/usr/share/grub/sbat.csv \ + --disable-shim-lock \ -o "${work_dir}/BOOTx64.EFI" "boot/grub/grub.cfg=${work_dir}/grub-embed.cfg" # Add GRUB to the list of files used to calculate the required FAT image size. efiboot_files+=("${work_dir}/BOOTx64.EFI" "${pacstrap_dir}/usr/share/edk2-shell/x64/Shell_Full.efi") - efiboot_imgsize="$(du -bc "${efiboot_files[@]}" \ - 2>/dev/null | awk 'END { print $1 }')" + efiboot_imgsize="$(du -bcs -- "${efiboot_files[@]}" 2>/dev/null | awk 'END { print $1 }')" # Create a FAT image for the EFI system partition _make_efibootimg "$efiboot_imgsize" - # Copy grub EFI binary to the default/fallback boot path - mcopy -i "${work_dir}/efiboot.img" \ - "${work_dir}/BOOTx64.EFI" ::/EFI/BOOT/BOOTx64.EFI + # Copy GRUB EFI binary to the default/fallback boot path + mcopy -D o -i "${efibootimg}" "${work_dir}/BOOTx64.EFI" ::/EFI/BOOT/BOOTx64.EFI - _run_once _make_efibootimg_grubcfg + # Copy GRUB files + _run_once _make_common_bootmode_grub_copy_to_efibootimg if [[ -e "${pacstrap_dir}/usr/share/edk2-shell/x64/Shell_Full.efi" ]]; then - mcopy -i "${work_dir}/efiboot.img" \ - "${pacstrap_dir}/usr/share/edk2-shell/x64/Shell_Full.efi" ::/shellx64.efi + mcopy -D o -i "${efibootimg}" \ + "${pacstrap_dir}/usr/share/edk2-shell/x64/Shell_Full.efi" \ + "::/shellx64.efi" fi _msg_info "Done! GRUB set up for UEFI booting successfully." } +_make_bootmode_uefi-x64.grub.esp_iso() { + _make_bootmode_uefi-x64.grub.esp +} + +_make_bootmode_uefi-x64.grub.esp_dongle() { + _make_bootmode_uefi-x64.grub.esp +} + # Prepare GRUB for El Torito booting _make_bootmode_uefi-x64.grub.eltorito() { + local bootable_dir="${1}" + local bootable="${2}" # El Torito UEFI boot requires an image containing the EFI system partition. # uefi-x64.grub.eltorito has the same requirements as uefi-x64.grub.esp - _run_once _make_bootmode_uefi-x64.grub.esp + _run_once "_make_bootmode_uefi-x64.grub.esp_${bootable}" + + # Prepare configuration files + _run_once _make_common_bootmode_grub_cfg # Additionally set up systemd-boot in ISO 9660. This allows creating a medium for the live environment by using # manual partitioning and simply copying the ISO 9660 file system contents. # This is not related to El Torito booting and no firmware uses these files. _msg_info "Preparing an /EFI directory for the ISO 9660 file system..." - install -d -m 0755 -- "${isofs_dir}/EFI/BOOT" + install -d -m 0755 -- "${bootable_dir}/EFI/BOOT" # Copy GRUB EFI binary to the default/fallback boot path install -m 0644 -- "${work_dir}/BOOTx64.EFI" \ - "${isofs_dir}/EFI/BOOT/BOOTx64.EFI" + "${bootable_dir}/EFI/BOOT/BOOTx64.EFI" - # Copy GRUB configuration files - install -m 0644 -- "${work_dir}/grub.cfg" "${isofs_dir}/EFI/BOOT" + # Copy GRUB files + _run_once "_make_common_bootmode_grub_copy_to_isofs_${bootable}" # edk2-shell based UEFI shell if [[ -e "${pacstrap_dir}/usr/share/edk2-shell/x64/Shell_Full.efi" ]]; then - install -m 0644 -- "${pacstrap_dir}/usr/share/edk2-shell/x64/Shell_Full.efi" "${isofs_dir}/shellx64.efi" + install -m 0644 -- "${pacstrap_dir}/usr/share/edk2-shell/x64/Shell_Full.efi" "${bootable_dir}/shellx64.efi" fi _msg_info "Done!" } +_make_bootmode_uefi-x64.grub.eltorito_iso() { + _make_bootmode_uefi-x64.grub.eltorito "${isofs_dir}" "iso" +} + +_make_bootmode_uefi-x64.grub.eltorito_dongle() { + _make_bootmode_uefi-x64.grub.eltorito "${dongle_isofs_dir}" "dongle" +} + # Prepare systemd-boot for booting when written to a disk (isohybrid) _make_bootmode_uefi-x64.systemd-boot.esp() { - local _file efiboot_imgsize + local _file efiboot_imgsize _results local _available_ucodes=() _msg_info "Setting up systemd-boot for UEFI booting..." @@ -706,31 +1531,30 @@ _make_bootmode_uefi-x64.systemd-boot.esp() { efiboot_files+=("${pacstrap_dir}/usr/lib/systemd/boot/efi/systemd-bootx64.efi" "${pacstrap_dir}/usr/share/edk2-shell/x64/Shell_Full.efi" "${profile}/efiboot/" - "${pacstrap_dir}/boot/vmlinuz-"* - "${pacstrap_dir}/boot/initramfs-"*".img" "${_available_ucodes[@]}") - efiboot_imgsize="$(du -bc "${efiboot_files[@]}" \ - 2>/dev/null | awk 'END { print $1 }')" + _images=("initramfs-*.img" "vmlinuz-*") + for _query in "${_images[@]}"; do + mapfile -t efiboot_files < <(find "${work_dir}" -iname "${_query}" -exec echo {} \; -quit) + done + + efiboot_imgsize="$(du -bcs -- "${efiboot_files[@]}" 2>/dev/null | awk 'END { print $1 }')" # Create a FAT image for the EFI system partition _make_efibootimg "$efiboot_imgsize" # Copy systemd-boot EFI binary to the default/fallback boot path - mcopy -i "${work_dir}/efiboot.img" \ + mcopy -D o -i "${efibootimg}" \ "${pacstrap_dir}/usr/lib/systemd/boot/efi/systemd-bootx64.efi" ::/EFI/BOOT/BOOTx64.EFI # Copy systemd-boot configuration files - mmd -i "${work_dir}/efiboot.img" ::/loader ::/loader/entries - mcopy -i "${work_dir}/efiboot.img" "${profile}/efiboot/loader/loader.conf" ::/loader/ + mmd -D o -i "${efibootimg}" ::/loader ::/loader/entries + mcopy -D o -i "${efibootimg}" "${profile}/efiboot/loader/loader.conf" ::/loader/ for _conf in "${profile}/efiboot/loader/entries/"*".conf"; do - sed "s|%ARCHISO_LABEL%|${iso_label}|g; - s|%INSTALL_DIR%|${install_dir}|g; - s|%ARCH%|${arch}|g" \ - "${_conf}" | mcopy -i "${work_dir}/efiboot.img" - "::/loader/entries/${_conf##*/}" - done + _build_bootloader_config "${_conf}" "systemd-boot" | mcopy -D o -i "${efibootimg}" - "::/loader/entries/${_conf##*/}" + done # shellx64.efi is picked up automatically when on / if [[ -e "${pacstrap_dir}/usr/share/edk2-shell/x64/Shell_Full.efi" ]]; then - mcopy -i "${work_dir}/efiboot.img" \ + mcopy -D o -i "${efibootimg}" \ "${pacstrap_dir}/usr/share/edk2-shell/x64/Shell_Full.efi" ::/shellx64.efi fi @@ -741,42 +1565,123 @@ _make_bootmode_uefi-x64.systemd-boot.esp() { _msg_info "Done! systemd-boot set up for UEFI booting successfully." } +_make_bootmode_uefi-x64.systemd-boot.esp_iso() { + _make_bootmode_uefi-x64.systemd-boot.esp +} + +_make_bootmode_uefi-x64.systemd-boot.esp_dongle() { + _make_bootmode_uefi-x64.systemd-boot.esp +} + # Prepare systemd-boot for El Torito booting _make_bootmode_uefi-x64.systemd-boot.eltorito() { + local bootable_dir="${1}" + local bootable="${2}" # El Torito UEFI boot requires an image containing the EFI system partition. # uefi-x64.systemd-boot.eltorito has the same requirements as uefi-x64.systemd-boot.esp - _run_once _make_bootmode_uefi-x64.systemd-boot.esp + _run_once "_make_bootmode_uefi-x64.systemd-boot.esp_${bootable}" # Additionally set up systemd-boot in ISO 9660. This allows creating a medium for the live environment by using # manual partitioning and simply copying the ISO 9660 file system contents. # This is not related to El Torito booting and no firmware uses these files. _msg_info "Preparing an /EFI directory for the ISO 9660 file system..." - install -d -m 0755 -- "${isofs_dir}/EFI/BOOT" + install -d -m 0755 -- "${bootable_dir}/EFI/BOOT" # Copy systemd-boot EFI binary to the default/fallback boot path install -m 0644 -- "${pacstrap_dir}/usr/lib/systemd/boot/efi/systemd-bootx64.efi" \ - "${isofs_dir}/EFI/BOOT/BOOTx64.EFI" + "${bootable_dir}/EFI/BOOT/BOOTx64.EFI" # Copy systemd-boot configuration files - install -d -m 0755 -- "${isofs_dir}/loader/entries" - install -m 0644 -- "${profile}/efiboot/loader/loader.conf" "${isofs_dir}/loader/" + install -d -m 0755 -- "${bootable_dir}/loader/entries" + install -m 0644 -- "${profile}/efiboot/loader/loader.conf" "${bootable_dir}/loader/" for _conf in "${profile}/efiboot/loader/entries/"*".conf"; do - sed "s|%ARCHISO_LABEL%|${iso_label}|g; - s|%INSTALL_DIR%|${install_dir}|g; - s|%ARCH%|${arch}|g" \ - "${_conf}" > "${isofs_dir}/loader/entries/${_conf##*/}" - done + _build_bootloader_config "${_conf}" "systemd-boot" > "${bootable_dir}/loader/entries/${_conf##*/}" + done + + # edk2-shell based UEFI shell + # shellx64.efi is picked up automatically when on / + if [[ -e "${pacstrap_dir}/usr/share/edk2-shell/x64/Shell_Full.efi" ]]; then + install -m 0644 -- "${pacstrap_dir}/usr/share/edk2-shell/x64/Shell_Full.efi" "${bootable_dir}/shellx64.efi" + fi + + _msg_info "Done!" +} + +# Prepare systemd-boot for El Torito booting +_make_bootmode_uefi-x64.systemd-boot.eltorito_iso() { + _make_bootmode_uefi-x64.systemd-boot.eltorito "${isofs_dir}" "iso" +} + +# Prepare systemd-boot for El Torito booting +_make_bootmode_uefi-x64.systemd-boot.eltorito_dongle() { + _make_bootmode_uefi-x64.systemd-boot.eltorito "${dongle_isofs_dir}" "dongle" +} + +_validate_common_requirements_bootmode_grub(){ + # Check if GRUB is available + if ! command -v grub-mkstandalone &> /dev/null; then + (( validation_error=validation_error+1 )) + _msg_error "Validating '${bootmode}': grub-mkstandalone is not available on this host. Install 'grub'!" 0 + fi + + # Check if GRUB configuration files exist + if [[ ! -d "${profile}/grub" ]]; then + (( validation_error=validation_error+1 )) + _msg_error "Validating '${bootmode}': The '${profile}/grub' directory is missing!" 0 + else + if [[ ! -e "${profile}/grub/grub.cfg" ]]; then + (( validation_error=validation_error+1 )) + _msg_error "Validating '${bootmode}': File '${profile}/grub/grub.cfg' not found!" 0 + fi + local conffile + for conffile in "${profile}/grub/"*'.cfg'; do + if [[ -e "${conffile}" ]]; then + break + else + (( validation_error=validation_error+1 )) + _msg_error "Validating '${bootmode}': No configuration file found in '${profile}/grub/'!" 0 + fi + done + fi +} + +_validate_common_requirements_bootmode_uefi(){ + # Check if mkfs.fat is available + if ! command -v mkfs.fat &> /dev/null; then + (( validation_error=validation_error+1 )) + _msg_error "Validating '${bootmode}': mkfs.fat is not available on this host. Install 'dosfstools'!" 0 + fi + # Check if mmd and mcopy are available + if ! { command -v mmd &> /dev/null && command -v mcopy &> /dev/null; }; then + _msg_error "Validating '${bootmode}': mmd and/or mcopy are not available on this host. Install 'mtools'!" 0 + fi +} + +_validate_requirements_bootmode_bios.grub.mbr() { + # bios.syslinux.mbr requires bios.grub.eltorito + # actually I'm not sure this is true + _validate_common_requirements_bootmode_grub + # shellcheck disable=SC2076 + if [[ ! " ${bootmodes[*]} " =~ ' bios.grub.eltorito ' ]]; then + (( validation_error=validation_error+1 )) + _msg_error "Using 'bios.grub.mbr' boot mode without 'bios.grub.eltorito' is not supported." 0 + fi - # edk2-shell based UEFI shell - # shellx64.efi is picked up automatically when on / - if [[ -e "${pacstrap_dir}/usr/share/edk2-shell/x64/Shell_Full.efi" ]]; then - install -m 0644 -- "${pacstrap_dir}/usr/share/edk2-shell/x64/Shell_Full.efi" "${isofs_dir}/shellx64.efi" + # Check if the grub package is in the package list + # shellcheck disable=SC2076 + if [[ ! " ${pkg_list[*]} " =~ ' grub ' ]]; then + (( validation_error=validation_error+1 )) + _msg_error "Validating '${bootmode}': The 'grub' package is missing from the package list!" 0 fi +} - _msg_info "Done!" +_validate_requirements_bootmode_bios.grub.eltorito() { + # bios.grub.eltorito has the exact same requirements as bios.grub.mbr + _validate_requirements_bootmode_bios.grub.mbr } _validate_requirements_bootmode_bios.syslinux.mbr() { + local _msg # bios.syslinux.mbr requires bios.syslinux.eltorito # shellcheck disable=SC2076 if [[ ! " ${bootmodes[*]} " =~ ' bios.syslinux.eltorito ' ]]; then @@ -810,7 +1715,9 @@ _validate_requirements_bootmode_bios.syslinux.mbr() { # Check for optional packages # shellcheck disable=SC2076 if [[ ! " ${pkg_list[*]} " =~ ' memtest86+ ' ]]; then - _msg_info "Validating '${bootmode}': 'memtest86+' is not in the package list. Memmory testing will not be available from syslinux." + _msg=("Validating '${bootmode}': 'memtest86+' is not in the package list." + "Memory testing will not be available from syslinux.") + _msg_info "${_msg[*]}" fi } @@ -825,17 +1732,7 @@ _validate_requirements_bootmode_uefi-x64.systemd-boot.esp() { _msg_error "Validating '${bootmode}': cannot be used with bootmode uefi-x64.grub.esp!" 0 fi - # Check if mkfs.fat is available - if ! command -v mkfs.fat &> /dev/null; then - (( validation_error=validation_error+1 )) - _msg_error "Validating '${bootmode}': mkfs.fat is not available on this host. Install 'dosfstools'!" 0 - fi - - # Check if mmd and mcopy are available - if ! { command -v mmd &> /dev/null && command -v mcopy &> /dev/null; }; then - (( validation_error=validation_error+1 )) - _msg_error "Validating '${bootmode}': mmd and/or mcopy are not available on this host. Install 'mtools'!" 0 - fi + _validate_common_requirements_bootmode_uefi # Check if systemd-boot configuration files exist if [[ ! -d "${profile}/efiboot/loader/entries" ]]; then @@ -875,12 +1772,7 @@ _validate_requirements_bootmode_uefi-x64.systemd-boot.eltorito() { } _validate_requirements_bootmode_uefi-ia32.grub.esp() { - # Check if GRUB is available - if ! command -v grub-mkstandalone &> /dev/null; then - (( validation_error=validation_error+1 )) - _msg_error "Validating '${bootmode}': grub-install is not available on this host. Install 'grub'!" 0 - fi - + _validate_common_requirements_bootmode_grub # shellcheck disable=SC2076 if [[ " ${bootmodes[*]} " =~ ' uefi-x64.systemd-boot.esp ' ]]; then _validate_requirements_bootmode_uefi-x64.systemd-boot.esp @@ -897,48 +1789,13 @@ _validate_requirements_bootmode_uefi-ia32.grub.eltorito() { } _validate_requirements_bootmode_uefi-x64.grub.esp() { + _validate_common_requirements_bootmode_uefi + _validate_common_requirements_bootmode_grub # shellcheck disable=SC2076 if [[ " ${bootmodes[*]} " =~ ' uefi-x64.systemd-boot.esp ' ]]; then _msg_error "Validating '${bootmode}': cannot be used with bootmode uefi-x64.systemd-boot.esp!" 0 fi - # Check if GRUB is available - if ! command -v grub-mkstandalone &> /dev/null; then - (( validation_error=validation_error+1 )) - _msg_error "Validating '${bootmode}': grub-install is not available on this host. Install 'grub'!" 0 - fi - - # Check if mkfs.fat is available - if ! command -v mkfs.fat &> /dev/null; then - (( validation_error=validation_error+1 )) - _msg_error "Validating '${bootmode}': mkfs.fat is not available on this host. Install 'dosfstools'!" 0 - fi - - # Check if mmd and mcopy are available - if ! { command -v mmd &> /dev/null && command -v mcopy &> /dev/null; }; then - _msg_error "Validating '${bootmode}': mmd and/or mcopy are not available on this host. Install 'mtools'!" 0 - fi - - # Check if GRUB configuration files exist - if [[ ! -d "${profile}/grub" ]]; then - (( validation_error=validation_error+1 )) - _msg_error "Validating '${bootmode}': The '${profile}/grub' directory is missing!" 0 - else - if [[ ! -e "${profile}/grub/grub.cfg" ]]; then - (( validation_error=validation_error+1 )) - _msg_error "Validating '${bootmode}': File '${profile}/grub/grub.cfg' not found!" 0 - fi - local conffile - for conffile in "${profile}/grub/"*'.cfg'; do - if [[ -e "${conffile}" ]]; then - break - else - (( validation_error=validation_error+1 )) - _msg_error "Validating '${bootmode}': No configuration file found in '${profile}/grub/'!" 0 - fi - done - fi - # Check for optional packages # shellcheck disable=SC2076 if [[ ! " ${pkg_list[*]} " =~ ' edk2-shell ' ]]; then @@ -955,12 +1812,90 @@ _validate_requirements_bootmode_uefi-x64.grub.eltorito() { _validate_requirements_bootmode_uefi-x64.grub.esp } +_get_airootfs_path(){ + echo "${install_dir}/${arch}/airootfs.$(_get_fs_ext "airootfs")" +} + +_get_airootfs_img(){ + echo "${airootfs_img_dir}/airootfs.$(_get_fs_ext "airootfs")" +} + # Build airootfs filesystem image _prepare_airootfs_image() { + local _sig_dir airootfs_size pacstrap_size + + pacstrap_size=$(du -s "${pacstrap_dir}" | cut -f 1) + airootfs_size=$((pacstrap_size+200000)) + + _run_once "_mkairootfs_common" _run_once "_mkairootfs_${airootfs_image_type}" - _mkchecksum + _mkchecksum "$(_get_airootfs_img)" if [[ -n "${gpg_key}" ]]; then - _mksignature + _mksignature "$(_get_airootfs_img)" + if [[ " ${buildmodes[*]} " =~ ' dongle ' ]]; then + dongle_isofs_dir="${work_dir}/dongle_iso" + dongle_persistent_dir="${work_dir}/dongle_persistent" + _sig_dir="${dongle_isofs_dir}/${install_dir}/${arch}" + install -d -m711 "${_sig_dir}" + cp "$(_get_airootfs_img).sig" "${_sig_dir}" + fi + fi +} + +# Build keys filesystem image +_prepare_keys_image() { + keys_size=25000 + _run_once "_mkkeys_common" + _run_once "_mkkeys_${keys_image_type}" + + _mkchecksum "${dongle_isofs_dir}/keys.$(_get_fs_ext "keys")" + if [[ -n "${gpg_key}" ]]; then + _mksignature "${dongle_isofs_dir}/keys.$(_get_fs_ext "keys")" + fi +} + +# Build persistent filesystem image +_prepare_persistent_image() { + local _msg persistent_dir_size persistent_epoch persistent_uuid + local crypttab="${profile}/airootfs/etc/crypttab" + persistent_epoch=$((SOURCE_DATE_EPOCH + 3 * 1415)) + persistent_uuid="$(uuidgen --sha1 \ + --namespace 93a870ff-8565-4cf3-a67b-f47299271a96 \ + --name "${persistent_epoch}")" + if [ -d "${profile}/persistent" ]; then + cp -af --no-preserve=ownership,mode -- "${profile}/persistent/." "${persistent_dir}" + persistent_dir_size=$(du -s "${persistent_dir}" | cut -f 1) + persistent_size_kib=$(( persistent_dir_size > persistent_size_kib ? persistent_dir_size : persistent_size_kib )) + fi + + if [ "${persistent_size_kib}" != "" ]; then + if [[ "${persistent_image_type}" == *"luks" ]]; then + sed "s|%PERSISTENT_UUID%|${persistent_uuid}|g" "${crypttab}" > "${pacstrap_dir}/etc/crypttab" + if [[ "${airootfs_image_type}" == *"luks" ]] || [[ " ${buildmodes[*]} " =~ ' dongle ' ]]; then + persistent_encryption_key="${work_dir}/persistent.key" + _gen_key "${persistent_encryption_key}" + install -d -m700 "${pacstrap_dir}/etc/keys" + install -D -m400 "${persistent_encryption_key}" "${pacstrap_dir}/etc/keys" + elif [[ "${airootfs_image_type}" != *"luks" ]] && [[ " ${buildmodes[*]} " =~ ' dongle ' ]]; then + persistent_encryption_key="${work_dir}/persistent.key" + _gen_key "${persistent_encryption_key}" + install -d -m700 "${dongle_persistent_dir}/etc/keys" + install -D -m400 "${persistent_encryption_key}" "${dongle_persistent_dir}/etc/keys" + else + _msg=("Rootfs encryption and dongle buildmode disabled." + "You will be prompted to insert a password for the boot partition.") + _msg_warning "${_msg[*]}" + persistent_encryption_key="" + fi + else + sed '/%PERSISTENT_UUID%/d' "${crypttab}" > "${pacstrap_dir}/etc/crypttab" + fi + _run_once _make_boot_on_dongle + _run_once "_mkpersistent_common" + _run_once "_mkpersistent_${persistent_image_type}" + if [[ " ${buildmodes[*]} " =~ ' dongle ' ]]; then + _run_once "_mkdonglepersistent_${persistent_image_type}" + fi fi } @@ -978,13 +1913,13 @@ _sign_netboot_artifacts() { local _file _dir local _files_to_sign=() _msg_info "Signing netboot artifacts..." - _dir="${isofs_dir}/${install_dir}/boot/" + _dir="${isofs_dir}/${install_dir}/boot" for _file in "${ucodes[@]}"; do - if [[ -e "${_dir}${_file}" ]]; then - _files_to_sign+=("${_dir}${_file}") + if [[ -e "${_dir}/${_file}" ]]; then + _files_to_sign+=("${_dir}/${_file}") fi done - for _file in "${_files_to_sign[@]}" "${_dir}${arch}/vmlinuz-"* "${_dir}${arch}/initramfs-"*.img; do + for _file in "${_files_to_sign[@]}" "${_dir}/${arch}/vmlinuz-"* "${_dir}/${arch}/initramfs-"*.img; do openssl cms \ -sign \ -binary \ @@ -994,8 +1929,8 @@ _sign_netboot_artifacts() { -inkey "${cert_list[1]}" \ -outform DER \ -out "${_file}".ipxe.sig - done - _msg_info "Done!" + done + _msg_info "Done!" } _validate_requirements_airootfs_image_type_squashfs() { @@ -1005,12 +1940,63 @@ _validate_requirements_airootfs_image_type_squashfs() { fi } -_validate_requirements_airootfs_image_type_ext4+squashfs() { +_validate_requirements_airootfs_image_type_ext4() { if ! { command -v mkfs.ext4 &> /dev/null && command -v tune2fs &> /dev/null; }; then (( validation_error=validation_error+1 )) _msg_error "Validating '${airootfs_image_type}': mkfs.ext4 and/or tune2fs is not available on this host. Install 'e2fsprogs'!" 0 fi +} + +_validate_requirements_airootfs_image_type_ext4+squashfs() { + _validate_requirements_airootfs_image_type_ext4 + _validate_requirements_airootfs_image_type_squashfs +} + +_validate_requirements_persistent_image_type_ext4() { + local _msg + _validate_requirements_airootfs_image_type_ext4 + if [[ ! " ${buildmodes[*]} " =~ ' dongle ' ]]; then + _msg=("Persistent image not encrypted." + "Be sure not to put sensitive data on it and write the resulting image on a write-once device.") + _msg_warning "${_msg[*]}" + fi +} + +_validate_requirements_persistent_image_type_ext4+luks() { + _validate_requirements_airootfs_image_type_luks + _validate_requirements_persistent_image_type_ext4 + if [ ! -e "${profile}/airootfs/etc/crypttab" ]; then + (( validation_error=validation_error+1 )) + _msg_error "Validating '${persistent_image_type}' for persistent partition: crypttab not found!" 0 + fi +} + +_gen_key() { + local _out="${1}" + dd if=/dev/random bs=512 count=1 of="${_out}" > /dev/null 2>&1 +} + +_resolve_encryption_key(){ + if [ ! -f "${encryption_key}" ]; then + if [ "${encryption_key}" = 'auto' ]; then + encryption_key="${work_dir}/${iso_label}.key" + [ ! -d "${work_dir}" ] && install -d "${work_dir}" + _gen_key "${encryption_key}" + elif [ "${encryption_key}" != 'auto' ]; then + _msg_error "File '${encryption_key}' does not exist." 0 + fi + fi +} + +_validate_requirements_airootfs_image_type_squashfs+luks() { + _validate_requirements_airootfs_image_type_luks + _validate_requirements_airootfs_image_type_squashfs +} + +_validate_requirements_airootfs_image_type_ext4+squashfs+luks() { + _validate_requirements_airootfs_image_type_luks _validate_requirements_airootfs_image_type_squashfs + _validate_requirements_airootfs_image_type_ext4 } _validate_requirements_airootfs_image_type_erofs() { @@ -1020,7 +2006,21 @@ _validate_requirements_airootfs_image_type_erofs() { fi } +_validate_requirements_airootfs_image_type_luks() { + if ! command -v cryptsetup &> /dev/null && command -v mkfs.erofs &> /dev/null; then + (( validation_error=validation_error+1 )) + _msg_error "Validating '${airootfs_image_type}': cryptsetup is not available on this host. Install 'cryptsetup'!" 0 + fi + [[ -n "$encryption_key" ]] && _resolve_encryption_key +} + +_validate_requirements_airootfs_image_type_erofs+luks() { + _validate_requirements_airootfs_image_type_luks + _validate_requirements_airootfs_image_type_erofs +} + _validate_common_requirements_buildmode_all() { + local _msg=() if ! command -v pacman &> /dev/null; then (( validation_error=validation_error+1 )) _msg_error "Validating build mode '${_buildmode}': pacman is not available on this host. Install 'pacman'!" 0 @@ -1033,6 +2033,12 @@ _validate_common_requirements_buildmode_all() { (( validation_error=validation_error+1 )) _msg_error "Validating build mode '${_buildmode}': gzip is not available on this host. Install 'gzip'!" 0 fi + if [[ ! " ${buildmodes[*]} " =~ ' dongle ' ]] && [[ "${persistent_image_type}" != *"luks" ]]; then + _msg=("Validating build mode '${_buildmode}':" + "'dongle' build mode not selected; persistent partition encryption disabled." + "Be sure to use the resulting image on a write-once device to not be vulnerable to 'evil maids'.") + _msg_warning "${_msg[*]}" + fi } _validate_requirements_buildmode_bootstrap() { @@ -1062,6 +2068,7 @@ _validate_requirements_buildmode_bootstrap() { _validate_common_requirements_buildmode_iso_netboot() { local bootmode local pkg_list_from_file=() + local _msg=() # Check if the package list file exists and read packages from it if [[ -e "${packages}" ]]; then @@ -1081,7 +2088,9 @@ _validate_common_requirements_buildmode_iso_netboot() { if typeset -f "_validate_requirements_airootfs_image_type_${airootfs_image_type}" &> /dev/null; then "_validate_requirements_airootfs_image_type_${airootfs_image_type}" else - _msg_warning "Function '_validate_requirements_airootfs_image_type_${airootfs_image_type}' does not exist. Validating the requirements of '${airootfs_image_type}' airootfs image type will not be possible." + _msg=("Function '_validate_requirements_airootfs_image_type_${airootfs_image_type}' does not exist." + "Validating the requirements of '${airootfs_image_type}' airootfs image type will not be possible.") + _msg_warning "${_msg[*]}" fi else (( validation_error=validation_error+1 )) @@ -1089,7 +2098,16 @@ _validate_common_requirements_buildmode_iso_netboot() { fi } +_validate_requirements_buildmode_dongle() { + _validate_common_requirements_buildmode_all + [[ -n "$encryption_key" ]] && _resolve_encryption_key + if [[ ! "${airootfs_image_type}" == *luks ]]; then + _msg_warning "Dongle buildmode active but root file system encryption disabled." + fi +} + _validate_requirements_buildmode_iso() { + local _msg=() _validate_common_requirements_buildmode_iso_netboot _validate_common_requirements_buildmode_all # Check if the specified bootmodes are supported @@ -1102,7 +2120,9 @@ _validate_requirements_buildmode_iso() { if typeset -f "_validate_requirements_bootmode_${bootmode}" &> /dev/null; then "_validate_requirements_bootmode_${bootmode}" else - _msg_warning "Function '_validate_requirements_bootmode_${bootmode}' does not exist. Validating the requirements of '${bootmode}' boot mode will not be possible." + _msg=("Function '_validate_requirements_bootmode_${bootmode}' does not exist." + "Validating the requirements of '${bootmode}' boot mode will not be possible.") + _msg_warning "${_msg[*]}" fi else (( validation_error=validation_error+1 )) @@ -1114,6 +2134,20 @@ _validate_requirements_buildmode_iso() { (( validation_error=validation_error+1 )) _msg_error "Validating build mode '${_buildmode}': awk is not available on this host. Install 'awk'!" 0 fi + + # Check if the profile supports the specified persistent_image_type + if typeset -f "_mkpersistent_${persistent_image_type}" &> /dev/null; then + if typeset -f "_validate_requirements_persistent_image_type_${persistent_image_type}" &> /dev/null; then + "_validate_requirements_persistent_image_type_${persistent_image_type}" + else + _msg=("Function '_validate_requirements_persistent_image_type_${persistent_image_type}' does not exist." + "Validating the requirements of '${persistent_image_type}' airootfs image type will not be possible.") + _msg_warning "${_msg[*]}" + fi + else + (( validation_error=validation_error+1 )) + _msg_error "Unsupported image type for persistent partition: '${persistent_image_type}'" 0 + fi } _validate_requirements_buildmode_netboot() { @@ -1144,23 +2178,79 @@ _validate_requirements_buildmode_netboot() { fi } -# SYSLINUX El Torito -_add_xorrisofs_options_bios.syslinux.eltorito() { - xorrisofs_options+=( - # El Torito boot image for x86 BIOS - '-eltorito-boot' 'syslinux/isolinux.bin' - # El Torito boot catalog file - '-eltorito-catalog' 'syslinux/boot.cat' - # Required options to boot with ISOLINUX - '-no-emul-boot' '-boot-load-size' '4' '-boot-info-table' +_add_xorrisofs_options_persistent_partition() { + local _img _xorrisofs_options=("$@") + if [[ "${persistent_image_type}" == *"luks" ]]; then + persistent_guid="CA7D7CCB-63ED-4C53-861C-1742536059CC" + else + persistent_guid="0FC63DAF-8483-4772-8E79-3D69D8477DE4" + fi + if [[ "${buildmode}" == "dongle" ]]; then + _img="${donglepersistentimg}" + else + _img="${persistentimg}" + fi + _xorrisofs_options+=('-append_partition' '3' "${persistent_guid}" "${_img}") + echo "${_xorrisofs_options[@]}" +} + +# GRUB (isohybrid) +_add_xorrisofs_options_bios.grub.mbr() { + local bootable_dir="${1}" + shift + local _xorrisofs_options=("$@") + _xorrisofs_options+=( + # SYSLINUX MBR bootstrap code; does not work without "-eltorito-boot syslinux/isolinux.bin" + '--grub2-mbr' "${bootable_dir}/EFI/BOOT/boot_hybrid.img" + # When GPT is used, create an additional partition in the MBR (besides 0xEE) for sectors 0–1 (MBR + # bootstrap code area) and mark it as bootable + # May allow booting on some systems + # https://wiki.archlinux.org/title/Partitioning#Tricking_old_BIOS_into_booting_from_GPT + '--mbr-force-bootable' + # Move the first partition away from the start of the ISO to match the expectations of partition editors + # May allow booting on some systems + # https://dev.lovelyhq.com/libburnia/libisoburn/src/branch/master/doc/partition_offset.wiki + '-partition_offset' '16' ) + echo "${_xorrisofs_options[@]}" +} + +_add_xorrisofs_options_bios.grub.mbr_iso() { + xorrisofs_options=("$(_add_xorrisofs_options_bios.grub.mbr "${isofs_dir}" "${xorrisofs_options[@]}")") +} + +_add_xorrisofs_options_bios.grub.mbr_dongle() { + dongle_xorrisofs_options=("$(_add_xorrisofs_options_bios.grub.mbr "${dongle_isofs_dir}" "${dongle_xorrisofs_options[@]}")") +} + +# GRUB El Torito +_add_xorrisofs_options_bios.grub.eltorito() { + local _xorrisofs_options=("$@") + _xorrisofs_options+=(# El Torito boot image for x86 BIOS + '-eltorito-boot' 'EFI/BOOT/BOOTIA32.ELTORITO' + # El Torito boot catalog file + '-eltorito-catalog' 'EFI/BOOT/boot.catalog' + # Required options to boot with GRUB + '-no-emul-boot' '-boot-load-size' '4' '-boot-info-table' '--grub2-boot-info') + echo "${_xorrisofs_options[@]}" +} + +_add_xorrisofs_options_bios.grub.eltorito_iso() { + xorrisofs_options=("$(_add_xorrisofs_options_bios.grub.eltorito "${xorrisofs_options[@]}")") +} + +_add_xorrisofs_options_bios.grub.eltorito_dongle() { + dongle_xorrisofs_options=("$(_add_xorrisofs_options_bios.grub.eltorito "${dongle_xorrisofs_options[@]}")") } # SYSLINUX MBR (isohybrid) _add_xorrisofs_options_bios.syslinux.mbr() { - xorrisofs_options+=( + local bootable_dir="${1}" + shift + local _xorrisofs_options=("$@") + _xorrisofs_options+=( # SYSLINUX MBR bootstrap code; does not work without "-eltorito-boot syslinux/isolinux.bin" - '-isohybrid-mbr' "${isofs_dir}/syslinux/isohdpfx.bin" + '-isohybrid-mbr' "${bootable_dir}/syslinux/isohdpfx.bin" # When GPT is used, create an additional partition in the MBR (besides 0xEE) for sectors 0–1 (MBR # bootstrap code area) and mark it as bootable # May allow booting on some systems @@ -1171,38 +2261,91 @@ _add_xorrisofs_options_bios.syslinux.mbr() { # https://dev.lovelyhq.com/libburnia/libisoburn/src/branch/master/doc/partition_offset.wiki '-partition_offset' '16' ) + echo "${_xorrisofs_options[@]}" +} + +_add_xorrisofs_options_bios.syslinux.mbr_iso() { + xorrisofs_options=("$(_add_xorrisofs_options_bios.syslinux.mbr "${isofs_dir}" "${xorrisofs_options[@]}")") +} + +_add_xorrisofs_options_bios.syslinux.mbr_dongle() { + dongle_xorrisofs_options=("$(_add_xorrisofs_options_bios.syslinux.mbr "${dongle_isofs_dir}" "${dongle_xorrisofs_options[@]}")") +} + +# SYSLINUX El Torito +_add_xorrisofs_options_bios.syslinux.eltorito() { + local _xorrisofs_options=("$@") + _xorrisofs_options+=(# El Torito boot image for x86 BIOS + '-eltorito-boot' 'syslinux/isolinux.bin' + # El Torito boot catalog file + '-eltorito-catalog' 'syslinux/boot.cat' + # Required options to boot with ISOLINUX + '-no-emul-boot' '-boot-load-size' '4' '-boot-info-table') + echo "${_xorrisofs_options[@]}" +} + +_add_xorrisofs_options_bios.syslinux.eltorito_iso() { + xorrisofs_options=("$(_add_xorrisofs_options_bios.syslinux.eltorito "${xorrisofs_options[@]}")") +} + +_add_xorrisofs_options_bios.syslinux.eltorito_dongle() { + dongle_xorrisofs_options=("$(_add_xorrisofs_options_bios.syslinux.eltorito "${dongle_xorrisofs_options[@]}")") } # GRUB in an attached EFI system partition _add_xorrisofs_options_uefi-ia32.grub.esp() { + local _xorrisofs_options=("$@") # TODO: how does the bootmodes systemd-boot vs x64.grub affect ${bootmodes[*]} tests in _add_xorrisofs_options_uefi-x64.systemd-boot.esp etc? # shellcheck disable=SC2076 if [[ ! " ${bootmodes[*]} " =~ ' uefi-x64.systemd-boot.esp ' && ! " ${bootmodes[*]} " =~ ' uefi-x64.grub.esp ' ]]; then # _add_xorrisofs_options_uefi-x64.systemd-boot.esp - _add_xorrisofs_options_uefi-x64.grub.esp + _xorrisofs_options=("$(_add_xorrisofs_options_uefi-x64.grub.esp "${_xorrisofs_options[@]}")") fi + echo "${_xorrisofs_options[@]}" +} + +_add_xorrisofs_options_uefi-ia32.grub.esp_iso() { + xorrisofs_options=("$(_add_xorrisofs_options_uefi-ia32.grub.esp "${xorrisofs_options[@]}")") +} + +_add_xorrisofs_options_uefi-ia32.grub.esp_dongle() { + dongle_xorrisofs_options=("$(_add_xorrisofs_options_uefi-ia32.grub.esp "${dongle_xorrisofs_options[@]}")") } # GRUB via El Torito _add_xorrisofs_options_uefi-ia32.grub.eltorito() { + local _xorrisofs_options=("$@") # shellcheck disable=SC2076 if [[ ! " ${bootmodes[*]} " =~ ' uefi-x64.systemd-boot.eltorito ' && ! " ${bootmodes[*]} " =~ ' uefi-x64.grub.eltorito ' ]]; then # _add_xorrisofs_options_uefi-x64.systemd-boot.eltorito - _add_xorrisofs_options_uefi-x64.grub.eltorito + _xorrisofs_options=("$(_add_xorrisofs_options_uefi-x64.grub.eltorito "${_xorrisofs_options[@]}")") fi + echo "${_xorrisofs_options[@]}" +} + +_add_xorrisofs_options_uefi-ia32.grub.eltorito_iso() { + xorrisofs_options=("$(_add_xorrisofs_options_uefi-ia32.grub.eltorito "${xorrisofs_options[@]}")") +} + +_add_xorrisofs_options_uefi-ia32.grub.eltorito_dongle() { + dongle_xorrisofs_options=("$(_add_xorrisofs_options_uefi-ia32.grub.eltorito "${dongle_xorrisofs_options[@]}")") } # systemd-boot in an attached EFI system partition _add_xorrisofs_options_uefi-x64.systemd-boot.esp() { + local _xorrisofs_options=("$@") # Move the first partition away from the start of the ISO, otherwise the GPT will not be valid and ISO 9660 # partition will not be mountable # shellcheck disable=SC2076 - [[ " ${xorrisofs_options[*]} " =~ ' -partition_offset ' ]] || xorrisofs_options+=('-partition_offset' '16') + [[ " ${_xorrisofs_options[*]} " =~ ' -partition_offset ' ]] || _xorrisofs_options+=('-partition_offset' '16') # Attach efiboot.img as a second partition and set its partition type to "EFI system partition" - xorrisofs_options+=('-append_partition' '2' 'C12A7328-F81F-11D2-BA4B-00A0C93EC93B' "${work_dir}/efiboot.img") + _xorrisofs_options+=('-append_partition' '2' 'C12A7328-F81F-11D2-BA4B-00A0C93EC93B' "${efibootimg}") + if [ "${persistent_size_kib}" != "" ] || [[ " ${buildmodes[*]} " =~ ' dongle ' ]]; then + _xorrisofs_options=("$(_add_xorrisofs_options_persistent_partition "${_xorrisofs_options[@]}")") + fi # Ensure GPT is used as some systems do not support UEFI booting without it # shellcheck disable=SC2076 - if [[ " ${bootmodes[*]} " =~ ' bios.syslinux.mbr ' ]]; then + if [[ " ${bootmodes[*]} " =~ ' bios.syslinux.mbr ' ]] || [[ " ${bootmodes[*]} " =~ ' bios.grub.mbr ' ]]; then # A valid GPT prevents BIOS booting on some systems, instead use an invalid GPT (without a protective MBR). # The attached partition will have the EFI system partition type code in MBR, but in the invalid GPT it will # have a Microsoft basic partition type code. @@ -1210,70 +2353,94 @@ _add_xorrisofs_options_uefi-x64.systemd-boot.esp() { # If '-isohybrid-gpt-basdat' is specified before '-e', then the appended EFI system partition will have the # EFI system partition type ID/GUID in both MBR and GPT. If '-isohybrid-gpt-basdat' is specified after '-e', # the appended EFI system partition will have the Microsoft basic data type GUID in GPT. - if [[ ! " ${xorrisofs_options[*]} " =~ ' -isohybrid-gpt-basdat ' ]]; then - xorrisofs_options+=('-isohybrid-gpt-basdat') + if [[ ! " ${_xorrisofs_options[*]} " =~ ' -isohybrid-gpt-basdat ' ]]; then + _xorrisofs_options+=('-isohybrid-gpt-basdat') fi fi else # Use valid GPT if BIOS booting support will not be required - xorrisofs_options+=('-appended_part_as_gpt') + _xorrisofs_options+=('-appended_part_as_gpt') fi + echo "${_xorrisofs_options[@]}" +} + +_add_xorrisofs_options_uefi-x64.systemd-boot.esp_iso() { + xorrisofs_options=("$(_add_xorrisofs_options_uefi-x64.systemd-boot.esp "${xorrisofs_options[@]}")") +} + +_add_xorrisofs_options_uefi-x64.systemd-boot.esp_dongle() { + dongle_xorrisofs_options=("$(_add_xorrisofs_options_uefi-x64.systemd-boot.esp "${dongle_xorrisofs_options[@]}")") } # systemd-boot via El Torito _add_xorrisofs_options_uefi-x64.systemd-boot.eltorito() { + local bootable_dir="${1}" + shift + local _xorrisofs_options=("$@") # shellcheck disable=SC2076 if [[ " ${bootmodes[*]} " =~ ' uefi-x64.systemd-boot.esp ' || " ${bootmodes[*]} " =~ ' uefi-ia32.grub.esp ' ]]; then # systemd-boot in an attached EFI system partition via El Torito - xorrisofs_options+=( - # Start a new El Torito boot entry for UEFI - '-eltorito-alt-boot' - # Set the second partition as the El Torito UEFI boot image - '-e' '--interval:appended_partition_2:all::' - # Boot image is not emulating floppy or hard disk; required for all known boot loaders - '-no-emul-boot' - ) - # A valid GPT prevents BIOS booting on some systems, use an invalid GPT instead. - if [[ " ${bootmodes[*]} " =~ ' bios.syslinux.mbr ' ]]; then - # If '-isohybrid-gpt-basdat' is specified before '-e', then the appended EFI system partition will have the - # EFI system partition type ID/GUID in both MBR and GPT. If '-isohybrid-gpt-basdat' is specified after '-e', - # the appended EFI system partition will have the Microsoft basic data type GUID in GPT. - if [[ ! " ${xorrisofs_options[*]} " =~ ' -isohybrid-gpt-basdat ' ]]; then - xorrisofs_options+=('-isohybrid-gpt-basdat') - fi + _xorrisofs_options+=( + # Start a new El Torito boot entry for UEFI + '-eltorito-alt-boot' + # Set the second partition as the El Torito UEFI boot image + '-e' '--interval:appended_partition_2:all::' + # Boot image is not emulating floppy or hard disk; required for all known boot loaders + '-no-emul-boot' + ) + # A valid GPT prevents BIOS booting on some systems, use an invalid GPT instead. + if [[ " ${bootmodes[*]} " =~ ' bios.syslinux.mbr ' ]] || [[ " ${bootmodes[*]} " =~ ' bios.grub.mbr ' ]]; then + # If '-isohybrid-gpt-basdat' is specified before '-e', then the appended EFI system partition will have the + # EFI system partition type ID/GUID in both MBR and GPT. If '-isohybrid-gpt-basdat' is specified after '-e', + # the appended EFI system partition will have the Microsoft basic data type GUID in GPT. + if [[ ! " ${_xorrisofs_options[*]} " =~ ' -isohybrid-gpt-basdat ' ]]; then + _xorrisofs_options+=('-isohybrid-gpt-basdat') fi + fi else # The ISO will not contain a GPT partition table, so to be able to reference efiboot.img, place it as a # file inside the ISO 9660 file system - install -d -m 0755 -- "${isofs_dir}/EFI/archiso" - cp -a -- "${work_dir}/efiboot.img" "${isofs_dir}/EFI/archiso/efiboot.img" + install -d -m 0755 -- "${bootable_dir}/EFI/archiso" + cp -a -- "${efibootimg}" "${bootable_dir}/EFI/archiso/efiboot.img" # systemd-boot in an embedded efiboot.img via El Torito - xorrisofs_options+=( + _xorrisofs_options+=( # Start a new El Torito boot entry for UEFI '-eltorito-alt-boot' # Set efiboot.img as the El Torito UEFI boot image '-e' 'EFI/archiso/efiboot.img' # Boot image is not emulating floppy or hard disk; required for all known boot loaders - '-no-emul-boot' - ) + '-no-emul-boot') fi # Specify where to save the El Torito boot catalog file in case it is not already set by bios.syslinux.eltorito # shellcheck disable=SC2076 - [[ " ${bootmodes[*]} " =~ ' bios.' ]] || xorrisofs_options+=('-eltorito-catalog' 'EFI/boot.cat') + [[ " ${bootmodes[*]} " =~ ' bios.' ]] || _xorrisofs_options+=('-eltorito-catalog' 'EFI/boot.cat') + echo "${_xorrisofs_options[@]}" +} + +_add_xorrisofs_options_uefi-x64.systemd-boot.eltorito_iso() { + xorrisofs_options=("$(_add_xorrisofs_options_uefi-x64.systemd-boot.eltorito "${isofs_dir}" "${xorrisofs_options[@]}")") +} + +_add_xorrisofs_options_uefi-x64.systemd-boot.eltorito_dongle() { + dongle_xorrisofs_options=("$(_add_xorrisofs_options_uefi-x64.systemd-boot.eltorito "${dongle_isofs_dir}" "${dongle_xorrisofs_options[@]}")") } # GRUB in an attached EFI system partition. # Same as _add_xorrisofs_options_uefi-x64.systemd-boot.esp. _add_xorrisofs_options_uefi-x64.grub.esp() { + local _xorrisofs_options=("$@") # Move the first partition away from the start of the ISO, otherwise the GPT will not be valid and ISO 9660 # partition will not be mountable # shellcheck disable=SC2076 - [[ " ${xorrisofs_options[*]} " =~ ' -partition_offset ' ]] || xorrisofs_options+=('-partition_offset' '16') + [[ " ${_xorrisofs_options[*]} " =~ ' -partition_offset ' ]] || _xorrisofs_options+=('-partition_offset' '16') # Attach efiboot.img as a second partition and set its partition type to "EFI system partition" - xorrisofs_options+=('-append_partition' '2' 'C12A7328-F81F-11D2-BA4B-00A0C93EC93B' "${work_dir}/efiboot.img") + _xorrisofs_options+=('-append_partition' '2' 'C12A7328-F81F-11D2-BA4B-00A0C93EC93B' "${efibootimg}") + if [ "${persistent_size_kib}" != "" ] || [[ " ${buildmodes[*]} " =~ ' dongle ' ]]; then + _xorrisofs_options=("$(_add_xorrisofs_options_persistent_partition "${_xorrisofs_options[@]}")") + fi # Ensure GPT is used as some systems do not support UEFI booting without it # shellcheck disable=SC2076 - if [[ " ${bootmodes[*]} " =~ ' bios.syslinux.mbr ' ]]; then + if [[ " ${bootmodes[*]} " =~ ' bios.syslinux.mbr ' ]] || [[ " ${bootmodes[*]} " =~ ' bios.grub.mbr ' ]]; then # A valid GPT prevents BIOS booting on some systems, instead use an invalid GPT (without a protective MBR). # The attached partition will have the EFI system partition type code in MBR, but in the invalid GPT it will # have a Microsoft basic partition type code. @@ -1281,57 +2448,78 @@ _add_xorrisofs_options_uefi-x64.grub.esp() { # If '-isohybrid-gpt-basdat' is specified before '-e', then the appended EFI system partition will have the # EFI system partition type ID/GUID in both MBR and GPT. If '-isohybrid-gpt-basdat' is specified after '-e', # the appended EFI system partition will have the Microsoft basic data type GUID in GPT. - if [[ ! " ${xorrisofs_options[*]} " =~ ' -isohybrid-gpt-basdat ' ]]; then - xorrisofs_options+=('-isohybrid-gpt-basdat') + if [[ ! " ${_xorrisofs_options[*]} " =~ ' -isohybrid-gpt-basdat ' ]]; then + _xorrisofs_options+=('-isohybrid-gpt-basdat') fi fi else # Use valid GPT if BIOS booting support will not be required - xorrisofs_options+=('-appended_part_as_gpt') + _xorrisofs_options+=('-appended_part_as_gpt') fi + echo "${_xorrisofs_options[@]}" +} + +_add_xorrisofs_options_uefi-x64.grub.esp_iso() { + xorrisofs_options=("$(_add_xorrisofs_options_uefi-x64.grub.esp "${xorrisofs_options[@]}")") +} + +_add_xorrisofs_options_uefi-x64.grub.esp_dongle() { + dongle_xorrisofs_options=("$(_add_xorrisofs_options_uefi-x64.grub.esp "${dongle_xorrisofs_options[@]}")") } # GRUB via El Torito # Same as _add_xorrisofs_options_uefi-x64.systemd-boot.eltorito. _add_xorrisofs_options_uefi-x64.grub.eltorito() { + local bootable_dir="${1}" + shift + local _xorrisofs_options=("$@") # shellcheck disable=SC2076 if [[ " ${bootmodes[*]} " =~ ' uefi-x64.grub.esp ' || " ${bootmodes[*]} " =~ ' uefi-ia32.grub.esp ' ]]; then # grub in an attached EFI system partition via El Torito - xorrisofs_options+=( - # Start a new El Torito boot entry for UEFI - '-eltorito-alt-boot' - # Set the second partition as the El Torito UEFI boot image - '-e' '--interval:appended_partition_2:all::' - # Boot image is not emulating floppy or hard disk; required for all known boot loaders - '-no-emul-boot' + _xorrisofs_options+=( + # Start a new El Torito boot entry for UEFI + '-eltorito-alt-boot' + # Set the second partition as the El Torito UEFI boot image + '-e' '--interval:appended_partition_2:all::' + # Boot image is not emulating floppy or hard disk; required for all known boot loaders + '-no-emul-boot' ) - # A valid GPT prevents BIOS booting on some systems, use an invalid GPT instead. - if [[ " ${bootmodes[*]} " =~ ' bios.syslinux.mbr ' ]]; then - # If '-isohybrid-gpt-basdat' is specified before '-e', then the appended EFI system partition will have the - # EFI system partition type ID/GUID in both MBR and GPT. If '-isohybrid-gpt-basdat' is specified after '-e', - # the appended EFI system partition will have the Microsoft basic data type GUID in GPT. - if [[ ! " ${xorrisofs_options[*]} " =~ ' -isohybrid-gpt-basdat ' ]]; then - xorrisofs_options+=('-isohybrid-gpt-basdat') - fi + # A valid GPT prevents BIOS booting on some systems, use an invalid GPT instead. + if [[ " ${bootmodes[*]} " =~ ' bios.syslinux.mbr ' ]] || [[ " ${bootmodes[*]} " =~ ' bios.grub.mbr ' ]]; then + # If '-isohybrid-gpt-basdat' is specified before '-e', then the appended EFI system partition will have the + # EFI system partition type ID/GUID in both MBR and GPT. If '-isohybrid-gpt-basdat' is specified after '-e', + # the appended EFI system partition will have the Microsoft basic data type GUID in GPT. + if [[ ! " ${_xorrisofs_options[*]} " =~ ' -isohybrid-gpt-basdat ' ]]; then + _xorrisofs_options+=('-isohybrid-gpt-basdat') fi + fi else # The ISO will not contain a GPT partition table, so to be able to reference efiboot.img, place it as a # file inside the ISO 9660 file system - install -d -m 0755 -- "${isofs_dir}/EFI/archiso" - cp -a -- "${work_dir}/efiboot.img" "${isofs_dir}/EFI/archiso/efiboot.img" + install -d -m 0755 -- "${bootable_dir}/EFI/archiso" + cp -a -- "${efibootimg}" "${bootable_dir}/EFI/archiso/efiboot.img" # grub in an embedded efiboot.img via El Torito - xorrisofs_options+=( - # Start a new El Torito boot entry for UEFI - '-eltorito-alt-boot' - # Set efiboot.img as the El Torito UEFI boot image - '-e' 'EFI/archiso/efiboot.img' - # Boot image is not emulating floppy or hard disk; required for all known boot loaders - '-no-emul-boot' + _xorrisofs_options+=( + # Start a new El Torito boot entry for UEFI + '-eltorito-alt-boot' + # Set efiboot.img as the El Torito UEFI boot image + '-e' 'EFI/archiso/efiboot.img' + # Boot image is not emulating floppy or hard disk; required for all known boot loaders + '-no-emul-boot' ) fi # Specify where to save the El Torito boot catalog file in case it is not already set by bios.syslinux.eltorito # shellcheck disable=SC2076 - [[ " ${bootmodes[*]} " =~ ' bios.' ]] || xorrisofs_options+=('-eltorito-catalog' 'EFI/boot.cat') + [[ " ${bootmodes[*]} " =~ ' bios.' ]] || _xorrisofs_options+=('-eltorito-catalog' 'EFI/boot.cat') + echo "${_xorrisofs_options[@]}" +} + +_add_xorrisofs_options_uefi-x64.grub.eltorito_iso() { + xorrisofs_options=("$(_add_xorrisofs_options_uefi-x64.grub.eltorito "${isofs_dir}" "${xorrisofs_options[@]}")") +} + +_add_xorrisofs_options_uefi-x64.grub.eltorito_dongle() { + dongle_xorrisofs_options=("$(_add_xorrisofs_options_uefi-x64.grub.eltorito "${dongle_isofs_dir}" "${dongle_xorrisofs_options[@]}")") } # Build bootstrap image @@ -1350,9 +2538,57 @@ _build_bootstrap_image() { cd -- "${OLDPWD}" } +_build_dongle_image() { + local bootmode dongle_xorriso_cmd_line dongle_xorriso_options=() dongle_xorrisofs_options=() + dongle_isofs_dir="${work_dir}/dongle_iso" + + # Add required xorrisofs options for each boot mode + for bootmode in "${bootmodes[@]}"; do + "_add_xorrisofs_options_${bootmode}_dongle" + done + + [[ -d "${out_dir}" ]] || install -d -- "${out_dir}" + + if [[ "${quiet}" == "y" ]]; then + # The when xorriso is run in mkisofs compatibility mode (xorrisofs), the mkisofs option -quiet is interpreted + # too late (e.g. messages about SOURCE_DATE_EPOCH still get shown). + # Instead use native xorriso option to silence the output. + dongle_xorriso_options+=('-report_about' 'SORRY' "${dongle_xorriso_options[@]}") + fi + + # Update SORCE_DATE_EPOCH for dongle ISO UUID + SOURCE_DATE_EPOCH=$((SOURCE_DATE_EPOCH + 69 * 42)) + + rm -f -- "${out_dir}/${image_name}" + _msg_info "Creating ISO image..." + _msg_info "xorriso options: ${dongle_xorriso_options[*]}" + _msg_info "xorrisofs options: ${dongle_xorrisofs_options[*]}" + # shellcheck disable=SC2116 + dongle_xorriso_cmd_line=( + "${dongle_xorriso_options[@]}" + '-as' 'mkisofs' + '-iso-level' 3 + '-full-iso9660-filenames' + '-joliet' + '-joliet-long' + '-rational-rock' + '-volid' "${iso_label}_DONGLE" + '-appid' "\"${iso_application} dongle CD\"" + '-publisher' "\"${iso_publisher}\"" + '-preparer' "\"prepared by ${app_name}\"" + "${dongle_xorrisofs_options[@]}" + '-output' "\"${out_dir}/${image_name}\"" + "${dongle_isofs_dir}/" + ) + eval "xorriso ${dongle_xorriso_cmd_line[*]}" + _msg_info "Done!" + du -h -- "${out_dir}/${image_name}" +} + # Build ISO _build_iso_image() { - local xorriso_options=() xorrisofs_options=() + local xorriso_cmd_line + isofs_dir="${work_dir}/iso" local bootmode [[ -d "${out_dir}" ]] || install -d -- "${out_dir}" @@ -1361,31 +2597,40 @@ _build_iso_image() { # The when xorriso is run in mkisofs compatibility mode (xorrisofs), the mkisofs option -quiet is interpreted # too late (e.g. messages about SOURCE_DATE_EPOCH still get shown). # Instead use native xorriso option to silence the output. - xorriso_options=('-report_about' 'SORRY' "${xorriso_options[@]}") + xorriso_options+=('-report_about' 'SORRY' "${xorriso_options[@]}") fi # Add required xorrisofs options for each boot mode for bootmode in "${bootmodes[@]}"; do - typeset -f "_add_xorrisofs_options_${bootmode}" &> /dev/null && "_add_xorrisofs_options_${bootmode}" + "_add_xorrisofs_options_${bootmode}_iso" done rm -f -- "${out_dir}/${image_name}" + # shellcheck disable=SC2116 + xorriso_cmd_line=( + "${xorriso_options[@]}" '-as' 'mkisofs' + '-iso-level' 3 + '-full-iso9660-filenames' + '-joliet' + '-joliet-long' + '-rational-rock' + '-volid' "${iso_label}" + '-appid' "\"${iso_application}\"" + '-publisher' "\"${iso_publisher}\"" + '-preparer' "\"prepared by ${app_name}\"" + "${xorrisofs_options[@]}" + '-output' "${out_dir}/${image_name}" + "${isofs_dir}/" + ) _msg_info "Creating ISO image..." - xorriso "${xorriso_options[@]}" -as mkisofs \ - -iso-level 3 \ - -full-iso9660-filenames \ - -joliet \ - -joliet-long \ - -rational-rock \ - -volid "${iso_label}" \ - -appid "${iso_application}" \ - -publisher "${iso_publisher}" \ - -preparer "prepared by ${app_name}" \ - "${xorrisofs_options[@]}" \ - -output "${out_dir}/${image_name}" \ - "${isofs_dir}/" + _msg_info "xorriso args: ${xorriso_cmd_line[*]}" + eval "xorriso ${xorriso_cmd_line[*]}" _msg_info "Done!" du -h -- "${out_dir}/${image_name}" + + if [[ "${airootfs_image_type}" == *"luks"* ]] && [[ ! " ${buildmodes[*]} " =~ ' dongle ' ]]; then + cp "${encryption_key}" "${out_dir}" + fi } # Read profile's values from profiledef.sh @@ -1423,7 +2668,7 @@ _read_profile() { # Validate set options _validate_options() { - local validation_error=0 _buildmode + local validation_error=0 _buildmode _msg=() _msg_info "Validating options..." @@ -1439,7 +2684,9 @@ _validate_options() { if typeset -f "_validate_requirements_buildmode_${_buildmode}" &> /dev/null; then "_validate_requirements_buildmode_${_buildmode}" else - _msg_warning "Function '_validate_requirements_buildmode_${_buildmode}' does not exist. Validating the requirements of '${_buildmode}' build mode will not be possible." + _msg=("Function '_validate_requirements_buildmode_${_buildmode}' does not exist." + "Validating the requirements of '${_buildmode}' build mode will not be possible.") + _msg_warning "${_msg[*]}" fi else (( validation_error=validation_error+1 )) @@ -1502,6 +2749,7 @@ _set_overrides() { fi [[ ! -v override_gpg_key ]] || gpg_key="$override_gpg_key" [[ ! -v override_gpg_sender ]] || gpg_sender="$override_gpg_sender" + [[ ! -v override_gpg_home ]] || gpg_home="$override_gpg_home" if [[ -v override_cert_list ]]; then sign_netboot_artifacts="y" fi @@ -1514,12 +2762,27 @@ _set_overrides() { # Set variables that do not have overrides [[ -n "$airootfs_image_type" ]] || airootfs_image_type="squashfs" + [[ -n "$keys_image_type" ]] || keys_image_type="squashfs+luks" + [[ -n "$persistent_image_type" ]] || persistent_image_type="ext4+luks" [[ -n "$iso_name" ]] || iso_name="${app_name}" + [[ -n "$isofs_dir" ]] || isofs_dir="${work_dir}/iso" + [[ -n "$airootfs_img_dir" ]] || airootfs_img_dir="${isofs_dir}/${install_dir}/${arch}" + [[ -n "$dongle_isofs_dir" ]] || dongle_isofs_dir="${work_dir}/dongle_iso" + [[ -n "$efibootimg" ]] || efibootimg="${work_dir}/efiboot.img" + [[ -n "$persistentimg" ]] || persistentimg="${work_dir}/persistent."$(_get_fs_ext "persistent") + [[ -n "$donglepersistentimg" ]] || donglepersistentimg="${work_dir}/donglepersistent."$(_get_fs_ext "persistent") } _export_gpg_publickey() { rm -f -- "${work_dir}/pubkey.gpg" - gpg --batch --no-armor --output "${work_dir}/pubkey.gpg" --export "${gpg_key}" + if [ "${gpg_home}" != "" ]; then + GNUPGHOME="${gpg_home}" gpg --homedir "${gpg_home}" \ + --batch --no-armor --output "${work_dir}/pubkey.gpg" \ + --export "${gpg_key}" + else + gpg --batch --no-armor --output "${work_dir}/pubkey.gpg" \ + --export "${gpg_key}" + fi } _make_version() { @@ -1551,6 +2814,11 @@ _make_version() { [[ ! -e "${_os_release}" ]] || sed -i '/^IMAGE_ID=/d;/^IMAGE_VERSION=/d' "${_os_release}" printf 'IMAGE_ID=%s\nIMAGE_VERSION=%s\n' "${iso_name}" "${iso_version}" >> "${_os_release}" fi + + # Touch /usr/lib/clock-epoch to give another hint on date and time + # for systems with screwed or broken RTC. + touch -m -d"@${SOURCE_DATE_EPOCH}" -- "${pacstrap_dir}/usr/lib/clock-epoch" + _msg_info "Done!" } @@ -1560,7 +2828,13 @@ _make_pkglist() { "bootstrap") pacman -Q --sysroot "${pacstrap_dir}" > "${pacstrap_dir}/pkglist.${arch}.txt" ;; - "iso"|"netboot") + "iso") + install -d -m 0755 -- "${isofs_dir}/${install_dir}" + if [[ ! "${airootfs_image_type}" == *"luks"* ]]; then + pacman -Q --sysroot "${pacstrap_dir}" > "${isofs_dir}/${install_dir}/pkglist.${arch}.txt" + fi + ;; + "netboot") install -d -m 0755 -- "${isofs_dir}/${install_dir}" pacman -Q --sysroot "${pacstrap_dir}" > "${isofs_dir}/${install_dir}/pkglist.${arch}.txt" ;; @@ -1568,6 +2842,30 @@ _make_pkglist() { _msg_info "Done!" } +# build the base for an ISO and/or a netboot target +_build_dongle_base() { + local run_once_mode="base" + local _sig_dir + _sig_dir="${dongle_isofs_dir}/${install_dir}/${arch}" + keys_dir="${work_dir}/keys" + + [[ -d "${keys_dir}" ]] || install -d -- "${keys_dir}" + [[ -d "${dongle_isofs_dir}" ]] || install -d -- "${dongle_isofs_dir}" + [[ -d "${dongle_persistent_dir}" ]] || install -d -- "${dongle_persistent_dir}" + + if [ "${encryption_key}" != "" ]; then + cp "${encryption_key}" "${keys_dir}/airootfs.key" + fi + + if [[ "${airootfs_image_type}" == *"luks"* ]] && [[ " ${buildmodes[*]} " =~ ' dongle ' ]]; then + _run_once _prepare_keys_image + fi + + _run_once _make_boot_on_dongle + _make_bootmodes_dongle +} + + # build the base for an ISO and/or a netboot target _build_iso_base() { local run_once_mode="base" @@ -1577,9 +2875,17 @@ _build_iso_base() { # Set up essential directory paths pacstrap_dir="${work_dir}/${arch}/airootfs" isofs_dir="${work_dir}/iso" + keys_dir="${work_dir}/keys" + airootfs_img_dir="${isofs_dir}/${install_dir}/${arch}" + persistent_dir="${work_dir}/persistent" + dongle_persistent_dir="${work_dir}/dongle_persistent" # Create working directory - [[ -d "${work_dir}" ]] || install -d -- "${work_dir}" + [[ -d "${airootfs_img_dir}" ]] || install -d -- "${airootfs_img_dir}" + [[ -d "${keys_dir}" ]] || install -d -- "${keys_dir}" + [[ -d "${persistent_dir}" ]] || install -d -- "${persistent_dir}" + [[ -d "${dongle_persistent_dir}" ]] || install -d -- "${dongle_persistent_dir}" + # Write build date to file or if the file exists, read it from there if [[ -e "${work_dir}/build_date" ]]; then SOURCE_DATE_EPOCH="$(<"${work_dir}/build_date")" @@ -1595,10 +2901,11 @@ _build_iso_base() { _run_once _make_version _run_once _make_customize_airootfs _run_once _make_pkglist + _run_once _prepare_persistent_image if [[ "${buildmode}" == 'netboot' ]]; then _run_once _make_boot_on_iso9660 else - _make_bootmodes + _make_bootmodes_iso fi _run_once _cleanup_pacstrap_dir _run_once _prepare_airootfs_image @@ -1641,10 +2948,20 @@ _build_buildmode_netboot() { _build_buildmode_iso() { local image_name="${iso_name}-${iso_version}-${arch}.iso" local run_once_mode="${buildmode}" + efibootimg="${work_dir}/efiboot.img" _build_iso_base _run_once _build_iso_image } +# Build the dongle ISO buildmode +_build_buildmode_dongle() { + local image_name="${iso_name}-dongle-${iso_version}-${arch}.iso" + local run_once_mode="${buildmode}" + efibootimg="${work_dir}/efiboot.img" + _build_dongle_base + _run_once _build_dongle_image +} + # build all buildmodes _build() { local buildmode @@ -1655,7 +2972,7 @@ _build() { done } -while getopts 'c:p:C:L:P:A:D:w:m:o:g:G:vh?' arg; do +while getopts 'c:p:C:L:P:A:D:w:m:o:g:G:H:vh?' arg; do case "${arg}" in p) read -r -a override_pkg_list <<< "${OPTARG}" ;; C) override_pacman_conf="${OPTARG}" ;; @@ -1669,6 +2986,7 @@ while getopts 'c:p:C:L:P:A:D:w:m:o:g:G:vh?' arg; do o) override_out_dir="${OPTARG}" ;; g) override_gpg_key="${OPTARG}" ;; G) override_gpg_sender="${OPTARG}" ;; + H) override_gpg_home="${OPTARG}" ;; v) override_quiet="n" ;; h|?) _usage 0 ;; *) diff --git a/configs/baseline/airootfs/etc/crypttab b/configs/baseline/airootfs/etc/crypttab new file mode 100644 index 0000000000000000000000000000000000000000..5960ee1b4979b929750de2864bde8a606d7c390a --- /dev/null +++ b/configs/baseline/airootfs/etc/crypttab @@ -0,0 +1,4 @@ +# Configuration for encrypted block devices. +# See crypttab(5) for details. + + persistent /dev/disk/by-uuid/%PERSISTENT_UUID% /etc/keys/persistent.key x-systemd.device-timeout=3 diff --git a/configs/baseline/airootfs/etc/mkinitcpio.conf b/configs/baseline/airootfs/etc/mkinitcpio.conf index 34b1a0666c6aadaf6a1e58be545ce7d508500840..4dec74d44819b6e163753a80b357c2276badfa9d 100644 --- a/configs/baseline/airootfs/etc/mkinitcpio.conf +++ b/configs/baseline/airootfs/etc/mkinitcpio.conf @@ -49,7 +49,7 @@ FILES=() # ## NOTE: If you have /usr on a separate partition, you MUST include the # usr, fsck and shutdown hooks. -HOOKS=(base udev modconf archiso block filesystems) +HOOKS=(base udev modconf %ARCHISO_HOOKS% block filesystems) # COMPRESSION # Use this to compress the initramfs image. By default, gzip compression diff --git a/configs/baseline/airootfs/etc/systemd/system/first-boot-user-setup.service b/configs/baseline/airootfs/etc/systemd/system/first-boot-user-setup.service new file mode 100644 index 0000000000000000000000000000000000000000..347030964ba4fd7701826111c94a64249fbd0a50 --- /dev/null +++ b/configs/baseline/airootfs/etc/systemd/system/first-boot-user-setup.service @@ -0,0 +1,12 @@ +[Unit] +Description=Set up an user if not found on the system +After=systemd-homed.service + +[Service] +Type=oneshot +ExecStart=/usr/local/bin/user-setup user +ExecStop= +RemainAfterExit=no + +[Install] +WantedBy=multi-user.target diff --git a/configs/baseline/airootfs/etc/systemd/system/home.automount b/configs/baseline/airootfs/etc/systemd/system/home.automount new file mode 100644 index 0000000000000000000000000000000000000000..25a86a8ee8a5af54da3cb38a48fb4cce62086b5d --- /dev/null +++ b/configs/baseline/airootfs/etc/systemd/system/home.automount @@ -0,0 +1,9 @@ +[Unit] +Description=Home partition automount + +[Automount] +Where=/home +TimeoutIdleSec=3min + +[Install] +WantedBy=multi-user.target diff --git a/configs/baseline/airootfs/etc/systemd/system/home.mount b/configs/baseline/airootfs/etc/systemd/system/home.mount new file mode 100644 index 0000000000000000000000000000000000000000..da0b752e680cad73132efa22ce1c2621e15cbe4a --- /dev/null +++ b/configs/baseline/airootfs/etc/systemd/system/home.mount @@ -0,0 +1,14 @@ +[Unit] +Description=Home bind mount +RequiresMountsFor=/run/archiso/persistent + +[Mount] +What=/run/archiso/persistent/home +Where=/home +Type=None +Options=bind,nofail +DirectoryMode=0711 +TimeoutSec=5 + +[Install] +WantedBy=multi-user.target diff --git a/configs/baseline/airootfs/etc/systemd/system/multi-user.target.wants/first-boot-user-setup.service b/configs/baseline/airootfs/etc/systemd/system/multi-user.target.wants/first-boot-user-setup.service new file mode 120000 index 0000000000000000000000000000000000000000..f28ef13eae5b81d2dab585698605f9ddcba48f7b --- /dev/null +++ b/configs/baseline/airootfs/etc/systemd/system/multi-user.target.wants/first-boot-user-setup.service @@ -0,0 +1 @@ +/etc/systemd/system/first-boot-user-setup.service \ No newline at end of file diff --git a/configs/baseline/airootfs/etc/systemd/system/multi-user.target.wants/home.automount b/configs/baseline/airootfs/etc/systemd/system/multi-user.target.wants/home.automount new file mode 120000 index 0000000000000000000000000000000000000000..85b9bf831212538da93094d6cc5da5a366d89148 --- /dev/null +++ b/configs/baseline/airootfs/etc/systemd/system/multi-user.target.wants/home.automount @@ -0,0 +1 @@ +/etc/systemd/system/home.automount \ No newline at end of file diff --git a/configs/baseline/airootfs/etc/systemd/system/multi-user.target.wants/run-archiso-persistent.automount b/configs/baseline/airootfs/etc/systemd/system/multi-user.target.wants/run-archiso-persistent.automount new file mode 120000 index 0000000000000000000000000000000000000000..a94d01ba3f8411c50742d4ac04004ad758dad5f3 --- /dev/null +++ b/configs/baseline/airootfs/etc/systemd/system/multi-user.target.wants/run-archiso-persistent.automount @@ -0,0 +1 @@ +/etc/systemd/system/run-archiso-persistent.automount \ No newline at end of file diff --git a/configs/baseline/airootfs/etc/systemd/system/multi-user.target.wants/systemd-homed.service b/configs/baseline/airootfs/etc/systemd/system/multi-user.target.wants/systemd-homed.service new file mode 120000 index 0000000000000000000000000000000000000000..7ccdeb9c66cf4e85e7e09fe233f2a2c0e8a8c457 --- /dev/null +++ b/configs/baseline/airootfs/etc/systemd/system/multi-user.target.wants/systemd-homed.service @@ -0,0 +1 @@ +/usr/lib/systemd/system/systemd-homed.service \ No newline at end of file diff --git a/configs/baseline/airootfs/etc/systemd/system/multi-user.target.wants/var-lib-bluetooth.automount b/configs/baseline/airootfs/etc/systemd/system/multi-user.target.wants/var-lib-bluetooth.automount new file mode 120000 index 0000000000000000000000000000000000000000..5a67852e208e186635bf9452557918bfc590cd65 --- /dev/null +++ b/configs/baseline/airootfs/etc/systemd/system/multi-user.target.wants/var-lib-bluetooth.automount @@ -0,0 +1 @@ +/etc/systemd/system/var-lib-bluetooth.automount \ No newline at end of file diff --git a/configs/baseline/airootfs/etc/systemd/system/multi-user.target.wants/var-lib-systemd-home.automount b/configs/baseline/airootfs/etc/systemd/system/multi-user.target.wants/var-lib-systemd-home.automount new file mode 120000 index 0000000000000000000000000000000000000000..f84a07410b6563c6c3539b8b39d5f55a0d4d2cf0 --- /dev/null +++ b/configs/baseline/airootfs/etc/systemd/system/multi-user.target.wants/var-lib-systemd-home.automount @@ -0,0 +1 @@ +/etc/systemd/system/var-lib-systemd-home.automount \ No newline at end of file diff --git a/configs/baseline/airootfs/etc/systemd/system/run-archiso-persistent.automount b/configs/baseline/airootfs/etc/systemd/system/run-archiso-persistent.automount new file mode 100644 index 0000000000000000000000000000000000000000..91206aed67a91c08260512df811cb36e99882887 --- /dev/null +++ b/configs/baseline/airootfs/etc/systemd/system/run-archiso-persistent.automount @@ -0,0 +1,9 @@ +[Unit] +Description=Persistent partition automount + +[Automount] +Where=/run/archiso/persistent +TimeoutIdleSec=3min + +[Install] +WantedBy=multi-user.target diff --git a/configs/baseline/airootfs/etc/systemd/system/run-archiso-persistent.mount b/configs/baseline/airootfs/etc/systemd/system/run-archiso-persistent.mount new file mode 100644 index 0000000000000000000000000000000000000000..5e32adfcd998da25e576161c3c73176268b96ee3 --- /dev/null +++ b/configs/baseline/airootfs/etc/systemd/system/run-archiso-persistent.mount @@ -0,0 +1,12 @@ +[Unit] +Description=Persistent mount + +[Mount] +What=/dev/mapper/persistent +Where=/run/archiso/persistent +Type=ext4 +Options=rw,noatime,nofail +TimeoutSec=5 + +[Install] +WantedBy=multi-user.target diff --git a/configs/baseline/airootfs/etc/systemd/system/system-generators/systemd-gpt-auto-generator b/configs/baseline/airootfs/etc/systemd/system/system-generators/systemd-gpt-auto-generator new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/configs/baseline/airootfs/etc/systemd/system/var-lib-bluetooth.automount b/configs/baseline/airootfs/etc/systemd/system/var-lib-bluetooth.automount new file mode 100644 index 0000000000000000000000000000000000000000..1ee955683b2e9199f2bf8c232790ea0ce5f07755 --- /dev/null +++ b/configs/baseline/airootfs/etc/systemd/system/var-lib-bluetooth.automount @@ -0,0 +1,9 @@ +[Unit] +Description=Bluetooth data automount + +[Automount] +Where=/var/lib/bluetooth +TimeoutIdleSec=3min + +[Install] +WantedBy=multi-user.target diff --git a/configs/baseline/airootfs/etc/systemd/system/var-lib-bluetooth.mount b/configs/baseline/airootfs/etc/systemd/system/var-lib-bluetooth.mount new file mode 100644 index 0000000000000000000000000000000000000000..d659a675f663c22142c5856a76c4b4cd69f9560b --- /dev/null +++ b/configs/baseline/airootfs/etc/systemd/system/var-lib-bluetooth.mount @@ -0,0 +1,14 @@ +[Unit] +Description=Bluetooth data directory bind mount +RequiresMountsFor=/run/archiso/persistent + +[Mount] +What=/run/archiso/persistent/var/lib/bluetooth +Where=/var/lib/bluetooth +Type=None +Options=bind,nofail +DirectoryMode=0755 +TimeoutSec=5 + +[Install] +WantedBy=multi-user.target diff --git a/configs/baseline/airootfs/etc/systemd/system/var-lib-systemd-home.automount b/configs/baseline/airootfs/etc/systemd/system/var-lib-systemd-home.automount new file mode 100644 index 0000000000000000000000000000000000000000..6469f08216f8ff67be61088524ca9eb92b68581c --- /dev/null +++ b/configs/baseline/airootfs/etc/systemd/system/var-lib-systemd-home.automount @@ -0,0 +1,9 @@ +[Unit] +Description=systemd-homed credentials automount + +[Automount] +Where=/var/lib/systemd/home +TimeoutIdleSec=3min + +[Install] +WantedBy=multi-user.target diff --git a/configs/baseline/airootfs/etc/systemd/system/var-lib-systemd-home.mount b/configs/baseline/airootfs/etc/systemd/system/var-lib-systemd-home.mount new file mode 100644 index 0000000000000000000000000000000000000000..2fd891b631d0be7856f5c2f547992d272117a3a2 --- /dev/null +++ b/configs/baseline/airootfs/etc/systemd/system/var-lib-systemd-home.mount @@ -0,0 +1,14 @@ +[Unit] +Description=Systemd home credentials bind mount +RequiresMountsFor=/run/archiso/persistent + +[Mount] +What=/run/archiso/persistent/var/lib/systemd/home +Where=/var/lib/systemd/home +Type=None +Options=bind,nofail +DirectoryMode=0755 +TimeoutSec=5 + +[Install] +WantedBy=multi-user.target diff --git a/configs/baseline/airootfs/home/.gitkeep b/configs/baseline/airootfs/home/.gitkeep new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/configs/baseline/airootfs/run/archiso/keys/.gitkeep b/configs/baseline/airootfs/run/archiso/keys/.gitkeep new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/configs/baseline/airootfs/run/archiso/persistent/.gitkeep b/configs/baseline/airootfs/run/archiso/persistent/.gitkeep new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/configs/baseline/airootfs/usr/local/bin/user-setup b/configs/baseline/airootfs/usr/local/bin/user-setup new file mode 100755 index 0000000000000000000000000000000000000000..ee8d8068e5c72d151d91c10e83c4fb651452c06a --- /dev/null +++ b/configs/baseline/airootfs/usr/local/bin/user-setup @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + + +_user_setup(){ + local _user=${1} + local _does_exist + _does_exist="$(/usr/bin/homectl list | awk '{print $1}' | grep -q "\b${_user}\b")" + + if ! "${_does_exist}"; then + /usr/bin/homectl create "${_user}" + fi +} + +_user="${1}" +_user_setup "${_user}" diff --git a/configs/baseline/airootfs/var/lib/bluetooth/.gitkeep b/configs/baseline/airootfs/var/lib/bluetooth/.gitkeep new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/configs/baseline/airootfs/var/lib/systemd/home/.gitkeep b/configs/baseline/airootfs/var/lib/systemd/home/.gitkeep new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/configs/baseline/efiboot/loader/entries/01-archiso-x86_64-linux.conf b/configs/baseline/efiboot/loader/entries/01-archiso-x86_64-linux.conf index 11624b658a80564635eeded5c08f2a93c1375124..950d004f183fc239af2d6cda933a4bd1d763df47 100644 --- a/configs/baseline/efiboot/loader/entries/01-archiso-x86_64-linux.conf +++ b/configs/baseline/efiboot/loader/entries/01-archiso-x86_64-linux.conf @@ -1,4 +1,5 @@ -title Arch Linux (x86_64, UEFI) -linux /%INSTALL_DIR%/boot/x86_64/vmlinuz-linux -initrd /%INSTALL_DIR%/boot/x86_64/initramfs-linux.img -options archisobasedir=%INSTALL_DIR% archisolabel=%ARCHISO_LABEL% +title Arch Linux (%ARCH%, UEFI) +sort-key 01 +linux %INSTALL_DIR%/boot/x86_64/vmlinuz-linux +initrd %INSTALL_DIR%/boot/x86_64/initramfs-linux.img +options %KERNEL_PARAMS% diff --git a/configs/baseline/efiboot/loader/entries/02-archiso-x86_64-ram-linux.conf b/configs/baseline/efiboot/loader/entries/02-archiso-x86_64-ram-linux.conf index d66f5a6b9642968a72011efbb2792426e051e08b..a5521bade328d97dad1b793bda9a21b7085d603f 100644 --- a/configs/baseline/efiboot/loader/entries/02-archiso-x86_64-ram-linux.conf +++ b/configs/baseline/efiboot/loader/entries/02-archiso-x86_64-ram-linux.conf @@ -1,4 +1,5 @@ -title Arch Linux (x86_64, UEFI) Copy to RAM -linux /%INSTALL_DIR%/boot/x86_64/vmlinuz-linux -initrd /%INSTALL_DIR%/boot/x86_64/initramfs-linux.img -options archisobasedir=%INSTALL_DIR% archisolabel=%ARCHISO_LABEL% copytoram +title Arch Linux (%ARCH%, UEFI) Copy to RAM +sort-key 02 +linux %INSTALL_DIR%/boot/x86_64/vmlinuz-linux +initrd %INSTALL_DIR%/boot/x86_64/initramfs-linux.img +options %KERNEL_PARAMS% copytoram diff --git a/configs/baseline/grub/grub.cfg b/configs/baseline/grub/grub.cfg index 3df0b985152462fa896f29f467c117031e2f329a..b85c776827f7b638665713fd0404f428ed45ff5d 100644 --- a/configs/baseline/grub/grub.cfg +++ b/configs/baseline/grub/grub.cfg @@ -1,12 +1,14 @@ +# Load partition table and file system modules +insmod cryptodisk +insmod luks2 insmod part_gpt insmod part_msdos insmod fat insmod iso9660 +# Use graphics-mode output insmod all_video - insmod font - if loadfont "${prefix}/fonts/unicode.pf2" ; then insmod gfxterm set gfxmode="auto" @@ -14,20 +16,30 @@ if loadfont "${prefix}/fonts/unicode.pf2" ; then terminal_output gfxterm fi +# Enable serial console +if serial --unit=0 --speed=115200; then + terminal_input --append serial + terminal_output --append serial +fi + +# Set default menu entry default=archlinux timeout=15 timeout_style=menu -menuentry "Arch Linux (x86_64, UEFI)" --class arch --class gnu-linux --class gnu --class os --id 'archlinux' { + +# Menu entries + +menuentry "Arch Linux (%ARCH%)" --class arch --class gnu-linux --class gnu --class os --id 'archlinux' { set gfxpayload=keep - search --no-floppy --set=root --label %ARCHISO_LABEL% - linux /%INSTALL_DIR%/boot/x86_64/vmlinuz-linux archisobasedir=%INSTALL_DIR% archisolabel=%ARCHISO_LABEL% - initrd /%INSTALL_DIR%/boot/x86_64/initramfs-linux.img + %DEVICE_SELECT_CMDLINE% + linux %INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux %KERNEL_PARAMS% + initrd %INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img } -menuentry "Arch Linux (x86_64, UEFI) Copy to RAM" --class arch --class gnu-linux --class gnu --class os --id 'archlinux-copy-to-ram' { +menuentry "Arch Linux (%ARCH%) Copy to RAM" --class arch --class gnu-linux --class gnu --class os --id 'archlinux-copy-to-ram' { set gfxpayload=keep - search --no-floppy --set=root --label %ARCHISO_LABEL% - linux /%INSTALL_DIR%/boot/x86_64/vmlinuz-linux archisobasedir=%INSTALL_DIR% archisolabel=%ARCHISO_LABEL% copytoram - initrd /%INSTALL_DIR%/boot/x86_64/initramfs-linux.img + %DEVICE_SELECT_CMDLINE% + linux %INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux %KERNEL_PARAMS% copytoram + initrd %INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img } diff --git a/configs/baseline/packages.x86_64 b/configs/baseline/packages.x86_64 index cbb93b03e1d8e02af6eb441268b22fd50492403e..7a187ca3494696817fee7f6cdad9cd7368e523f9 100644 --- a/configs/baseline/packages.x86_64 +++ b/configs/baseline/packages.x86_64 @@ -1,5 +1,6 @@ base cloud-init +grub hyperv linux mkinitcpio @@ -8,5 +9,4 @@ open-vm-tools openssh pv qemu-guest-agent -syslinux virtualbox-guest-utils-nox diff --git a/configs/baseline/persistent/home/.gitkeep b/configs/baseline/persistent/home/.gitkeep new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/configs/baseline/persistent/var/lib/bluetooth/.gitkeep b/configs/baseline/persistent/var/lib/bluetooth/.gitkeep new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/configs/baseline/persistent/var/lib/systemd/home/.gitkeep b/configs/baseline/persistent/var/lib/systemd/home/.gitkeep new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/configs/baseline/profiledef.sh b/configs/baseline/profiledef.sh index 411558149c0f5e652a030cc75f5f6b005c8ea572..5a76aabb23e4e95038ad7800ede58a94312636f0 100644 --- a/configs/baseline/profiledef.sh +++ b/configs/baseline/profiledef.sh @@ -7,14 +7,27 @@ iso_publisher="Arch Linux " iso_application="Arch Linux baseline" iso_version="$(date +%Y.%m.%d)" install_dir="arch" -buildmodes=('iso') -bootmodes=('bios.syslinux.mbr' 'bios.syslinux.eltorito' - 'uefi-ia32.grub.esp' 'uefi-x64.grub.esp' - 'uefi-ia32.grub.eltorito' 'uefi-x64.grub.eltorito') +buildmodes=('iso' 'dongle') +bootmodes=('bios.grub.mbr' + 'bios.grub.eltorito' + 'uefi-ia32.grub.esp' + 'uefi-ia32.grub.eltorito' + 'uefi-x64.grub.esp' + 'uefi-x64.grub.eltorito') arch="x86_64" pacman_conf="pacman.conf" airootfs_image_type="erofs" airootfs_image_tool_options=('-zlz4hc,12' -E ztailpacking) +encryption_key="auto" +persistent_size_kib=500000 +persistent_image_type="ext4+luks" +keys_image_type="erofs" +keys_image_tool_options=('-zlz4hc,12' -E ztailpacking) file_permissions=( ["/etc/shadow"]="0:0:400" + ["/home"]="0:0:711" + ["/run/archiso/keys"]="0:0:700" + ["/run/archiso/persistent"]="0:0:711" + ["/usr/local/bin/user-setup"]="0:0:755" + ["/var/lib/bluetooth"]="0:0:755" ) diff --git a/configs/baseline/syslinux/syslinux-linux.cfg b/configs/baseline/syslinux/syslinux-linux.cfg index 6bfd0c3047fd8c6c716cd93a7579e4fd84ade0c7..490e4aff80fc45c2d3e242463a38f749ecb7ba83 100644 --- a/configs/baseline/syslinux/syslinux-linux.cfg +++ b/configs/baseline/syslinux/syslinux-linux.cfg @@ -1,11 +1,11 @@ LABEL arch -MENU LABEL Arch Linux (x86_64, BIOS) -LINUX /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux -INITRD /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img -APPEND archisobasedir=%INSTALL_DIR% archisolabel=%ARCHISO_LABEL% +MENU LABEL Arch Linux (%ARCH%, BIOS) +LINUX %INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux +INITRD %INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img +APPEND %KERNEL_PARAMS% LABEL arch-ram -MENU LABEL Arch Linux (x86_64, BIOS) Copy to RAM -LINUX /%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux -INITRD /%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img -APPEND archisobasedir=%INSTALL_DIR% archisolabel=%ARCHISO_LABEL% copytoram +MENU LABEL Arch Linux (%ARCH%, BIOS) Copy to RAM +LINUX %INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux +INITRD %INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img +APPEND %KERNEL_PARAMS% copytoram diff --git a/configs/releng/airootfs/etc/mkinitcpio.conf b/configs/releng/airootfs/etc/mkinitcpio.conf index f57dbdd2e9332d14ff6122f746a1d14a7c90f544..e337e5be4b0d74bb51e2ce8a81c253caf739b50c 100644 --- a/configs/releng/airootfs/etc/mkinitcpio.conf +++ b/configs/releng/airootfs/etc/mkinitcpio.conf @@ -4,7 +4,7 @@ # run. Advanced users may wish to specify all system modules # in this array. For instance: # MODULES=(piix ide_disk reiserfs) -MODULES=() +MODULES=(%ARCHISO_MODULES%) # BINARIES # This setting includes any additional binaries a given user may @@ -49,7 +49,7 @@ FILES=() # ## NOTE: If you have /usr on a separate partition, you MUST include the # usr, fsck and shutdown hooks. -HOOKS=(base udev modconf memdisk archiso archiso_loop_mnt archiso_pxe_common archiso_pxe_nbd archiso_pxe_http archiso_pxe_nfs archiso_kms block filesystems keyboard) +HOOKS=(base udev modconf memdisk keyboard %ARCHISO_HOOKS% archiso_loop_mnt archiso_pxe_common archiso_pxe_nbd archiso_pxe_http archiso_pxe_nfs archiso_kms block filesystems) # COMPRESSION # Use this to compress the initramfs image. By default, gzip compression diff --git a/configs/releng/airootfs/etc/systemd/system/multi-user.target.wants/setup-persistent-storage.service b/configs/releng/airootfs/etc/systemd/system/multi-user.target.wants/setup-persistent-storage.service new file mode 120000 index 0000000000000000000000000000000000000000..875f86200480093059234649cdfc1a772dd59aa4 --- /dev/null +++ b/configs/releng/airootfs/etc/systemd/system/multi-user.target.wants/setup-persistent-storage.service @@ -0,0 +1 @@ +/etc/systemd/system/setup-persistent-storage.service \ No newline at end of file diff --git a/configs/releng/airootfs/etc/systemd/system/setup-persistent-storage.service b/configs/releng/airootfs/etc/systemd/system/setup-persistent-storage.service new file mode 100644 index 0000000000000000000000000000000000000000..823cb1ee949a28c2b1e18fd5f139cf782e74f522 --- /dev/null +++ b/configs/releng/airootfs/etc/systemd/system/setup-persistent-storage.service @@ -0,0 +1,9 @@ +[Unit] +Description=Setup an encrypted persistent storage on the USB drive + +[Service] +Type=oneshot +ExecStart=/usr/local/bin/setup-persistent-storage + +[Install] +WantedBy=multi-user.target diff --git a/configs/releng/airootfs/run/archiso/keys/.gitkeep b/configs/releng/airootfs/run/archiso/keys/.gitkeep new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/configs/releng/airootfs/usr/local/bin/setup-persistent-storage b/configs/releng/airootfs/usr/local/bin/setup-persistent-storage new file mode 100755 index 0000000000000000000000000000000000000000..743f959541929d76597abbf1cb99ae52d714f4b2 --- /dev/null +++ b/configs/releng/airootfs/usr/local/bin/setup-persistent-storage @@ -0,0 +1,139 @@ +#!/bin/bash + +grub_cfg="/run/archiso/bootmnt/EFI/BOOT/grub.cfg" +archisouuid="$(grep "archisouuid=" < "${grub_cfg}" | head -1 | awk '{print $5}' | sed 's/archisouuid=//g')" +archisolabel="$(grep "archisolabel=" < "${grub_cfg}" | head -1 | awk '{print $5}' | sed 's/archisouuid=//g')" +guid="0FC63DAF-8483-4772-8E79-3D69D8477DE4" + +_find_iso() { + for disk in /dev/disk/by-diskseq/*; do + disk_uuid=$(blkid -o value -s UUID "${disk}") + disk_type=$(blkid -o value -s TYPE "${disk}") + #shellcheck disable=SC2154 + if [ "${disk_uuid}" = "${archisouuid}" ] && [ "${disk_type}" = "iso9660" ]; then + break + fi + done +} + +_get_avail_space() { + disk_avail="$(parted "${disk}" unit MB print free | grep 'Free Space' | tail -n1 | awk '{print $3}' | sed 's/MB//g')" + mem_avail=$(( $(grep MemAvailable <"/proc/meminfo" | awk '{print $2}') / 1000)) + avail=$(( disk_avail < mem_avail ? disk_avail : mem_avail )) +} + +_mount_keys_device() { + keys_device="/dev/disk/by-label/${archisolabel}_KEYS" + mkdir /ckey /ckey2 + chmod 700 /ckey /ckey2 + mount "${keys_device}" "/ckey" + cryptsetup --type luks2 open "/ckey/keys.erofs" "keys.map" + mount -t erofs "/dev/mapper/keys.map" "/ckey2" +} + +_unmount_keys_device() { + umount "/ckey2" + _close_luks_device "/dev/mapper/keys.map" + umount "/ckey" +} + +_resolve_encryption_key() { + local passwd_path="/tmp/persistent.passwd" + if [ "${encryption_key}" = "" ]; then + echo -n "Enter a new password for the persistent partition:" + read -rs password + echo "${password}" > "${passwd_path}" + encryption_key="${passwd_path}" + elif [ ! -f "${encryption_key}" ]; then + if [ "${encryption_key}" = "auto" ]; then + encryption_key="${passwd_path}" + dd if="/dev/random" bs=32 count=1 of="${encryption_key}" + echo "The encryption key has been saved to ${encryption_key}" + elif [ "${encryption_key}" = "airootfs" ]; then + _mount_keys_device + mkdir /tmp/keys + chmod 700 /tmp/keys + cp /ckey2/airootfs.key /tmp/keys/storage.key + encryption_key="/tmp/keys/storage.key" + else + echo "File ${encryption_key} does not exist." + exit 0 + fi + fi +} + +_luks_format(){ + mapper=$(basename "${image_path}")".map" + image_device="/dev/mapper/${mapper}" + cryptsetup_opts=('--type' 'luks2') + if [ "${encryption_key}" != "" ]; then + cryptsetup_opts+=("--key-file=${encryption_key}") + fi + cryptsetup_opts+=('--integrity' 'cmac-aes' \ + '--sector-size' '4096' \ + '--pbkdf-memory' 256) + while ! eval "cryptsetup -q -y luksFormat ${cryptsetup_opts[*]} ${image_path}"; do + sleep 1 + done + while ! eval "cryptsetup open ${image_path} ${mapper}"; do + _close_luks_device "${image_device}" + done +} + +_close_luks_device() { + local device="${1}" + local mapper + mapper=$(basename "${device}") + blockdev --flushbufs "${device}" + cryptsetup luksClose "${mapper}" || true + if [ -e "${device}" ]; then + dmsetup remove "${device}" || true + fi + sync +} + +_make_persistent() { + encryption_key="${1}" + image_path="/tmp/persistent.img" + passwd_path="/tmp/persistent.passwd" + mount -o remount,rw,size=1E "/run/archiso/cowspace" + fallocate -l "${avail}M" "${image_path}" + losetup "/dev/loop314" "${image_path}" + luks_format + mkfs.ext4 -O '^has_journal,^resize_inode' \ + -E 'lazy_itable_init=0,root_owner=0:0' \ + -m '0' -F -U 'clear' -- "${image_device}" + tune2fs -c 0 -i 0 - "${image_device}" > /dev/null + sync + _close_luks_device "${image_device}" + losetup -d "/dev/loop314" + # rm "${encryption_key}" +} + +_add_persistent() { + xorriso -indev "${disk}" -outdev "${disk}" -append_partition 3 "${guid}" "${image_path}" -boot_image any replay +} + +_validate_requirements() { + partitions=$(partx -g "${disk}" | wl -l) + if [ "${partitions}" -gt 2 ]; then + exit 0 + fi + _resolve_encryption_key +} + +_refresh_partitions() { + partprobe "${disk}" +} + +_start() { + local encryption_key="${1}" + _find_iso + _validate_requirements + _get_avail_space + _make_persistent "${encryption_key}" + _add_persistent + _refresh_partitions +} + +_start "${1}" diff --git a/configs/releng/efiboot/loader/entries/01-archiso-x86_64-linux.conf b/configs/releng/efiboot/loader/entries/01-archiso-x86_64-linux.conf index 1c2a7a82ae3cb6038c4d321c08fa736cfe2e4be4..2fcee5ff2f478a63baa3fd0c3f6778de1dbe180f 100644 --- a/configs/releng/efiboot/loader/entries/01-archiso-x86_64-linux.conf +++ b/configs/releng/efiboot/loader/entries/01-archiso-x86_64-linux.conf @@ -1,7 +1,7 @@ -title Arch Linux install medium (x86_64, UEFI) +title Arch Linux install medium (%ARCH%, UEFI) sort-key 01 -linux /%INSTALL_DIR%/boot/x86_64/vmlinuz-linux -initrd /%INSTALL_DIR%/boot/intel-ucode.img -initrd /%INSTALL_DIR%/boot/amd-ucode.img -initrd /%INSTALL_DIR%/boot/x86_64/initramfs-linux.img -options archisobasedir=%INSTALL_DIR% archisolabel=%ARCHISO_LABEL% +linux %INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux +initrd %INSTALL_DIR%/boot/intel-ucode.img +initrd %INSTALL_DIR%/boot/amd-ucode.img +initrd %INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img +options %KERNEL_PARAMS% diff --git a/configs/releng/efiboot/loader/entries/02-archiso-x86_64-speech-linux.conf b/configs/releng/efiboot/loader/entries/02-archiso-x86_64-speech-linux.conf index 64253d3b5d306803224f647424d5e8a26b079f1e..d80802ed7d480a3dbacaa37aad151c28df431fe1 100644 --- a/configs/releng/efiboot/loader/entries/02-archiso-x86_64-speech-linux.conf +++ b/configs/releng/efiboot/loader/entries/02-archiso-x86_64-speech-linux.conf @@ -1,7 +1,7 @@ -title Arch Linux install medium (x86_64, UEFI) with speech +title Arch Linux install medium (%ARCH%, UEFI) with speech sort-key 02 -linux /%INSTALL_DIR%/boot/x86_64/vmlinuz-linux -initrd /%INSTALL_DIR%/boot/intel-ucode.img -initrd /%INSTALL_DIR%/boot/amd-ucode.img -initrd /%INSTALL_DIR%/boot/x86_64/initramfs-linux.img -options archisobasedir=%INSTALL_DIR% archisolabel=%ARCHISO_LABEL% accessibility=on +linux %INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux +initrd %INSTALL_DIR%/boot/intel-ucode.img +initrd %INSTALL_DIR%/boot/amd-ucode.img +initrd %INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img +options %KERNEL_PARAMS% accessibility=on diff --git a/configs/releng/efiboot/loader/entries/03-archiso-x86_64-ram-linux.conf b/configs/releng/efiboot/loader/entries/03-archiso-x86_64-ram-linux.conf index 3e2665193b4f6e0a62c1df8fd45ed201859a11cc..6cc794fe6c3c0421a60010f8828242f4e2164873 100644 --- a/configs/releng/efiboot/loader/entries/03-archiso-x86_64-ram-linux.conf +++ b/configs/releng/efiboot/loader/entries/03-archiso-x86_64-ram-linux.conf @@ -1,7 +1,7 @@ -title Arch Linux install medium (x86_64, UEFI, Copy to RAM) +title Arch Linux install medium (%ARCH%, UEFI, Copy to RAM) sort-key 03 -linux /%INSTALL_DIR%/boot/x86_64/vmlinuz-linux -initrd /%INSTALL_DIR%/boot/intel-ucode.img -initrd /%INSTALL_DIR%/boot/amd-ucode.img -initrd /%INSTALL_DIR%/boot/x86_64/initramfs-linux.img -options archisobasedir=%INSTALL_DIR% archisolabel=%ARCHISO_LABEL% copytoram +linux %INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux +initrd %INSTALL_DIR%/boot/intel-ucode.img +initrd %INSTALL_DIR%/boot/amd-ucode.img +initrd %INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img +options %KERNEL_PARAMS% copytoram diff --git a/configs/releng/efiboot/loader/entries/04-archiso-x86_64-ram-speech-linux.conf b/configs/releng/efiboot/loader/entries/04-archiso-x86_64-ram-speech-linux.conf index 0d67999c57d8092e5f29131af55b99fbe008705e..64f5c69bd70f16c7d2dcd64c91f55de425d694ea 100644 --- a/configs/releng/efiboot/loader/entries/04-archiso-x86_64-ram-speech-linux.conf +++ b/configs/releng/efiboot/loader/entries/04-archiso-x86_64-ram-speech-linux.conf @@ -1,7 +1,7 @@ -title Arch Linux install medium (x86_64, UEFI, Copy to RAM) with speech +title Arch Linux install medium (%ARCH%, UEFI, Copy to RAM) with speech sort-key 04 -linux /%INSTALL_DIR%/boot/x86_64/vmlinuz-linux -initrd /%INSTALL_DIR%/boot/intel-ucode.img -initrd /%INSTALL_DIR%/boot/amd-ucode.img -initrd /%INSTALL_DIR%/boot/x86_64/initramfs-linux.img -options archisobasedir=%INSTALL_DIR% archisolabel=%ARCHISO_LABEL% copytoram accessibility=on +linux %INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux +initrd %INSTALL_DIR%/boot/intel-ucode.img +initrd %INSTALL_DIR%/boot/amd-ucode.img +initrd %INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img +options %KERNEL_PARAMS% copytoram accessibility=on diff --git a/configs/releng/grub/grub.cfg b/configs/releng/grub/grub.cfg index 9b78603a03a75803b91f4ddc0ec270bf1db0ed95..2cccf8000bee511ceb311000e8f649310059824f 100644 --- a/configs/releng/grub/grub.cfg +++ b/configs/releng/grub/grub.cfg @@ -1,12 +1,14 @@ +# Load partition table and file system modules +insmod cryptodisk +insmod luks2 insmod part_gpt insmod part_msdos insmod fat insmod iso9660 +# Use graphics-mode output insmod all_video - insmod font - if loadfont "${prefix}/fonts/unicode.pf2" ; then insmod gfxterm set gfxmode="auto" @@ -14,39 +16,47 @@ if loadfont "${prefix}/fonts/unicode.pf2" ; then terminal_output gfxterm fi +# Enable serial console +if serial --unit=0 --speed=115200; then + terminal_input --append serial + terminal_output --append serial +fi + +# Set default menu entry default=archlinux timeout=15 timeout_style=menu # GRUB init tune for accessibility -# play 600 988 1 1319 4 -menuentry "Arch Linux install medium (x86_64, UEFI)" --class arch --class gnu-linux --class gnu --class os --id 'archlinux' { +# Menu entries + +menuentry "Arch Linux install medium (%ARCH%)" --class arch --class gnu-linux --class gnu --class os --id 'archlinux' { set gfxpayload=keep - search --no-floppy --set=root --label %ARCHISO_LABEL% - linux /%INSTALL_DIR%/boot/x86_64/vmlinuz-linux archisobasedir=%INSTALL_DIR% archisolabel=%ARCHISO_LABEL% - initrd /%INSTALL_DIR%/boot/intel-ucode.img /%INSTALL_DIR%/boot/amd-ucode.img /%INSTALL_DIR%/boot/x86_64/initramfs-linux.img + %DEVICE_SELECT_CMDLINE% + linux %INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux %KERNEL_PARAMS% + initrd %INSTALL_DIR%/boot/intel-ucode.img %INSTALL_DIR%/boot/amd-ucode.img %INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img } -menuentry "Arch Linux install medium with speakup screen reader (x86_64, UEFI)" --hotkey s --class arch --class gnu-linux --class gnu --class os --id 'archlinux-accessibility' { +menuentry "Arch Linux install medium with speakup screen reader (%ARCH%)" --hotkey s --class arch --class gnu-linux --class gnu --class os --id 'archlinux-accessibility' { set gfxpayload=keep - search --no-floppy --set=root --label %ARCHISO_LABEL% - linux /%INSTALL_DIR%/boot/x86_64/vmlinuz-linux archisobasedir=%INSTALL_DIR% archisolabel=%ARCHISO_LABEL% accessibility=on - initrd /%INSTALL_DIR%/boot/intel-ucode.img /%INSTALL_DIR%/boot/amd-ucode.img /%INSTALL_DIR%/boot/x86_64/initramfs-linux.img + %DEVICE_SELECT_CMDLINE% + linux %INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux %KERNEL_PARAMS% accessibility=on + initrd %INSTALL_DIR%/boot/intel-ucode.img %INSTALL_DIR%/boot/amd-ucode.img %INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img } if [ "${grub_platform}" == "efi" ]; then if [ "${grub_cpu}" == "x86_64" ]; then menuentry "UEFI Shell" { insmod chain - search --no-floppy --set=root --label %ARCHISO_LABEL% + search --no-floppy --set=root --fs-uuid %BOOTABLE_UUID% chainloader /shellx64.efi } elif [ "${grub_cpu}" == "i386" ]; then menuentry "UEFI Shell" { insmod chain - search --no-floppy --set=root --label %ARCHISO_LABEL% + search --no-floppy --set=root --fs-uuid %BOOTABLE_UUID% chainloader /shellia32.efi } fi diff --git a/configs/releng/packages.x86_64 b/configs/releng/packages.x86_64 index 8a0c80dbf8f6a60662cba51d4d97ee1574d03931..fdb2c1597251b95fe19f053393248d61dc91281e 100644 --- a/configs/releng/packages.x86_64 +++ b/configs/releng/packages.x86_64 @@ -101,7 +101,6 @@ smartmontools sof-firmware squashfs-tools sudo -syslinux systemd-resolvconf tcpdump terminus-font @@ -121,4 +120,5 @@ wpa_supplicant wvdial xfsprogs xl2tpd +xorriso zsh diff --git a/configs/releng/profiledef.sh b/configs/releng/profiledef.sh index 4b4e68c0227e41091d6013275d2c8dfb370fea3f..52d1ded7e7a156d4c811dc21c555ac8534df6514 100644 --- a/configs/releng/profiledef.sh +++ b/configs/releng/profiledef.sh @@ -7,19 +7,32 @@ iso_publisher="Arch Linux " iso_application="Arch Linux Live/Rescue CD" iso_version="$(date +%Y.%m.%d)" install_dir="arch" -buildmodes=('iso') -bootmodes=('bios.syslinux.mbr' 'bios.syslinux.eltorito' - 'uefi-ia32.grub.esp' 'uefi-x64.grub.esp' - 'uefi-ia32.grub.eltorito' 'uefi-x64.grub.eltorito') +buildmodes=('iso' 'dongle') +bootmodes=('bios.grub.mbr' + 'bios.grub.eltorito' + 'uefi-ia32.grub.esp' + 'uefi-ia32.grub.eltorito' + 'uefi-x64.grub.esp' + 'uefi-x64.grub.eltorito') arch="x86_64" pacman_conf="pacman.conf" -airootfs_image_type="squashfs" -airootfs_image_tool_options=('-comp' 'xz' '-Xbcj' 'x86' '-b' '1M' '-Xdict-size' '1M') +airootfs_image_type="erofs" +airootfs_image_tool_options=('-zlz4hc,12' -E ztailpacking) +encryption_key="auto" +persistent_size_kib=500000 +persistent_image_type="ext4+luks" +keys_image_type="erofs" +keys_image_tool_options=('-zlz4hc,12' -E ztailpacking) file_permissions=( ["/etc/shadow"]="0:0:400" - ["/root"]="0:0:750" + ["/home"]="0:0:711" + ["/root"]="0:0:700" ["/root/.automated_script.sh"]="0:0:755" + ["/run/archiso/keys"]="0:0:700" ["/usr/local/bin/choose-mirror"]="0:0:755" ["/usr/local/bin/Installation_guide"]="0:0:755" ["/usr/local/bin/livecd-sound"]="0:0:755" + ["/usr/local/bin/setup-persistent-storage"]="0:0:755" + ["/usr/local/bin/user-setup"]="0:0:755" + ["/var/lib/bluetooth"]="0:0:755" ) diff --git a/configs/releng/syslinux/archiso_pxe-linux.cfg b/configs/releng/syslinux/archiso_pxe-linux.cfg index d812402a94b25a5b09558c017bc95f1819d273b9..c355c143ef02072d6bf3c675b566038e372d038a 100644 --- a/configs/releng/syslinux/archiso_pxe-linux.cfg +++ b/configs/releng/syslinux/archiso_pxe-linux.cfg @@ -3,10 +3,10 @@ TEXT HELP Boot the Arch Linux install medium using NBD. It allows you to install Arch Linux or perform system maintenance. ENDTEXT -MENU LABEL Arch Linux install medium (x86_64, NBD) -LINUX ::/%INSTALL_DIR%/boot/x86_64/vmlinuz-linux -INITRD ::/%INSTALL_DIR%/boot/intel-ucode.img,::/%INSTALL_DIR%/boot/amd-ucode.img,::/%INSTALL_DIR%/boot/x86_64/initramfs-linux.img -APPEND archisobasedir=%INSTALL_DIR% archisolabel=%ARCHISO_LABEL% archiso_nbd_srv=${pxeserver} checksum verify +MENU LABEL Arch Linux install medium (%ARCH%, NBD) +LINUX ::%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux +INITRD ::%INSTALL_DIR%/boot/intel-ucode.img,::%INSTALL_DIR%/boot/amd-ucode.img,::%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img +APPEND %KERNEL_PARAMS% archiso_nbd_srv=${pxeserver} checksum verify SYSAPPEND 3 LABEL arch64_nfs @@ -14,10 +14,10 @@ TEXT HELP Boot the Arch Linux live medium using NFS. It allows you to install Arch Linux or perform system maintenance. ENDTEXT -MENU LABEL Arch Linux install medium (x86_64, NFS) -LINUX ::/%INSTALL_DIR%/boot/x86_64/vmlinuz-linux -INITRD ::/%INSTALL_DIR%/boot/intel-ucode.img,::/%INSTALL_DIR%/boot/amd-ucode.img,::/%INSTALL_DIR%/boot/x86_64/initramfs-linux.img -APPEND archisobasedir=%INSTALL_DIR% archiso_nfs_srv=${pxeserver}:/run/archiso/bootmnt checksum verify +MENU LABEL Arch Linux install medium (%ARCH%, NFS) +LINUX ::%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux +INITRD ::%INSTALL_DIR%/boot/intel-ucode.img,::%INSTALL_DIR%/boot/amd-ucode.img,::%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img +APPEND %KERNEL_PARAMS% archiso_nfs_srv=${pxeserver}:/run/archiso/bootmnt checksum verify SYSAPPEND 3 LABEL arch64_http @@ -25,8 +25,8 @@ TEXT HELP Boot the Arch Linux live medium using HTTP. It allows you to install Arch Linux or perform system maintenance. ENDTEXT -MENU LABEL Arch Linux install medium (x86_64, HTTP) -LINUX ::/%INSTALL_DIR%/boot/x86_64/vmlinuz-linux -INITRD ::/%INSTALL_DIR%/boot/intel-ucode.img,::/%INSTALL_DIR%/boot/amd-ucode.img,::/%INSTALL_DIR%/boot/x86_64/initramfs-linux.img -APPEND archisobasedir=%INSTALL_DIR% archiso_http_srv=http://${pxeserver}/ checksum verify +MENU LABEL Arch Linux install medium (%ARCH%, HTTP) +LINUX ::%INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux +INITRD ::%INSTALL_DIR%/boot/intel-ucode.img,::%INSTALL_DIR%/boot/amd-ucode.img,::%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img +APPEND %KERNEL_PARAMS% archiso_http_srv=http://${pxeserver}/ checksum verify SYSAPPEND 3 diff --git a/configs/releng/syslinux/archiso_sys-linux.cfg b/configs/releng/syslinux/archiso_sys-linux.cfg index 0d85fccf1b0b34216e7a33234b9bcf056701ad5c..18c434a18144ceba8f9249e932ac1aae318ac3bb 100644 --- a/configs/releng/syslinux/archiso_sys-linux.cfg +++ b/configs/releng/syslinux/archiso_sys-linux.cfg @@ -3,10 +3,10 @@ TEXT HELP Boot the Arch Linux install medium on BIOS. It allows you to install Arch Linux or perform system maintenance. ENDTEXT -MENU LABEL Arch Linux install medium (x86_64, BIOS) -LINUX /%INSTALL_DIR%/boot/x86_64/vmlinuz-linux -INITRD /%INSTALL_DIR%/boot/intel-ucode.img,/%INSTALL_DIR%/boot/amd-ucode.img,/%INSTALL_DIR%/boot/x86_64/initramfs-linux.img -APPEND archisobasedir=%INSTALL_DIR% archisolabel=%ARCHISO_LABEL% +MENU LABEL Arch Linux install medium (%ARCH%, BIOS) +LINUX %INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux +INITRD %INSTALL_DIR%/boot/intel-ucode.img,%INSTALL_DIR%/boot/amd-ucode.img,%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img +APPEND %KERNEL_PARAMS% # Accessibility boot option LABEL arch64speech @@ -14,10 +14,10 @@ TEXT HELP Boot the Arch Linux install medium on BIOS with speakup screen reader. It allows you to install Arch Linux or perform system maintenance with speech feedback. ENDTEXT -MENU LABEL Arch Linux install medium (x86_64, BIOS) with ^speech -LINUX /%INSTALL_DIR%/boot/x86_64/vmlinuz-linux -INITRD /%INSTALL_DIR%/boot/intel-ucode.img,/%INSTALL_DIR%/boot/amd-ucode.img,/%INSTALL_DIR%/boot/x86_64/initramfs-linux.img -APPEND archisobasedir=%INSTALL_DIR% archisolabel=%ARCHISO_LABEL% accessibility=on +MENU LABEL Arch Linux install medium (%ARCH%, BIOS) with ^speech +LINUX %INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux +INITRD %INSTALL_DIR%/boot/intel-ucode.img,%INSTALL_DIR%/boot/amd-ucode.img,%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img +APPEND %KERNEL_PARAMS% accessibility=on # Copy to RAM boot option LABEL arch64ram @@ -25,7 +25,7 @@ TEXT HELP Boot the Arch Linux install medium on BIOS with Copy-to-RAM option It allows you to install Arch Linux or perform system maintenance. ENDTEXT -MENU LABEL Arch Linux install medium (x86_64, BIOS, Copy to RAM) -LINUX /%INSTALL_DIR%/boot/x86_64/vmlinuz-linux -INITRD /%INSTALL_DIR%/boot/intel-ucode.img,/%INSTALL_DIR%/boot/amd-ucode.img,/%INSTALL_DIR%/boot/x86_64/initramfs-linux.img -APPEND archisobasedir=%INSTALL_DIR% archisolabel=%ARCHISO_LABEL% copytoram +MENU LABEL Arch Linux install medium (%ARCH%, BIOS, Copy to RAM) +LINUX %INSTALL_DIR%/boot/%ARCH%/vmlinuz-linux +INITRD %INSTALL_DIR%/boot/intel-ucode.img,%INSTALL_DIR%/boot/amd-ucode.img,%INSTALL_DIR%/boot/%ARCH%/initramfs-linux.img +APPEND %KERNEL_PARAMS% copytoram diff --git a/docs/README.profile.rst b/docs/README.profile.rst index efcb86124a5a3481d81d7de3cfb63043b29c94a0..d4da0d809c97c947b4a5b1fb6d018a0332422313 100644 --- a/docs/README.profile.rst +++ b/docs/README.profile.rst @@ -39,6 +39,7 @@ The image file is constructed from some of the variables in ``profiledef.sh``: ` understood: - ``bootstrap``: Build a compressed file containing a minimal system to bootstrap from + - ``keys``: Build a "keys" ISO that is able to boot encrypted bootable ISO images. - ``iso``: Build a bootable ISO image (implicit default, if no ``buildmodes`` are set) - ``netboot``: Build artifacts required for netboot using iPXE * ``bootmodes``: A list of strings, that state the supported boot modes of the resulting image. Only the following are @@ -60,10 +61,21 @@ The image file is constructed from some of the variables in ``profiledef.sh``: ` * ``airootfs_image_type``: The image type to create. The following options are understood (defaults to ``squashfs``): - ``squashfs``: Create a squashfs image directly from the airootfs work directory + - ``squashfs+luks``: Create a LUKS image containing a squashfs generated directly from the airootfs work directory - ``ext4+squashfs``: Create an ext4 partition, copy the airootfs work directory to it and create a squashfs image from it + - ``ext4+squashfs+luks``: Create an ext4 partition, copy the airootfs work directory to it and create a LUKS containing a squashfs image generated from it - ``erofs``: Create an EROFS image for the airootfs work directory + - ``erofs+luks``: Create a LUKS image containing an EROFS image for the airootfs work directory * ``airootfs_image_tool_options``: An array of options to pass to the tool to create the airootfs image. ``mksquashfs`` and ``mkfs.erofs`` are supported. See ``mksquashfs --help`` or ``mkfs.erofs --help`` for all possible options +* ``encryption_key``: If pointing to a file, it will use as encryption key for the airootfs; if "auto" will generate a key + at build time; if empty, will prompt for password. +* ``persistent_size_kib``: Size in KB of the persistent partition. +* ``persistent_image_type``: The type of the persistent portion of the ISO. + - ``ext4``: Create an ext4 partition. + - ``ext4+luks``: Create a LUKS container with an ext4 partition inside. +* ``keys_image_type``: Same as ``airootfs_image_type`` for the "keys" ISO. +* ``keys_image_tool_options``: Same as ``airootfs_image_tool_options`` for the "keys" ISO. * ``file_permissions``: An associative array that lists files and/or directories who need specific ownership or permissions. The array's keys contain the path and the value is a colon separated list of owner UID, owner GID and access mode. E.g. ``file_permissions=(["/etc/shadow"]="0:0:400")``. When directories are listed with a trailing backslash (``/``) **all** files and directories contained within the listed directory will have the same owner UID, owner GID, and access mode applied recursively.