From 7d2c56e52b0a8c2cdc802576b33b2870621b11c5 Mon Sep 17 00:00:00 2001 From: Ondrej Oprala Date: Thu, 29 Nov 2012 18:21:27 +0100 Subject: [PATCH] df: Fix outputting filesystem duplicities caused by bind-mounts * NEWS: Mention the fix. * src/df.c (devlist_add): Add another entry to the global device number list. (dev_examined): Check if a filesystem has been examined already. * tests/df/df-dup-remove.sh: Add a new test simulating mtab entries. * tests/local.mk: Add the test to the list. --- NEWS | 3 ++ src/df.c | 70 +++++++++++++++++++++++++++++++++++++++++++- tests/df/df-dup-remove.sh | 69 ++++++++++++++++++++++++++++++++++++++++++++ tests/local.mk | 1 + 4 files changed, 141 insertions(+), 2 deletions(-) create mode 100755 tests/df/df-dup-remove.sh diff --git a/NEWS b/NEWS index d4aebeb..d0e13f6 100644 --- a/NEWS +++ b/NEWS @@ -22,6 +22,9 @@ GNU coreutils NEWS -*- outline -*- another range. Before, "echo 123|cut --output-delim=: -b2-,3" would print "2:3". Now it prints "23". [bug introduced in 5.3.0] + df now properly outputs filesystem information with bind mounts + present on the system. + install -m M SOURCE DEST no longer has a race condition where DEST's permissions are temporarily derived from SOURCE instead of from M. diff --git a/src/df.c b/src/df.c index cac26b7..0a1a691 100644 --- a/src/df.c +++ b/src/df.c @@ -43,6 +43,17 @@ proper_name ("David MacKenzie"), \ proper_name ("Paul Eggert") +/* Filled with device numbers of examined filesystems to avoid + duplicities in output. */ +struct devlist +{ + dev_t dev_num; + struct devlist *dev_next; +}; + +/* Device list help pointer. */ +static struct devlist *devlist_head; + /* If true, show even file systems with zero size or uninteresting types. */ static bool show_all_fs; @@ -1076,6 +1087,48 @@ get_entry (char const *name, struct stat const *statp) get_point (name, statp); } +/* Add a device number of the soon-to-be examined + filesystem to the global list devlist. */ + +static void +devlist_add (dev_t dev_num) +{ + struct devlist *devlist = devlist_head; + while (devlist->dev_next) + devlist = devlist->dev_next; + + if (!devlist->dev_num) + devlist->dev_num = dev_num; + else + { + devlist->dev_next = xmalloc (sizeof *devlist); + devlist = devlist->dev_next; + devlist->dev_num = dev_num; + devlist->dev_next = NULL; + } +} + +/* Check if the device was already examined. */ + +static bool +dev_examined (struct devlist *devlist, char *mount_dir) +{ + dev_t dev_num; + struct stat buf; + stat (mount_dir, &buf); + + dev_num = buf.st_dev; + while (devlist) + { + if (devlist->dev_num == dev_num) + return true; + devlist = devlist->dev_next; + } + + devlist_add (dev_num); + return false; +} + /* Show all mounted file systems, except perhaps those that are of an unselected type or are empty. */ @@ -1083,10 +1136,23 @@ static void get_all_entries (void) { struct mount_entry *me; + struct devlist *devlist = xmalloc (sizeof *devlist); + devlist_head = devlist; + + devlist->dev_num = 0; + devlist->dev_next = NULL; for (me = mount_list; me; me = me->me_next) - get_dev (me->me_devname, me->me_mountdir, NULL, me->me_type, - me->me_dummy, me->me_remote, NULL, true); + if (!dev_examined (devlist, me->me_mountdir)) + get_dev (me->me_devname, me->me_mountdir, NULL, me->me_type, + me->me_dummy, me->me_remote, NULL, true); + + while (devlist) + { + devlist = devlist->dev_next; + free (devlist_head); + devlist_head = devlist; + } } /* Add FSTYPE to the list of file system types to display. */ diff --git a/tests/df/df-dup-remove.sh b/tests/df/df-dup-remove.sh new file mode 100755 index 0000000..d097d4a --- /dev/null +++ b/tests/df/df-dup-remove.sh @@ -0,0 +1,69 @@ +#!/bin/sh +# Test df's behaviour when the mount list contains duplicit entries. +# This test is skipped on systems that lack LD_PRELOAD support; that's fine. + +# Copyright (C) 2012 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 . + +. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src +print_ver_ df + +df || skip_ "df fails" + +# Simulate "mtab" failure. +cat > k.c <<'EOF' || framework_failure_ +#include +#include + +struct mntent *getmntent (FILE *fp) +{ + /* Prove that LD_PRELOAD works. */ + static int done = 0; + if (!done) + { + fclose (fopen ("x", "w")); + ++done; + } + /* Now simulate an mtab file with + two entries. */ + + static struct mntent mntent; + + while (done++ < 3) + { + mntent.mnt_fsname = "fsname"; + mntent.mnt_dir = "/"; + mntent.mnt_type = "-"; + + return &mntent; + } + return NULL; +} +EOF + +# Then compile/link it: +gcc --std=gnu99 -shared -fPIC -ldl -O2 k.c -o k.so \ + || skip_ "getmntent hack does not work on this platform" + +# Test if LD_PRELOAD works: +LD_PRELOAD=./k.so df +test -f x || skip_ "internal test failure: maybe LD_PRELOAD doesn't work?" + +#The fake mtab file should only contain 2 entries, both +#having the same device number; thus the output should +#consist of a header and one entry +test $(LD_PRELOAD=./k.so df | wc -l) -eq 2 || fail=1 + +Exit $fail diff --git a/tests/local.mk b/tests/local.mk index 1b0ace4..8f5def2 100644 --- a/tests/local.mk +++ b/tests/local.mk @@ -457,6 +457,7 @@ all_tests = \ tests/df/unreadable.sh \ tests/df/total-unprocessed.sh \ tests/df/no-mtab-status.sh \ + tests/df/df-dup-remove.sh \ tests/dd/direct.sh \ tests/dd/misc.sh \ tests/dd/nocache.sh \ -- 1.7.7