Skip to content

Instantly share code, notes, and snippets.

@SuzanneSoy
Created August 19, 2010 22:14
Show Gist options
  • Save SuzanneSoy/539080 to your computer and use it in GitHub Desktop.
Save SuzanneSoy/539080 to your computer and use it in GitHub Desktop.
#!/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=""
COULEUR_OPTION=""
COULEUR_NORMAL=""
# 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