Skip to content

Instantly share code, notes, and snippets.

@arrjay
Created August 10, 2024 19:19
Show Gist options
  • Select an option

  • Save arrjay/72f871cba40dea787a08f1c657e98fb4 to your computer and use it in GitHub Desktop.

Select an option

Save arrjay/72f871cba40dea787a08f1c657e98fb4 to your computer and use it in GitHub Desktop.
latest fs-layout from fossil
#!/usr/bin/env bash
# Created by argbash-init v2.10.0
# ARG_OPTIONAL_SINGLE([targetpath],[T],[mounted target system path],[/mnt/sysimage])
# ARG_OPTIONAL_SINGLE([lukspass],[p],[password for LUKS unlock])
# ARG_OPTIONAL_SINGLE([minsize],[m],[minimum size in bytes to consider disk for usage],[6442450944])
# ARG_OPTIONAL_BOOLEAN([data-partition],[S],[create slices for data volumes],[off])
# ARG_OPTIONAL_BOOLEAN([noop],[W],[actually run partitioning commands],[on])
# ARG_OPTIONAL_BOOLEAN([force-hfs-efi],[H],[force creating EFI partition as HFS filesystem],[off])
# ARG_OPTIONAL_BOOLEAN([stack-integrity-volumes],[I],[create integrity volumes in storage stack (SLOW)],[off])
# ARG_OPTIONAL_REPEATED([exclude],[X],[exclude device from disk usage])
# ARG_OPTIONAL_REPEATED([device],[d],[candidate device glob for partitioning],["/dev/[hsv]d[a-z]" "/dev/[hsv]d[a-z][a-z]" "/dev/xvd?" "/dev/fio?" "/dev/mmcblk[0-9]" "/dev/nvme?n?"])
# ARG_HELP([create filesystem layout for new system from discovered devices])
# ARGBASH_GO()
# needed because of Argbash --> m4_ignore([
### START OF CODE GENERATED BY Argbash v2.10.0 one line above ###
# Argbash is a bash code generator used to get arguments parsing right.
# Argbash is FREE SOFTWARE, see https://argbash.io for more info
die()
{
local _ret="${2:-1}"
test "${_PRINT_HELP:-no}" = yes && print_help >&2
echo "$1" >&2
exit "${_ret}"
}
begins_with_short_option()
{
local first_option all_short_options='TpmSWHIXdh'
first_option="${1:0:1}"
test "$all_short_options" = "${all_short_options/$first_option/}" && return 1 || return 0
}
# THE DEFAULTS INITIALIZATION - OPTIONALS
_arg_targetpath="/mnt/sysimage"
_arg_lukspass=
_arg_minsize="6442450944"
_arg_data_partition="off"
_arg_noop="on"
_arg_force_hfs_efi="off"
_arg_stack_integrity_volumes="off"
_arg_exclude=()
_arg_device=("/dev/[hsv]d[a-z]" "/dev/[hsv]d[a-z][a-z]" "/dev/xvd?" "/dev/fio?" "/dev/mmcblk[0-9]" "/dev/nvme?n?")
print_help()
{
printf '%s\n' "create filesystem layout for new system from discovered devices"
printf 'Usage: %s [-T|--targetpath <arg>] [-p|--lukspass <arg>] [-m|--minsize <arg>] [-S|--(no-)data-partition] [-W|--(no-)noop] [-H|--(no-)force-hfs-efi] [-I|--(no-)stack-integrity-volumes] [-X|--exclude <arg>] [-d|--device <arg>] [-h|--help]\n' "$0"
printf '\t%s\n' "-T, --targetpath: mounted target system path (default: '/mnt/sysimage')"
printf '\t%s\n' "-p, --lukspass: password for LUKS unlock (no default)"
printf '\t%s\n' "-m, --minsize: minimum size in bytes to consider disk for usage (default: '6442450944')"
printf '\t%s\n' "-S, --data-partition, --no-data-partition: create slices for data volumes (off by default)"
printf '\t%s\n' "-W, --noop, --no-noop: actually run partitioning commands (on by default)"
printf '\t%s\n' "-H, --force-hfs-efi, --no-force-hfs-efi: force creating EFI partition as HFS filesystem (off by default)"
printf '\t%s\n' "-I, --stack-integrity-volumes, --no-stack-integrity-volumes: create integrity volumes in storage stack (SLOW) (off by default)"
printf '\t%s\n' "-X, --exclude: exclude device from disk usage (empty by default)"
printf '\t%s' "-d, --device: candidate device glob for partitioning (default array elements:"
printf " '%s'" "/dev/[hsv]d[a-z]" "/dev/[hsv]d[a-z][a-z]" "/dev/xvd?" "/dev/fio?" "/dev/mmcblk[0-9]" "/dev/nvme?n?"
printf ')\n'
printf '\t%s\n' "-h, --help: Prints help"
}
parse_commandline()
{
while test $# -gt 0
do
_key="$1"
case "$_key" in
-T|--targetpath)
test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
_arg_targetpath="$2"
shift
;;
--targetpath=*)
_arg_targetpath="${_key##--targetpath=}"
;;
-T*)
_arg_targetpath="${_key##-T}"
;;
-p|--lukspass)
test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
_arg_lukspass="$2"
shift
;;
--lukspass=*)
_arg_lukspass="${_key##--lukspass=}"
;;
-p*)
_arg_lukspass="${_key##-p}"
;;
-m|--minsize)
test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
_arg_minsize="$2"
shift
;;
--minsize=*)
_arg_minsize="${_key##--minsize=}"
;;
-m*)
_arg_minsize="${_key##-m}"
;;
-S|--no-data-partition|--data-partition)
_arg_data_partition="on"
test "${1:0:5}" = "--no-" && _arg_data_partition="off"
;;
-S*)
_arg_data_partition="on"
_next="${_key##-S}"
if test -n "$_next" -a "$_next" != "$_key"
then
{ begins_with_short_option "$_next" && shift && set -- "-S" "-${_next}" "$@"; } || die "The short option '$_key' can't be decomposed to ${_key:0:2} and -${_key:2}, because ${_key:0:2} doesn't accept value and '-${_key:2:1}' doesn't correspond to a short option."
fi
;;
-W|--no-noop|--noop)
_arg_noop="on"
test "${1:0:5}" = "--no-" && _arg_noop="off"
;;
-W*)
_arg_noop="on"
_next="${_key##-W}"
if test -n "$_next" -a "$_next" != "$_key"
then
{ begins_with_short_option "$_next" && shift && set -- "-W" "-${_next}" "$@"; } || die "The short option '$_key' can't be decomposed to ${_key:0:2} and -${_key:2}, because ${_key:0:2} doesn't accept value and '-${_key:2:1}' doesn't correspond to a short option."
fi
;;
-H|--no-force-hfs-efi|--force-hfs-efi)
_arg_force_hfs_efi="on"
test "${1:0:5}" = "--no-" && _arg_force_hfs_efi="off"
;;
-H*)
_arg_force_hfs_efi="on"
_next="${_key##-H}"
if test -n "$_next" -a "$_next" != "$_key"
then
{ begins_with_short_option "$_next" && shift && set -- "-H" "-${_next}" "$@"; } || die "The short option '$_key' can't be decomposed to ${_key:0:2} and -${_key:2}, because ${_key:0:2} doesn't accept value and '-${_key:2:1}' doesn't correspond to a short option."
fi
;;
-I|--no-stack-integrity-volumes|--stack-integrity-volumes)
_arg_stack_integrity_volumes="on"
test "${1:0:5}" = "--no-" && _arg_stack_integrity_volumes="off"
;;
-I*)
_arg_stack_integrity_volumes="on"
_next="${_key##-I}"
if test -n "$_next" -a "$_next" != "$_key"
then
{ begins_with_short_option "$_next" && shift && set -- "-I" "-${_next}" "$@"; } || die "The short option '$_key' can't be decomposed to ${_key:0:2} and -${_key:2}, because ${_key:0:2} doesn't accept value and '-${_key:2:1}' doesn't correspond to a short option."
fi
;;
-X|--exclude)
test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
_arg_exclude+=("$2")
shift
;;
--exclude=*)
_arg_exclude+=("${_key##--exclude=}")
;;
-X*)
_arg_exclude+=("${_key##-X}")
;;
-d|--device)
test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
_arg_device+=("$2")
shift
;;
--device=*)
_arg_device+=("${_key##--device=}")
;;
-d*)
_arg_device+=("${_key##-d}")
;;
-h|--help)
print_help
exit 0
;;
-h*)
print_help
exit 0
;;
*)
_PRINT_HELP=yes die "FATAL ERROR: Got an unexpected argument '$1'" 1
;;
esac
shift
done
}
parse_commandline "$@"
# OTHER STUFF GENERATED BY Argbash
### END OF CODE GENERATED BY Argbash (sortof) ### ])
# [ <-- needed because of Argbash
set -eu
set -o pipefail
shopt -s nullglob
# stderr
infomsg () {
printf '%s\n' 1>&2 "${@}"
}
# define a cleanup handler
_cleanup() {
[[ "${_WORKDIR:-}" ]] && [[ -d "${_WORKDIR}" ]] && {
case "${_WORKDIR}" in
/) : ;;
*) rm -rf "${_WORKDIR}" ;;
esac
}
}
trap _cleanup EXIT ERR
# lsblk groveling, get a value...
get_lsblk_val () {
local blkline blk key
declare -A blkline
blk="${1}"
key="${2}"
for word in $blk ; do
if [[ $word = *"="* ]] ; then
val=${word#*=}
val=${val#'"'}
val=${val%'"'}
blkline[${word%%=*}]=${val}
fi
done
echo "${blkline[$key]:-}"
}
# guessing at lsblk devices
get_lsblk_dev () {
local dev
dev="${1}"
if [ -b "/dev/${dev}" ] ; then
dev="/dev/${dev}"
elif [ -b "/dev/mapper/${dev}" ] ; then
dev="/dev/mapper/${dev}"
fi
echo "${dev}"
}
# run dmsetup to get block device underlying a dm-synthetic device (luks, lvm)
grovel_dm() {
local blkname type dmoutput
blkname="${1}"
type="${2}"
blkname=$(get_lsblk_dev "${blkname}")
dmoutput=$(sudo dmsetup table "${blkname}")
dma=(${dmoutput})
# yes, these are magic numbers.
case "${type}" in
lvm)
echo "${dma[3]}"
;;
crypt)
echo "${dma[6]}"
;;
esac
}
# given a lsblk k/v pair, try to find the actual disk behind it
# NOTE: recursive ;)
key2disk () {
local filt blk type
filt="${1}"
blk=$(lsblk -P -o NAME,MAJ:MIN,RM,SIZE,RO,TYPE,MOUNTPOINT | grep -F "${filt}")
[[ "${blk}" ]] || return 0
type=$(get_lsblk_val "${blk}" TYPE)
case "${type}" in
lvm|crypt)
local dmblk nblk blkname nblkname
blkname=$(get_lsblk_val "${blk}" NAME)
dmblk=$(grovel_dm "${blkname}" "${type}")
nblk=$(lsblk -P | grep -F "MAJ:MIN=\"${dmblk}\"")
nblkname=$(get_lsblk_val "${nblk}" NAME)
key2disk "NAME=\"${nblkname}\""
;;
part)
local blkname disk part
blkname=$(get_lsblk_val "${blk}" NAME)
part="${blkname##*[[:alpha:]]}"
disk="${blkname%"${part}"}"
disk="${disk%p}"
key2disk "NAME=\"${disk}\""
;;
disk)
local blkname
blkname=$(get_lsblk_val "${blk}" NAME)
echo "${blkname}"
;;
esac
}
# exclude iODD/ZVME devices from disk candidate pool
return_iodds () {
local glob string ent scsiven
glob=( "${_arg_device[@]/"/dev/"/"/sys/class/block/"}" )
for string in "${glob[@]}" ; do
for ent in $(compgen -G "${string}") ; do
scsiven=""
[[ -e "${ent}/device/vendor" ]] && read -r scsiven < "${ent}/device/vendor"
case "${scsiven}" in
iODD|ZMVE) echo "${ent##*/}" ;;
esac
done
done
}
# return resolved list of candidate disks, filtering excluded ones
get_baseblocks () {
local string ent res blocks disktype queue_rotational
disktype="${1:-}"
[[ "${disktype:-}" ]] && {
case "${disktype}" in
flash|rotational) : ;;
*) "you may ask for flash or rotational disks" ; return 1 ;;
esac
}
res=()
for string in "${_arg_device[@]}" ; do
for ent in $(compgen -G "${string}") ; do
ent="${ent#/dev/}"
case " ${_arg_exclude[*]} " in
*" ${ent} "*) continue ;;
esac
blocks="$(sudo blockdev --getsize64 "/dev/${ent}" 2>/dev/null)" || true
[[ "${blocks}" ]] || continue
[[ "${blocks}" -ge "${_arg_minsize}" ]] || continue
[[ "${disktype:-}" ]] && {
[[ -e "/sys/class/block/${ent}/queue/rotational" ]] || continue
read -r queue_rotational < "/sys/class/block/${ent}/queue/rotational"
case "${disktype}" in
flash) [[ "${queue_rotational}" -eq 0 ]] || continue ;;
rotational) [[ "${queue_rotational}" -eq 1 ]] || continue ;;
esac
}
res+=("${ent}")
done
done
echo "${res[@]}"
}
# given a string (or rather, any collection of 1 or more arguments), count the words by whitespace in it.
count_words() {
local count word
count=0
# shellcheck disable=SC2034,SC2068
for word in ${@} ; do
count=$((count + 1))
done
echo "${count}"
}
# detach all io stacked on top of a disk, wipe the partitions, then the partition table
wipedisk () {
local part disk vgs vg lsline mtpt usetype arrays array lvs lv lvname submount crypts crypt caches cache name
arrays=()
disk="${1}"
case "${disk}" in
mmcblk*|nvme*) disk="${disk}p" ;;
esac
for part in "/dev/${disk}"[0-9]* ; do
arrays=() ; lvs=() ; crypts=() ; name='' ; caches=()
# stop any raid device using the partition
# unmount anything using the partition. stop any arrays using the partition.
while read -r lsline ; do
usetype=$(get_lsblk_val "${lsline}" TYPE)
mtpt=$(get_lsblk_val "${lsline}" MOUNTPOINT)
[[ "${mtpt:-}" ]] && {
for submount in $(grep " ${mtpt}" /proc/mounts | sort -k 2 -r | cut -d' ' -f 2) ; do
umount "${submount}"
done
}
case "${usetype}" in
raid*) arrays+=("$(get_lsblk_val "${lsline}" NAME)") ;;
lvm) lvs+=("$(get_lsblk_val "${lsline}" NAME)") ;;
# integrity volumes show as crypt.
crypt) crypts+=("$(get_lsblk_val "${lsline}" NAME)") ;;
# bcache shows as a disk device...
disk)
name="$(get_lsblk_val "${lsline}" NAME)"
case "${name}" in
bcache*) caches+=("${name}")
esac
;;
esac
done < <(lsblk -P -o NAME,MAJ:MIN,RM,SIZE,RO,TYPE,MOUNTPOINT "${part}")
# check for any volume groups directly using the device and stop them. it's ok to fail.
read -r -a vgs <<< "$(pvs --devices "${part}" -o vg_uuid --noheadings)" || true
[[ "${vgs[0]:-}" ]] && {
for vg in "${vgs[@]}" ; do
vgchange -a n --select vg_uuid="${vg}"
done
}
# check for any volume groups using a child of the device and stop them, too
[[ "${lvs[0]:-}" ]] && {
for lv in "${lvs[@]}" ; do
# the lvm command is just...bad
lvname="$(lvdisplay -S "lv_dm_path=/dev/mapper/${lv}" | awk '$0 ~ "VG Name" { print $3 }')"
[[ "${lvname}" ]] && vgchange -a n "${lvname}"
done
}
# shut down any LUKS devices
[[ "${crypts[0]:-}" ]] && {
for crypt in "${crypts[@]}" ; do
# skip integ devices here
case "${crypt}" in
integ-*) : ;;
*) cryptsetup remove "${crypt}" ;;
esac
done
}
# shut down any bcache devices (backing)
[[ "${caches[0]:-}" ]] && {
for cache in "${caches[@]}" ; do
# ask for a stop
printf 1 > "/sys/block/${cache}/bcache/stop"
# wait for it to go away
while [[ -e "/sys/block/${cache}" ]] ; do
sleep 1
done
done
}
# shut down any bcache devices (cache) on a raw partition.
[[ -e "/sys/class/block/${part##*/}/bcache" ]] && {
printf 1 > "/sys/class/block/${part##*/}/bcache/set/stop"
while [[ -e "/sys/class/block/${part##*/}/bcache" ]] ; do
sleep 1
done
}
[[ "${arrays[0]:-}" ]] && {
for array in "${arrays[@]}" ; do
# shut down any bcache devices on arrays here...
[[ -e "/sys/class/block/${array}/bcache" ]] && {
printf 1 > "/sys/class/block/${array}/bcache/set/stop"
while [[ -e "/sys/class/block/${array}/bcache" ]] ; do
sleep 1
done
}
# then stop the array
mdadm --stop "/dev/${array}"
done
}
# stop any integ devices now, reusing the crypt array
[[ "${crypts[0]:-}" ]] && {
for crypt in "${crypts[@]}" ; do
case "${crypt}" in
integ-*) cryptsetup remove "${crypt}" ;;
esac
done
}
wipefs -a "${part}"
done
wipefs -a "/dev/${disk%p}"
}
# this is actually what partitions disks - can set raidflags but doesn't set up RAIDs.
# it returns which device got which partition encoded here (biosboot,efiboot,sysboot,system,data)
# I use it with a while loop to create strings of partitions _to_ RAID.
partition_disk () {
# setup
local disk name total_disks
# partition alignment
local pblsz optio align chunk mult
# counters
local partition pstart
# partition chunk holders
local biosend efiend bootend syssz
partition="0"
total_disks="1"
name="${1}"
disk="/dev/${name}"
[[ "${2+x}" ]] && {
total_disks="${2}"
}
# calculate partition alignment
read -r pblsz < "/sys/class/block/${name}/queue/physical_block_size"
read -r optio < "/sys/class/block/${name}/queue/optimal_io_size"
read -r align < "/sys/class/block/${name}/alignment_offset"
chunk=$(($((optio + align)) / pblsz))
[ "${chunk}" -eq 0 ] && chunk=4096
# we use 4096 bytes as a 'base' unit for small partition calculations
mult=$((4096 / pblsz))
# partition label
parted "${disk}" mklabel gpt > /dev/null
# legacy BIOS boot partition
{
biosend=$(($((mult * 1024)) + chunk))
parted -a optimal "${disk}" mkpart biosboot "${chunk}s" "${biosend}s" && partition=$((partition + 1))
parted "${disk}" toggle "${partition}" bios_grub
parted "${disk}" toggle "${partition}" legacy_boot
} > /dev/null 2>&1
case "${name}" in
mmcblk*|nvme*) echo "biosboot=${disk}p${partition}" ;;
*) echo "biosboot=${disk}${partition}" ;;
esac
# EFI system partition
{
pstart=1 ; while [ $((pstart * chunk)) -lt "${biosend}" ] ; do pstart=$((pstart + 1)) ; done ; pstart=$((pstart + 1))
case "${_arg_force_hfs_efi}" in
on)
parted -a optimal "${disk}" mkpart '"EFI System Partition"' hfs+ "$((pstart * chunk))s" 300MB && partition=$((partition + 1))
;;
off)
parted -a optimal "${disk}" mkpart '"EFI System Partition"' "$((pstart * chunk))s" 300MB && partition=$((partition + 1))
parted "${disk}" toggle "${partition}" boot
;;
esac
} > /dev/null 2>&1
case "${name}" in
mmcblk*|nvme*) echo "efiboot=${disk}p${partition}" ;;
*) echo "efiboot=${disk}${partition}" ;;
esac
# /boot partition
{
efiend=$(printf 'unit s\nprint' | parted "${disk}" | awk -F' ' "\$1 == ${partition} { print \$3; }")
efiend="${efiend%s}"
pstart=1 ; while [ $((pstart * chunk)) -lt "${efiend}" ] ; do pstart=$((pstart + 1)) ; done
parted "${disk}" mkpart sysboot "$((pstart * chunk))s" 800m && partition=$((partition + 1))
if [ "${total_disks}" -gt 1 ] ; then
parted "${disk}" toggle "${partition}" raid
fi
} > /dev/null 2>&1
case "${name}" in
mmcblk*|nvme*) echo "sysboot=${disk}p${partition}" ;;
*) echo "sysboot=${disk}${partition}" ;;
esac
# system partition
if [[ "${_arg_data_partition}" == "on" ]] ; then
syssz="24g"
else
syssz="100%"
fi
# create partition...
{
bootend=$(printf 'unit s\nprint' | parted "${disk}" | awk -F' ' "\$1 == ${partition} { print \$3; }")
bootend="${bootend%s}"
pstart=1 ; while [ $((pstart * chunk)) -lt "${bootend}" ] ; do pstart=$((pstart + 1)) ; done
parted "${disk}" mkpart system "$((pstart * chunk))s" "${syssz}" && partition=$((partition + 1))
if [ "${total_disks}" -gt 1 ] ; then
parted "${disk}" toggle "${partition}" raid
fi
} > /dev/null 2>&1
case "${name}" in
mmcblk*|nvme*) sysdisk="${disk}p${partition}" ;;
*) sysdisk="${disk}${partition}" ;;
esac
# now, if we are in integrity mode, do integritysetup. here.
if [[ "${_arg_stack_integrity_volumes}" == "on" ]] ; then
while [[ ! -e "${sysdisk}" ]] ; do sleep 1 ; done
sleep 1 # what? why?
integritysetup format -s 4096 --progress-frequency=1 --batch-mode "${sysdisk}"
partuuid="$(get_lsblk_val "$(blkid "${sysdisk}")" PARTUUID)"
integritysetup open "${sysdisk}" "integ-${partuuid}"
sysdisk="/dev/mapper/integ-${partuuid}"
fi
echo "system=${sysdisk}"
# data partition
if [ "${_arg_data_partition}" == "on" ] ; then
{
parted "${disk}" mkpart data 24g 100% && partition=$((partition + 1))
if [ "${total_disks}" -gt 1 ] ; then
parted "${disk}" toggle "${partition}" raid
fi
} > /dev/null 2>&1
case "${name}" in
mmcblk*|nvme*) datadisk="${disk}p${partition}" ;;
*) datadisk="${disk}${partition}" ;;
esac
# now, if we are in integrity mode, do integritysetup. here.
if [[ "${_arg_stack_integrity_volumes}" == "on" ]] ; then
while [[ ! -e "${datadisk}" ]] ; do sleep 1 ; done
sleep 1 # what? why?
integritysetup format -s 4096 --progress-frequency=1 --batch-mode "${datadisk}"
partuuid="$(get_lsblk_val "$(blkid "${datadisk}")" PARTUUID)"
integritysetup open "${datadisk}" "integ-${partuuid}"
datadisk="/dev/mapper/integ-${partuuid}"
fi
echo "data=${datadisk}"
fi
# give the kernel/udev/drive a moment to settle
sleep 1
}
# this does partitioning for cache disks. same sort of deal, but only returns 'cache' value.
partition_cache () {
local disk name total_disks partition
partition="0"
total_disks="1"
name="${1}"
disk="/dev/${name}"
[[ "${2+x}" ]] && {
total_disks="${2}"
}
# partition label
parted "${disk}" mklabel gpt > /dev/null
# cache partition
{
parted "${disk}" mkpart cache 1m 100% && partition=$((partition +1))
if [ "${total_disks}" -gt 1 ] ; then
parted "${disk}" toggle "${partition}" raid
fi
} > /dev/null
case "${name}" in
mmcblk*|nvme*) echo "cache=${disk}p${partition}" ;;
*) echo "cache=${disk}${partition}" ;;
esac
}
# create flexibly-block-aligned pv
lvm_create () {
local pv vg
pv="${2}"
vg="${1}"
wipefs -a "${pv}"
pvcreate --dataalignment 8192s "${pv}"
vgcreate -An --dataalignment 8192s "${vg}" "${pv}"
}
ready_lv () {
local lvname vgname fstyp sizeM lmount devpath mpath fs_opts fs_nos
lvname="${1}"
vgname="${2}"
fstyp="${3}"
sizeM="${4}"
lmount=""
fs_opts="defaults"
fs_nos="1 2"
case "${fstyp}" in swap) lmount="swap" ; mpath="swap" fs_nos="0 0" ;; esac
[[ "${5+x}" ]] && {
lmount="${5}" ; mpath="${_arg_targetpath}${lmount}"
case "${lmount}" in /) fs_nos="1 1" ;; esac
}
devpath="/dev/${vgname}/${lvname}"
printf '%s %s %s %s %s\n' "${devpath}" "${mpath}" "${fstyp}" "${fs_opts}" "${fs_nos}" >> "${fstab}"
lvcreate -An -Wy -y "-L${sizeM}M" "-n${lvname}" "${vgname}"
wipefs -a "${devpath}"
case "${fstyp}" in
ext4) mkfs.ext4 -I 256 "${devpath}" ;;
swap) mkswap "${devpath}" ;;
esac
}
ready_thin () {
local lvname vgname tpoolname fstyp sizeM lmount devpath mpath fs_opts fs_nos
lvname="${1}"
vgname="${2}"
tpoolname="${3}"
fstyp="${4}"
sizeM="${5}"
lmount=""
fs_opts="defaults,discard"
fs_nos="1 2"
[[ "${6+x}" ]] && { lmount="${6}" ; mpath="${_arg_targetpath}${lmount}" ; }
devpath="/dev/${vgname}/${lvname}"
printf '%s %s %s %s %s\n' "${devpath}" "${mpath}" "${fstyp}" "${fs_opts}" "${fs_nos}" >> "${fstab}"
lvcreate -An "-V${sizeM}M" "-n${lvname}" --thinpool "${tpoolname}" "${vgname}"
case "${fstyp}" in
ext4) mkfs.ext4 "${devpath}" ;;
swap) mkswap "${devpath}" ;;
esac
}
# create a md device, format it
ready_md () {
local mdname mdlevel datalevel mddev fstyp mount extra fs_opts fs_nos rname uuid
datalevel="1.0"
mdname="${1}"
mdlevel="${2}"
mddev="${3}"
fstyp="${4}"
mount="${5}"
extra=""
fs_opts="defaults"
fs_nos="1 2"
# MD_COUNTER is a global for the next time we call mdadm
[[ "${MD_COUNTER:-}" ]] || MD_COUNTER=0
MD_COUNTER=$(( MD_COUNTER + 1 ))
mpath="${_arg_targetpath}${mount}"
# efi and boot mds get 1.0 metadata, others 1.1
case "${mdname}" in
efi)
[[ "${_arg_force_hfs_efi}" == "on" ]] && { infomsg "oh no, we don't handle hfs efi partitions with raid1" ; exit 1 ; }
extra='--label="EFISP" --noformat --fsoptions="umask=0077,shortname=winnt"'
fs_opts='umask=0077,shortname=winnt'
fs_nos='0 2'
;;
boot) extra='--label="/boot"' ;;
*) datalevel="1.1" ;;
esac
# shellcheck disable=SC2086
mdadm --create "/dev/md/${mdname}" -N"${mdname}" -l"${mdlevel}" -n "$(count_words "${mddev}")" --metadata="${datalevel}" ${mddev}
wipefs -a "/dev/md/${mdname}"
case "${fstyp}" in
efi) mkfs.vfat -F32 -n EFISP "/dev/md/${mdname}" ;;
# ext2 is special cased to create with the stupidest options for bootloader-ing
# you can always tune2fs and rethink your life later.
ext2) mkfs.ext2 -O none,ext_attr,resize_inode,dir_index,filetype,sparse_super "/dev/md/${mdname}" ;;
esac
case "${fstyp}" in
lvmpv|bcache) : ;;
efi) printf '/dev/md/%s %s %s %s %s\n' "${mdname}" "${mpath}" "vfat" "${fs_opts}" "${fs_nos}" >> "${fstab}" ;;
*) printf '/dev/md/%s %s %s %s %s\n' "${mdname}" "${mpath}" "${fstyp}" "${fs_opts}" "${fs_nos}" >> "${fstab}" ;;
esac
rname=$(readlink "/dev/md/${mdname}")
rname=${rname##*/}
read -r uuid < "/sys/class/block/${rname}/md/uuid"
uuid="${uuid//-/}"
printf 'ARRAY /dev/md/%s metadata=%s UUID=%s name=%s\n' \
"${mdname}" "${datalevel}" "${uuid:0:8}:${uuid:8:8}:${uuid:16:8}:${uuid:24:8}" "${mdname}" \
>> "${scratchtree}/etc/mdadm/mdadm.conf"
sleep 1
case "${mdname}" in
data)
printf 'idle' > "/sys/class/block/${rname}/md/sync_action"
;;
esac
}
# format a partition, updating fstab if needed
ready_part () {
local partition fstyp mount extra fs_opts fs_nos mpath fsuuid efifs_type
partition="${1}"
fstyp="${2}"
mount="${3}"
extra=""
fs_opts="defaults"
fs_nos="1 2"
mpath="${_arg_targetpath}${mount}"
# always update fstab, though we may rewrite later.
case "${fstyp}" in
lvmpv|bcache) : ;;
efi)
case "${_arg_force_hfs_efi}" in
on) efifs_type="hfsplus" ;;
off) efifs_type="vfat" ;;
esac
printf '%s %s %s %s %s\n' "${partition}" "${mpath}" "${efifs_type}" "${fs_opts}" "${fs_nos}" >> "${fstab}"
;;
*)
printf '%s %s %s %s %s\n' "${partition}" "${mpath}" "${fstyp}" "${fs_opts}" "${fs_nos}" >> "${fstab}"
;;
esac
# format and update faketab
case "${fstyp}" in
lvmpv|bcache) : ;;
efi)
case "${efifs_type}" in
vfat) mkfs.vfat -F32 -n EFISP "${partition}" ;;
hfsplus) mkfs.hfsplus -v EFISP "${partition}" ;;
esac
;;
# ext2 is special cased to create with the stupidest options for bootloader-ing
# you can always tune2fs and rethink your life later.
ext2)
mkfs.ext2 -O none,ext_attr,resize_inode,dir_index,filetype,sparse_super "${partition}"
;;
*)
"mkfs.${fstyp}" "${partition}"
;;
esac
# handle efi for the next bit..
case "${fstyp}" in efi) fstyp="${efifs_type}" ;; esac
# rewrite fstab with UUID now.
# shellcheck disable=SC2015
grep -q "${partition}" "${fstab}" && {
fsuuid="$(blkid -s UUID "${partition}")"
fsuuid="${fsuuid#*UUID=\"}"
fsuuid="${fsuuid%\"}"
sed -i -e 's@^'"${partition}"'.*@UUID='"${fsuuid} ${mpath} ${fstyp} ${fs_opts} ${fs_nos}"'@' "${fstab}"
} || true
}
# walk our fstab and mkdir/mount as needed
make_n_mount () {
local pass found dev path fstyp fsopt dump chk
pass=0
found=1
while [ "${found}" -eq 1 ] ; do
found=0
# shellcheck disable=SC2034
while read -r dev path fstyp fsopt dump chk ; do
if [ "${chk}" -eq "${pass}" ] ; then
found=1
case "${fstyp}" in swap) continue ;; esac
fsopt="${fsopt#defaults,}"
fsopt="${fsopt#defaults}"
mkdir -p "${path}"
if [[ "${fsopt}" ]] ; then
mount -t "${fstyp}" -o "${fsopt}" "${dev}" "${path}"
else
mount -t "${fstyp}" "${dev}" "${path}"
fi
fi
done <<< "$(sort -k2 < "${fstab}")"
pass=$(( pass + 1 ))
done
}
# open a luks device and return what we mapped it to (/dev/mapper/luks-UUID)
luks_open () {
local linkunwind dir candidate luks_map
candidate="${1}"
dir="${candidate%/*}"
if [ -L "${candidate}" ] ; then linkunwind="$(readlink "${candidate}")" ; else linkunwind="${candidate##*/}" ; fi
luks_uuid=$(cryptsetup luksUUID "${dir}/${linkunwind}")
luks_map="luks-${luks_uuid}"
printf '%s' "${_arg_lukspass}" | cryptsetup luksOpen "${candidate}" "${luks_map}" -
echo "/dev/mapper/${luks_map}"
printf 'luks-%s UUID=%s none luks\n' "${luks_uuid}" "${luks_uuid}" >> "${scratchtree}/etc/crypttab"
}
### END OF FUNCTIONS
# get a nice temp directory.
_WORKDIR="$(env TMPDIR=/tmp mktemp -d)"
export TMPDIR="${_WORKDIR}"
# exclude the running root disk from fs-layout
_arg_exclude+=("$(key2disk 'MOUNTPOINT="/"')")
# exlude any iodds from fs-layout
iodds=()
IFS=" " read -r -a iodds <<< "$(return_iodds)"
_arg_exclude+=("${iodds[@]}")
# gather any candidate devices
all_disks=$(get_baseblocks)
disknr=$(count_words "${all_disks}")
[[ "${disknr}" -lt 1 ]] && { infomsg "no suitable disk devices discovered" ; exit 1 ; }
infomsg "considering ${disknr} disk devices - ${all_disks}"
if [[ "${disknr}" -eq 1 ]] ; then
# if we only have one disk do this
candidate_disks="${all_disks}"
elif [[ "${disknr}" -eq 2 ]] ; then
# we're going to guess raid1...
candidate_disks="${all_disks}"
elif [[ "${disknr}" -gt 2 ]] ; then
# do we have flash or spinny disks?
candidate_disks=$(get_baseblocks rotational)
flash_disks=$(get_baseblocks flash)
fi
# we want to know how many devices are going into arrays
candidate_disk_nr=$(count_words "${candidate_disks}")
flash_disk_nr=$(count_words "${flash_disks}")
infomsg "proceed?"
read -r input
case "${input}" in
y*|Y*) : ;;
*) infomsg "aborting" ; exit 1 ;;
esac
# create mixin tree
scratchtree="$(mktemp -d)"
mkdir -p "${scratchtree}/etc"/{keys,mdadm}
fstab="${scratchtree}/etc/fstab"
touch "${fstab}"
# wipe partitions now
for disk in ${candidate_disks} ${flash_disks}; do
wipedisk "${disk}"
done
# holding variables for mdadm and such
bios_bootdevs=""
efi_bootdevs=""
sys_bootdevs=""
sys_devs=""
data_devs=""
cache_devs=""
# new partition tables
for disk in ${candidate_disks} ; do
while read -r kv ; do
key="${kv%=*}" ; val="${kv#*=}"
case "${key}" in
biosboot) printf -v bios_bootdevs '%s%s ' "${bios_bootdevs}" "${val}" ;;
efiboot) printf -v efi_bootdevs '%s%s ' "${efi_bootdevs}" "${val}" ;;
sysboot) printf -v sys_bootdevs '%s%s ' "${sys_bootdevs}" "${val}" ;;
system) printf -v sys_devs '%s%s ' "${sys_devs}" "${val}" ;;
data) printf -v data_devs '%s%s ' "${data_devs}" "${val}" ;;
esac
done < <(partition_disk "${disk}" "${candidate_disk_nr}")
done
# take a moment to consider the flash drives.
for disk in ${flash_disks} ; do
while read -r kv ; do
key=${kv%=*} ;val=${kv#*=}
case ${key} in
cache) printf -v cache_devs '%s%s ' "${cache_devs}" "${val}" ;;
esac
done < <(partition_cache "${disk}" "${flash_disk_nr}")
done
# adjust raid levels depending on numbers of disks
efiboot_raid_level=1
sysboot_raid_level=1
system_raid_level=1
data_raid_level=1
cache_raid_level=1
# if we have 4 or more drives, switch to raid10/raid6 for system/data
if [ "${candidate_disk_nr}" -ge 4 ] ; then
system_raid_level=10
data_raid_level=6
fi
# create arrays/partitions
if [ "${candidate_disk_nr}" -gt 1 ] ; then
ready_md efi "${efiboot_raid_level}" "${efi_bootdevs}" "efi" "/boot/efi"
ready_md boot "${sysboot_raid_level}" "${sys_bootdevs}" "ext2" "/boot"
ready_md system "${system_raid_level}" "${sys_devs}" "lvmpv" "pv.0"
if [ "${_arg_data_partition}" == "on" ] ; then
ready_md data "${data_raid_level}" "${data_devs}" "lvmpv" "pv.1"
fi
else
for part in ${efi_bootdevs} ; do ready_part "${part}" "efi" "/boot/efi" ; done
for part in ${sys_bootdevs} ; do ready_part "${part}" "ext2" "/boot" ; done
for part in ${sys_devs} ; do ready_part "${part}" "lvmpv" "pv.0" ; done
if [ "${_arg_data_partition}" == "on" ] ; then
for part in ${data_devs} ; do ready_part "${part}" "lvmpv" "pv.1" ; done
fi
fi
# we will create a bcache setup _if_ we are using the data partition
[[ "${_arg_data_partition}" == "on" ]] && {
# partitions/arrays
if [[ "${flash_disk_nr}" -gt 1 ]] ; then
ready_md cache "${cache_raid_level}" "${cache_devs}" "bcache" "bcache0"
elif [[ "${flash_disk_nr}" -eq 1 ]] ; then
for part in ${cache_devs} ; do
ready_part "${part}" "bcache" "bcache0"
done
fi
# create bcache device if we have flash disks
bcache_backing=""
bcache_cache=""
# we need the backing device for bcache
if [[ "${candidate_disk_nr}" -gt 1 ]] ; then
bcache_backing=/dev/md/data
else
for part in ${data_devs} ; do
bcache_backing="${part}"
done
fi
# determine the cache device here
if [[ "${flash_disk_nr}" -gt 1 ]] ; then
bcache_cache=/dev/md/cache
bcache_bdev="$(readlink "${bcache_cache}")"
bcache_bdev="${bcache_bdev##*/}"
else
for part in ${cache_devs} ; do
bcache_cache="${part}"
bcache_bdev="${part##*/}"
done
fi
# create the bcache setup if we filled out a cache device
[[ "${bcache_cache}" ]] && {
# devices
make-bcache --data-offset 161280k --block 4k --bucket 4M -B "${bcache_backing}"
make-bcache --block 4k --bucket 4M -C "${bcache_cache}"
# now, we need to get the uuid off the cache device and bind it to the backing one.
cacheuuid="$(bcache-super-show "${bcache_cache}" | awk '$1 ~ "cset.uuid" { print $2 }')"
# wait for backing device to become attachable
while [[ ! -f /sys/block/bcache0/bcache/attach ]] ; do sleep 1 ; done
# wait for cache device to become attachable
while [[ ! -e "/sys/block/${bcache_bdev}/bcache" ]] ; do sleep 1 ; done
# plug it in
echo "${cacheuuid}" > /sys/block/bcache0/bcache/attach
# configure the cache mode
echo "writeback" > /sys/block/bcache0/bcache/cache_mode
}
}
# if we're asked to encrypt do that here...
data_luks_source=""
sys_luks_source=""
if [ "${_arg_data_partition}" == "on" ] ; then
if [ -b /dev/bcache0 ] ; then
data_luks_source="/dev/bcache0"
elif [ -e /dev/md/data ] ; then
data_luks_source=$(readlink /dev/md/data)
data_luks_source="/dev/md/${data_luks_source}"
else
for part in ${data_devs} ; do data_luks_source="${part}" ; done
fi
fi
if [ -e /dev/md/system ] ; then
sys_luks_source="/dev/md/system"
else
for part in ${sys_devs} ; do sys_luks_source="${part}" ; done
fi
if [[ "${_arg_lukspass}" ]] ; then
# set up encryption via cryptsetup
# anaconda sets the aes-xts-plain64 cipher out of the box. no bets on the rest tho. YOLO.
luksopts=("-c" "aes-xts-plain64" "-s" "512" "-h" "sha256" "-i" "8000" "--align-payload=8192")
{
printf '%s' "${_arg_lukspass}" | cryptsetup luksFormat "${luksopts[@]}" "${sys_luks_source}"
if [[ "${data_luks_source}" ]] ; then
printf '%s' "${_arg_lukspass}" | cryptsetup luksFormat "${luksopts[@]}" "${data_luks_source}"
fi
}
fi
# create system volume group
system_pv=""
if [[ "${_arg_lukspass}" ]] ; then
# we have a cryptodev, open it and get the uuid
system_pv="$(luks_open "${sys_luks_source}")"
else
# handle array case
if [[ -e /dev/md/system ]] ; then
system_pv=/dev/md/system
else
for part in ${sys_devs} ; do system_pv="${part}" ; done
fi
fi
lvm_create system "${system_pv}"
[[ "${_arg_data_partition}" == "on" ]] && {
# data
data_pv=""
if [[ "${_arg_lukspass}" ]] ; then
data_pv="$(luks_open "${data_luks_source}")"
else
if [[ -e /dev/bcache0 ]] ; then
data_pv=/dev/bcache0
elif [[ -e /dev/md/data ]] ; then
data_pv=/dev/md/data
else
for part in ${data_devs} ; do data_pv="${part}" ; done
fi
fi
lvm_create data "${data_pv}"
# create a thinpool on it
lvcreate -An -l100%FREE --type thin-pool --thinpool thinpool data
# udev/kernel sync
sleep 1
}
# at this point we've virtualized away block devices :) go forth and create logical volumes!
# lvname vgname fstype sizeM (lmount)
ready_lv swap system swap 512
ready_lv root system ext4 4096 /
[[ "${_arg_data_partition}" == "on" ]] && {
ready_thin var data thinpool ext4 8192 /var
}
# mount errything, making needed parent fs directories as we go.
make_n_mount
# copy scratchtree to new system
tar cf - -C "${scratchtree}" . | tar xf - -C "${_arg_targetpath}"
# edit fstab on new system
sed -e 's@'"${_arg_targetpath}"'@@g' -i "${_arg_targetpath}/etc/fstab"
# if we set up luks, rekey the data pv now.
[[ "${_arg_data_partition}" == "on" ]] && [[ "${_arg_lukspass}" ]] && {
dd if=/dev/random of="${_arg_targetpath}/etc/keys/datavol.luks" bs=1 count=32
printf '%s' "${_arg_lukspass}" | cryptsetup luksAddKey "${data_luks_source}" "${_arg_targetpath}/etc/keys/datavol.luks" -
printf '%s' "${_arg_lukspass}" | cryptsetup luksRemoveKey "${data_luks_source}"
}
mkdir -p "${_arg_targetpath}/root"
printf 'BIOS_BOOTDEVS="%s"\n' "${bios_bootdevs}" > "${_arg_targetpath}/root/fs-env"
# ^^^ TERMINATE YOUR CODE BEFORE THE BOTTOM ARGBASH MARKER ^^^
# ] <-- needed because of Argbash
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment