[PATCH v5 2/2] io: Use gnulib fts implementation (BZ 22944, BZ 20331)

Collin Funk collin.funk1@gmail.com
Sun Apr 12 02:27:35 GMT 2026


Florian Weimer <fweimer@redhat.com> writes:

> * Collin Funk:
>
>> This optimization is quite important. For example, 'chown' and 'chmod'
>> do not need to stat the files that they are processing. Using the
>> information from stat in those programs would likely introduce some
>> nasty TOCTOU races. Therefore, there is no need to do all this extra
>> work that glibc does:
>
> This makes sense.
>
> Still I think we need to version the symbol, with an internal flag that
> does more stat'ing with FTS_NOSTAT.  If we find problems pretty much
> immediately after updating glibc, it tends to suggest fairly widespread
> impact.

Here is a Gnulib patch which avoids the stat and preserves the old glibc
behavior:

diff --git a/lib/fts.c b/lib/fts.c
index c61e8d89d8..fb42f243a0 100644
--- a/lib/fts.c
+++ b/lib/fts.c
@@ -1035,6 +1035,22 @@ check_for_dir:
                     if (! enter_dir (sp, p))
                       return NULL;
                   }
+
+                if (p->fts_info == FTS_NSOK)
+                  {
+                    if (S_ISREG (p->fts_statp->st_mode))
+                      p->fts_info = FTS_F;
+                    else if (S_ISDIR (p->fts_statp->st_mode))
+                      {
+                        if (! ISDOT (p->fts_name))
+                          p->fts_info = FTS_D;
+                        else
+                          {
+                            p->fts_info = (p->fts_level == FTS_ROOTLEVEL
+                                           ? FTS_D : FTS_DOT);
+                          }
+                      }
+                  }
                 return p;
         }
 cd_dot_dot:


Here is the test script with some typo fixes and the addition of
symbolic links, both dangling and existent:

    $ cat test.sh
    #!/bin/sh
    rm -rf testdir1 *.o main-glibc main-gnulib
    gnulib-tool --create-testdir --dir testdir1 fts
    (cd testdir1 && ./configure && make)
    mkdir -p a/b
    touch a/b/1 a/b/2 a/b/3
    ln -s $PWD/a/b/no-where $PWD/a/b/link-to-nowhere
    ln -s $PWD/a/b/1 $PWD/a/b/link
    gcc -c main.c -o main-glibc.o
    gcc -Itestdir1/gllib -Itestdir1 -DGNULIB=1 -c main.c -o main-gnulib.o
    gcc -o main-glibc main-glibc.o
    gcc -Ltestdir1/gllib -o main-gnulib main-gnulib.o -lgnu
    echo glibc
    ./main-glibc
    echo gnulib
    ./main-gnulib

And the output:

    $ ./test.sh
    [...]
    glibc
    a FTS_D
    a/b FTS_D
    a/b/1 FTS_F
    a/b/2 FTS_F
    a/b/3 FTS_F
    a/b/link-to-nowhere FTS_SLNONE
    a/b/link FTS_F
    a/b FTS_DP
    a FTS_DP
    gnulib
    a FTS_D
    a/b FTS_D
    a/b/1 FTS_F
    a/b/2 FTS_F
    a/b/3 FTS_F
    a/b/link-to-nowhere FTS_SLNONE
    a/b/link FTS_F
    a/b FTS_DP
    a FTS_DP

The FTS_NSOK is used for control flow, so setting ent->fts_info earlier
would be risky and likely introduce performance regressions. However,
ent->fts_statp->fts_info will always be initialized to zero or a file
type from dirp->d_type. Therefore, we can use the type just before
returning from fts_read.

Collin


More information about the Libc-alpha mailing list