refactor, cleanup, add features to partitioning
authorIan Kelling <iank@fsf.org>
Sat, 1 Mar 2025 18:06:00 +0000 (13:06 -0500)
committerIan Kelling <iank@fsf.org>
Sat, 1 Mar 2025 18:06:00 +0000 (13:06 -0500)
fai/config/hooks/partition.DEFAULT

index 034a98ce73003a1ab3caff53c58710c6d2a84fc6..95006f14ed7bfa1b130c8a0cb25cb7dc98e94aa9 100755 (executable)
@@ -29,96 +29,139 @@ if [[ $EUID != 0 ]]; then
   exit 1
 fi
 
+usage() {
+  cat <<'EOF'
+Example of calling outside of FAI:
 
-# for calling outside of FAI.
-#
-# source /b/fai/fai-wrapper (note: this exports everything, including functions).
-# - set any appropriate classes with: fai-setclass OPT1... which sets CLASS_OPT1=true...
-#   or run eval-fai-classfile FILE.
-# - fai-setclass IANK|FSF (if not already set in 51-multiboot)
-# /a/bin/fai/fai/config/hooks/partition.DEFAULT
-#
-## ARGS (only 0 or 1 is valid). All args are for use outside of fai.
-#
-# mkroot2: for setting up the root2/boot2 luks and btrfs and tab files
-#
-# mkroot2tab: for setting up the root2/boot2 tab files, in case luks and btrfs
-# happen to already be setup.
-#
-# mktab: for generating a crypttab for the main root fs in
-# /tmp/fai. Requires DISTRO be set, see below.
-#
-# Example use in a bootstrap distro:
-# scp /b/fai/fai-wrapper /a/bin/fai/fai/config/{distro-install-common/devbyid,hooks/partition.DEFAULT} root@HOST:
-# sl root@HOST
-# . fai-wrapper # ignore error that FAI is not set, it is not needed for SPECIAL_DISK purposes.
-# fai-setclass FSF
-# export SPECIAL_DISK=/dev/sde
-# export DISTRO=trisquelecne # not actually used
-# . ./partition.DEFAULT
-#
-# ## cryptsetup wont take within a pipeline
-# mapfile -t lines < <(awk '! /swap/ {print $2,$1}' /tmp/fai/crypttab )
-# for l in "${lines[@]}"; do cryptsetup luksOpen $l; done
-#
-# # or alternatively, to avoid typing it many times:
-# read -r lukspw; for l in "${lines[@]}"; do yes "$lukspw" | cryptsetup luksOpen $l; done
+Optionally: source /b/fai/fai-wrapper (note: this exports everything, including functions) and
+set any appropriate classes with: fai-setclass OPT1... which sets CLASS_OPT1=true...
+or run eval-fai-classfile FILE.
 
-## potentially useful later:
-# sed 's#/root/keyscript,#decrypt_keyctl,#;s/$/,noauto/' /tmp/fai/crypttab >/etc/crypttab
-#
-##### environment variables:
-#
-# HOSTNAME: if demohost, we set the luks password to just
-# 'x'. Used in various other ways too.
-#
-# SPECIAL_DISK: For use outside of fai. A base disk name like
-# /dev/sdk. If set, we just cryptsetup and partition this one disk then
-# exit. It creates a single lvm pv/vg/lv called "data". If this is
-# partitioning intended to go into a root filesystem, you can modify the
-# script to change that, or run commands manually afterwards. This also
-# sets REPARTITION.
-#
-# DISTRO: if not set, derived from VOL_... variable in a switch.
-# Required except for mkroot2, mkroot2tab, or SPECIAL_DISK.
-#
-##### classes:
-#
-# FSF|IANK: what kind of partitioning we want to do. Required anytime we
-# do partitioning.
-#
-# REPARTITION: forces repartitioning even if we detect the proper amount
-# of partitions already exist.
-#
-# NOWIPE: use existing subvolumes if they exist
-#
-# REROOTFS: Don't reuse the root filesystem, even if we normally would
-#
-# ROTATIONAL: forces to install onto hdds instead of sdds. normally sdds
-# are chosen if they exist.
-#
-# PARTITION_PROMPT: command line prompt before partitioning. This is good
-# to set if we don't expect repartitioning to happen.
-#
-# ROTATIONAL: in a system with ssd and hdd, install to the hdd
-# instead of the default ssd.
-#
-# RAID0: forces raid0 filesystem. Normally with 4+ devices, we use
-# raid10.
-# RAID1: forces raid1 filesystem.
-# RAID1c3: forces raid1c3 filesystem (btrfs raid 1, 3 copies).
+/a/bin/fai/fai/config/hooks/partition.DEFAULT
+
+Example use in a bootstrap distro:
+scp /b/fai/fai-wrapper /a/bin/fai/fai/config/{distro-install-common/devbyid,hooks/partition.DEFAULT} root@HOST:
+sl root@HOST
+. fai-wrapper # ignore error that FAI is not set, it is not needed for SPECIAL_DISK purposes.
+fai-setclass FSF
+./partition.DEFAULT -s /dev/sde
+cat /tmp/fai/crypttab >>/etc/crypttab
+cat /tmp/fai/fstab >>/etc/fstab
+# possibly edit fstab and do some mkfs.btrfs
+
+# cryptsetup wont take within a pipeline
+mapfile -t lines < <(awk '! /swap/ {print $2,$1}' /tmp/fai/crypttab )
+for l in "${lines[@]}"; do cryptsetup luksOpen $l; done
+
+# or alternatively, to avoid typing it many times:
+read -r lukspw; for l in "${lines[@]}"; do yes "$lukspw" | cryptsetup luksOpen $l; done
+
+potentially useful later:
+sed 's#/root/keyscript,#decrypt_keyctl,#;s/$/,noauto/' /tmp/fai/crypttab >/etc/crypttab
+
+Options
+
+-c DISK_COUNT   Meant for use with -s, the count of disks for doing swap size calculation.
+
+-d   Create data partition instead of root partition. Meant for use with -s.
+
+-m   Create root/data partition with maximum size instead of the 1tb limit. Meant for use with -s.
+
+-s SPECIAL_DISK    For use outside of fai. A base disk name like
+/dev/sdk. If set, we just cryptsetup and partition this one disk then
+exit. It creates a single lvm pv/vg/lv called "data". If this is
+partitioning intended to go into a root filesystem, you can modify the
+script to change that, or run commands manually afterwards. This also
+sets REPARTITION.
+
+ARGS (only 0 or 1 is valid). All args are for use outside of fai.
+
+mkroot2: for setting up the root2/boot2 luks and btrfs and tab files
+
+mkroot2tab: for setting up the root2/boot2 tab files, in case luks and btrfs
+happen to already be setup.
+
+mktab: for generating a crypttab for the main root fs in
+/tmp/fai. Requires DISTRO be set, see below.
+
+
+environment variables:
+
+HOSTNAME: if demohost, we set the luks password to just
+'x'. Used in various other ways too.
+
+DISTRO: if not set, derived from VOL_... variable in a switch.
+Required except for mkroot2, mkroot2tab, or SPECIAL_DISK.
+
+classes:
+
+FSF: Use fsf partitioning logic rather than for iank systems.
+
+REPARTITION: forces repartitioning even if we detect the proper amount
+of partitions already exist.
+
+NOWIPE: use existing subvolumes if they exist
+
+REROOTFS: Don't reuse the root filesystem, even if we normally would
+
+ROTATIONAL: forces to install onto hdds instead of sdds. normally sdds
+are chosen if they exist.
+
+PARTITION_PROMPT: command line prompt before partitioning. This is good
+to set if we don't expect repartitioning to happen.
+
+ROTATIONAL: in a system with ssd and hdd, install to the hdd
+instead of the default ssd.
+
+RAID0: forces raid0 filesystem. Normally with 4+ devices, we use
+raid10.
+RAID1: forces raid1 filesystem.
+RAID1c3: forces raid1c3 filesystem (btrfs raid 1, 3 copies).
+EOF
+  exit $1
+}
+
+
+## duplicates fai-wrapper, for convenience of not needing it
+if ! type -t ifclass &>/dev/null; then
+  ifclass() {
+    local var=${1/#/CLASS_}
+    [[ $HOSTNAME == "$1" || ${!var} ]]
+  }
+fi
+
+
+##### begin command line parsing ########
+
+data_part=false
+max_size=false
+temp=$(getopt -l help hc:dms: "$@") || usage 1
+eval set -- "$temp"
+while true; do
+  case $1 in
+    -c)
+      devs_count=$2
+      # note: i haven't had a need to run with -s on a system that
+      # should have a different devs_count and boot_devs_count, so
+      # setting them the same for simplicity.
+      boot_devs_count=$2
+      shift
+      ;;
+    -d) data_part=true ;;
+    -m) max_size=true ;;
+    -s) special_disk="$2"; shift ;;
+    -h|--help) usage ;;
+    --) shift; break ;;
+    *) echo "$0: Internal error! unexpected args: $*" ; exit 1 ;;
+  esac
+  shift
+done
 
 mkroot2tab=false
 mkroot2=false
 mktab=false
