terça-feira, 3 de junho de 2025

LFS construído dentro de arquivo e destinado para pendrive bootável

Este texto descreve as etapas para se criar um armazenamento em arquivo e usá-lo como mídia para construir um sistema operacional completo Linux From Scratch (LFS).


Resumo:

Etapa um: criar arquivos a serem usados como armazenamento

Etapa dois: construir o sistema operacional a partir dos pacotes de códigos fonte

Etapa três: copiar o arquivo para uma unidade USB flash


Etapa um: criar arquivos a serem usados como armazenamento

Objetivo: criar dois arquivos, um provisório e outro final, para servirem de armazenamento. O arquivo provisório será onde o sistema operacional LFS inteiro será construído (tamanho mínimo 10 GB). O arquivo final será menor (cerca de 3 GB), para acomodar o sistema com os programas e as bibliotecas já despojados dos símbolos de depuração. Se a intenção for a de produzir uma unidade inicializável maior que 10 GB, então pode-se criar somente o arquivo final (o que exigirá uma unidade USB flash com capacidade de armazenamento maior que 10 GB).

Existem dois métodos: um cria um "arquivo contíguo" e o outro cria um "arquivo esparso". Um deles deverá ser escolhido. Somente um dos comandos abaixo deverá ser escolhido e executado!

Método um (lento): criando um arquivo contíguo

dd if=/dev/zero of=disco-provisorio.img bs=4096B count=3000000 status=progress

A saída gerada pelo comando acima deverá ser semelhante a esta:

12255150080 bytes (12 GB, 11 GiB) copied, 147 s, 83,4 MB/s
3000000+0 records in
3000000+0 records out
12288000000 bytes (12 GB, 11 GiB) copied, 151,031 s, 81,4 MB/s


dd if=/dev/zero of=disco-virtual.img bs=100MB count=30 status=progress

A saída gerada pelo comando acima deverá ser semelhante a esta:

3000000000 bytes (3,0 GB, 2,8 GiB) copied, 27 s, 111 MB/s
30+0 records in
30+0 records out
3000000000 bytes (3,0 GB, 2,8 GiB) copied, 27,0483 s, 111 MB/s


Método dois (rápido): criando um arquivo esparso

fallocate --lenght 12GB disco-provisorio.img

fallocate --lenght 3GB disco-virtual.img

truncate --size=12GB disco-provisorio.img

truncate --size=3GB disco-virtual.img

OBSERVAÇÃO IMPORTANTÍSSIMA: É PROIBIDO (PELO NÚCLEO Linux) USAR ARQUIVOS "ESPARSOS" (ARQUIVOS CUJOS TAMANHO DE BLOCO SEJA IGUAL A ZERO) COMO ÁREA DE TROCA (ARQUIVO DE SWAP).

Resultado: Criado um arquivo ("disco-provisorio.img") cuja capacidade de armazenamento é de 12 GB e um arquivo ("disco-virtual.img") cuja capacidade de armazenamento é de 3 GB.

Próximo passo é o de se criar o sistema de arquivos dentro desses dois arquivos. Anexe esse arquivo ao sistema de arquivos do sistema operacional em execução (o comando abaixo exige privilégios de administração do sistema e só pode ser executado como "root" ou via "sudo"):

losetup --partscan --show --verbose --find disco-provisorio.img
/dev/loop0

losetup --list
NAME       SIZELIMIT OFFSET AUTOCLEAR RO BACK-FILE   DIO LOG-SEC
/dev/loop0         0      0         0  0 disco-provisorio.img   0     512

Com os arquivos (contíguos ou esparsos) criados, agora é a hora de se criar o sistema de arquivos dentro de "disco-provisorio.img" (ainda como "root" ou via "sudo"):

mkfs.ext4 -L prov /dev/loop0

