Специальный файл, вызывающий ошибку ввода / вывода

13

Я хочу автоматически проверить, реагирует ли программа должным образом, если не удается прочитать необходимый файл БД SQLite (что приводит к ошибке ввода-вывода). Именно это произошло несколько дней назад у клиента. Мы исправили это вручную, но теперь я хочу создать автоматический код, чтобы исправить это, и мне нужен доступ к поврежденному файлу, чтобы проверить это.

Поскольку все в Unix - это файл, я подозревал, что может существовать специальный файл, который всегда вызывает ошибки ввода-вывода, когда кто-то пытается его прочитать (например, в / dev).

Некоторые похожие файлы (IMO) будут:

  • /dev/full который всегда говорит "Нет свободного места на устройстве", если вы пытаетесь написать его
  • /dev/null и /dev/zero

поэтому я предположил, что просто должен быть такой файл (но пока не нашел).

Кто-нибудь знает такой файл или любой другой метод для меня, чтобы получить желаемый результат (намеренно неисправный образ раздела, обертка вокруг open () с использованием LD_PRELOAD, ...)?
Какой лучший способ пойти сюда?

mreithub
источник
Насколько я знаю, в Linux нет специального файла, который выдает SIGIO, когда вы читаете из него. В прошлый раз я получил SIGIO из-за USB-флешки, емкость которой была намного выше реальной, физической. Может быть, это может быть возможность?
lgeorget
хм, я мог бы попробовать это с небольшим разделением изображения, которое я
обрежу
SIGIO не означает, что произошла ошибка, это способ, которым программа может запросить уведомление о том, что теперь возможен неблокирующий ввод-вывод, вместо вызова select () или poll ().
psusi
Упс, да, ты прав, конечно. Я написал SIGIO, но думал о коде ошибки EIO. Но, может быть, ОП тоже? Почему отказ от чтения дает SIGIO?
lgeorget
о, я сделал ту же ошибку в вопросе ... отредактировал его ...
mreithub

Ответы:

8

Вы можете использовать dmsetupдля создания устройства сопоставления устройств, используя цели errorили flakeyдля имитации сбоев.

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

Где 123 - длина устройства, в секторах, а / dev / loop0 - исходное устройство, на котором вы хотите симулировать ошибки. Для ошибки вам не нужны последующие аргументы, поскольку она всегда возвращает ошибку.

psusi
источник
1
В этой команде я обнаружил как минимум две ошибки: отсутствующее имя устройства, опечатка в кавычках и что должен означать «1 0 / dev / null»?
Хауке Лагинг
@HaukeLaging, ааа, да, я пропустил имя и как-то ошибся цитатой. Значение 1 0 / dev / null означает 1 цель, начиная со смещения 0 и поддержанную устройством / dev / null. Это необходимо для flakey, но, по-видимому, необязательно для ошибки.
psusi
Мне кажется, что это не «необязательно», а просто игнорируется. Вы можете проверить с dmsetup table test. Вы можете даже написать foo barпозади error; это просто не волнует (и поэтому должно быть удалено).
Хауке Лагинг
@HaukeLaging, отредактировано.
psusi
Спасибо за ответ, я думаю, что именно так я и пойду. Единственная небольшая проблема, с которой я сталкиваюсь, заключается в том, что для этого требуется доступ с правами root, но я думаю, что вам это понадобится в любом случае или что-то вроде низкого уровня ...
mreithub
14

Ответы на этот вопрос уже есть о переполнении стека и сбое сервера, но некоторые методы отсутствовали. Чтобы упростить жизнь, вот список виртуальных устройств / блочных устройств Linux / файловой системы Linux / библиотеки пользовательских пространств ввода-вывода Linux:

Дополнительный факт: в SQLite есть драйвер VFS для имитации ошибок, поэтому он может обеспечить хорошее тестовое покрытие.

Связанный:

скоро
источник
5

Вы хотите механизм ввода неисправности для ввода / вывода.

В Linux есть метод, который не требует предварительной настройки и генерирует необычную ошибку (не EIO «Ошибка ввода-вывода», а ESRCH «Нет такого процесса»):

cat /proc/1234/mem

где 1234 - PID процесса, запущенного от имени того же пользователя, что и тестируемый процесс, но не самого процесса. Кредиты rubasov для мышления о /proc/$pid/mem.

Если вы используете PID самого процесса, вы получаете EIO, но только если вы читаете из области, которая не отображается в памяти процесса. Первая страница никогда не отображается, поэтому нормально, если вы читаете файл последовательно, но не подходит для процесса базы данных, который ищет непосредственно в середине файла.

С некоторыми дополнительными настройками в качестве пользователя root вы можете использовать устройство отображения карт для создания файлов с допустимыми секторами и поврежденными секторами.

Другой подход заключается в реализации небольшой файловой системы FUSE . EIO - это код ошибки по умолчанию, когда драйвер вашей файловой системы пользователя делает что-то не так, так что это легко сделать. Оба Perl и Python привязки поставляются с примерами , чтобы начать работу, вы можете быстро написать файловую систему , которая в основном зеркала существующие файлы , но впрыскивает EIO в тщательно выбранных местах. Существует такая файловая система: petardfs ( статья ), я не знаю, насколько хорошо она работает из коробки.

Еще один метод - это LD_PRELOADобертка. Существующим является Libfiu (внедрение ошибок в пользовательском пространстве). Он работает путем предварительной загрузки библиотеки, которая перегружает вызовы API POSIX. Вы можете написать простые директивы или произвольный C-код для переопределения нормального поведения.

Жиль "ТАК - перестань быть злым"
источник
Libfiu выглядит действительно многообещающе (и это в репозиториях Debian). Отличный ответ, спасибо +1
mreithub
1

Решение намного проще, если можно использовать файл устройства как «файл с ошибками ввода-вывода». Мое предложение для тех случаев, когда обычный файл должен иметь такие ошибки.

> 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

Я должен признать, что я немного запутался, потому что мне не удалось прочитать отдельные сектора из этого файла без ошибки (с dd .. seek=...). Может быть, это проблема упреждающего чтения.

Хауке Лагинг
источник
Размер блоков вашей файловой системы составляет не менее 4096 байт, поэтому они будут занимать несколько секторов, даже если файл небольшой.
Anon
1

Вы можете использовать CharybdeFS, которая была сделана именно для этой цели.

Это сквозная файловая система с предохранителями типа PetardFS, но гораздо более настраиваемая.

Смотрите кулинарную книгу CharybdeFS здесь: http://www.scylladb.com/2016/05/02/fault-injection-filesystem-cookbook/

Он достаточно продвинут, чтобы проверить базу данных.

Бенуа Кане
источник