#!/bin/bash # Copyright (C) 2010,2011,2012 Zakhar for ubuntu-fr # 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. # The text of the GNU General Public License is available at # http://www.gnu.org/licenses/gpl-2.0.html; if you cannot access it, # write to the Free Software Foundation, Inc., 51 Franklin Street, # Fifth Floor, Boston, MA 02110-1301 USA. #=========================================================== # Fonction : # ---------- # - Rassemble des fichiers découpés par XtremSplit # - Vérifie le MD5 s'il est inclus dans les fichiers source # # Usage : # ------- # tuXtremMerge Nom_Du_Fichier_NNN.xtm # (N'importe lequel des fichiers fonctionne : 001 ou autre) # # Tested : Ubuntu Lucid,Karmic, Busybox (Synology DS211j, DS1010+, DS411+, DS209, DS211+) # ------ # # Version : 1.7.1 # ------- # # Date : 2012-01-21 # ----- # # Author : Zakhar (ubuntu-fr) # ------ # # Contributor : // Ajouter votre nom si vous contribuez et redistribuez // # ----------- # Hypnose @ ubuntu.fr : amélioration des spécifications # Totor @ ubuntu.fr : amélioration et optimisation du code # Moonface @ ubuntu.fr : témoignage, test et debug de fonctionnement sur Synology DS211j # stadros83 @ ubuntu.fr : debug sur Synology DS1010+ (merci pour la patience !) # A partir de là, certaines spécificités Synology (Busybox) sont # dans le script "compagnon" busyXtremMerge # zootroopa @ ubuntu.fr : debug et test sur Synology DS411+ # Gajo22 @ ubuntu.fr : témoignage sur Synology DS209 = OK (après installation coreutils) # NiKo88 @ ubuntu.fr : témoignage sur Synology DS211+ # Respawner @ ubuntu.fr : aide pour le format des .exe # Hizoka @ ubuntu.fr : signalement de bug pour noms de fichiers bizarres ! # Josh63 @ ubuntu.fr : témoignage sur Synology DS211 # McMyst @ ubuntu.fr : témoignage sur Dlink DNS 325 # # History : # --------- # 1.7.1 # - Correction du calcul du nom de la destination lorsque le nom du fichier source # contient des caractères tels que {} # 1.7.0 # - Prise en compte des fichiers au format .exe # Le différence réside seulement dans le premier fichier dont il faut retirer # 305664 octets au début et 24 octets à la fin, pour avoir l'équivalent du xtm. # 1.6.2 # - Retrait du "process substitution" a profit d'une fonction avec un simple "pipe" # davantage compatible avec d'autres Linux (comme busybox) # 1.6.1 # - Simplification de la substitution du md5sum pour busybox (nécessite 1.0.1 busyXtremMerge) # 1.6.0 # - Modification de l'algorithme pour le premier fichier afin de le rendre compatible Busybox # (pas de ibs/obs sur le dd). Ainsi modifié, c'est également plus rapide sur Linux standard ! # - Simplification algo sur le dernier fichier (idem ci-dessus). # - Rajout de l'option pipefail pour ne pas "masquer" les éventuelles erreurs lors des "pipes" # du processus de recollage. # 1.5.6 # - Remplacement de od par dd lorsqu'on doit lire des chaines. # - Remplacement de basemane par interne ${var##*/} # 1.5.5 # - Retour au code 1.5.3. (plus amélioration: suppression de 'du + cut' remplacé par 'stat -c%s') # et séparation du code non fini pour Synology 1010+ # 1.5.4 # - Tentative du mise au point pour Synology 1010+ qui utilise une Busybox 1.16.1 # 1.5.2 # - Suppression useless cat ! (Totor @ ubuntu.fr) # - Remplacement du fichier temporaire par un process substitution (Totor @ ubuntu.fr) # - Isolation des libellés et codes erreur. # - Nouveaux paramètre : V (Version), t (temps) [et donc désormais n'affiche plus par défaut les temps] # 1.5.1 # - Sécurité : création du fichier temporaire avec mktemp # - Amélioration de libellés # - Ordonnancement plus logique des tests de cohérence initiaux. # - Optimisations mineures. # 1.5.0 # - Suppression et fusion des options -o et -a !.. # Le script utilise désormais toujours l'option -o (ce qui est sa plus-value) # Si un traitement partiel est détecté, il reprend là où ça en était (deuxième plus-value) # Ces deux options sont donc désormais inutiles car implicites... et cela simplifie le code ! # - Le script fonctionne désormais "à la mode Firefox", c'est à dire que le fichier résultat # est d'abord écrit dans resultat.part, et celui-ci est renommé à la fin du traitement. # La fonction de reprise tient compte de ce nouveau fonctionnement, de même que l'option -f # - Le premier fichier bénéficie aussi de l'optimisation (parallelisme copie/md5) # 1.2.0 # - Suppression de l'option -s devenue inutile. Le fichier est maintenant mis par défaut (suggestion Hypnose @ ubuntu-fr) # dans le même répertoire que les fichiers source (xtm). Le mode par défaut précédent s'obtient en spécifiant "." # (c'est à dire le répertoire courant) comme destination. # - Rajout des options d'optimisation -a et -o # -a permet de lancer le script en cours de téléchargement et d'obtenir un résultat partiel # -o optimise les MD5/concaténation via un tee (tout est fait en une seule commande, une seule lecture). # - Diverses corrections de bugs # 1.1.0 # - Rajout de l'exploration des arguments # - Rajout des options : voir description dans la fonction usage # - Un peu de couleurs pour égayer l'affichage ! # 1.0.1 # - Correction du chemin si le script est lancé depuis via PATH (exemple script placé dans /usr/local/bin) # - Affichage des fichiers tels que passés en paramètres avec leurs éventuels chemins relatifs/absolus # 1.0.0 # - Supporte d'être lancé avec des noms de fichiers contenant des chemins # - Deuxième paramètre optionnel pour spécifier le fichier destination # - Support des noms de fichiers contenant des espaces (source et destination) # - Vérification au préalable de l'existence du fichier destination pour éviter les écrasement intempestifs # - Vérification avant de calculer les MD5 qu'on va bien pouvoir écrire sur le fichier destination #========================================================== # Messages and Error codes. #+ This way it is easyier, if somebody would like to localise readonly MSG_ERROR="\E[1;31mErreur\E[0m\n" readonly MSG_OK="\E[1;32mOK\E[0m" readonly MSG_ATTENTION="\E[1;33mAttention !\E[0m" readonly MSG_BAD_OPTION='Option - incorrecte' readonly MSG_TOO_MANY_PARAMS='Trop de paramètre :' readonly MSG_UNKNOWN_OPTION='Option inconnue :' readonly MSG_UNSPECIFIED_SOURCE_FILE='Fichier source non spécifié' readonly MSG_VERSION='tuXtremMerge (turbo XTM), version 1.7.1' readonly MSG_OPTION_M_AND_N="Vous ne pouvez spécifier les options \E[1;32m-m\E[0m et \E[1;32m-n\E[0m simultanément." readonly MSG_IGNORING_OPTION_F="L'option \E[1;32m-f\E[0m sera ignorée puisque l'option -\E[1;32m-m\E[0m est spécifiée." readonly MSG_IGNORING_DEST="Le nom du fichier résultat sera ignoré puisque l'option \E[1;32m-m\E[0m est spécifiée." readonly MSG_CHECKING='Vérifications ... ' readonly MSG_CHECKING_FIRST_SOURCE_FILE="Vérification d'existence du premier fichier source..." readonly MSG_TIP="\E[1;34mAstuce :\E[0;34m il faut le premier et le dernier fichier, corrects et complets, pour que le script puisse fonctionner.\nVous pouvez optimiser le résultat en récupérant ces deux fichiers en priorité.\E[0m" readonly MSG_SUCCESS="==================================================\n\E[1;32mToutes les opérations sont terminées avec succès !\E[0m" readonly MSG_FIRST_FILE_ERROR=' non trouvé, vide ou erreur' readonly MSG_FIRST_FILE_FOUND='Premier fichier source trouvé :' readonly MSG_OPTION_M_AND_NO_MD5="\E[1;33mRien à faire !\E[0m\nL'option \E[1;32m-m\E[0m est spécifiée, or il n'y a pas MD5 à vérifier dans ces fichiers xtm." readonly MSG_CHECKING_LAST_SOURCE_FILE="Vérification d'existence du dernier fichier source..." readonly MSG_LAST_FILE_ERROR="Fichier %s%3.3u.xtm non trouvé ou vide\n" readonly MSG_LAST_FILE_FOUND="Dernier fichier source trouvé: %s%3.3u.xtm\n" readonly MSG_FILE_SIZES_OK='Tailles premier et dernier fichier cohérentes.' readonly MSG_FILE_SIZES_ERROR='Premier ou dernier fichier de taille incohérente.' readonly MSG_NO_FILE_WRITTEN="Aucun fichier résultat ne sera écrit car l'option \E[1;32m-m\E[0m est spécifiée." readonly MSG_COMPUTING_DEST="Détermination de l'emplacement du résultat..." readonly MSG_DISPLAY_DEST='Emplacement du résultat :' readonly MSG_CHECK_DEST_WRITABLE="Vérification de la possibilité d'écrire le résultat : existence, autorisation d'écriture, espace disponible, etc..." readonly MSG_WARN_FORCED_OVERWRITE="\nEcrasement forcé par l'option \E[1;32m-f\E[0m, le fichier résultat existe déjà." readonly MSG_WARN_OVERWRITE="\nLe fichier : %s existe déjà.\n" readonly MSG_FILE_SIZE_MATCHES='La taille du fichier correspond au résultat prévu dans le xtm.' readonly MSG_FILE_SIZE_DOES_NOT_MATCH='La taille du fichier ne correspond pas au résultat prévu dans le xtm.' readonly MSG_OVERWRITE_HINT="Si vous désirez ré-écrire ce fichier effacez/renommez-le au préalable ou spécifiez l'option \E[1;32m-f\E[0m pour forcer l'écrasement" readonly MSG_WRITE_ERROR="Ecriture de : %s impossible.\nVeuillez vérifier que vous avez l'autorisation d'écrire ce fichier et que son nom est correct.\n" readonly MSG_INSUFFICIENT_SPACE="Espace insuffisant sur %s pour créer le fichier %s.\nEspace nécessaire : %'u\nEspace disponible : %'u\n" readonly MSG_CHUNKS_AVAIL='fichiers déjà traités.' readonly MSG_INFO_DELETED_OLD_FILE="Fichier partiel incohérent supprimé par option \E[1;32m-f\E[0m" readonly MSG_INCOHERENT_PARTIAL_FILE="La taille du fichier partiel n'est pas un multiple de la taille de découpage des fichiers xtm.\nSupprimez ce fichier (%s.part) ou utilisez l'option \E[1;32m-f\E[0m\n" readonly MSG_ALL_CHECKED_OK='Vérifications pour le fichier résultat terminées.' readonly MSG_PROCESSING_START="\E[1mTraitement optimisé des %u fichiers\E[0m\n" readonly MSG_PROCESSING_RESTART="\E[1mReprise du traitement à partir du fichier %u\E[0m\nReste à traiter %u fichier(s)\n" readonly MSG_SEPARATOR='==================================' readonly MSG_PROCESSING_FILE='Traitement de %s%3.3u.%s ... ' readonly MSG_FILE_MISSING='*** Le fichier est manquant ou de taille incorrecte.' readonly MSG_FILE_MISSING_TIP='*** Relancez le programme lorsque le fichier sera complet.' readonly E_BAD_OPTION=65 readonly E_UNKNOWN_OPTION=66 readonly E_TOO_MANY_PARAMS=67 readonly E_UNSPECIFIED_SOURCE_FILE=68 readonly E_MSG_OPTION_M_AND_N=69 readonly E_FIRST_FILE_ERROR=80 readonly E_LAST_FILE_ERROR=81 readonly E_FILE_SIZES_ERROR=82 readonly E_WRITE_ERROR=83 readonly E_INSUFFICIENT_SPACE=83 readonly E_INCOHERENT_PARTIAL_FILE=84 readonly E_WARN_OVERWRITE=96 readonly E_CRITICAL_ERROR=127 #========================================================== # Utility functions # usage : affiche l'usage/aide du script # v_echo : affichage pour l'option verbeux usage() { if [ -n "$1" ]; then echo "$1"; fi cat </dev/null } md5check() { md5sum | cut -b 1-32 | grep -iq "$( echo ${1} )" } timing() { if [ $OPTION_t -ge $1 ]; then date +%T.%N; fi } #========================================================== # START OF THE MAIN PROGRAM HERE ! # /TODO # -> Traiter les interruptions (Ctrl-C) et erreurs par un truncate (au lieu de supprimer) #_________________________________________ # Variables declaration and initialisation declare -i i size SOURCE_FILE_NB DEST_FILE_NAME_LENGTH SPACE_AVAIL DISK_SPACE_NEEDED DEST_FILE_SIZE CHUNK_SIZE CHUNKS_AVAIL fMD5 LAST_SOURCE_FILE_SIZE OPTION_m='' OPTION_n='' OPTION_f='' OPTION_v='' declare -i OPTION_t=0 DISPLAY_SOURCE_FILE_NAME='' DISPLAY_DEST_FILE_NAME='' declare -i CHUNKS_AVAIL=0 set -o pipefail scan_parameters "$@" timing 1 if [ ! $OPTION_v ]; then echo -n "$MSG_CHECKING" fi check_parameters #========================================================== # Checking file existence # - On commence par vérifier le 001 qui contient le header v_echo "$MSG_CHECKING_FIRST_SOURCE_FILE" declare -i fEXE EXE_OFFSET if echo "$SOURCE_FILE_NAME" | grep -q '.exe$'; then fEXE=1 EXE_OFFSET=305664 FIRST_SUFFIX='exe' else fEXE=0 EXE_OFFSET=0 FIRST_SUFFIX='xtm' fi RADIX=$( echo "$SOURCE_FILE_NAME" | sed "s/...\.${FIRST_SUFFIX}\$//" ) DISPLAY_RADIX=$( echo "$DISPLAY_SOURCE_FILE_NAME" | sed "s/...\.${FIRST_SUFFIX}\$//" ) FIRST_SOURCE_FILE_NAME="${RADIX}001.${FIRST_SUFFIX}" if [ ! -f "$FIRST_SOURCE_FILE_NAME" ] || [ ! -s "$FIRST_SOURCE_FILE_NAME" ]; then echo -e "${MSG_ERROR}${DISPLAY_RADIX}001.${FIRST_SUFFIX}${MSG_FIRST_FILE_ERROR}" tip $E_FIRST_FILE_ERROR else v_echo "$MSG_FIRST_FILE_FOUND ${DISPLAY_RADIX}001.${FIRST_SUFFIX}" fi fMD5=$( od -An -vtu1 -j$(( ${EXE_OFFSET} + 91 )) -N1 "$FIRST_SOURCE_FILE_NAME" ) #========================================================== # Checking if MD5 is included in that xtm # If not and only a MD5 check is asked (option -m) exit with message if [ $fMD5 -eq 0 ] && [ $OPTION_m ]; then echo -e "$MSG_OPTION_M_AND_NO_MD5" exit 0 fi CHUNK_SIZE=$(( $( stat -c%s "$FIRST_SOURCE_FILE_NAME" ) - 104 - ${fEXE} * 305688 )) SOURCE_FILE_NB=$( od -An -vtu4 -j$(( ${EXE_OFFSET} + 92 )) -N4 "$FIRST_SOURCE_FILE_NAME" ) DEST_FILE_SIZE=$( od -An -vtu8 -j$(( ${EXE_OFFSET} + 96 )) -N8 "$FIRST_SOURCE_FILE_NAME" ) #--------------------------------- # - On vérifie le dernier fichier # => Dans le cas de MD5, celui-ci contient les MD5 # => Dans le cas non-MD5, cela sert à vérifier au moins la cohérence de taille des fichiers v_echo "$MSG_CHECKING_LAST_SOURCE_FILE" LAST_SOURCE_FILE_NAME=$( printf "${RADIX}%3.3u.xtm" $SOURCE_FILE_NB ) if [ ! -f "$LAST_SOURCE_FILE_NAME" ] || [ ! -s "$LAST_SOURCE_FILE_NAME" ]; then printf "$MSG_ERROR$MSG_LAST_FILE_ERROR" "$DISPLAY_RADIX" $SOURCE_FILE_NB tip $E_LAST_FILE_ERROR else v_echo "$( printf "$MSG_LAST_FILE_FOUND" "$DISPLAY_RADIX" $SOURCE_FILE_NB )" fi LAST_SOURCE_FILE_SIZE=$(( $( stat -c%s "$LAST_SOURCE_FILE_NAME" ) - $fMD5 * $SOURCE_FILE_NB * 32 )) if [ $(( $CHUNK_SIZE * ($SOURCE_FILE_NB - 1) + $LAST_SOURCE_FILE_SIZE )) -eq $DEST_FILE_SIZE ]; then v_echo "$MSG_FILE_SIZES_OK" else echo -e "$MSG_ERROR$MSG_FILE_SIZES_ERROR" tip $E_FILE_SIZES_ERROR fi #========================================================== # From here we have first and last file, we can do something! # Checking output file # - Vérifie qu'on ne va pas écraser un fichier (si option -f, juste message verbeux) # - Vérifie qu'on peut bien écrire le fichier (autorisation, nom/chemin correct) # - Vérifie qu'on a la place d'écrire le fichier # # Mais on commence par déterminer le nom du fichier résultat # Ca dépend de plusieurs choses : # - si un fichier destination a été spécifié en paramètre # + et si c'est le cas, si c'est un répertoire existant ou pas if [ $OPTION_m ]; then v_echo "$MSG_NO_FILE_WRITTEN" else v_echo "$MSG_COMPUTING_DEST" if [ -z "$DISPLAY_DEST_FILE_NAME" ] || [ -d "$DISPLAY_DEST_FILE_NAME" ]; then DEST_FILE_NAME_LENGTH=$( od -An -vtu1 -j$(( ${EXE_OFFSET} + 40 )) -N1 "$FIRST_SOURCE_FILE_NAME" ) DEFAULT_DEST_FILE_NAME=$( dd if="$FIRST_SOURCE_FILE_NAME" bs=1 skip=$(( ${EXE_OFFSET} + 41 )) count=$DEST_FILE_NAME_LENGTH 2>/dev/null) if [ -z "$DISPLAY_DEST_FILE_NAME" ]; then this_file_radix=${DISPLAY_SOURCE_FILE_NAME%"${DISPLAY_SOURCE_FILE_NAME##*/}"} else # Ici DISPLAY_DEST_FILE_NAME est forcément un répertoire # on rajoute le / final si nécessaire if [ ${DISPLAY_DEST_FILE_NAME:(-1)} == '/' ]; then this_file_radix="$DISPLAY_DEST_FILE_NAME" else this_file_radix="$DISPLAY_DEST_FILE_NAME/" fi fi DISPLAY_DEST_FILE_NAME="$this_file_radix$DEFAULT_DEST_FILE_NAME" fi v_echo "$MSG_DISPLAY_DEST $DISPLAY_DEST_FILE_NAME" DEST_FILE_NAME=$( readlink -fn "$DISPLAY_DEST_FILE_NAME" ) DEST_FILE_NAME_PART="${DEST_FILE_NAME}.part" #--------------------------------- # Vérifications de la possibilité d'écrire le fichier résultat v_echo "$MSG_CHECK_DEST_WRITABLE" if [ -f "$DEST_FILE_NAME" ] && [ -s "$DEST_FILE_NAME" ]; then if [ $OPTION_f ]; then v_echo "$MSG_ATTENTION$MSG_WARN_FORCED_OVERWRITE" else size=$( stat -c%s "$DEST_FILE_NAME" ) if [ $size -eq $DEST_FILE_SIZE ]; then printf "$MSG_ATTENTION$MSG_WARN_OVERWRITE" $DISPLAY_DEST_FILE_NAME echo "$MSG_FILE_SIZE_MATCHES" else printf "$MSG_ERROR$MSG_WARN_OVERWRITE" $DISPLAY_DEST_FILE_NAME echo "$MSG_FILE_SIZE_DOES_NOT_MATCH" fi echo -e "$MSG_OVERWRITE_HINT" exit $E_WARN_OVERWRITE fi fi touch "$DEST_FILE_NAME" "$DEST_FILE_NAME_PART" 2>/dev/null if [ $? -ne 0 ]; then printf "$MSG_ERROR$MSG_WRITE_ERROR" $DISPLAY_DEST_FILE_NAME exit $E_WRITE_ERROR fi TMP=$( df "$DEST_FILE_NAME" | sed -n '2p' ) SPACE_AVAIL=$(( $( echo $TMP | cut -d' ' -f 4 ) * 1024 - 1024)) size=$( stat -c%s "$DEST_FILE_NAME_PART" ) DISK_SPACE_NEEDED=$(( $DEST_FILE_SIZE - $size )) if [ $DISK_SPACE_NEEDED == 0 ]; then success fi if [ $SPACE_AVAIL -lt $DISK_SPACE_NEEDED ]; then printf "$MSG_ERROR$MSG_INSUFFICIENT_SPACE" $( echo $TMP | cut -d' ' -f 6 ) "$DISPLAY_DEST_FILE_NAME" $DISK_SPACE_NEEDED $SPACE_AVAIL exit $E_INSUFFICIENT_SPACE fi CHUNKS_AVAIL=$(( $size / $CHUNK_SIZE )) if [ $(( $size % $CHUNK_SIZE )) -eq 0 ]; then v_echo "$CHUNKS_AVAIL $MSG_CHUNKS_AVAIL" else if [ $OPTION_f ]; then v_echo "$MSG_INFO_DELETED_OLD_FILE" rm "$DEST_FILE_NAME_PART" 2>/dev/null CHUNKS_AVAIL=0 else printf "$MSG_ERROR$MSG_INCOHERENT_PARTIAL_FILE" $DISPLAY_DEST_FILE_NAME exit $E_INCOHERENT_PARTIAL_FILE fi fi v_echo "$MSG_ALL_CHECKED_OK" fi #========================================================== # The Real work starts here! # - Toutes les vérifications et préparations étant finies on commence # la concaténation/checksum à proprement parler if [ $OPTION_m ]; then DEST_FILE_NAME_PART="/dev/null" fi if [ $fMD5 -eq 0 ] || [ $OPTION_n ]; then MD5_PROG="nop" else MD5_PROG="md5check" fi if [ ! $OPTION_v ]; then echo -e "$MSG_OK" fi timing 1 if [ $CHUNKS_AVAIL -eq 0 ]; then printf "$MSG_PROCESSING_START" $SOURCE_FILE_NB else printf "$MSG_PROCESSING_RESTART" $(( $CHUNKS_AVAIL + 1 )) $(( $SOURCE_FILE_NB - $CHUNKS_AVAIL )) fi echo "$MSG_SEPARATOR" i=$CHUNKS_AVAIL while (( $i < $SOURCE_FILE_NB )) ; do if [ $fMD5 -eq 1 ]; then this_file_MD5=$( dd if="$LAST_SOURCE_FILE_NAME" bs=1 skip=$(( $LAST_SOURCE_FILE_SIZE + $i * 32)) count=32 2>/dev/null ) fi i=$(( $i + 1 )) this_file_radix=$( printf "%s%3.3u" "$RADIX" $i ) if [ ${i} -eq 1 ]; then printf "$MSG_PROCESSING_FILE" "$DISPLAY_RADIX" $i ${FIRST_SUFFIX} else printf "$MSG_PROCESSING_FILE" "$DISPLAY_RADIX" $i 'xtm' fi case $i in 1 ) if [ ${fEXE} -eq 0 ]; then # Pour le premier fichier on ne 'tee' pas les 104 premiers octets, mais ils rentrent dans le md5 { dd if="$this_file_radix.xtm" bs=104 count=1 2>/dev/null && # On fait un deuxième dd pour "aligner" à un bloc de 4096, et ensuite par 4096 # C'est 7% plus rapide que de ne faire que des dd par 104. # Si le fichier est plus petit que 4096 (peu probable) ça fonctionne tout de même # car dans ce cas le deuxième s'arrête à la fin du fichier et le dernier dd # ne retourne rien. { dd if="$this_file_radix.xtm" bs=8 skip=13 count=499 2>/dev/null dd if="$this_file_radix.xtm" bs=4096 skip=1 2>/dev/null } | tee "$DEST_FILE_NAME_PART"; } | $MD5_PROG "${this_file_MD5}" else FIRST_FILE_BLOCKS=$(( ( ${CHUNK_SIZE} - 1432 ) / 4096 )) FIRST_FILE_PAD=$(( ${CHUNK_SIZE} - 1432 - ${FIRST_FILE_BLOCKS} * 4096 )) { dd if="$this_file_radix.exe" bs=8 skip=38208 count=13 2>/dev/null && # Idem que ci-dessus pour les optimisations avec des PGCD recalculés # C'est juste un peu plus compliqué car on doit aussi sauter 24 # octets à la fin, donc il faut savoir combien de blocs de 4096 on a. { dd if="$this_file_radix.exe" bs=8 skip=38221 count=179 2>/dev/null dd if="$this_file_radix.exe" bs=4096 skip=75 count=${FIRST_FILE_BLOCKS} 2>/dev/null dd if="$this_file_radix.exe" bs=1 skip=$(( (${FIRST_FILE_BLOCKS} + 75) * 4096 )) count=${FIRST_FILE_PAD} 2>/dev/null } | tee "$DEST_FILE_NAME_PART"; } | $MD5_PROG "${this_file_MD5}" fi ;; $SOURCE_FILE_NB ) # Pour le dernier fichier on enlève les octets de MD5 (ou pas... s'il n'y en a pas !) dd if="$this_file_radix.xtm" bs=$LAST_SOURCE_FILE_SIZE count=1 2>/dev/null | \ tee -a "$DEST_FILE_NAME_PART" | \ $MD5_PROG "${this_file_MD5}" ;; *) # On vérifie la cohérence de taille du fichier seulement si ce n'est ni le premier # ni le dernier, car pour c'est deux fichiers, c'est déjà vérifié avant ! size=$( stat -c%s "$this_file_radix.xtm" 2>/dev/null ) if [ $? -ne 0 ] || [ $size -ne $CHUNK_SIZE ]; then echo -e "$MSG_ATTENTION" echo "$MSG_FILE_MISSING" if [ $OPTION_m ]; then continue else echo "$MSG_FILE_MISSING_TIP" exit $E_WARN_FILE_MISSING fi fi tee -a "$DEST_FILE_NAME_PART" <"$this_file_radix.xtm" | $MD5_PROG "${this_file_MD5}" esac if [ $? -eq 0 ]; then # No error: all is OK echo -e "$MSG_OK" else # Error: it means we get an incorrect MD5, a write error, or other bad things, so here we cancel and abort echo -e "$MSG_ERROR" if [ ! $OPTION_m ]; then # But we stop only if no option -m. # If option -m we continue so that we check ALL the files and not stop on the first error rm "$DEST_FILE_NAME_PART" "$DEST_FILE_NAME" 2>/dev/null exit $E_CRITICAL_ERROR fi fi timing 2 done #========================================================== # Success! success