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