mkarchiso 48.2 KB
Newer Older
1
#!/usr/bin/env bash
2
3
#
# SPDX-License-Identifier: GPL-3.0-or-later
4

5
6
set -e -u

7
8
9
10
# Control the environment
umask 0022
export LANG="C"
export SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:-"$(date +%s)"}"
11

12
# Set application name from the script's file name
13
app_name="${0##*/}"
14
15

# Define global variables. All of them will be overwritten later
16
pkg_list=()
17
bootstrap_pkg_list=()
18
19
20
quiet=""
work_dir=""
out_dir=""
21
gpg_key=""
22
23
24
25
iso_name=""
iso_label=""
iso_publisher=""
iso_application=""
26
iso_version=""
27
28
29
30
install_dir=""
arch=""
pacman_conf=""
packages=""
31
32
33
bootstrap_packages=""
pacstrap_dir=""
buildmodes=()
34
bootmodes=()
35
36
airootfs_image_type=""
airootfs_image_tool_options=()
37
declare -A file_permissions=()
38
39


40
41
42
43
# Show an INFO message
# $1: message string
_msg_info() {
    local _msg="${1}"
44
    [[ "${quiet}" == "y" ]] || printf '[%s] INFO: %s\n' "${app_name}" "${_msg}"
45
46
}

47
48
49
50
# Show a WARNING message
# $1: message string
_msg_warning() {
    local _msg="${1}"
51
    printf '[%s] WARNING: %s\n' "${app_name}" "${_msg}" >&2
52
53
}

54
55
56
57
58
59
# Show an ERROR message then exit with status
# $1: message string
# $2: exit code number (with 0 does not exit)
_msg_error() {
    local _msg="${1}"
    local _error=${2}
60
    printf '[%s] ERROR: %s\n' "${app_name}" "${_msg}" >&2
61
    if (( _error > 0 )); then
62
        exit "${_error}"
63
64
65
    fi
}

66
67
_mount_airootfs() {
    trap "_umount_airootfs" EXIT HUP INT TERM
68
    install -d -m 0755 -- "${work_dir}/mnt/airootfs"
69
70
    _msg_info "Mounting '${pacstrap_dir}.img' on '${work_dir}/mnt/airootfs'..."
    mount -- "${pacstrap_dir}.img" "${work_dir}/mnt/airootfs"
71
    _msg_info "Done!"
72
73
}

74
_umount_airootfs() {
75
    _msg_info "Unmounting '${work_dir}/mnt/airootfs'..."
76
    umount -d -- "${work_dir}/mnt/airootfs"
77
    _msg_info "Done!"
78
    rmdir -- "${work_dir}/mnt/airootfs"
79
80
81
82
83
    trap - EXIT HUP INT TERM
}

# Show help usage, with an exit status.
# $1: exit status number.
84
_usage() {
85
    IFS='' read -r -d '' usagetext <<ENDUSAGETEXT || true
86
usage: ${app_name} [options] <profile_dir>
87
88
89
90
91
92
93
94
95
96
97
98
  options:
     -A <application> Set an application name for the ISO
                      Default: '${iso_application}'
     -C <file>        pacman configuration file.
                      Default: '${pacman_conf}'
     -D <install_dir> Set an install_dir. All files will by located here.
                      Default: '${install_dir}'
                      NOTE: Max 8 characters, use only [a-z0-9]
     -L <label>       Set the ISO volume label
                      Default: '${iso_label}'
     -P <publisher>   Set the ISO publisher
                      Default: '${iso_publisher}'
99
     -g <gpg_key>     Set the PGP key ID to be used for signing the rootfs image
100
     -h               This message
101
102
     -m [build modes] Build modes to use (valid modes are: 'bootstrap' and 'iso').
                      Multiple build modes are provided as quoted, space delimited list.
103
104
     -o <out_dir>     Set the output directory
                      Default: '${out_dir}'
105
106
     -p PACKAGE(S)    Package(s) to install.
                      Multiple packages are provided as quoted, space delimited list.
107
108
109
110
111
     -v               Enable verbose output
     -w <work_dir>    Set the working directory
                      Default: '${work_dir}'

  profile_dir:        Directory of the archiso profile to build
112
ENDUSAGETEXT
113
    printf '%s' "${usagetext}"
114
    exit "${1}"
115
116
}

117
# Shows configuration options.
118
_show_config() {
119
    local build_date
120
    build_date="$(date --utc --iso-8601=seconds -d "@${SOURCE_DATE_EPOCH}")"
121
    _msg_info "${app_name} configuration settings"
122
123
124
    _msg_info "             Architecture:   ${arch}"
    _msg_info "        Working directory:   ${work_dir}"
    _msg_info "   Installation directory:   ${install_dir}"
125
126
    _msg_info "               Build date:   ${build_date}"
    _msg_info "         Output directory:   ${out_dir}"
127
128
    _msg_info "       Current build mode:   ${buildmode}"
    _msg_info "              Build modes:   ${buildmodes[*]}"
129
130
131
    _msg_info "                  GPG key:   ${gpg_key:-None}"
    _msg_info "                  Profile:   ${profile}"
    _msg_info "Pacman configuration file:   ${pacman_conf}"
132
    _msg_info "          Image file name:   ${image_name:-None}"
133
134
135
136
    _msg_info "         ISO volume label:   ${iso_label}"
    _msg_info "            ISO publisher:   ${iso_publisher}"
    _msg_info "          ISO application:   ${iso_application}"
    _msg_info "               Boot modes:   ${bootmodes[*]}"
137
138
    _msg_info "            Packages File:   ${buildmode_packages}"
    _msg_info "                 Packages:   ${buildmode_pkg_list[*]}"
139
140
}

