Home Comunidade Eventos YouTube Networks Desafios // produtos Academy News CTI | CVE Contato

02 - Estrutura e Conteúdo (cpio, gzip)

video explicativo
Video explicativoEspaco reservado para o video desta anotação — adicione video: https://youtu.be/... no frontmatter da nota.

A transição arquitetural do obsoleto initrd para o moderno initramfs exigiu uma mudança fundamental na forma como os dados de inicialização são empacotados e processados pelo kernel Linux. Enquanto o initrd era uma imagem de disco geométrica e formatada (geralmente ext2), o initramfs é, em sua essência, um arquivo compactado que contém uma hierarquia de arquivos e diretórios a ser extraída sequencialmente para a memória RAM. Para entender a eficiência e a flexibilidade deste modelo, é imperativo dissecar o formato de arquivamento escolhido, os algoritmos de compressão suportados e a anatomia exata do conteúdo que habita este ambiente efêmero.

1. O Formato CPIO (Copy-In, Copy-Out)

O formato padrão absoluto para o empacotamento do initramfs não é o tar (Tape Archive), onipresente no espaço de usuário, mas sim o cpio. Historicamente derivado do Unix System III e padronizado pelo POSIX.1, o cpio foi selecionado para o kernel Linux devido a vantagens arquiteturais cruciais no momento de extração crua (Ring 0).

1.1. Por que CPIO e não TAR?

Embora o tar seja mais popular, seu formato de cabeçalho exige preenchimento (padding) em blocos de 512 bytes e não foi originalmente desenhado para representar nós de dispositivos especiais (como blocos e caracteres) de forma padronizada entre diferentes arquiteturas Unix.
O cpio, especificamente o formato **SVR4 newc** (código mágico hexadecimal 070701), foi aprimorado para suportar sistemas de arquivos modernos com identificadores de inode de 32 bits (e mais tarde de 64 bits), propriedades de dispositivos (major/minor numbers) e hard links robustos.

A maior vantagem do cpio para o kernel é sua natureza estritamente contínua (streamable). O kernel pode alocar um fluxo de leitura a partir do endereço de memória onde o bootloader despejou o arquivo e processar metadados e conteúdos em uma única passagem (leitura sequencial $O(n)$), sem jamais precisar de operações de seek (busca aleatória para frente ou para trás).

1.2. O Desempacotador Interno do Kernel

Durante a rotina de inicialização, a função C populate_rootfs() (presente em init/initramfs.c no código-fonte do kernel) assume o controle. Ela não "monta" o cpio; ela literalmente lê o cabeçalho de cada arquivo e executa as syscalls sys_mkdir, sys_mknod, sys_symlink e sys_write para instanciar instantaneamente os inodes nas estruturas VFS do rootfs (um tmpfs alocado dinamicamente na RAM).

2. Compressão de Dados: Do gzip aos Algoritmos Modernos

Um cpio descompactado contendo binários, bibliotecas genéricas (como a glibc) e módulos de kernel pode facilmente ultrapassar 100 MB. Como esse arquivo precisa ser transferido do disco para a RAM pelo bootloader (um processo frequentemente lento em hardwares antigos) e ocupa memória valiosa irrecuperável até a execução do switch_root, a compressão é mandatória.

2.1. O Padrão gzip

O padrão histórico para a compressão do initramfs é o algoritmo Deflate, encapsulado no formato gzip (.cpio.gz). O código de descompressão do zlib é pequeno o suficiente para ser embutido com baixo impacto no kernel monolítico, oferecendo um excelente equilíbrio entre taxa de compressão e velocidade de extração. O magic number do gzip (0x1F 0x8B) é varrido nativamente pela rotina de extração.

2.2. A Evolução: lz4, xz e zstd

Com o aumento do tamanho das imagens (devido ao firmware de GPUs e módulos estendidos de rede), novos algoritmos foram integrados ao kernel:

  • XZ / LZMA: Oferece compressão máxima. Ideal para sistemas embarcados onde o armazenamento (Flash/EEPROM) é escasso, mas penaliza severamente o tempo de CPU (aumentando o tempo de boot).
  • LZ4: O extremo oposto do XZ. O lz4 possui uma taxa de compressão inferior ao gzip, mas sua velocidade de descompressão é vertiginosa, operando quase à velocidade limite da RAM. Em servidores modernos, substituir gzip por lz4 no initramfs pode cortar segundos do tempo de boot.
  • Zstandard (zstd): A solução definitiva atual. Desenvolvido no Facebook, o zstd oferece taxas de compressão comparáveis ao xz, com velocidades de descompressão próximas ao lz4. A maioria das distribuições modernas (Fedora, Arch, Ubuntu) migrou a geração do initramfs para zstd por padrão.

