diff --git a/system_setup/install.sh b/system_setup/install.sh index e289bd2..df3b9a8 100755 --- a/system_setup/install.sh +++ b/system_setup/install.sh @@ -79,6 +79,13 @@ PT_MNT=() # /home | - PT_FREE=() # free MiB (integer) PT_OK=() # 1 = shrinkable +# Parallel arrays for unpartitioned free space regions, indexed 0..FS_IDX-1 +FS_IDX=0 +FS_DISK=() # /dev/sda +FS_START=() # region start, MiB +FS_END=() # region end, MiB +FS_SIZE=() # region size, MiB + collect_partitions() { local NAME SIZE FSTYPE LABEL UUID MOUNTPOINT TYPE PKNAME @@ -124,6 +131,34 @@ collect_partitions() { done < <(lsblk -o NAME,SIZE,FSTYPE,LABEL,UUID,MOUNTPOINT,TYPE,PKNAME -p -P -n 2>/dev/null) } +collect_free_space() { + local disk + while IFS= read -r disk; do + [[ -b "$disk" ]] || continue + while read -r s e sz; do + FS_DISK[$FS_IDX]="$disk" + FS_START[$FS_IDX]="$s" + FS_END[$FS_IDX]="$e" + FS_SIZE[$FS_IDX]="$sz" + (( FS_IDX++ )) || true + done < <( + parted -s "$disk" -m unit MiB print free 2>/dev/null \ + | awk -F'[;:]' -v min="$SHRINK_MIB" ' + $1+0 > 0 { + for (i = 1; i <= NF; i++) { + if ($i == "free") { + start=$2; end=$3; size=$4; + gsub(/MiB/,"",start); gsub(/MiB/,"",end); gsub(/MiB/,"",size); + s=int(start+0); e=int(end+0); sz=int(size+0); + if (sz >= min) print s " " e " " sz; + break + } + } + }' + ) + done < <(lsblk -d -p -n -o NAME 2>/dev/null) +} + # ── Table display ───────────────────────────────────────────────────────────── show_table() { @@ -160,6 +195,25 @@ show_table() { echo } +show_free_table() { + [[ $FS_IDX -gt 0 ]] || return + echo + echo "Unpartitioned free space regions (>= ${SHRINK_MIB} MiB):" + hr + printf " %-4s %-14s %-12s %-12s %s\n" "#" "Disk" "Start (MiB)" "End (MiB)" "Size (MiB)" + hr + for (( i=0; i to use free space, s to shrink a partition, or q to quit: " INPUT + elif [[ $FS_IDX -gt 0 ]]; then + read -r -p "Enter number of free space region to use, or q to quit: " INPUT + [[ "$INPUT" =~ ^[0-9]+$ ]] && INPUT="f${INPUT}" + else + read -r -p "Enter number of partition to shrink, or q to quit: " INPUT + [[ "$INPUT" =~ ^[0-9]+$ ]] && INPUT="s${INPUT}" + fi + [[ "$INPUT" =~ ^[qQ]$ ]] && { echo "Aborted."; exit 0; } - [[ "$INPUT" =~ ^[0-9]+$ ]] || { echo " Please enter a number."; continue; } - local_i=$(( INPUT - 1 )) - (( local_i >= 0 && local_i < PT_IDX )) || { echo " Number out of range."; continue; } - [[ "${PT_OK[$local_i]}" == "1" ]] || { echo " That partition cannot be shrunk (see marks above)."; continue; } - SEL=$local_i - break + + if [[ "$INPUT" =~ ^[fF]([0-9]+)$ ]]; then + local_i=$(( ${BASH_REMATCH[1]} - 1 )) + (( local_i >= 0 && local_i < FS_IDX )) || { echo " Number out of range."; continue; } + MODE="freespace"; SEL=$local_i; break + + elif [[ "$INPUT" =~ ^[sS]([0-9]+)$ ]]; then + local_i=$(( ${BASH_REMATCH[1]} - 1 )) + (( local_i >= 0 && local_i < PT_IDX )) || { echo " Number out of range."; continue; } + [[ "${PT_OK[$local_i]}" == "1" ]] || { echo " That partition cannot be shrunk (see marks above)."; continue; } + MODE="shrink"; SEL=$local_i; break + + else + echo " Invalid input." + fi done -S_DEV="${PT_DEV[$SEL]}" -S_DISK="${PT_DISK[$SEL]}" -S_PNUM="${PT_PNUM[$SEL]}" -S_FS="${PT_FS[$SEL]}" -S_MNT="${PT_MNT[$SEL]}" +# ── Confirm ─────────────────────────────────────────────────────────────────── echo -echo " Partition : $S_DEV (${PT_FS[$SEL]}, ${PT_SIZE[$SEL]}, ${PT_FREE[$SEL]} MiB free)" -echo " Disk : $S_DISK Partition number: $S_PNUM" -echo " Mountpoint: $S_MNT" -echo -echo " $S_DEV will be shrunk by ${SHRINK_MIB} MiB." -echo " A new ${SHRINK_MIB} MiB BTRFS partition labeled OEMDRV will be created." -echo " It will be mounted at $MOUNT_POINT and the repository cloned into it." +if [[ "$MODE" == "freespace" ]]; then + WORK_DISK="${FS_DISK[$SEL]}" + OEMDRV_START="${FS_START[$SEL]}" + OEMDRV_END=$(( FS_START[$SEL] + SHRINK_MIB )) + echo " Disk : $WORK_DISK" + echo " Free region : ${FS_START[$SEL]} MiB – ${FS_END[$SEL]} MiB (${FS_SIZE[$SEL]} MiB available)" + echo " New OEMDRV : ${OEMDRV_START} MiB – ${OEMDRV_END} MiB (${SHRINK_MIB} MiB)" + echo " It will be mounted at $MOUNT_POINT and the repository cloned into it." +else + S_DEV="${PT_DEV[$SEL]}" + S_DISK="${PT_DISK[$SEL]}" + S_PNUM="${PT_PNUM[$SEL]}" + S_FS="${PT_FS[$SEL]}" + S_MNT="${PT_MNT[$SEL]}" + WORK_DISK="$S_DISK" + echo " Partition : $S_DEV (${PT_FS[$SEL]}, ${PT_SIZE[$SEL]}, ${PT_FREE[$SEL]} MiB free)" + echo " Disk : $S_DISK Partition number: $S_PNUM" + echo " Mountpoint: $S_MNT" + echo + echo " $S_DEV will be shrunk by ${SHRINK_MIB} MiB." + echo " A new ${SHRINK_MIB} MiB BTRFS partition labeled OEMDRV will be created." + echo " It will be mounted at $MOUNT_POINT and the repository cloned into it." +fi echo read -r -p " Type YES (uppercase) to confirm, anything else to abort: " CONFIRM [[ "$CONFIRM" == "YES" ]] || { echo "Aborted."; exit 0; } -# ── Geometry ────────────────────────────────────────────────────────────────── - -info "Reading partition geometry on $S_DISK..." - -CURR_END=$(part_end_mib "$S_DISK" "$S_PNUM") -CURR_START=$(part_start_mib "$S_DISK" "$S_PNUM") -[[ -n "$CURR_END" && -n "$CURR_START" ]] || die "Could not read geometry for partition $S_PNUM on $S_DISK." - -NEW_END=$(( CURR_END - SHRINK_MIB )) -NEW_FS_MIB=$(( NEW_END - CURR_START )) -OEMDRV_START=$NEW_END -OEMDRV_END=$CURR_END - -echo " Current partition : ${CURR_START} MiB – ${CURR_END} MiB" -echo " After shrink : ${CURR_START} MiB – ${NEW_END} MiB (filesystem: ${NEW_FS_MIB} MiB)" -echo " New OEMDRV : ${OEMDRV_START} MiB – ${OEMDRV_END} MiB (${SHRINK_MIB} MiB)" - -# ── Unmount ─────────────────────────────────────────────────────────────────── +# ── Shrink path: geometry + filesystem resize + partition table update ───────── WAS_MOUNTED=0 ORIG_MNT="" -if [[ "$S_MNT" != "-" && -n "$S_MNT" ]]; then - info "Unmounting $S_DEV from $S_MNT..." - ORIG_MNT="$S_MNT" - umount "$S_DEV" || die "Cannot unmount $S_DEV. Close all open files and try again." - WAS_MOUNTED=1 + +if [[ "$MODE" == "shrink" ]]; then + info "Reading partition geometry on $S_DISK..." + + CURR_END=$(part_end_mib "$S_DISK" "$S_PNUM") + CURR_START=$(part_start_mib "$S_DISK" "$S_PNUM") + [[ -n "$CURR_END" && -n "$CURR_START" ]] \ + || die "Could not read geometry for partition $S_PNUM on $S_DISK." + + NEW_END=$(( CURR_END - SHRINK_MIB )) + NEW_FS_MIB=$(( NEW_END - CURR_START )) + OEMDRV_START=$NEW_END + OEMDRV_END=$CURR_END + + echo " Current partition : ${CURR_START} MiB – ${CURR_END} MiB" + echo " After shrink : ${CURR_START} MiB – ${NEW_END} MiB (filesystem: ${NEW_FS_MIB} MiB)" + echo " New OEMDRV : ${OEMDRV_START} MiB – ${OEMDRV_END} MiB (${SHRINK_MIB} MiB)" + + if [[ "$S_MNT" != "-" && -n "$S_MNT" ]]; then + info "Unmounting $S_DEV from $S_MNT..." + ORIG_MNT="$S_MNT" + umount "$S_DEV" || die "Cannot unmount $S_DEV. Close all open files and try again." + WAS_MOUNTED=1 + fi + + if [[ "$S_FS" == "ext4" ]]; then + info "Running e2fsck on $S_DEV..." + e2fsck -f "$S_DEV" || die "e2fsck found errors on $S_DEV. Fix them before retrying." + + info "Shrinking ext4 filesystem to ${NEW_FS_MIB} MiB..." + resize2fs "$S_DEV" "${NEW_FS_MIB}M" || die "resize2fs failed." + + elif [[ "$S_FS" == "btrfs" ]]; then + info "Shrinking btrfs filesystem on $S_DEV to ${NEW_FS_MIB} MiB..." + TMP_MNT=$(mktemp -d /tmp/oemdrv_btrfs.XXXXXX) + mount "$S_DEV" "$TMP_MNT" || { rmdir "$TMP_MNT"; die "Cannot mount $S_DEV for btrfs resize."; } + btrfs filesystem resize "${NEW_FS_MIB}m" "$TMP_MNT" \ + || { umount "$TMP_MNT"; rmdir "$TMP_MNT"; die "btrfs filesystem resize failed."; } + sync + umount "$TMP_MNT" && rmdir "$TMP_MNT" + fi + + info "Shrinking partition $S_PNUM to ${NEW_END} MiB in partition table..." + parted -s "$S_DISK" resizepart "$S_PNUM" "${NEW_END}MiB" \ + || die "parted resizepart failed." fi -# ── Filesystem resize ───────────────────────────────────────────────────────── +# ── Create OEMDRV partition ─────────────────────────────────────────────────── -if [[ "$S_FS" == "ext4" ]]; then - info "Running e2fsck on $S_DEV..." - e2fsck -f "$S_DEV" || die "e2fsck found errors on $S_DEV. Fix them before retrying." +info "Creating new OEMDRV partition (${OEMDRV_START}–${OEMDRV_END} MiB) on $WORK_DISK..." +parted -s "$WORK_DISK" mkpart primary btrfs "${OEMDRV_START}MiB" "${OEMDRV_END}MiB" \ + || die "parted mkpart failed. Check that the target area is free space on $WORK_DISK." - info "Shrinking ext4 filesystem to ${NEW_FS_MIB} MiB..." - resize2fs "$S_DEV" "${NEW_FS_MIB}M" || die "resize2fs failed." - -elif [[ "$S_FS" == "btrfs" ]]; then - info "Shrinking btrfs filesystem on $S_DEV to ${NEW_FS_MIB} MiB..." - TMP_MNT=$(mktemp -d /tmp/oemdrv_btrfs.XXXXXX) - mount "$S_DEV" "$TMP_MNT" || { rmdir "$TMP_MNT"; die "Cannot mount $S_DEV for btrfs resize."; } - btrfs filesystem resize "${NEW_FS_MIB}m" "$TMP_MNT" \ - || { umount "$TMP_MNT"; rmdir "$TMP_MNT"; die "btrfs filesystem resize failed."; } - sync - umount "$TMP_MNT" && rmdir "$TMP_MNT" -fi - -# ── Partition table update ──────────────────────────────────────────────────── - -info "Shrinking partition $S_PNUM to ${NEW_END} MiB in partition table..." -parted -s "$S_DISK" resizepart "$S_PNUM" "${NEW_END}MiB" \ - || die "parted resizepart failed." - -info "Creating new OEMDRV partition (${OEMDRV_START}–${OEMDRV_END} MiB)..." -parted -s "$S_DISK" mkpart primary btrfs "${OEMDRV_START}MiB" "${OEMDRV_END}MiB" \ - || die "parted mkpart failed. Check that the target area is free space on $S_DISK." - -partprobe "$S_DISK" +partprobe "$WORK_DISK" sleep 1 # Determine new partition number (highest on the disk after partprobe) -NEW_PNUM=$(parted -s "$S_DISK" -m unit MiB print 2>/dev/null \ +NEW_PNUM=$(parted -s "$WORK_DISK" -m unit MiB print 2>/dev/null \ | awk -F: '/^[0-9]/{n=$1} END{print n}') -[[ -n "$NEW_PNUM" ]] || die "Could not determine new partition number on $S_DISK." +[[ -n "$NEW_PNUM" ]] || die "Could not determine new partition number on $WORK_DISK." -OEMDRV_DEV=$(new_part_device "$S_DISK" "$NEW_PNUM") +OEMDRV_DEV=$(new_part_device "$WORK_DISK" "$NEW_PNUM") [[ -b "$OEMDRV_DEV" ]] || die "New partition device $OEMDRV_DEV not found after partprobe." info "New partition device: $OEMDRV_DEV" @@ -306,7 +398,7 @@ info "New partition device: $OEMDRV_DEV" info "Formatting $OEMDRV_DEV as BTRFS (label: $OEMDRV_LABEL)..." mkfs.btrfs -f -L "$OEMDRV_LABEL" "$OEMDRV_DEV" || die "mkfs.btrfs failed." -# ── Remount original partition ──────────────────────────────────────────────── +# ── Remount original partition (shrink path only) ───────────────────────────── if [[ $WAS_MOUNTED -eq 1 && -n "$ORIG_MNT" ]]; then info "Remounting $S_DEV to $ORIG_MNT..."