OBSERVAÇÃO IMPORTANTÍSSIMA: O ARQUIVO "disco-provisorio.img" COMO FOI FORMATADO CONSEGUE ABRIGAR A CONSTRUÇÃO DO SISTEMA OPERACIONAL LFS, PORÉM É IMPOSSÍVEL SE INSTALAR O CARREGADOR DE INICIALIZAÇÃO "GRUB" NELE, POIS LHE FALTAM AS PARTIÇÕES ESPECIALIZADAS PARA TAL FINALIDADE. TENTAR INSTALAR O GRUB (POR PADRÃO NO DIRETÓRIO /boot) GERARÁ UM ERRO DE AUSÊNCIA DE SUPORTE A "EMBUTIMENTO" NO SISTEMA DE ARQUIVOS, UMA REFERÊNCIA À ÁREA ENTRE O REGISTRO MESTRE DE INICIALIZAÇÃO E A PRIMEIRA PARTIÇÃO.

Desanexe "disco-provisorio.img"

losetup --detach /dev/loop0


Anexe o arquivo "disco-virtual.img" ao sistema de arquivos do sistema operacional em execução (o comando abaixo exige privilégios de administração do sistema e só pode ser executado como "root" ou via "sudo"):

losetup --partscan --show --verbose --find disco-virtual.img
/dev/loop0

Agora é hora de acessá-lo e de criar as partições especializadas a serem usadas para se instalar o carregador de inicialização e o sistema de arquivos do sistema operacional.

fdisk /dev/loop0

dentro do ambiente interativo do programa "fdisk", teclar "p" (opcional; apenas para imprimir na tela o esquema atual):

Comando (m para ajuda): p
Disco /dev/loop0: 2,79 GiB, 3000000000 bytes, 5859375 setores
Unidades: setor de 1 * 512 = 512 bytes
Tamanho de setor (lógico/físico): 512 bytes / 512 bytes
Tamanho E/S (mínimo/ótimo): 512 bytes / 512 bytes
Tipo de rótulo do disco: dos
Identificador do disco: 0xfe2bdb0d

Comando (m para ajuda):


Teclar "g" para criar um "rótulo de disco" do tipo GPT.

Comando (m para ajuda): g
Criado um novo rótulo de disco GPT (GUID: FAD0BC7D-63C9-C84E-80BD-4070028A9B9E).


Teclar "p" (opcional; apenas para confirmar que o esquema de partições foi criado conforme solicitado).

Comando (m para ajuda): p
Disco /dev/loop0: 2,79 GiB, 3000000000 bytes, 5859375 setores
Unidades: setor de 1 * 512 = 512 bytes
Tamanho de setor (lógico/físico): 512 bytes / 512 bytes
Tamanho E/S (mínimo/ótimo): 512 bytes / 512 bytes
Tipo de rótulo do disco: gpt
Identificador do disco: FAD0BC7D-63C9-C84E-80BD-4070028A9B9E

Comando (m para ajuda):

Observe que o "tipo de rótulo do disco" antes era "dos" e atualmente é "gpt", confirmando a criação (na verdade mudança) do esquema de partições.

Para criar efetivamente as partições no armazenamento, teclar "n".

Comando (m para ajuda): n
Número da partição (1-128, padrão 1):

O programa te solicitará que informe o número da partição, bastando teclar "enter" para aceitar o valor sugerido.

A seguir, o programa solicitará que você informe qual será o primeiro setor (início) da partição.

Primeiro setor (2048-5859341, padrão 2048):

Novamente, basta teclar "enter" e aceitar o valor sugerido. O próximo valor solicitado será o último setor (o final) da partição.

Último setor, +/-setores ou +/-tamanho{K,M,G,T,P} (2048-5859341, padrão 5859327):

Digitar "+5MB" e teclar "enter" para criar uma partição de tamanho cinco (05) mega bytes.

Criada uma nova partição 1 do tipo "Linux filesystem" e de tamanho 5 MiB.

Observação: nessa primeira partição será instalado o estágio "1,5" do GRUB para BIOS. Esse programa, o GRUB, é um carregador de inicialização e exige uma partição de tamanho mínimo de 1 mega byte.

Teclar "p" (opcional; apenas para confirmar que a partição foi criada conforme solicitado).

Comando (m para ajuda): p
Disco /dev/loop0: 2,79 GiB, 3000000000 bytes, 5859375 setores
Unidades: setor de 1 * 512 = 512 bytes
Tamanho de setor (lógico/físico): 512 bytes / 512 bytes
Tamanho E/S (mínimo/ótimo): 512 bytes / 512 bytes
Tipo de rótulo do disco: gpt
Identificador do disco: FAD0BC7D-63C9-C84E-80BD-4070028A9B9E

