#!/usr/bin/bash # vim:ft=sh:fdm=marker:fmr={,} # archlabs installer library script file # this file is not meant to be run directly # sourcing this file in a non bash shell is not advised # shellcheck disable=2154,2155,2153,2046,2034 declare -Agr FS_CMDS=( [ext2]="mkfs.ext2 -q" [ext3]="mkfs.ext3 -q" [ext4]="mkfs.ext4 -q" [f2fs]="mkfs.f2fs" [jfs]="mkfs.jfs -q" [xfs]="mkfs.xfs -f" [nilfs2]="mkfs.nilfs2 -q" [ntfs]="mkfs.ntfs -q" [reiserfs]="mkfs.reiserfs -q" [vfat]="mkfs.vfat -F32" ) declare -Agr FS_OPTS=([vfat]="" [ntfs]="" [ext2]="" [ext3]="" [ext4]="discard - off dealloc - off nofail - off noacl - off relatime - off noatime - off nobarrier - off nodelalloc - off" [jfs]="discard - off errors=continue - off errors=panic - off nointegrity - off" [reiserfs]="acl - off nolog - off notail - off replayonly - off user_xattr - off" [xfs]="discard - off filestreams - off ikeep - off largeio - off noalign - off nobarrier - off norecovery - off noquota - off wsync - off" [nilfs2]="discard - off nobarrier - off errors=continue - off errors=panic - off order=relaxed - off order=strict - off norecovery - off" [f2fs]="data_flush - off disable_roll_forward - off disable_ext_identify - off discard - off fastboot - off flush_merge - off inline_xattr - off inline_data - off inline_dentry - off no_heap - off noacl - off nobarrier - off noextent_cache - off noinline_data - off norecovery - off" ) format() { infobox "$_FSTitle" "\nFormatting: $1\n\nCommand: ${FS_CMDS[$2]}\n" 0 ${FS_CMDS[$2]} $1 >/dev/null 2>$ERR echeck "${FS_CMDS[$2]} $1" } partition() { local device if [[ $# -eq 0 ]]; then select_device 'root' || return 1 device="$DEVICE" else device="$1" fi tput civis local choice if ! choice="$(menubox "$_PartTitle" "$_PartBody" 0 0 0 "$_PartShowTree" "-" "$_PartAuto" "-" \ $({ [[ $DISPLAY ]] && hash gparted >/dev/null 2>&1; } && printf "gparted -") \ "cfdisk" "-" "parted" "-" "$_PartWipe" "-")"; then return 1 fi tput civis if [[ $choice != "$_PartWipe" && $choice != "$_PartAuto" && $choice != "$_PartShowTree" ]]; then clear; tput cnorm; $choice $device elif [[ $choice == "$_PartShowTree" ]]; then msgbox "$_PrepShowDev" "\n$(lsblk -o NAME,MODEL,TYPE,FSTYPE,SIZE,MOUNTPOINT "$device")\n" partition $device elif [[ $choice == "$_PartWipe" ]]; then yesno "$_PartWipe" "$_PartBody1 $device $_PartWipeBody2" && wipe -Ifrev $device partition $device else # if auto_partition fails we need to empty the partition variables auto_partition $device || { ROOT_PART=""; BOOT_PART=""; BOOT_DEVICE=""; return 1; } fi } decr_count() { # remove a partition from the dialog list and decrement the number partitions left (( $# == 1 )) || return 1 local p="$1" PARTS="$(sed "s~${p} [0-9]*[G-M]~~; s~${p} [0-9]*\.[0-9]*[G-M]~~" <<< "$PARTS")" (( COUNT > 0 )) && (( COUNT-- )) return 0 } enable_swap() { if [[ $1 == "$MNT/swapfile" && $SWAP_SIZE ]]; then fallocate -l $SWAP_SIZE $1 2>$ERR echeck "fallocate -l $SWAP_SIZE $1" chmod 600 $1 2>$ERR echeck "chmod 600 $1" fi mkswap $1 >/dev/null 2>$ERR echeck "mkswap $1" swapon $1 >/dev/null 2>$ERR echeck "swapon $1" return 0 } device_tree() { tput civis local msg if [[ $IGNORE_DEV != "" ]]; then msg="$(lsblk -o NAME,MODEL,TYPE,FSTYPE,SIZE,MOUNTPOINT | awk "!/$IGNORE_DEV/"' && /disk|part|lvm|crypt|NAME/ {print $0}')" else msg="$(lsblk -o NAME,MODEL,TYPE,FSTYPE,SIZE,MOUNTPOINT | awk '/disk|part|lvm|crypt|NAME/ {print $0}')" fi msgbox "$_PrepShowDev" "$msg" } select_device() { local dev local msg if [[ $1 == 'boot' ]]; then msg="$_DevSelTitle for bootloader\n" else umount_dir $MNT fi if [[ $DEV_COUNT -eq 1 && $SYS_DEVS ]]; then DEVICE="$(awk '{print $1}' <<< "$SYS_DEVS")" msg="\nOnly one device available$([[ $1 == 'boot' ]] && printf " for bootloader"):" infobox "$_DevSelTitle" "$msg $DEVICE\n" 1 elif (( DEV_COUNT > 1 )); then tput civis if ! DEVICE="$(menubox "$_DevSelTitle " "${msg}$_DevSelBody" 0 0 0 $SYS_DEVS)"; then return 1 fi elif [[ $DEV_COUNT -lt 1 && $1 != 'boot' ]]; then msgbox "$_ErrTitle" "\nNo available devices to use.\n$_Exit"; die 1 fi # if the device selected was for bootloader, set the BOOT_DEVICE [[ $1 == 'boot' ]] && BOOT_DEVICE="$DEVICE" return 0 } confirm_mount() { local part="$1" local mount="$2" if [[ $mount == "$MNT" ]]; then local msg="Partition: $part\nMountpoint: / (root)" else local msg="Partition: $part\nMountpoint: ${mount#$MNT}" fi if [[ $(mount) == *"$mount"* ]]; then infobox "$_MntTitle" "$_MntSucc\n$msg\n" 1; decr_count "$part" else infobox "$_MntTitle" "$_MntFail\n$msg\n" 1; return 1 fi return 0 } check_cryptlvm() { local dev="" local part="$1" local devs="$(lsblk -lno NAME,FSTYPE,TYPE)" # Identify if $part is "crypt" (LUKS on LVM, or LUKS alone) if [[ $(lsblk -lno TYPE "$part") =~ 'crypt' ]]; then LUKS=' encrypted' LUKS_NAME="${part#/dev/mapper/}" for dev in $(awk '/lvm/ && /crypto_LUKS/ {print "/dev/mapper/"$1}' <<< "$devs" | uniq); do if grep -q "$LUKS_NAME" <<< "$(lsblk -lno NAME "$dev")"; then LUKS_DEV="$LUKS_DEV cryptdevice=$dev:$LUKS_NAME" LVM=' logical volume' break fi done for dev in $(awk '/part/ && /crypto_LUKS/ {print "/dev/"$1}' <<< "$devs" | uniq); do if grep -q "$LUKS_NAME" <<< "$(lsblk -lno NAME "$dev")"; then LUKS_UUID="$(lsblk -lno UUID,TYPE,FSTYPE "$dev" | awk '/part/ && /crypto_LUKS/ {print $1}')" LUKS_DEV="$LUKS_DEV cryptdevice=UUID=$LUKS_UUID:$LUKS_NAME" break fi done elif [[ $(lsblk -lno TYPE "$part") =~ 'lvm' ]]; then LVM=' logical volume' VOLUME_NAME="${part#/dev/mapper/}" for dev in $(awk '/crypt/ && /lvm2_member/ {print "/dev/mapper/"$1}' <<< "$devs" | uniq); do if grep -q "$VOLUME_NAME" <<< "$(lsblk -lno NAME "$dev")"; then LUKS_NAME="$(sed 's~/dev/mapper/~~g' <<< "$dev")" break fi done for dev in $(awk '/part/ && /crypto_LUKS/ {print "/dev/"$1}' <<< "$devs" | uniq); do if grep -q "$LUKS_NAME" <<< "$(lsblk -lno NAME "$dev")"; then LUKS_UUID="$(lsblk -lno UUID,TYPE,FSTYPE "$dev" | awk '/part/ && /crypto_LUKS/ {print $1}')" LUKS_DEV="$LUKS_DEV cryptdevice=UUID=$LUKS_UUID:$LUKS_NAME" LUKS=' encrypted' break fi done fi } auto_partition() { local device="$1" local size size=$(lsblk -lno SIZE $device | awk 'NR == 1 { if ($1 ~ "G") { sub(/G/, ""); print ($1 * 1000 - 512) / 1000"G" } else { sub(/M/, ""); print ($1 - 512)"M" } }') if [[ $SYS == 'BIOS' ]]; then local msg="$(sed 's|vfat/fat32|ext4|' <<< "$_PartBody2")" local table="msdos" local fs="ext4" else local msg="$_PartBody2" local table="gpt" local fs="fat32"; fi # confirm or bail yesno "$_PrepParts" "$_PartBody1 $device $msg ($size)$_PartBody3" || return 0 infobox "$_PrepParts" "\nRemoving existing partitions on $device and setting partition table to $table\n" 1 swapoff -a # in case the device was previously used for swap local dev_info dev_info="$(parted -s $device print)" # walk the partitions on the device in reverse order and delete them while read -r i; do parted -s $device rm $i >/dev/null 2>&1 done <<< "$(awk '/^ [1-9][0-9]?/ {print $1}' <<< "$dev_info" | sort -r)" if [[ $(awk '/Table:/ {print $3}' <<< "$dev_info") != "$table" ]]; then parted -s $device mklabel $table >/dev/null 2>&1 fi infobox "$_PrepParts" "\nCreating a 512M $fs boot partition.\n" 1 if [[ $SYS == "BIOS" ]]; then parted -s $device mkpart primary $fs 1MiB 513MiB >/dev/null 2>&1 else parted -s $device mkpart ESP $fs 1MiB 513MiB >/dev/null 2>&1 fi sleep 0.1 BOOT_DEVICE="$device" BOOT_PART=$(lsblk -lno NAME,TYPE $device | awk 'NR == 2 {print "/dev/"$1}') if [[ $SYS == "BIOS" ]]; then mkfs.ext4 -q $BOOT_PART >/dev/null 2>&1 else mkfs.vfat -F32 $BOOT_PART >/dev/null 2>&1 fi infobox "$_PrepParts" "\nCreating a $size ext4 root partition.\n" 0 parted -s $device mkpart primary ext4 513MiB 100% >/dev/null 2>&1 sleep 0.1 ROOT_PART=$(lsblk -lno NAME,TYPE $device | awk 'NR == 3 {print "/dev/"$1}') mkfs.ext4 -q $ROOT_PART >/dev/null 2>&1 tput civis; sleep 0.5 msgbox "$_PrepParts" "\nAuto partitioning complete.\n\n$(lsblk -o NAME,MODEL,TYPE,FSTYPE,SIZE $device)" } mount_partition() { local part="$1" local mountp="${MNT}$2" local fs="$(lsblk -lno FSTYPE $part)" mkdir -p "$mountp" if [[ ${FS_OPTS[$fs]} != "" && $part != "$BOOT_PART" ]] && select_mount_opts "$part" "$fs"; then mount -o $MNT_OPTS $part "$mountp" 2>$ERR echeck "mount -o $MNT_OPTS $part $mountp" else mount $part "$mountp" 2>$ERR echeck "mount $part $mountp" fi confirm_mount $part "$mountp" || return 1 check_cryptlvm "$part" return 0 } mount_boot_part() { if ! mount_partition "$BOOT_PART" "${BMNTS[$SYS-$BOOTLDR]}"; then src /usr/share/archlabs/installer/lib/boot.sh return 1 else SEPERATE_BOOT=true fi } find_partitions() { local str="$1" local err='' # string of partitions as /TYPE/PART SIZE if [[ $IGNORE_DEV != "" ]]; then PARTS="$(lsblk -lno TYPE,NAME,SIZE | awk "/$str/"' && !'"/$IGNORE_DEV/"' { sub(/^part/, "/dev/"); sub(/^lvm|^crypt/, "/dev/mapper/") print $1$2 " " $3 }')" else PARTS="$(lsblk -lno TYPE,NAME,SIZE | awk "/$str/"' { sub(/^part/, "/dev/") sub(/^lvm|^crypt/, "/dev/mapper/") print $1$2 " " $3 }')" fi # number of partitions total COUNT=$(wc -l <<< "$PARTS") # ensure we have enough partitions for the system and action type case $str in 'part|lvm|crypt') [[ $COUNT -eq 0 || ($SYS == 'UEFI' && $COUNT -lt 2) ]] && err="$_PartErrBody" ;; 'part|crypt') (( COUNT == 0 )) && err="$_LvmPartErrBody" ;; 'part|lvm') (( COUNT < 2 )) && err="$_LuksPartErrBody" ;; esac # if there aren't enough partitions show the error message if [[ $err ]]; then msgbox "$_ErrTitle" "$err" return 1 fi return 0 }