File speciale che causa errori I / O


13

Voglio testare automaticamente se un software reagisce come previsto se un file DB SQLite essenziale non viene letto (causando un errore I / O). È successo esattamente qualche giorno fa ad un cliente. L'abbiamo risolto manualmente, ma ora voglio creare un codice automatico per risolverlo e ho bisogno di accedere a un file danneggiato per verificarlo.

Dato che tutto in Unix è un file, sospettavo che potesse esserci un file speciale che causa sempre errori I / O quando si cerca di leggerlo (ad esempio in / dev).

Alcuni file simili (imo) sarebbero:

  • /dev/full che dice sempre "Nessuno spazio lasciato sul dispositivo" se si tenta di scriverlo
  • /dev/null e /dev/zero

quindi ho pensato che ci fosse un file come quello (ma non ne ho ancora trovato uno).

Qualcuno conosce un tale file o qualsiasi altro metodo per ottenere il risultato desiderato (un'immagine di partizione intenzionalmente difettosa, un wrapper attorno ad open () usando LD_PRELOAD, ...)?
Qual è il modo migliore per andare qui?


Per quanto ne so, non esiste alcun file speciale su Linux che dia SIGIO quando si legge da esso. L'ultima volta che ho ricevuto un SIGIO è stato a causa di una chiavetta USB che ha dichiarato una capacità molto più grande di quella reale, fisica. Forse potrebbe essere una possibilità?
lgeorget,

hmmm, potrei essere in grado di provarlo con una piccola immagine di partizione che
ritaglierò

SIGIO non significa che si sia verificato un errore, è un modo in cui un programma può richiedere di essere avvisato che ora è possibile non bloccare IO, invece di chiamare select () o poll ().
psusi

Ups, sì, hai ragione, ovviamente. Ho scritto SIGIO ma pensavo al codice di errore EIO. Ma forse anche l'OP? Perché una mancata lettura darebbe un SIGIO?
lgeorget,

oh, ho fatto lo stesso errore nella domanda ... Modificato ...
mreithub,

Risposte:


8

È possibile utilizzare dmsetupper creare un dispositivo Device Mapper utilizzando le destinazioni erroro flakeyper simulare errori.

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

Dove 123 è la lunghezza del dispositivo, in settori e / dev / loop0 è il dispositivo originale su cui si desidera simulare gli errori. Per errore, non sono necessari gli argomenti successivi poiché restituisce sempre un errore.


1
Trovo almeno due errori in quel comando: il nome del dispositivo mancante, l'errore di battitura e cosa dovrebbe significare "1 0 / dev / null"?
Hauke ​​Laging,

@HaukeLaging, ahh, sì, ho lasciato fuori il nome e in qualche modo ho colpito la citazione sbagliata. 1 0 / dev / null significa 1 target, a partire dall'offset 0, supportato dal dispositivo / dev / null. È necessario per flakey, ma apparentemente è facoltativo per errore.
psusi

Mi sembra che non sia "opzionale" ma semplicemente ignorato. Puoi verificare con dmsetup table test. Puoi persino scrivere foo bardietro error; semplicemente non importa (e quindi dovrebbe essere eliminato).
Hauke ​​Laging,

@HaukeLaging, modificato.
psusi,

Grazie per la risposta, penso che sia il modo in cui andrò per ora. L'unico problema minore che ho con questo è che richiede l'accesso come root, ma immagino che ne avrai bisogno comunque o roba così di basso livello ... (Scavando nell'idea LD_PRELOAD quando avrò tempo).
mreithub,

14

C'è già un grande insieme di risposte a Stack Overflow e Server Fault, ma mancavano alcune tecniche. Per semplificare la vita ecco un elenco di meccanismi di blocco degli errori di I / O della libreria di spazio VM / Linux / filesystem Linux / Linux userspace:

Informazioni aggiuntive: SQLite ha un driver VFS per la simulazione degli errori in modo da ottenere una buona copertura dei test.

Relazionato:


5

Volete un meccanismo di iniezione dei guasti per l'I / O.

Su Linux, ecco un metodo che non richiede alcuna configurazione precedente e genera un errore insolito (non EIO "Errore di input / output" ma ESRCH "Nessun processo di questo tipo"):

cat /proc/1234/mem

dove 1234 è il PID di un processo in esecuzione come lo stesso utente del processo che si sta testando, ma non quel processo stesso. Crediti per rubasov per pensare di /proc/$pid/mem.

Se si utilizza il PID del processo stesso, si ottiene EIO, ma solo se si sta leggendo da un'area che non è mappata nella memoria del processo. La prima pagina non viene mai mappata, quindi va bene se leggi il file in sequenza, ma non è adatto per un processo di database che cerca direttamente nel mezzo del file.

Con un po 'più di configurazione come root, è possibile sfruttare il mapper del dispositivo per creare file con settori validi e settori danneggiati.

Un altro approccio sarebbe quello di implementare un piccolo filesystem FUSE . EIO è il codice di errore predefinito quando il driver del filesystem dello spazio utente fa qualcosa di sbagliato, quindi è facile da ottenere. Entrambe le associazioni Perl e Python vengono fornite con esempi per iniziare, puoi scrivere rapidamente un filesystem che rispecchia principalmente i file esistenti ma inietta un EIO in luoghi scelti con cura. Esiste un tale filesystem: petardfs ( articolo ), non so quanto funzioni bene.

Ancora un altro metodo è un LD_PRELOADwrapper. Uno esistente è Libfiu (iniezione di errori nello spazio utente). Funziona precaricando una libreria che sovraccarica le chiamate API POSIX. È possibile scrivere semplici direttive o codice C arbitrario per sovrascrivere il comportamento normale.


Libfiu sembra davvero promettente (ed è nei repository debian). Ottima risposta, grazie, +1
mreithub,

1

La soluzione è molto più semplice se è corretto utilizzare un file dispositivo come "file con errori I / O". La mia proposta riguarda quei casi in cui un file normale deve contenere tali errori.

> 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 ammettere che sono un po 'confuso perché non sono riuscito a leggere singoli settori da quel file senza un errore (con dd .. seek=...). Forse questo è un problema di lettura anticipata.


I blocchi del tuo filesystem hanno una dimensione di almeno 4096 byte, quindi si estenderanno su più settori anche se il file è piccolo.
Anon,

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.