Dispositivo  Início   Fim Setores Tamanho Tipo
/dev/loop0p1   2048 12287   10240      5M Linux sistema de arquivos

Observação: por pré definição, o tipo da partição será "Linux". Precisamos mudar isso! Teclar "m" e depois "enter" mostrará o menu do programa. A opção para se mudar o tipo da partição é a "t". Tecle "t" e depois "enter" para mudar o tipo da partição. Informe o novo tipo (se não souber qual valor informar, então digite "L" (maiúsculo) para exibir todos os tipos conhecidos pelo programa "fdisk"). O tipo desejado é o "4" (BIOS boot). Teclar "q" para sair da exibição da lista. Teclar "4" e depois "enter" para selecionar o novo tipo, ou seja, "BIOS boot". O programa exibirá uma mensagem informativa, mas sempre é possível teclar-se "p" para imprimir na tela como está atualmente a configuração. Confirme na sexta coluna (rotulada de "Tipo") o tipo atual da partição, que deverá ser "BIOS inicialização". Sugere-se agora teclar "w" e depois "enter" para escrever as mudanças no armazenamento. Como efeito colateral da ação de salvar as mudanças, automaticamente o ambiente interativo será terminado. Sem problemas! Bastar acessar novamente o ambiente interativo do programa.

Atualmente, espera-se que exista somente uma partição, de tamanho 5MB, do tipo "BIOS inicialização". Essa partição servirá para se instalar o carregador de inicialização GRUB destinado especificamente para máquinas BIOS. Para máquinas EFI, será criada outra partição destinada para se instalar o GRUB adequado para tal método de inicialização de sistema operacional. É isso o que agora será feito.

Pressionar e manter pressionada a tecla "Ctrl" enquanto pressiona a tecla "l" "limpa" a tela para melhor visualização. Teclar "n" e depois "enter" para criar uma partição nova. É o mesmo procedimento usado para criar a partição 1. O programa solicitará que seja informado um número para a partição, bastando teclar "enter" para aceitar o valor sugerido. A seguir, será solicitado que seja informado o início da partição (primeiro setor). Novamente, teclar "enter" para aceitar o valor automaticamente sugerido pelo programa. Por último, será solicitado o valor que representa o final da partição (o último setor). Sugere-se informar o tamanho "+50MB" para que seja criada uma partição de tamanho 50 mega bytes. O programa então informará que foi criada uma segunda partição, do tipo "Linux filesystem" (Linux sistema de arquivos) e de tamanho de 48 mibibytes. Pressione e mantenha pressionada a tecla "Ctrl" e tecle "l" para "limpar" a tela e melhorar a visualização e em seguida tecla "p" mais uma vez para imprimir na tela o estado atual do esquema de partições. Mais uma vez a partição 2 deverá ser mudada para ajustar o tipo para o tipo correto destinado a se instalar o GRUB para máquinas EFI. Teclar "t" e depois "enter" para mudar o tipo da partição. O programa informará que a partição 2 já está selecionada para mudanças. Basta teclar "enter" para prosseguir. Agora o valor a ser informado deverá ser aquele que corresponde a partições especializadas para inicializar máquinas EFI. Teclar "L" (maiúsculo) listará todos os tipos conhecidos. O valor "1" deverá ser o valor informado. Teclar "q" para sair da lista. Teclar "1" e depois "enter". O programa informará que o tipo da partição foi mudado de "Linux filesystem" para "EFI System". "Limpe" a tela ("Ctrl" + l) e imprima na tela o estado atual da configuração teclando "p". Confirme se a sexta coluna mostra o tipo da partição como "Sistema EFI". Tecle "w" e depois "enter" para escrever as mudanças. Acesse mais uma vez o ambiente interativo do programa "fdisk". Tecla "p" mais uma vez. Finalmente, a última partição será criada teclando-se "n", depois "enter", depois "enter" novamente para se aceitar o número sugerido para essa nova partição, "enter" para aceitar a sugestão de valor para o início (primeiro setor) da partição e "enter" para aceitar a sugestão de final (último setor) da partição. "Limpe" a tela (com "Ctrl" + "l") para melhorar a visualização e tecle "p" para imprimir na tela o estado atual da configuração. No final, o esquema de partições deverá ser semelhante a este (exceto para o nome do disco "teste3" que deverá ser "disco-virtual.img"):

