df: report correct device in presence of eclipsed mounts

* src/df.c (last_device_for_mount): A new function to identify
the last device mounted for a mount point.
(get_disk): Use the above to discard mount entries for a device,
where a later mount entry uses a different device name than
that of the user specified device.
* tests/df/over-mount-device.sh: A new root test.
* tests/local.mk: Reference the new test.
* NEWS: Reword for all these related recent fixes.
Discussed at: http://bugs.gnu.org/16539#69
This commit is contained in:
Pádraig Brady 2014-06-24 15:34:39 +01:00
parent 828801a174
commit d71c12f1e4
4 changed files with 104 additions and 5 deletions

9
NEWS
View File

@ -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.

View File

@ -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;
}

57
tests/df/over-mount-device.sh Executable file
View File

@ -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

View File

@ -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 \