Add free space detection to install.sh as alternative to partition shrinking
If a disk has unpartitioned space >= 4096 MiB, it is offered as a direct target for the OEMDRV partition, avoiding any filesystem resize. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+172
-80
@@ -79,6 +79,13 @@ PT_MNT=() # /home | -
|
|||||||
PT_FREE=() # free MiB (integer)
|
PT_FREE=() # free MiB (integer)
|
||||||
PT_OK=() # 1 = shrinkable
|
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() {
|
collect_partitions() {
|
||||||
local NAME SIZE FSTYPE LABEL UUID MOUNTPOINT TYPE PKNAME
|
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)
|
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 ─────────────────────────────────────────────────────────────
|
# ── Table display ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
show_table() {
|
show_table() {
|
||||||
@@ -160,6 +195,25 @@ show_table() {
|
|||||||
echo
|
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<FS_IDX; i++ )); do
|
||||||
|
printf " %-4s %-14s %-12s %-12s %s\n" \
|
||||||
|
"$((i+1))" \
|
||||||
|
"${FS_DISK[$i]}" \
|
||||||
|
"${FS_START[$i]}" \
|
||||||
|
"${FS_END[$i]}" \
|
||||||
|
"${FS_SIZE[$i]}"
|
||||||
|
done
|
||||||
|
hr
|
||||||
|
echo
|
||||||
|
}
|
||||||
|
|
||||||
# ── Partition geometry via parted -m ─────────────────────────────────────────
|
# ── Partition geometry via parted -m ─────────────────────────────────────────
|
||||||
|
|
||||||
part_end_mib() {
|
part_end_mib() {
|
||||||
@@ -187,116 +241,154 @@ new_part_device() {
|
|||||||
require_root
|
require_root
|
||||||
check_tools
|
check_tools
|
||||||
|
|
||||||
info "Scanning partitions for shrinkable ext4 / btrfs volumes..."
|
info "Scanning for shrinkable partitions and unpartitioned free space..."
|
||||||
collect_partitions
|
collect_partitions
|
||||||
|
collect_free_space
|
||||||
|
|
||||||
[[ $PT_IDX -gt 0 ]] || die "No ext4 or btrfs partitions found on this system."
|
[[ $PT_IDX -gt 0 || $FS_IDX -gt 0 ]] \
|
||||||
|
|| die "No ext4 or btrfs partitions and no free disk space found on this system."
|
||||||
|
|
||||||
show_table
|
# Count shrinkable partitions
|
||||||
|
|
||||||
# Count shrinkable
|
|
||||||
shrink_count=0
|
shrink_count=0
|
||||||
for (( i=0; i<PT_IDX; i++ )); do [[ "${PT_OK[$i]}" == "1" ]] && (( shrink_count++ )) || true; done
|
for (( i=0; i<PT_IDX; i++ )); do [[ "${PT_OK[$i]}" == "1" ]] && (( shrink_count++ )) || true; done
|
||||||
[[ $shrink_count -gt 0 ]] || die "No shrinkable partitions found. A non-root ext4 or btrfs partition needs at least ${MIN_FREE_MIB} MiB free."
|
|
||||||
|
[[ $PT_IDX -gt 0 ]] && show_table
|
||||||
|
show_free_table
|
||||||
|
|
||||||
|
[[ $shrink_count -gt 0 || $FS_IDX -gt 0 ]] \
|
||||||
|
|| die "No shrinkable partitions and no free space found. A non-root ext4 or btrfs partition needs at least ${MIN_FREE_MIB} MiB free."
|
||||||
|
|
||||||
# ── Selection ─────────────────────────────────────────────────────────────────
|
# ── Selection ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
MODE="" # "freespace" | "shrink"
|
||||||
SEL=-1
|
SEL=-1
|
||||||
|
|
||||||
while true; do
|
while true; do
|
||||||
read -r -p "Enter number of partition to shrink, or q to quit: " INPUT
|
echo
|
||||||
|
if [[ $FS_IDX -gt 0 && $shrink_count -gt 0 ]]; then
|
||||||
|
read -r -p "Enter f<n> to use free space, s<n> 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" =~ ^[qQ]$ ]] && { echo "Aborted."; exit 0; }
|
||||||
[[ "$INPUT" =~ ^[0-9]+$ ]] || { echo " Please enter a number."; continue; }
|
|
||||||
local_i=$(( INPUT - 1 ))
|
if [[ "$INPUT" =~ ^[fF]([0-9]+)$ ]]; then
|
||||||
(( local_i >= 0 && local_i < PT_IDX )) || { echo " Number out of range."; continue; }
|
local_i=$(( ${BASH_REMATCH[1]} - 1 ))
|
||||||
[[ "${PT_OK[$local_i]}" == "1" ]] || { echo " That partition cannot be shrunk (see marks above)."; continue; }
|
(( local_i >= 0 && local_i < FS_IDX )) || { echo " Number out of range."; continue; }
|
||||||
SEL=$local_i
|
MODE="freespace"; SEL=$local_i; break
|
||||||
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
|
done
|
||||||
|
|
||||||
S_DEV="${PT_DEV[$SEL]}"
|
# ── Confirm ───────────────────────────────────────────────────────────────────
|
||||||
S_DISK="${PT_DISK[$SEL]}"
|
|
||||||
S_PNUM="${PT_PNUM[$SEL]}"
|
|
||||||
S_FS="${PT_FS[$SEL]}"
|
|
||||||
S_MNT="${PT_MNT[$SEL]}"
|
|
||||||
|
|
||||||
echo
|
echo
|
||||||
echo " Partition : $S_DEV (${PT_FS[$SEL]}, ${PT_SIZE[$SEL]}, ${PT_FREE[$SEL]} MiB free)"
|
if [[ "$MODE" == "freespace" ]]; then
|
||||||
echo " Disk : $S_DISK Partition number: $S_PNUM"
|
WORK_DISK="${FS_DISK[$SEL]}"
|
||||||
echo " Mountpoint: $S_MNT"
|
OEMDRV_START="${FS_START[$SEL]}"
|
||||||
echo
|
OEMDRV_END=$(( FS_START[$SEL] + SHRINK_MIB ))
|
||||||
echo " $S_DEV will be shrunk by ${SHRINK_MIB} MiB."
|
echo " Disk : $WORK_DISK"
|
||||||
echo " A new ${SHRINK_MIB} MiB BTRFS partition labeled OEMDRV will be created."
|
echo " Free region : ${FS_START[$SEL]} MiB – ${FS_END[$SEL]} MiB (${FS_SIZE[$SEL]} MiB available)"
|
||||||
echo " It will be mounted at $MOUNT_POINT and the repository cloned into it."
|
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
|
echo
|
||||||
read -r -p " Type YES (uppercase) to confirm, anything else to abort: " CONFIRM
|
read -r -p " Type YES (uppercase) to confirm, anything else to abort: " CONFIRM
|
||||||
[[ "$CONFIRM" == "YES" ]] || { echo "Aborted."; exit 0; }
|
[[ "$CONFIRM" == "YES" ]] || { echo "Aborted."; exit 0; }
|
||||||
|
|
||||||
# ── Geometry ──────────────────────────────────────────────────────────────────
|
# ── Shrink path: geometry + filesystem resize + partition table update ─────────
|
||||||
|
|
||||||
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 ───────────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
WAS_MOUNTED=0
|
WAS_MOUNTED=0
|
||||||
ORIG_MNT=""
|
ORIG_MNT=""
|
||||||
if [[ "$S_MNT" != "-" && -n "$S_MNT" ]]; then
|
|
||||||
info "Unmounting $S_DEV from $S_MNT..."
|
if [[ "$MODE" == "shrink" ]]; then
|
||||||
ORIG_MNT="$S_MNT"
|
info "Reading partition geometry on $S_DISK..."
|
||||||
umount "$S_DEV" || die "Cannot unmount $S_DEV. Close all open files and try again."
|
|
||||||
WAS_MOUNTED=1
|
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
|
fi
|
||||||
|
|
||||||
# ── Filesystem resize ─────────────────────────────────────────────────────────
|
# ── Create OEMDRV partition ───────────────────────────────────────────────────
|
||||||
|
|
||||||
if [[ "$S_FS" == "ext4" ]]; then
|
info "Creating new OEMDRV partition (${OEMDRV_START}–${OEMDRV_END} MiB) on $WORK_DISK..."
|
||||||
info "Running e2fsck on $S_DEV..."
|
parted -s "$WORK_DISK" mkpart primary btrfs "${OEMDRV_START}MiB" "${OEMDRV_END}MiB" \
|
||||||
e2fsck -f "$S_DEV" || die "e2fsck found errors on $S_DEV. Fix them before retrying."
|
|| die "parted mkpart failed. Check that the target area is free space on $WORK_DISK."
|
||||||
|
|
||||||
info "Shrinking ext4 filesystem to ${NEW_FS_MIB} MiB..."
|
partprobe "$WORK_DISK"
|
||||||
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"
|
|
||||||
sleep 1
|
sleep 1
|
||||||
|
|
||||||
# Determine new partition number (highest on the disk after partprobe)
|
# 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}')
|
| 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."
|
[[ -b "$OEMDRV_DEV" ]] || die "New partition device $OEMDRV_DEV not found after partprobe."
|
||||||
|
|
||||||
info "New partition device: $OEMDRV_DEV"
|
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)..."
|
info "Formatting $OEMDRV_DEV as BTRFS (label: $OEMDRV_LABEL)..."
|
||||||
mkfs.btrfs -f -L "$OEMDRV_LABEL" "$OEMDRV_DEV" || die "mkfs.btrfs failed."
|
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
|
if [[ $WAS_MOUNTED -eq 1 && -n "$ORIG_MNT" ]]; then
|
||||||
info "Remounting $S_DEV to $ORIG_MNT..."
|
info "Remounting $S_DEV to $ORIG_MNT..."
|
||||||
|
|||||||
Reference in New Issue
Block a user