From ba0067d5376f258d15765d876a96b9ef56bc40bb 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 returning due to a prior stream error.
* lib/close-stream.c (close_stream): Don't clobber errno if prev_fail
is true.  Reported by Jaroslav Škarvada in https://bugs.gnu.org/77800
---
 ChangeLog          |  7 +++++++
 lib/close-stream.c | 19 +++++++++++--------
 2 files changed, 18 insertions(+), 8 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 28322a2655..7c09f61044 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+2025-04-15  Jim Meyering  <meyering@meta.com>
+
+	close-stream: don't clobber errno upon prior failure
+	Don't reset errno when returning due to a prior stream error.
+	* lib/close-stream.c (close_stream): Don't clobber errno if prev_fail
+	is true.  Reported by Jaroslav Škarvada in https://bugs.gnu.org/77800
+
 2025-04-15  Paul Eggert  <eggert@cs.ucla.edu>

 	dfa: pacify gcc -Wstringop-overflow
diff --git a/lib/close-stream.c b/lib/close-stream.c
index 5dbb19c6ce..bf22f9f454 100644
--- a/lib/close-stream.c
+++ b/lib/close-stream.c
@@ -61,17 +61,20 @@ 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;
     }

+  if (prev_fail || (fclose_fail && some_pending))
+    return EOF;
+
   return 0;
 }
-- 
2.49.0.281.g77d6ee513f

