Project

General

Profile

Actions

Bug #12144

open

Bug in ``df -t`` filtering if two filesystems use the same mountpoint

Added by Jim Pingle over 2 years ago. Updated over 2 years ago.

Status:
In Progress
Priority:
Normal
Assignee:
Category:
Operating System
Target version:
-
Start date:
07/19/2021
Due date:
% Done:

0%

Estimated time:
Plus Target Version:
Release Notes:
Default
Affected Version:
2.5.2
Affected Architecture:

Description

When using RAM disks for /tmp and /var on a ZFS installation, the System Information widget on the Dashboard appears to show multiple copies of /var instead of the expected ZFS datasets mounted under /var.

This is simple to reproduce:

1. Install fresh with ZFS
2. Enable RAM disks for /tmp and /var under System > Advanced, Misc with the default values
3. Reboot

This appears to be due to a bug in df -t <type>.

The full output of df is OK:

: df -Th
Filesystem            Type     Size    Used   Avail Capacity  Mounted on
pfSense/ROOT/default  zfs       29G    670M     28G     2%    /
devfs                 devfs    1.0K    1.0K      0B   100%    /dev
pfSense/cf            zfs       28G     96K     28G     0%    /cf
pfSense/var           zfs       28G    3.2M     28G     0%    /var
pfSense/home          zfs       28G     96K     28G     0%    /home
pfSense               zfs       28G     96K     28G     0%    /pfSense
pfSense/cf/conf       zfs       28G    616K     28G     0%    /cf/conf
pfSense/var/log       zfs       28G    188K     28G     0%    /var/log
pfSense/var/empty     zfs       28G     96K     28G     0%    /var/empty
pfSense/var/cache     zfs       28G     96K     28G     0%    /var/cache
pfSense/var/db        zfs       28G    1.1M     28G     0%    /var/db
pfSense/var/tmp       zfs       28G    104K     28G     0%    /var/tmp
/dev/md0              ufs       38M     76K     35M     0%    /tmp
/dev/md1              ufs       58M    9.5M     44M    18%    /var
devfs                 devfs    1.0K    1.0K      0B   100%    /var/dhcpd/dev

Filtering to only UFS shows the expected entries for /tmp and /var:

: df -Tht ufs
Filesystem  Type    Size    Used   Avail Capacity  Mounted on
/dev/md0    ufs      38M     76K     35M     0%    /tmp
/dev/md1    ufs      58M    9.5M     44M    18%    /var

Filtering for ZFS shows multiple copies of /var and not the datasets:

: df -Tht zfs
Filesystem            Type    Size    Used   Avail Capacity  Mounted on
pfSense/ROOT/default  zfs      29G    670M     28G     2%    /
pfSense/cf            zfs      28G     96K     28G     0%    /cf
/dev/md1              ufs      58M    9.5M     44M    18%    /var
pfSense/home          zfs      28G     96K     28G     0%    /home
pfSense               zfs      28G     96K     28G     0%    /pfSense
pfSense/cf/conf       zfs      28G    616K     28G     0%    /cf/conf
/dev/md1              ufs      58M    9.5M     44M    18%    /var
/dev/md1              ufs      58M    9.5M     44M    18%    /var
/dev/md1              ufs      58M    9.5M     44M    18%    /var
/dev/md1              ufs      58M    9.5M     44M    18%    /var
/dev/md1              ufs      58M    9.5M     44M    18%    /var

N.B. The /var lines show their type as ufs which doesn't match what we asked for

This appears to be an upstream bug in df but it would be nice if we could work out a fix before the next release, either on our own or by coordinating with the maintainer of df in FreeBSD.

Actions #1

Updated by Renato Botelho over 2 years ago

  • Status changed from New to In Progress
  • Assignee set to Mateusz Guzik

Mateusz reproduced the issue on stock FreeBSD and will work on a fix

Actions #2

Updated by Mateusz Guzik over 2 years ago

This comes from a deficiency in the API exposed to userspace.

Consider the following:

tmpfs on /tmp (tmpfs, local)
/dev/md0 on /tmp (ufs, local)
tmpfs on /tmp/test (tmpfs, local)

df sorts this out in 2 phases:
1. mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);

This gets ALL filesystems, but the MNT_NOWAIT flag tells the kernel to not issue any I/O.

2. mntsize = regetmntinfo(&mntbuf, mntsize, vfslist);

