tempfile: set errno to a known value before calling ferror()

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>
This commit is contained in:
Jeff King 2017-02-17 16:07:49 -05:00 коммит произвёл Junio C Hamano
Родитель 0838cbc22f
Коммит 7e8c9355b7
1 изменённых файлов: 7 добавлений и 2 удалений

Просмотреть файл

@ -247,8 +247,13 @@ int close_tempfile(struct tempfile *tempfile)
tempfile->fd = -1; tempfile->fd = -1;
if (fp) { if (fp) {
tempfile->fp = NULL; tempfile->fp = NULL;
err = ferror(fp); if (ferror(fp)) {
err |= fclose(fp); err = -1;
if (!fclose(fp))
errno = EIO;
} else {
err = fclose(fp);
}
} else { } else {
err = close(fd); err = close(fd);
} }