Hi,

Guillem Jover wrote:
> On Tue, 2010-02-02 at 13:04:29 -0600, Jonathan Nieder wrote:

> > Better would be to add a new gzclearerr() function to allow the two
> > error indicators to be explicitly cleared.
> 
> There's already a gzclearerr provided by zlib, which clears both the
> EOF and error flags. And I think it's perfectly acceptable for code
> which might want to handle the interrupted case to call gzclearerr,
> and resume the previous zlib call.
> 
> The actual problem though, as you initially pointed out in the
> debian-dpkg thread, is that not all zlib IO functions preserve
> enough state to always recover from a partial read/write.

Thanks for the reminder.  (Since I had to look it up, the thread in
question is at
<http://lists.debian.org/debian-dpkg/2009/10/msg00091.html>.)
 
> I had skimmed previously over the gzio.c (stdio based) code and there
> were several other problematic functions,

I think gzwrite is the worst one.  gzwrite, gzprintf, gzputs, gzgets,
gzflush, and gzclose are not resumable, but I suspect it is only worth
fixing gzwrite (hence gzputs), gzflush, and gzclose.

Considering each function in gzio.c from zlib 1.2.3.5 in turn:

 - gzread() returns the number of bytes read, so in theory it should
   be fine.  In practice, it is pretty good, too.  Just one issue:

     In transparent mode, on short reads, the fread() call sets
     the error flag on the file handle but not the zlib error
     number.  The caller has to do another one-byte gzread() to
     learn the nature of the error.

 - gzwrite() forgets the number of bytes actually written and returns
   0 on error (-1 in gzlib.c).  A resumable function would return the
   short count.

 - gzprintf(): it is not worth making this resumable.  Applications that
   need a resumable gzprintf should write a sprintf coroutine (or use
   snprintf() in one fell swoop, if the string to be written is short
   enough) and use gzwrite().  In the simplest case, the semantics
   of SA_RESTART might be implementable and good enough, though.

 - gzputs() is implemented in terms of gzwrite() and shares its problems.

 - gzgets() is not resumable, but neither is fgets().  A resumable
   gzgets() would need another output parameter to tell how many bytes
   were read.

 - gzputc() and gzputc() are resumable.

 - gzungetc() does not do I/O.

 - do_flush() forgets the number of bytes actually written on error, so
   gzflush() is not resumable.

 - if gzseek() or gzrewind() fails, the error indicator is not set, so
   one can’t detect if it is a zlib internal failure or fseek() failure.
   gzseek() and gzrewind() are resumable.  They might screw up the file
   offset if not called again after an error.

 - gztell() does not do I/O.

 - gzoffset() is not implemented.

 - gzeof() returns 1 for I/O errors as well as EOF.  This is fine, but
   it should be documented.

 - gzdirect() does not do I/O.

 - gzclose() is not resumable.  Neither is fclose(), unless one calls
   fflush() first.  Unfortunately, a gzflush() is not enough to write
   out everything that gzclose() is going to write to the file.  Maybe
   a new resumable function to do everything except close the file
   would help?

   If a close() call is interrupted, I am not sure what one is
   supposed to do.  Friendly operating systems will prevent that, I
   guess.

   gzclose() does not report I/O errors when writing its last few
   bytes.

 - gzerror() looks safe.

 - gzclearerr() does exactly what one would want it to.  Sorry for the
   nonsense!

> but doing so now over the new
> gzlib.c based code it seems to be in a way better shape, although there
> might still be some other problems, I've not checked thoroughly.

I haven’t read this over yet.  Glad to hear there seems to have been
some progress.

> The EINTR, EAGAIN, EWOULDBLOCK errors seem to be safe to be handled now
> with gzclearerr, as there's no possibility of a partial fread/fwrite
> returning with those anymore.

write() still can, no?

>From gz_comp(), it seems the length of partial writes are still
sometimes forgotten:

 if (have && write(state->fd, state->next, have) != have) {
        gz_error(state, Z_ERRNO, zstrerror());
        return -1;
 }

Hope that helps,
Jonathan



-- 
To UNSUBSCRIBE, email to debian-bugs-dist-requ...@lists.debian.org
with a subject of "unsubscribe". Trouble? Contact listmas...@lists.debian.org

Reply via email to