Last active
March 29, 2017 01:55
-
-
Save MelvinTo/7801ab02d7e17d638cd68bdd0c0e2f44 to your computer and use it in GitHub Desktop.
Reduce the size of an image file
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 | |
# | |
# [email protected] | |
# | |
# | |
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