Ir para o conteúdo
AZ Tools

Inspetor de fluxos xz (.xz)

Um arquivo .xz é emoldurado, e é aí que está toda a diferença em relação ao gzip. Todo fluxo termina em um rodapé de 12 bytes cujo tamanho para trás aponta para um índice, e o índice guarda um registro por bloco com o tamanho sem preenchimento daquele bloco e o seu tamanho descomprimido. Ou seja, o tamanho original verdadeiro, a quantidade de blocos e todos os pontos de salto saem de algumas centenas de bytes no fim do arquivo: é por isso que o xz --list responde na hora sobre um arquivo de 4 GB, enquanto o gzip -l só consegue repetir os quatro últimos bytes de um .gz e torcer para que descrevam o conjunto. Esta página lê essa mesma moldura, percorrendo-a de trás para frente exatamente como o xz faz, e mostra tudo: os bytes mágicos fd 37 7a 58 5a 00, os sinalizadores do fluxo com o tipo de verificação (None, CRC32, CRC64 — o padrão — ou SHA-256) e o CRC32 que protege esses dois bytes; depois, por bloco, o tamanho do cabeçalho, se o cabeçalho declara os próprios tamanhos comprimido e descomprimido, o valor de verificação armazenado e a cadeia de filtros interpretada direito. O tamanho do dicionário do LZMA2 não é um número simples, e sim um código de seis bits numa escada que vai de 4 KiB a 4 GiB, e na frente dele pode entrar um filtro BCJ para x86, ARM, ARM64, PowerPC, IA-64, SPARC, ARM-Thumb ou RISC-V com o seu próprio deslocamento inicial; por isso um arquivo feito com --x86 tem dois filtros na cadeia, e aqui a cadeia é escrita do jeito que você a devolveria ao xz. Depois dos blocos vêm os registros do índice, o CRC32 do próprio índice e, no rodapé, o tamanho para trás, os sinalizadores repetidos e os bytes mágicos YZ. Nada é descomprimido. O LZMA2 não está implementado aqui e não precisa estar, que é justamente a ideia — mas isso também significa que o valor de verificação de cada bloco é copiado em vez de conferido, porque provar que um CRC64 está certo exigiria decodificar os dados. Já os quatro CRC32 que protegem a moldura são recalculados, e uma divergência é relatada em vez de recusada, de modo que um arquivo com o índice danificado, ou com um rodapé cujos sinalizadores discordam do cabeçalho, ainda mostra tudo o que está intacto. A contagem de blocos merece mais atenção do que parece: um único bloco não pode ser descomprimido em mais de um núcleo e ninguém consegue saltar para dentro dele, que é exatamente o que o xz -T0 muda ao cortar a entrada em blocos de cerca de três dicionários, então uma entrada menor que isso continua sendo um bloco só, por mais threads que você peça. Também podem existir vários fluxos concatenados no mesmo arquivo, separados por um preenchimento que precisa ser múltiplo de quatro bytes zero; eles são listados um a um, assim como os bytes finais que não pertencem a fluxo nenhum.

Como usar

  1. Solte um .xz ou um .tar.xz sobre a caixa. A leitura acontece dentro da página: nada é enviado e nada é descomprimido.
  2. Comece pelo resumo: fluxos, blocos, o tamanho descomprimido verdadeiro tirado do índice, a razão de compressão e qual verificação de integridade o arquivo usa.
  3. Abra um fluxo para ver o cabeçalho, a tabela de blocos e o índice. A coluna de filtros é escrita como você passaria ao xz, então --x86 --lzma2=dict=8MiB é a cadeia que gerou aquele bloco.
  4. Confira a contagem de blocos antes de planejar uma descompressão paralela ou uma leitura parcial: um bloco só significa um núcleo e nenhuma busca, por mais threads que o leitor tenha.
  5. Se o quadro de estrutura disser divergência, procure o selo vermelho: o cabeçalho do fluxo, cada cabeçalho de bloco, o índice e o rodapé têm cada um o seu CRC32, e o rodapé repete os sinalizadores do fluxo.

Perguntas frequentes