Comando (m para ajuda): p
Disco /dev/loop0: 2,79 GiB, 3000000000 bytes, 5859375 setores
Unidades: setor de 1 * 512 = 512 bytes
Tamanho de setor (lógico/físico): 512 bytes / 512 bytes
Tamanho E/S (mínimo/ótimo): 512 bytes / 512 bytes
Tipo de rótulo do disco: gpt
Identificador do disco: FAD0BC7D-63C9-C84E-80BD-4070028A9B9E

Dispositivo  Início     Fim Setores Tamanho Tipo
/dev/loop0p1   2048   12287   10240      5M BIOS inicialização
/dev/loop0p2  12288  110591   98304     48M Sistema EFI
/dev/loop0p3 110592 5859327 5748736    2,7G Linux sistema de arquivos

Teclar "w" para escrever as mudanças no arquivo de armazenamento e sair do ambiente interativo do programa "fdisk".

Desanexe "disco-virtual.img" do sistema de arquivos:

losetup --list
NAME       SIZELIMIT OFFSET AUTOCLEAR RO BACK-FILE              DIO LOG-SEC
/dev/loop0         0      0         0  0 disco-virtual.img   0     512

losetup --detach-all

losetup --list


Etapa dois: construir o sistema operacional a partir dos pacotes de códigos fonte

