tempfile: set errno to a known value before calling ferror()
authorJeff King <peff@peff.net>
Fri, 17 Feb 2017 21:07:49 +0000 (16:07 -0500)
committerJunio C Hamano <gitster@pobox.com>
Fri, 17 Feb 2017 22:40:29 +0000 (14:40 -0800)
In close_tempfile(), we return an error if ferror()
indicated a previous failure, or if fclose() failed. In the
latter case, errno is set and it is useful for callers to
report it.

However, if _only_ ferror() triggers, then the value of
errno is based on whatever syscall happened to last fail,
which may not be related to our filehandle at all. A caller
cannot tell the difference between the two cases, and may
use "die_errno()" or similar to report a nonsense errno value.

One solution would be to actually pass back separate return
values for the two cases, so a caller can write a more
appropriate message for each case. But that makes the
interface clunky.

Instead, let's just set errno to the generic EIO in this case.
That's not as descriptive as we'd like, but at least it's
predictable. So it's better than the status quo in all cases
but one: when the last syscall really did involve a failure
on our filehandle, we'll be wiping that out. But that's a
fragile thing for us to rely on.

In any case, we'll let the errno result from fclose() take
precedence over our value, as we know that's recent and
accurate (and many I/O errors will persist through the
fclose anyway).

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
tempfile.c
index ffcc2723755f1b97dd6626ed7c13801f905fdf18..68437106703470f39a6079aaad7d3bb407ac3389 100644 (file)
@@ -247,8 +247,13 @@ int close_tempfile(struct tempfile *tempfile)
        tempfile->fd = -1;
        if (fp) {
                tempfile->fp = NULL;
-               err = ferror(fp);
-               err |= fclose(fp);
+               if (ferror(fp)) {
+                       err = -1;
+                       if (!fclose(fp))
+                               errno = EIO;
+               } else {
+                       err = fclose(fp);
+               }
        } else {
                err = close(fd);
        }