]> git.ipfire.org Git - thirdparty/coreutils.git/commitdiff
du: handle sub-bind-mount cycles gracefully
authorBoris Ranto <branto@redhat.com>
Mon, 1 Dec 2014 08:24:14 +0000 (09:24 +0100)
committerPádraig Brady <P@draigBrady.com>
Tue, 2 Dec 2014 01:01:35 +0000 (01:01 +0000)
This patch fixes the handling of sub-bind-mount cycles which are
incorrectly detected as the file system errors.  If you bind mount the
directory 'a' to its subdirectory 'a/b/c' and then run 'du a/b' you
will get the circular dependency warning even though nothing is wrong
with the file system.  This happens because the first directory that is
traversed twice in this case is not a bind mount but a child of bind
mount.  The solution is to traverse all the directories in the cycle
that fts detected and check whether they are not a (bind) mount.

* src/du.c (mount_point_in_fts_cycle): New function that checks whether
any of the directories in the cycle that fts detected is a mount point.
* src/du.c (process_file): Update the function to use the new function
that looks up all the directories in the fts cycle instead of only the
last one.
* tests/du/bind-mount-dir-cycle-v2.sh: New test case that exhibits the
described behavior.
* tests/local.mk: Reference the new root test.
* NEWS: Mention the bug fix.

NEWS
src/du.c
tests/du/bind-mount-dir-cycle-v2.sh [new file with mode: 0755]
tests/local.mk

diff --git a/NEWS b/NEWS
index 3a626563ed1cb30c5fab6a7cd33011063a794d7e..94e6d25296afb1be15397230926f938b25f4f67f 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -7,6 +7,10 @@ GNU coreutils NEWS                                    -*- outline -*-
   dd supports more robust SIGINFO/SIGUSR1 handling for outputting statistics.
   Previously those signals may have inadvertently terminated the process.
 
+  du now silently ignores all directory cycles due to bind mounts.
+  Previously it would issue a warning and exit with a failure status.
+  [bug introduced in coreutils-8.1 and partially fixed in coreutils-8.23]
+
   chroot again calls chroot(DIR) and chdir("/"), even if DIR is "/".
   This handles separate bind mounted "/" trees, and environments
   depending on the implicit chdir("/").
index ba2012059da8155c64fb266dafb59778a5129d88..f5726c7c466b8371d744c57563444dc4eccc2622 100644 (file)
--- a/src/du.c
+++ b/src/du.c
@@ -419,6 +419,27 @@ print_size (const struct duinfo *pdui, const char *string)
   fflush (stdout);
 }
 
+/* This function checks whether any of the directories in the cycle that
+   fts detected is a mount point.  */
+
+static bool
+mount_point_in_fts_cycle (FTSENT const *ent)
+{
+  FTSENT const *cycle_ent = ent->fts_cycle;
+
+  while (ent && ent != cycle_ent)
+    {
+      if (di_set_lookup (di_mnt, ent->fts_statp->st_dev,
+                         ent->fts_statp->st_ino) > 0)
+        {
+          return true;
+        }
+      ent = ent->fts_parent;
+    }
+
+  return false;
+}
+
 /* This function is called once for every file system object that fts
    encounters.  fts does a depth-first traversal.  This function knows
    that and accumulates per-directory totals based on changes in
@@ -516,7 +537,7 @@ process_file (FTS *fts, FTSENT *ent)
         case FTS_DC:
           /* If not following symlinks and not a (bind) mount point.  */
           if (cycle_warning_required (fts, ent)
-              && ! di_set_lookup (di_mnt, sb->st_dev, sb->st_ino))
+              && ! mount_point_in_fts_cycle (ent))
             {
               emit_cycle_warning (file);
               return false;
diff --git a/tests/du/bind-mount-dir-cycle-v2.sh b/tests/du/bind-mount-dir-cycle-v2.sh
new file mode 100755 (executable)
index 0000000..08bfae2
--- /dev/null
@@ -0,0 +1,38 @@
+#!/bin/sh
+# Check that du can handle sub-bind-mounts cycles as well.
+
+# Copyright (C) 2014 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_ du
+require_root_
+
+cleanup_() { umount a/b/c; }
+
+mkdir -p a/b/c || framework_failure_
+mount --bind a a/b/c \
+  || skip_ 'This test requires mount with a working --bind option.'
+
+echo a/b/c > exp || framework_failure_
+echo a/b >> exp || framework_failure_
+
+du a/b > out 2> err || fail=1
+sed 's/^[0-9][0-9]*    //' out > k && mv k out
+
+compare /dev/null err || fail=1
+compare exp out || fail=1
+
+Exit $fail
index 653c984d19644a4e1d4feb19d98fdb690d959791..349e322b05e4fe38b9832f82f24ab10105d24251 100644 (file)
@@ -117,6 +117,7 @@ all_root_tests =                            \
   tests/df/problematic-chars.sh                        \
   tests/df/over-mount-device.sh                        \
   tests/du/bind-mount-dir-cycle.sh             \
+  tests/du/bind-mount-dir-cycle-v2.sh          \
   tests/id/setgid.sh                           \
   tests/install/install-C-root.sh              \
   tests/ls/capability.sh                       \