Esta etapa deverá seguir rigorosamente as instruções contidas no livro do projeto LFS (https://www.br.linuxfromscratch.org/), exceto que a mídia usada para armazenagem do sistema de arquivos será "loop0p3" (a partição de 12 GB) criada na etapa um.

Observação: conforme está escrito no livro, "2.4. Criando uma Nova Partição", o sistema operacional LFS completo e mínimo só pode ser construído dentro de uma partição de pelo menos 10 GB de tamanho, embora o sistema final ocupe cerca de 5 GB antes de se despojar os executáveis e as bibliotecas dos símbolos de depuração. Após o despojamento dos símbolos de depuração, o sistema operacional LFS inteiro ocupa cerca de 2,9 GB de espaço de armazenagem.

Em "2.5. Criando um Sistema de Arquivos na Partição", a partição a ser escolhida como mídia de armazenagem para o sistema de arquivos será "loop0p3" (adaptar conforme a saída gerada pelos programas "losetup --list" e "lsblk").

Anexe os dois arquivos ("disco-provisorio.img" e "disco-virtual.img") ao sistema de arquivos do sistema operacional atualmente em execução (como "root" ou via "sudo"):

losetup --partscan --show --verbose --find disco-provisorio.img
/dev/loop0

losetup --partscan --show --verbose --find disco-virtual.img
/dev/loop1

OBSERVAÇÃO IMPORTANTE: ATENTE-SE PARA QUAL DISPOSITIVO DE CICLO ("loop") FOI ATRIBUÍDO A QUAL ARQUIVO. SEMPRE É POSSÍVEL INVOCAR "losetup --list" PARA SE LISTAR OS DISPOSITIVOS DE CICLO VINCULADOS. SUGERE-SE ANEXAR NA ORDEM MOSTRADA.

losetup --list
NAME       SIZELIMIT OFFSET AUTOCLEAR RO BACK-FILE                 DIO LOG-SEC
/dev/loop1         0      0         0  0 /tmp/disco-virtual.img      0     512
/dev/loop0         0      0         0  0 /tmp/disco-provisorio.img   0     512

Crie o sistema de arquivos dentro do arquivo "disco-provisorio.img":

mkfs.ext4 -L prov /dev/loop0

A saída gerada pelo comando acima deverá ser semelhante a esta:

mke2fs 1.47.0 (5-Feb-2023)
Discarding device blocks: done                            
Creating filesystem with 3000000 4k blocks and 750720 inodes
Filesystem UUID: 7f7c2aef-da6a-4462-b84d-e2d472b72e33
Superblock backups stored on blocks:
    32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208

Allocating group tables: done                            
Writing inode tables: done                            
Creating journal (16384 blocks): done
Writing superblocks and filesystem accounting information: done


Crie o sistema de arquivos dentro do arquivo "disco-virtual.img":

mkfs.ext4 -L lfs /dev/loop1p3

A saída gerada pelo comando acima deverá ser semelhante a esta:

mke2fs 1.47.0 (5-Feb-2023)
Discarding device blocks: done                            
Creating filesystem with 718592 4k blocks and 179872 inodes
Filesystem UUID: 03e56950-95c7-4119-8f65-9addf10bd8e4
Superblock backups stored on blocks:
    32768, 98304, 163840, 229376, 294912

Allocating group tables: done                            
Writing inode tables: done                            
Creating journal (16384 blocks): done
Writing superblocks and filesystem accounting information: done


Etapa três: copiar o arquivo para uma unidade USB flash

Neste ponto existe uma pequena armadilha: a partição existente dentro do arquivo provisório de armazenagem ("disco-provisorio.img") tem o tamanho de 12 GB, enquanto que o sistema operacional LFS inteiro mínimo já despojado dos símbolos de depuração (isso é crucial!) ocupa, como já dito, cerca de 2,9 GB de espaço de armazenamento. Possivelmente (foi o meu caso) a unidade USB ("pendrive") não tenha 12 GB de espaço para armazenar a partição atualmente existente dentro do arquivo de armazenagem. Portanto, é preciso copiar toda a árvore do sistema de arquivos (atualmente residente no arquivo "disco-provisorio.img", 12 GB) para um arquivo menor ("disco-virtual.img", 3 GB) e preparado para essa finalidade.

Mais uma vez, confirme se os dois arquivos estão vinculados aos respectivos dispositivos de ciclo:

losetup --list
NAME       SIZELIMIT OFFSET AUTOCLEAR RO BACK-FILE                 DIO LOG-SEC
/dev/loop1         0      0         0  0 /tmp/disco-virtual.img      0     512
/dev/loop0         0      0         0  0 /tmp/disco-provisorio.img   0     512

Com os arquivos já vinculados, monte os dois sistemas de arquivos existentes dentro desses dois arquivos:

mount -v /dev/loop0 /mnt/origem/
mount: /dev/loop0 montada em /mnt/origem.
 
mount -v /dev/loop1p3 /mnt/destino/
mount: /dev/loop1p3 montada em /mnt/destino.

OBSERVAÇÃO: caso se disponha de uma unidade USB flash capaz de armazenar mais que 12 GB, então os próximos passos são opcionais.

Crie um arquivamento provisório para copiar todo o sistema de arquivos do arquivo maior ("disco-provisorio.img"):

cd /mnt/origem/
tar --create --file=/tmp/arquivos.tar --wildcards .

ATENTE-SE PARA O CARACTERE PONTO (".") AO FINAL DO COMANDO. O CARACTERE COMPÕE O COMANDO.


Extraia todos os arquivos dentro do arquivo menor ("disco-virtual.img"):

cd /mnt/destino/
tar --extract --file=/tmp/arquivos.tar

Plugue a unidade USB na entrada USB da máquina. Aguarde alguns segundos para o núcleo Linux identificar as mídias de armazenamento existentes nele (se existirem).

Execute o programa "lsblk" para listar todos os dispositivos de bloco anexados ao sistema operacional em execução.

Identifique qual dispositivo de bloco corresponde à tua unidade USB (geralmente "/dev/sdb").

Para gerar a unidade USB flash inicializável ("pendrive bootável"), executar este comando (como "root" ou via "sudo"):

dd if=disco-virtual.img of=/dev/sdb bs=4M status=progress


Agradecimentos:

Fernando Ferreira Silva, pelo vídeo que inspirou todo esse trabalho: https://youtu.be/j_AxE1_Rl7U.

Paulo Kretcheu, pela palestra ministrada em 2020. https://youtu.be/eZtK1cFHvcQ.