Hi,
I've redone the patch to use mountlist instead of mntent, added the !remote && !dummy
condition and created a test for it.
Cheers,
 Ondrej
>From 0e462bfc67e8bab3b7d5eea37b96b050d8db47e6 Mon Sep 17 00:00:00 2001
From: Ondrej Oprala <[email protected]>
Date: Thu, 9 Aug 2012 11:06:51 +0200
Subject: [PATCH] du: Fix an issue with bogus warnings on bind-mounted
 directories

* NEWS: Mention the fix.
* src/du.c: Add a function to read+stat mount points
and properly check cyclic directory entries.
* tests/Makefile.am: Add a new file.
* tests/du/cyclic-dir: Add a test for the fix.
---
 NEWS                |  3 +++
 src/du.c            | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++---
 tests/Makefile.am   |  1 +
 tests/du/cyclic-dir | 40 +++++++++++++++++++++++++++++++++++
 4 files changed, 102 insertions(+), 3 deletions(-)
 create mode 100755 tests/du/cyclic-dir

diff --git a/NEWS b/NEWS
index ca4568a..8f85c2b 100644
--- a/NEWS
+++ b/NEWS
@@ -4,6 +4,9 @@ GNU coreutils NEWS                                    -*- 
outline -*-
 
 ** Bug fixes
 
+  du no longer emits bogus warnings when traversing bind-mounted
+  directory cycles.
+
   cksum now prints checksums atomically so that concurrent
   processes will not intersperse their output.
   [the bug dates back to the initial implementation]
diff --git a/src/du.c b/src/du.c
index 7333941..1f3a3e4 100644
--- a/src/du.c
+++ b/src/du.c
@@ -27,6 +27,7 @@
 #include <getopt.h>
 #include <sys/types.h>
 #include <assert.h>
+#include "mountlist.h"
 #include "system.h"
 #include "argmatch.h"
 #include "argv-iter.h"
@@ -60,9 +61,14 @@ extern bool fts_debug;
 # define FTS_CROSS_CHECK(Fts)
 #endif
 
+#define MTAB "/etc/mtab"
+
 /* A set of dev/ino pairs.  */
 static struct di_set *di_set;
 
+/* A hash table for mount points. */
+static struct di_set *di_mnt;
+
 /* Keep track of the preceding "level" (depth in hierarchy)
    from one call of process_file to the next.  */
 static size_t prev_level;
@@ -333,11 +339,11 @@ Mandatory arguments to long options are mandatory for 
short options too.\n\
   exit (status);
 }
 
-/* Try to insert the INO/DEV pair into the global table, HTAB.
+/* Try to insert the INO/DEV pair into the di_set table.
    Return true if the pair is successfully inserted,
    false if the pair is already in the table.  */
 static bool
-hash_ins (ino_t ino, dev_t dev)
+hash_ins (struct di_set *di_set, ino_t ino, dev_t dev)
 {
   int inserted = di_set_insert (di_set, dev, ino);
   if (inserted < 0)
@@ -461,7 +467,7 @@ process_file (FTS *fts, FTSENT *ent)
       if (excluded
           || (! opt_count_all
               && (hash_all || (! S_ISDIR (sb->st_mode) && 1 < sb->st_nlink))
-              && ! hash_ins (sb->st_ino, sb->st_dev)))
+              && ! hash_ins (di_set, sb->st_ino, sb->st_dev)))
         {
           /* If ignoring a directory in preorder, skip its children.
              Ignore the next fts_read output too, as it's a postorder
@@ -476,6 +482,17 @@ process_file (FTS *fts, FTSENT *ent)
           return true;
         }
 
+      /*Check if dir is already in the mount table */
+      if (S_ISDIR (sb->st_mode))
+        {
+          if (di_set_lookup (di_mnt, sb->st_dev, sb->st_ino))
+            {
+              fts_set (fts, ent, FTS_SKIP);
+              error (0, 0, _("mount point %s already traversed"), quote 
(file));
+              return false;
+            }
+        }
+
       switch (info)
         {
         case FTS_D:
@@ -623,6 +640,36 @@ du_files (char **files, int bit_flags)
   return ok;
 }
 
+/* Fill the di_mnt table with dev/ino pairs
+ * of mount points  */
+
+static void
+fill_mount_table (void)
+{
+  struct mount_entry *mnt_ent, *mnt_free;
+  struct stat *buf;
+  buf = xmalloc (sizeof *buf);
+
+  mnt_ent = read_file_system_list (false);
+  while (mnt_ent)
+    {
+      if (!mnt_ent->me_remote && !mnt_ent->me_dummy)
+        {
+          stat (mnt_ent->me_mountdir, buf);
+          hash_ins (di_mnt, buf->st_ino, buf->st_dev);
+        }
+
+      mnt_free = mnt_ent;
+      mnt_ent = mnt_ent->me_next;
+
+      free (mnt_free->me_devname);
+      free (mnt_free->me_mountdir);
+      free (mnt_free->me_type);
+      free (mnt_free);
+    }
+  free (buf);
+}
+
 int
 main (int argc, char **argv)
 {
@@ -922,6 +969,13 @@ main (int argc, char **argv)
     xalloc_die ();
 
   /* Initialize the set of dev,inode pairs.  */
+
+  di_mnt = di_set_alloc ();
+  if (!di_mnt)
+    xalloc_die ();
+
+  fill_mount_table ();
+
   di_set = di_set_alloc ();
   if (!di_set)
     xalloc_die ();
@@ -1002,6 +1056,7 @@ main (int argc, char **argv)
 
   argv_iter_free (ai);
   di_set_free (di_set);
+  di_set_free (di_mnt);
 
   if (files_from && (ferror (stdin) || fclose (stdin) != 0) && ok)
     error (EXIT_FAILURE, 0, _("error reading %s"), quote (files_from));
diff --git a/tests/Makefile.am b/tests/Makefile.am
index edc04b4..20c313d 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -32,6 +32,7 @@ root_tests =                                  \
   cp/sparse-fiemap                             \
   dd/skip-seek-past-dev                                \
   df/problematic-chars                         \
+  du/cyclic-dir                                        \
   install/install-C-root                       \
   ls/capability                                        \
   ls/nameless-uid                              \
diff --git a/tests/du/cyclic-dir b/tests/du/cyclic-dir
new file mode 100755
index 0000000..65569ce
--- /dev/null
+++ b/tests/du/cyclic-dir
@@ -0,0 +1,40 @@
+#!/bin/sh
+# Ensure du properly handles bind mounts
+
+# Copyright (C) 2006-2012 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=.}/init.sh"; path_prepend_ ../src
+print_ver_ rm
+require_root_
+
+cleanup_()
+{
+  # When you take the undesirable shortcut of making /etc/mtab a link
+  # to /proc/mounts, unmounting "$other_partition_tmpdir" would fail.
+  # So, here we unmount a/b instead.
+  umount a/b
+  rm -rf "$other_partition_tmpdir"
+}
+. "$abs_srcdir/other-fs-tmpdir"
+
+t=$other_partition_tmpdir
+mkdir -p a/b $t/y
+mount --bind $t a/b \
+  || skip_ "This test requires mount with a working --bind option."
+
+du a && fail=1
+
+Exit $fail
-- 
1.7.11.2

Reply via email to