summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS9
-rw-r--r--src/df.c42
-rwxr-xr-xtests/df/over-mount-device.sh57
-rw-r--r--tests/local.mk1
4 files changed, 104 insertions, 5 deletions
diff --git a/NEWS b/NEWS
index 090254d8a..653278528 100644
--- a/NEWS
+++ b/NEWS
@@ -44,10 +44,11 @@ GNU coreutils NEWS -*- outline -*-
[These dd bugs were present in "the beginning".]
- df now elides duplicates for virtual file systems like tmpfs.
- Displays the correct device details for points mounted multiple times.
- Displays placeholder values for inaccessible file systems,
- rather than error messages or values for the wrong file system.
+ df has more fixes related to the newer dynamic representation of file systems:
+ Duplicates are elided for virtual file systems like tmpfs.
+ Details for the correct device are output for points mounted multiple times.
+ Placeholder values are output for inaccessible file systems, rather than
+ than error messages or values for the wrong file system.
[These bugs were present in "the beginning".]
du now silently ignores directory cycles introduced with bind mounts.
diff --git a/src/df.c b/src/df.c
index dc6544bc6..063cabf30 100644
--- a/src/df.c
+++ b/src/df.c
@@ -1114,6 +1114,33 @@ get_dev (char const *disk, char const *mount_point, char const* file,
free (dev_name);
}
+/* Scan the mount list returning the _last_ device found for MOUNT.
+ NULL is returned if MOUNT not found. The result is malloced. */
+static char *
+last_device_for_mount (char const* mount)
+{
+ struct mount_entry const *me;
+ struct mount_entry const *le = NULL;
+
+ for (me = mount_list; me; me = me->me_next)
+ {
+ if (STREQ (me->me_mountdir, mount))
+ le = me;
+ }
+
+ if (le)
+ {
+ char *devname = le->me_devname;
+ char *canon_dev = canonicalize_file_name (devname);
+ if (canon_dev && IS_ABSOLUTE_FILE_NAME (canon_dev))
+ return canon_dev;
+ free (canon_dev);
+ return xstrdup (le->me_devname);
+ }
+ else
+ return NULL;
+}
+
/* If DISK corresponds to a mount point, show its usage
and return true. Otherwise, return false. */
static bool
@@ -1122,6 +1149,7 @@ get_disk (char const *disk)
struct mount_entry const *me;
struct mount_entry const *best_match = NULL;
bool best_match_accessible = false;
+ bool eclipsed_device = false;
char const *file = disk;
char *resolved = canonicalize_file_name (disk);
@@ -1139,9 +1167,12 @@ get_disk (char const *disk)
if (STREQ (disk, devname))
{
+ char *last_device = last_device_for_mount (me->me_mountdir);
+ eclipsed_device = last_device && ! STREQ (last_device, devname);
size_t len = strlen (me->me_mountdir);
- if (! best_match_accessible || len < best_match_len)
+ if (! eclipsed_device
+ && (! best_match_accessible || len < best_match_len))
{
struct stat disk_stats;
bool this_match_accessible = false;
@@ -1159,6 +1190,8 @@ get_disk (char const *disk)
best_match_len = len;
}
}
+
+ free (last_device);
}
free (canon_dev);
@@ -1173,6 +1206,13 @@ get_disk (char const *disk)
best_match->me_remote, NULL, false);
return true;
}
+ else if (eclipsed_device)
+ {
+ error (0, 0, _("cannot access %s: over-mounted by another device"),
+ quote (file));
+ exit_status = EXIT_FAILURE;
+ return true;
+ }
return false;
}
diff --git a/tests/df/over-mount-device.sh b/tests/df/over-mount-device.sh
new file mode 100755
index 000000000..a85ce8dbf
--- /dev/null
+++ b/tests/df/over-mount-device.sh
@@ -0,0 +1,57 @@
+#!/bin/sh
+# Ensure that df /dev/loop0 errors out if overmounted by another device
+
+# 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_ df
+require_root_
+
+cwd=$(pwd)
+cleanup_() { cd /; umount "$cwd/mnt"; umount "$cwd/mnt"; }
+
+skip=0
+
+# Create 2 file systems
+for i in 1 2; do
+ dd if=/dev/zero of=blob$i bs=8192 count=200 > /dev/null 2>&1 \
+ || skip=1
+ mkfs -t ext2 -F blob$i \
+ || skip_ "failed to create ext2 file system"
+done
+
+# Mount both at the same place (eclipsing the first)
+mkdir mnt || skip=1
+mount -oloop blob1 mnt || skip=1
+eclipsed_dev=$(df --o=source mnt | tail -n1) || skip=1
+mount -oloop blob2 mnt || skip=1
+
+test $skip = 1 \
+ && skip_ "insufficient mount/ext2 support"
+
+df . || skip_ "failed to lookup the device for the current dir"
+
+echo "df: cannot access '$eclipsed_dev': over-mounted by another device" > exp
+
+# We should get an error for the eclipsed device and continue
+df $eclipsed_dev . > out 2> err && fail=1
+
+# header and single entry in output
+test $(wc -l < out) = 2 || fail=1
+
+compare exp err || fail=1
+
+Exit $fail
diff --git a/tests/local.mk b/tests/local.mk
index cd7da5b2f..86050dc79 100644
--- a/tests/local.mk
+++ b/tests/local.mk
@@ -115,6 +115,7 @@ all_root_tests = \
tests/cp/sparse-fiemap.sh \
tests/dd/skip-seek-past-dev.sh \
tests/df/problematic-chars.sh \
+ tests/df/over-mount-device.sh \
tests/du/bind-mount-dir-cycle.sh \
tests/id/setgid.sh \
tests/install/install-C-root.sh \