Warum hat meine initrd nur ein Verzeichnis, nämlich "Kernel"?

28

Ich benutze Debian Live-Build, um auf einem bootfähigen System zu arbeiten. Am Ende des Prozesses erhalte ich die typischen Dateien, die zum Booten eines Live-Systems verwendet werden: eine Squashfs-Datei, einige GRUB-Module und Konfigurationsdateien sowie eine initrd.img-Datei.

Mit diesen Dateien kann ich problemlos booten und die initrd über an den Kernel übergeben

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

in der Bootloader-Befehlszeile. Aber wenn ich versuche, den Inhalt meines initrd-Images zu untersuchen, dann so:

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

Der Dateibaum, den ich bekomme, sieht folgendermaßen aus:

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

Wo befindet sich der tatsächliche Dateisystembaum mit dem typischen Verzeichnis / bin, / etc, / sbin ..., das die tatsächlichen Dateien enthält, die während des Startvorgangs verwendet wurden?

user986730
quelle
1
Dafür wurde der Befehl 'lsinitramfs' entwickelt.
Earlgrey

Antworten:

31

Die angegebene Methode zum Überspringen von cpio-Blöcken funktioniert nicht zuverlässig. Das liegt daran, dass bei den initrd-Images, die ich selbst erhalten habe, nicht beide Archive mit einer 512-Byte-Grenze verknüpft waren.

Tun Sie stattdessen Folgendes:

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

Verwenden Sie die letzte Nummer (21136), die sich für mich nicht an einer 512-Byte-Grenze befindet:

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
quelle
In der Tat übertrifft Ihre Antwort meine. Ich hätte nie gedacht, dass die Ausrichtung ein Problem sein würde. Ich frage mich jedoch, ob cpio eine interessantere Ausgabe liefern würde, wenn das erste Bild in der Multi-Image-Datei nicht mit 512B belegt wäre.
User986730
Wie kann ich es nach der Änderung mit derselben Ordnerhierarchie zurücksetzen (in den ursprünglichen Zustand zurücksetzen)?
EdiD
2
Gerade cdin dem Verzeichnis , in dem Sie Ihre cpio - Archiv extrahiert, laufen find | cpio -H newc -o > /tmp/my_archive.cpio, gzip es dann mit gzip /tmp/my_archive.cpiound schließlich verketten es mit der mit dem Mikrobild, wenn Sie einen hatte: cat my_microcode_image.cpio /tmp/my_archive.cpio.gz > mynewinitrd.img. Wenn Sie kein Mikrocode-Image hatten, können Sie Ihre gezippte Datei einfach so verwenden, wie sie in Ihrem Bootloader enthalten ist
user986730
Beim Lesen dieser Antwort scheint es offensichtlich zu sein, dass dies nur funktioniert, wenn der gezippte Inhalt in der Datei bereits zur Hälfte vorhanden ist. Andernfalls sollten Sie die Blockgröße auf 1 ändern und "Überspringen" auf die Anzahl der zu überspringenden Bytes festlegen. Gibt es einen Grund, das nicht immer zu tun?
TamaMcGlinn
Zweitens: Um die Dateien tatsächlich zu schreiben, anstatt nur einige aufzulisten, ändern Sie den Befehl final in der Pipe in cpio -ianstatt in cpio -tdv | head.
TamaMcGlinn
21

Wenn Sie wissen, dass Ihre initrd.imgDatei aus einem unkomprimierten cpio-Archiv und einem gz-komprimierten cpio-Archiv besteht, können Sie mit den folgenden Methoden alle Dateien (aus beiden Archiven) in Ihr aktuelles Arbeitsverzeichnis extrahieren (in Bash getestet):

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

Die obige Befehlszeilengibt den Inhalt initrd.imgals Standardeingabe in eine Subshell , die die beiden Befehle ausführt cpio -idund zcat | cpio -idsequentiell. Der erste Befehl ( cpio -id) wird beendet, sobald alle Daten des ersten cpio-Archivs gelesen wurden. Der verbleibende Inhalt wird dann an übergeben zcat | cpio -id, der das zweite Archiv dekomprimiert und entpackt.

Woolpool
quelle
1
Dies
scheint die
1
Es funktioniert wunderbar
TurboHz
Geheimnisvollerweise ist @ woolpools feine Antwort die einzige Antwort, die der Benutzer jemals gepostet hat. Das ist Stil. Wenn Sie während Ihrer gesamten StackExchange-Karriere nur eine Antwort veröffentlichen würden, könnten Sie kaum besseres tun, als eine solche zu veröffentlichen. OP könnte erwägen, die akzeptierte Antwort auf diese zu ändern .
1.
16

Es stellt sich heraus, dass die initrd, die durch Debians Live-Build erzeugt wird (und zu meiner Überraschung vom Kernel akzeptiert wird), tatsächlich die Verkettung von zwei Bildern ist:

  • ein CPIO-Archiv mit Mikrocode-Aktualisierungen, die auf den Prozessor angewendet werden sollen;
  • ein gzip-ed cpio-Archiv, das tatsächlich den initrd-Dateibaum enthält (mit den erwarteten Verzeichnissen / etc / bin / sbin / dev ...).

Nach dem Extrahieren der ursprünglichen initrd.img, direkt aus der Live-Build-Ausgabe, habe ich diese Ausgabe erhalten:

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

Dies bedeutet, dass die CPIO-Extraktion nach dem Parsen von 896 Blöcken mit jeweils 512 Bytes beendet wurde. Aber die ursprüngliche initrd.img war viel größer als 896 * 512 = 458752B = 448 KB:

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

Das gesuchte initrd-Image wurde also direkt nach dem ersten cpio-Archiv (das die Mikrocode-Updates enthält) angehängt und kann mit dd aufgerufen werden:

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

Sie können unmkinitramfsinitramfs-tools> = 0.126 verwenden, die seit Debian 9 (stretch) und Ubuntu 18.04 (bionic) enthalten sind.

Benjamin Drung
quelle
1

Basierend auf der Idee in der Antwort von @ woolpool habe ich eine rekursive Funktion geschrieben, die für jedes cpio-Archiv unabhängig von der Anordnung der verketteten Daten funktioniert und keine speziellen Tools wie binwalk erfordert. Zum Beispiel produzierte mein mkinitramfs eine cpio; cpio; gzip-Datei. Es funktioniert, indem jeder Teil der verketteten initrd-Datei extrahiert wird, der Rest in einem tempfile gespeichert wird und dann das "file" -Programm verwendet wird, um zu entscheiden, was mit dem nächsten Teil geschehen soll.

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
}

Zur Verwendung geben Sie ein: uncpio initrdfilename

Überraschungshund
quelle
0

Wenn Sie diese Aufgabe häufig ausführen müssen, möchten Sie möglicherweise eine kleine Bash-Funktion wie die folgende erstellen (und sie möglicherweise Ihrer .bashrc-Datei hinzufügen):

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 $@
}

Der Code basiert auf Marc's Antwort, ist aber deutlich schneller, da binwalk nur nach gzip-Dateien sucht. Sie können es folgendermaßen aufrufen:

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

Sie müssen binwalkinstalliert sein, damit es funktioniert.

Tyrion
quelle