141
# Cleanup airootfs
142
143
_cleanup_pacstrap_dir() {
    _msg_info "Cleaning up in pacstrap location..."
144

145
    # Delete all files in /boot
146
    [[ -d "${pacstrap_dir}/boot" ]] && find "${pacstrap_dir}/boot" -mindepth 1 -delete
147
    # Delete pacman database sync cache files (*.tar.gz)
148
    [[ -d "${pacstrap_dir}/var/lib/pacman" ]] && find "${pacstrap_dir}/var/lib/pacman" -maxdepth 1 -type f -delete
149
    # Delete pacman database sync cache
150
    [[ -d "${pacstrap_dir}/var/lib/pacman/sync" ]] && find "${pacstrap_dir}/var/lib/pacman/sync" -delete
151
    # Delete pacman package cache
152
    [[ -d "${pacstrap_dir}/var/cache/pacman/pkg" ]] && find "${pacstrap_dir}/var/cache/pacman/pkg" -type f -delete
153
    # Delete all log files, keeps empty dirs.
154
    [[ -d "${pacstrap_dir}/var/log" ]] && find "${pacstrap_dir}/var/log" -type f -delete
155
    # Delete all temporary files and dirs
156
    [[ -d "${pacstrap_dir}/var/tmp" ]] && find "${pacstrap_dir}/var/tmp" -mindepth 1 -delete
157
    # Delete package pacman related files.
158
    find "${work_dir}" \( -name '*.pacnew' -o -name '*.pacsave' -o -name '*.pacorig' \) -delete
159
    # Create an empty /etc/machine-id
160
161
    rm -f -- "${pacstrap_dir}/etc/machine-id"
    printf '' > "${pacstrap_dir}/etc/machine-id"
162
163

    _msg_info "Done!"
164
}
165

166
167
168
169
_run_mksquashfs() {
    local image_path="${isofs_dir}/${install_dir}/${arch}/airootfs.sfs"
    if [[ "${quiet}" == "y" ]]; then
        mksquashfs "$@" "${image_path}" -noappend "${airootfs_image_tool_options[@]}" -no-progress > /dev/null
170
    else
171
        mksquashfs "$@" "${image_path}" -noappend "${airootfs_image_tool_options[@]}"
172
173
174
    fi
}

175
# Makes a ext4 filesystem inside a SquashFS from a source directory.
176
_mkairootfs_ext4+squashfs() {
177
    [[ -e "${pacstrap_dir}" ]] || _msg_error "The path '${pacstrap_dir}' does not exist" 1
178

179
    _msg_info "Creating ext4 image of 32 GiB..."
180
    if [[ "${quiet}" == "y" ]]; then
181
        mkfs.ext4 -q -O '^has_journal,^resize_inode' -E 'lazy_itable_init=0' -m 0 -F -- "${pacstrap_dir}.img" 32G
182
    else
183
        mkfs.ext4 -O '^has_journal,^resize_inode' -E 'lazy_itable_init=0' -m 0 -F -- "${pacstrap_dir}.img" 32G
184
    fi
185
    tune2fs -c 0 -i 0 -- "${pacstrap_dir}.img" > /dev/null
186
    _msg_info "Done!"
187
    _mount_airootfs
188
189
    _msg_info "Copying '${pacstrap_dir}/' to '${work_dir}/mnt/airootfs/'..."
    cp -aT -- "${pacstrap_dir}/" "${work_dir}/mnt/airootfs/"
190
    chown -- 0:0 "${work_dir}/mnt/airootfs/"
191
    _msg_info "Done!"
192
    _umount_airootfs
193
    install -d -m 0755 -- "${isofs_dir}/${install_dir}/${arch}"
194
    _msg_info "Creating SquashFS image, this may take some time..."
195
    _run_mksquashfs "${pacstrap_dir}.img"
196
    _msg_info "Done!"
197
    rm -- "${pacstrap_dir}.img"
198
199
}

200
# Makes a SquashFS filesystem from a source directory.
201
_mkairootfs_squashfs() {
202
    [[ -e "${pacstrap_dir}" ]] || _msg_error "The path '${pacstrap_dir}' does not exist" 1
203

204
    install -d -m 0755 -- "${isofs_dir}/${install_dir}/${arch}"
205
    _msg_info "Creating SquashFS image, this may take some time..."
206
    _run_mksquashfs "${pacstrap_dir}"
nl6720's avatar
nl6720 committed
207
208
209
210
211
}

# Makes an EROFS file system from a source directory.
_mkairootfs_erofs() {
    local fsuuid
212
    [[ -e "${pacstrap_dir}" ]] || _msg_error "The path '${pacstrap_dir}' does not exist" 1
nl6720's avatar
nl6720 committed
213
214
215
216
217
218

    install -d -m 0755 -- "${isofs_dir}/${install_dir}/${arch}"
    local image_path="${isofs_dir}/${install_dir}/${arch}/airootfs.erofs"
    # Generate reproducible file system UUID from SOURCE_DATE_EPOCH
    fsuuid="$(uuidgen --sha1 --namespace 93a870ff-8565-4cf3-a67b-f47299271a96 --name "${SOURCE_DATE_EPOCH}")"
    _msg_info "Creating EROFS image, this may take some time..."
219
    mkfs.erofs -U "${fsuuid}" "${airootfs_image_tool_options[@]}" -- "${image_path}" "${pacstrap_dir}"
220
221
222
    _msg_info "Done!"
}

223
_mkchecksum() {
224
    _msg_info "Creating checksum file for self-test..."
225
    cd -- "${isofs_dir}/${install_dir}/${arch}"
nl6720's avatar
nl6720 committed
226
227
228
229
230
    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
    fi
231
    cd -- "${OLDPWD}"
232
    _msg_info "Done!"
233
234
}