+
 if (( $# >= 1 )); then
-  ## duplicates fai-wrapper, for convenience of not needing it
-  if ! type -t ifclass &>/dev/null; then
-    ifclass() {
-      local var=${1/#/CLASS_}
-      [[ $HOSTNAME == "$1" || ${!var} ]]
-    }
-  fi
 
   case $1 in
     mkroot2)
@@ -137,13 +180,11 @@ if (( $# >= 1 )); then
   esac
 fi
 
-if [[ ! $SPECIAL_DISK ]] && ! $mkroot2 && ! $mkroot2tab && ! $mktab \
-     && ! ifclass IANK && ! ifclass FSF; then
-  echo $0: error: need class IANK or FSF or SPECIAL_DISK for running in fai
-fi
+##### end command line parsing ########
+
 
 
-if [[ $SPECIAL_DISK ]]; then
+if [[ $special_disk ]]; then
   export CLASS_REPARTITION=true
 fi
 
@@ -287,9 +328,10 @@ luks-setup() {
 }
 
 mktab() {
+  fstabstd="x-systemd.device-timeout=30s,x-systemd.mount-timeout=30s"
+
   mkdir -p /tmp/fai
   dev=${boot_devs[0]}
-  fstabstd="x-systemd.device-timeout=30s,x-systemd.mount-timeout=30s"
 
   if [[ $DISTRO == *_bootstrap ]]; then
     cat > /tmp/fai/fstab <<EOF
@@ -300,45 +342,59 @@ EOF
 BOOT_DEVICE="${short_devs[@]}"
 ROOT_PARTITION=$first_boot_dev
 EOF
-  else
-    # note, fai creates the mountpoints listed here
-    cat > /tmp/fai/fstab <<EOF
+    return 0
+  fi
+
+
+  if $data_part; then
+    vg=${vgs[0]}
+    cat >/tmp/fai/crypttab <<EOF
+crypt-$vg-data /dev/$vg/data  /mnt/root/q/root/luks/iank  discard,luks
+EOF
+    cat >/tmp/fai/fstab <<EOF
+/dev/mapper/crypt-$vg-data   /mnt/dataTBD  btrfs    nofail,$fstabstd,noatime,subvolid=0$mopts  0 0
+EOF
+    return 0
+  fi
+
+  # note, fai creates the mountpoints listed here
+  cat > /tmp/fai/fstab <<EOF
 $first_root_dev  /  btrfs                   $fstabstd,noatime,subvol=root_$DISTRO$mopts  0 0
 $first_root_dev  /mnt/root  btrfs    nofail,$fstabstd,noatime,subvolid=0$mopts  0 0
 $first_boot_dev  /boot  btrfs        nofail,$fstabstd,noatime,subvol=$boot_vol  0 0
 $first_efi  /boot/efi  vfat          nofail,$fstabstd  0 0
 $first_boot_dev  /mnt/boot  btrfs    nofail,$fstabstd,noatime,subvolid=0  0 0
 EOF
-    if ! fsf; then
-      cat >> /tmp/fai/fstab <<EOF
+  if ! fsf; then
+    cat >> /tmp/fai/fstab <<EOF
 /dev/mapper/crypt-${vgs[0]}-o  /mnt/o  btrfs       nofail,$fstabstd,noatime,subvolid=0$mopts  0 0
 EOF
-    fi
-    rm -f /tmp/fai/crypttab
-    for vg in ${vgs[@]}; do
-      if ! fsf; then
-        cat >>/tmp/fai/crypttab <<EOF
+  fi
+  rm -f /tmp/fai/crypttab
+  for vg in ${vgs[@]}; do
+    if ! fsf; then
+      cat >>/tmp/fai/crypttab <<EOF
 crypt-$vg-root /dev/$vg/root  none  keyscript=/root/keyscript,discard,luks,initramfs
 crypt-$vg-o    /dev/$vg/o     none  keyscript=/root/keyscript,discard,luks,initramfs
 crypt-$vg-swap /dev/$vg/swap  /dev/urandom  swap,cipher=aes-xts-plain64,size=256,hash=ripemd160
 EOF
-      fi
-      if fsf; then
-        cat >> /tmp/fai/fstab <<EOF
+    fi
+    if fsf; then
+      cat >> /tmp/fai/fstab <<EOF
 /dev/$vg/swap  none  swap  nofail,$fstabstd,sw  0 0
 EOF
-      else
-        cat >> /tmp/fai/fstab <<EOF
+    else
+      cat >> /tmp/fai/fstab <<EOF
 /dev/mapper/crypt-$vg-swap  none  swap  nofail,$fstabstd,sw  0 0
 EOF
-      fi
-    done
+    fi
+  done
 
-    # fai would do this:
-    #BOOT_DEVICE=\${BOOT_DEVICE:-"${devs[0]}"}
+  # fai would do this:
+  #BOOT_DEVICE=\${BOOT_DEVICE:-"${devs[0]}"}
 
-    # note: swaplist seems to do nothing.
-    cat >/tmp/fai/disk_var.sh <<EOF
+  # note: swaplist seems to do nothing.
+  cat >/tmp/fai/disk_var.sh <<EOF
 BOOT_DEVICE="${short_devs[@]}"
 BOOT_PARTITION=\${BOOT_PARTITION:-$first_boot_dev}
 # ROOT_PARTITIONS is added by me, used in arch setup.
@@ -347,44 +403,43 @@ ROOT_PARTITION=\${ROOT_PARTITION:-$first_root_dev}
 SWAPLIST=\${SWAPLIST:-"${swap_devs[@]}"}
 EOF
 
-    # now example if i add big disks.
-    #     if [[ $HOSTNAME == frodo ]]; then
-    #       big_disks=(
-    #         ata-Hitachi_HDS722020ALA330_JK1121YAG7SXWS-part1
-    #         ata-Hitachi_HDS722020ALA330_JK1121YAG7SY4S-part1
-    #         ata-Hitachi_HDS723030ALA640_MK0311YHG2WUSA-part1
-    #         ata-ST4000DM000-1F2168_Z300AZ6K-part1
-    #         ata-ST6000DM001-1XY17Z_Z4D2WMZK-part1
-    #         ata-TOSHIBA_MD04ACA500_8539K4TQFS9A-part1
-    #         ata-TOSHIBA_MD04ACA500_85NAK4T2FS9A-part1
-    #         ata-TOSHIBA_MD04ACA500_9551K615FS9A-part1
-    #         ata-TOSHIBA_MD04ACA500_Y5IFK6IJFS9A-part1
-    #       )
-    #       for d in ${big_disks[@]}; do
-    #         cat >>/tmp/fai/crypttab <<EOF
-    # crypt_dev_$d  /dev/disk/by-id/$d  /mnt/root/q/root/luks/iank  discard,luks
-    # EOF
-    #       done
-    #       cat >> /tmp/fai/fstab <<EOF
-    # /dev/mapper/crypt_dev_${big_disks[0]}  /mnt/i  btrfs  nofail,$fstabstd,noatime,subvolid=0  0 0
-    # EOF
-    #     fi
-    # helpful for manually running later if these disks move hosts
-    target_etc=/etc
-    target_etc=/tmp/fai
-    if [[ $HOSTNAME == frodo ]]; then
-      cat >>$target_etc/crypttab <<EOF
+  # now example if i add big disks.
+  #     if [[ $HOSTNAME == frodo ]]; then
+  #       big_disks=(
+  #         ata-Hitachi_HDS722020ALA330_JK1121YAG7SXWS-part1
+  #         ata-Hitachi_HDS722020ALA330_JK1121YAG7SY4S-part1
+  #         ata-Hitachi_HDS723030ALA640_MK0311YHG2WUSA-part1
+  #         ata-ST4000DM000-1F2168_Z300AZ6K-part1
+  #         ata-ST6000DM001-1XY17Z_Z4D2WMZK-part1
+  #         ata-TOSHIBA_MD04ACA500_8539K4TQFS9A-part1
+  #         ata-TOSHIBA_MD04ACA500_85NAK4T2FS9A-part1
+  #         ata-TOSHIBA_MD04ACA500_9551K615FS9A-part1
+  #         ata-TOSHIBA_MD04ACA500_Y5IFK6IJFS9A-part1
+  #       )
+  #       for d in ${big_disks[@]}; do
+  #         cat >>/tmp/fai/crypttab <<EOF
+  # crypt_dev_$d  /dev/disk/by-id/$d  /mnt/root/q/root/luks/iank  discard,luks
+  # EOF
+  #       done
+  #       cat >> /tmp/fai/fstab <<EOF
+  # /dev/mapper/crypt_dev_${big_disks[0]}  /mnt/i  btrfs  nofail,$fstabstd,noatime,subvolid=0  0 0
+  # EOF
+  #     fi
+  # helpful for manually running later if these disks move hosts
+  target_etc=/etc
+  target_etc=/tmp/fai
+  if [[ $HOSTNAME == frodo ]]; then
+    cat >>$target_etc/crypttab <<EOF
 crypt_dev_ata-Samsung_SSD_870_QVO_8TB_S5VUNG0N900656V${even_bigsuf} /dev/disk/by-id/ata-Samsung_SSD_870_QVO_8TB_S5VUNG0N900656V${even_bigsuf}  /mnt/root/q/root/luks/iank  discard,luks
 crypt_dev_ata-TOSHIBA_MD04ACA500_84R2K773FS9A-part1 /dev/disk/by-id/ata-TOSHIBA_MD04ACA500_84R2K773FS9A-part1  /mnt/root/q/root/luks/iank  discard,luks
 crypt_dev_ata-ST6000DM001-1XY17Z_Z4D29EBL-part1 /dev/disk/by-id/ata-ST6000DM001-1XY17Z_Z4D29EBL-part1  /mnt/root/q/root/luks/iank  discard,luks
 EOF
-      cat >> $target_etc/fstab <<EOF
+    cat >> $target_etc/fstab <<EOF
 # r7 = root partition7. it isnt actually #7 anymore, not a great name, but whatever
 /dev/mapper/crypt_dev_ata-Samsung_SSD_870_QVO_8TB_S5VUNG0N900656V${even_bigsuf}  /mnt/r7  btrfs  nofail,$fstabstd,noatime,compress=zstd,subvolid=0  0 0
 /dev/mapper/crypt_dev_ata-TOSHIBA_MD04ACA500_84R2K773FS9A-part1  /mnt/rust1  btrfs  nofail,$fstabstd,noatime,compress=zstd,subvolid=0  0 0
 /dev/mapper/crypt_dev_ata-ST6000DM001-1XY17Z_Z4D29EBL-part1  /mnt/rust2  btrfs  nofail,$fstabstd,noatime,compress=zstd,subvolid=0  0 0
 EOF
-    fi
   fi
 }
 
@@ -433,7 +488,7 @@ EOF
 }
 
 maybe-prompt() {
-  if [[ $SPECIAL_DISK ]]; then
+  if [[ $special_disk ]]; then
     cmd_prompt+=("$*")
   else
     "$@"
@@ -555,59 +610,69 @@ if [[ $HOSTNAME == x[23] ]]; then
 fi
 
 declare -A disk_excludes
-if ! $mkroot2 && ! $mkroot2tab && ! $mktab ! ifclass USE_MOUNTED; then
-  ## ignore disks that are mounted, eg when running from fai-cd
-  while read -r l; do
-    eval "$l"
-    if [[ ! $PKNAME ]]; then
-      # shellcheck disable=SC2153 # not a misspelling
-      PKNAME="$KNAME"
-    fi
-    if [[ $MOUNTPOINT ]]; then
-      disk_excludes[$PKNAME]=true
-    fi
-  done < <(lsblk -nP -o KNAME,MOUNTPOINT,PKNAME)
-fi
 
-hdds=()
-ssds=()
-# Example output:
-# sda
-# sdb
-# sdc
-#
-# This excludes "usb". vda disk has empty tran (transport). This may need adjustment
-# for some new type we come across. cdrom has type "rom"
-for disk in $(lsblk -ndo name,type,tran | awk '$3 ~ "^(sata|nvme|sas|)$" && $2 == "disk" { print $1 }'); do
-  if [[ ${disk_excludes[$disk]} ]]; then
-    continue
+
+if [[ $special_disk ]]; then
+  short_devs=($special_disk)
+  #tmps=$(devbyid $special_disk)
+  #devs=("$tmps")
+else
+
+  if ! $mkroot2 && ! $mkroot2tab && ! $mktab ! ifclass USE_MOUNTED; then
+    ## ignore disks that are mounted, eg when running from fai-cd
+    while read -r l; do
+      eval "$l"
+      if [[ ! $PKNAME ]]; then
+        # shellcheck disable=SC2153 # not a misspelling
+        PKNAME="$KNAME"
+      fi
+      if [[ $MOUNTPOINT ]]; then
+        disk_excludes[$PKNAME]=true
+      fi
+    done < <(lsblk -nP -o KNAME,MOUNTPOINT,PKNAME)
   fi
-  case $disk in
-    # cdrom
-    sr*) continue ;;
-  esac
-  case $(cat /sys/block/$disk/queue/rotational) in
-    0)
-      ssds+=("/dev/$disk")
-      echo $0: found ssd /dev/$disk
-      ;;
-    1)
-      hdds+=("/dev/$disk")
-      echo $0: found hdd /dev/$disk
-      ;;
-    *) echo "$0: error: unknown /sys/block/$disk/queue/rotational: \
+
+  hdds=()
+  ssds=()
+  # Example output:
+  # sda
+  # sdb
+  # sdc
+  #
+  # This excludes "usb". vda disk has empty tran (transport). This may need adjustment
+  # for some new type we come across. cdrom has type "rom"
+  for disk in $(lsblk -ndo name,type,tran | awk '$3 ~ "^(sata|nvme|sas|)$" && $2 == "disk" { print $1 }'); do
+    if [[ ${disk_excludes[$disk]} ]]; then
+      continue
+    fi
+    case $disk in
+      # cdrom
+      sr*) continue ;;
+    esac
+    case $(cat /sys/block/$disk/queue/rotational) in
+      0)
+        ssds+=("/dev/$disk")
+        echo $0: found ssd /dev/$disk
+        ;;
+      1)
+        hdds+=("/dev/$disk")
+        echo $0: found hdd /dev/$disk
+        ;;
+      *) echo "$0: error: unknown /sys/block/$disk/queue/rotational: \
 $(cat $disk/queue/rotational)"; exit 1 ;;
