Fichier spécial qui provoque une erreur d'E / S

13

Je veux tester automatiquement si un logiciel réagit comme prévu si un fichier DB SQLite essentiel ne parvient pas à être lu (provoquant une erreur d'E / S). C'est exactement ce qui s'est passé il y a quelques jours chez un client. Nous l'avons corrigé manuellement mais maintenant je veux créer du code automatique pour le réparer et j'ai besoin d'accéder à un fichier cassé pour le tester.

Comme tout dans Unix est un fichier, je soupçonne qu'il pourrait y avoir un fichier spécial qui provoque toujours des erreurs d'E / S quand on essaie de le lire (par exemple dans / dev).

Certains fichiers similaires (imo) seraient:

  • /dev/full qui dit toujours "Il n'y a plus d'espace sur l'appareil" si vous essayez de l'écrire
  • /dev/null et /dev/zero

j'ai donc supposé qu'il devait juste y avoir un fichier comme ça (mais je n'en ai pas encore trouvé).

Est-ce que quelqu'un connaît un tel fichier ou toute autre méthode pour que j'obtienne le résultat souhaité (une image de partition intentionnellement défectueuse, un wrapper autour de open () en utilisant LD_PRELOAD, ...)?
Quelle est la meilleure façon d'aller ici?

mreithub
la source
Pour autant que je sache, il n'y a pas de fichier spécial sur Linux qui donne SIGIO lorsque vous lisez. La dernière fois que j'ai eu un SIGIO, c'était à cause d'une clé USB qui déclarait une capacité beaucoup plus grande que la vraie, physique. Peut-être que cela pourrait être une possibilité?
lgeorget
hmmm, je pourrais peut-être essayer ça avec une petite image de partition que je recadrerai quelque part au milieu ...
mreithub
SIGIO ne signifie pas qu'il y a eu une erreur, c'est un moyen pour un programme de demander à être informé que les entrées / sorties non bloquantes sont désormais possibles, au lieu d'appeler select () ou poll ().
psusi
Oui, vous avez raison, bien sûr. J'ai écrit SIGIO mais je pensais au code d'erreur EIO. Mais peut-être que l'OP aussi? Pourquoi un échec de lecture donnerait-il un SIGIO?
lgeorget
oh, j'ai fait la même erreur dans la question ... édité ...
mreithub

Réponses:

8

Vous pouvez utiliser dmsetuppour créer un périphérique de mappage de périphérique à l'aide des cibles errorou flakeypour simuler les échecs.

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

Où 123 est la longueur du périphérique, en secteurs et / dev / loop0 est le périphérique d'origine sur lequel vous souhaitez simuler les erreurs. Pour l'erreur, vous n'avez pas besoin des arguments suivants car il renvoie toujours une erreur.

psusi
la source
1
Je trouve au moins deux erreurs dans cette commande: le nom du périphérique manquant, la faute de frappe entre guillemets et que signifie "1 0 / dev / null"?
Hauke ​​Laging
@HaukeLaging, ahh, oui, j'ai oublié le nom et j'ai en quelque sorte mal cité. Le 1 0 / dev / null signifie 1 cible, en commençant à l'offset 0, soutenu par le périphérique / dev / null. Il est nécessaire pour flakey, mais est apparemment facultatif en cas d'erreur.
psusi
Il me semble que ce n'est pas "facultatif" mais simplement ignoré. Vous pouvez vérifier avec dmsetup table test. Vous pouvez même écrire foo barderrière error; cela ne fait rien (et devrait donc être supprimé).
Hauke ​​Laging
@HaukeLaging, édité.
psusi
Merci pour la réponse, je pense que c'est la voie que je vais suivre pour l'instant. Le seul problème mineur que j'ai avec cela est qu'il nécessite un accès root, mais je suppose que vous en aurez besoin de toute façon ou de ces trucs de bas niveau ... (je vais creuser dans l'idée LD_PRELOAD quand j'en aurai le temps).
mreithub
14

Il y a déjà un grand nombre de réponses à cela sur Stack Overflow et Server Fault mais certaines techniques manquaient. Pour vous simplifier la vie, voici une liste des mécanismes d'injection de défaut d'E / S de la machine virtuelle / périphérique de bloc Linux / système de fichiers Linux / bibliothèque d'espace utilisateur Linux:

Bonus: SQLite dispose d'un pilote VFS pour simuler les erreurs afin d'obtenir une bonne couverture de test.

En relation:

Anon
la source
5

Vous voulez un mécanisme d' injection de défaut pour les E / S.

Sous Linux, voici une méthode qui ne nécessite aucune configuration préalable et génère une erreur inhabituelle (pas EIO «Erreur d'entrée / sortie» mais ESRCH «Aucun processus de ce type»):

cat /proc/1234/mem

où 1234 est le PID d'un processus exécuté sous le même utilisateur que le processus que vous testez, mais pas ce processus lui-même. Crédits à rubasov pour penser de /proc/$pid/mem.

Si vous utilisez le PID du processus lui-même, vous obtenez EIO, mais uniquement si vous lisez à partir d'une zone qui n'est pas mappée dans la mémoire du processus. La première page n'est jamais mappée, donc c'est correct si vous lisez le fichier séquentiellement, mais ne convient pas à un processus de base de données qui cherche directement au milieu du fichier.

Avec un peu plus de configuration en tant que root, vous pouvez utiliser le mappeur de périphériques pour créer des fichiers avec des secteurs valides et des secteurs défectueux.

Une autre approche serait d'implémenter un petit système de fichiers FUSE . EIO est le code d'erreur par défaut lorsque votre pilote de système de fichiers de l'espace utilisateur fait quelque chose de mal, il est donc facile à réaliser. Les liaisons Perl et Python sont accompagnées d'exemples pour commencer, vous pouvez rapidement écrire un système de fichiers qui reflète principalement les fichiers existants mais injecte un EIO à des endroits soigneusement choisis. Il existe un tel système de fichiers: petardfs ( article ), je ne sais pas comment cela fonctionne hors de la boîte.

Pourtant, une autre méthode est un LD_PRELOADwrapper. Un système existant est Libfiu (injection de défauts dans l'espace utilisateur). Il fonctionne en préchargeant une bibliothèque qui surcharge les appels de l'API POSIX. Vous pouvez écrire des directives simples ou du code C arbitraire pour remplacer le comportement normal.

Gilles 'SO- arrête d'être méchant'
la source
Libfiu semble vraiment prometteur (et il se trouve dans les dépôts debian). Excellente réponse, merci, +1
mreithub
1

La solution est beaucoup plus simple si vous pouvez utiliser un fichier de périphérique comme "fichier avec des erreurs d'E / S". Ma proposition concerne les cas où un fichier ordinaire comportera de telles erreurs.

> 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

Je dois admettre que je suis un peu confus car je n'ai pas réussi à lire des secteurs uniques de ce fichier sans erreur (avec dd .. seek=...). C'est peut-être un problème de lecture anticipée.

Hauke ​​Laging
la source
Les blocs de votre système de fichiers ont une taille d'au moins 4096 octets, de sorte qu'ils s'étendent sur plusieurs secteurs même si le fichier est petit.
Anon