Como ele informa o tamanho descomprimido sem descomprimir nada?
Porque o formato .xz deixa esse número escrito. Cada fluxo termina com um índice que lista, para cada bloco, o tamanho sem preenchimento e o tamanho descomprimido, e o tamanho para trás do rodapé diz quanto antes esse índice começa. O leitor salta para os últimos 12 bytes, vai até o índice, soma os registros e acabou: é o que o xz --list faz e é o que esta página faz. É também o motivo de dar para saltar dentro de um .xz, já que o índice diz qual bloco contém um determinado deslocamento descomprimido. O gzip não tem nada equivalente: o ISIZE fica nos últimos quatro bytes, descreve só o último membro e é tomado módulo 2^32, então um .gz com vários membros ou com mais de 4 GB simplesmente não consegue declarar o seu tamanho a partir da moldura.
Por que meu arquivo tem um bloco só se eu comprimi com -T0?
Porque a compressão com threads divide a entrada em blocos de aproximadamente três vezes o tamanho do dicionário, e na predefinição 6 padrão o dicionário tem 8 MiB, então nada abaixo de uns 24 MiB chega a ser cortado em dois. O -T0 apenas pede tantas threads quantos forem os núcleos; ele não consegue inventar um segundo bloco a partir de uma entrada que cabe em um. Se você quer os blocos por si mesmos — descompressão em paralelo, ou um leitor que salte — peça-os explicitamente com --block-size ou --block-list. O preço é a razão de compressão: cada bloco começa com o dicionário vazio, então nenhuma repetição atravessa a fronteira entre blocos.
Qual tipo de verificação usar, e quanto custa?
Os sinalizadores do fluxo nomeiam um entre quatro: None, CRC32, CRC64 ou SHA-256, guardando 0, 4, 8 ou 32 bytes depois de cada bloco. O CRC64 é o padrão e quase sempre a resposta certa: custa poucos bytes por bloco e detecta o dano acidental que armazenamento e transferências realmente produzem. O SHA-256 custa 32 bytes por bloco e bem mais CPU, e vale ser claro sobre o que ele não compra: o resumo mora dentro do arquivo, então quem consegue alterar os dados também consegue alterá-lo. É detecção de acidente, não defesa contra um atacante; para isso é preciso uma assinatura sobre o arquivo inteiro. Já o --check=none economiza de quatro a oito bytes e remove a única coisa que teria avisado que os dados foram danificados, o que se defende quando o conteúdo já carrega a própria soma de verificação e raramente fora disso.
Qual a diferença entre tamanho sem preenchimento, tamanho total e tamanho comprimido?
São três vistas do mesmo bloco. O tamanho comprimido conta apenas o que o LZMA2 produziu. O tamanho sem preenchimento — o número que de fato vai para o índice — é o cabeçalho do bloco mais esses dados comprimidos mais o valor de verificação, sem contar o preenchimento. O tamanho total é esse valor arredondado para cima até um múltiplo de quatro, porque cada bloco é completado com bytes zero para que o próximo comece alinhado. Subtraindo, você obtém o preenchimento, de zero a três bytes. Esta página mostra o tamanho dos dados comprimidos e o preenchimento separadamente e confere os dois contra o cabeçalho do bloco sempre que o cabeçalho declara os próprios tamanhos, que é o que o c e o u da coluna de tamanhos indicam.
A página aponta uma divergência e o xz -t só diz que os dados estão corrompidos. Quem está certo?
Os dois, em profundidades diferentes. O xz -t decodifica todo o conteúdo e verifica o valor de verificação de cada bloco, então pega dano em qualquer lugar, mas para no primeiro erro e relata com a mesma frase seja qual for o problema. Esta página nunca decodifica o conteúdo, então não enxerga corrupção dentro dos dados comprimidos; em compensação, recalcula separadamente cada CRC32 da moldura e por isso consegue dizer qual estrutura está danificada: o cabeçalho do fluxo, o cabeçalho de um bloco específico, o índice ou o rodapé. Ela também compara os sinalizadores do fluxo no cabeçalho com a cópia do rodapé, uma divergência invisível para qualquer conferência baseada em tamanhos e um sinal genuíno de corrupção. Use esta página para localizar o dano e o xz -t para decidir se os dados ainda servem.
Dá para listar os arquivos dentro de um .tar.xz?
Não, e nenhum leitor consegue sem descomprimir. O arquivo tar — com os nomes, tamanhos e permissões — é o conteúdo, e esse conteúdo está comprimido com LZMA2 como uma sequência opaca de bytes. O que a moldura conta é quanto aquele tar vai ocupar quando sair, o que já diz se ele cabe antes de você começar; quantos blocos ele tem, o que diz se a extração pode usar mais de um núcleo; e se o contêiner está íntegro. Se o tamanho descomprimido for um múltiplo exato de 512, é um bom sinal de que realmente se trata de um tar, porque o tar completa cada membro e o arquivo inteiro até esse tamanho de bloco.
O que o tamanho do dicionário na cadeia de filtros realmente diz?
Até onde o compressor pôde olhar para trás em busca de uma repetição e, portanto, quanta memória o descompressor vai precisar aproximadamente: um decodificador reserva um buffer do tamanho do dicionário, então um .xz escrito com -9 (64 MiB) pede uns 65 MB para abrir numa máquina que talvez não os tenha, e essa é a razão habitual de uma descompressão falhar num sistema embarcado ou na integração contínua enquanto o mesmo arquivo abre sem problema num notebook. O LZMA2 guarda isso como um código de seis bits: o bit 0 escolhe uma mantissa de 2 ou 3 e o resto é o expoente, de forma que a escada vai 4 KiB, 6 KiB, 8 KiB, 12 KiB e assim por diante até 3 GiB, com o único valor 40 reservado para 4 GiB menos um. Lido como inteiro simples, o dicionário padrão de 8 MiB apareceria como 22.

Ferramentas relacionadas