]> git.ipfire.org Git - thirdparty/coreutils.git/commitdiff
readlink,realpath: promptly diagnose write errors
authorCollin Funk <collin.funk1@gmail.com>
Fri, 9 Jan 2026 06:52:33 +0000 (22:52 -0800)
committerCollin Funk <collin.funk1@gmail.com>
Fri, 9 Jan 2026 07:16:06 +0000 (23:16 -0800)
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

* src/readlink.c (main): Check if standard output has it's error flag
set after printing a file name.
* src/realpath.c (process_path): Likewise.
* NEWS: Mention the improvement.

NEWS
src/readlink.c
src/realpath.c

diff --git a/NEWS b/NEWS
index 822146f9858bf334a7391911bce8d4722f1c03ab..da733cbd15efdd895725b6aa361c052c1c2ef624 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -81,6 +81,10 @@ GNU coreutils NEWS                                    -*- outline -*-
   'pinky' will now exit immediately upon receiving a write error, which is
   significant when reading large plan or project files.
 
+  'readlink' and 'realpath' will now exit promptly upon receiving a write error,
+  which is significant when canonicalizing multiple file names longer than
+  PATH_MAX.
+
   '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.
 
index 23f9d3b8372b088c2d27b02e5d1c82acc776c33d..498826882cb3fa3976788e1a6745198f3ad8d3c4 100644 (file)
@@ -171,6 +171,8 @@ main (int argc, char **argv)
           if (! no_newline)
             putchar (use_nuls ? '\0' : '\n');
           free (value);
+          if (ferror (stdout))
+            write_error ();
         }
       else
         {
index 24689a62411154e9208767861208adaf4cb32aba..d6c3864890cf93d2ca80b900af8a05fa082559bd 100644 (file)
@@ -166,6 +166,9 @@ process_path (char const *fname, int can_mode)
 
   free (can_fname);
 
+  if (ferror (stdout))
+    write_error ();
+
   return true;
 }