Spezielle Datei, die einen E / A-Fehler verursacht

13

Ich möchte automatisch testen, ob eine Software wie erwartet reagiert, wenn eine wichtige SQLite-DB-Datei nicht gelesen werden kann (was zu einem E / A-Fehler führt). Genau das ist vor einigen Tagen bei einem Kunden passiert. Wir haben es manuell behoben, aber jetzt möchte ich automatischen Code erstellen, um es zu beheben, und brauche Zugriff auf eine beschädigte Datei, um dies zu testen.

Da alles in Unix eine Datei ist, vermutete ich, dass es eine spezielle Datei geben könnte, die immer I / O-Fehler verursacht, wenn man versucht, sie zu lesen (zB in / dev).

Einige ähnliche Dateien (imo) wären:

  • /dev/full die immer "No space left on device" sagt, wenn Sie versuchen, es zu schreiben
  • /dev/null und /dev/zero

Ich nahm also an, dass es nur eine solche Datei geben muss (aber noch keine gefunden hat).

Kennt jemand eine solche Datei oder eine andere Methode, mit der ich das gewünschte Ergebnis erzielen kann (ein absichtlich fehlerhaftes Partitionsimage, ein Wrapper um open () mit LD_PRELOAD, ...)?
Was ist der beste Weg, um hierher zu kommen?

mreithub
quelle
Soweit ich weiß, gibt es unter Linux keine spezielle Datei, die SIGIO anzeigt, wenn Sie davon lesen. Das letzte Mal, dass ich ein SIGIO bekam, war wegen eines USB-Sticks, der eine Kapazität deklarierte, die viel größer ist als die reale, physische. Vielleicht könnte das eine Möglichkeit sein?
29.
hmmm, ich könnte das mit einem kleinen Partitions-Image versuchen, das ich irgendwo in der Mitte
zuschneiden
SIGIO bedeutet nicht, dass ein Fehler aufgetreten ist. Auf diese Weise kann ein Programm die Benachrichtigung anfordern, dass nicht blockierende E / A-Vorgänge jetzt möglich sind, anstatt select () oder poll () aufzurufen.
Psusi
Ups, ja, du hast natürlich recht. Ich schrieb SIGIO, dachte aber an den EIO-Fehlercode. Aber vielleicht auch das OP? Warum würde ein Fehler beim Lesen ein SIGIO ergeben?
29.
oh, ich habe den gleichen Fehler in der Frage gemacht ... Bearbeitet ...
mreithub

Antworten:

8

Mit können Sie dmsetupein Device-Mapper-Gerät erstellen, indem Sie entweder die Ziele erroroder flakeyverwenden, um Fehler zu simulieren.

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

Wobei 123 die Länge des Geräts ist und / dev / loop0 das ursprüngliche Gerät ist, auf dem Sie Fehler simulieren möchten. Für einen Fehler benötigen Sie die nachfolgenden Argumente nicht, da immer ein Fehler zurückgegeben wird.

Psusi
quelle
1
Ich finde mindestens zwei Fehler in diesem Befehl: Den fehlenden Gerätenamen, den Tippfehler und was soll "1 0 / dev / null" bedeuten?
Hauke ​​Laging
@HaukeLaging, ahh, ja, ich habe den Namen ausgelassen und irgendwie das falsche Zitat getroffen. 1 0 / dev / null bedeutet 1 Ziel, beginnend mit Offset 0, gesichert durch device / dev / null. Es wird für Flakey benötigt, ist aber anscheinend für Fehler optional.
Psusi
Es scheint mir, dass es nicht "optional" ist, sondern einfach ignoriert wird. Sie können mit überprüfen dmsetup table test. Sie können sogar foo bardahinter schreiben error; es ist einfach egal (und sollte daher gelöscht werden).
Hauke ​​Laging
@HaukeLaging, bearbeitet.
Psusi
Danke für die Antwort, ich denke, das ist der Weg, den ich jetzt gehen werde. Das einzige kleine Problem, das ich dabei habe, ist, dass Root-Zugriff erforderlich ist, aber ich vermute, dass Sie das sowieso oder solche Sachen mit niedrigem Level benötigen ... (Ich werde mich mit der Idee von LD_PRELOAD befassen, wenn ich Zeit habe).
mreithub
14

