#!/bin/sh DEPENDENCIES='tune2fs parted lsblk blkid' READ_ONLY=y die() { echo "Error: $*" >&2 exit 1 } parted() { if [ "$READ_ONLY" ] then echo parted "$@" else (set -x; command parted "$@") fi } check_deps() { for dep in $DEPENDENCIES do command -v "$dep" >/dev/null || die "Missing dependency: $dep" done } filesystem_size() { [ "$1" ] || return fstype=$(blkid -o value -s TYPE "$1") || die "blkid could not determine FS type" case "$fstype" in ext[234]) filesystem_size_e2fs "$1";; *) die "Unsupported filesystem type: $FSTYPE";; esac } filesystem_size_e2fs() { tune2fs -l "$1" | { unset block_count block_size while read line do case "$line" in 'Block count:'*) block_count=${line#*:} ;; 'Block size:'*) block_size=${line#*:} ;; esac done [ "$block_count" -a "$block_size" ] || return echo $((block_count * block_size)) } } get_block_device_names() # Outputs: $PARTNAME $DEVNAME { [ -b "$1" ] || die "Not a block device: $1" PARTNAME=/dev/$(lsblk -no KNAME "$1") || die "lsblk failed to get kernel device name for $1" [ -b "$PARTNAME" ] || die "Not a block device: $PARTNAME" DEVNAME=/dev/$(lsblk -no PKNAME "$PARTNAME") || die "lsblk failed to get parent device name for $PARTNAME" [ -b "$DEVNAME" ] || die "Not a block device: $DEVNAME" } shrink_partition() { get_block_device_names "$1" partnum=${PARTNAME#$DEVNAME} [ "$PARTNAME" = "$DEVNAME$partnum" ] || die "Cannot compute partition number; (partname, devname) = ($PARTNAME, $DEVNAME)" partsize=$(filesystem_size "$PARTNAME") || die "Failed to obtain fs size" parted "$DEVNAME" resizepart "$partnum" $((partsize))B } check_deps shrink_partition "$1"