235
_mksignature() {
236
    _msg_info "Signing rootfs image..."
237
    cd -- "${isofs_dir}/${install_dir}/${arch}"
238
    # always use the .sig file extension, as that is what mkinitcpio-archiso's hooks expect
nl6720's avatar
nl6720 committed
239
    if [[ -e "${isofs_dir}/${install_dir}/${arch}/airootfs.sfs" ]]; then
240
        gpg --output airootfs.sfs.sig --detach-sign --default-key "${gpg_key}" airootfs.sfs
nl6720's avatar
nl6720 committed
241
    elif [[ -e "${isofs_dir}/${install_dir}/${arch}/airootfs.erofs" ]]; then
242
        gpg --output airootfs.erofs.sig --detach-sign --default-key "${gpg_key}" airootfs.erofs
nl6720's avatar
nl6720 committed
243
    fi
244
    cd -- "${OLDPWD}"
245
246
247
    _msg_info "Done!"
}

248
249
# Helper function to run functions only one time.
_run_once() {
250
    if [[ ! -e "${work_dir}/${run_once_mode}.${1}" ]]; then
251
        "$1"
252
        touch "${work_dir}/${run_once_mode}.${1}"
253
254
    fi
}
255

256
# Set up custom pacman.conf with custom cache and pacman hook directories
257
_make_pacman_conf() {
258
259
260
261
262
263
264
265
266
267
268
269
270
271
    local _cache_dirs _system_cache_dirs _profile_cache_dirs
    _system_cache_dirs="$(pacman-conf CacheDir| tr '\n' ' ')"
    _profile_cache_dirs="$(pacman-conf --config "${pacman_conf}" CacheDir| tr '\n' ' ')"

    # only use the profile's CacheDir, if it is not the default and not the same as the system cache dir
    if [[ "${_profile_cache_dirs}" != "/var/cache/pacman/pkg" ]] && \
        [[ "${_system_cache_dirs}" != "${_profile_cache_dirs}" ]]; then
        _cache_dirs="${_profile_cache_dirs}"
    else
        _cache_dirs="${_system_cache_dirs}"
    fi

    _msg_info "Copying custom pacman.conf to work directory..."
    _msg_info "Using pacman CacheDir: ${_cache_dirs}"
272
    # take the profile pacman.conf and strip all settings that would break in chroot when using pacman -r
273
274
    # append CacheDir and HookDir to [options] section
    # HookDir is *always* set to the airootfs' override directory
275
276
277
    # 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}
278
        /\[options\]/a HookDir = ${pacstrap_dir}/etc/pacman.d/hooks/" > "${work_dir}/${buildmode}.pacman.conf"
279
280
281
282
283
}

# Prepare working directory and copy custom airootfs files (airootfs)
_make_custom_airootfs() {
    local passwd=()
284
    local filename permissions
285

286
    install -d -m 0755 -o 0 -g 0 -- "${pacstrap_dir}"
287

288
    if [[ -d "${profile}/airootfs" ]]; then
289
        _msg_info "Copying custom airootfs files..."
290
        cp -af --no-preserve=ownership,mode -- "${profile}/airootfs/." "${pacstrap_dir}"
291
292
293
        # Set ownership and mode for files and directories
        for filename in "${!file_permissions[@]}"; do
            IFS=':' read -ra permissions <<< "${file_permissions["${filename}"]}"
294
295
296
            # Prevent file path traversal outside of $pacstrap_dir
            if [[ "$(realpath -q -- "${pacstrap_dir}${filename}")" != "${pacstrap_dir}"* ]]; then
                _msg_error "Failed to set permissions on '${pacstrap_dir}${filename}'. Outside of valid path." 1
297
            # Warn if the file does not exist
298
299
            elif [[ ! -e "${pacstrap_dir}${filename}" ]]; then
                _msg_warning "Cannot change permissions of '${pacstrap_dir}${filename}'. The file or directory does not exist."
300
            else
301
                if [[ "${filename: -1}" == "/" ]]; then
302
303
                    chown -fhR -- "${permissions[0]}:${permissions[1]}" "${pacstrap_dir}${filename}"
                    chmod -fR -- "${permissions[2]}" "${pacstrap_dir}${filename}"
304
                else
305
306
                    chown -fh -- "${permissions[0]}:${permissions[1]}" "${pacstrap_dir}${filename}"
                    chmod -f -- "${permissions[2]}" "${pacstrap_dir}${filename}"
307
                fi
308
309
            fi
        done
310
        _msg_info "Done!"
311
312
313
    fi
}

314
# Install desired packages to airootfs
315
_make_packages() {
316
    _msg_info "Installing packages to '${pacstrap_dir}/'..."
317

318
319
320
321
    if [[ -n "${gpg_key}" ]]; then
        exec {ARCHISO_GNUPG_FD}<>"${work_dir}/pubkey.gpg"
        export ARCHISO_GNUPG_FD
    fi
322
323

    if [[ "${quiet}" = "y" ]]; then
324
        pacstrap -C "${work_dir}/${buildmode}.pacman.conf" -c -G -M -- "${pacstrap_dir}" "${buildmode_pkg_list[@]}" &> /dev/null
325
    else
326
        pacstrap -C "${work_dir}/${buildmode}.pacman.conf" -c -G -M -- "${pacstrap_dir}" "${buildmode_pkg_list[@]}"
327
328
    fi

329
330
331
332
    if [[ -n "${gpg_key}" ]]; then
        exec {ARCHISO_GNUPG_FD}<&-
        unset ARCHISO_GNUPG_FD
    fi
333
334

    _msg_info "Done! Packages installed successfully."
335
336
337
338
339
}

