develooper Front page | perl.perl5.porters | Postings from July 2013

[perl #43097] print doesn't overwrite $!

From:
Tony Cook via RT
Date:
July 4, 2013 02:14
Subject:
[perl #43097] print doesn't overwrite $!
Message ID:
rt-3.6.HEAD-2552-1372904079-47.43097-15-0@perl.org
On Mon Sep 08 04:07:40 2008, lists@der-pepe.de wrote:
> > On Aug 18, 2008, at 18:40, Christoph Bussenius via RT wrote:
> >> pp_print calls do_print.  If that is successful *and* the
file-handle is
> >> autoflushing, pp_print calls PerlIO_flush.
> >> The success of do_print depends on the handle's error flag, which gets
> >> set when a flush fails and will never be cleared again when writing
into
> >> the buffer; that means the filehandle will never be flushed again
(until
> >> the buffer fills up).
> >> My fix simply clears the error flag before each write.
> 
> I've added a new patch below.  Instead of clearing the error flag, it
> will now avoid to consider the previous error flag for the return status
> of do_print.  This fixes the bug and still keeps the error flag so that
> close knows about previous errors.
> 
> 
> On Tue, Aug 19, 2008 at 09:45:24AM +0200, Gisle Aas wrote:
> > It's common to just use print 
> > without testing its outcome and then in the end verify that all prints 
> > succeeded by testing if close() is successful.
> >
> >      open(my $fh, ">", $file) || die "Can't open $file: $!";
> >      print $fh $buf;
> >      print $fh $buf;
> >      ...
> >      close($fh) || die "Can't write to $file: $!";
> >
> > With your patch the first print could fail and then the second could 
> > succeed; leading to close() not reporting the first error.
> 
> This piece of code is still a bit problematic in my opinion.  In close's
> error message, $! is not guaranteed to describe the error that happened.
> If an error happens (as you suggest) in the first print, $! will be
> something like "no space left on device".  However, in the passage you
> marked as "...", $! can end up with any strange error code, and so the
> error that gets reported may not have anything to do with writing to the
> file.
> 
> I think the only way to get this to work properly is to save errno along
> with the error flag for every PerlIO handle.
> 
> Regards,
> Christoph
> 
> 
> --- perl-5.10.0/doio.c	2007-12-18 11:47:07.000000000 +0100
> +++ perl-5.10.0-debug/doio.c	2008-09-08 12:32:57.325273275 +0200
> @@ -1247,7 +1247,7 @@
>  	if (len && (PerlIO_write(fp,tmps,len) == 0))
>  	    happy = FALSE;
>  	Safefree(tmpbuf);
> -	return happy ? !PerlIO_error(fp) : FALSE;
> +	return happy;
>      }
>  }

I believe this change is dangerous under the following circumstances:

- no autoflush to a full filesystem
- program calls print() until the buffer fills, at which point flush is
called, which fails, setting the error flags. Note that the final write
may only be partly copied to the buffer
- file space is freed up
- the next write forces a flush, which writes the buffer with a possibly
truncated final write to the file system
- the new content is then written to the buffer (possibly flushing), and
so on with new writes

This produces output with some content missing.

A patch that saves errno per file handle would be less dangerous, but I
don't know if we want perlio to go to that level to preserve error codes.

Tony

---
via perlbug:  queue: perl5 status: open
https://rt.perl.org:443/rt3/Ticket/Display.html?id=43097



nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About