#!/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 -euo pipefail

COREOS_RESIZE="3884DD41-8582-4404-B9A8-E9B84F2DF50E"

while read -r dev_info; do
    eval "$dev_info"
    echo "Found ${SOURCE}"
    if [[ $(cgpt show -t "${SOURCE}") != "${COREOS_RESIZE}" ]]
    then
        continue
    fi

    echo "Checking size of ${SOURCE}"
    old_size=$(blockdev --getsz "${SOURCE}")
    cgpt resize "${SOURCE}"

    # 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 "${SOURCE}"; then break; fi; sleep 0.5; done)" ]]; then
        echo "Old size kept for ${SOURCE}"
        continue
    fi
    echo "Resized partition ${SOURCE}"

    # 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 "${SOURCE}" | \
                        awk -v "d=${SOURCE}" -e 'd == $8 {print $2}')
            btrfs filesystem resize "${device_id}:max" "${TARGET}"
            ;;
        "ext4")
            resize2fs "${SOURCE}"
            ;;
        "xfs")
            xfs_growfs "${SOURCE}"
            ;;
    esac
    echo "Resized filesystem in ${SOURCE}"
# Find matching mount points and unique them by SOURCE so that we don't try to
# resize them more than once because bind mounts can appear as duplicates.
done < <(findmnt --types btrfs,ext4,xfs --options rw --pairs --shell --output SOURCE,FSTYPE,TARGET | sort -u -k1,1)
