https://cgit.git.savannah.gnu.org/cgit/coreutils.git/commit/?id=0d6fcb99d691d920961938e61c43478566ef626e

From 0d6fcb99d691d920961938e61c43478566ef626e Mon Sep 17 00:00:00 2001
From: Collin Funk <collin.funk1@gmail.com>
Date: Mon, 18 May 2026 20:40:28 -0700
Subject: tee: fix infinite loop when write returns EAGAIN and short write
 errors

* NEWS: Mention the bug fixes.
* THANKS.in: Add Bernhard M. Wiedemann for reporting the bugs.
* src/iopoll.c (close_wait): Remove function.
(write_wait): Don't call wait_for_nonblocking_write if write is
successful. Handle errors more robustly.
* src/iopoll.h (close_wait): Remove declaration.
* src/tee.c (tee_files): Use close instead of close_wait.
* tests/tee/short-write.sh: New test for the bug.
* tests/tee/write-eagain.sh: Likewise.
* tests/local.mk (all_tests): Add the new tests.
Fixes https://bugs.gnu.org/81060
---
 src/iopoll.c              | 59 +++++++++++++++++++++++++++++------------------
 src/iopoll.h              |  1 -
 src/tee.c                 |  2 +-
 8 files changed, 111 insertions(+), 25 deletions(-)
 create mode 100755 tests/tee/short-write.sh
 create mode 100755 tests/tee/write-eagain.sh

diff --git a/src/iopoll.c b/src/iopoll.c
index de20bc8d9..0bd2d6bf6 100644
--- a/src/iopoll.c
+++ b/src/iopoll.c
@@ -194,17 +194,6 @@ wait_for_nonblocking_write (int fd)
   return true;
 }
 
-/* wrapper for close() that also waits for FD if non blocking.  */
-
-extern bool
-close_wait (int fd)
-{
-  while (wait_for_nonblocking_write (fd))
-    ;
-  return close (fd) == 0;
-}
-
-
 /* wrapper for write() that also waits for FD if non blocking.  */
 
 extern bool
@@ -212,19 +201,43 @@ write_wait (int fd, void const *buffer, size_t size)
 {
   unsigned char const *buf = buffer;
 
-  while (true)
+  do
     {
-      ssize_t written = write (fd, buf, size);
-      if (written < 0)
-        written = 0;
-
-      size -= written;
-      if (size <= 0)  /* everything written */
-        return true;
-
-      if (! wait_for_nonblocking_write (fd))
-        return false;
+      const ssize_t written = write (fd, buf, size);
+      /* POSIX says that calling write with SIZE of zero may detect and
+         return errors.  If no error occurs, or write makes no attempt
+         to detect errors, then write returns zero with no other
+         results.  write_fail will return successfully in this case.  */
+      if (written == 0)
+        {
+          if (size == 0)
+            return true;
+          else
+            {
+              /* If SIZE is greater than zero and write returns zero,
+                 treat it as an error.  Some buggy drivers behave this
+                 way.  See src/dd.c and Gnulib's lib/full-write.c for
+                 more details.  */
+              errno = ENOSPC;
+              return false;
+            }
+        }
 
-      buf += written;
+      if (written < 0)
+        {
+          /* Return an error if write detected one with a SIZE of zero.
+             Otherwise, if SIZE is greater than zero, fail if it does
+             not become writable.  */
+          if (size == 0 || ! wait_for_nonblocking_write (fd))
+            return false;
+        }
+      else
+        {
+          buf += written;
+          size -= written;
+        }
     }
+  while (0 < size);
+
+  return true;
 }
diff --git a/src/iopoll.h b/src/iopoll.h
index 1711fdab9..a1561b9ff 100644
--- a/src/iopoll.h
+++ b/src/iopoll.h
@@ -5,5 +5,4 @@ int iopoll (int fdin, int fdout, bool block);
 bool iopoll_input_ok (int fdin);
 bool iopoll_output_ok (int fdout);
 
-bool close_wait (int fd);
 bool write_wait (int fd, void const *buffer, size_t size);
diff --git a/src/tee.c b/src/tee.c
index 32a18e340..fba6ae089 100644
--- a/src/tee.c
+++ b/src/tee.c
@@ -329,7 +329,7 @@ tee_files (int nfiles, char **files, bool pipe_check)
 
   /* Close the files, but not standard output.  */
   for (int i = 1; i <= nfiles; i++)
-    if (0 <= descriptors[i] && ! close_wait (descriptors[i]))
+    if (0 <= descriptors[i] && close (descriptors[i]) < 0)
       {
         error (0, errno, "%s", quotef (files[i]));
         ok = false;
