Skip to content

Instantly share code, notes, and snippets.

@MelvinTo
Last active March 29, 2017 01:55
Show Gist options
  • Save MelvinTo/7801ab02d7e17d638cd68bdd0c0e2f44 to your computer and use it in GitHub Desktop.
Save MelvinTo/7801ab02d7e17d638cd68bdd0c0e2f44 to your computer and use it in GitHub Desktop.
Reduce the size of an image file
#!/bin/bash
# This script will shrink a given image file to the minimal size to store existing data
# Which is very helpful to reduce the disk space fot the image file
#
# Limitation
# Only ext4 is supported
# Only support single partition
#
# melvinto@gmail.com
#
#
IMAGE_FILE=$1
function perr_and_exit()
{
echo "$1" >&2
exit 1
}
function is_image_file()
{
if [[ $(file $1) =~ startsec.*sectors$ ]]; then
return 0
else
return 1
fi
}
function usage()
{
cat << EOM
Usage: $0 device
This script is designed to shrink image file to the minimal size storing existing data. This can save time and space if you deal with image files often. Using it improperly may cause unrecoverable damage. Use at your own risk!
EOM
}
if [[ ! $(whoami) =~ "root" ]]; then
echo "This script requires root privilege!"
exit 1
fi
if ! is_image_file $IMAGE_FILE; then
echo "ERROR: $IMAGE_FILE is not an image file!"
exit 2
fi
modprobe loop || perr_and_exit "ERROR: Module loop is required by this script!"
LOOP_DEVICE=$(losetup -f)
losetup $LOOP_DEVICE $IMAGE_FILE || perr_and_exit "ERROR: Failed to mount image as loop device"
partprobe $LOOP_DEVICE || perr_and_exit "ERROR: Failed to inspect loop device"
if [[ $(ls -1 ${LOOP_DEVICE}p* | wc -l) != "1" ]]; then
losetup -d $LOOP_DEVICE
perr_and_exit "ERROR: Only support image containing one partition"
fi
LOOP_DEVICE_P1=${LOOP_DEVICE}p1
if ! parted $LOOP_DEVICE_P1 p | tail -n 2 | head -n 1 | grep ext4 &>/dev/null; then
perr_and_exit "ERROR: Only ext4 filesystem is supported"
fi
FREE_BLOCK=$(tune2fs -l /dev/sdc1 | grep "Free blocks:" | awk -F: '{print $2}' | sed 's= ==g')
TOTAL_BLOCK=$(tune2fs -l /dev/sdc1 | grep "Block count:" | awk -F: '{print $2}' | sed 's= ==g')
USED_PERCENTAGE=$(echo "100 - $FREE_BLOCK * 100 / $TOTAL_BLOCK + 1" | bc) # add additional 1% for safety
e2fsck -f $LOOP_DEVICE_P1 || perr_and_exit "ERROR: Failed to run e2fsck on partition $LOOP_DEVICE_P1"
# This command will do the actual work to shrink the filesystem
# BUT this command will NOT touch partition, so partition still
# takes the origin space, and image file is still as large as original size
#
# -M means resize to minimal
resize2fs -M $LOOP_DEVICE_P1 || perr_and_exit "ERROR: Failed to run resize2fs on partition $LOOP_DEVICE_P1"
losetup -d $LOOP_DEVICE
# Resize the partition so that the new filesystem fits the new partition
parted $IMAGE_FILE rm 1 mkpart primary ext4 0% ${USED_PERCENTAGE}% || perr_and_exit "ERROR: Failed to shrink the partition"
# Image file is still very large, need to truncate it to the right size
PARTITION_SIZE=$(parted $IMAGE_FILE unit b p | tail -n 2 | head -n 1 | grep ext4 | awk '{print $3}' | sed 's=B==')
TRUNCATE_SIZE=$(echo "$PARTITION_SIZE * 1.1 / 1024 / 1024" | bc)
# Truncate image file
truncate --size=${TRUNCATE_SIZE}M $IMAGE_FILE
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment