This commit is contained in:
overcuriousity 2025-10-20 14:30:48 +02:00
parent 81bc66e905
commit f60cf35235

View File

@ -496,7 +496,7 @@ get_preset_or_custom() {
PARTITION_SCHEME="msdos" PARTITION_SCHEME="msdos"
PARTITION_COUNT=1 PARTITION_COUNT=1
print_info "Preset: MS-DOS (MBR)" print_info "Preset: MS-DOS (MBR)"
print_note "Single FAT12 partition (max 16MB)" print_note "Single FAT12 partition"
if [ "$DISK_SIZE_MB" -gt 16 ]; then if [ "$DISK_SIZE_MB" -gt 16 ]; then
print_warning "MS-DOS typically uses FAT12 which is limited to 16MB" print_warning "MS-DOS typically uses FAT12 which is limited to 16MB"
print_info "Consider reducing disk size or the partition will use FAT16" print_info "Consider reducing disk size or the partition will use FAT16"
@ -707,7 +707,7 @@ get_partition_count() {
get_partition_configs() { get_partition_configs() {
PARTITION_CONFIGS=() PARTITION_CONFIGS=()
local total_allocated=0 local total_allocated=0
local available_space=$((DISK_SIZE_MB - 2)) # Reserve 2MB for partition table local available_space=$((DISK_SIZE_MB - PARTITION_TABLE_RESERVED_MB)) # Reserve for partition table / metadata
for i in $(seq 1 $PARTITION_COUNT); do for i in $(seq 1 $PARTITION_COUNT); do
echo "" echo ""
@ -1012,8 +1012,14 @@ create_disk_image() {
setup_loop_device() { setup_loop_device() {
print_info "Setting up loop device..." print_info "Setting up loop device..."
# Use atomic operation (find + attach in one command) # Try to attach the image and ask the kernel to create partition devices (-P) when supported.
LOOP_DEVICE=$(losetup -f --show "$FILENAME" 2>/dev/null || true) # Fall back to a plain attach if -P is not available.
LOOP_DEVICE=$(losetup -f --show -P "$FILENAME" 2>/dev/null || true)
if [ -z "$LOOP_DEVICE" ]; then
# older losetup may not support -P; attach without it then trigger partprobe later
LOOP_DEVICE=$(losetup -f --show "$FILENAME" 2>/dev/null || true)
fi
if [ -z "$LOOP_DEVICE" ]; then if [ -z "$LOOP_DEVICE" ]; then
print_error "Failed to create loop device" print_error "Failed to create loop device"
@ -1027,64 +1033,252 @@ setup_loop_device() {
# Create partition table and partitions # Create partition table and partitions
create_partitions() { create_partitions() {
print_info "Creating $PARTITION_SCHEME partition table..." print_info "Creating $PARTITION_SCHEME partition table..."
parted -s "$LOOP_DEVICE" mklabel "$PARTITION_SCHEME" parted -s "$LOOP_DEVICE" mklabel "$PARTITION_SCHEME"
local start_mb=1 # Reserve a small margin at the end of the disk to avoid creating partitions that
local part_num=1 # extend into GPT backup header / metadata. This mirrors the -2MB reserve used
# interactively elsewhere in the script.
local PARTITION_TABLE_RESERVED_MB=2
if [ "$DISK_SIZE_MB" -le $PARTITION_TABLE_RESERVED_MB ]; then
print_error "Disk size (${DISK_SIZE_MB}MB) too small to reserve required metadata space"
cleanup
exit 1
fi
local usable_mb=$((DISK_SIZE_MB - PARTITION_TABLE_RESERVED_MB))
# Parse PARTITION_CONFIGS into arrays for easier processing
local fs size label
local -a fs_arr size_arr label_arr
for config in "${PARTITION_CONFIGS[@]}"; do for config in "${PARTITION_CONFIGS[@]}"; do
IFS='|' read -r fs size label <<< "$config" IFS='|' read -r fs size label <<< "$config"
fs_arr+=("$fs")
if [ "$size" = "remaining" ]; then size_arr+=("$size")
end="100%" label_arr+=("$label")
done
local count=${#fs_arr[@]}
if [ "$count" -eq 0 ]; then
print_info "No partition configurations provided"
return
fi
# Convert sizes: numeric values stay numeric; 'remaining' will be resolved below
local -a numeric_size
local total_fixed=0
local -a remaining_idxs
for i in $(seq 0 $((count - 1))); do
s="${size_arr[$i]}"
if [ "$s" = "remaining" ]; then
numeric_size[$i]=-1
remaining_idxs+=("$i")
else else
end=$(echo "$start_mb + $size" | bc) # sanitize numeric sizes (should be integer MB)
end="${end}MiB" if ! [[ "$s" =~ ^[0-9]+$ ]]; then
print_error "Invalid partition size for entry $((i+1)): '$s'"
cleanup
exit 1
fi
numeric_size[$i]=$s
total_fixed=$((total_fixed + s))
fi fi
done
# Distribute remaining space among 'remaining' entries using the usable space
if [ "${#remaining_idxs[@]}" -gt 0 ]; then
local free_space=$((usable_mb - total_fixed))
if [ "$free_space" -le 0 ]; then
print_error "Not enough space to satisfy 'remaining' partitions (free: ${free_space}MB)"
cleanup
exit 1
fi
local rem_count=${#remaining_idxs[@]}
local base=$((free_space / rem_count))
local leftover=$((free_space - base * rem_count))
for idx in "${remaining_idxs[@]}"; do
numeric_size[$idx]=$base
if [ "$leftover" -gt 0 ]; then
numeric_size[$idx]=$((numeric_size[$idx] + 1))
leftover=$((leftover - 1))
fi
done
fi
# Find last index that will actually produce a partition entry (skip 'unallocated')
local last_mkpart_idx=-1
for i in $(seq 0 $((count - 1))); do
if [ "${fs_arr[$i]}" != "unallocated" ]; then
last_mkpart_idx=$i
fi
done
# If everything is unallocated, nothing to do
if [ "$last_mkpart_idx" -eq -1 ]; then
print_info "All configured space is unallocated - no partition entries will be created"
return
fi
# Compute how much explicit unallocated space is trailing after the last mkpart so we can reserve it
local trailing_unalloc_sum=0
for i in $(seq $((last_mkpart_idx + 1)) $((count - 1))); do
if [ "$i" -ge 0 ] && [ "${fs_arr[$i]}" = "unallocated" ]; then
trailing_unalloc_sum=$((trailing_unalloc_sum + numeric_size[$i]))
fi
done
# Sanity checks: ensure no numeric sizes are negative and the requested layout fits within usable space
local sum_all=0
for i in $(seq 0 $((count - 1))); do
if [ -z "${numeric_size[$i]}" ] || [ "${numeric_size[$i]}" -lt 0 ]; then
print_error "Internal error: unresolved partition size for entry $((i+1))"
cleanup
exit 1
fi
sum_all=$((sum_all + numeric_size[$i]))
done
if [ "$sum_all" -gt "$usable_mb" ]; then
print_error "Configured partitions (${sum_all}MB) exceed usable disk space (${usable_mb}MB) (reserved ${PARTITION_TABLE_RESERVED_MB}MB for metadata)"
cleanup
exit 1
fi
# Create partitions. We'll iterate in forward order, but the last mkpart will be explicitly ended
# before any trailing unallocated space so that 'unallocated' regions remain as requested.
local start_mb=1
local part_num=1
# Attempt to create a partition, retrying with shrinking end boundary when parted
# complains the end is outside the device (handles alignment/metadata rounding)
try_mkpart() {
local loopdev="$1"
local start_mb="$2"
local end_mb="$3" # numeric MB
local fstype="$4" # optional parted fs type (e.g. ntfs, ext4)
local max_attempts=8
local attempt_end=$end_mb
local output ret last_output
for ((try=0; try<max_attempts; try++)); do
local end_str="${attempt_end}MiB"
if [ -n "$fstype" ]; then
output=$(parted -s "$loopdev" mkpart primary "$fstype" "${start_mb}MiB" "$end_str" 2>&1)
ret=$?
else
output=$(parted -s "$loopdev" mkpart primary "${start_mb}MiB" "$end_str" 2>&1)
ret=$?
fi
if [ $ret -eq 0 ]; then
return 0
fi
last_output="$output"
# If the error looks like 'outside device' / 'not enough space' try shrinking the end
if echo "$output" | grep -Ei 'outside|out of range|not enough space|beyond|außerhalb|außer' >/dev/null 2>&1; then
print_warning "parted failed to create partition with end ${end_str} - retrying with ${attempt_end-1}MiB: $output"
attempt_end=$((attempt_end - 1))
if [ "$attempt_end" -le "$start_mb" ]; then
print_error "Cannot allocate partition: insufficient space after retries"
echo "$last_output"
cleanup
exit 1
fi
continue
else
print_error "parted failed creating partition: $output"
cleanup
exit 1
fi
done
print_error "Failed to create partition after ${max_attempts} retries: $last_output"
cleanup
exit 1
}
for i in $(seq 0 $((count - 1))); do
fs="${fs_arr[$i]}"
size_mb=${numeric_size[$i]}
label="${label_arr[$i]}"
if [ "$fs" = "unallocated" ]; then
print_info "Leaving ${size_mb}MB unallocated (no partition entry)"
start_mb=$((start_mb + size_mb))
continue
fi
# determine end for this partition
if [ "$i" -eq "$last_mkpart_idx" ]; then
# Make sure last mkpart ends before any trailing unallocated space and within usable area
end_mb=$((usable_mb - trailing_unalloc_sum))
if [ "$start_mb" -ge "$end_mb" ]; then
print_error "Not enough space to create partition $((i+1)): needed ${size_mb}MB, available $((end_mb - start_mb))MB"
cleanup
exit 1
fi
end="${end_mb}MiB"
numeric_end_mb=$end_mb
else
end_val=$((start_mb + size_mb))
# ensure we don't exceed usable area (should be caught by earlier checks)
if [ "$end_val" -gt "$usable_mb" ]; then
print_error "Partition $((i+1)) would exceed usable disk area (end ${end_val}MB > usable ${usable_mb}MB)"
cleanup
exit 1
fi
end="${end_val}MiB"
numeric_end_mb=$end_val
fi
print_info "Creating partition $part_num: ${start_mb}MiB -> $end" print_info "Creating partition $part_num: ${start_mb}MiB -> $end"
# Set partition type based on filesystem
case $fs in case $fs in
swap) swap)
parted -s "$LOOP_DEVICE" mkpart primary linux-swap "${start_mb}MiB" "$end" try_mkpart "$LOOP_DEVICE" "$start_mb" "$numeric_end_mb" linux-swap
;; ;;
fat12|fat16|vfat) fat12|fat16|vfat)
parted -s "$LOOP_DEVICE" mkpart primary fat32 "${start_mb}MiB" "$end" try_mkpart "$LOOP_DEVICE" "$start_mb" "$numeric_end_mb" fat32
;; ;;
ntfs) ntfs)
parted -s "$LOOP_DEVICE" mkpart primary ntfs "${start_mb}MiB" "$end" try_mkpart "$LOOP_DEVICE" "$start_mb" "$numeric_end_mb" ntfs
;; ;;
ext2|ext3|ext4) ext2|ext3|ext4)
parted -s "$LOOP_DEVICE" mkpart primary ext4 "${start_mb}MiB" "$end" try_mkpart "$LOOP_DEVICE" "$start_mb" "$numeric_end_mb" ext4
;; ;;
xfs) xfs)
parted -s "$LOOP_DEVICE" mkpart primary xfs "${start_mb}MiB" "$end" try_mkpart "$LOOP_DEVICE" "$start_mb" "$numeric_end_mb" xfs
;; ;;
hfsplus) hfsplus)
parted -s "$LOOP_DEVICE" mkpart primary hfs+ "${start_mb}MiB" "$end" try_mkpart "$LOOP_DEVICE" "$start_mb" "$numeric_end_mb" hfs+
;;
unallocated)
# Don't create a partition for unallocated space - just leave it empty
print_info "Leaving space unallocated (no partition entry)"
;; ;;
*) *)
parted -s "$LOOP_DEVICE" mkpart primary "${start_mb}MiB" "$end" try_mkpart "$LOOP_DEVICE" "$start_mb" "$numeric_end_mb" ""
;; ;;
esac esac
if [ "$size" != "remaining" ]; then # advance start pointer for next partition (skip this for the explicitly-ended last mkpart)
start_mb=$(echo "$start_mb + $size" | bc) if [ "$i" -ne "$last_mkpart_idx" ]; then
start_mb=$((start_mb + size_mb))
else
start_mb=$((end_mb))
fi fi
part_num=$((part_num + 1)) part_num=$((part_num + 1))
done done
# Inform kernel about partition table changes # Inform kernel about partition table changes and give it a moment to create /dev nodes
partprobe "$LOOP_DEVICE" 2>/dev/null || true partprobe "$LOOP_DEVICE" 2>/dev/null || true
sleep 2 sleep 2
print_success "Partitions created" print_success "Partitions created"
} }