]> git.ipfire.org Git - thirdparty/coreutils.git/commitdiff
tests: verify that fts diagnoses readdir() failures
authorPeter Benie <pjb1008@cam.ac.uk>
Sun, 26 Jun 2016 18:07:45 +0000 (19:07 +0100)
committerPádraig Brady <P@draigBrady.com>
Sun, 26 Jun 2016 20:48:22 +0000 (21:48 +0100)
* tests/rm/rm-readdir-fail.sh: A new test to simulate readdir()
failing immediately or after returning a few entries, and verifying
that rm does the appropriate thing.
This was initially reported at:
http://bugzilla.opensuse.org/show_bug.cgi?id=984910
where it was mentioned that readdir() may fail
when an NFS server has a poor readdir cookie implementation.

tests/local.mk
tests/rm/rm-readdir-fail.sh [new file with mode: 0755]

index d3afb6e5c022891e872a8828779b41d87cd08dce..3032bdafaf47ec795ea8b4001a9df68b611b8dbc 100644 (file)
@@ -223,6 +223,7 @@ all_tests =                                 \
   tests/rm/unreadable.pl                       \
   tests/rm/v-slash.sh                          \
   tests/rm/many-dir-entries-vs-OOM.sh          \
+  tests/rm/rm-readdir-fail.sh                  \
   tests/chgrp/default-no-deref.sh              \
   tests/chgrp/deref.sh                         \
   tests/chgrp/no-x.sh                          \
diff --git a/tests/rm/rm-readdir-fail.sh b/tests/rm/rm-readdir-fail.sh
new file mode 100755 (executable)
index 0000000..15ef1d6
--- /dev/null
@@ -0,0 +1,106 @@
+#!/bin/sh
+# Test rm's behaviour when the directory cannot be read.
+# This test is skipped on systems that lack LD_PRELOAD support.
+
+# Copyright (C) 2016 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ rm
+require_gcc_shared_
+
+mkdir -p dir/notempty || framework_failure_
+
+# Simulate "readdir" failure.
+cat > k.c <<\EOF || framework_failure_
+#define _GNU_SOURCE
+#include <dlfcn.h>
+#include <dirent.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+struct dirent *readdir (DIR *dirp)
+{
+  static struct dirent *(*real_readdir)(DIR *dirp);
+  if (! real_readdir && ! (real_readdir = dlsym (RTLD_NEXT, "readdir")))
+    {
+      fprintf (stderr, "Failed to find readdir()\n");
+      errno = ESRCH;
+      return NULL;
+    }
+  static struct dirent* d;
+  if (! d && ! ( d = real_readdir (dirp)))
+    {
+      fprintf (stderr, "Failed to get dirent\n");
+      errno = ENOENT;
+      return NULL;
+    }
+
+  /* Flag that LD_PRELOAD and above functions work.  */
+  static int count = 1;
+  if (count == 1)
+    fclose (fopen ("preloaded", "w"));
+
+  /* Return some entries to trigger partial read failure,
+     ensuring we don't return ignored '.' or '..'  */
+  char const *readdir_partial = getenv ("READDIR_PARTIAL");
+  if (readdir_partial && *readdir_partial && count <= 3)
+    {
+      count++;
+      d->d_name[0]='0'+count; d->d_name[1]='\0';
+#ifdef _DIRENT_HAVE_D_NAMLEN
+      _D_EXACT_NAMELEN(d)=2;
+#endif
+      errno = 0;
+      return d;
+    };
+
+  /* Fail.  */
+  errno = ENOENT;
+  return NULL;
+}
+
+struct dirent64 *readdir64 (DIR *dirp)
+{
+  return (struct dirent64 *) readdir (dirp);
+}
+EOF
+
+# Then compile/link it:
+gcc_shared_ k.c k.so \
+  || framework_failure_ 'failed to build shared library'
+
+# Test if LD_PRELOAD works:
+export READDIR_PARTIAL
+for READDIR_PARTIAL in '' '1'; do
+  rm -f preloaded
+  (LD_PRELOAD=$LD_PRELOAD:./k.so returns_ 1 rm -Rf dir 2>>err) || fail=1
+  test -f preloaded ||
+    skip_ "internal test failure: maybe LD_PRELOAD doesn't work?"
+done
+
+# First case is failure to read any items from dir, then assume empty.
+# Generally that will be diagnosed when rm tries to rmdir().
+# Second case is more general error where we fail immediately
+# (with ENOENT in this case but it could be anything).
+cat <<EOF > exp
+rm: cannot remove 'dir': Directory not empty
+rm: traversal failed: dir: No such file or directory
+EOF
+
+compare exp err || fail=1
+
+Exit $fail