diff -urNp coreutils-8.12-orig/doc/coreutils.texi coreutils-8.12/doc/coreutils.texi
--- coreutils-8.12-orig/doc/coreutils.texi 2011-04-25 10:55:27.000000000 +0200
+++ coreutils-8.12/doc/coreutils.texi 2012-12-12 13:28:29.588025346 +0100
@@ -10379,6 +10379,14 @@ Normally the disk space is printed in un
1024 bytes, but this can be overridden (@pxref{Block size}).
Non-integer quantities are rounded up to the next higher unit.
+For bind mounts and without arguments, @command{df} only outputs the statistics
+for the first occurence of that device in the list of file systems (@var{mtab}),
+i.e., it hides duplicate entries, unless the @option{-a} option is specified.
+
+By default, @command{df} omits the early-boot pseudo file system type
+@samp{rootfs}, unless the @option{-a} option is specified or that file system
+type is explicitly to be included by using the @option{-t} option.
+
@cindex disk device file
@cindex device file, disk
If an argument @var{file} is a disk device file containing a mounted
diff -urNp coreutils-8.12-orig/src/df.c coreutils-8.12/src/df.c
--- coreutils-8.12-orig/src/df.c 2011-04-25 11:45:49.000000000 +0200
+++ coreutils-8.12/src/df.c 2012-12-12 13:28:29.593156345 +0100
@@ -25,6 +25,7 @@
#include <assert.h>
#include "system.h"
+#include "canonicalize.h"
#include "error.h"
#include "fsusage.h"
#include "human.h"
@@ -45,6 +46,17 @@
/* If true, show inode information. */
static bool inode_format;
+/* Filled with device numbers of examined file systems to avoid
+ duplicities in output. */
+struct devlist
+{
+ dev_t dev_num;
+ struct devlist *next;
+};
+
+/* Store of already-processed device numbers. */
+static struct devlist *devlist_head;
+
/* If true, show even file systems with zero size or
uninteresting types. */
static bool show_all_fs;
@@ -56,6 +68,12 @@ static bool show_local_fs;
command line argument -- even if it's a dummy (automounter) entry. */
static bool show_listed_fs;
+/* If true, include rootfs in the output. */
+static bool show_rootfs;
+
+/* The literal name of the initial root file system. */
+static char const *ROOTFS = "rootfs";
+
/* Human-readable options for output. */
static int human_output_opts;
@@ -349,6 +367,29 @@ excluded_fstype (const char *fstype)
return false;
}
+/* Check if the device was already examined. */
+
+static bool
+dev_examined (char const *mount_dir, char const *devname)
+{
+ struct stat buf;
+ if (-1 == stat (mount_dir, &buf))
+ return false;
+
+ struct devlist *devlist = devlist_head;
+ for ( ; devlist; devlist = devlist->next)
+ if (devlist->dev_num == buf.st_dev)
+ return true;
+
+ /* Add the device number to the global list devlist. */
+ devlist = xmalloc (sizeof *devlist);
+ devlist->dev_num = buf.st_dev;
+ devlist->next = devlist_head;
+ devlist_head = devlist;
+
+ return false;
+}
+
/* Return true if N is a known integer value. On many file systems,
UINTMAX_MAX represents an unknown value; on AIX, UINTMAX_MAX - 1
represents unknown. Use a rule that works on AIX file systems, and
@@ -417,6 +458,17 @@ add_uint_with_neg_flag (uintmax_t *dest,
*dest = -*dest;
}
+/* Return true if S ends in a string that may be a 36-byte UUID,
+ i.e., of the form HHHHHHHH-HHHH-HHHH-HHHH-HHHHHHHHHHHH, where
+ each H is an upper or lower case hexadecimal digit. */
+static bool _GL_ATTRIBUTE_PURE
+has_uuid_suffix (char const *s)
+{
+ size_t len = strlen (s);
+ return (36 < len
+ && strspn (s + len - 36, "-0123456789abcdefABCDEF") == 36);
+}
+
/* Optain a space listing for the disk device with absolute file name DISK.
If MOUNT_POINT is non-NULL, it is the name of the root of the
file system on DISK.
@@ -428,13 +480,16 @@ add_uint_with_neg_flag (uintmax_t *dest,
If FSTYPE is non-NULL, it is the type of the file system on DISK.
If MOUNT_POINT is non-NULL, then DISK may be NULL -- certain systems may
not be able to produce statistics in this case.
- ME_DUMMY and ME_REMOTE are the mount entry flags. */
+ ME_DUMMY and ME_REMOTE are the mount entry flags.
+ Caller must set PROCESS_ALL to true when iterating over all entries, as
+ when df is invoked with no non-option argument. See below for details. */
static void
get_dev (char const *disk, char const *mount_point,
char const *stat_file, char const *fstype,
bool me_dummy, bool me_remote,
- const struct fs_usage *force_fsu)
+ const struct fs_usage *force_fsu,
+ bool process_all)
{
struct fs_usage fsu;
char buf[LONGEST_HUMAN_READABLE + 2];
@@ -459,6 +514,15 @@ get_dev (char const *disk, char const *m
if (!selected_fstype (fstype) || excluded_fstype (fstype))
return;
+ if (process_all && !show_all_fs && !show_listed_fs)
+ {
+ /* No arguments nor "df -a", then check if df has to ... */
+ if (!show_rootfs && STREQ (disk, ROOTFS))
+ return; /* ... skip rootfs: (unless -trootfs is given. */
+ if (dev_examined (mount_point, disk))
+ return; /* ... skip duplicate entries (bind mounts). */
+ }
+
/* If MOUNT_POINT is NULL, then the file system is not mounted, and this
program reports on the file system that the special file is on.
It would be better to report on the unmounted file system,
@@ -488,6 +552,24 @@ get_dev (char const *disk, char const *m
if (! disk)
disk = "-"; /* unknown */
+
+ char *dev_name = xstrdup (disk);
+ char *resolved_dev;
+
+ /* On some systems, dev_name is a long-named symlink like
+ /dev/disk/by-uuid/828fc648-9f30-43d8-a0b1-f7196a2edb66 pointing to a
+ much shorter and more useful name like /dev/sda1. It may also look
+ like /dev/mapper/luks-828fc648-9f30-43d8-a0b1-f7196a2edb66 and point to
+ /dev/dm-0. When process_all is true and dev_name is a symlink whose
+ name ends with a UUID use the resolved name instead. */
+ if (process_all
+ && has_uuid_suffix (dev_name)
+ && (resolved_dev = canonicalize_filename_mode (dev_name, CAN_EXISTING)))
+ {
+ free (dev_name);
+ dev_name = resolved_dev;
+ }
+
if (! fstype)
fstype = "-"; /* unknown */
@@ -537,7 +619,7 @@ get_dev (char const *disk, char const *m
switch (field)
{
case DEV_FIELD:
- cell = xstrdup (disk);
+ cell = dev_name;
break;
case TYPE_FIELD:
@@ -648,7 +730,7 @@ get_disk (char const *disk)
{
get_dev (best_match->me_devname, best_match->me_mountdir, NULL,
best_match->me_type, best_match->me_dummy,
- best_match->me_remote, NULL);
+ best_match->me_remote, NULL, false);
return true;
}
@@ -734,7 +816,7 @@ get_point (const char *point, const stru
if (best_match)
get_dev (best_match->me_devname, best_match->me_mountdir, point,
best_match->me_type, best_match->me_dummy, best_match->me_remote,
- NULL);
+ NULL, false);
else
{
/* We couldn't find the mount entry corresponding to POINT. Go ahead and
@@ -745,7 +827,7 @@ get_point (const char *point, const stru
char *mp = find_mount_point (point, statp);
if (mp)
{
- get_dev (NULL, mp, NULL, NULL, false, false, NULL);
+ get_dev (NULL, mp, NULL, NULL, false, false, NULL, false);
free (mp);
}
}
@@ -774,7 +856,7 @@ get_all_entries (void)
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);
+ me->me_dummy, me->me_remote, NULL, true);
}
/* Add FSTYPE to the list of file system types to display. */
@@ -940,6 +1022,7 @@ main (int argc, char **argv)
/* Accept -F as a synonym for -t for compatibility with Solaris. */
case 't':
add_fs_type (optarg);
+ show_rootfs = selected_fstype (ROOTFS);
break;
case 'v': /* For SysV compatibility. */
@@ -1066,13 +1149,21 @@ main (int argc, char **argv)
{
if (inode_format)
grand_fsu.fsu_blocks = 1;
- get_dev ("total", NULL, NULL, NULL, false, false, &grand_fsu);
+ get_dev ("total", NULL, NULL, NULL, false, false, &grand_fsu, false);
}
print_table ();
if (! file_systems_processed)
error (EXIT_FAILURE, 0, _("no file systems processed"));
+ IF_LINT (
+ while (devlist_head)
+ {
+ struct devlist *devlist = devlist_head->next;
+ free (devlist_head);
+ devlist_head = devlist;
+ }
+ );
exit (exit_status);
}
diff -urNp coreutils-8.12-orig/tests/df/skip-duplicates coreutils-8.12/tests/df/skip-duplicates
--- coreutils-8.12-orig/tests/df/skip-duplicates 1970-01-01 01:00:00.000000000 +0100
+++ coreutils-8.12/tests/df/skip-duplicates 2012-12-12 13:28:29.590144032 +0100
@@ -0,0 +1,77 @@
+#!/bin/sh
+# Test df's behavior when the mount list contains duplicate 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 <http://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/init.sh"; path_prepend_ ../src
+print_ver_ df
+
+df || skip_ "df fails"
+
+# Simulate an mtab file with two entries of the same device number.
+cat > k.c <<'EOF' || framework_failure_
+#include <stdio.h>
+#include <mntent.h>
+
+struct mntent *getmntent (FILE *fp)
+{
+ /* Prove that LD_PRELOAD works. */
+ static int done = 0;
+ if (!done)
+ {
+ fclose (fopen ("x", "w"));
+ ++done;
+ }
+
+ 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.
+LD_PRELOAD=./k.so df >out || fail=1
+test $(wc -l <out) -eq 2 || { fail=1; cat out; }
+
+# Ensure that filtering duplicates does not affect -a processing.
+LD_PRELOAD=./k.so df -a >out || fail=1
+test $(wc -l <out) -eq 3 || { fail=1; cat out; }
+
+# Ensure that filtering duplcates does not affect
+# argument processing (now without the fake getmntent()).
+df '.' '.' >out || fail=1
+test $(wc -l <out) -eq 3 || { fail=1; cat out; }
+
+Exit $fail
diff -urNp coreutils-8.12-orig/tests/df/skip-rootfs coreutils-8.12/tests/df/skip-rootfs
--- coreutils-8.12-orig/tests/df/skip-rootfs 1970-01-01 01:00:00.000000000 +0100
+++ coreutils-8.12/tests/df/skip-rootfs 2012-12-12 13:28:29.591024467 +0100
@@ -0,0 +1,46 @@
+#!/bin/sh
+# Test df's behavior for skipping the pseudo "rootfs" file system.
+
+# 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 <http://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/init.sh"; path_prepend_ ../src
+print_ver_ df
+
+df || skip_ "df fails"
+
+# Verify that rootfs is in mtab (and shown when the -a option is specified).
+df -a >out || fail=1
+grep '^rootfs' out || skip_ "no rootfs in mtab"
+
+# Ensure that rootfs is supressed when no options is specified.
+df >out || fail=1
+grep '^rootfs' out && { fail=1; cat out; }
+
+# Ensure that the rootfs is shown when explicitly specifying "-t rootfs".
+df -t rootfs >out || fail=1
+grep '^rootfs' out || { fail=1; cat out; }
+
+# Ensure that the rootfs is shown when explicitly specifying "-t rootfs",
+# even when the -a option is specified.
+df -t rootfs -a >out || fail=1
+grep '^rootfs' out || { fail=1; cat out; }
+
+# Ensure that the rootfs is omitted in all_fs mode when it is explicitly
+# black-listed.
+df -a -x rootfs >out || fail=1
+grep '^rootfs' out && { fail=1; cat out; }
+
+Exit $fail
diff -urNp coreutils-8.12-orig/tests/Makefile.am coreutils-8.12/tests/Makefile.am
--- coreutils-8.12-orig/tests/Makefile.am 2011-04-25 11:45:27.000000000 +0200
+++ coreutils-8.12/tests/Makefile.am 2012-12-12 13:28:29.592155734 +0100
@@ -352,6 +352,8 @@ TESTS = \
cp/symlink-slash \
cp/thru-dangling \
df/unreadable \
+ df/skip-duplicates \
+ df/skip-rootfs \
dd/direct \
dd/misc \
dd/nocache \