File khusus yang menyebabkan kesalahan I / O

13

Saya ingin menguji secara otomatis apakah perangkat lunak bereaksi seperti yang diharapkan jika file DB SQLite penting gagal dibaca (menyebabkan kesalahan I / O). Persisnya itu terjadi beberapa hari yang lalu di klien. Kami memperbaikinya secara manual, tetapi sekarang saya ingin membuat kode otomatis untuk memperbaikinya dan memerlukan akses ke file yang rusak untuk mengujinya.

Karena semua yang ada di Unix adalah file, saya curiga bahwa mungkin ada file khusus yang selalu menyebabkan kesalahan I / O ketika seseorang mencoba membacanya (misalnya di / dev).

Beberapa file serupa (imo) adalah:

  • /dev/full yang selalu mengatakan "Tidak ada ruang yang tersisa di perangkat" jika Anda mencoba untuk menulisnya
  • /dev/null dan /dev/zero

jadi saya berasumsi hanya ada file seperti itu (tetapi belum menemukan satu).

Adakah yang tahu file seperti itu atau metode lain bagi saya untuk mendapatkan hasil yang diinginkan (gambar partisi yang sengaja rusak, pembungkus terbuka () menggunakan LD_PRELOAD, ...)?
Apa cara terbaik untuk pergi ke sini?

mreithub
sumber
Sejauh yang saya tahu, tidak ada file khusus di Linux yang memberikan SIGIO ketika Anda membacanya. Terakhir kali saya mendapatkan SIGIO adalah karena stik USB yang menyatakan kapasitasnya jauh lebih besar daripada yang asli, fisik. Mungkin itu bisa jadi kemungkinan?
Sayang
hmmm, saya mungkin bisa mencobanya dengan gambar partisi kecil yang akan saya
potong
SIGIO tidak berarti telah terjadi kesalahan, itu adalah cara suatu program dapat meminta untuk diberitahu bahwa non-blocking IO sekarang dimungkinkan, alih-alih memanggil select () atau poll ().
psusi
Ups, ya, Anda benar, tentu saja. Saya menulis SIGIO tetapi memikirkan kode kesalahan EIO. Tapi mungkin OP juga? Mengapa kegagalan membaca memberi SIGIO?
lgeorget
oh, saya membuat kesalahan yang sama dalam pertanyaan ... Diedit ...
mreithub

Jawaban:

8

Anda dapat menggunakan dmsetupuntuk membuat perangkat device-mapper menggunakan salah satu erroratau flakeytarget untuk mensimulasikan kegagalan.

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

Di mana 123 adalah panjang perangkat, di sektor dan / dev / loop0 adalah perangkat asli yang Anda inginkan untuk mensimulasikan kesalahan. Untuk kesalahan, Anda tidak perlu argumen berikutnya karena selalu mengembalikan kesalahan.

psusi
sumber
1
Saya menemukan setidaknya dua kesalahan dalam perintah itu: Nama perangkat yang hilang, salah ketik, dan apa artinya "1 0 / dev / null"?
Hauke ​​Laging
@ HaukeLaging, ahh, ya, saya meninggalkan nama dan entah bagaimana mengenai kutipan yang salah. 1 0 / dev / null berarti 1 target, mulai dari offset 0, didukung oleh perangkat / dev / null. Ini diperlukan untuk flakey, tetapi tampaknya opsional untuk kesalahan.
psusi
Menurut saya itu bukan "opsional" tetapi diabaikan saja. Anda dapat memeriksa dmsetup table test. Anda bahkan dapat menulis di foo barbelakang error; tidak peduli (dan karenanya harus dihapus).
Hauke ​​Laging
@ HaukeLaging, diedit.
psusi
Terima kasih atas jawabannya, saya pikir itulah cara saya akan pergi untuk saat ini. Satu-satunya masalah kecil yang saya miliki dengan ini adalah bahwa ia memerlukan akses root, tapi saya kira Anda akan tetap membutuhkannya atau hal-hal tingkat rendah seperti itu ... (Saya akan menggali ide LD_PRELOAD ketika saya punya waktu).
mreithub
14

Ada set jawaban yang bagus untuk ini pada Stack Overflow dan Server Fault, tetapi beberapa teknik tidak ada. Untuk membuat hidup lebih mudah di sini adalah daftar VM / Linux block device / Linux filesystem / Linux userspace library I / O mekanisme injeksi kesalahan:

Fakta bonus: SQLite memiliki driver VFS untuk mensimulasikan kesalahan sehingga bisa mendapatkan cakupan tes yang baik.

Terkait:

Segera
sumber
5

Anda menginginkan mekanisme injeksi kesalahan untuk I / O.

Di Linux, inilah metode yang tidak memerlukan pengaturan sebelumnya dan menghasilkan kesalahan yang tidak biasa (bukan EIO "Kesalahan input / output" tetapi ESRCH "Tidak ada proses seperti itu"):

cat /proc/1234/mem

di mana 1234 adalah PID dari suatu proses yang berjalan sebagai pengguna yang sama dengan proses yang Anda uji, tetapi bukan proses itu sendiri. Kredit untuk rubasov untuk berpikir dari /proc/$pid/mem.

Jika Anda menggunakan PID dari proses itu sendiri, Anda mendapatkan EIO, tetapi hanya jika Anda membaca dari area yang tidak dipetakan dalam memori proses. Halaman pertama tidak pernah dipetakan, jadi tidak apa-apa jika Anda membaca file secara berurutan, tetapi tidak cocok untuk proses database yang mencari langsung ke tengah file.

Dengan beberapa pengaturan lainnya sebagai root, Anda dapat memanfaatkan mapper perangkat untuk membuat file dengan sektor yang valid dan sektor yang buruk.

Pendekatan lain adalah menerapkan sistem file FUSE kecil . EIO adalah kode kesalahan default ketika driver sistem file userspace Anda melakukan sesuatu yang salah, sehingga mudah dicapai. Baik binding dan Perl Python datang dengan contoh untuk memulai, Anda dapat dengan cepat menulis sistem file yang sebagian besar mencerminkan file yang ada tetapi menyuntikkan EIO di tempat yang dipilih dengan cermat. Ada filesystem yang ada: petardfs ( artikel ), saya tidak tahu seberapa baik kerjanya di luar kotak.

Namun metode lain adalah LD_PRELOADpembungkus. Yang sudah ada adalah Libfiu (injeksi kesalahan di userspace). Ini bekerja dengan preloading perpustakaan yang membebani panggilan API POSIX. Anda dapat menulis arahan sederhana atau kode C arbitrer untuk mengesampingkan perilaku normal.

Gilles 'SANGAT berhenti menjadi jahat'
sumber
Libfiu terlihat sangat menjanjikan (dan itu ada di repo debian). Jawaban bagus, terima kasih, +1
mreithub
1

Solusinya jauh lebih mudah jika boleh menggunakan file perangkat sebagai "file dengan kesalahan I / O". Proposal saya untuk kasus-kasus di mana file biasa akan memiliki kesalahan seperti itu.

> 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

Saya harus mengakui bahwa saya agak bingung karena saya belum berhasil membaca sektor tunggal dari file itu tanpa kesalahan (dengan dd .. seek=...). Mungkin itu masalah baca-depan.

Hauke ​​Laging
sumber
Blok filesystem Anda setidaknya berukuran 4096 byte sehingga mereka akan menjangkau beberapa sektor bahkan jika file tersebut kecil.
Anon