Why is it that my initrd only has one directory, namely, 'kernel'?

The cpio block skip method given doesn't work reliably. That's because the initrd images I was getting myself didn't have both archives concatenated on a 512 byte boundary.

Instead, do this:

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 the last number (21136) which is not on a 512 byte boundary for me:

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

If you know your initrd.img consists of an uncompressed cpio archive followed by a gz-compressed cpio archive, you can use the following to extract all files (from both archives) into your current working directory (tested in bash):

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

The above command-line passes the contents of initrd.img as standard input into a subshell which executes the two commands cpio -id and zcat | cpio -id sequentially. The first command (cpio -id) terminates once it has read all the data belonging to the first cpio archive. The remaining content is then passed to zcat | cpio -id, which decompresses and unpacks the second archive.


It turns out the initrd generated by Debian's live-build (and to my surprise, accepted by the kernel) is actually the concatenation of two images:

  • a CPIO archive containing microcode updates to be applied on the processor;
  • a gzip-ed cpio archive, which actually contains the initrd file tree (with the /etc /bin /sbin /dev ... directories that were expected).

Upon extracting the original initrd.img, straight out of the live-build output, I got this output:

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

Which means that the cpio extraction ended after parsing 896 blocks of 512 Bytes each. But the original initrd.img was way bigger than 896*512 = 458752B = 448 KB :

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

So the actual initrd image I was looking for was appended right after the first cpio archive (the one containing the microcode updates) and could be accessed using dd:

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