pull-image: Auto-resize rootfs to 16GB before pull
- Unmounts and resizes partition to exactly 16GB - Handles both shrinking (large cards) and expanding (small cards) - Disables Pi OS auto-expand service - Consistent image size regardless of source SD card - 16GB = minimum disk requirement = image size 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,12 +1,18 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# Pull Raspberry Pi image from SD card (after setup)
|
# Pull Raspberry Pi image from SD card (after setup)
|
||||||
# Only pulls the actual used partition space, not the entire SD card
|
# Resizes rootfs to 16GB for consistent image size, then pulls
|
||||||
#
|
#
|
||||||
# Usage: ./pull-image.sh <device> <output.img.zst>
|
# Usage: ./pull-image.sh <device> <output.img.zst>
|
||||||
# Example: ./pull-image.sh /dev/sdb stegasoo-rpi-4.1.5.img.zst
|
# Example: ./pull-image.sh /dev/sdb stegasoo-rpi-4.1.5.img.zst
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BOLD='\033[1m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
if [ $# -ne 2 ]; then
|
if [ $# -ne 2 ]; then
|
||||||
echo "Usage: $0 <device> <output.img.zst>"
|
echo "Usage: $0 <device> <output.img.zst>"
|
||||||
echo "Example: $0 /dev/sdb stegasoo-rpi-4.1.5.img.zst"
|
echo "Example: $0 /dev/sdb stegasoo-rpi-4.1.5.img.zst"
|
||||||
@@ -16,25 +22,139 @@ fi
|
|||||||
DEVICE="$1"
|
DEVICE="$1"
|
||||||
OUTPUT="$2"
|
OUTPUT="$2"
|
||||||
|
|
||||||
if [ ! -b "$DEVICE" ]; then
|
# Check for root
|
||||||
echo "Error: Device not found: $DEVICE"
|
if [ "$EUID" -ne 0 ]; then
|
||||||
|
echo -e "${RED}Error: Must run as root (sudo)${NC}"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Device info:"
|
if [ ! -b "$DEVICE" ]; then
|
||||||
|
echo -e "${RED}Error: Device not found: $DEVICE${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${BOLD}Device info:${NC}"
|
||||||
lsblk "$DEVICE"
|
lsblk "$DEVICE"
|
||||||
echo
|
echo
|
||||||
|
|
||||||
# Get partition info
|
# Find partitions
|
||||||
echo "Partition table:"
|
if [ -b "${DEVICE}1" ]; then
|
||||||
sudo parted -s "$DEVICE" unit s print
|
BOOT_PART="${DEVICE}1"
|
||||||
|
ROOT_PART="${DEVICE}2"
|
||||||
|
elif [ -b "${DEVICE}p1" ]; then
|
||||||
|
BOOT_PART="${DEVICE}p1"
|
||||||
|
ROOT_PART="${DEVICE}p2"
|
||||||
|
else
|
||||||
|
echo -e "${RED}Error: Could not find partitions${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Unmount any mounted partitions
|
||||||
|
echo -e "${YELLOW}Unmounting partitions...${NC}"
|
||||||
|
umount "$BOOT_PART" 2>/dev/null || true
|
||||||
|
umount "$ROOT_PART" 2>/dev/null || true
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Resize rootfs to 16GB
|
||||||
|
# ============================================================================
|
||||||
|
echo
|
||||||
|
echo -e "${BOLD}Checking partition size...${NC}"
|
||||||
|
|
||||||
|
# Get current partition size in bytes
|
||||||
|
CURRENT_SIZE=$(blockdev --getsize64 "$ROOT_PART")
|
||||||
|
TARGET_BYTES=$((16 * 1024 * 1024 * 1024)) # 16GB in bytes
|
||||||
|
CURRENT_GB=$(echo "scale=2; $CURRENT_SIZE / 1073741824" | bc)
|
||||||
|
|
||||||
|
echo " Current rootfs size: ${CURRENT_GB}GB"
|
||||||
|
|
||||||
|
if [ "$CURRENT_SIZE" -gt "$TARGET_BYTES" ]; then
|
||||||
|
echo -e "${YELLOW}Resizing rootfs to 16GB...${NC}"
|
||||||
|
|
||||||
|
# Get boot partition end in sectors
|
||||||
|
BOOT_END=$(parted -s "$DEVICE" unit s print | grep "^ 1" | awk '{print $3}' | tr -d 's')
|
||||||
|
|
||||||
|
# Calculate 16GB in sectors (512 byte sectors)
|
||||||
|
ROOT_SIZE_SECTORS=33554432
|
||||||
|
ROOT_END=$((BOOT_END + ROOT_SIZE_SECTORS))
|
||||||
|
|
||||||
|
# SHRINKING: filesystem first, then partition
|
||||||
|
echo " Checking filesystem..."
|
||||||
|
e2fsck -f -y "$ROOT_PART" 2>/dev/null || true
|
||||||
|
|
||||||
|
# Shrink filesystem to 15.5GB (leave room for partition overhead)
|
||||||
|
echo " Shrinking filesystem to 15500M..."
|
||||||
|
resize2fs "$ROOT_PART" 15500M
|
||||||
|
|
||||||
|
# Delete and recreate partition 2 with 16GB size
|
||||||
|
echo " Shrinking partition to 16GB..."
|
||||||
|
parted -s "$DEVICE" rm 2
|
||||||
|
parted -s "$DEVICE" mkpart primary ext4 $((BOOT_END + 1))s ${ROOT_END}s
|
||||||
|
|
||||||
|
# Refresh partition table
|
||||||
|
partprobe "$DEVICE"
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
# Expand filesystem to fill the partition exactly
|
||||||
|
echo " Expanding filesystem to fill partition..."
|
||||||
|
e2fsck -f -y "$ROOT_PART" 2>/dev/null || true
|
||||||
|
resize2fs "$ROOT_PART"
|
||||||
|
|
||||||
|
echo -e "${GREEN} Rootfs resized to 16GB${NC}"
|
||||||
|
elif [ "$CURRENT_SIZE" -lt "$TARGET_BYTES" ]; then
|
||||||
|
echo -e "${YELLOW} Rootfs is smaller than 16GB - expanding...${NC}"
|
||||||
|
|
||||||
|
# Get boot partition end in sectors
|
||||||
|
BOOT_END=$(parted -s "$DEVICE" unit s print | grep "^ 1" | awk '{print $3}' | tr -d 's')
|
||||||
|
ROOT_SIZE_SECTORS=33554432
|
||||||
|
ROOT_END=$((BOOT_END + ROOT_SIZE_SECTORS))
|
||||||
|
|
||||||
|
# EXPANDING: partition first, then filesystem
|
||||||
|
parted -s "$DEVICE" rm 2
|
||||||
|
parted -s "$DEVICE" mkpart primary ext4 $((BOOT_END + 1))s ${ROOT_END}s
|
||||||
|
|
||||||
|
partprobe "$DEVICE"
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
e2fsck -f -y "$ROOT_PART" 2>/dev/null || true
|
||||||
|
resize2fs "$ROOT_PART"
|
||||||
|
|
||||||
|
echo -e "${GREEN} Rootfs expanded to 16GB${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${GREEN} Rootfs already ~16GB${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Disable auto-expand on first boot
|
||||||
|
# ============================================================================
|
||||||
|
echo
|
||||||
|
echo -e "${YELLOW}Disabling auto-expand...${NC}"
|
||||||
|
TEMP_ROOT=$(mktemp -d)
|
||||||
|
mount "$ROOT_PART" "$TEMP_ROOT"
|
||||||
|
|
||||||
|
# Remove resize2fs_once service if it exists
|
||||||
|
rm -f "$TEMP_ROOT/etc/init.d/resize2fs_once"
|
||||||
|
rm -f "$TEMP_ROOT/etc/rc3.d/S01resize2fs_once"
|
||||||
|
|
||||||
|
# Disable the systemd resize service
|
||||||
|
rm -f "$TEMP_ROOT/etc/systemd/system/multi-user.target.wants/rpi-resizerootfs.service"
|
||||||
|
|
||||||
|
umount "$TEMP_ROOT"
|
||||||
|
rmdir "$TEMP_ROOT"
|
||||||
|
echo -e "${GREEN} Auto-expand disabled${NC}"
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Pull image
|
||||||
|
# ============================================================================
|
||||||
|
echo
|
||||||
|
echo -e "${BOLD}Partition table:${NC}"
|
||||||
|
parted -s "$DEVICE" unit s print
|
||||||
echo
|
echo
|
||||||
|
|
||||||
# Get the end of the last partition (partition 2 = rootfs)
|
# Get the end of the last partition (partition 2 = rootfs)
|
||||||
END_SECTOR=$(sudo parted -s "$DEVICE" unit s print | grep "^ 2" | awk '{print $3}' | tr -d 's')
|
END_SECTOR=$(parted -s "$DEVICE" unit s print | grep "^ 2" | awk '{print $3}' | tr -d 's')
|
||||||
|
|
||||||
if [ -z "$END_SECTOR" ]; then
|
if [ -z "$END_SECTOR" ]; then
|
||||||
echo "Error: Could not determine partition 2 end sector"
|
echo -e "${RED}Error: Could not determine partition 2 end sector${NC}"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -43,8 +163,8 @@ TOTAL_SECTORS=$((END_SECTOR + 2048))
|
|||||||
TOTAL_BYTES=$((TOTAL_SECTORS * 512))
|
TOTAL_BYTES=$((TOTAL_SECTORS * 512))
|
||||||
TOTAL_GB=$(echo "scale=2; $TOTAL_BYTES / 1073741824" | bc)
|
TOTAL_GB=$(echo "scale=2; $TOTAL_BYTES / 1073741824" | bc)
|
||||||
|
|
||||||
echo "Image will be approximately ${TOTAL_GB}GB (${TOTAL_SECTORS} sectors)"
|
echo -e "Image size: ${YELLOW}~${TOTAL_GB}GB${NC} (${TOTAL_SECTORS} sectors)"
|
||||||
echo "Output file: $OUTPUT"
|
echo -e "Output: ${YELLOW}$OUTPUT${NC}"
|
||||||
echo
|
echo
|
||||||
|
|
||||||
read -p "Proceed with image pull? [Y/n] " confirm
|
read -p "Proceed with image pull? [Y/n] " confirm
|
||||||
@@ -53,24 +173,24 @@ if [[ "$confirm" =~ ^[Nn]$ ]]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Pulling image..."
|
echo
|
||||||
|
echo -e "${GREEN}Pulling image...${NC}"
|
||||||
echo
|
echo
|
||||||
|
|
||||||
# Use pv if available for progress, otherwise fallback to dd status
|
# Use pv if available for progress, otherwise fallback to dd status
|
||||||
if command -v pv &> /dev/null; then
|
if command -v pv &> /dev/null; then
|
||||||
sudo dd if="$DEVICE" bs=512 count=$TOTAL_SECTORS 2>/dev/null | \
|
dd if="$DEVICE" bs=512 count=$TOTAL_SECTORS 2>/dev/null | \
|
||||||
pv -s $TOTAL_BYTES | \
|
pv -s $TOTAL_BYTES | \
|
||||||
zstd -T0 -3 > "$OUTPUT"
|
zstd -T0 -3 > "$OUTPUT"
|
||||||
else
|
else
|
||||||
sudo dd if="$DEVICE" bs=512 count=$TOTAL_SECTORS status=progress | \
|
dd if="$DEVICE" bs=512 count=$TOTAL_SECTORS status=progress | \
|
||||||
zstd -T0 -3 > "$OUTPUT"
|
zstd -T0 -3 > "$OUTPUT"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo
|
echo
|
||||||
echo "Done! Image saved to: $OUTPUT"
|
echo -e "${GREEN}Done!${NC} Image saved to: $OUTPUT"
|
||||||
ls -lh "$OUTPUT"
|
ls -lh "$OUTPUT"
|
||||||
|
|
||||||
# Show verification info
|
|
||||||
echo
|
echo
|
||||||
echo "To verify, you can check the image with:"
|
echo "To verify:"
|
||||||
echo " zstdcat $OUTPUT | fdisk -l /dev/stdin"
|
echo " zstdcat $OUTPUT | fdisk -l /dev/stdin"
|
||||||
|
|||||||
Reference in New Issue
Block a user