ikegami has asked for the wisdom of the Perl Monks concerning the following question:

Does anyone know how to generate an IO failure during a read so I can write some tests. Something portable would be nice, but I'll take anything.

Replies are listed 'Best First'.
Re: Generating a readline error
by BrowserUk (Patriarch) on Jul 17, 2009 at 16:50 UTC

    Like this?

    open NUL, '<', 'nul' or die $!;; defined( $s = readline( NUL )) or die $!;; [Died at (eval 9) line 1.

    Or

    open O, '>', 'nul' or die $!;; defined( $s = readline( O )) or die $!;; Filehandle O opened only for output at (eval 11) line 1.

    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
      You're misreporting eof as an error.

      Thanks, the method used in the second snippet (added since my original reply) will be useful to me. It's even portable (using the appropriate device name)!

      It would be ideal if I had a means of generating an error after doing some valid reads, but I can make do without.

        I guess you could read for awhile and then close the filehandle. It is possible to open 2 filehandles to same file. Close one and keep going with the other one after first bombs.

        My seek idea didn't work as seek past EOF is not an error (that's how to generate "sparse files"). I've tested that on Perl before (*nix and even XP) and it works fine and you just get EOF if you read something like that. Of course EOF is not an error. And of course a write past EOF is completely legal and produces no error.

        I've written device drivers that will pass back "known bad data" from say disk system. But that's a special thing and drivers,O/S,app have to know what's going on. That sort of thing is used in huge volume data applications where say 12 bits wrong won't matter in say a video image or a seismic data app.

        Still curious as to what kind of data recovery is possible in typ Perl app after "bad data" from an I/O device?

      That seems to die at the open, not the readline.
        This will generate a "read error", but the only way I can see to do this is just the "filehandle not open" case. I don't how to easily create a "file system corrupted, data read error". I tried seeking past EOF and doing read but Perl does the right thing and returns EOF. Creating a data file that is intentionally corrupted is not something that I'm inclined to try on my main working machine!

        Its not clear to me why corrupted data case needs to be checked for, just let the program die if read can't get bytes. I mean what sort of error recovery would be feasible in that case by Perl? I think none.

        #!/usr/bin/perl -w use strict; open (IN, "asdf"); #this fails but return code ignored print while (<IN>); #readline() on closed filehandle #IN at C:\TEMP\ioerror.pl line 6.
Re: Generating a readline error
by ikegami (Patriarch) on Jul 17, 2009 at 18:28 UTC

    I should have checked the docs for the underlying system call! Possible failure modes for read on this system:

    EAGAIN
    Non-blocking I/O has been selected using O_NONBLOCK and no data was immediately available for reading.
    EBADF
    fd is not a valid file descriptor or is not open for reading.
    EFAULT
    buf is outside your accessible address space.
    EINTR
    The call was interrupted by a signal before any data was read.
    EINVAL
    fd is attached to an object which is unsuitable for reading; or the file was opened with the O_DIRECT flag, and either the address specified in buf, the value specified in count, or the current file offset is not suitably aligned.
    EIO
    I/O error. This will happen for example when the process is in a background process group, tries to read from its controlling tty, and either it is ignoring or blocking SIGTTIN or its process group is orphaned. It may also occur when there is a low-level I/O error while reading from a disk or tape.
    EISDIR
    fd refers to a directory.

    Using non-blocking IO might do the trick. Or maybe closing the underlying fd using POSIX::close.

      Lots of these things like EAGAIN aren't really "errors", somebody doing that would be expected to handle this situation. aysnc read/write is common in realtime systems.

      Some of these things like EFAULT, can't be done as a user (O/S) won't allow it.

      One simple idea for you is to setup some I/O intensive thing on a flash drive. Then reach down and unplug that thing. This will look like disk drive died midstream and some kind of error will happen. Depending upon file system, timing of this, etc. the result might be even complete loss of all data on that stick. But if it formatted to look like a hard drive, then I think you can set up (maybe with a few ties) something like looks like EIO (bad sector) because the "checksum" for that sector will be wrong. If I'm right, then you have a known bad file on a "disk drive" that you can test with. Setup up loop with write intensive thing, unplug drive, then see if you can read all files after you reset program, replug in and try reading. May take a couple of tries to hit this right and get desire effect.

        Lots of these things like EAGAIN aren't really "errors",

        But Perl's not gonna treat them any different than EIO, it's a good substitute for EIO for testing.

Re: Generating a readline error
by MidLifeXis (Monsignor) on Jul 17, 2009 at 16:43 UTC

    Is there some sort of mock IO object available? How about a tie interface with the real IO object on the back end, but injecting your errors at the right point?

    Update: Data::Storage::Mock perhaps?

    --MidLifeXis

    The tomes, scrolls etc are dusty because they reside in a dusty old house, not because they're unused. --hangon in this post

      I'm testing Perl itself, so using a tied object is not ideal.
Re: Generating a readline error
by ig (Vicar) on Jul 17, 2009 at 20:45 UTC

    On linux I created a small file system on a loop device, filled it with a single file then truncated the underlying file. After deleting and recreating the loop device (I suppose this flushed cashed data), reading the contained file resulted in several successful reads followed by a read failure with EBADF.