Arquivo especial que causa erro de E / S

13

Desejo testar automaticamente se um software reage conforme o esperado, se um arquivo essencial do SQLite DB não for lido (causando um erro de E / S). Exatamente isso aconteceu alguns dias atrás em um cliente. Nós o corrigimos manualmente, mas agora quero criar um código automático para corrigi-lo e preciso acessar um arquivo quebrado para testá-lo.

Como tudo no Unix é um arquivo, suspeitei que possa haver um arquivo especial que sempre cause erros de E / S quando alguém tentar lê-lo (por exemplo, em / dev).

Alguns arquivos semelhantes (imo) seriam:

  • /dev/full que sempre diz "Não resta espaço no dispositivo" se você tentar escrevê-lo
  • /dev/null e /dev/zero

então eu assumi que só precisa haver um arquivo assim (mas ainda não o encontrei).

Alguém conhece um arquivo ou outro método para obter o resultado desejado (uma imagem de partição com defeito intencional, um invólucro em torno de open () usando LD_PRELOAD, ...)?
Qual é a melhor maneira de ir aqui?

mreithub
fonte
Até onde eu sei, não há nenhum arquivo especial no Linux que fornece o SIGIO quando você o lê. A última vez que comprei um SIGIO foi por causa de um pendrive USB que declarou uma capacidade muito maior que a real, física. Talvez isso possa ser uma possibilidade?
Lgeorget 29/05
hmmm, eu poderia ser capaz de experimentar essa imagem uma pequena partição com que eu vou cortar em algum lugar no meio ...
mreithub
SIGIO não significa que houve um erro, é uma maneira de um programa solicitar que seja notificado que agora não é possível bloquear IO, em vez de chamar select () ou poll ().
Psusi 29/05
Ups, sim, você está certo, é claro. Eu escrevi SIGIO, mas estava pensando no código de erro EIO. Mas talvez o OP também? Por que uma falha na leitura daria um SIGIO?
Lgeorget
oh, eu fiz o mesmo erro na questão ... Editado-lo ...
mreithub

Respostas:

8

Você pode usar dmsetuppara criar um dispositivo mapeador de dispositivos usando os destinos errorou flakeypara simular falhas.

dmsetup create test --table '0 123 flakey 1 0 /dev/loop0'

Onde 123 é o comprimento do dispositivo, em setores e / dev / loop0 é o dispositivo original no qual você deseja simular erros. Por erro, você não precisa dos argumentos subsequentes, pois sempre retorna um erro.

psusi
fonte
1
Encontro pelo menos dois erros nesse comando: o nome do dispositivo ausente, o erro de digitação entre aspas e o que "1 0 / dev / null" deve significar?
Hauke ​​Laging
@HaukeLaging, ahh, sim, deixei de fora o nome e, de alguma forma, acertei a citação errada. O 1 0 / dev / null significa 1 destino, começando no deslocamento 0, apoiado pelo dispositivo / dev / null. É necessário para o flakey, mas aparentemente é opcional para erro.
Psusi 29/05
Parece-me que não é "opcional", mas simplesmente ignorado. Você pode verificar com dmsetup table test. Você pode até escrever para foo bartrás error; simplesmente não se importa (e, portanto, deve ser excluído).
Hauke ​​Laging
@HaukeLaging, editado.
Psusi 29/05
Obrigado pela resposta, acho que é assim que irei por enquanto. O único pequeno problema que tenho com isso é que ele requer acesso root, mas acho que você precisará disso de qualquer maneira ou de coisas de baixo nível ... (vou explorar a ideia LD_PRELOAD quando tiver tempo).
Mreithub
14

Já existe um ótimo conjunto de respostas para isso no Stack Overflow e Server Fault, mas algumas técnicas estavam faltando. Para facilitar a vida, aqui está uma lista dos mecanismos de injeção de falha de E / S do dispositivo de bloco VM / Linux / sistema de arquivos Linux / biblioteca de espaço de usuário Linux:

Fato bônus: o SQLite possui um driver VFS para simular erros, para obter uma boa cobertura de teste.

Palavras-chave:

Anon
fonte
5

Você deseja um mecanismo de injeção de falha para E / S.

No Linux, aqui está um método que não requer nenhuma configuração anterior e gera um erro incomum (não EIO "Erro de entrada / saída", mas ESRCH "Não existe esse processo"):

cat /proc/1234/mem

onde 1234 é o PID de um processo em execução como o mesmo usuário que o processo que você está testando, mas não o processo em si. Créditos a rubasov por pensar em /proc/$pid/mem.

Se você usar o PID do próprio processo, obterá a EIO, mas apenas se estiver lendo de uma área que não está mapeada na memória do processo. A primeira página nunca é mapeada; portanto, tudo bem se você ler o arquivo sequencialmente, mas não for adequado para um processo de banco de dados que procura diretamente no meio do arquivo.

Com um pouco mais de configuração como raiz, você pode aproveitar o mapeador de dispositivos para criar arquivos com setores válidos e setores defeituosos.

Outra abordagem seria implementar um pequeno sistema de arquivos FUSE . EIO é o código de erro padrão quando o driver do sistema de arquivos do espaço do usuário faz algo errado, por isso é fácil de obter. As ligações Perl e Python vêm com exemplos para começar; você pode escrever rapidamente um sistema de arquivos que espelha principalmente os arquivos existentes, mas injeta uma EIO em locais cuidadosamente escolhidos. Existe um sistema de arquivos existente: petardfs ( artigo ), eu não sei o quão bem ele funciona imediatamente .

Ainda outro método é um LD_PRELOADinvólucro. Um existente é o Libfiu (injeção de falha no espaço do usuário). Ele funciona pré-carregando uma biblioteca que sobrecarrega as chamadas da API POSIX. Você pode escrever diretivas simples ou código C arbitrário para substituir o comportamento normal.

Gilles 'SO- parar de ser mau'
fonte
Libfiu parece realmente promissor (e está nos repositórios debian). Ótima resposta, obrigado, +1
mreithub 30/05
1

A solução é muito mais fácil se estiver OK usar um arquivo de dispositivo como "arquivo com erros de E / S". Minha proposta é para os casos em que um arquivo regular deve ter esses erros.

> dd if=/dev/zero of=/path/to/ext2.img bs=10M count=10
> losetup /dev/loop0 /path/to/ext2.img
> blockdev --getsz /dev/loop0
204800
> echo "0 204800 linear /dev/loop0 0" | dmsetup create sane_dev
> mke2fs /dev/mapper/sane_dev # ext2 reicht
> mount -t ext2 /dev/mapper/sane_dev /some/where
> dd if=/dev/zero of=/some/where/unreadable_file bs=512 count=4
> hdparm --fibmap /some/where/unreadable_file
/mnt/tmp/unreadable_file:
 filesystem blocksize 1024, begins at LBA 0; assuming 512 byte sectors.
 byte_offset  begin_LBA    end_LBA    sectors
           0       2050       2053          4
> umount /dev/mapper/sane_dev
> dmsetup remove sane_dev
> start_sector=$((204800-2053-1))
> echo $'0 2053 linear /dev/loop0 0\n2053 1 error\n2054 '"${start_sector} linear /dev/loop0 2054" | 
>   dmsetup create error_dev
> mount -t ext2 /dev/mapper/error_dev /some/where
> cat /some/where/unreadable_file # 3rd sector of file is unreadable
cat: /some/where/unreadable_file: Input/output error

Devo admitir que estou um pouco confuso porque não consegui ler setores únicos desse arquivo sem erro (com dd .. seek=...). Talvez esse seja um problema de leitura antecipada.

Hauke ​​Laging
fonte
Os blocos do seu sistema de arquivos têm pelo menos 4096 bytes de tamanho e, portanto, abrangem vários setores, mesmo que o arquivo seja pequeno.
Anon