







|
Introduction
Plusieurs utilitaires (notamment l'utilitaire de disque et hdiutil), permettent
sur le Macintosh de produire et de manipuler des images de disques. Ces images
peuvent se présenter dans différents formats, dont certains sont déclarés comme
obsolètes (exemple des images de disquettes).
Les images de disques peuvent être de véritables images d'un disque, donc une copie
secteur après secteur d'un disque réel (matériel). Il peut également s'agir d'un
conteneur jouant le rôle de disque virtuel et pouvant être monté et manipulé
comme un disque, mais qui contient par exemple les fichiers et les dossiers
placés dans un dossier du disque.
En ce sens, les images de disque constituent des solutions fréquemment utilisées
pour la distribution de logiciels.
Formats des images
Le logiciel « Utilitaire de disque » offre plusieurs options dans la création
d'une image de disque vide, qui correspondent essentiellement aux options plus
générales ci-dessous, hormis celle qui propose de créer une table de partition
unique avec MBR, qui s'identifie par la présence du code 0xAA55 à la fin du premier
secteur et le code de partition Macintosh (0xAF) dans la première rubrique de
système. Cette rubrique pointe sur un volume formaté selon le mécanisme dit de la
disquette, donc sans table de partition, avec la tête de volume logique au
secteur 2.
Une autre variante similaire est celle d'une partition de type EFI/GPT, où le
code de système est 0xEE. Le secteur 0 est suivi d'une table de partition EFI
(voir la page Wiki
pour plus de précisions.
Je n'ai jamais rencontré ces images avec compression, mais rien ne s'y oppose
en principe.
Quant à lui, le manuel de l'utilitaire hdiutil donne les différents
formats suivants :
UDRW - UDIF read/write image
UDRO - UDIF read-only image
UDCO - UDIF ADC-compressed image
UDZO - UDIF zlib-compressed image
UDBZ - UDIF bzip2-compressed image (OS X 10.4+ only)
UFBI - UDIF entire image with MD5 checksum
UDRo - UDIF read-only (obsolete format)
UDCo - UDIF compressed (obsolete format)
UDTO - DVD/CD-R master for export
UDxx - UDIF stub image
UDSP - SPARSE (grows with content)
RdWr - NDIF read/write image (deprecated)
Rdxx - NDIF read-only image (Disk Copy 6.3.3 format)
ROCo - NDIF compressed image (deprecated)
Rken - NDIF compressed (obsolete format)
DC42 - Disk Copy 4.2 image
Formats non compressés
Si l'on veut bien ignorer les 5 dernières lignes, qui sont clairement historiques,
les formats UDRW (Image UDIF en lecture/écriture) et UDTO (image-maîtresse de
CD-R/DVD-R) sont des formats non compressés, donc des images simples d'un disque réel
ou virtuel. Elles peuvent être ouvertes avec des utilitaires comme MacImage ou montées comme des disques sous Linux.
Le format UDSP est une image dans laquelle la totalité de l'espace libre du disque
n'est pas attribuée, ce qui permet de conserver une image de petite taille qui
peut grandir en fonction des besoins. Ce format est très spécifique et nous
ne l'aborderons pas ici.
Formats compressés
Les formats compressés peuvent utiliser plusieurs modes de compression, du plus
simple où l'on ne copie pas les secteurs qui ne contiennent que des zéros, au
plus compliqué utilisant des techniques poussées de compression, le problème
étant toujours le même : la bonne compression demande du temps.
Il existe donc un compromis à trouver entre le temps de traitement pour la
compression, le temps de traitement pour la décompression et l'espace occupé sur
le disque par l'image comprimée.
Maquette générale
Une image comprimée comprend essentiellement trois parties, qui se succèdent
dans le fichier :
- Données
- Tableau des blocs
- Bloc binaire 'koly'
Les données sont de longueur variable, de même que le tableau des blocs. Par
contre, le bloc binaire 'koly' est de taille fixe (512 octets) et il se situe à
l'extrémité du fichier.
Bloc binaire 'koly'
Le bloc binaire 'koly' est appelé ainsi parce que le premier entier du bloc
contient la signature 'koly'. Dans ce bloc, qui contient toujours beaucoup de
données parasites parce que le logiciel ne nettoie pas le tampon utilisé avant
de renseigner les variables, on distingue cependant aisément la présence de
deux entiers sur 64 bits qui codent le décalage du tableau des blocs dans le
fichier comprimé ainsi que, après le second, un autre entier sur 64 bits codant
la longueur de ce tableau.
Plus près de la fin, on voit le CRC32 de l'image comprimée ainsi que le nombre des
secteurs composant l'image.
Au tout début du bloc, les quelques premiers entiers sur 32 bits pourraient coder
le nombre des partitions et la taille des secteurs.
Tableau des blocs
Le tableau des blocs se présente sous la forme d'un fichier XML. Il est souvent
appelé fichier de préférences parce qu'il suit le format XML des fichiers de
préférences (plist) sur le Macintosh. On voit aussi à son propos utiliser le terme de
branche de ressources et on observe effectivement la présence de la chaîne
'resource-fork'. D'un autre côté, appeler branche de ressources un segment de
données placé dans un conteneur XML lui même inclus dans un fichier de données
ne me semble pas vraiment justifié, mais puisque c'est ainsi...
Les données utiles constituent plusieurs séquences de données, placées entre des
balises <data> et </data>. Dans le cas d'une image de disque ou de dossier Macintosh,
nous avons généralement quatre séquences de données, également appelées partitions
dans le fichier XML :
- Descripteur des pilotes (Driver Descriptor Map)
- Table de partitions
- Partition Apple_HFS
- Partition Apple_Free
La première de ces « partitions » contient le secteur 0 du disque. La deuxième
contient le secteur 1 et les suivants jusqu'au début du volume logique. La
troisième contient ce volume logique (les données utiles). La quatrième correspond
à la petite partition Apple_Free qui est parfois présente pour regrouper quelques
secteurs libres à la fin d'un disque ou d'une image de disque.
Ces séquences de données sont stockées sous la forme de code base64. Voir par
exemple la page Wikipedia pour
plus de renseignements.
Après décodage, on obtient un bloc d'en-tête commençant par la signature 'mish',
suivi d'une séquence de blocs qui constituent des pointeurs sur les différents
segments de données comprimées se trouvant dans l'image. La structure de ce
bloc est la suivante :
- Type de bloc (32 bits)
- Drapeaux ?? (32 bits)
- Décalage de sortie (position du résultat), sur 64 bits
- Taille de la sortie (taille du résultat), sur 64 bits
- Décalage d'entrée (position des données comprimées), sur 64 bits
- Taille de l'entrée (longueur du segment à décoder), sur 64 bits
Ce bloc permet de traiter les segments de données. Les types de blocs identifiés
sont les suivants :
- 0x00000000 - Bloc de zéros
- 0x00000001 - Bloc à copier purement et simplement
- 0x00000002 - Encore un bloc de zéros
- 0x80000004 - Bloc comprimé en mode ADC
- 0x80000005 - Bloc comprimé en mode Zlib
- 0x80000006 - Bloc comprimé en mode Bzlib2
- 0x7FFFFFFE - Bloc mystérieux que l'on peut ignorer
- 0xFFFFFFFF - Marqueur de fin d'une série de blocs
Sources d'informations
Pour recueillir et mettre en forme les informations ci-dessus, nous nous sommes
avant tout fondés sur un examen d'images créées par l'utilitaire de disque et
l'outil hdiutil. Cela a permis assez rapidement d'identifier les modes de
compressioon Zlib et Bzlib2 et de trouver des bibliothèques qui mettent en œuvre
ces techniques, à savoir Zlib.net, bibliothèque
écrite par Jean-Loup Gailly et Mark Adler, et Bzlib.org,
bibliothèque écrite par Julian Seward. Qu'ils soient tous trois sincèrement
remerciés pour leur travail et pour les explications données dans leur code.
Plusieurs confirmations bienvenues ont été trouvées dans les deux bibliothèques
dmg2iso et dmg2img, diffusées sur le site vultur.eu.org
et écrites par Vu1tur et Jean-Pierre Demailly.
La partie la plus ardue a été le décodage du mode de compression ADC, pour lequel
il ne semble pas exister de documentation accessible publiquement. Cependant,
le travail a pu être achevé en quelques jours.
Mode de compression ADC
Le mode de compression ADC (Apple Data Compression) repose à la fois sur
la réduction des séquences répétitives et sur un stockage des données répétitives
dans un dictionnaire. Le mieux est de donner du pseudo-code :
Lire un octet.
Si le bit 7 est armé, c'est une séquence de données.
Prendre le reste de l'octet, ajouter 1 et copier le tampon d'origine vers le
tampon de destination.
Si le bit 6 est armé, c'est un code sur 3 octets.
Le premier octet code la longueur, les suivants le décalage dans le dictionnaire
(qui est en fait le tampon de destination lui-même).
Tous les décalages doivent être incrémentés de 1.
Ajouter 4 à la longueur après désarmement du bit 6.
Placer un pointeur de décalage dans le tampon de destination en reculant à partir du
pointeur de destination. S'il y a assez de place entre les deux pour la longueur
de données, utiliser memcpy pour copier les données entre le pointeur de décalage
et le pointeur de destination. Sinon, utiliser memset pour recopier n fois l'octet
pointé tout seul.
Si aucun des deux n'est armé, c'est un code sur 2 octets.
La longueur est codée sur les bits 2345. Elle est comptée à partir de 3 (c'est-à-dire
0000 = 3, 0001 = 4, etc.). Les bits 01 doivent être associés aux 7 bits de l'octet
suivant pour donner un décalage. Faire la même chose que dans le cas précédent
pour utiliser soit memcpy, soit memset.
Jusqu'à ce qu'il n'y ait plus de données à décomprimer.
|