You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
513 lines
18 KiB
513 lines
18 KiB
diff --git a/src/df.c b/src/df.c |
|
index e28a656..fe222d9 100644 |
|
--- a/src/df.c |
|
+++ b/src/df.c |
|
@@ -45,12 +45,12 @@ |
|
|
|
/* Filled with device numbers of examined file systems to avoid |
|
duplicities in output. */ |
|
-struct devlist |
|
+static struct devlist |
|
{ |
|
dev_t dev_num; |
|
struct mount_entry *me; |
|
struct devlist *next; |
|
-}; |
|
+} *device_list; |
|
|
|
/* If true, show even file systems with zero size or |
|
uninteresting types. */ |
|
@@ -609,13 +609,10 @@ excluded_fstype (const char *fstype) |
|
me_mountdir wins. */ |
|
|
|
static void |
|
-filter_mount_list (void) |
|
+filter_mount_list (bool devices_only) |
|
{ |
|
struct mount_entry *me; |
|
|
|
- /* Store of already-processed device numbers. */ |
|
- struct devlist *devlist_head = NULL; |
|
- |
|
/* Sort all 'wanted' entries into the list devlist_head. */ |
|
for (me = mount_list; me;) |
|
{ |
|
@@ -623,41 +620,66 @@ filter_mount_list (void) |
|
struct devlist *devlist; |
|
struct mount_entry *discard_me = NULL; |
|
|
|
- if (-1 == stat (me->me_mountdir, &buf)) |
|
+ /* Avoid stating remote file systems as that may hang. |
|
+ On Linux we probably have me_dev populated from /proc/self/mountinfo, |
|
+ however we still stat() in case another device was mounted later. */ |
|
+ if ((me->me_remote && show_local_fs) |
|
+ || -1 == stat (me->me_mountdir, &buf)) |
|
{ |
|
- /* Stat failed - add ME to be able to complain about it later. */ |
|
+ /* If remote, and showing just local, add ME for filtering later. |
|
+ If stat failed; add ME to be able to complain about it later. */ |
|
buf.st_dev = me->me_dev; |
|
} |
|
else |
|
{ |
|
- /* If the device name is a real path name ... */ |
|
- if (strchr (me->me_devname, '/')) |
|
- { |
|
- /* ... 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; |
|
+ /* If we've already seen this device... */ |
|
+ for (devlist = device_list; devlist; devlist = devlist->next) |
|
+ if (devlist->dev_num == buf.st_dev) |
|
+ break; |
|
|
|
- if (devlist) |
|
+ if (devlist) |
|
+ { |
|
+ if (! print_grand_total && me->me_remote && devlist->me->me_remote |
|
+ && ! STREQ (devlist->me->me_devname, me->me_devname)) |
|
{ |
|
+ /* Don't discard remote entries with different locations, |
|
+ as these are more likely to be explicitly mounted. |
|
+ However avoid this when producing a total to give |
|
+ a more accurate value in that case. */ |
|
+ } |
|
+ else if ((strchr (me->me_devname, '/') |
|
+ /* let "real" devices with '/' in the name win. */ |
|
+ && ! strchr (devlist->me->me_devname, '/')) |
|
+ /* let a shorter mountdir win. */ |
|
+ || (strlen (devlist->me->me_mountdir) |
|
+ > strlen (me->me_mountdir)) |
|
+ /* let an entry overmounted on a new device win... */ |
|
+ || (! STREQ (devlist->me->me_devname, me->me_devname) |
|
+ /* ... but only when matching an existing mnt point, |
|
+ to avoid problematic replacement when given |
|
+ inaccurate mount lists, seen with some chroot |
|
+ environments for example. */ |
|
+ && STREQ (me->me_mountdir, |
|
+ devlist->me->me_mountdir))) |
|
+ { |
|
+ /* Discard mount entry for existing device. */ |
|
+ discard_me = devlist->me; |
|
+ devlist->me = me; |
|
+ } |
|
+ else |
|
+ { |
|
+ /* Discard mount entry currently being processed. */ |
|
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; |
|
- } |
|
} |
|
+ |
|
} |
|
} |
|
|
|
if (discard_me) |
|
{ |
|
me = me->me_next; |
|
- free_mount_entry (discard_me); |
|
+ if (! devices_only) |
|
+ free_mount_entry (discard_me); |
|
} |
|
else |
|
{ |
|
@@ -665,28 +687,49 @@ filter_mount_list (void) |
|
devlist = xmalloc (sizeof *devlist); |
|
devlist->me = me; |
|
devlist->dev_num = buf.st_dev; |
|
- devlist->next = devlist_head; |
|
- devlist_head = devlist; |
|
+ devlist->next = device_list; |
|
+ device_list = devlist; |
|
|
|
me = me->me_next; |
|
} |
|
} |
|
|
|
/* Finally rebuild the mount_list from the devlist. */ |
|
- mount_list = NULL; |
|
- while (devlist_head) |
|
+ if (! devices_only) { |
|
+ mount_list = NULL; |
|
+ while (device_list) |
|
+ { |
|
+ /* Add the mount entry. */ |
|
+ me = device_list->me; |
|
+ me->me_next = mount_list; |
|
+ mount_list = me; |
|
+ /* Free devlist entry and advance. */ |
|
+ struct devlist *devlist = device_list->next; |
|
+ free (device_list); |
|
+ device_list = devlist; |
|
+ } |
|
+ } |
|
+} |
|
+ |
|
+/* Search a mount entry list for device id DEV. |
|
+ Return the corresponding mount entry if found or NULL if not. */ |
|
+ |
|
+static struct mount_entry const * _GL_ATTRIBUTE_PURE |
|
+me_for_dev (dev_t dev) |
|
+{ |
|
+ struct devlist *dl = device_list; |
|
+ |
|
+ while (dl) |
|
{ |
|
- /* Add the mount entry. */ |
|
- me = devlist_head->me; |
|
- me->me_next = mount_list; |
|
- mount_list = me; |
|
- /* Free devlist entry and advance. */ |
|
- struct devlist *devlist = devlist_head->next; |
|
- free (devlist_head); |
|
- devlist_head = devlist; |
|
+ if (dl->dev_num == dev) |
|
+ return dl->me; |
|
+ dl = dl->next; |
|
} |
|
+ |
|
+ return NULL; |
|
} |
|
|
|
+ |
|
/* 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 |
|
@@ -856,6 +899,11 @@ get_dev (char const *disk, char const *mount_point, char const* file, |
|
if (!selected_fstype (fstype) || excluded_fstype (fstype)) |
|
return; |
|
|
|
+ /* Ignore relative MOUNT_POINTs, which are present for example |
|
+ in /proc/mounts on Linux with network namespaces. */ |
|
+ if (!force_fsu && mount_point && ! IS_ABSOLUTE_FILE_NAME (mount_point)) |
|
+ return; |
|
+ |
|
/* 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, |
|
@@ -868,9 +916,43 @@ get_dev (char const *disk, char const *mount_point, char const* file, |
|
fsu = *force_fsu; |
|
else if (get_fs_usage (stat_file, disk, &fsu)) |
|
{ |
|
- error (0, errno, "%s", quote (stat_file)); |
|
- exit_status = EXIT_FAILURE; |
|
- return; |
|
+ /* If we can't access a system provided entry due |
|
+ to it not being present (now), or due to permissions, |
|
+ just output placeholder values rather than failing. */ |
|
+ if (process_all && (errno == EACCES || errno == ENOENT)) |
|
+ { |
|
+ if (! show_all_fs) |
|
+ return; |
|
+ |
|
+ fstype = "-"; |
|
+ fsu.fsu_blocksize = fsu.fsu_blocks = fsu.fsu_bfree = |
|
+ fsu.fsu_bavail = fsu.fsu_files = fsu.fsu_ffree = UINTMAX_MAX; |
|
+ } |
|
+ else |
|
+ { |
|
+ error (0, errno, "%s", quote (stat_file)); |
|
+ exit_status = EXIT_FAILURE; |
|
+ return; |
|
+ } |
|
+ } |
|
+ else if (process_all && show_all_fs) |
|
+ { |
|
+ /* Ensure we don't output incorrect stats for over-mounted directories. |
|
+ Discard stats when the device name doesn't match. Though don't |
|
+ discard when used and current mount entries are both remote due |
|
+ to the possibility of aliased host names or exports. */ |
|
+ struct stat sb; |
|
+ if (stat (stat_file, &sb) == 0) |
|
+ { |
|
+ struct mount_entry const * dev_me = me_for_dev (sb.st_dev); |
|
+ if (dev_me && ! STREQ (dev_me->me_devname, disk) |
|
+ && (! dev_me->me_remote || ! me_remote)) |
|
+ { |
|
+ fstype = "-"; |
|
+ fsu.fsu_blocksize = fsu.fsu_blocks = fsu.fsu_bfree = |
|
+ fsu.fsu_bavail = fsu.fsu_files = fsu.fsu_ffree = UINTMAX_MAX; |
|
+ } |
|
+ } |
|
} |
|
|
|
if (fsu.fsu_blocks == 0 && !show_all_fs && !show_listed_fs) |
|
@@ -1275,8 +1357,7 @@ get_all_entries (void) |
|
{ |
|
struct mount_entry *me; |
|
|
|
- if (!show_all_fs) |
|
- filter_mount_list (); |
|
+ filter_mount_list (show_all_fs); |
|
|
|
for (me = mount_list; me; me = me->me_next) |
|
get_dev (me->me_devname, me->me_mountdir, NULL, NULL, me->me_type, |
|
@@ -1325,7 +1406,7 @@ or all file systems by default.\n\ |
|
emit_mandatory_arg_note (); |
|
|
|
fputs (_("\ |
|
- -a, --all include dummy file systems\n\ |
|
+ -a, --all include pseudo, duplicate, inaccessible file systems\n\ |
|
-B, --block-size=SIZE scale sizes by SIZE before printing them; e.g.,\n\ |
|
'-BM' prints sizes in units of 1,048,576 bytes;\n\ |
|
see SIZE format below\n\ |
|
diff --git a/doc/coreutils.texi b/doc/coreutils.texi |
|
index 942d9a1..1df1eac 100644 |
|
--- a/doc/coreutils.texi |
|
+++ b/doc/coreutils.texi |
|
@@ -11123,11 +11123,15 @@ The program accepts the following options. Also see @ref{Common options}. |
|
@itemx --all |
|
@opindex -a |
|
@opindex --all |
|
-@cindex automounter file systems |
|
@cindex ignore file systems |
|
-Include in the listing dummy file systems, which |
|
-are omitted by default. Such file systems are typically special-purpose |
|
-pseudo-file-systems, such as automounter entries. |
|
+Include in the listing dummy, duplicate, or inaccessible file systems, which |
|
+are omitted by default. Dummy file systems are typically special purpose |
|
+pseudo file systems such as @samp{/proc}, with no associated storage. |
|
+Duplicate file systems are local or remote file systems that are mounted |
|
+at separate locations in the local file hierarchy, or bind mounted locations. |
|
+Inaccessible file systems are those which are mounted but subsequently |
|
+over-mounted by another file system at that point, or otherwise inaccessible |
|
+due to permissions of the mount point etc. |
|
|
|
@item -B @var{size} |
|
@itemx --block-size=@var{size} |
|
diff --git a/tests/df/skip-duplicates.sh b/tests/df/skip-duplicates.sh |
|
index 1e94dc0..4069604 100755 |
|
--- a/tests/df/skip-duplicates.sh |
|
+++ b/tests/df/skip-duplicates.sh |
|
@@ -2,7 +2,7 @@ |
|
# 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-2013 Free Software Foundation, Inc. |
|
+# Copyright (C) 2012-2015 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 |
|
@@ -21,19 +21,73 @@ |
|
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. |
|
-cat > k.c <<'EOF' || framework_failure_ |
|
+export CU_NONROOT_FS=$(df --local --output=target 2>&1 | grep /. | head -n1) |
|
+export CU_REMOTE_FS=$(df --local --output=target 2>&1 | grep /. | |
|
+ tail -n+2 | head -n1) |
|
+ |
|
+unique_entries=1 |
|
+test -z "$CU_NONROOT_FS" || unique_entries=$(expr $unique_entries + 1) |
|
+test -z "$CU_REMOTE_FS" || unique_entries=$(expr $unique_entries + 2) |
|
+ |
|
+grep '^#define HAVE_MNTENT_H 1' $CONFIG_HEADER > /dev/null \ |
|
+ || skip_ "no mntent.h available to confirm the interface" |
|
+ |
|
+grep '^#define HAVE_GETMNTENT 1' $CONFIG_HEADER > /dev/null \ |
|
+ || skip_ "getmntent is not used on this system" |
|
+ |
|
+# Simulate an mtab file to test various cases. |
|
+cat > k.c <<EOF || framework_failure_ |
|
+#define _GNU_SOURCE |
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
+#include <errno.h> |
|
#include <mntent.h> |
|
+#include <string.h> |
|
+#include <dlfcn.h> |
|
+ |
|
+#define STREQ(a, b) (strcmp (a, b) == 0) |
|
+ |
|
+FILE* fopen(const char *path, const char *mode) |
|
+{ |
|
+ static FILE* (*fopen_func)(char const *, char const *); |
|
+ |
|
+ /* get reference to original (libc provided) fopen */ |
|
+ if (!fopen_func) |
|
+ { |
|
+ fopen_func = (FILE*(*)(char const *, char const *)) |
|
+ dlsym(RTLD_NEXT, "fopen"); |
|
+ if (!fopen_func) |
|
+ { |
|
+ fprintf (stderr, "Failed to find fopen()\n"); |
|
+ errno = ESRCH; |
|
+ return NULL; |
|
+ } |
|
+ } |
|
+ |
|
+ /* Returning ENOENT here will get read_file_system_list() |
|
+ to fall back to using getmntent() below. */ |
|
+ if (STREQ (path, "/proc/self/mountinfo")) |
|
+ { |
|
+ errno = ENOENT; |
|
+ return NULL; |
|
+ } |
|
+ else |
|
+ return fopen_func(path, mode); |
|
+} |
|
+ |
|
+#define STREQ(a, b) (strcmp (a, b) == 0) |
|
|
|
struct mntent *getmntent (FILE *fp) |
|
{ |
|
+ static char *nonroot_fs; |
|
+ static char *remote_fs; |
|
+ static int done; |
|
+ |
|
/* Prove that LD_PRELOAD works. */ |
|
- static int done = 0; |
|
if (!done) |
|
{ |
|
fclose (fopen ("x", "w")); |
|
@@ -41,50 +95,92 @@ 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="/short", .mnt_dir="/invalid/mount/dir", .mnt_opts=""}, |
|
+ {.mnt_fsname="fsname", .mnt_dir="/", .mnt_opts=""}, |
|
+ {.mnt_fsname="/fsname", .mnt_dir="/.", .mnt_opts=""}, |
|
+ {.mnt_fsname="/fsname", .mnt_dir="/", .mnt_opts=""}, |
|
+ {.mnt_fsname="virtfs", .mnt_dir="/NONROOT", .mnt_type="t1", .mnt_opts=""}, |
|
+ {.mnt_fsname="virtfs2", .mnt_dir="/NONROOT", .mnt_type="t2", .mnt_opts=""}, |
|
+ {.mnt_fsname="netns", .mnt_dir="net:[1234567]", .mnt_opts=""}, |
|
+ {.mnt_fsname="rem:ote1",.mnt_dir="/REMOTE", .mnt_opts=""}, |
|
+ {.mnt_fsname="rem:ote1",.mnt_dir="/REMOTE", .mnt_opts=""}, |
|
+ {.mnt_fsname="rem:ote2",.mnt_dir="/REMOTE", .mnt_opts=""}, |
|
}; |
|
|
|
- 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. */ |
|
+ |
|
+ remote_fs = getenv ("CU_REMOTE_FS"); |
|
+ } |
|
+ |
|
+ if (done == 1 && !getenv ("CU_TEST_DUPE_INVALID")) |
|
done++; /* skip the first entry. */ |
|
|
|
- while (done++ <= 4) |
|
+ while (done++ <= 10) |
|
{ |
|
- mntents[done-2].mnt_type = "-"; |
|
+ if (!mntents[done-2].mnt_type) |
|
+ mntents[done-2].mnt_type = "-"; |
|
+ if (!mntents[done-2].mnt_opts) |
|
+ mntents[done-2].mnt_opts = "-"; |
|
+ if (STREQ (mntents[done-2].mnt_dir, "/NONROOT")) |
|
+ mntents[done-2].mnt_dir = nonroot_fs; |
|
+ if (STREQ (mntents[done-2].mnt_dir, "/REMOTE")) |
|
+ { |
|
+ if (!remote_fs || !*remote_fs) |
|
+ continue; |
|
+ else |
|
+ mntents[done-2].mnt_dir = remote_fs; |
|
+ } |
|
return &mntents[done-2]; |
|
} |
|
+ |
|
return NULL; |
|
} |
|
EOF |
|
|
|
# Then compile/link it: |
|
-gcc --std=gnu99 -shared -fPIC -ldl -O2 k.c -o k.so \ |
|
+gcc_shared_ k.c k.so \ |
|
|| framework_failure_ 'failed to build shared library' |
|
|
|
# Test if LD_PRELOAD works: |
|
-LD_PRELOAD=./k.so df |
|
+LD_PRELOAD=$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 entries |
|
# 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; } |
|
+# consist of a header and unique entries. |
|
+LD_PRELOAD=$LD_PRELOAD:./k.so df -T >out || fail=1 |
|
+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; } |
|
+# With --total we should suppress the duplicate but separate remote file system |
|
+LD_PRELOAD=$LD_PRELOAD:./k.so df --total >out || fail=1 |
|
+test "$CU_REMOTE_FS" && elide_remote=1 || elide_remote=0 |
|
+test $(wc -l <out) -eq $(expr 2 + $unique_entries - $elide_remote) || |
|
+ { fail=1; cat out; } |
|
+ |
|
+# Ensure we don't fail when unable to stat (currently) unavailable entries |
|
+LD_PRELOAD=$LD_PRELOAD:./k.so CU_TEST_DUPE_INVALID=1 df -T >out || fail=1 |
|
+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; } |
|
+if test "$unique_entries" = 2; then |
|
+ test $(grep -c '/fsname' <out) -eq 1 || { fail=1; cat out; } |
|
+ # ... and "/fsname" with '/' as Mounted on over '/.' |
|
+ test $(grep -cF '/.' <out) -eq 0 || { fail=1; cat out; } |
|
+fi |
|
+ |
|
+# df should use the last seen devname (mnt_fsname) and devtype (mnt_type) |
|
+test $(grep -c 'virtfs2.*t2' <out) -eq 1 || { 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; } |
|
+LD_PRELOAD=$LD_PRELOAD:./k.so df -a >out || fail=1 |
|
+total_fs=6; test "$CU_REMOTE_FS" && total_fs=$(expr $total_fs + 3) |
|
+test $(wc -l <out) -eq $total_fs || { fail=1; cat out; } |
|
+# Ensure placeholder "-" values used for the eclipsed "virtfs" |
|
+test $(grep -c 'virtfs *-' <out) -eq 1 || { fail=1; cat out; } |
|
|
|
# Ensure that filtering duplicates does not affect |
|
# argument processing (now without the fake getmntent()). |
|
diff --git a/init.cfg b/init.cfg |
|
index 360d4da..16f9813 100644 |
|
--- a/init.cfg |
|
+++ b/init.cfg |
|
@@ -472,6 +472,18 @@ require_sparse_support_() |
|
fi |
|
} |
|
|
|
+# Compile a shared lib using the GCC options for doing so. |
|
+# Pass input and output file as parameters respectively. |
|
+# Any other optional parmeters are passed to $CC. |
|
+gcc_shared_() |
|
+{ |
|
+ local in=$1 |
|
+ local out=$2 |
|
+ shift 2 || return 1 |
|
+ |
|
+ $CC -Wall -shared --std=gnu99 -fPIC -O2 $* "$in" -o "$out" -ldl |
|
+} |
|
+ |
|
# There are a myriad of ways to build shared libs, |
|
# so we only consider running tests requiring shared libs, |
|
# on platforms that support building them as follows.
|
|
|