forked from obel1x/fedora-OEMDRV
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_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<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 ─────────────────────────────────────────
|
||||
|
||||
part_end_mib() {
|
||||
@@ -187,116 +241,154 @@ new_part_device() {
|
||||
require_root
|
||||
check_tools
|
||||
|
||||
info "Scanning partitions for shrinkable ext4 / btrfs volumes..."
|
||||
info "Scanning for shrinkable partitions and unpartitioned free space..."
|
||||
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
|
||||
# Count shrinkable partitions
|
||||
shrink_count=0
|
||||
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 ─────────────────────────────────────────────────────────────────
|
||||
|
||||
MODE="" # "freespace" | "shrink"
|
||||
SEL=-1
|
||||
|
||||
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" =~ ^[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..."
|
||||
|
||||
Reference in New Issue
Block a user