O kernel tenta identificar a assinatura de compressão lendo os primeiros bytes da imagem. Se a compilação do kernel possuir o módulo descompactador apropriado (ex: CONFIG_RD_ZSTD=y), ele transparentemente envia o fluxo comprimido para a biblioteca de descompressão antes de passar pelo parser do cpio.

3. Anatomia e Hierarquia Interna

Se você interromper a inicialização de um sistema ou descompactar manualmente a imagem do initramfs (via zcat initramfs.img | cpio -id), revelará uma estrutura de diretórios minimalista que mimetiza o FHS (Filesystem Hierarchy Standard), mas puramente focada na sobrevivência transitória.

3.1. O Binário Central: /init

O coração absoluto do arquivo é o executável localizado na raiz e nomeado estritamente como /init. O kernel, após extrair o arquivo cpio, tenta invocar este binário em espaço de usuário, outorgando-lhe o PID 1. Se este arquivo não existir (ou falhar devido a dependências compartilhadas ausentes, como a libc), o kernel invoca uma chamada de Panic (kernel panic - not syncing: No working init found). Este binário não é o systemd completo da máquina, mas frequentemente uma versão podada do mesmo, ou um shell script sequencial espesso.

3.2. A Estrutura de Diretórios Crítica

A árvore gerada tipicamente contém:

  • /bin, /sbin, /usr/bin: Contêm um conjunto mínimo de ferramentas. Para economizar espaço, em vez de incluir centenas de binários (como ls, mkdir, mount, modprobe, sh), ferramentas geradoras frequentemente incorporam o BusyBox. O BusyBox é um único executável estático que combina o funcionamento das utilidades core do UNIX; os comandos são apenas symlinks apontando para o /bin/busybox. Em implementações baseadas em systemd (via Dracut), as versões reais dos binários podem ser utilizadas, dependendo das configurações.
  • /lib, /lib64: Bibliotecas compartilhadas estritamente necessárias para a execução dos binários de /bin. A ferramenta ldd é usada indiretamente durante a criação do initramfs para puxar os arquivos .so exatos requeridos.
  • /lib/modules/$(uname -r)/: O núcleo do propósito do initramfs. Não contém todos os drivers do sistema, apenas o subconjunto selecionado na criação (controladoras SATA/AHCI, NVMe, USB, teclados, drivers de criptografia dm-crypt).
  • /etc: Configurações mínimas. Geralmente um fstab reduzido, configurações do LVM (lvm.conf), módulos que devem ser forçados a carregar (/etc/modules-load.d/), ou chaves SSH se o initramfs for configurado para aceitar desbloqueio remoto de partições via rede (usando dropbear).
  • /dev, /proc, /sys, /run: Diretórios vazios (pontos de montagem) que o /init povoará imediatamente antes de procurar o disco rígido raiz.

4. O CPIO Não Comprimido (Early Microcode)

Uma anomalia arquitetural vital introduzida em tempos recentes é o conceito de arquivos concatenados (Early Initramfs). Processadores x86 modernos (Intel e AMD) necessitam de atualizações de microcódigo (correções de falhas de silício, como Spectre e Meltdown). Essas correções precisam ser aplicadas à CPU nos primeiros milissegundos de operação do kernel, muito antes das rotinas de descompressão de memória (zlib, zstd) serem instanciadas pela MMU e pelos algoritmos de gerenciamento de memória inicial do Ring 0.

Como aplicar o microcódigo se o initramfs está comprimido?
A solução da arquitetura do Linux explora uma peculiaridade do utilitário cpio: o kernel suporta múltiplos arquivos cpio concatenados sequencialmente em um único binário (initramfs.img).

Estruturalmente, um initramfs moderno é composto por:

  1. Um pequeno arquivo CPIO em formato RAW (não comprimido) contendo exclusivamente arquivos sob o diretório /kernel/x86/microcode/. Como não está comprimido, o subsistema de boot inicial do kernel x86 pode varrer os bytes na memória física e aplicar a correção de CPU imediatamente.
  2. Seguido, no mesmo arquivo, pelo CPIO principal comprimido (gzip ou zstd), contendo a estrutura de pastas /bin, /lib, e o /init.

O kernel lê o fluxo de memória; quando encontra o final do primeiro arquivo cpio (sinalizado pelo registro mágico TRAILER!!!), ele verifica se os próximos bytes correspondem a uma assinatura de compressão e redireciona o fluxo restante para o descompressor dinâmico, criando um sistema empacotado bifásico de altíssima eficiência.