.. default-role:: code
.. target pdf html
Empilement de périphériques en mode bloc
========================================
La démo est modifiée pour vous permettre de la rejouer sur votre machine en
tant que `root`, sans utiliser de disque physique externe, clef USB, carte sd
ou autre, uniquement des périphérique boucle.
Si vous utilisez WSL sous windows, il faut passer à WSL2 (ou mieux : profitez
de l'occasion pour passer à `virtualbox`).
On part de deux périphériques `loop` qu'on va découper, coller, chiffrer, etc
- Point de départ : 2 disques de `100 MiB` chacun
- Point d'arrivée :
- un périphérique en mode bloc chiffré de `70 MiB`, formaté `ext4`
- un périphérique en mode bloc de `70 MiB`, formaté `xfs`
- un périphérique en mode bloc de `50 MiB`, formaté `vfat`
On se place dans le répertoire temporaire `/tmp` :
::
# cd /tmp
On crée un fichier `/tmp/disque1` rempli de zéros de taille `100 MiB`. Pour
cela, on utilise le fichier spécial `/dev/zero` qui est un périphérique
caractère qui génère une infinité de zéros. On utilise la commande `dd` car il
faut bien s'arrêter à un moment.
::
# dd if=/dev/zero of=disque1 bs=1MiB count=100
100+0 enregistrements lus
100+0 enregistrements écrits
104857600 octets (105 MB, 100 MiB) copiés, 0,0612908 s, 1,7 GB/s
Remarque : dans la démo originale, on avait d'abord testé :
::
# cp /dev/zero disque1
et on s'était rendu compte que le système de fichiers sur lequel était monté
`/tmp` s'était rempli très vite, avec un fichier `disque1` qui a pris toute
la place avant de créer une erreur. Ne le faites pas sur les machines de TP
(ou alors allez dans `/dev/shm` et n'oubliez pas d'effacer le fichier
ensuite).
On peut voir que le fichier est plein de zéros :
::
# hexdump disque1
0000000 0000 0000 0000 0000 0000 0000 0000 0000
*
6400000
On fait pareil avec le fichier `/tmp/disque2`, ou alors on recopie le contenu
de `disque1` dans `disque2` :
::
# cp disque1 disque2
Ces fichiers sont des fichiers réguliers, on peut les promouvoir en
périphériques en mode bloc :
::
# losetup -f disque1
On regarde quel périphérique boucle est associé à `disque1` :
::
# losetup --associated disque1
/dev/loop0: [0031]:3565662 (/tmp/disque1)
On fait pareil pour `disque2` :
::
# losetup -f disque2
On liste les périphériques boucles existants :
::
# losetup --list
NAME SIZELIMIT OFFSET AUTOCLEAR RO BACK-FILE DIO LOG-SEC
/dev/loop1 0 0 0 0 /tmp/disque2 0 512
/dev/loop0 0 0 0 0 /tmp/disque1 0 512
On peut voir que deux périphériques bloc sont apparus :
::
# lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
...
loop0 7:0 0 100M 0 loop
loop1 7:1 0 100M 0 loop
On crée une table de partitions de type `msdos` sur `/dev/loop0` (`man parted`) :
::
# parted /dev/loop0 mklabel msdos
Information: You may need to update /etc/fstab.
On peut voir qu'une *en-tête* (en: *header*) a été écrite au début du fichier :
::
# hexdump disque1
0000000 b8fa 1000 d08e 00bc b8b0 0000 d88e c08e
0000010 befb 7c00 00bf b906 0200 a4f3 21ea 0006
0000020 be00 07be 0438 0b75 c683 8110 fefe 7507
0000030 ebf3 b416 b002 bb01 7c00 80b2 748a 8b01
0000040 024c 13cd 00ea 007c eb00 00fe 0000 0000
0000050 0000 0000 0000 0000 0000 0000 0000 0000
*
00001b0 0000 0000 0000 0000 e571 5a09 0000 0000
00001c0 0000 0000 0000 0000 0000 0000 0000 0000
*
00001f0 0000 0000 0000 0000 0000 0000 0000 aa55
0000200 0000 0000 0000 0000 0000 0000 0000 0000
*
6400000
On crée deux partitions primaires de taille `50 MiB` :
::
# parted /dev/loop0 mkpart primary 0 50MiB
Warning: The resulting partition is not properly aligned for best performance.
Ignore/Cancel? C
Il est pas content, on annule (`C`) et on re-éssaye :
::
# parted /dev/loop0 mkpart primary 1MiB 50MiB
Information: You may need to update /etc/fstab.
# parted /dev/loop0 mkpart primary 50MiB 100%
Information: You may need to update /etc/fstab.
On liste ("`ls`") les périphériques en mode bloc ("`blk`") pour voir ou on en
est :
::
# lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
...
loop0 7:0 0 100M 0 loop
├─loop0p1 259:0 0 49M 0 part
└─loop0p2 259:1 0 50M 0 part
loop1 7:1 0 100M 0 loop
On peut aussi remarquer que le fait d'avoir créé deux partitions, n'a fait que
modifier la table de partitions, mais par exemple, rien n'est écrit au milieu
du disque (à la frontière entre les partitions) :
::
# hexdump disque1
0000000 b8fa 1000 d08e 00bc b8b0 0000 d88e c08e
0000010 befb 7c00 00bf b906 0200 a4f3 21ea 0006
0000020 be00 07be 0438 0b75 c683 8110 fefe 7507
0000030 ebf3 b416 b002 bb01 7c00 80b2 748a 8b01
0000040 024c 13cd 00ea 007c eb00 00fe 0000 0000
0000050 0000 0000 0000 0000 0000 0000 0000 0000
*
00001b0 0000 0000 0000 0000 e571 5a09 0000 2000
00001c0 0021 5f83 0619 0800 0000 8800 0001 5f00
00001d0 061a be83 0c32 9000 0001 9000 0001 0000
00001e0 0000 0000 0000 0000 0000 0000 0000 0000
00001f0 0000 0000 0000 0000 0000 0000 0000 aa55
0000200 0000 0000 0000 0000 0000 0000 0000 0000
*
6400000
Bien sur, c'est le même contenu que `/dev/loop0` :
::
# hexdump /dev/loop0
0000000 b8fa 1000 d08e 00bc b8b0 0000 d88e c08e
0000010 befb 7c00 00bf b906 0200 a4f3 21ea 0006
0000020 be00 07be 0438 0b75 c683 8110 fefe 7507
0000030 ebf3 b416 b002 bb01 7c00 80b2 748a 8b01
0000040 024c 13cd 00ea 007c eb00 00fe 0000 0000
0000050 0000 0000 0000 0000 0000 0000 0000 0000
*
00001b0 0000 0000 0000 0000 e571 5a09 0000 2000
00001c0 0021 5f83 0619 0800 0000 8800 0001 5f00
00001d0 061a be83 0c32 9000 0001 9000 0001 0000
00001e0 0000 0000 0000 0000 0000 0000 0000 0000
00001f0 0000 0000 0000 0000 0000 0000 0000 aa55
0000200 0000 0000 0000 0000 0000 0000 0000 0000
*
6400000
On peut voir que l'espace occupé par les partitions n'a que des zéros :
::
# hexdump /dev/loop0p1
0000000 0000 0000 0000 0000 0000 0000 0000 0000
*
3100000
On va utiliser `LVM` pour recoller la première partition de `/dev/loop0` (`50
MiB`) et `/dev/loop1` (`100 MiB`), redécouper l'ensemble en deux périphériques
de tailles `70 MiB` chacun (voir le schéma des commandes `LVM` dans les slides
du cours) :
On transforme les deux périphériques en volumes physiques `LVM` :
::
# pvcreate /dev/loop0p1
Physical volume "/dev/loop0p1" successfully created.
On peut observer que ça a écrit des choses au début de la partition (notez les indications) :
::
# hexdump -C /dev/loop0p1
00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00000200 4c 41 42 45 4c 4f 4e 45 01 00 00 00 00 00 00 00 |LABELONE........|
00000210 12 2c 0d 55 20 00 00 00 4c 56 4d 32 20 30 30 31 |.,.U ...LVM2 001|
00000220 33 4b 34 39 62 6a 37 63 32 30 75 31 45 65 76 39 |3K49bj7c20u1Eev9|
00000230 32 42 58 62 37 4c 46 39 4b 67 72 73 61 48 42 52 |2BXb7LF9KgrsaHBR|
00000240 00 00 10 03 00 00 00 00 00 00 10 00 00 00 00 00 |................|
00000250 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000260 00 00 00 00 00 00 00 00 00 10 00 00 00 00 00 00 |................|
00000270 00 f0 0f 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000280 00 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00 |................|
00000290 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00001000 16 d6 8e db 20 4c 56 4d 32 20 78 5b 35 41 25 72 |.... LVM2 x[5A%r|
00001010 30 4e 2a 3e 01 00 00 00 00 10 00 00 00 00 00 00 |0N*>............|
00001020 00 f0 0f 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00001030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
03100000
On fait pareil pour `/dev/loop1` :
::
# pvcreate /dev/loop1
Physical volume "/dev/loop1" successfully created.
On les fusionne en un volume group `LVM` nommé `vg_disque` :
::
# vgcreate vg_disque /dev/loop0p1 /dev/loop1
Volume group "vg_disque" successfully created
Si vous refaites un `hexdump -C` sur les deux périphériques bloc, vous verrez
les indications sur le volume group (exercice).
On découpe le volume groupe `LVM` en deux volumes logiques de `70 MiB` :
::
# lvcreate -L 70MiB vg_disque
Rounding up size to full physical extent 72,00 MiB
Logical volume "lvol0" created.
# lvcreate -L 80MiB vg_disque
Volume group "vg_disque" has insufficient free space (18 extents): 20 required.
# lvcreate -L 70MiB vg_disque
Rounding up size to full physical extent 72,00 MiB
Logical volume "lvol1" created.
On jette un oeil, les deux volumes logiques sont bien des périphériques en mode
bloc :
::
# lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
...
loop0 7:0 0 100M 0 loop
├─loop0p1 259:0 0 49M 0 part
│ └─vg_disque-lvol1 253:2 0 72M 0 lvm
└─loop0p2 259:1 0 50M 0 part
loop1 7:1 0 100M 0 loop
├─vg_disque-lvol0 253:1 0 72M 0 lvm
└─vg_disque-lvol1 253:2 0 72M 0 lvm
On remarque que bien que `lsblk` nous montre la situation comme s'il s'agissait
d'arbres, en fait `vg_disque-lvol1` apparaît 2 fois, car ce périphérique bloc
est à cheval entre les périphériques `loop0p1` et `loop1`. Comme on l'a discuté
en cours, la relation entre les périphériques en mode bloc est plutôt un DAG
(*directed acyclic graph*, structure représentant une relation de dépendance).
Notez aussi que les volumes physiques `LVM` et le volume group `LVM` ne sont
pas des périphériques bloc, mais des étapes intérmédiaires spécifiques à `LVM`
(ils n'apparaîssent pas dans la sortie de la commande `lsblk`).
On chiffre le premier volume logique :
::
# cryptsetup luksFormat /dev/mapper/vg_disque-lvol0
WARNING!
========
Cette action écrasera définitivement les données sur /dev/mapper/vg_disque-lvol0.
Are you sure? (Type uppercase yes): YES
Saisissez la phrase secrète pour /dev/mapper/vg_disque-lvol0 :
Vérifiez la phrase secrète :
Remarque, si on fait un :
::
# hexdump -C /dev/mapper/vg_disque-lvol0
on observe qu'un énorme header a été ajouté à la suite du header `LVM` : il
commence par "`SKUL`" (qui est le renversé de "`LUKS`" : *Linux Unified Key
Setup*), c'est le header de la nouvelle "couche de chiffrement" correspondant
au prériphérique bloc chiffré. Ce header contient de nombreux octets
apparement aléatoires, car il contient la clef de chiffrement du périphérique
chiffré, cette clef est elle-même chiffrée avec la phrase de passe que nous
venons d'entrer.
Après le chiffrement de ce périphérique en mode bloc, on ne voit pas de
périphérique en mode bloc apparaître :
::
# lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
loop0 7:0 0 100M 0 loop
├─loop0p1 259:0 0 49M 0 part
│ └─vg_disque-lvol1 253:2 0 72M 0 lvm
└─loop0p2 259:1 0 50M 0 part
loop1 7:1 0 100M 0 loop
├─vg_disque-lvol0 253:1 0 72M 0 lvm
└─vg_disque-lvol1 253:2 0 72M 0 lvm
C'est normal car, étant chiffré, on a un truc illisible, il faut d'abord
permettre au système de le déchiffrer (en lui donnant la phrase de passe) pour
que le système puisse le manipuler :
::
# cryptsetup open /dev/mapper/vg_disque-lvol0 crypt_disque
Saisissez la phrase secrète pour /dev/mapper/vg_disque-lvol0 :
On regarde qu'un périphérique en mode bloc est bien apparu :
::
# lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
loop0 7:0 0 100M 0 loop
├─loop0p1 259:0 0 49M 0 part
│ └─vg_disque-lvol1 253:2 0 72M 0 lvm
└─loop0p2 259:1 0 50M 0 part
loop1 7:1 0 100M 0 loop
├─vg_disque-lvol0 253:1 0 72M 0 lvm
│ └─crypt_disque 253:3 0 56M 0 crypt
└─vg_disque-lvol1 253:2 0 72M 0 lvm
Remarque : les périphériques bloc de type `LVM2` et `LUKS` sont numérotés
`/dev/dm-` mais sont accessibles par leur petit nom dans `/dev/mapper/` via
un lien symbolique :
::
# ls -l /dev/mapper/
total 0
crw------- 1 root root 10, 236 mars 2 15:05 control
lrwxrwxrwx 1 root root 7 mars 3 00:19 crypt_disque -> ../dm-2
lrwxrwxrwx 1 root root 7 mars 3 00:19 vg_disque-lvol0 -> ../dm-0
lrwxrwxrwx 1 root root 7 mars 3 00:22 vg_disque-lvol1 -> ../dm-1
À partir de là, on peut installer des systèmes de fichiers dans les
périphériques en mode bloc qu'on a obtenu, au hasard `ext4`, `xfs`, `vfat` :
::
# mkfs.ext4 /dev/mapper/crypt_disque
mke2fs 1.44.5 (15-Dec-2018)
Creating filesystem with 57344 1k blocks and 14336 inodes
Filesystem UUID: 36386848-5f90-4ab7-b7d8-ad1be16bfcbb
Superblock backups stored on blocks:
8193, 24577, 40961
Allocating group tables: done
Writing inode tables: done
Creating journal (4096 blocks): done
Writing superblocks and filesystem accounting information: done
# mkfs.xfs /dev/mapper/vg_disque-lvol1
meta-data=/dev/mapper/vg_disque-lvol1 isize=512 agcount=4, agsize=4608 blks
= sectsz=512 attr=2, projid32bit=1
= crc=1 finobt=1, sparse=1, rmapbt=0
= reflink=0
data = bsize=4096 blocks=18432, imaxpct=25
= sunit=0 swidth=0 blks
naming =version 2 bsize=4096 ascii-ci=0, ftype=1
log =internal log bsize=4096 blocks=855, version=2
= sectsz=512 sunit=0 blks, lazy-count=1
realtime =none extsz=4096 blocks=0, rtextents=0
# mkfs.vfat /dev/loop0p2
mkfs.fat 4.1 (2017-01-24)
On peut monter ces systèmes de fichier sur des répertoires :
::
# mkdir m1 m2 m3
# mount /dev/mapper/crypt_disque m1
# mount /dev/mapper/vg_disque-lvol1 m2
# mount /dev/loop0p2 m3
# mount
[...]
/dev/mapper/crypt_disque on /tmp/m1 type ext4 (rw,relatime)
/dev/mapper/vg_disque-lvol1 on /tmp/m2 type xfs (rw,relatime,attr2,inode64,noquota)
/dev/loop0p2 on /tmp/m3 type vfat (rw,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=ascii,shortname=mixed,utf8,errors=remount-ro)
Par contre, toutes ces encapsulations et systèmes de fichiers prennent de la
place (tables des inoeuds, etc), de sorte qu'on a perdu par rapport aux
périphériques en mode bloc initiaux :
::
# df -h
Sys. de fichiers Taille Utilisé Dispo Uti% Monté sur
[...]
/dev/mapper/crypt_disque 51M 1,1M 46M 3% /tmp/m1
/dev/mapper/vg_disque-lvol1 69M 4,0M 65M 6% /tmp/m2
/dev/loop0p2 50M 0 50M 0% /tmp/m3
On a bien un truc qui marche :
::
# echo hop > m1/plop
# ls m1/
lost+found plop
Remarque : on peut obtenir des infos sur les périphériques bloc et les systèmes
de fichiers avec d'autres outils :
On peut lister les périphériques bloc avec "`fdisk -l`" :
::
# fdisk -l
...
...
...
Disque /dev/loop0 : 100 MiB, 104857600 octets, 204800 secteurs
Unités : secteur de 1 × 512 = 512 octets
Taille de secteur (logique / physique) : 512 octets / 512 octets
taille d'E/S (minimale / optimale) : 512 octets / 512 octets
Type d'étiquette de disque : dos
Identifiant de disque : 0xf2c230bd
Périphérique Amorçage Début Fin Secteurs Taille Id Type
/dev/loop0p1 2048 102399 100352 49M 83 Linux
/dev/loop0p2 102400 204799 102400 50M 83 Linux
Disque /dev/loop1 : 100 MiB, 104857600 octets, 204800 secteurs
Unités : secteur de 1 × 512 = 512 octets
Taille de secteur (logique / physique) : 512 octets / 512 octets
taille d'E/S (minimale / optimale) : 512 octets / 512 octets
Disque /dev/mapper/vg_disque-lvol0 : 72 MiB, 75497472 octets, 147456 secteurs
Unités : secteur de 1 × 512 = 512 octets
Taille de secteur (logique / physique) : 512 octets / 512 octets
taille d'E/S (minimale / optimale) : 512 octets / 512 octets
Disque /dev/mapper/vg_disque-lvol1 : 72 MiB, 75497472 octets, 147456 secteurs
Unités : secteur de 1 × 512 = 512 octets
Taille de secteur (logique / physique) : 512 octets / 512 octets
taille d'E/S (minimale / optimale) : 512 octets / 512 octets
Disque /dev/mapper/crypt_disque : 56 MiB, 58720256 octets, 114688 secteurs
Unités : secteur de 1 × 512 = 512 octets
Taille de secteur (logique / physique) : 512 octets / 512 octets
taille d'E/S (minimale / optimale) : 512 octets / 512 octets
On peut directement regarder le fichier `/proc/partitions` :
::
# cat /proc/partitions
major minor #blocks name
...
...
...
7 0 102400 loop0
259 0 50176 loop0p1
259 1 51200 loop0p2
7 1 102400 loop1
253 1 73728 dm-0
253 2 73728 dm-1
253 3 57344 dm-2
On peut regarder les systèmes de fichiers montés avec `findmnt`, qui est plus
sympa à lire que ce que retourne `mount` :
::
# findmnt
TARGET SOURCE FSTYPE OPTIONS
/ ...... ...... .......
├─/...
├─/...
├─/...
└─/tmp tmpfs tmpfs rw,relatime,size=8388608k
├─/tmp/m1 /dev/mapper/crypt_disque ext4 rw,relatime
├─/tmp/m2 /dev/mapper/vg_disque-lvol1 xfs rw,relatime,attr2,inode64,noquota
└─/tmp/m3 /dev/loop0p2 vfat rw,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=ascii,shortname=mixed,utf8,errors=remount-ro
Si on veut tout ranger avant de quitter, on remonte le temps :
::
# umount m1 m2 m3
# cryptsetup close crypt_disque
# vgremove vg_disque
Do you really want to remove volume group "vg_disque" containing 2 logical volumes? [y/n]: y
Do you really want to remove active logical volume vg_disque/lvol0? [y/n]: y
Logical volume "lvol0" successfully removed
Do you really want to remove active logical volume vg_disque/lvol1? [y/n]: y
Logical volume "lvol1" successfully removed
Volume group "vg_disque" successfully removed
# losetup -d /dev/loop0 /dev/loop1
Exercice : faites un dessin de la situation, avec les empilements de
périphériques en mode bloc, les systèmes de fichiers, et leurs montages dans
l'arborescence.