Pidora-2014-Shrink-Script

From CDOT Wiki
Jump to: navigation, search
#!/bin/bash
#
# Script to take an SD card image (such as for a
# Raspberry Pi) with a dos disklabel and two 
# partitions (boot vfat and rootfs extX), and shrink
# it so that the rootfs (2nd partition) is as short 
# as possible plus a small free space allowance.
#
# To use this script, provide the image name as
# the first argument.
#
# Requirements (Fedora 17 package):
# bash      (bash)
# fdisk     (util-linux)
# e2fsck    (e2fsprogs)
# resize2fs (e2fsprogs)
# kpartx    (kpartx)
# truncate  (coreutils)
#
# Version 1.0                       2013-03-14
#
# Authors:
# Chris Tyler, Seneca College       2013-03-14
#

# Copyright (C)2013 Chris Tyler, Seneca College, and others
# (see Authors, above).
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301 USA
#

# Desired free space allowance in MB
FREESPACE=220

# Check number of args
if [ "$#" -ne 1 ]
then
    echo "$0: Usage: $0 imagename" >&2
    exit 10
fi

# Image name, from first argument
IMAGE="$1"

# Make sure the image is a file
if [ ! -f "$IMAGE" ]
then
    echo "$0: '$IMAGE' is not a valid image name." >&2
    exit 1
fi

echo "Shrinking $IMAGE to minimum size plus $FREESPACE MB."

# Make a block device out of each partition
DEVICE="/dev/mapper/$(kpartx -av "$IMAGE" | sed -n "2s/add map \(.*\) (.*/\1/p")"

# Complain if kpartx fails
if [ "$?" -ne 0 ]
then
    echo "$0: '$IMAGE' has a bad partition table." >&2
    exit 2
fi

sleep 3    # give the kpartx stuff time to settle (?!)

# Create a placeholder file to reserve free space
MOUNTPOINT="$(mktemp)"
RESERVE="${MOUNTPOINT}/resize-reserve"
rm "${MOUNTPOINT}" 2>/dev/null 
mkdir -p "${MOUNTPOINT}"
mount "${DEVICE}" "${MOUNTPOINT}"
df -h "${MOUNTPOINT}"
dd if=/dev/zero of=${RESERVE} bs=1M count=${FREESPACE}
df -h "${MOUNTPOINT}"
umount "${DEVICE}"

# fsck and minimally resize the partition

TEMPFILE=$(mktemp)
e2fsck -fy "$DEVICE" # >$TEMPFILE 2>&1
RESULT=$?
if [ "$RESULT" -gt 2 ]
then
    echo "$0: e2fsck on $DEVICE reported errors ($RESULT):" >&2
    cat $TEMPFILE
    exit 3
fi

NEWBLOCKS=$(resize2fs -M "$DEVICE" 2>&1 | sed -n "s/.* \([0-9]\+\) blocks long.*/\1/p")
if [ "$NEWBLOCKS" = "" ]
then
    echo "$0: error: New block size is null." >&2
    exit 4
fi

OLDK=$(expr  "$NEWBLOCKS" '*' 4     ) # 4K blocks to KB
#FREEK=$(expr "$FREESPACE" '*' 1024  ) # MB to KB
FREEK=0
NEWK=$(expr "$OLDK" + "$FREEK" )
NEWSECTORS=$(expr "$NEWK" '*' 2 )

# Unmake block devices
kpartx -d "$IMAGE" >/dev/null 2>&1

# Repartition
echo -e "d\n2\nn\np\n2\n\n+${NEWSECTORS}\nw\np" | fdisk "$IMAGE" >/dev/null 2>&1
fdisk -l "$IMAGE"

# Make a block device out of each partition
DEVICE="/dev/mapper/$(kpartx -av "$IMAGE" | sed -n "2s/add map \(.*\) (.*/\1/p")"
sleep 3

# Remove the placeholder file
mount "${DEVICE}" "${MOUNTPOINT}"
df -h "${MOUNTPOINT}"
rm -v "${RESERVE}"
df -h "${MOUNTPOINT}"
umount "${DEVICE}"

# Unmake the block devices
kpartx -d "$IMAGE" >/dev/null 2>&1

# Get the last block number
NEWIMAGEBLOCKS=$(fdisk -l "$IMAGE" | tail -1 | tr -s " " | cut -d" " -f3)
NEWIMAGEK=$(expr ${NEWIMAGEBLOCKS} '/' 2 + 1)

# Truncate the image
truncate -s ${NEWIMAGEK}K ${IMAGE}

# Done!
echo "Image shrink completed."
exit 0