Files
forgefirm/meta-forgefirm/recipes-forgefirm/slotmigrate/files/slotmigrate
T
2026-09-08 16:21:37 -04:00

136 lines
4.6 KiB
Bash

#!/bin/sh
# Copyright 2020-2026 514 LLC d/b/a OpenGlow
# Written by Scott Wiederhold
# https://community.openglow.org
# SPDX-License-Identifier: MIT
#
# Legacy-layout migration: reclaims the legacy ForgeFIRM partition (p4)
# and grows /data (p3) to the end of the eMMC, restoring the factory
# disk footprint. Runs in rcS before mountall, so /data is not yet
# mounted. Every step is keyed off the actual disk state - an
# interrupted run resumes on the next boot; a factory-layout disk is a
# fast no-op. Only acts when booted from an eMMC rootfs slot: SD boots
# (bench/dev) never touch the eMMC and p4 itself must not saw off its
# own branch.
DISK=/dev/mmcblk2
P3=${DISK}p3
log () {
echo "slotmigrate: $*"
echo "slotmigrate: $*" > /dev/kmsg 2>/dev/null
}
case "$1" in
start|"") ;;
*) exit 0 ;;
esac
# Runs at S02, possibly before the sysfs/proc mount scripts.
mountpoint -q /proc 2>/dev/null || mount -t proc proc /proc 2>/dev/null
mountpoint -q /sys 2>/dev/null || mount -t sysfs sysfs /sys 2>/dev/null
[ -r /proc/cmdline ] || exit 0
ROOT=$(sed -n 's/.*root=\([^ ]*\).*/\1/p' /proc/cmdline)
case "$ROOT" in
/dev/mmcblk2p1|/dev/mmcblk2p2) ;;
*) exit 0 ;;
esac
command -v sfdisk >/dev/null || { log "sfdisk missing, skipping"; exit 0; }
if grep -q "^${P3} " /proc/mounts; then
log "/data already mounted, skipping this boot"
exit 0
fi
# --- partition table -----------------------------------------------------
if sfdisk -d "$DISK" 2>/dev/null | grep -q "^${DISK}p4"; then
log "removing legacy partition p4"
sfdisk --no-reread --force --delete "$DISK" 4 >/dev/null 2>&1 \
|| { log "p4 delete FAILED"; exit 0; }
partx -d --nr 4 "$DISK" 2>/dev/null
fi
DISK_SECT=$(cat /sys/class/block/mmcblk2/size 2>/dev/null)
P3_START=$(sfdisk -d "$DISK" 2>/dev/null | sed -n "s|^${P3} : start=[ ]*\([0-9]*\),.*|\1|p")
P3_SIZE=$(sfdisk -d "$DISK" 2>/dev/null | sed -n "s|^${P3} .*size=[ ]*\([0-9]*\),.*|\1|p")
[ -n "$DISK_SECT" ] && [ -n "$P3_START" ] && [ -n "$P3_SIZE" ] \
|| { log "cannot read disk/p3 geometry"; exit 0; }
# 2048-sector tolerance (mirrors the filesystem branch): on a disk where
# the grow cannot land exactly on the last sector, an exact comparison
# would rewrite the MBR at S02 on EVERY boot - and a power loss inside
# that window costs the partition table and /data.
if [ $((P3_START + P3_SIZE)) -lt $((DISK_SECT - 2048)) ]; then
log "growing p3 toward the end of the disk ($((P3_START + P3_SIZE)) -> $DISK_SECT sectors)"
echo ", +" | sfdisk --no-reread --force -N 3 "$DISK" >/dev/null 2>&1 \
|| { log "p3 grow FAILED"; exit 0; }
partx -u --nr 3 "$DISK" 2>/dev/null
P3_NEW=$(sfdisk -d "$DISK" 2>/dev/null | sed -n "s|^${P3} .*size=[ ]*\([0-9]*\),.*|\1|p")
if [ -n "$P3_NEW" ] && [ "$P3_NEW" = "$P3_SIZE" ]; then
log "p3 grow made no progress ($P3_SIZE sectors); leaving the table alone"
exit 0
fi
fi
# --- filesystem ----------------------------------------------------------
PART_SECT=$(cat /sys/class/block/mmcblk2p3/size 2>/dev/null)
[ -n "$PART_SECT" ] || { log "cannot read p3 size"; exit 0; }
FS_BLOCKS=$(tune2fs -l "$P3" 2>/dev/null | sed -n 's/^Block count:[ ]*//p')
FS_BSIZE=$(tune2fs -l "$P3" 2>/dev/null | sed -n 's/^Block size:[ ]*//p')
[ -n "$FS_BLOCKS" ] && [ -n "$FS_BSIZE" ] || { log "cannot read p3 filesystem"; exit 0; }
# Bounded retry: a resize that keeps failing must not cost a full
# e2fsck pass on every boot forever. Nothing else is mounted this
# early, so the attempt counter lives on p3 itself.
TRY_FILE=.slotmigrate-resize-tries
read_tries () {
TRIES=0
T=$(mktemp -d) || return
if mount "$P3" "$T" 2>/dev/null; then
TRIES=$(cat "$T/$TRY_FILE" 2>/dev/null)
umount "$T" 2>/dev/null
fi
rmdir "$T" 2>/dev/null
case "$TRIES" in
''|*[!0-9]*) TRIES=0 ;;
esac
}
write_tries () {
T=$(mktemp -d) || return
if mount "$P3" "$T" 2>/dev/null; then
if [ "$1" -gt 0 ]; then
echo "$1" > "$T/$TRY_FILE"
else
rm -f "$T/$TRY_FILE"
fi
umount "$T" 2>/dev/null
fi
rmdir "$T" 2>/dev/null
}
FS_SECT=$((FS_BLOCKS * (FS_BSIZE / 512)))
if [ "$FS_SECT" -lt $((PART_SECT - 2048)) ]; then
read_tries
if [ "$TRIES" -ge 3 ]; then
log "resize2fs failed $TRIES times; giving up (grow /data manually with resize2fs $P3)"
exit 0
fi
log "growing /data filesystem ($FS_SECT -> $PART_SECT sectors)"
e2fsck -f -p "$P3" >/dev/null 2>&1
RC=$?
if [ "$RC" -ge 4 ]; then
log "e2fsck found errors (rc=$RC), NOT resizing"
exit 0
fi
if resize2fs "$P3" >/dev/null 2>&1; then
log "/data grown to full size"
write_tries 0
else
log "resize2fs FAILED (attempt $((TRIES + 1)) of 3)"
write_tries $((TRIES + 1))
fi
fi
exit 0