#!/bin/bash

#
# Fichier image
#
# Vérification des arguments
if [ -z "$1" ]
    then
    echo
    echo -e "\033[38;2;255;0;0;1m[ERREUR] Indiquer le fichier image à redimentionner\e[0m"
    echo
    exit 1
fi
imageFile=$1

#
# Demande de confirmation
#
echo -e "\033[38;2;255;0;0;1mATTENTION! ce script est fait pour redimentionner les images de cartes SD de Raspberry Pi utilisant Raspberry Pi OS. L'utilisation de ce script pour un autre utilisation risque de produire des résultats inattendus.\e[0m"
echo -e "\033[38;2;255;0;0;1mPour plus de détails et l'explication du script, voir ici: https://www.jesus-forain.fr/blog/reduire-la-taille-d-une-image-de-carte-sd-d-un-raspberry-pi-136.html\e[0m"
echo
echo -e "\033[38;2;255;0;0;1mLe fichier image donné en argument sera modifié, il est conseillé de faire une copie de sauvegarde de ce fichier image\e[0m"
echo
echo -e "Fichier à réduire: \033[38;2;0;255;255;1m$imageFile\e[0m"
echo -n "Confirmer (O/N)? "
read confirm
confirm=$(echo $confirm | tr '[:upper:]' '[:lower:]')
if [ $confirm != o ]; then
    exit 0
fi

#
# Recherche d'un périphérique loop libre
#
echo -e "\033[38;2;0;255;255;1m[INFO] Recherche d'un périphérique /dev/loopX libre\e[0m"
loopNb=0
nbLoopDevices=$(ls -l /dev | grep -e "loop[0-9]\+" | wc -l)
echo -e "$nbLoopDevices périphériques /dev/loopX disponibles"
losetup | grep /dev/loop$loopNb > /dev/null
while (( $? == 0 )); do
    echo "/dev/loop$loopNb est utilisé"
    loopNb=$(($loopNb + 1))

    # Plus de loopX disponible
    if (($loopNb >= $nbLoopDevices)); then
        echo -e "\033[38;2;255;0;0;1m[ERREUR] aucun périphérique /dev/loopX libre\e[0m"
        exit 1
    fi
    losetup | grep /dev/loop$loopNb > /dev/null
done

echo -e "\033[38;2;0;255;255;1m[INFO] /dev/loop$loopNb est libre\e[0m"

#
# Montage de l'image
#
echo -e "\033[38;2;0;255;255;1m[INFO] Montage de l'image\e[0m"
losetup /dev/loop$loopNb $imageFile
if (( $? != 0 )); then
    echo -e "\033[38;2;255;0;0;1m[ERREUR] échec du montage de l'image\e[0m"
    exit 1
fi

#
# Rechargement de la table des partitions
#
echo -e "\033[38;2;0;255;255;1m[INFO] Rechargement de la table des partitions\e[0m"
partprobe /dev/loop$loopNb
if (( $? != 0 )); then
    echo -e "\033[38;2;255;0;0;1m[ERREUR] échec du rechargement de la table des partitione\e[0m"
    exit 1
fi

# Affichage de la structure des partitions
echo -e "\033[38;2;0;255;255;1m[INFO] affichage de la structure des partitions\e[0m"
lsblk /dev/loop$loopNb

#
# Vérification du filesystem
#
echo -e "\033[38;2;0;255;255;1m[INFO] Vérification du filesystem\e[0m"
e2fsck -y -f /dev/loop${loopNb}p2
# Pas de vérifications du code retour car s'il y a des corrections la valeur est "faux"

#
# Redimensionnement du filesystem
#
echo -e "\033[38;2;0;255;255;1m[INFO] Redimensionnement du filesystem. Cette étape est la plus longue du script et peut durer plusieurs dizaines de minutes selon la taille du fichier image\e[0m"
resize2fs -p /dev/loop${loopNb}p2 -M
if (( $? != 0 )); then
    echo -e "\033[38;2;255;0;0;1m[ERREUR] échec du redimensionnement du filesystem\e[0m"
    exit 1
fi

# Récupération d'informations sur les partitions
blockCount=$(dumpe2fs -h /dev/loop${loopNb}p2 2>/dev/null | grep "Block count" | awk '{print $3}')
blockSize=$(dumpe2fs -h /dev/loop${loopNb}p2 2>/dev/null | grep "Block size" | awk '{print $3}')
sectorsSize=$(fdisk -l $imageFile | grep Units | awk '{print $8}')

partitionStart=$(fdisk -l /dev/loop$loopNb | grep -e "/dev/loop${loopNb}p2.*Linux" | awk '{print $2}')
partitionEnd=$((($blockCount * $blockSize) / $sectorsSize + 1))

echo -e "\033[38;2;0;255;255;1m[INFO] Informations sur les partitions\e[0m"
echo "nombre de blocs   : $blockCount"
echo "taille de bloc    : $blockSize octets"
echo "taille de secteur : $sectorsSize octets"
echo "début de partition: $partitionStart"
echo "fin de partition  : ($blockCount * $blockSize) / $sectorsSize + 1) = $partitionEnd"

#
# Modification des partitions
#

echo -e "\033[38;2;0;255;255;1m[INFO] Modification des partitions\e[0m"

# Script fdisk trouvé ici:
# https://superuser.com/questions/332252/how-to-create-and-format-a-partition-using-a-bash-script
sed -e 's/\s*\([\+0-9a-zA-Z]*\).*/\1/' << EOF | fdisk /dev/loop$loopNb
    p # afficher la table des partitions
    d # menu supprimer la partition
    2 # supprimer la partition N°2
    n # menu nouvelle partition
    p # type de partition (primaire)
    2 # appliquer à la partition N°2
    $partitionStart # début de la partition
    +$partitionEnd # fin de la partition. Attention, il y a un '+' devant la taille
     # Do you want to remove the signature? [Y]es/[N]o: => non par défaut
    p # affichage du nouveau partitionnement
    w # écriture des modifications sur les partitions
    q # Quitter
EOF

# Pas de vérification de la valeur de retour car ça sera toujours faux à cause du message "Re-reading the partition table failed.: Argument invalide"

# Récupération d'infos sur la nouvelle partition
newPartitionEnd=$(fdisk -l /dev/loop$loopNb | grep -e "/dev/loop${loopNb}p2.*Linux" | awk '{print $3}')
newFileSize=$((($newPartitionEnd + 1) * $sectorsSize))

#
# Redimentionnement du fichier image
#

# Détacher la disque virtuel /dev/loopX
echo -e "\033[38;2;0;255;255;1m[INFO] Détacher la disque virtuel /dev/loop$loopNb\e[0m"
losetup -d /dev/loop$loopNb 
if (( $? != 0 )); then
    echo -e "\033[38;2;255;0;0;1m[ERREUR] échec du détachement du périphérique /dev/loop$loopNb\e[0m"
    exit 1
fi

# Réduction du fichier image
echo -e "\033[38;2;0;255;255;1m[INFO] Réduction du fichier image à $newFileSize octets\e[0m"
truncate -s $newFileSize $imageFile
if (( $? != 0 )); then
    echo -e "\033[38;2;255;0;0;1m[ERREUR] échec de la réduction du fichier image\e[0m"
    exit 1
fi

echo
echo -e "\033[38;2;0;255;0;1mTERMINÉ\e[0m"
echo

