740027
From dc1c0523a61932fb0c26a795b7e7391eadf2171a Mon Sep 17 00:00:00 2001
740027
From: Boris Ranto <branto@redhat.com>
740027
Date: Mon, 1 Dec 2014 09:24:14 +0100
740027
Subject: [PATCH 1/1] du: handle sub-bind-mount cycles gracefully
740027
740027
This patch fixes the handling of sub-bind-mount cycles which are
740027
incorrectly detected as the file system errors.  If you bind mount the
740027
directory 'a' to its subdirectory 'a/b/c' and then run 'du a/b' you
740027
will get the circular dependency warning even though nothing is wrong
740027
with the file system.  This happens because the first directory that is
740027
traversed twice in this case is not a bind mount but a child of bind
740027
mount.  The solution is to traverse all the directories in the cycle
740027
that fts detected and check whether they are not a (bind) mount.
740027
740027
* src/du.c (mount_point_in_fts_cycle): New function that checks whether
740027
any of the directories in the cycle that fts detected is a mount point.
740027
* src/du.c (process_file): Update the function to use the new function
740027
that looks up all the directories in the fts cycle instead of only the
740027
last one.
740027
* tests/du/bind-mount-dir-cycle-v2.sh: New test case that exhibits the
740027
described behavior.
740027
* tests/local.mk: Reference the new root test.
740027
---
740027
 src/du.c                            |   23 ++++++++++++++++++++-
740027
 tests/du/bind-mount-dir-cycle-v2.sh |   38 +++++++++++++++++++++++++++++++++++
740027
 tests/local.mk                      |    1 +
740027
 3 files changed, 61 insertions(+), 1 deletions(-)
740027
 create mode 100755 tests/du/bind-mount-dir-cycle-v2.sh
740027
740027
diff --git a/src/du.c b/src/du.c
740027
index ba20120..f5726c7 100644
740027
--- a/src/du.c
740027
+++ b/src/du.c
740027
@@ -419,6 +419,27 @@ print_size (const struct duinfo *pdui, const char *string)
740027
   fflush (stdout);
740027
 }
740027
 
740027
+/* This function checks whether any of the directories in the cycle that
740027
+   fts detected is a mount point.  */
740027
+
740027
+static bool
740027
+mount_point_in_fts_cycle (FTSENT const *ent)
740027
+{
740027
+  FTSENT const *cycle_ent = ent->fts_cycle;
740027
+
740027
+  while (ent && ent != cycle_ent)
740027
+    {
740027
+      if (di_set_lookup (di_mnt, ent->fts_statp->st_dev,
740027
+                         ent->fts_statp->st_ino) > 0)
740027
+        {
740027
+          return true;
740027
+        }
740027
+      ent = ent->fts_parent;
740027
+    }
740027
+
740027
+  return false;
740027
+}
740027
+
740027
 /* This function is called once for every file system object that fts
740027
    encounters.  fts does a depth-first traversal.  This function knows
740027
    that and accumulates per-directory totals based on changes in
740027
@@ -514,15 +514,11 @@ process_file (FTS *fts, FTSENT *ent)
740027
           break;
740027
 
740027
         case FTS_DC:
740027
-          if (cycle_warning_required (fts, ent))
740027
+          /* If not following symlinks and not a (bind) mount point.  */
740027
+          if (cycle_warning_required (fts, ent)
740027
+              && ! mount_point_in_fts_cycle (ent))
740027
             {
740027
-              /* If this is a mount point, then diagnose it and avoid
740027
-                 the cycle.  */
740027
-              if (di_set_lookup (di_mnt, sb->st_dev, sb->st_ino))
740027
-                error (0, 0, _("mount point %s already traversed"),
740027
-                       quote (file));
740027
-              else
740027
-                emit_cycle_warning (file);
740027
+              emit_cycle_warning (file);
740027
               return false;
740027
             }
740027
           return true;
740027
diff --git a/tests/du/bind-mount-dir-cycle-v2.sh b/tests/du/bind-mount-dir-cycle-v2.sh
740027
new file mode 100755
740027
index 0000000..08bfae2
740027
--- /dev/null
740027
+++ b/tests/du/bind-mount-dir-cycle-v2.sh
740027
@@ -0,0 +1,38 @@
740027
+#!/bin/sh
740027
+# Check that du can handle sub-bind-mounts cycles as well.
740027
+
740027
+# Copyright (C) 2014 Free Software foundation, Inc.
740027
+
740027
+# This program is free software: you can redistribute it and/or modify
740027
+# it under the terms of the GNU General Public License as published by
740027
+# the Free Software Foundation, either version 3 of the License, or
740027
+# (at your option) any later version.
740027
+
740027
+# This program is distributed in the hope that it will be useful,
740027
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
740027
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
740027
+# GNU General Public License for more details.
740027
+
740027
+# You should have received a copy of the GNU General Public License
740027
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
740027
+
740027
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
740027
+print_ver_ du
740027
+require_root_
740027
+
740027
+cleanup_() { umount a/b/c; }
740027
+
740027
+mkdir -p a/b/c || framework_failure_
740027
+mount --bind a a/b/c \
740027
+  || skip_ 'This test requires mount with a working --bind option.'
740027
+
740027
+echo a/b/c > exp || framework_failure_
740027
+echo a/b >> exp || framework_failure_
740027
+
740027
+du a/b > out 2> err || fail=1
740027
+sed 's/^[0-9][0-9]*	//' out > k && mv k out
740027
+
740027
+compare /dev/null err || fail=1
740027
+compare exp out || fail=1
740027
+
740027
+Exit $fail
740027
diff --git a/tests/local.mk b/tests/local.mk
740027
index 653c984..349e322 100644
740027
--- a/tests/local.mk
740027
+++ b/tests/local.mk
740027
@@ -117,6 +117,7 @@ all_root_tests =				\
740027
   tests/dd/skip-seek-past-dev.sh		\
740027
   tests/df/problematic-chars.sh			\
740027
   tests/du/bind-mount-dir-cycle.sh		\
740027
+  tests/du/bind-mount-dir-cycle-v2.sh		\
740027
   tests/id/setgid.sh				\
740027
   tests/install/install-C-root.sh		\
740027
   tests/ls/capability.sh			\
740027
-- 
740027
1.7.2.5
740027
diff -urNp coreutils-8.22-orig/tests/du/bind-mount-dir-cycle.sh coreutils-8.22/tests/du/bind-mount-dir-cycle.sh
740027
--- coreutils-8.22-orig/tests/du/bind-mount-dir-cycle.sh	2013-12-04 15:48:30.000000000 +0100
740027
+++ coreutils-8.22/tests/du/bind-mount-dir-cycle.sh	2015-07-02 15:58:49.230632316 +0200
740027
@@ -27,12 +27,11 @@ mount --bind a a/b \
740027
   || skip_ "This test requires mount with a working --bind option."
740027
 
740027
 echo a > exp || framework_failure_
740027
-echo "du: mount point 'a/b' already traversed" > exp-err || framework_failure_
740027
 
740027
-du a > out 2> err && fail=1
740027
+du a > out 2> err || fail=1
740027
 sed 's/^[0-9][0-9]*	//' out > k && mv k out
740027
 
740027
-compare exp-err err || fail=1
740027
+compare /dev/null err || fail=1
740027
 compare exp out || fail=1
740027
 
740027
 Exit $fail