# Customize installation (airootfs)
_make_customize_airootfs() {
    local passwd=()
340

341
    if [[ -e "${profile}/airootfs/etc/passwd" ]]; then
342
        _msg_info "Copying /etc/skel/* to user homes..."
343
        while IFS=':' read -a passwd -r; do
344
            # Only operate on UIDs in range 1000–59999
345
            (( passwd[2] >= 1000 && passwd[2] < 60000 )) || continue
346
            # Skip invalid home directories
347
348
            [[ "${passwd[5]}" == '/' ]] && continue
            [[ -z "${passwd[5]}" ]] && continue
349
350
351
352
            # Prevent path traversal outside of $pacstrap_dir
            if [[ "$(realpath -q -- "${pacstrap_dir}${passwd[5]}")" == "${pacstrap_dir}"* ]]; then
                if [[ ! -d "${pacstrap_dir}${passwd[5]}" ]]; then
                    install -d -m 0750 -o "${passwd[2]}" -g "${passwd[3]}" -- "${pacstrap_dir}${passwd[5]}"
353
                fi
354
355
356
                cp -dnRT --preserve=mode,timestamps,links -- "${pacstrap_dir}/etc/skel/." "${pacstrap_dir}${passwd[5]}"
                chmod -f 0750 -- "${pacstrap_dir}${passwd[5]}"
                chown -hR -- "${passwd[2]}:${passwd[3]}" "${pacstrap_dir}${passwd[5]}"
357
            else
358
                _msg_error "Failed to set permissions on '${pacstrap_dir}${passwd[5]}'. Outside of valid path." 1
359
            fi
360
        done < "${profile}/airootfs/etc/passwd"
361
        _msg_info "Done!"
362
363
    fi

364
365
    if [[ -e "${pacstrap_dir}/root/customize_airootfs.sh" ]]; then
        _msg_info "Running customize_airootfs.sh in '${pacstrap_dir}' chroot..."
366
        _msg_warning "customize_airootfs.sh is deprecated! Support for it will be removed in a future archiso version."
367
368
369
        chmod -f -- +x "${pacstrap_dir}/root/customize_airootfs.sh"
        eval -- arch-chroot "${pacstrap_dir}" "/root/customize_airootfs.sh"
        rm -- "${pacstrap_dir}/root/customize_airootfs.sh"
370
        _msg_info "Done! customize_airootfs.sh run successfully."
371
372
373
    fi
}

374
375
376
377
# Set up boot loaders
_make_bootmodes() {
    local bootmode
    for bootmode in "${bootmodes[@]}"; do
378
        _run_once "_make_bootmode_${bootmode}"
379
380
381
    done
}

382
# Prepare kernel/initramfs ${install_dir}/boot/
383
_make_boot_on_iso9660() {
384
    local ucode_image
Christian Hesse's avatar
Christian Hesse committed
385
    _msg_info "Preparing kernel and initramfs for the ISO 9660 file system..."
386
    install -d -m 0755 -- "${isofs_dir}/${install_dir}/boot/${arch}"
387
388
    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}/"
389
390

    for ucode_image in {intel-uc.img,intel-ucode.img,amd-uc.img,amd-ucode.img,early_ucode.cpio,microcode.cpio}; do
391
392
393
        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
394
                install -d -m 0755 -- "${isofs_dir}/${install_dir}/boot/licenses/${ucode_image%.*}/"
395
                install -m 0644 -- "${pacstrap_dir}/usr/share/licenses/${ucode_image%.*}/"* \
396
397
398
399
                    "${isofs_dir}/${install_dir}/boot/licenses/${ucode_image%.*}/"
            fi
        fi
    done
400
    _msg_info "Done!"
401
402
}

nl6720's avatar
nl6720 committed
403
# Prepare /syslinux for booting from MBR
404
_make_bootmode_bios.syslinux.mbr() {
405
    _msg_info "Setting up SYSLINUX for BIOS booting from a disk..."
nl6720's avatar
nl6720 committed
406
    install -d -m 0755 -- "${isofs_dir}/syslinux"
407
408
    for _cfg in "${profile}/syslinux/"*.cfg; do
        sed "s|%ARCHISO_LABEL%|${iso_label}|g;
409
410
             s|%INSTALL_DIR%|${install_dir}|g;
             s|%ARCH%|${arch}|g" \
nl6720's avatar
nl6720 committed
411
             "${_cfg}" > "${isofs_dir}/syslinux/${_cfg##*/}"
412
    done
413
    if [[ -e "${profile}/syslinux/splash.png" ]]; then
nl6720's avatar
nl6720 committed
414
        install -m 0644 -- "${profile}/syslinux/splash.png" "${isofs_dir}/syslinux/"
415
    fi
416
417
418
    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/"
419

420
    _run_once _make_boot_on_iso9660
421

nl6720's avatar
nl6720 committed
422
423
    if [[ -e "${isofs_dir}/syslinux/hdt.c32" ]]; then
        install -d -m 0755 -- "${isofs_dir}/syslinux/hdt"
424
425
        if [[ -e "${pacstrap_dir}/usr/share/hwdata/pci.ids" ]]; then
            gzip -cn9 "${pacstrap_dir}/usr/share/hwdata/pci.ids" > \
nl6720's avatar
nl6720 committed
426
                "${isofs_dir}/syslinux/hdt/pciids.gz"
427
        fi
428
        find "${pacstrap_dir}/usr/lib/modules" -name 'modules.alias' -print -exec gzip -cn9 '{}' ';' -quit > \
nl6720's avatar
nl6720 committed
429
            "${isofs_dir}/syslinux/hdt/modalias.gz"
430
    fi
431
432

    # Add other aditional/extra files to ${install_dir}/boot/
433
    if [[ -e "${pacstrap_dir}/boot/memtest86+/memtest.bin" ]]; then
434
        # rename for PXE: https://wiki.archlinux.org/index.php/Syslinux#Using_memtest
435
        install -m 0644 -- "${pacstrap_dir}/boot/memtest86+/memtest.bin" "${isofs_dir}/${install_dir}/boot/memtest"
436
        install -d -m 0755 -- "${isofs_dir}/${install_dir}/boot/licenses/memtest86+/"
437
        install -m 0644 -- "${pacstrap_dir}/usr/share/licenses/common/GPL2/license.txt" \
438
439
            "${isofs_dir}/${install_dir}/boot/licenses/memtest86+/"
    fi
440
    _msg_info "Done! SYSLINUX set up for BIOS booting from a disk successfully."
441
442
}

