¿Por qué mi initrd solo tiene un directorio, a saber, 'kernel'?

29

Estoy usando debian live-build para trabajar en un sistema de arranque. Al final del proceso obtengo los archivos típicos utilizados para arrancar un sistema en vivo: un archivo squashfs, algunos módulos GRUB y archivos de configuración, y un archivo initrd.img.

Puedo arrancar bien usando esos archivos, pasando el initrd al kernel a través de

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

en la línea de comando del gestor de arranque. Pero cuando trato de examinar el contenido de mi imagen initrd, así:

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

el árbol de archivos que obtengo se ve así:

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

¿Dónde está el árbol del sistema de archivos real, con el típico / bin, / etc, / sbin ... que contiene los archivos reales utilizados durante el arranque?

usuario986730
fuente
1
El comando 'lsinitramfs' fue diseñado para esto.
earlgrey

Respuestas:

32

El método de omisión de bloque cpio dado no funciona de manera confiable. Esto se debe a que las imágenes de initrd que estaba obteniendo no tenían ambos archivos concatenados en un límite de 512 bytes.

En cambio, haz esto:

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

Use el último número (21136) que no está en un límite de 512 bytes para mí:

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
fuente
De hecho, tu respuesta supera a la mía. Nunca pensé que la alineación sería un problema. Sin embargo, me pregunto si cpio daría un resultado más interesante si la primera imagen contenida en el archivo de múltiples imágenes no tuviera 512B.
user986730
¿Cómo revertirlo (volver a empaquetar al estado original) después de modificar, con la misma jerarquía de carpetas?
EdiD
2
Sólo cd en el directorio en el que extrajo el archivo cpio, carrera find | cpio -H newc -o > /tmp/my_archive.cpio, entonces gzip con gzip /tmp/my_archive.cpioy, finalmente, concatenar con la imagen de microcódigo con, si tuviera uno: cat my_microcode_image.cpio /tmp/my_archive.cpio.gz > mynewinitrd.img. Si no tenía una imagen de microcódigo, puede usar su archivo comprimido tal como está en su gestor de arranque
user986730
Al leer esta respuesta, parece obvio que esto solo funcionará si el contenido comprimido está más allá de la mitad del archivo. De lo contrario, debe cambiar el tamaño de bloque a 1 y establecer el salto al número de bytes a saltar. ¿Alguna razón para no hacerlo siempre?
TamaMcGlinn
segundo, para escribir realmente los archivos en lugar de solo enumerar algunos de ellos, cambie el comando final en la tubería a cpio -i, en lugar de cpio -tdv | head.
TamaMcGlinn
22

Si sabe que su initrd.imgarchivo consta de un archivo cpio sin comprimir seguido de un archivo cpio comprimido con gz, puede usar lo siguiente para extraer todos los archivos (de ambos archivos) en su directorio de trabajo actual (probado en bash):

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

La línea de comandos anterior pasa el contenido de initrd.imgcomo entrada estándar a una subshell que ejecuta los dos comandos cpio -idy de forma zcat | cpio -idsecuencial. El primer comando ( cpio -id) termina una vez que ha leído todos los datos que pertenecen al primer archivo cpio. El contenido restante se pasa a zcat | cpio -id, que descomprime y desempaqueta el segundo archivo.

lana
fuente
1
Esta parece la solución más limpia con diferencia
velis
1
Funciona muy bien
TurboHz
Misteriosamente, la buena respuesta de @ woolpool es la única respuesta que el usuario ha publicado. Eso es estilo Si iba a publicar solo una respuesta durante toda su carrera en StackExchange, difícilmente podría hacerlo mejor que publicar una como esta. OP podría considerar cambiar la respuesta aceptada a esta.
thb
16

Resulta que el initrd generado por la construcción en vivo de Debian (y para mi sorpresa, aceptado por el núcleo) es en realidad la concatenación de dos imágenes:

  • un archivo CPIO que contiene actualizaciones de microcódigo que se aplicarán en el procesador;
  • un archivo cpio gzip-ed, que en realidad contiene el árbol de archivos initrd (con los directorios / etc / bin / sbin / dev ... que se esperaban).

Al extraer el initrd.img original, directamente de la salida de construcción en vivo, obtuve esta salida:

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

Lo que significa que la extracción de cpio terminó después de analizar 896 bloques de 512 bytes cada uno. Pero el initrd.img original era mucho más grande que 896 * 512 = 458752B = 448 KB:

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

Entonces, la imagen de initrd real que estaba buscando se agregó justo después del primer archivo cpio (el que contiene las actualizaciones de microcódigo) y se podía acceder a ella usando dd:

$dd if=initrd.img of=myActualInitrdImage.img.gz bs=512 skip=896
usuario986730
fuente
2

Puede usar unmkinitramfsdesde initramfs-tools> = 0.126, que se incluye desde Debian 9 (stretch) y Ubuntu 18.04 (bionic).

Benjamin Drung
fuente
1

Basado en la idea dada en la respuesta de @ woolpool, escribí una función recursiva que funcionará para cualquier archivo cpio independientemente de la disposición de los datos concatenados y no requiere herramientas especiales como binwalk. Por ejemplo, mi mkinitramfs estaba produciendo un archivo cpio; cpio; gzip. Funciona al extraer cada parte del archivo initrd concatenado, guardar el resto en un archivo temporal y luego usar el programa "archivo" para decidir qué hacer con la siguiente parte.

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
}

Para usar el tipo: uncpio initrdfilename

SorpresaPerro
fuente
0

Si necesita realizar esta tarea con frecuencia, es posible que desee crear una pequeña función bash como la siguiente (y tal vez agregarla a su .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 $@
}

El código se basa en la respuesta de Marc, pero es significativamente más rápido ya que binwalk solo buscará archivos gzip. Puedes invocarlo así:

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

Deberá binwalkinstalarlo para que funcione.

tyrion
fuente