-  esac
-done
+    esac
+  done
+
+  # Example of short_devs: (/dev/sda /dev/sdb /dev/sdc)
+  # Install all ssds, or if there are none, all hdds.
+  # Note, usb flash disks are seen as rotational, which is
+  # very odd, but convenient for ignoring them here.
+  if ! ifclass ROTATIONAL && (( ${#ssds[@]} > 0 )); then
+    read -ra short_devs<<<"${ssds[@]}"
+  else
+    read -ra short_devs<<<"${hdds[@]}"
+  fi
 
-# Example of short_devs: (/dev/sda /dev/sdb /dev/sdc)
-# Install all ssds, or if there are none, all hdds.
-# Note, usb flash disks are seen as rotational, which is
-# very odd, but convenient for ignoring them here.
-if ! ifclass ROTATIONAL && (( ${#ssds[@]} > 0 )); then
-  read -ra short_devs<<<"${ssds[@]}"
-else
-  read -ra short_devs<<<"${hdds[@]}"
 fi
 
 pvn=1
@@ -647,11 +712,7 @@ done
 
 if $partition && ifclass PARTITION_PROMPT; then
   echo "Press any key except ctrl-c to continue and partition these drives:"
-  if [[ $SPECIAL_DISK ]]; then
-    echo $SPECIAL_DISK
-  else
-    echo "  ${short_devs[*]}"
-  fi
+  echo "  ${short_devs[*]}"
   read -r
 fi
 
@@ -686,7 +747,7 @@ for short_dev in ${short_devs[@]}; do
   fi
 done
 first_root_dev=${root_devs[0]}
-if [[ ! $SPECIAL_DISK && ! ${devs[0]} ]]; then
+if [[ ! $special_disk && ! ${devs[0]} ]]; then
   echo "$0: error: failed to detect devs" >&2
   exit 1
 fi
@@ -748,24 +809,30 @@ for dev in ${devs[@]}; do
 done
 first_boot_dev=${boot_devs[0]}
 
+if [[ ! $devs_count ]]; then
+  boot_devs_count=${#boot_devs[@]}
+  devs_count=${#devs[@]}
+fi
+
 even_raid=false
-if (( ${#boot_devs[@]} == 1 )); then
+if (( boot_devs_count == 1 )); then
   raid_level=0
   raid_duplication=1
 elif ifclass RAID0; then
   raid_level=0
-  if (( ${#boot_devs[@]} >= 3 )); then
+  raid_duplication=1
+  if (( boot_devs_count >= 3 )); then
     # Default is raid1 metadata for >=2 devices, but metadata is <.5%
     # data use, so lets increase that redundancy.
     raid_metadata_arg="-m raid1c3"
   fi
-elif ifclass RAID1 || (( ${#boot_devs[@]} == 2 )); then
-  if (( ${#boot_devs[@]} == 2 )); then
+elif ifclass RAID1 || (( boot_devs_count == 2 )); then
+  if (( boot_devs_count == 2 )); then
     even_raid=true
   fi
   raid_level=1
   raid_duplication=2
-elif ifclass RAID1c3 || (( ${#boot_devs[@]} == 3 )); then
+elif ifclass RAID1c3 || (( boot_devs_count == 3 )); then
   raid_level=1c3
   raid_duplication=3
 else
@@ -804,16 +871,16 @@ else
   boot2_mib=100
 fi
 
-if [[ $SPECIAL_DISK ]]; then
+if [[ $special_disk ]]; then
   # In this case, the number of detected boot_devs may not be what we
   # actually end up using if and when we use this disk's boot partition.
   boot_part_mib=$boot_mib
 else
-  boot_part_mib=$(( boot_mib * raid_duplication / ${#boot_devs[@]} ))
+  boot_part_mib=$(( boot_mib * raid_duplication / boot_devs_count ))
 fi
 
 if zilap; then
-  boot2_part_mib=$(( boot2_mib * raid_duplication / ${#boot_devs[@]}  ))
+  boot2_part_mib=$(( boot2_mib * raid_duplication / boot_devs_count  ))
   root2_part_mib=$(( root2_mib * raid_duplication / ${#root_devs[@]}  ))
 else
   boot2_part_mib=0
@@ -857,7 +924,7 @@ if [[ ! $DISTRO ]]; then
     DISTRO=trisquelaramo
   elif ifclass VOL_ECNE; then
     DISTRO=trisquelecne
-  elif $mkroot2 || $mkroot2tab || [[ $SPECIAL_DISK ]]; then
+  elif $mkroot2 || $mkroot2tab || [[ $special_disk ]]; then
     :
   else
     echo "PARTITIONER ERROR: no distro class/var set" >&2
@@ -874,16 +941,24 @@ else
 fi
 
 
-#
-# for servers, a bit randomly picking 6gb per disk based on https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/managing_storage_devices/getting-started-with-swap_managing-storage-devices#recommended-system-swap-space_getting-started-with-swap
-# meh, i'll just do that * drives for machines with lots of ram.
 mem_kb=$(awk '$1 == "MemTotal:" {print $2}' /proc/meminfo)
+mem_mib=$(( mem_kb / 1000 ))
 mem_gb=$(( mem_kb / 1024 / 1024 ))
-if (( mem_gb < 64 )); then
-  # enable hibernate with 1.5x of ram, split per disk
-  swap_mib=$(( mem_gb * 3 / ${#devs[@]} ))
+
+
+# somewhat random based on https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html/managing_storage_devices/getting-started-with-swap_managing-storage-devices#recommended-system-swap-space_getting-started-with-swap
+if fsf; then
+  if (( mem_gb >= 64 )); then
+    swap_mib=6144
+  else
+    swap_mib=$(( mem_mib * raid_duplication / devs_count ))
+  fi
 else
-  swap_mib=6144
+  if [[ -e /sys/class/power_supply/BAT0/capacity ]]; then
+    swap_mib=$(( mem_mib * 15 * raid_duplication / ( devs_count * 10 ) ))
+  else
+    swap_mib=$(( mem_mib * raid_duplication / devs_count ))
+  fi
 fi
 
 shopt -s nullglob
@@ -908,10 +983,10 @@ fi
 
 if $partition; then
   ### begin wipefs
-  if [[ $SPECIAL_DISK ]]; then
-    lvmwipe $SPECIAL_DISK
+  if [[ $special_disk ]]; then
+    lvmwipe $special_disk
     maybe-prompt sleep 10
-    maybe-prompt wipefs --backup -a $SPECIAL_DISK*
+    maybe-prompt wipefs --backup -a $special_disk*
     echo "$0: execute the following?"
     for cmd in "${cmd_prompt[@]}"; do
       echo "$cmd"
@@ -952,10 +1027,6 @@ if $partition; then
     fi
   fi
 
-  if [[ $SPECIAL_DISK ]]; then
-    tmps=$(devbyid $SPECIAL_DISK)
-    devs=("$tmps")
-  fi
   for dev in ${devs[@]}; do
     vg=vg${dev##*/}
     vg=${vg//:/}
@@ -1004,16 +1075,24 @@ if $partition; then
       # so use fixed sizes to allow both to grow
       # 600 = uefi 512 + grubext 8 + bios grub 3 + some extra cuz this is lvm
       #root_mib=$(( disk_mib - root2_part_mib - swap_mib - boot_part_mib - boot2_part_mib - 600 ))
-      o_mib=$(( 180 * 1000 ))
+
+      # If we have extra devices, give /o some overhead so that we can
+      # remove & then add a device if needed.
+      o_factor=$devs_count
+      if (( o_factor >= 2 )); then
+        o_factor=$(( o_factor - 1 ))
+      fi
+      # note: math operations are left to right, so divide at the end.
+      o_mib=$(( 180 * 1000 * raid_duplication / o_factor ))
       # max minus o, minus a gig just for some extra space
       max_root_mib=$(( disk_mib - root2_part_mib - swap_mib - boot_part_mib - boot2_part_mib - 600 - o_mib - 1000 ))
       root_mib=$(( 1700 * 1000 )) # * 1000 to make it in gb.
-      if (( max_root_mib < root_mib )); then
+      if $max_size || (( max_root_mib < root_mib )); then
         root_mib=$max_root_mib
       fi
     fi
 
-    if [[ $SPECIAL_DISK ]] && ! fsf; then
+    if [[ $special_disk ]] && $data_part; then
       lvcreate -y -L $root_mib $vg -n data
     else
       # -L unit default mebibyte
@@ -1095,7 +1174,7 @@ if $partition; then
     # into ipxe which can't persist data, if we ever got that working.
     mkfs.ext2 $dev$grub_extsuf
 
-    if [[ $SPECIAL_DISK ]]; then
+    if [[ $special_disk ]] && $data_part; then
       if ! fsf; then
         luks-setup /dev/$vg/data crypt-$vg-data
       fi
@@ -1109,6 +1188,10 @@ if $partition; then
       luks-setup /dev/$vg/o crypt-$vg-o
     fi
 
+    if [[ $special_disk ]]; then
+      exit 0
+    fi
+
   done
   ls -la /dev/btrfs-control # this was probably for debugging...
   sleep 1
@@ -1206,5 +1289,5 @@ umount /mnt
 # initial setup of extra data fs, mounted,
 # btrfs subvol create nocow
 # chattr +C nocow
-# chown iank.iank nocow
-
+# mkdir nocow/{t,user}
+# chown iank.iank nocow/{t,user}