Created
August 19, 2010 22:14
-
-
Save SuzanneSoy/539080 to your computer and use it in GitHub Desktop.
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/sh | |
# Implémentation d'un générateur d'images de disquette au format SFS | |
# (Système de Fichiers Simple) | |
# | |
# Spécification : http://dimensionalrift.homelinux.net/combuster/vdisk/sfs.html | |
. `dirname $0`/../inclusions/math.sh | |
. `dirname $0`/../inclusions/date.sh | |
. `dirname $0`/../inclusions/dev.sh | |
COULEUR_VARIABLE="[1m" | |
COULEUR_OPTION="[0;36m" | |
COULEUR_NORMAL="[m" | |
# Variables | |
secteurBoot=/dev/zero | |
nbBlocsReserves=0 | |
nbBlocs=-1 | |
nbBlocsLibres=-1 | |
nbOctetsParBloc=512 | |
repertoireRacine= | |
nomVolume="`date +%s`" | |
destination= | |
force="" | |
# Affiche l'aide | |
usage() { | |
cat <<EOF | |
Utilisation: $0 [options] image | |
Créer un système de fichiers simple (sfs). | |
$COULEUR_OPTION-B$COULEUR_NORMAL <secteur de boot + blocs réservés> | |
$COULEUR_OPTION-r$COULEUR_NORMAL <nb blocs réservés> (avec le super-bloc) | |
$COULEUR_OPTION-b$COULEUR_NORMAL <nb blocs> (taille de l'image en blocs) | |
$COULEUR_OPTION-l$COULEUR_NORMAL <nb blocs> (nombre de blocs à laisser libres) | |
$COULEUR_OPTION-n$COULEUR_NORMAL <nb octets par bloc> (128..2^15) | |
$COULEUR_OPTION-L$COULEUR_NORMAL <étiquette du volume> | |
$COULEUR_OPTION-d$COULEUR_NORMAL <repertoire racine> | |
$COULEUR_OPTION-f$COULEUR_NORMAL forcer l'écrasement de fichier | |
$COULEUR_OPTION-h$COULEUR_NORMAL afficher ce message | |
Les options $COULEUR_OPTION-b$COULEUR_NORMAL et $COULEUR_OPTION-l$COULEUR_NORMAL sont mutuellement exclusives. | |
EOF | |
exit 1 | |
} | |
# Affiche un message d'erreur et quite. | |
# $1 = message | |
die() { | |
echo "$1" | |
exit 2 | |
} | |
# Récupération des options. Les lettres correspondent à une option | |
# précédée d'un tirer (-B, -r, ...), les : à un argument pour l'option | |
# précédente. | |
while getopts ":B:r:b:l:n:L:d:fh" option; do | |
case "$option" in | |
B) secteurBoot="$OPTARG" ;; | |
r) nbBlocsReserves="$OPTARG" ;; | |
b) nbBlocs="$OPTARG" ;; | |
l) nbBlocsLibres="$OPTARG" ;; | |
n) nbOctetsParBloc="$OPTARG" ;; | |
L) nomVolume="$OPTARG" ;; | |
d) repertoireRacine="$OPTARG" ;; | |
f) force="f" ;; | |
h) usage ;; | |
*) usage ;; | |
esac | |
done | |
# On récupère le nom de l'image à générer | |
shift "$(($OPTIND-1))" | |
destination="$1" | |
# Messages d'erreur | |
[ -z "$destination" ] && \ | |
echo "Nom de l'image de disquette non spécifié ou vide." && usage | |
[ "$nbOctetsParBloc" -lt 128 ] && \ | |
die "${COULEUR_VARIABLE}nbOctetsParBloc${COULEUR_NORMAL} doit être >= 128" | |
[ "$nbBlocs" != -1 -a "$nbBlocsLibres" != -1 ] && \ | |
die "Les options ${COULEUR_OPTION}-b${COULEUR_NORMAL} et ${COULEUR_OPTION}-l${COULEUR_NORMAL} sont mutuellement exclusives." | |
[ -e "$destination" -a "$force" != "f" ] && \ | |
die "${COULEUR_VARIABLE}$destination${COULEUR_NORMAL} existe, utiliser ${COULEUR_OPTION}-f${COULEUR_NORMAL} pour l'écraser" | |
# Exécute dd $@ et renvoie le nombre de blocs copiés. | |
# $@ = options pour dd. | |
countdd() { | |
local count IFS | |
IFS="" | |
count="$(dd "$@" 2>&1 | tail -n 1)" | |
echo -n "${count%%+*}" | |
} | |
# Renvoie la taille en octets de $1 | |
# $1 = fichier. | |
tailleFichier() { | |
local l s IFS | |
IFS="" | |
l="$(wc -c "$1")" | |
l="${l#${l%%[0-9]*}}" | |
l="${l%% *}" | |
echo -n "$l" | |
} | |
# Renvoie une entrée de l'index de SFS de type Nom de Volume | |
# $1 = nom. | |
entreeVolume() { | |
echo -ne "\\001" | |
echo -ne "\\000\\000\\000" | |
date64 | |
echo -ne "$1\\000" | |
} | |
# Renvoie une entrée de l'index de SFS de type Marqueur de Début | |
entreeMarqueurDebut() { | |
echo -ne "\\002" | |
} | |
# Renvoie une entrée de l'index de SFS de type Répertoire | |
# $1 = nom, $2 = nb d'entrées (continuations + 1). | |
entreeRepertoire() { | |
echo -ne "\\021" | |
intToByte "$(($2 - 1))" 8 | |
date64 "$1" | |
echo -ne "$1\\000" | |
} | |
# Renvoie une entrée de l'index de SFS de type Fichier | |
# $1 = nom, $2 = bloc début, $3 = bloc fin, $4 = longueur en octets, | |
# $5 = nb d'entrées (continuations + 1). | |
entreeFichier() { | |
echo -ne "\\022" | |
intToByte "$(($5 - 1))" 8 | |
date64 "$1" | |
intToByte "$2" 64 | |
intToByte "$3" 64 | |
intToByte "$4" 64 | |
echo -ne "$1\\000" | |
} | |
# On efface la destination | |
:>"$destination" | |
# On crée le super-bloc (partie 1) et les blocs réservés | |
# La partie 2 du super-bloc est à la fin. | |
# Le nombre de blocs réservés contient le super-bloc, qui fait 512 | |
# octets. Il y a donc au moins 512/nbOctetsParBloc (si | |
# nbOctetsParBloc <= 512) blocs réservés, et au moins 1 bloc (si | |
# nbOctetsParBloc >= 512) | |
nbBlocsReserves="$(max $((512/nbOctetsParBloc)) 1 "$nbBlocsReserves")" | |
# On copie le fichier donné en tant que $secteurBoot | |
if [ -n "$secteurBoot" ]; then | |
n="$(countdd if="$secteurBoot" \ | |
of="$destination" \ | |
bs="$((nbBlocsReserves*nbOctetsParBloc))" \ | |
seek=0 \ | |
count=1 \ | |
conv=notrunc \ | |
conv=sync)" | |
else | |
n=0 | |
fi | |
# Si le fichier $secteurBoot n'est pas assez grand, on complète | |
# avec des zéros. | |
devZero | dd of="$destination" \ | |
bs="$nbOctetsParBloc" \ | |
seek=0 \ | |
count="$((nbBlocsReserves - n))" \ | |
conv=notrunc \ | |
conv=sync 2>&1 | devNull | |
# On va copier les données, l'espace libre et l'index | |
# Pré-calcule le nombre de blocs. Permet de savoir où sera l'espace | |
# libre et où sera l'index, quand c'est l'espace libre qui détermine | |
# la taille de l'image et non nbBlocs. | |
# $1 = nbBlocsReserves, $2 = nbBlocsLibres | |
precalcNbBlocs() { | |
local nbEntrees fichier longueurFichier nbFichiers nbEntreesTotal | |
local nbBlocsEntrees tailleZoneIndex tailleZoneDonnees | |
# On aditionne les tailles de tous les fichiers en arrondissant au | |
# bloc supérieur, et on calcule le nombre d'entrées à insérer dans | |
# l'Index. | |
# C'est équivalent au code de ajouterDonnees(), sans les | |
# modifications. | |
find "$repertoireRacine" | (while read fichier; do | |
if [ -d "$fichier" ]; then | |
# Zone Index | |
nbEntrees="$((((${#fichier} + 9 + 1) / 64) + 1))" | |
else | |
# Zone Donnees | |
longueurFichier="$(tailleFichier "$fichier")" | |
tailleZoneDonnees="$((tailleZoneDonnees + ((longueurFichier - 1 + nbOctetsParBloc) / nbOctetsParBloc)))" | |
# Zone Index | |
nbEntrees="$((((${#fichier} + 33 + 1) / 64) + 1))" | |
fi | |
# Taille de l'index | |
nbEntreesTotal="$((nbEntreesTotal + nbEntrees))" | |
tailleZoneIndex="$((nbEntreesTotal * 64))" | |
nbBlocsEntrees="$(((tailleZoneIndex - 1 + nbOctetsParBloc) / nbOctetsParBloc))" | |
done | |
# Calcul du nombre de blocs | |
nbBlocs="$(($1 + tailleZoneDonnees + $2 + nbBlocsEntrees))" | |
echo "$nbBlocs") | |
} | |
# Ajout des données, de l'espace libre et de l'index à l'image | |
# $1 = Premier bloc de données | |
ajouterDonnees() { | |
local fichier pointeurZoneDonnees blocDebut blocFin nbEntrees | |
local longueurFichier nbFichiers nbEntreesTotal nbBlocsEntrees | |
local tailleZoneIndex tailleZoneDonnees nbBlocsLibres nbBlocs i n | |
local debutZoneDonnees | |
debutZoneDonnees="$1" | |
nbFichiers=0 | |
nbEntreesTotal=2 # marqueur début et identifiant volume | |
nbBlocsEntrees=1 | |
i=0 | |
n=0 | |
# Calcul du nombre de blocs | |
if [ "$nbBlocs" == -1 ]; then | |
if [ "$nbBlocsLibres" == -1 ]; then | |
nbBlocsLibres=0 | |
fi | |
if [ -n "$repertoireRacine" ]; then | |
# BUG : il faudrait utiliser -print0 dans find, mais c'est | |
# incompatible avec read. | |
# | |
# BUG : vu que sh ne supporte pas les tableaux, on ne peut | |
# pas s'occuper de la zone de données en stockant les noms | |
# de fichier et numéros de bloc, puis faire la zone Index | |
# une fois qu'on connaît la taille de la zone de données. | |
# | |
# Vu qu'on veut éviter autant que possible d'utiliser des | |
# fichiers temporaires, on peut pas non plus écrire | |
# séparément les données et l'index | |
# | |
# Donc on calcule ici la taille de la zone de données et | |
# on espère qu'aucun fichier ne sera modifié durant la | |
# copie. | |
nbBlocs="$(precalcNbBlocs "$debutZoneDonnees" "$nbBlocsLibres")" | |
else | |
nbBlocs="$((debutZoneDonnees + 0 + nbBlocsLibres + nbBlocsEntrees))" | |
fi | |
fi | |
# On positionne le pointeurZoneDonnees sur le début de la zone de | |
# données, de taille initiale 0. | |
pointeurZoneDonnees="$debutZoneDonnees" | |
tailleZoneDonnees=0 | |
# On commence à remplir la zine d'index par le bout, en sautant | |
# une entrée pour le nom de volume. | |
pointeurZoneIndex="$(((nbBlocs * nbOctetsParBloc / 64) - 1))" | |
# Nom de volume (dans l'index) | |
entreeVolume "$nomVolume" | \ | |
dd of="$destination" bs=64 count=1 seek="$pointeurZoneIndex" conv=notrunc conv=sync 2>&1 | devNull | |
# Si on a un répertoire racine, on ajoute les fichiers qu'il | |
# contient, sinon on crée une image vide. | |
if [ -n "$repertoireRacine" ]; then | |
# BUG : il faudrait utiliser -print0 dans find, mais c'est | |
# incompatible avec read. | |
find "$repertoireRacine" | |
fi | (while read fichier; do | |
if [ -d "$fichier" ]; then | |
# Zone Index | |
# Nombre d'entrées nécessaires au stockage du nom de dossier | |
nbEntrees="$((((${#fichier} + 9 + 1) / 64) + 1))" | |
pointeurZoneIndex="$((pointeurZoneIndex - nbEntrees))" | |
# BUG : Si on utilise un pipe comme ça : | |
# entreeRepertoire ... | dd of=... ... | |
# entreeFichier ... | dd of=... ... | |
# ça ne marche pas, dd met des morceaux de la sortie | |
# n'importe où dans le fichier... Avec un fichier | |
# intermédiaire, ça marche | |
# On crée une entrée de répertoire qu'on insère à | |
# l'endroit voulu dans le fichier. | |
entreeRepertoire "$fichier" "$nbEntrees" > .temp-$$ | |
dd if=.temp-$$ of="$destination" bs=64 seek="$pointeurZoneIndex" conv=notrunc conv=sync 2>&1 | devNull | |
else | |
# Zone Donnees | |
longueurFichier="$(tailleFichier "$fichier")" | |
# On compte le nombre de blocs utilisés par le stockage du | |
# fichier | |
# n = (longueurFichier - 1 + nbOctetsParBloc) / nbOctetsParBloc | |
n="$(countdd if="$fichier" \ | |
of="$destination" \ | |
bs="$nbOctetsParBloc" \ | |
seek="$pointeurZoneDonnees" \ | |
conv=notrunc \ | |
conv=sync)" | |
# Si le fichier est vide, la spécification nous indique de | |
# positionner blocDebut et blocFin à zéro. | |
if [ "$n" == 0 ]; then | |
blocDebut=0 | |
blocFin=0 | |
else | |
blocDebut="$((pointeurZoneDonnees-debutZoneDonnees))" | |
pointeurZoneDonnees="$((pointeurZoneDonnees+n))" | |
tailleZoneDonnees="$((tailleZoneDonnees+n))" | |
blocFin="$((pointeurZoneDonnees-debutZoneDonnees))" | |
fi | |
# Zone Index | |
# Nombre d'entrées nécessaires au stockage du nom de fichier | |
nbEntrees="$((((${#fichier} + 33 + 1) / 64) + 1))" | |
pointeurZoneIndex="$((pointeurZoneIndex - nbEntrees))" | |
# Création de l'entrée de fichier et insertion, comme pour | |
# les dossiers, même bug aussi d'ailleurs... | |
entreeFichier "$fichier" "$blocDebut" "$blocFin" \ | |
"$longueurFichier" "$nbEntrees" > .temp-$$ | |
dd if=.temp-$$ of="$destination" bs=64 seek="$pointeurZoneIndex" conv=notrunc conv=sync 2>&1 | devNull | |
fi | |
# On incrémente le nombre total d'entrées dans l'index. | |
nbEntreesTotal="$((nbEntreesTotal + nbEntrees))" | |
# On incrémente le nombre de fichiers. | |
i=$((i+1)) | |
done | |
nbFichiers="$i" | |
# Calcul de la taille de la zone d'index. | |
tailleZoneIndex="$((nbEntreesTotal * 64))" | |
nbBlocsEntrees="$(((tailleZoneIndex - 1 + nbOctetsParBloc) / nbOctetsParBloc))" | |
# Il faudra remplir le début du premier bloc de la zone d'index | |
# avec des zéros, on calcule combien il en faudra. | |
nbOctetsAvantMarqueurDebut="$(((nbBlocsEntrees * nbOctetsParBloc) - tailleZoneIndex))" | |
# Écriture de la zone libre; | |
# Zone libre = nbBlocs - (tout le reste) | |
nbBlocsLibres=$((nbBlocs - debutZoneDonnees - tailleZoneDonnees - nbBlocsEntrees)) | |
devZero | dd of="$destination" \ | |
bs="$nbOctetsParBloc" \ | |
seek="$pointeurZoneDonnees" \ | |
count="$nbBlocsLibres" \ | |
conv=notrunc \ | |
conv=sync 2>&1 | devNull | |
# Remplissage du début du premier bloc de l'index avec des zéros | |
devZero | dd of="$destination" \ | |
bs=1 \ | |
count="$nbOctetsAvantMarqueurDebut" \ | |
seek="$(((pointeurZoneDonnees + nbBlocsLibres)*nbOctetsParBloc))" \ | |
conv=notrunc \ | |
conv=sync 2>&1 | devNull | |
#seek="$pointeurZoneIndex" | |
# On insère l'entrée "Marqueur de début" de l'index | |
pointeurZoneIndex="$((pointeurZoneIndex - 1))" | |
entreeMarqueurDebut | \ | |
dd of="$destination" bs=64 count=1 seek="$pointeurZoneIndex" conv=notrunc conv=sync 2>&1 | devNull | |
# On renvoie les infos intéressantes. | |
# A faire : renvoyer le nombre de fichers pour afficher des stats | |
# à l'utilisateur | |
echo "tailleZoneDonnees=$tailleZoneDonnees" | |
echo "tailleZoneIndex=$tailleZoneIndex" | |
echo "nbBlocs=$nbBlocs") | |
} | |
# On calcule la tailleBloc de la spécification | |
tailleBloc="$(($(log2 "$nbOctetsParBloc") - 7))" | |
# A faire : calculer la somme de contrôle. | |
sommeControle=0 | |
tailleZoneDonnees=0 | |
tailleZoneIndex=0 | |
# On ajoute les données pour de bon. | |
eval "$(ajouterDonnees "$nbBlocsReserves")" | |
# Super-bloc partie 2 | |
# On remplit les valeurs du super-bloc qu'on connaît à présent | |
date64 | dd of="$destination" bs=1 skip=0 seek=404 count=8 conv=notrunc 2>&1 | devNull | |
intToByte "$tailleZoneDonnees" 64 | dd of="$destination" bs=1 skip=0 seek=412 count=8 conv=notrunc 2>&1 | devNull | |
intToByte "$tailleZoneIndex" 64 | dd of="$destination" bs=1 skip=0 seek=420 count=8 conv=notrunc 2>&1 | devNull | |
echo -n "SFS" | dd of="$destination" bs=1 skip=0 seek=428 count=3 conv=notrunc 2>&1 | devNull | |
# 16 = 0x10 = version 1.0 | |
intToByte 16 8 | dd of="$destination" bs=1 skip=0 seek=431 count=1 conv=notrunc 2>&1 | devNull | |
intToByte "$nbBlocs" 64 | dd of="$destination" bs=1 skip=0 seek=432 count=8 conv=notrunc 2>&1 | devNull | |
intToByte "$nbBlocsReserves" 32 | dd of="$destination" bs=1 skip=0 seek=440 count=4 conv=notrunc 2>&1 | devNull | |
intToByte "$tailleBloc" 8 | dd of="$destination" bs=1 skip=0 seek=444 count=1 conv=notrunc 2>&1 | devNull | |
intToByte "$sommeControle" 8 | dd of="$destination" bs=1 skip=0 seek=445 count=1 conv=notrunc 2>&1 | devNull | |
# Nettoyage : | |
rm .temp-$$ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment