On 09/01/2026 07:19, Collin Funk wrote:
The 'readlink' and 'realpath' programs have an uncommon case where they
can run for a very long time. When canonicalizing file names longer than
PATH_MAX, we have to call 'openat' for each directory up the tree until
we reach root which takes a long time. Here is an example of the current
behavior:
$ mkdir -p $(yes a/ | head -n $((32 * 1024)) | tr -d '\n')
$ while cd $(yes a/ | head -n 1024 | tr -d '\n'); do :; \
done 2>/dev/null
$ pwd | tr '/' '\n' | wc -l
32771
$ env time --format=%E readlink -f $(yes . | head -n 5) > /dev/full
readlink: write error: No space left on device
Command exited with non-zero status 1
0:59.72
$ env time --format=%E realpath $(yes . | head -n 5) > /dev/full
realpath: write error: No space left on device
Command exited with non-zero status 1
1:00.32
It is better to exit as soon as there is an error writing to standard
output:
$ env time --format=%E readlink -f $(yes . | head -n 5) > /dev/full
readlink: write error: No space left on device
Command exited with non-zero status 1
0:11.88
$ env time --format=%E realpath $(yes . | head -n 5) > /dev/full
realpath: write error: No space left on device
Command exited with non-zero status 1
0:12.04
Yes this is worth doing, as even though these commands can't run indefinitely,
they can run for a while, and the very simple code addition is worth it.
Consider also the time difference with something like:
yes | xargs sh -c 'realpath -ms "$@" || exit 255' >/dev/full
Since this is just a perf thing there is no real test I can think of.
Though that got me thinking that commands with --files0-from
can have indefinite output, so I adjusted du and wc to output early
and added tests in the attached.
cheers,
Padraig
From d629d542aab336cf219cb1bbd303dc9a60cb0b70 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?P=C3=A1draig=20Brady?= <[email protected]>
Date: Fri, 9 Jan 2026 14:16:04 +0000
Subject: [PATCH 1/3] wc: promptly diagnose write errors
* src/wc.c (write_counts): Call write_error() if any pending errors.
* tests/misc/write-errors.sh: Add a test case.
* NEWS: Mention the improvement.
---
NEWS | 3 +++
src/wc.c | 3 +++
tests/misc/write-errors.sh | 1 +
3 files changed, 7 insertions(+)
diff --git a/NEWS b/NEWS
index da733cbd1..a3239537e 100644
--- a/NEWS
+++ b/NEWS
@@ -88,6 +88,9 @@ GNU coreutils NEWS -*- outline -*-
'timeout' on Linux will always terminate the child in the case where the
timeout process itself dies, like when it receives a KILL signal for example.
+ 'wc' now exits promptly upon receiving a write error,
+ which is significant when processing many input files.
+
** Build-related
'kill' and 'uptime' are no longer built by default. These programs can be
diff --git a/src/wc.c b/src/wc.c
index a6285ba0b..77bfc66bb 100644
--- a/src/wc.c
+++ b/src/wc.c
@@ -258,6 +258,9 @@ write_counts (uintmax_t lines,
if (file)
printf (" %s", strchr (file, '\n') ? quotef (file) : file);
putchar ('\n');
+
+ if (ferror (stdout))
+ write_error ();
}
/* Read FD and return a summary. */
diff --git a/tests/misc/write-errors.sh b/tests/misc/write-errors.sh
index 0f85e6476..a84cb8d7a 100755
--- a/tests/misc/write-errors.sh
+++ b/tests/misc/write-errors.sh
@@ -56,6 +56,7 @@ tee < /dev/zero
tr . . < /dev/zero
unexpand /dev/zero
uniq -z -D /dev/zero
+wc --version; yes /dev/null | tr '\\\\n' '\\\\0' | wc --files0-from=-
yes
" |
sort -k 1b,1 > all_writers || framework_failure_
--
2.52.0
From 5ccb381dd453f7dca9ba261bce37682cc15af69a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?P=C3=A1draig=20Brady?= <[email protected]>
Date: Fri, 9 Jan 2026 14:20:56 +0000
Subject: [PATCH 2/3] du: promptly diagnose write errors
* src/du.c (print_size): Call write_error() if can't flush.
* tests/misc/write-errors.sh: Add a test case.
* NEWS: Mention the improvement.
---
NEWS | 6 +++---
src/du.c | 4 +++-
tests/misc/write-errors.sh | 1 +
3 files changed, 7 insertions(+), 4 deletions(-)
diff --git a/NEWS b/NEWS
index a3239537e..63db27dee 100644
--- a/NEWS
+++ b/NEWS
@@ -78,6 +78,9 @@ GNU coreutils NEWS -*- outline -*-
'du' now processes directories with 10,000 or more entries up to 9 times
faster on the Lustre file system.
+ 'du', and 'wc' now exit promptly upon receiving a write error,
+ which is significant when processing many input files.
+
'pinky' will now exit immediately upon receiving a write error, which is
significant when reading large plan or project files.
@@ -88,9 +91,6 @@ GNU coreutils NEWS -*- outline -*-
'timeout' on Linux will always terminate the child in the case where the
timeout process itself dies, like when it receives a KILL signal for example.
- 'wc' now exits promptly upon receiving a write error,
- which is significant when processing many input files.
-
** Build-related
'kill' and 'uptime' are no longer built by default. These programs can be
diff --git a/src/du.c b/src/du.c
index 1565c9078..a38c96174 100644
--- a/src/du.c
+++ b/src/du.c
@@ -406,7 +406,9 @@ print_size (const struct duinfo *pdui, char const *string)
}
}
printf ("\t%s%c", string, opt_nul_terminate_output ? '\0' : '\n');
- fflush (stdout);
+ if (fflush (stdout) != 0)
+ write_error ();
+
}
/* Fill the di_mnt set with local mount point dev/ino pairs. */
diff --git a/tests/misc/write-errors.sh b/tests/misc/write-errors.sh
index a84cb8d7a..719b17d68 100755
--- a/tests/misc/write-errors.sh
+++ b/tests/misc/write-errors.sh
@@ -34,6 +34,7 @@ cut -z -f1- /dev/zero
date +%${OFF64_T_MAX}c
date --version; yes 0 | date -f-
dd if=/dev/zero
+du --version; yes /dev/null | tr '\\\\n' '\\\\0' | du -l --files0-from=-
expand /dev/zero
factor --version; yes 1 | factor
fmt /dev/zero
--
2.52.0
From 621a785261ed58c86a189d771213603bba861f4d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?P=C3=A1draig=20Brady?= <[email protected]>
Date: Fri, 9 Jan 2026 14:25:24 +0000
Subject: [PATCH 3/3] maint: remove redundant processing in a test
* tests/misc/write-errors.sh: This was a no-op anyway
due to inadequate escaping. Also document the escaping requirement.
---
tests/misc/write-errors.sh | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/tests/misc/write-errors.sh b/tests/misc/write-errors.sh
index 719b17d68..e335e52d6 100755
--- a/tests/misc/write-errors.sh
+++ b/tests/misc/write-errors.sh
@@ -24,8 +24,9 @@ if ! test -w /dev/full || ! test -c /dev/full; then
skip_ '/dev/full is required'
fi
-# Writers that may output data indefinitely
-# First word in command line is checked against built programs
+# Writers that may output data indefinitely.
+# First word in command line is checked against built programs.
+# Escapes must be double escaped.
printf '%s' "\
cat /dev/zero
comm -z /dev/zero /dev/zero
@@ -42,7 +43,7 @@ fmt --version; yes | fmt
fold /dev/zero
fold -b /dev/zero
fold -c /dev/zero
-fold --version; yes | tr -d '\\n' | fold
+fold --version; yes | fold
head -z -n-1 /dev/zero
join -a 1 -z /dev/zero /dev/null
nl --version; yes | nl
--
2.52.0