#!/bin/bash
# SPDX-License-Identifier: GPL-2.0-or-later
# /usr/sbin/pocketds-fs-resize -- first-boot expansion of PARTLABEL=STORAGE
# (the rootfs partition; sda2 on a typical Pocket DS SD layout) to fill
# the entire SD/eMMC, followed by an online ext4 resize. Triggered by
# pocketds-fs-resize.service when /storage/.please_resize_me exists --
# pocketds.spec %post base writes that marker on every install/upgrade,
# but the systemd unit's ExecStartPost removes it after a successful run
# so on every subsequent boot the unit's ConditionPathExists is false.
#
# Two-phase resize:
#   1. sgdisk -e <disk>     -- move the GPT backup header to the real
#                              end of the disk (otherwise parted
#                              refuses to extend past the original
#                              backup-header location)
#   2. parted resizepart    -- extend partition to 100% of free space
#   3. partprobe            -- kernel re-reads partition table
#   4. resize2fs            -- online grow ext4 to fill new partition
#
# Online resize2fs is supported by every kernel since ~3.6; the rootfs
# stays mounted r/w throughout. If the partition is already at the disk
# end (no slack to consume) parted's resizepart is effectively a no-op
# and the script exits 0.

set -eu -o pipefail

ROOT_DEV=$(/usr/sbin/blkid -L STORAGE 2>/dev/null || /usr/bin/findfs PARTLABEL=STORAGE)
if [ -z "${ROOT_DEV:-}" ]; then
    echo "pocketds-fs-resize: no PARTLABEL=STORAGE; nothing to do" >&2
    exit 0
fi

# /dev/sda2 -> parent /dev/sda + partnum 2
PARENT_NAME=$(/usr/bin/lsblk -no PKNAME "$ROOT_DEV")
PARENT="/dev/$PARENT_NAME"
PART_NUM=$(echo "$ROOT_DEV" | /usr/bin/grep -oE '[0-9]+$')

echo "pocketds-fs-resize: ROOT=$ROOT_DEV  PARENT=$PARENT  PART_NUM=$PART_NUM"

# Step 1: move GPT backup header to disk end. Quiet on already-aligned.
if /usr/bin/lsblk -no PTTYPE "$PARENT" | /usr/bin/grep -q '^gpt$'; then
    /usr/sbin/sgdisk -e "$PARENT" >/dev/null 2>&1 || true
fi

# Capture the existing GPT partition Name BEFORE resizing -- some
# parted versions (observed on aarch64 / Fedora 44) clear it during
# `resizepart`, which then makes `root=PARTLABEL=STORAGE` lookups in
# our busybox /init fail and PID 1 panic. We re-set it after.
ORIG_NAME=""
if /usr/bin/lsblk -no PTTYPE "$PARENT" | /usr/bin/grep -q '^gpt$'; then
    ORIG_NAME=$(/usr/sbin/sgdisk -i "$PART_NUM" "$PARENT" 2>/dev/null \
                | /usr/bin/awk -F"'" '/Partition name/{print $2}')
    echo "pocketds-fs-resize: pre-resize PARTNAME=\"$ORIG_NAME\""
fi

# Step 2: extend the partition. parted's --script keeps it noninteractive;
# the trailing 100% literally means "to the end of the disk".
/usr/sbin/parted -s -- "$PARENT" resizepart "$PART_NUM" 100% || {
    echo "pocketds-fs-resize: parted resizepart failed (already at max?)" >&2
}

# Step 2b: re-set the GPT Name if parted dropped it. Idempotent; sgdisk
# rewrites only the partition's name field. If ORIG_NAME was empty
# (no Name was set, e.g. mkosi.repart 20-storage.conf wasn't applied),
# default to "STORAGE" so PARTLABEL=STORAGE lookups always work.
if /usr/bin/lsblk -no PTTYPE "$PARENT" | /usr/bin/grep -q '^gpt$'; then
    NEW_NAME=$(/usr/sbin/sgdisk -i "$PART_NUM" "$PARENT" 2>/dev/null \
               | /usr/bin/awk -F"'" '/Partition name/{print $2}')
    if [ -z "$NEW_NAME" ] || [ "$NEW_NAME" != "$ORIG_NAME" ]; then
        SET_NAME=${ORIG_NAME:-STORAGE}
        echo "pocketds-fs-resize: re-setting PARTNAME=\"$SET_NAME\" (was=\"$NEW_NAME\")"
        /usr/sbin/sgdisk -c "$PART_NUM:$SET_NAME" "$PARENT" >/dev/null 2>&1 || true
    fi
fi

# Step 3: kernel re-reads partition table. Without this the new size
# isn't visible to resize2fs, which then no-ops.
/usr/sbin/partprobe "$PARENT" 2>/dev/null || \
    /usr/bin/udevadm trigger -s block --action=change || true
/usr/bin/udevadm settle || true

# Step 4: online grow ext4. Safe on a mounted r/w rootfs.
/usr/sbin/resize2fs "$ROOT_DEV"

echo "pocketds-fs-resize: done"
