Dlaczego mój initrd ma tylko jeden katalog, a mianowicie „jądro”?

29

Korzystam z kompilacji live Debiana do pracy na systemie startowym. Pod koniec procesu otrzymuję typowe pliki używane do rozruchu systemu na żywo: plik squashfs, niektóre moduły GRUB i pliki konfiguracyjne oraz plik initrd.img.

Mogę dobrze uruchomić przy użyciu tych plików, przekazując initrd do jądra przez

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

w wierszu polecenia bootloadera. Ale kiedy próbuję zbadać zawartość mojego obrazu initrd, tak:

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

otrzymane drzewo plików wygląda następująco:

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

Gdzie jest rzeczywiste drzewo systemu plików, z typowym / bin, / etc, / sbin ... zawierające rzeczywiste pliki używane podczas uruchamiania?

użytkownik986730
źródło
1
Zaprojektowano do tego polecenie „lsinitramfs”.
Earlrey

Odpowiedzi:

32

Podana metoda pomijania bloku cpio nie działa niezawodnie. To dlatego, że obrazy initrd, które sobie robiłem, nie łączyły obu archiwów na granicy 512 bajtów.

Zamiast tego wykonaj następujące czynności:

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

Użyj ostatniego numeru (21136), który nie jest dla mnie na granicy 512 bajtów:

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
źródło
Rzeczywiście, twoja odpowiedź bije moją. Nigdy nie myślałem, że wyrównanie będzie problemem. Zastanawiam się jednak, czy cpio dałoby bardziej interesujące wyjście, gdyby pierwszy obraz zawarty w pliku wielu obrazów nie był podszyty 512B.
user986730,
Jak przywrócić go z powrotem (przepakować do stanu oryginalnego) po modyfikacji, z tą samą hierarchią folderów?
EdiD
2
Wystarczy cddo katalogu, w którym zostały wyodrębnione archiwum cpio, uruchom find | cpio -H newc -o > /tmp/my_archive.cpio, a następnie gzip go gzip /tmp/my_archive.cpiowreszcie złączyć go z obrazem z mikrokodu, gdybyś miał jeden: cat my_microcode_image.cpio /tmp/my_archive.cpio.gz > mynewinitrd.img. Jeśli nie masz obrazu mikrokodu, możesz po prostu użyć swojego pliku spakowanego gzipem, tak jak jest to w bootloaderze
user986730
Czytanie tej odpowiedzi wydaje się oczywiste, że zadziała to tylko wtedy, gdy spakowana zawartość znajdzie się w połowie pliku. W przeciwnym razie należy zmienić rozmiar bloku na 1 i ustawić wartość pominięcia na liczbę bajtów do pominięcia. Czy jest jakiś powód, aby nie zawsze tak robić?
TamaMcGlinn
po drugie, aby faktycznie napisać pliki zamiast tylko wypisywać niektóre z nich, zmień ostatnie polecenie w potoku na cpio -i, zamiast cpio -tdv | head.
TamaMcGlinn
22

Jeśli wiesz, że twoje initrd.imgskłada się z nieskompresowanego archiwum cpio, a następnie archiwum cpio skompresowanego gz, możesz użyć następującego polecenia, aby wyodrębnić wszystkie pliki (z obu archiwów) do bieżącego katalogu roboczego (przetestowanego w bash):

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

Polecenie powyżej linii przekazuje zawartość initrd.imgjako standardowe wejście w podpowłoce, który wykonuje dwa polecenia cpio -idi zcat | cpio -idkolejno. Pierwsze polecenie ( cpio -id) kończy się po odczytaniu wszystkich danych należących do pierwszego archiwum CPIO. Pozostała zawartość jest następnie przekazywana do zcat | cpio -iddekompresji i rozpakowywania drugiego archiwum.

woolpool
źródło
1
To jak dotąd najczystsze rozwiązanie
velis
1
Działa pięknie
TurboHz
W tajemniczy sposób świetna odpowiedź @ woolpool jest jedyną odpowiedzią, jaką użytkownik kiedykolwiek opublikował. To jest styl. Jeśli zamierzałeś zamieścić tylko jedną odpowiedź podczas całej swojej kariery StackExchange, nie mógłbyś zrobić nic lepszego niż opublikowanie takiej jak ta. OP może rozważyć zmianę zaakceptowanej odpowiedzi na to.
thb
16

Okazuje się, że initrd wygenerowany przez live-build Debiana (i ku mojemu zaskoczeniu, zaakceptowany przez jądro) jest w rzeczywistości połączeniem dwóch obrazów:

  • archiwum CPIO zawierające aktualizacje mikrokodu do zastosowania na procesorze;
  • archiwum cpio z edytorem gzip, które faktycznie zawiera drzewo plików initrd (z oczekiwanymi katalogami / etc / bin / sbin / dev ...).

Po wypakowaniu oryginalnego pliku initrd.img, bezpośrednio z wyjścia kompilacji na żywo, otrzymałem ten wynik:

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

Co oznacza, że ​​ekstrakcja procesora zakończyła się po przeanalizowaniu 896 bloków po 512 bajtów każdy. Ale oryginalny initrd.img był znacznie większy niż 896 * 512 = 458752B = 448 KB:

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

Tak więc rzeczywisty obraz initrd, którego szukałem, został dołączony zaraz po pierwszym archiwum CPIO (tym, które zawiera aktualizacje mikrokodu) i można było uzyskać do niego dostęp za pomocą dd:

$dd if=initrd.img of=myActualInitrdImage.img.gz bs=512 skip=896
użytkownik986730
źródło
2

Możesz używać unmkinitramfsz initramfs-tools> = 0.126, która jest dołączana od Debiana 9 (stretch) i Ubuntu 18.04 (bionic).

Benjamin Drung
źródło
1

Opierając się na pomyśle podanym w odpowiedzi na @ woolpool, napisałem funkcję rekurencyjną, która będzie działać dla każdego archiwum CPIO niezależnie od ułożenia połączonych danych i nie wymaga żadnych specjalnych narzędzi, takich jak binwalk. Na przykład moje mkinitramfs produkowało plik cpio; cpio; gzip. Działa poprzez wyodrębnienie każdej części połączonego pliku initrd, zapisanie reszty w pliku tymczasowym, a następnie za pomocą programu „file”, aby zdecydować, co zrobić z następną częścią.

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
}

Aby użyć wpisz: uncpio initrdfilename

SurpriseDog
źródło
0

Jeśli musisz często wykonywać to zadanie, możesz utworzyć małą funkcję bash, taką jak poniżej (i być może dodać ją do swojego .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 $@
}

Kod oparty jest na odpowiedzi Marca, ale jest znacznie szybszy, ponieważ binwalk będzie szukał tylko plików gzip. Możesz go wywołać w następujący sposób:

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

Musisz go binwalkzainstalować, aby działał.

tyrion
źródło