diff --git a/install.sh b/install.sh index 6d6070a..0336b72 100755 --- a/install.sh +++ b/install.sh @@ -118,17 +118,31 @@ read -rp "Type 'yes' to proceed: " confirm # === Shrink Windows === # Convert NIXOS_SIZE (e.g. "200G") to bytes for ntfsresize -case "$NIXOS_SIZE" in - *G|*g) nixos_bytes=$(( ${NIXOS_SIZE%[Gg]} * 1024 * 1024 * 1024 )) ;; - *M|*m) nixos_bytes=$(( ${NIXOS_SIZE%[Mm]} * 1024 * 1024 )) ;; - *) nixos_bytes="$NIXOS_SIZE" ;; -esac -log "Shrinking Windows partition by $NIXOS_SIZE ($nixos_bytes bytes) — this may take a few minutes..." +size_to_bytes() { + local s="$1" + case "$s" in + *G|*g) echo $(( ${s%[Gg]} * 1024 * 1024 * 1024 )) ;; + *M|*m) echo $(( ${s%[Mm]} * 1024 * 1024 )) ;; + *K|*k) echo $(( ${s%[Kk]} * 1024 )) ;; + *) echo "$s" ;; + esac +} +nixos_bytes=$(size_to_bytes "$NIXOS_SIZE") +# Same for SWAP_SIZE so we can shrink the Windows partition by the *total* of +# both new partitions (NixOS root + swap). Without this, the swap partition +# would never fit on disk. +if [[ "$SWAP_SIZE" == "0" ]]; then + shrink_bytes=$nixos_bytes +else + swap_bytes=$(size_to_bytes "$SWAP_SIZE") + shrink_bytes=$(( nixos_bytes + swap_bytes )) +fi +log "Shrinking Windows partition by $NIXOS_SIZE (nixos) ${SWAP_SIZE:+ + $SWAP_SIZE (swap)} ($shrink_bytes bytes total) — this may take a few minutes..." ntfsfix -d "$WIN_PART" 2>/dev/null || true # ntfsresize syntax: -s = perform the shrink, --size = new size in bytes -# We want the new Windows partition to be (current size - nixos_bytes). +# We want the new Windows partition to be (current size - shrink_bytes). win_size_bytes=$(lsblk -ndo SIZE -b "$WIN_PART") -new_win_bytes=$(( win_size_bytes - nixos_bytes )) +new_win_bytes=$(( win_size_bytes - shrink_bytes )) ntfsresize -s --size "$new_win_bytes" "$WIN_PART" 2>&1 | tail -5 \ || fail "ntfsresize failed; aborting" @@ -137,24 +151,78 @@ ntfsresize -s --size "$new_win_bytes" "$WIN_PART" 2>&1 | tail -5 \ partprobe "$DISK" sleep 2 -# === Find next available partition number === -existing=$(lsblk -nlo NAME "$DISK" 2>/dev/null | wc -l) -log "Existing partitions on $DISK: $existing" +# Snapshot the partition list BEFORE we create the new ones, so we can diff +# and identify which device nodes are new. Plain `sort | tail -1` is unsafe +# when more than one new partition is created (NixOS root + swap). +existing_parts=$(lsblk -nlo NAME "$DISK" 2>/dev/null | tail -n +2 | sort) -# Create NixOS root partition in the freed space -log "Creating NixOS root partition ($NIXOS_SIZE)..." -parted -s -a opt "$DISK" mkpart primary ext4 -- -"$NIXOS_SIZE" 100% || fail "parted mkpart failed" +# === Create NixOS root + swap in the freed space === +# Compute the start = end of the (now smaller) Windows partition. +# We deliberately do NOT use `-NIXOS_SIZE 100%` because on a T490 the recovery +# partition sits at the end of the disk and that range would overlap it. +# We work in 1MiB units and let parted align. +win_new_bytes=$(lsblk -ndo SIZE -b "$WIN_PART") +win_end_mib=$(( (win_new_bytes + 1024*1024 - 1) / (1024*1024) )) + +# Extract the numeric prefix of NIXOS_SIZE for the parted range calculation. +nix_num=${NIXOS_SIZE%[GgMmKk]*}; nix_unit=${NIXOS_SIZE##*[0-9]} +case "$nix_unit" in + G|g) nix_size_mib=$(( nix_num * 1024 )) ;; + M|m) nix_size_mib=$nix_num ;; + *) fail "NIXOS_SIZE must be suffixed with M or G (got '$NIXOS_SIZE')" ;; +esac +nix_start_mib=$win_end_mib +nix_end_mib=$(( nix_start_mib + nix_size_mib )) +log "Creating NixOS root partition (${NIXOS_SIZE}) at ${nix_start_mib}MiB..${nix_end_mib}MiB..." +parted -s "$DISK" mkpart primary ext4 "${nix_start_mib}MiB" "${nix_end_mib}MiB" \ + || fail "parted mkpart (nixos) failed" partprobe "$DISK" -sleep 2 +sleep 1 -# Find the new partition -NIXOS_PART=$(lsblk -nlo NAME "$DISK" 2>/dev/null | sed "s|^|/dev/|" | sort | tail -1) -log "New partition: $NIXOS_PART" +if [[ "$SWAP_SIZE" != "0" ]]; then + swap_num=${SWAP_SIZE%[GgMmKk]*}; swap_unit=${SWAP_SIZE##*[0-9]} + case "$swap_unit" in + G|g) swap_size_mib=$(( swap_num * 1024 )) ;; + M|m) swap_size_mib=$swap_num ;; + *) fail "SWAP_SIZE must be suffixed with M or G (got '$SWAP_SIZE')" ;; + esac + swap_end_mib=$(( nix_end_mib + swap_size_mib )) + log "Creating swap partition (${SWAP_SIZE}) at ${nix_end_mib}MiB..${swap_end_mib}MiB..." + parted -s "$DISK" mkpart primary linux-swap "${nix_end_mib}MiB" "${swap_end_mib}MiB" \ + || fail "parted mkpart (swap) failed" + partprobe "$DISK" + sleep 1 +fi + +# Identify the new partitions by diffing the partition table. New partitions +# are the ones that didn't exist before we ran mkpart. +new_parts=$(lsblk -nlo NAME "$DISK" 2>/dev/null | tail -n +2 | sort) +NIXOS_PART=$(comm -13 <(echo "$existing_parts") <(echo "$new_parts") \ + | grep -v "$(basename "$DISK")" \ + | head -1 \ + | sed 's|^|/dev/|') +[[ -n "$NIXOS_PART" ]] || fail "could not identify the new NixOS partition" +log "New NixOS partition: $NIXOS_PART" +if [[ "$SWAP_SIZE" != "0" ]]; then + SWAP_PART=$(comm -13 <(echo "$existing_parts") <(echo "$new_parts") \ + | grep -v "$NIXOS_PART" \ + | grep -v "$(basename "$DISK")" \ + | head -1 \ + | sed 's|^|/dev/|') + [[ -n "$SWAP_PART" ]] || fail "could not identify the new swap partition" + log "New swap partition: $SWAP_PART" +fi # === Format and mount === log "Formatting $NIXOS_PART as ext4..." mkfs.ext4 -F -L nixos "$NIXOS_PART" +if [[ "$SWAP_SIZE" != "0" ]]; then + log "Setting up swap on $SWAP_PART..." + mkswap -L swap "$SWAP_PART" + swapon "$SWAP_PART" +fi + log "Mounting..." mount "$NIXOS_PART" /mnt mkdir -p /mnt/boot