#!/bin/bash
# Automatic resize of btrfs and ext4 filesystems.
#
# The only partitions that may be considered:
#  - GPT partitions with the 'coreos-resize'
#  - Have a btrfs, ext4 or xfs filesystem mounted read-write
#  - Have at least 2MB of unpartitioned space to grow into (cgpt default)
#
# This last restriction means that if cgpt resize succeeds in extending
# the partition but the filesystem resize fails the operation will not
# be re-attempted by this script later unless the disk grows even more.

set -e -o pipefail

COREOS_RESIZE="3884dd41-8582-4404-b9a8-e9b84f2df50e"

declare -a DEV_LIST
mapfile DEV_LIST < <(lsblk -P -o NAME,PARTTYPE,FSTYPE,MOUNTPOINT)

for dev_info in "${DEV_LIST[@]}"; do
    eval "$dev_info"
    NAME=${NAME//\!//}
    echo "Found ${NAME}"
    if [[ "${PARTTYPE}" != "${COREOS_RESIZE}" ]] || \
       [[ -z "${NAME}" ]] || \
       [[ -z "${MOUNTPOINT}" ]] || \
       [[ ! -w "${MOUNTPOINT}" ]] || \
       [[ "${FSTYPE}" != "btrfs" && "${FSTYPE}" != "ext4" && "${FSTYPE}" != "xfs" ]]
    then
        continue
    fi

    device="/dev/${NAME}"
    echo "Checking size of ${device}"
    old_size=$(blockdev --getsz "${device}")
    cgpt resize "${device}"

    # Only resize filesystem if the partition changed
    # (need to retry in case udev triggered a kernel partition re-read which causes the device to disappear shortly,
    # this condition here works under the assumption that we always get the new size because cgpt notifies the kernel
    # about it before it touches the disk).
    if [[ "${old_size}" -eq "$(for s in {1..20}; do if blockdev --getsz "${device}"; then break; fi; sleep 0.5; done)" ]]; then
        echo "Old size kept for ${device}"
        continue
    fi
    echo "Resized partition ${device}"

    # The device may still disappear here because we can't know if udev already retriggered a full partition re-read
    # (a solution could be to use synthetic uevent tagging or to rerun the resize commands a few times).
    case "${FSTYPE}" in
        "btrfs")
            # map the device name to the btrfs device id
            device_id=$(btrfs filesystem show -d "${device}" | \
                        awk -v "d=${device}" -e 'd == $8 {print $2}')
            btrfs filesystem resize "${device_id}:max" "${MOUNTPOINT}"
            ;;
        "ext4")
            resize2fs "${device}"
            ;;
        "xfs")
            xfs_growfs "${MOUNTPOINT}"
            ;;
    esac
    echo "Resized filesystem in ${device}"
done
