bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

a real fts.c bug + fix


From: Jim Meyering
Subject: a real fts.c bug + fix
Date: Thu, 12 Jan 2006 11:12:00 +0100

I discovered a long-standing bug in fts.c yesterday:

2006-01-11  Jim Meyering  <address@hidden>

        * fts.c (fts_stat): When following a symlink-to-directory,
        don't necessarily interpret stat-fails+lstat-succeeds as indicating
        a dangling symlink.  That can also happen at least for ELOOP.
        The fix: return FTS_SLNONE only when the stat errno is ENOENT.
        FYI, this bug predates the inclusion of fts.c in coreutils.

I've included the test case, below.
If any of you know of a system with file name resolution code that
doesn't fail for a chain of 400 symlinks, or for which you get a
different diagnostic than `Too many levels of symbolic links' (ELOOP),
please provide details.

2006-01-11  Jim Meyering  <address@hidden>

        * tests/du/long-sloop: New file.  Test for today's fts.c bug fix.
        That bug could make du -L, chgrp -L, or chown -L fail to diagnose
        a very long sequence of symbolic links (not necessarily a loop).
        * tests/du/Makefile.am (TESTS): Add long-sloop.

Index: lib/fts.c
===================================================================
RCS file: /fetish/cu/lib/fts.c,v
retrieving revision 1.42
retrieving revision 1.43
diff -u -p -u -r1.42 -r1.43
--- lib/fts.c   11 Jan 2006 16:29:35 -0000      1.42
+++ lib/fts.c   11 Jan 2006 21:00:36 -0000      1.43
@@ -1069,7 +1069,8 @@ fts_stat(FTS *sp, register FTSENT *p, bo
        if (ISSET(FTS_LOGICAL) || follow) {
                if (stat(p->fts_accpath, sbp)) {
                        saved_errno = errno;
-                       if (!lstat(p->fts_accpath, sbp)) {
+                       if (errno == ENOENT
+                           && lstat(p->fts_accpath, sbp) == 0) {
                                __set_errno (0);
                                return (FTS_SLNONE);
                        }

Index: tests/du/long-sloop
===================================================================
RCS file: tests/du/long-sloop
diff -N tests/du/long-sloop
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ tests/du/long-sloop 12 Jan 2006 09:25:33 -0000      1.3
@@ -0,0 +1,68 @@
+#!/bin/sh
+# Use du to exercise a corner of fts's FTS_LOGICAL code.
+# Show that du fails with ELOOP (Too many levels of symbolic links)
+# when it encounters that condition.
+
+if test "$VERBOSE" = yes; then
+  set -x
+  du --version
+fi
+
+. $srcdir/../lang-default
+
+pwd=`pwd`
+t0=`echo "$0"|sed 's,.*/,,'`.tmp; tmp=$t0/$$
+trap 'status=$?; cd $pwd; chmod -R u+rwx $t0; rm -rf $t0 && exit $status' 0
+trap '(exit $?); exit $?' 1 2 13 15
+
+framework_failure=0
+mkdir -p $tmp || framework_failure=1
+cd $tmp || framework_failure=1
+
+# Create lots of directories, each containing a single symlink
+# pointing at the next directory in the list.
+
+# This number should be larger than the number of symlinks allowed in
+# file name resolution, but not too large as a number of entries
+# in a single directory.
+n=400
+
+dir_list=`seq $n`
+mkdir $dir_list || framework_failure=1
+for i in $dir_list; do
+  ip1=`expr $i + 1`
+  ln -s ../$ip1 $i/s || framework_failure=1
+done
+
+if test $framework_failure = 1; then
+  echo "$0: failure in testing framework" 1>&2
+  (exit 1); exit 1
+fi
+
+# If a system can handle this many symlinks in a file name,
+# just skip this test.
+file=1`printf %${n}s ' '|sed 's, ,/s,g'`
+cat $file > /dev/null 2>&1 && \
+  {
+    cat <<EOF >&2
+$0: Your systems appears to be able to handle more than $n symlinks
+in file name resolution, so skipping this test.
+EOF
+    (exit 77); exit 77
+  }
+
+fail=0
+
+# With coreutils-5.93 there was no failure.
+# With coreutils-5.94 we get a diagnostic like this:
+# du: cannot access `1/s/s/s/.../s': Too many levels of symbolic links
+du -L 1 > /dev/null 2> out1 && fail=1
+sed "s,1/s/s/s/[/s]*','," out1 > out || fail=1
+cat <<\EOF > exp || fail=1
+du: cannot access `': Too many levels of symbolic links
+EOF
+
+cmp out exp || fail=1
+test $fail = 1 && diff out exp 2> /dev/null
+
+(exit $fail); exit $fail




reply via email to

[Prev in Thread] Current Thread [Next in Thread]