nl6720's avatar
nl6720 committed
443
# Prepare /syslinux for El-Torito booting
444
_make_bootmode_bios.syslinux.eltorito() {
445
    _msg_info "Setting up SYSLINUX for BIOS booting from an optical disc..."
nl6720's avatar
nl6720 committed
446
    install -d -m 0755 -- "${isofs_dir}/syslinux"
447
448
    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/"
449

nl6720's avatar
nl6720 committed
450
    # ISOLINUX and SYSLINUX installation is shared
451
    _run_once _make_bootmode_bios.syslinux.mbr
452
453

    _msg_info "Done! SYSLINUX set up for BIOS booting from an optical disc successfully."
454
455
456
}

# Prepare /EFI on ISO-9660
457
_make_efi_dir_on_iso9660() {
458
459
    _msg_info "Preparing an /EFI directory for the ISO 9660 file system..."
    install -d -m 0755 -- "${isofs_dir}/EFI/BOOT"
460
    install -m 0644 -- "${pacstrap_dir}/usr/lib/systemd/boot/efi/systemd-bootx64.efi" \
461
462
        "${isofs_dir}/EFI/BOOT/BOOTx64.EFI"

463
    install -d -m 0755 -- "${isofs_dir}/loader/entries"
464
465
    install -m 0644 -- "${profile}/efiboot/loader/loader.conf" "${isofs_dir}/loader/"

466
467
468
469
470
471
    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
472
473
474

    # edk2-shell based UEFI shell
    # shellx64.efi is picked up automatically when on /
475
476
    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"
477
    fi
478
    _msg_info "Done!"
479
480
}

