From 9953eb926862c7461292dde38e2a666d715f249d Mon Sep 17 00:00:00 2001
From: Jim Meyering <meyering@meta.com>
Date: Tue, 15 Apr 2025 18:44:55 -0700
Subject: [PATCH] close-stream: don't clobber errno upon prior failure
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Don't reset errno when fclose succeeds yet there was a preceding error.
* lib/close-stream.c (close_stream): Don't clobber errno if prev_fail is
true. Exposed via Fedora 42's new glibc vs grep's --help being precisely
4096 bytes. Reported by Jaroslav Škarvada in https://bugs.gnu.org/77800
---
 ChangeLog          |  8 ++++++++
 lib/close-stream.c | 18 +++++++++---------
 2 files changed, 17 insertions(+), 9 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 6df7b09a1e..77960a4993 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2025-04-15  Jim Meyering  <meyering@meta.com>
+
+	close-stream: don't clobber errno upon prior failure
+	Don't reset errno when fclose succeeds yet there was a preceding error.
+	* lib/close-stream.c (close_stream): Don't clobber errno if prev_fail is
+	true. Exposed via Fedora 42's new glibc vs grep's --help being precisely
+	4096 bytes. Reported by Jaroslav Škarvada in https://bugs.gnu.org/77800
+
 2025-04-17  Paul Eggert  <eggert@cs.ucla.edu>

 	memset_explicit-tests: pacify -Wuse-after-free
diff --git a/lib/close-stream.c b/lib/close-stream.c
index 5dbb19c6ce..576781a251 100644
--- a/lib/close-stream.c
+++ b/lib/close-stream.c
@@ -61,17 +61,17 @@ close_stream (FILE *stream)
   /* Return an error indication if there was a previous failure or if
      fclose failed, with one exception: ignore an fclose failure if
      there was no previous error, no data remains to be flushed, and
-     fclose failed with EBADF.  That can happen when a program like cp
-     is invoked like this 'cp a b >&-' (i.e., with standard output
-     closed) and doesn't generate any output (hence no previous error
-     and nothing to be flushed).  */
+     fclose failed with EBADF.  In the case of an ignorable fclose
+     failure, clear errno to avoid misleading diagnostics. That can
+     happen when a program like cp is invoked like this 'cp a b >&-'
+     (i.e., with standard output closed) and doesn't generate any output
+     (hence no previous error and nothing to be flushed).  */

-  if (prev_fail || (fclose_fail && (some_pending || errno != EBADF)))
+  if (!prev_fail && fclose_fail && !some_pending && errno == EBADF)
     {
-      if (! fclose_fail)
-        errno = 0;
-      return EOF;
+      errno = 0;
+      return 0;
     }

-  return 0;
+  return prev_fail || (fclose_fail && some_pending) ? EOF : 0;
 }
-- 
2.49.0.391.g4bbb303af6

