Package: zlib1g-dev
Version: 1:1.2.3.4.dfsg-3
Severity: wishlist

Hi,

zlib’s gzFile interface provides no way I can see to recover from
EINTR.

Context:

When library code makes syscalls, I live in constant danger that the
application may have installed a signal handler with SA_RESTART
disabled.  If the system call is interrupted by a signal, depending
on the application, one of three behaviors would be appropriate:

 a. if the goal is to walk the stack after a SIGINT and clean up
    before exiting, my function should return quickly to the
    application with an error;

 b. if the interruption is just a side effect of something else (maybe
    this is a System V-based system and the application writer forgot
    to use SA_RESTART, or maybe the interruption was intended for some
    other thread), my function should restart the call and return with
    success once the operation succeeds.

 c. if the interruption (perhaps from SIGIO) represents some
    high-priority job that needs to be done soon, my function should
    let that job finish and then resume.

The usual way to handle this is to either pick one or leave it to the
application by returning with enough information to resume the
operation later.

In my case, the library was libdpkg, and the chosen behavior was
option b.  Simple, right?

        char buffer[BUFSZ];
        int actualread;
        gzFile gzfile = gzdopen(fd_in, "r");

        for (;;) {
                int actualread = gzread(gzfile, &buffer[0], sizeof(buffer));

                if (actualread < 0) {
                        int err;
                        const char *errmsg = gzerror(gzfile, &err);

                        if (err == Z_ERRNO && errno == EINTR)
                                continue;
                        errx(_("read error: %s"), errmsg);
                }
                if (actualread == 0) /* EOF! */
                        break;
                write(fd_out, &buffer[0], actualread);
        }
        int err = gzclose(gzfile);
        // handle error or return

Unfortunately, when zlib hits its first EINTR, two bad things happen:

 - the error indicator for the underlying stdio stream is set;
 - the error number for the gzFile is set

and there is no way to undo either.  So in fact this is an infinite
loop.

Thoughts about fix:

I would love it if zlib just cleared the error indicator before each
potentially resumable operation (gzread(), gzwrite(), gzseek()).  But
probably someone out there is relying on zlib _not_ to do that, as
part of his implementation of error recovery plan a above, so such a
change would be dangerous.

Better would be to add a new gzclearerr() function to allow the two
error indicators to be explicitly cleared.

        void Z_EXPORT gzclearerr (file)
            gzFile file;
        {
                gz_stream *s = (gz_stream*)file;

                if (s == NULL) return;

                s->z_err = Z_OK;
                clearerr(s->file);
        }

What do you think?

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