Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

META | DietPi-Image: Create from disk or shrink an image file #2693

Closed
wants to merge 6 commits into from
Closed
182 changes: 182 additions & 0 deletions .meta/dietpi-image
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
#!/bin/bash
{
#////////////////////////////////////
# DietPi Image creation/finalise Script
#
#////////////////////////////////////
# Created by Daniel Knight / daniel.knight@dietpi.com / dietpi.com
# Updated by MichaIng / micha@dietpi.com / dietpi.com
#
#////////////////////////////////////
#
# Usage:
# - ./dietpi-image <source> <root_partition> <name> [GPT]
# source: /dev/* | *.img
# root_partition: 0 | 1 | 2 | ... ("0" means no partition table)
# name: DietPi_<device>-<arch>-<distro>
# device: RPi | OdroidXU4 | NativePC-BIOS | ...
# arch: ARMv6 | ARMv7 | ARMv8 | x86_64
# distro: Stretch | Buster
# "GPT": Required to correctly handle GPT partition tables
#////////////////////////////////////

# Grab input
SOURCE=$1
ROOT=$2
NAME=$3
[[ $4 == GPT ]] && GPT=1 || GPT=0

# Check root
(( $UID )) && { echo 'ERROR: This script must run with root permissions. Please retry with "sudo".'; exit 1; }

# Force locale
export LC_ALL='en_GB.UTF-8'

# Move to /tmp for temp file creation
cd /tmp

# Check source
SOURCE_IS_FILE=1
if [[ $SOURCE == /dev/* ]]; then

SOURCE_IS_FILE=0
[[ -b $SOURCE ]] || { echo "ERROR: Source block device ($SOURCE) does not exist"; exit 1; }

else

[[ -f $SOURCE ]] || { echo "ERROR: Source image file ($SOURCE) does not exist"; exit 1; }

fi

# Install required packages
apt-get -qq install gdisk dosfstools parted zerofree p7zip

# GPT images (RockPro64) after reading the image AND again after shrinking
# To fix:
# - GPT PMBR size mismatch (4458495 != 15523839) will be corrected by w(rite).
# - 15523806
# RUN
# - gdisk "$IMAGE_FP/$IMAGE_NAME"
# w | y
(( $GPT )) && gdisk $SOURCE

# Attach .img file to loop device
if (( $SOURCE_IS_FILE )); then

modprobe loop
SOURCE_FILE=$SOURCE
SOURCE=$(losetup -f)
losetup $SOURCE "$SOURCE_FILE"
partprobe $SOURCE

fi

# Estimate root partition dev
ROOT_DEV=$SOURCE
if [[ $ROOT != 0 ]]; then

[[ $SOURCE =~ /mmcblk || $SOURCE =~ /nvme || $SOURCE =~ /loop ]] && ROOT_DEV="${SOURCE}p${ROOT}"
[[ $SOURCE =~ /[sh]d[a-z] ]] && ROOT_DEV="${SOURCE}${ROOT}"

fi

# Fsck
e2fsck -f $ROOT_DEV

# Shrink file system to minimum
# - Run multiple times until no change is done any more
out=''
FS_SIZE=0
while :
do

resize2fs -M $ROOT_DEV 2>&1 | tee resize2fs_out
if out=$(grep -m1 'Nothing to do!' resize2fs_out); then

# Re-add 4M to be failsafe, was required on Raspbian Buster for successful boot
FS_SIZE=$(mawk '{print $5 + 1024}' <<< $out) # 4k blocks
rm resize2fs_out
resize2fs $ROOT_DEV $FS_SIZE
echo "[ OK ] Shrunken root file system to $(( $FS_SIZE / 256 + 1 )) MiB"
FS_SIZE=$(( $FS_SIZE * 8 )) # 4k blocks => 512 byte sectors
break

fi

done

# Estimate minimum end sector
PART_START=$(fdisk -l -o Start $SOURCE | tail -1) # 512 byte sectors
PART_END=$(( $PART_START + $FS_SIZE ))

# Resize partition to minimum
echo '[ INFO ] Shrinking partition. Please press "y" when asked.'
parted $SOURCE unit s resizepart 2 $PART_END

# Sync changes to disk now
sync

# Re-read partition layout
partprobe $SOURCE

# Override free space with zeros to purge removed data and allow further image/archive size reduction
zerofree -v $ROOT_DEV

# GPT images (RockPro64) after reading the image AND again after shrinking
# To fix:
# - GPT PMBR size mismatch (4458495 != 15523839) will be corrected by w(rite).
# - 15523806
# RUN
# - gdisk "$IMAGE_FP/$IMAGE_NAME"
# w | y
(( $GPT )) && gdisk $SOURCE

# Estimate used size
# - Without partition table, the whole raw disk space needs to be cloned
if [[ $ROOT == 0 ]]; then

PART_SIZE=$(lsblk -o SIZE --bytes $SOURCE) # Byte

else

PART_SIZE=$(( ( $(fdisk -l -o End $SOURCE | tail -1) + 1 ) * 512 )) # 512 byte sectors => Byte

fi
IMAGE_SIZE=$(( $PART_SIZE + 512*256 )) # 64 byte for secondary GPT + safety net

# Create final image in /root
cd /root

# If source is an image file, truncate to used size
if (( $SOURCE_IS_FILE )); then

truncate --size=$IMAGE_SIZE "$SOURCE_FILE"

# else create .img file from block device source via dd, stopping at block count that matches used size
else

[[ -f $NAME.img ]] && rm $NAME.img
dd if=$SOURCE of=$NAME.img bs=1M status=progress count=$(( $IMAGE_SIZE / 1024 / 1024 + 1 ))
echo "Image file created: /root/$NAME.img"

fi

# Generate hashes: MD5, SHA1, SHA256
cat << _EOF_ > hash.txt
MD5: $(md5sum $NAME.img | mawk '{print $1}')
SHA1: $(sha1sum $NAME.img | mawk '{print $1}')
SHA256: $(sha256sum $NAME.img | mawk '{print $1}')
_EOF_

# Download current README
wget https://raw.githubusercontent.com/MichaIng/DietPi/master/README.md -O README.md

# Generate 7z archive
# NB: LZMA2 ultra compression method requires 2G RAM
[[ -f $NAME.7z ]] && rm $NAME.7z
7zr a -m0=lzma2 -mx=9 $NAME.7z $NAME.img hash.txt README.md

#-----------------------------------------------------------------------------------
exit
#-----------------------------------------------------------------------------------
}