Bei Stack Overflow und Server Fault gibt es bereits eine Reihe von Antworten darauf, aber es fehlten einige Techniken. Um Ihnen das Leben zu erleichtern, finden Sie hier eine Liste der I / O-Fehlerinjektionsmechanismen für VM / Linux-Blockgeräte / Linux-Dateisysteme / Linux-Userspace-Bibliotheken:

Vorteil: SQLite verfügt über einen VFS-Treiber zum Simulieren von Fehlern , um eine gute Testabdeckung zu erzielen.

Verbunden:

Anon
quelle
5

Sie möchten einen Fehlerinjektionsmechanismus für E / A.

Unter Linux erfordert die folgende Methode keine vorherige Einrichtung und generiert einen ungewöhnlichen Fehler (nicht EIO „Eingabe- / Ausgabefehler“, sondern ESRCH „Kein solcher Prozess“):

cat /proc/1234/mem

Dabei ist 1234 die PID eines Prozesses, der als derselbe Benutzer wie der zu testende Prozess ausgeführt wird, nicht jedoch dieser Prozess. Credits rubasov für das Denken an /proc/$pid/mem.

Wenn Sie die PID des Prozesses selbst verwenden, erhalten Sie EIO, aber nur, wenn Sie aus einem Bereich lesen, der nicht im Speicher des Prozesses zugeordnet ist. Die erste Seite wird nie zugeordnet, daher ist es in Ordnung, wenn Sie die Datei nacheinander lesen, jedoch nicht für einen Datenbankprozess geeignet, der direkt in die Mitte der Datei sucht.

Mit etwas mehr Setup als root können Sie den Device Mapper nutzen , um Dateien mit gültigen Sektoren und fehlerhaften Sektoren zu erstellen.

Ein anderer Ansatz wäre die Implementierung eines kleinen FUSE- Dateisystems. EIO ist der Standardfehlercode, wenn Ihr Userspace-Dateisystemtreiber etwas falsch macht. Sowohl die Perl- als auch die Python- Bindung enthalten Beispiele für den Einstieg. Sie können schnell ein Dateisystem schreiben, das zumeist vorhandene Dateien spiegelt, aber an sorgfältig ausgewählten Stellen eine EIO einfügt. Es gibt ein solches Dateisystem: petardfs ( Artikel ). Ich weiß nicht, wie es sofort funktioniert.

Noch eine andere Methode ist ein LD_PRELOADWrapper. Eine existierende ist Libfiu (Fehlerinjektion im Userspace). Hierzu wird eine Bibliothek vorab geladen, die die POSIX-API-Aufrufe überlastet. Sie können einfache Anweisungen oder beliebigen C-Code schreiben, um das normale Verhalten zu überschreiben.

Gilles 'SO - hör auf böse zu sein'
quelle
Libfiu sieht wirklich vielversprechend aus (und es ist in den Debian-Repos). Tolle Antwort, danke, +1
mreithub
1

Die Lösung ist viel einfacher, wenn es in Ordnung ist, eine Gerätedatei als "Datei mit E / A-Fehlern" zu verwenden. Mein Vorschlag gilt für die Fälle, in denen eine reguläre Datei solche Fehler aufweisen soll.

> 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

Ich muss zugeben, dass ich ein bisschen verwirrt bin, weil ich es nicht geschafft habe, einzelne Sektoren aus dieser Datei fehlerfrei (mit dd .. seek=...) zu lesen . Vielleicht ist das ein Vorausleseproblem.

Hauke ​​Laging
quelle
Die Blöcke Ihres Dateisystems haben eine Größe von mindestens 4096 Byte, sodass sie sich über mehrere Sektoren erstrecken, selbst wenn die Datei klein ist.
Anon