]> git.ipfire.org Git - thirdparty/coreutils.git/commitdiff
df: also deduplicate virtual file systems
authorPádraig Brady <P@draigBrady.com>
Sat, 25 Jan 2014 01:14:29 +0000 (01:14 +0000)
committerPádraig Brady <P@draigBrady.com>
Tue, 13 May 2014 22:23:30 +0000 (23:23 +0100)
* src/df.c (filter_mountlist): Remove the constraint that
a '/' needs to be in the device name for a mount entry to
be considered for deduplication.  Virtual file systems also
have storage associated with them (like tmpfs for example),
and thus need to be deduplicated since they will be shown
in the default df output and subject to --total processing also.
* test/df/skip-duplicates.sh: Add a test to ensure we deduplicate
all entries, even for virtual file systems.  Also avoid possible
length operations on many remote file systems in the initial
check of df operation.  Also avoid the assumption that "/root"
is on the same file system as "/".
* NEWS: Mention the change in behavior.

NEWS
src/df.c
tests/df/skip-duplicates.sh

diff --git a/NEWS b/NEWS
index 4efd60d2ef0e81616cca6548326e9ba37ed4ee5b..c204b680dca9781ccd846dcf997e2ef06c03265e 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -44,6 +44,8 @@ GNU coreutils NEWS                                    -*- outline -*-
 
   [These dd bugs were present in "the beginning".]
 
+  df now correctly elides duplicates for virtual file systems like tmpfs.
+
   head --bytes=-N and --lines=-N now handles devices more
   consistently, not ignoring data from virtual devices like /dev/zero,
   or on BSD systems data from tty devices.
index e7639434e60bacbba33764c0119a4e9f77308eed..2b5a54e4a1beaf3f5ec4adadc7177cfd99858c85 100644 (file)
--- a/src/df.c
+++ b/src/df.c
@@ -630,26 +630,23 @@ filter_mount_list (void)
         }
       else
         {
-          /* If the device name is a real path name ...  */
-          if (strchr (me->me_devname, '/'))
+          /* If we've already seen this device...  */
+          for (devlist = devlist_head; devlist; devlist = devlist->next)
+            if (devlist->dev_num == buf.st_dev)
+              break;
+
+          if (devlist)
             {
-              /* ... try to find its device number in the devlist.  */
-              for (devlist = devlist_head; devlist; devlist = devlist->next)
-                if (devlist->dev_num == buf.st_dev)
-                  break;
+              discard_me = me;
 
-              if (devlist)
+              /* ...let the shorter mountdir win.  */
+              if ((strchr (me->me_devname, '/')
+                   && ! strchr (devlist->me->me_devname, '/'))
+                  || (strlen (devlist->me->me_mountdir)
+                      > strlen (me->me_mountdir)))
                 {
-                  discard_me = me;
-
-                  /* Let the shorter mountdir win.  */
-                  if (! strchr (devlist->me->me_devname, '/')
-                      || (strlen (devlist->me->me_mountdir)
-                         > strlen (me->me_mountdir)))
-                    {
-                      discard_me = devlist->me;
-                      devlist->me = me;
-                    }
+                  discard_me = devlist->me;
+                  devlist->me = me;
                 }
             }
         }
index 266520aa657f13da3e47221c7c0dd840680d135a..d872f27fc79489a9ccbc33fd7c57458286f3a534 100755 (executable)
 print_ver_ df
 require_gcc_shared_
 
-df || skip_ "df fails"
+# We use --local here so as to not activate
+# potentially very many remote mounts.
+df --local || skip_ "df fails"
 
-# Simulate an mtab file with two entries of the same device number.
-# Also add entries with unstatable mount dirs to ensure that's handled.
+export CU_NONROOT_FS=$(df --local --output=target 2>&1 | grep /. | head -n1)
+test -z "$CU_NONROOT_FS" && unique_entries=1 || unique_entries=2
+
+# Simulate an mtab file to test various cases.
 cat > k.c <<'EOF' || framework_failure_
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 #include <mntent.h>
 
+#define STREQ(a, b) (strcmp (a, b) == 0)
+
 struct mntent *getmntent (FILE *fp)
 {
+  static char *nonroot_fs;
+  static int done;
+
   /* Prove that LD_PRELOAD works. */
-  static int done = 0;
   if (!done)
     {
       fclose (fopen ("x", "w"));
@@ -43,18 +52,30 @@ struct mntent *getmntent (FILE *fp)
   static struct mntent mntents[] = {
     {.mnt_fsname="/short",  .mnt_dir="/invalid/mount/dir"},
     {.mnt_fsname="fsname",  .mnt_dir="/",},
-    {.mnt_fsname="/fsname", .mnt_dir="/root"},
+    {.mnt_fsname="/fsname", .mnt_dir="/."},
     {.mnt_fsname="/fsname", .mnt_dir="/"},
+    {.mnt_fsname="virtfs",  .mnt_dir="/NONROOT"},
+    {.mnt_fsname="virtfs",  .mnt_dir="/NONROOT"},
   };
 
-  if (!getenv ("CU_TEST_DUPE_INVALID") && done == 1)
+  if (done == 1)
+    {
+      nonroot_fs = getenv ("CU_NONROOT_FS");
+      if (!nonroot_fs || !*nonroot_fs)
+        nonroot_fs = "/"; /* merge into / entries.  */
+    }
+
+  if (done == 1 && !getenv ("CU_TEST_DUPE_INVALID"))
     done++;  /* skip the first entry.  */
 
-  while (done++ <= 4)
+  while (done++ <= 6)
     {
       mntents[done-2].mnt_type = "-";
+      if (STREQ (mntents[done-2].mnt_dir, "/NONROOT"))
+        mntents[done-2].mnt_dir = nonroot_fs;
       return &mntents[done-2];
     }
+
   return NULL;
 }
 EOF
@@ -69,22 +90,22 @@ test -f x || skip_ "internal test failure: maybe LD_PRELOAD doesn't work?"
 
 # The fake mtab file should only contain entries
 # having the same device number; thus the output should
-# consist of a header and one entry.
+# consist of a header and unique entries.
 LD_PRELOAD=./k.so df >out || fail=1
-test $(wc -l <out) -eq 2 || { fail=1; cat out; }
+test $(wc -l <out) -eq $(expr 1 + $unique_entries) || { fail=1; cat out; }
 
 # Ensure we fail when unable to stat invalid entries
 LD_PRELOAD=./k.so CU_TEST_DUPE_INVALID=1 df >out && fail=1
-test $(wc -l <out) -eq 2 || { fail=1; cat out; }
+test $(wc -l <out) -eq $(expr 1 + $unique_entries) || { fail=1; cat out; }
 
 # df should also prefer "/fsname" over "fsname"
 test $(grep -c '/fsname' <out) -eq 1 || { fail=1; cat out; }
-# ... and "/fsname" with '/' as Mounted on over '/root'
-test $(grep -c '/root' <out) -eq 0 || { fail=1; cat out; }
+# ... and "/fsname" with '/' as Mounted on over '/.'
+test $(grep -cF '/.' <out) -eq 0 || { 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 4 || { fail=1; cat out; }
+test $(wc -l <out) -eq 6 || { fail=1; cat out; }
 
 # Ensure that filtering duplicates does not affect
 # argument processing (now without the fake getmntent()).