Pourquoi mon initrd n'a-t-il qu'un seul répertoire, à savoir «noyau»?

29

J'utilise debian live-build pour travailler sur un système amorçable. À la fin du processus, j'obtiens les fichiers typiques utilisés pour démarrer un système en direct: un fichier squashfs, certains modules GRUB et fichiers de configuration, et un fichier initrd.img.

Je peux très bien démarrer en utilisant ces fichiers, en passant l'initrd au noyau via

initrd=/path/to/my/initrd.img

sur la ligne de commande du chargeur de démarrage. Mais quand j'essaie d'examiner le contenu de mon image initrd, comme ceci:

$file initrd.img
initrd.img: ASCII cpio archive (SVR4 with no CRC)
$mkdir initTree && cd initTree
$cpio -idv < ../initrd.img

l'arborescence des fichiers que je reçois ressemble à ceci:

$tree --charset=ASCII
.
`-- kernel
    `-- x86
        `-- microcode
            `-- GenuineIntel.bin

Où se trouve l'arborescence du système de fichiers, avec les fichiers / bin, / etc, / sbin ... contenant les fichiers réellement utilisés lors du démarrage?

user986730
la source
1
La commande 'lsinitramfs' a été conçue pour cela.
earlgrey

Réponses:

32

La méthode de saut de bloc cpio donnée ne fonctionne pas de manière fiable. C'est parce que les images initrd que j'obtenais moi-même n'avaient pas les deux archives concaténées sur une limite de 512 octets.

Au lieu de cela, procédez comme suit:

apt-get install binwalk
legolas [mc]# binwalk initrd.img 
DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             ASCII cpio archive (SVR4 with no CRC), file name: "kernel", file name length: "0x00000007", file size: "0x00000000"
120           0x78            ASCII cpio archive (SVR4 with no CRC), file name: "kernel/x86", file name length: "0x0000000B", file size: "0x00000000"
244           0xF4            ASCII cpio archive (SVR4 with no CRC), file name: "kernel/x86/microcode", file name length: "0x00000015", file size: "0x00000000"
376           0x178           ASCII cpio archive (SVR4 with no CRC), file name: "kernel/x86/microcode/GenuineIntel.bin", file name length: "0x00000026", file size: "0x00005000"
21004         0x520C          ASCII cpio archive (SVR4 with no CRC), file name: "TRAILER!!!", file name length: "0x0000000B", file size: "0x00000000"
21136         0x5290          gzip compressed data, from Unix, last modified: Sat Feb 28 09:46:24 2015

Utilisez le dernier numéro (21136) qui n'est pas sur une limite de 512 octets pour moi:

legolas [mc]# dd if=initrd.img bs=21136 skip=1 | gunzip | cpio -tdv | head
drwxr-xr-x   1 root     root            0 Feb 28 09:46 .
drwxr-xr-x   1 root     root            0 Feb 28 09:46 bin
-rwxr-xr-x   1 root     root       554424 Dec 17  2011 bin/busybox
lrwxrwxrwx   1 root     root            7 Feb 28 09:46 bin/sh -> busybox
-rwxr-xr-x   1 root     root       111288 Sep 23  2011 bin/loadkeys
-rwxr-xr-x   1 root     root         2800 Aug 19  2013 bin/cat
-rwxr-xr-x   1 root     root          856 Aug 19  2013 bin/chroot
-rwxr-xr-x   1 root     root         5224 Aug 19  2013 bin/cpio
-rwxr-xr-x   1 root     root         3936 Aug 19  2013 bin/dd
-rwxr-xr-x   1 root     root          984 Aug 19  2013 bin/dmesg
Marc Merlin
la source
En effet, votre réponse bat la mienne. Je n'ai jamais pensé que l'alignement serait un problème. Je me demande, cependant, si cpio donnerait une sortie plus intéressante si la première image contenue dans le fichier multi-images n'était pas doublée en 512B.
user986730
Comment le restaurer (reconditionner à son état d'origine) après l'avoir modifié, avec la même hiérarchie de dossiers?
EdiD
2
Juste cddans le répertoire où vous avez décompressé l' archive cpio, course find | cpio -H newc -o > /tmp/my_archive.cpio, puis gzip avec gzip /tmp/my_archive.cpioet enfin, concaténer avec la l'image de microcode, si vous aviez un: cat my_microcode_image.cpio /tmp/my_archive.cpio.gz > mynewinitrd.img. Si vous n'aviez pas d'image de microcode, alors vous pouvez simplement utiliser votre fichier compressé tel quel dans votre chargeur de démarrage
user986730
En lisant cette réponse, il semble évident que cela ne fonctionnera que si le contenu compressé est passé à mi-chemin dans le fichier. Sinon, vous devez changer la taille du bloc à 1 et définir skip sur le nombre d'octets à ignorer. Une raison de ne pas toujours faire ça?
TamaMcGlinn
deuxièmement, pour réellement écrire les fichiers au lieu de simplement en énumérer certains, changez la commande finale dans le canal en cpio -iplutôt que cpio -tdv | head.
TamaMcGlinn
22

Si vous savez que votre initrd.imgfichier se compose d'une archive cpio non compressée suivie d'une archive cpio compressée gz, vous pouvez utiliser ce qui suit pour extraire tous les fichiers (des deux archives) dans votre répertoire de travail actuel (testé en bash):

(cpio -id; zcat | cpio -id) < /path/to/initrd.img

La ligne de commande ci-dessus transmet le contenu de initrd.imgl'entrée standard dans un sous-shell qui exécute les deux commandes cpio -idet de zcat | cpio -idmanière séquentielle. La première commande ( cpio -id) se termine une fois qu'elle a lu toutes les données appartenant à la première archive cpio. Le contenu restant est ensuite transmis à zcat | cpio -id, qui décompresse et décompresse la deuxième archive.

Woolpool
la source
1
Cela ressemble de loin à la solution la plus propre
velis
1
Cela fonctionne à merveille
TurboHz
Mystérieusement, la bonne réponse de @ woolpool est la seule réponse que l'utilisateur ait jamais publiée. Voilà le style. Si vous deviez poster une seule réponse pendant toute votre carrière StackExchange, vous ne pourriez guère faire mieux que d'en poster une comme celle-ci. OP pourrait envisager de changer la réponse acceptée à celle-ci.
thb
16

Il s'avère que l'initrd généré par la construction live de Debian (et à ma grande surprise, accepté par le noyau) est en fait la concaténation de deux images:

  • une archive CPIO contenant des mises à jour de microcode à appliquer sur le processeur;
  • une archive cpio gzip-ed, qui contient en fait l'arborescence de fichiers initrd (avec les répertoires / etc / bin / sbin / dev ... qui étaient attendus).

En extrayant le initrd.img d'origine, directement à partir de la sortie live-build, j'ai obtenu cette sortie:

$cpio -idv ../initrd.img
kernel
kernel/x86
kernel/x86/microcode
kernel/x86/microcode/GenuineIntel.bin
896 blocks

Ce qui signifie que l'extraction de cpio s'est terminée après l'analyse de 896 blocs de 512 octets chacun. Mais le fichier initrd.img d'origine était bien plus grand que 896 * 512 = 458752B = 448 Ko:

$ls -liah initrd.img
3933924 -r--r--r-- 1 root root 21M Oct 21 10:05 initrd.img

Donc, l'image initrd réelle que je cherchais a été ajoutée juste après la première archive cpio (celle contenant les mises à jour du microcode) et était accessible en utilisant dd:

$dd if=initrd.img of=myActualInitrdImage.img.gz bs=512 skip=896
user986730
la source
2

Vous pouvez utiliser unmkinitramfsdepuis initramfs-tools> = 0.126, qui est inclus depuis Debian 9 (stretch) et Ubuntu 18.04 (bionic).

Benjamin Drung
la source
1

Sur la base de l'idée donnée dans la réponse de @ woolpool, j'ai écrit une fonction récursive qui fonctionnera pour n'importe quelle archive cpio quelle que soit la disposition des données concaténées et ne nécessite aucun outil spécial comme binwalk. Par exemple, mon mkinitramfs produisait un fichier cpio; cpio; gzip. Il fonctionne en extrayant chaque partie du fichier initrd concaténé, en enregistrant le reste dans un fichier temporaire puis en utilisant le programme "file" pour décider quoi faire avec la partie suivante.

uncpio(){
if [[ $(wc -c $1 | cut -d ' ' -f1) -eq 0 ]]; then
    return
fi

type=$(cat $1 | file -)
local tmpfile=$(date +%s.%N)
echo -e "\n$type"
if [[ $type =~ .*cpio.* ]]; then
    cat $1 | (cpio -id; cat >$tmpfile)
elif [[ $type =~ .*gzip.* ]]; then
    zcat $1 | (cpio -id; cat >$tmpfile)
else
    return
fi
uncpio $tmpfile 
rm $tmpfile
}

Pour utiliser, tapez: uncpio initrdfilename

SurpriseDog
la source
0

Si vous devez effectuer cette tâche souvent, vous souhaiterez peut-être créer une petite fonction bash comme la suivante (et peut-être l'ajouter à votre .bashrc):

initramfs-extract() {
    local target=$1
    local offset=$(binwalk -y gzip $1 | awk '$3 ~ /gzip/ { print $1; exit }')
    shift
    dd if=$target bs=$offset skip=1 | zcat | cpio -id --no-absolute-filenames $@
}

Le code est basé sur la réponse de Marc, mais il est beaucoup plus rapide car binwalk ne recherchera que les fichiers gzip. Vous pouvez l'invoquer, comme ceci:

$ initramfs-extract /boot/initrd.img -v

Vous aurez besoin binwalkinstallé pour le faire fonctionner.

tyrion
la source