This walks the list and issues an additional statfs(2) for filesystems of interest by passing the mount point as an identifier.

In the example above there are 2 filesystems with the same mount point and the kernel exports the second one. Should there be 3 filesystems stacked up, there would be no way to ask for the middle one.

Thus if you filter for tmpfs, you end up getting ufs as the first result.

In other words the interface needs to be fixed.

In the meantime as a hack the above can be reworked -- getmntinfo can be called with MNT_WAIT and then the result filtered out. The downside which makes it not committable upstream is the updates requested from all mount points, even ones of no interest.

Mount points carry "fs id" which in principle could be filtered on, but getting the value requires privileges and there is still no syscall to filter on it.

Another work around would be to zfs set mountpoint=none on the problematic dataset.

Actions #3

Updated by Mateusz Guzik over 2 years ago

The following is the hack which does the trick for me fwiw:

diff --git a/bin/df/df.c b/bin/df/df.c
index a0d43fca5789..1a11fae848f2 100644
--- a/bin/df/df.c
+++ b/bin/df/df.c
@@ -88,7 +88,7 @@ static void     prthuman(const struct statfs *, int64_t);
 static void      prthumanval(const char *, int64_t);
 static intmax_t          fsbtoblk(int64_t, uint64_t, u_long);
 static void      prtstat(struct statfs *, struct maxwidths *);
-static size_t    regetmntinfo(struct statfs **, long, const char **);
+static size_t    filtermntinfo(struct statfs **, long, const char **);
 static void      update_maxwidths(struct maxwidths *, const struct statfs *);
 static void      usage(void);

@@ -213,8 +213,8 @@ main(int argc, char *argv[])
        rv = 0;
        if (!*argv) {
                /* everything (modulo -t) */
-               mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
-               mntsize = regetmntinfo(&mntbuf, mntsize, vfslist);
+               mntsize = getmntinfo(&mntbuf, MNT_WAIT);
+               mntsize = filtermntinfo(&mntbuf, mntsize, vfslist);
        } else {
                /* just the filesystems specified on the command line */
                mntbuf = malloc(argc * sizeof(*mntbuf));
@@ -316,35 +316,24 @@ getmntpt(const char *name)
  * current (not cached) info.  Returns the new count of valid statfs bufs.
  */
 static size_t
-regetmntinfo(struct statfs **mntbufp, long mntsize, const char **vfslist)
+filtermntinfo(struct statfs **mntbufp, long mntsize, const char **vfslist)
 {
-       int error, i, j;
+       int i;
        struct statfs *mntbuf;

        if (vfslist == NULL)
                return (nflag ? mntsize : getmntinfo(mntbufp, MNT_WAIT));

        mntbuf = *mntbufp;
-       for (j = 0, i = 0; i < mntsize; i++) {
-               if (checkvfsname(mntbuf[i].f_fstypename, vfslist))
+
+       for (i = 0; i < mntsize; i++) {
+               if (checkvfsname(mntbuf[i].f_fstypename, vfslist)) {
+                       mntbuf[i].f_flags |= MNT_IGNORE;
                        continue;
-               /*
-                * XXX statfs(2) can fail for various reasons. It may be
-                * possible that the user does not have access to the
-                * pathname, if this happens, we will fall back on
-                * "stale" filesystem statistics.
-                */
-               error = statfs(mntbuf[i].f_mntonname, &mntbuf[j]);
-               if (nflag || error < 0)
-                       if (i != j) {
-                               if (error < 0)
-                                       xo_warnx("%s stats possibly stale",
-                                           mntbuf[i].f_mntonname);
-                               mntbuf[j] = mntbuf[i];
-                       }
-               j++;
+               }
        }
-       return (j);
+
+       return (mntsize);
 }

 static void

Actions #4

Updated by Jim Pingle over 2 years ago

  • Subject changed from System Information Dashboard widget shows incorrect information with ZFS and RAM Disks enabled to Bug in ``df -t`` filtering if two filesystems use the same mountpoint
  • Target version deleted (2.6.0)
  • Plus Target Version deleted (21.09)

The new Disks widget in #12349 uses df in a different way and doesn't hit this problem, thus it is no longer a current need. Though it would still be nice to see it fixed in FreeBSD, we don't appear to have any more remaining uses of df that use -t, so nothing else on pfSense could trigger the bug.

I updated the subject to reflect that it's no longer a widget issue.

Actions

Also available in: Atom PDF