diff options
Diffstat (limited to 'installer/lib')
| -rw-r--r-- | installer/lib/btrfs.sh | 69 | ||||
| -rw-r--r-- | installer/lib/common.sh | 41 | ||||
| -rw-r--r-- | installer/lib/config.sh | 22 |
3 files changed, 90 insertions, 42 deletions
diff --git a/installer/lib/btrfs.sh b/installer/lib/btrfs.sh index 0a34be0..67c96a0 100644 --- a/installer/lib/btrfs.sh +++ b/installer/lib/btrfs.sh @@ -340,6 +340,36 @@ create_btrfs_subvolumes() { # Btrfs Mount Functions ############################# +# Compose the mount-option string for a single subvolume: the shared +# BTRFS_OPTS prefixed with subvol=<name>, then the per-subvol extra +# flags applied. compress=no and nodatacow both drop the default +# compress=zstd; nodatacow also appends nodatacow; nosuid appends +# nosuid,nodev. Pure string transform — no I/O. Shared by +# mount_btrfs_subvolumes and generate_btrfs_fstab so the two stay in sync. +# Usage: parse_btrfs_subvol_opts NAME EXTRA +parse_btrfs_subvol_opts() { + local name="$1" extra="$2" + local opts="subvol=$name,$BTRFS_OPTS" + + if [[ -n "$extra" ]]; then + # compress=no: drop the default compression, don't add anything + if [[ "$extra" == *"compress=no"* ]]; then + opts=$(echo "$opts" | sed 's/,compress=zstd//') + fi + # nodatacow implies no compression (incompatible), so drop it too + if [[ "$extra" == *"nodatacow"* ]]; then + opts="$opts,nodatacow" + opts=$(echo "$opts" | sed 's/,compress=zstd//') + fi + # nosuid,nodev hardening for tmp subvolumes + if [[ "$extra" == *"nosuid"* ]]; then + opts="$opts,nosuid,nodev" + fi + fi + + echo "$opts" +} + mount_btrfs_subvolumes() { local partition="$1" @@ -356,25 +386,8 @@ mount_btrfs_subvolumes() { # Skip root, already mounted [[ "$name" == "@" ]] && continue - # Build mount options - local opts="subvol=$name,$BTRFS_OPTS" - - # Apply extra options (override defaults where specified) - if [[ -n "$extra" ]]; then - # Handle compress=no by removing compress from opts and not adding it - if [[ "$extra" == *"compress=no"* ]]; then - opts=$(echo "$opts" | sed 's/,compress=zstd//') - fi - # Handle nodatacow - if [[ "$extra" == *"nodatacow"* ]]; then - opts="$opts,nodatacow" - opts=$(echo "$opts" | sed 's/,compress=zstd//') - fi - # Handle nosuid,nodev for tmp - if [[ "$extra" == *"nosuid"* ]]; then - opts="$opts,nosuid,nodev" - fi - fi + local opts + opts=$(parse_btrfs_subvol_opts "$name" "$extra") info "Mounting $name -> $MNTPOINT$mountpoint" mkdir -p "$MNTPOINT$mountpoint" @@ -412,22 +425,8 @@ EOF for subvol_spec in "${BTRFS_SUBVOLS[@]}"; do IFS=':' read -r name mountpoint extra <<< "$subvol_spec" - # Build mount options - local opts="subvol=$name,$BTRFS_OPTS" - - # Apply extra options - if [[ -n "$extra" ]]; then - if [[ "$extra" == *"compress=no"* ]]; then - opts=$(echo "$opts" | sed 's/,compress=zstd//') - fi - if [[ "$extra" == *"nodatacow"* ]]; then - opts="$opts,nodatacow" - opts=$(echo "$opts" | sed 's/,compress=zstd//') - fi - if [[ "$extra" == *"nosuid"* ]]; then - opts="$opts,nosuid,nodev" - fi - fi + local opts + opts=$(parse_btrfs_subvol_opts "$name" "$extra") echo "UUID=$uuid $mountpoint btrfs $opts 0 0" >> $MNTPOINT/etc/fstab done diff --git a/installer/lib/common.sh b/installer/lib/common.sh index 0317034..0378756 100644 --- a/installer/lib/common.sh +++ b/installer/lib/common.sh @@ -166,6 +166,38 @@ aur_manifest_names() { awk -F'\t' 'NR>1 {print $1}' "$manifest" } +# Print the baked AUR packages that are ZFS-only tooling, one per line. The ISO +# bakes the full AUR set on every build, but these require a ZFS root: +# zfs-auto-snapshot has a hard `zfs` dependency, and zrepl is ZFS replication. +# On a non-ZFS install neither dependency exists, so installing them is at best +# pointless and at worst aborts pacstrap (zfs-auto-snapshot's unmet `zfs` dep +# fails the whole transaction). Keep in lockstep with build-aur.sh's +# aur_v1_packages: a new ZFS-only AUR package added there belongs here too. +aur_zfs_only_packages() { + printf '%s\n' \ + zfs-auto-snapshot \ + zrepl +} + +# Filter a list of AUR package names for the target filesystem, printing the +# kept names one per line in input order. On a ZFS target every package passes +# through. On any other filesystem the ZFS-only tooling (aur_zfs_only_packages) +# is dropped so it never reaches pacstrap. install_base runs the baked manifest +# names through this before appending them to the pacstrap set. +filter_aur_for_fs() { + local fs="$1"; shift + local -A drop=() + if [[ "$fs" != zfs ]]; then + local z + while IFS= read -r z; do drop["$z"]=1; done < <(aur_zfs_only_packages) + fi + local pkg + for pkg in "$@"; do + [[ -n "${drop[$pkg]:-}" ]] && continue + printf '%s\n' "$pkg" + done +} + # Remove the named repo's stanza (its [name] header and the config lines up to # the next [section] or EOF) from the pacman.conf at $2. Used to ensure the # installed target never references the baked [aur] repo, whose @@ -181,7 +213,14 @@ strip_repo_stanza() { skip { next } { print } ' "$pacman_conf" > "$tmp" - mv "$tmp" "$pacman_conf" + # Truncate-write in place rather than `mv` the temp over the target: mktemp + # creates the temp 0600, and a mv would carry that onto pacman.conf, + # clobbering its pristine 0644 and leaving the installed config root-only. + # That broke every user-level makepkg/yay ("config file /etc/pacman.conf + # could not be read: Permission denied"). Writing through the existing file + # keeps its inode and mode. + cat "$tmp" > "$pacman_conf" + rm -f "$tmp" } ############################# diff --git a/installer/lib/config.sh b/installer/lib/config.sh index 3ba2bb3..ed54e36 100644 --- a/installer/lib/config.sh +++ b/installer/lib/config.sh @@ -116,20 +116,30 @@ check_config() { validate_config() { local errors=0 - [[ -z "$HOSTNAME" ]] && { warn "HOSTNAME not set"; ((errors++)); } - [[ -z "$TIMEZONE" ]] && { warn "TIMEZONE not set"; ((errors++)); } - [[ ${#SELECTED_DISKS[@]} -eq 0 ]] && { warn "No disks selected"; ((errors++)); } - [[ -z "$ROOT_PASSWORD" ]] && { warn "ROOT_PASSWORD not set"; ((errors++)); } + [[ -z "$HOSTNAME" ]] && { warn "HOSTNAME not set"; ((++errors)); } + [[ -z "$TIMEZONE" ]] && { warn "TIMEZONE not set"; ((++errors)); } + [[ ${#SELECTED_DISKS[@]} -eq 0 ]] && { warn "No disks selected"; ((++errors)); } + [[ -z "$ROOT_PASSWORD" ]] && { warn "ROOT_PASSWORD not set"; ((++errors)); } # Validate disks exist for disk in "${SELECTED_DISKS[@]}"; do - [[ -b "$disk" ]] || { warn "Disk not found: $disk"; ((errors++)); } + [[ -b "$disk" ]] || { warn "Disk not found: $disk"; ((++errors)); } done # Validate timezone if [[ -n "$TIMEZONE" && ! -f "/usr/share/zoneinfo/$TIMEZONE" ]]; then warn "Invalid timezone: $TIMEZONE" - ((errors++)) + ((++errors)) + fi + + # Validate the RAID level against the selected disk count. The + # interactive path only offers levels valid for the count, so this + # guards the unattended config, where RAID_LEVEL is set by hand and + # can name a level the disk count can't support. raid_is_valid treats + # an empty level on a single disk (no RAID) as valid. + if ! raid_is_valid "$RAID_LEVEL" "${#SELECTED_DISKS[@]}"; then + warn "Invalid RAID_LEVEL '$RAID_LEVEL' for ${#SELECTED_DISKS[@]} disk(s)" + ((++errors)) fi if [[ $errors -gt 0 ]]; then |