481
482
# Prepare kernel/initramfs on efiboot.img
_make_boot_on_fat() {
483
    local ucode_image all_ucode_images=()
Christian Hesse's avatar
Christian Hesse committed
484
    _msg_info "Preparing kernel and initramfs for the FAT file system..."
485
    mmd -i "${work_dir}/efiboot.img" \
486
        "::/${install_dir}" "::/${install_dir}/boot" "::/${install_dir}/boot/${arch}"
487
488
    mcopy -i "${work_dir}/efiboot.img" "${pacstrap_dir}/boot/vmlinuz-"* \
        "${pacstrap_dir}/boot/initramfs-"*".img" "::/${install_dir}/boot/${arch}/"
489
    for ucode_image in \
490
        "${pacstrap_dir}/boot/"{intel-uc.img,intel-ucode.img,amd-uc.img,amd-ucode.img,early_ucode.cpio,microcode.cpio}
491
492
493
494
495
496
    do
        if [[ -e "${ucode_image}" ]]; then
            all_ucode_images+=("${ucode_image}")
        fi
    done
    if (( ${#all_ucode_images[@]} )); then
497
        mcopy -i "${work_dir}/efiboot.img" "${all_ucode_images[@]}" "::/${install_dir}/boot/"
498
    fi
499
    _msg_info "Done!"
500
501
502
}

# Prepare efiboot.img::/EFI for EFI boot mode
503
_make_bootmode_uefi-x64.systemd-boot.esp() {
504
    local efiboot_imgsize="0"
505
    _msg_info "Setting up systemd-boot for UEFI booting..."
506

507
508
    # the required image size in KiB (rounded up to the next full MiB with an additional MiB for reserved sectors)
    efiboot_imgsize="$(du -bc \
509
510
        "${pacstrap_dir}/usr/lib/systemd/boot/efi/systemd-bootx64.efi" \
        "${pacstrap_dir}/usr/share/edk2-shell/x64/Shell_Full.efi" \
511
        "${profile}/efiboot/" \
512
513
514
        "${pacstrap_dir}/boot/vmlinuz-"* \
        "${pacstrap_dir}/boot/initramfs-"*".img" \
        "${pacstrap_dir}/boot/"{intel-uc.img,intel-ucode.img,amd-uc.img,amd-ucode.img,early_ucode.cpio,microcode.cpio} \
515
516
517
518
519
        2>/dev/null | awk 'function ceil(x){return int(x)+(x>int(x))}
            function byte_to_kib(x){return x/1024}
            function mib_to_kib(x){return x*1024}
            END {print mib_to_kib(ceil((byte_to_kib($1)+1024)/1024))}'
        )"
520
521
    # 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
522
    [[ -e "${work_dir}/efiboot.img" ]] && rm -f -- "${work_dir}/efiboot.img"
523
    _msg_info "Creating FAT image of size: ${efiboot_imgsize} KiB..."
524
    mkfs.fat -C -n ARCHISO_EFI "${work_dir}/efiboot.img" "$efiboot_imgsize"
525

526
527
    mmd -i "${work_dir}/efiboot.img" ::/EFI ::/EFI/BOOT
    mcopy -i "${work_dir}/efiboot.img" \
528
        "${pacstrap_dir}/usr/lib/systemd/boot/efi/systemd-bootx64.efi" ::/EFI/BOOT/BOOTx64.EFI
529

530
531
    mmd -i "${work_dir}/efiboot.img" ::/loader ::/loader/entries
    mcopy -i "${work_dir}/efiboot.img" "${profile}/efiboot/loader/loader.conf" ::/loader/
532
533
534
535
    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" \
536
            "${_conf}" | mcopy -i "${work_dir}/efiboot.img" - "::/loader/entries/${_conf##*/}"
537
    done
538
539

    # shellx64.efi is picked up automatically when on /
540
    if [[ -e "${pacstrap_dir}/usr/share/edk2-shell/x64/Shell_Full.efi" ]]; then
541
        mcopy -i "${work_dir}/efiboot.img" \
542
            "${pacstrap_dir}/usr/share/edk2-shell/x64/Shell_Full.efi" ::/shellx64.efi
543
544
545
    fi

    # Copy kernel and initramfs
546
    _make_boot_on_fat
547

548
    _msg_info "Done! systemd-boot set up for UEFI booting successfully."
549
550
}

551
# Prepare efiboot.img::/EFI for "El Torito" EFI boot mode
552
553
554
555
_make_bootmode_uefi-x64.systemd-boot.eltorito() {
    _run_once _make_bootmode_uefi-x64.systemd-boot.esp
    # Set up /EFI on ISO-9660 to allow preparing an installation medium by manually copying files
    _run_once _make_efi_dir_on_iso9660
556
557
}

558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
_validate_requirements_bootmode_bios.syslinux.mbr() {
    # bios.syslinux.mbr requires bios.syslinux.eltorito
    # shellcheck disable=SC2076
    if [[ ! " ${bootmodes[*]} " =~ ' bios.syslinux.eltorito ' ]]; then
        (( validation_error=validation_error+1 ))
        _msg_error "Using 'bios.syslinux.mbr' boot mode without 'bios.syslinux.eltorito' is not supported." 0
    fi

    # Check if the syslinux package is in the package list
    # shellcheck disable=SC2076
    if [[ ! " ${pkg_list[*]} " =~ ' syslinux ' ]]; then
        (( validation_error=validation_error+1 ))
        _msg_error "Validating '${bootmode}': The 'syslinux' package is missing from the package list!" 0
    fi

    # Check if syslinux configuration files exist
    if [[ ! -d "${profile}/syslinux" ]]; then
        (( validation_error=validation_error+1 ))
        _msg_error "Validating '${bootmode}': The '${profile}/syslinux' directory is missing!" 0
577
    else
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
        local cfgfile
        for cfgfile in "${profile}/syslinux/"*'.cfg'; do
            if [[ -e "${cfgfile}" ]]; then
                break
            else
                (( validation_error=validation_error+1 ))
                _msg_error "Validating '${bootmode}': No configuration file found in '${profile}/syslinux/'!" 0
            fi
        done
    fi

    # 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."
593
    fi
594
}
595

596
_validate_requirements_bootmode_bios.syslinux.eltorito() {
nl6720's avatar
nl6720 committed
597
    _validate_requirements_bootmode_bios.syslinux.mbr
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
}

_validate_requirements_bootmode_uefi-x64.systemd-boot.esp() {
    # 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 systemd-boot configuration files exist
    if [[ ! -d "${profile}/efiboot/loader/entries" ]]; then
        (( validation_error=validation_error+1 ))
        _msg_error "Validating '${bootmode}': The '${profile}/efiboot/loader/entries' directory is missing!" 0
    else
        if [[ ! -e "${profile}/efiboot/loader/loader.conf" ]]; then
            (( validation_error=validation_error+1 ))
            _msg_error "Validating '${bootmode}': File '${profile}/efiboot/loader/loader.conf' not found!" 0
        fi
        local conffile
        for conffile in "${profile}/efiboot/loader/entries/"*'.conf'; do
            if [[ -e "${conffile}" ]]; then
                break
            else
                (( validation_error=validation_error+1 ))
                _msg_error "Validating '${bootmode}': No configuration file found in '${profile}/efiboot/loader/entries/'!" 0
            fi
        done
    fi

    # Check for optional packages
    # shellcheck disable=SC2076
    if [[ ! " ${pkg_list[*]} " =~ ' edk2-shell ' ]]; then
        _msg_info "'edk2-shell' is not in the package list. The ISO will not contain a bootable UEFI shell."
    fi
}

_validate_requirements_bootmode_uefi-x64.systemd-boot.eltorito() {
    # uefi-x64.systemd-boot.eltorito has the exact same requirements as uefi-x64.systemd-boot.esp
    _validate_requirements_bootmode_uefi-x64.systemd-boot.esp
}

# Build airootfs filesystem image
_prepare_airootfs_image() {
    _run_once "_mkairootfs_${airootfs_image_type}"
647
    _mkchecksum
David Runge's avatar
David Runge committed
648
649
650
    if [[ -n "${gpg_key}" ]]; then
        _mksignature
    fi
651
652
}

653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
_validate_requirements_airootfs_image_type_squashfs() {
    if ! command -v mksquashfs &> /dev/null; then
        (( validation_error=validation_error+1 ))
        _msg_error "Validating '${airootfs_image_type}': mksquashfs is not available on this host. Install 'squashfs-tools'!" 0
    fi
}

_validate_requirements_airootfs_image_type_ext4+squashfs() {
    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_squashfs
}

nl6720's avatar
nl6720 committed
668
669
670
671
672
673
674
_validate_requirements_airootfs_image_type_erofs() {
    if ! command -v mkfs.erofs; then
        (( validation_error=validation_error+1 ))
        _msg_error "Validating '${airootfs_image_type}': mkfs.erofs is not available on this host. Install 'erofs-utils'!" 0
    fi
}

675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
_validate_requirements_buildmode_all() {
    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
    fi
    if ! command -v find &> /dev/null; then
        (( validation_error=validation_error+1 ))
        _msg_error "Validating build mode '${_buildmode}': find is not available on this host. Install 'findutils'!" 0
    fi
    if ! command -v gzip &> /dev/null; then
        (( validation_error=validation_error+1 ))
        _msg_error "Validating build mode '${_buildmode}': gzip is not available on this host. Install 'gzip'!" 0
    fi
}

_validate_requirements_buildmode_bootstrap() {
    _validate_requirements_buildmode_all
    if ! command -v bsdtar &> /dev/null; then
        (( validation_error=validation_error+1 ))
        _msg_error "Validating build mode '${_buildmode}': bsdtar is not available on this host. Install 'libarchive'!" 0
    fi
}

_validate_requirements_buildmode_iso() {
    _validate_requirements_buildmode_all
    if ! command -v awk &> /dev/null; then
        (( validation_error=validation_error+1 ))
        _msg_error "Validating build mode '${_buildmode}': awk is not available on this host. Install 'awk'!" 0
    fi
}

706
707
708
709
# SYSLINUX El Torito
_add_xorrisofs_options_bios.syslinux.eltorito() {
    xorrisofs_options+=(
        # El Torito boot image for x86 BIOS
nl6720's avatar
nl6720 committed
710
        '-eltorito-boot' 'syslinux/isolinux.bin'
711
        # El Torito boot catalog file
nl6720's avatar
nl6720 committed
712
        '-eltorito-catalog' 'syslinux/boot.cat'
713
714
715
716
717
718
719
720
        # Required options to boot with ISOLINUX
        '-no-emul-boot' '-boot-load-size' '4' '-boot-info-table'
    )
}

# SYSLINUX MBR
_add_xorrisofs_options_bios.syslinux.mbr() {
    xorrisofs_options+=(
nl6720's avatar
nl6720 committed
721
722
        # SYSLINUX MBR bootstrap code; does not work without "-eltorito-boot syslinux/isolinux.bin"
        '-isohybrid-mbr' "${isofs_dir}/syslinux/isohdpfx.bin"
723
724
        # 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
725
        # May allow booting on some systems
726
727
        # https://wiki.archlinux.org/index.php/Partitioning#Tricking_old_BIOS_into_booting_from_GPT
        '--mbr-force-bootable'
728
        # Move the first partition away from the start of the ISO to match the expectations of partition editors
729
730
731
732
733
734
735
736
737
738
739
740
        # May allow booting on some systems
        # https://dev.lovelyhq.com/libburnia/libisoburn/src/branch/master/doc/partition_offset.wiki
        '-partition_offset' '16'
    )
}

# systemd-boot in an attached EFI system partition
_add_xorrisofs_options_uefi-x64.systemd-boot.esp() {
    # 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')
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
    # 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")
    # Ensure GPT is used as some systems do not support UEFI booting without it
    # shellcheck disable=SC2076
    if [[ " ${bootmodes[*]} " =~ ' bios.syslinux.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.
        if [[ ! " ${bootmodes[*]} " =~ ' uefi-x64.systemd-boot.eltorito ' ]]; 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
        # Use valid GPT if BIOS booting support will not be required
        xorrisofs_options+=('-appended_part_as_gpt')
    fi
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
}

# systemd-boot via El Torito
_add_xorrisofs_options_uefi-x64.systemd-boot.eltorito() {
    # shellcheck disable=SC2076
    if [[ " ${bootmodes[*]} " =~ ' uefi-x64.systemd-boot.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'
        )
776
777
778
779
780
781
782
783
784
        # 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
        fi
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
    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"
        # systemd-boot 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'
        )
    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')
}

805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
# Build bootstrap image
_build_bootstrap_image() {
    local _bootstrap_parent
    _bootstrap_parent="$(dirname -- "${pacstrap_dir}")"

    [[ -d "${out_dir}" ]] || install -d -- "${out_dir}"

    cd -- "${_bootstrap_parent}"

    _msg_info "Creating bootstrap image..."
    bsdtar -cf - "root.${arch}" | gzip -cn9 > "${out_dir}/${image_name}"
    _msg_info "Done!"
    du -h -- "${out_dir}/${image_name}"
    cd -- "${OLDPWD}"
}

821
# Build ISO
822
_build_iso_image() {
823
    local xorrisofs_options=()
824
    local bootmode
825

826
827
    [[ -d "${out_dir}" ]] || install -d -- "${out_dir}"

828
    [[ "${quiet}" == "y" ]] && xorrisofs_options+=('-quiet')
829

830
831
832
833
    # 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}"
    done
834
835
836
837
838

    _msg_info "Creating ISO image..."
    xorriso -as mkisofs \
            -iso-level 3 \
            -full-iso9660-filenames \
nl6720's avatar
nl6720 committed
839
840
            -joliet \
            -joliet-long \
841
842
843
844
845
846
            -rational-rock \
            -volid "${iso_label}" \
            -appid "${iso_application}" \
            -publisher "${iso_publisher}" \
            -preparer "prepared by ${app_name}" \
            "${xorrisofs_options[@]}" \
847
            -output "${out_dir}/${image_name}" \
848
            "${isofs_dir}/"
849
    _msg_info "Done!"
850
    du -h -- "${out_dir}/${image_name}"
851
852
853
}

# Read profile's values from profiledef.sh
854
_read_profile() {
855
856
857
858
859
860
861
862
    if [[ -z "${profile}" ]]; then
        _msg_error "No profile specified!" 1
    fi
    if [[ ! -d "${profile}" ]]; then
        _msg_error "Profile '${profile}' does not exist!" 1
    elif [[ ! -e "${profile}/profiledef.sh" ]]; then
        _msg_error "Profile '${profile}' is missing 'profiledef.sh'!" 1
    else
863
864
        cd -- "${profile}"

865
866
867
868
        # Source profile's variables
        # shellcheck source=configs/releng/profiledef.sh
        . "${profile}/profiledef.sh"

869
870
871
        # Resolve paths of files that are expected to reside in the profile's directory
        [[ -n "$packages" ]] || packages="${profile}/packages.${arch}"
        packages="$(realpath -- "${packages}")"
872
873
        pacman_conf="$(realpath -- "${pacman_conf}")"

874
875
876
877
878
879
880
        # Resolve paths of files that may reside in the profile's directory
        if [[ -z "$bootstrap_packages" ]] && [[ -e "${profile}/bootstrap_packages.${arch}" ]]; then
            bootstrap_packages="${profile}/bootstrap_packages.${arch}"
            bootstrap_packages="$(realpath -- "${bootstrap_packages}")"
            pacman_conf="$(realpath -- "${pacman_conf}")"
        fi

881
        cd -- "${OLDPWD}"
882
883
    fi
}
884

885
886
# Validate set options
_validate_options() {
887
    local validation_error=0 bootmode _buildmode
888
    local pkg_list_from_file=()
889
890
    local bootstrap_pkg_list_from_file=()

891
892
893
894
895
896
    _msg_info "Validating options..."
    # Check if the package list file exists and read packages from it
    if [[ -e "${packages}" ]]; then
        mapfile -t pkg_list_from_file < <(sed '/^[[:blank:]]*#.*/d;s/#.*//;/^[[:blank:]]*$/d' "${packages}")
        pkg_list+=("${pkg_list_from_file[@]}")
        if (( ${#pkg_list_from_file} < 1 )); then
897
            (( validation_error=validation_error+1 ))
898
            _msg_error "No package specified in '${packages}'." 0
899
        fi
900
901
    else
        (( validation_error=validation_error+1 ))
902
903
        _msg_error "Packages file '${packages}' does not exist." 0
    fi
904

905
906
907
908
909
910
911
912
913
914
915
916
917
918
    # Check if packages for the bootstrap image are specified
    if [[ "${buildmodes[*]}" == *bootstrap* ]]; then
        if [[ -e "${bootstrap_packages}" ]]; then
            mapfile -t bootstrap_pkg_list_from_file < \
                <(sed '/^[[:blank:]]*#.*/d;s/#.*//;/^[[:blank:]]*$/d' "${bootstrap_packages}")
            bootstrap_pkg_list+=("${bootstrap_pkg_list_from_file[@]}")
            if (( ${#bootstrap_pkg_list_from_file} < 1 )); then
                (( validation_error=validation_error+1 ))
                _msg_error "No package specified in '${bootstrap_packages}'." 0
            fi
        else
            (( validation_error=validation_error+1 ))
            _msg_error "Bootstrap packages file '${bootstrap_packages}' does not exist." 0
        fi
919
920
921
922
923
924
925
    fi

    # Check if pacman configuration file exists
    if [[ ! -e "${pacman_conf}" ]]; then
        (( validation_error=validation_error+1 ))
        _msg_error "File '${pacman_conf}' does not exist." 0
    fi
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940

    # Check if the specified buildmodes are supported
    for _buildmode in "${buildmodes[@]}"; do
        if typeset -f "_build_buildmode_${_buildmode}" &> /dev/null; then
            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."
            fi
        else
            (( validation_error=validation_error+1 ))
            _msg_error "${_buildmode} is not a valid build mode!" 0
        fi
    done

941
942
943
944
945
    # Check if the specified bootmodes are supported
    for bootmode in "${bootmodes[@]}"; do
        if typeset -f "_make_bootmode_${bootmode}" &> /dev/null; then
            if typeset -f "_validate_requirements_bootmode_${bootmode}" &> /dev/null; then
                "_validate_requirements_bootmode_${bootmode}"
946
            else
947
                _msg_warning "Function '_validate_requirements_bootmode_${bootmode}' does not exist. Validating the requirements of '${bootmode}' boot mode will not be possible."
948
949
950
            fi
        else
            (( validation_error=validation_error+1 ))
951
            _msg_error "${bootmode} is not a valid boot mode!" 0
952
        fi
953
954
955
956
957
958
959
    done
    # Check if the specified airootfs_image_type is supported
    if typeset -f "_mkairootfs_${airootfs_image_type}" &> /dev/null; then
        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."
960
        fi
961
962
963
964
    else
        (( validation_error=validation_error+1 ))
        _msg_error "Unsupported image type: '${airootfs_image_type}'" 0
    fi
965

966
967
    if (( validation_error )); then
        _msg_error "${validation_error} errors were encountered while validating the profile. Aborting." 1
968
    fi
David Runge's avatar
David Runge committed
969
    _msg_info "Done!"
970
971
}

972
# Set defaults and, if present, overrides from mkarchiso command line option parameters
973
_set_overrides() {
974
    # Set variables that have command line overrides
975
976
977
978
    [[ ! -v override_buildmodes ]] || buildmodes=("${override_buildmodes[@]}")
    if (( "${#buildmodes[@]}" < 1 )); then
        buildmodes+=('iso')
    fi
979
980
981
982
983
984