kernel - Fix df and unmount of bad NFS volumes
authorMatthew Dillon <dillon@apollo.backplane.com>
Wed, 27 Mar 2019 02:33:03 +0000 (19:33 -0700)
committerMatthew Dillon <dillon@apollo.backplane.com>
Wed, 27 Mar 2019 02:33:03 +0000 (19:33 -0700)
* Fix an issue where a bad filesystem would not be listed in the
  df output due to the vfsstat refresh failing.  Allow vfsstat refreshes
  to fail.

* Fix an issue where a NFS mount that goes bad could not be unmounted
  due to the path lookup failing with EBADRPC, ESTALE, or EIO.  These
  may now be unmounted with umount -f ...

sys/kern/vfs_nlookup.c
sys/kern/vfs_syscalls.c
sys/sys/nlookup.h

index c3c6fd5..2b0b2ba 100644 (file)
 static int naccess(struct nchandle *nch, int vmode, struct ucred *cred,
                int *stickyp);
 
+/*
+ * unmount operations flag NLC_IGNBADDIR in order to allow the
+ * umount to successfully issue a nlookup() on the path in order
+ * to extract the mount point.  Allow certain errors through.
+ */
+static __inline
+int
+keeperror(struct nlookupdata *nd, int error)
+{
+       if (error) {
+               if ((nd->nl_flags & NLC_IGNBADDIR) == 0 ||
+                  (error != EIO && error != EBADRPC && error != ESTALE)) {
+                       return 1;
+               }
+       }
+       return 0;
+}
+
 /*
  * Initialize a nlookup() structure, early error return for copyin faults
  * or a degenerate empty string (which is not allowed).
@@ -575,8 +593,11 @@ nlookup_start:
            error = naccess(&nd->nl_nch, NLC_EXEC, nd->nl_cred, NULL);
        else
            error = naccess(&nd->nl_nch, NLC_EXEC, nd->nl_cred, &dflags);
-       if (error)
-           break;
+       if (error) {
+           if (keeperror(nd, error))
+                   break;
+           error = 0;
+       }
 
        /*
         * Extract the next (or last) path component.  Path components are
@@ -734,6 +755,10 @@ nlookup_start:
                cache_lock_maybe_shared(&par, wantsexcllock(nd, ptr));
                error = naccess(&par, 0, nd->nl_cred, &dflags);
                cache_put(&par);
+               if (error) {
+                   if (!keeperror(nd, error))
+                           error = 0;
+               }
            }
        }
 
@@ -856,7 +881,7 @@ nlookup_start:
             */
            continue;
        }
-
+       
        /*
         * If the element is a directory and we are crossing a mount point,
         * Locate the mount.
@@ -897,19 +922,21 @@ again:
                error = VFS_ROOT(mp, &tdp);
                vfs_unbusy(mp);
                vfs_do_busy = 0;
-               if (error) {
+               if (keeperror(nd, error)) {
                    cache_dropmount(mp);
                    break;
                }
-               cache_setvp(&nch, tdp);
-               vput(tdp);
+               if (error == 0) {
+                   cache_setvp(&nch, tdp);
+                   vput(tdp);
+               }
            }
            if (vfs_do_busy)
                vfs_unbusy(mp);
            cache_dropmount(mp);
        }
 
-       if (error) {
+       if (keeperror(nd, error)) {
            cache_put(&nch);
 double_break:
            break;
@@ -972,7 +999,7 @@ double_break:
        if (nch.ncp->nc_vp && (nd->nl_flags & NLC_ALLCHKS)) {
            error = naccess(&nch, nd->nl_flags | dflags,
                            nd->nl_cred, NULL);
-           if (error) {
+           if (keeperror(nd, error)) {
                cache_put(&nch);
                break;
            }
@@ -987,7 +1014,7 @@ double_break:
                cache_lock(&nd->nl_nch);
                error = cache_vref(&nd->nl_nch, nd->nl_cred, &nd->nl_dvp);
                cache_unlock(&nd->nl_nch);
-               if (error) {
+               if (keeperror(nd, error)) {
                        kprintf("NLC_REFDVP: Cannot ref dvp of %p\n", nch.ncp);
                        cache_put(&nch);
                        break;
index 1ec320d..8ebab8b 100644 (file)
@@ -605,7 +605,8 @@ sys_unmount(struct unmount_args *uap)
        if (usermount == 0 && (error = priv_check(td, PRIV_ROOT)))
                goto done;
 
-       error = nlookup_init(&nd, uap->path, UIO_USERSPACE, NLC_FOLLOW);
+       error = nlookup_init(&nd, uap->path, UIO_USERSPACE,
+                            NLC_FOLLOW | NLC_IGNBADDIR);
        if (error == 0)
                error = nlookup(&nd);
        if (error)
@@ -864,9 +865,9 @@ dounmount(struct mount *mp, int flags, int halting)
                        error = VFS_UNMOUNT(mp, flags);
                } else {
                        error = VFS_SYNC(mp, MNT_WAIT);
-                       if ((error == 0) ||
-                           (error == EOPNOTSUPP) || /* No sync */
-                           (flags & MNT_FORCE)) {
+                       if (error == 0 ||               /* no error */
+                           error == EOPNOTSUPP ||      /* no sync avail */
+                           (flags & MNT_FORCE)) {      /* force anyway */
                                error = VFS_UNMOUNT(mp, flags);
                        }
                }
@@ -1284,8 +1285,14 @@ kern_statfs(struct nlookupdata *nd, struct statfs *buf)
                return (error);
        mp = nd->nl_nch.mount;
        sp = &mp->mnt_stat;
-       if ((error = VFS_STATFS(mp, sp, nd->nl_cred)) != 0)
-               return (error);
+
+       /*
+        * Ignore refresh error, user should have visibility.
+        * This can happen if a NFS mount goes bad (e.g. server
+        * revokes perms or goes down).
+        */
+       error = VFS_STATFS(mp, sp, nd->nl_cred);
+       /* ignore error */
 
        error = mount_path(p, mp, &fullpath, &freepath);
        if (error)
@@ -1353,9 +1360,14 @@ kern_fstatfs(int fd, struct statfs *buf)
                error = EINVAL;
                goto done;
        }
+
+       /*
+        * Ignore refresh error, user should have visibility.
+        * This can happen if a NFS mount goes bad (e.g. server
+        * revokes perms or goes down).
+        */
        sp = &mp->mnt_stat;
-       if ((error = VFS_STATFS(mp, sp, fp->f_cred)) != 0)
-               goto done;
+       error = VFS_STATFS(mp, sp, fp->f_cred);
 
        if ((error = mount_path(p, mp, &fullpath, &freepath)) != 0)
                goto done;
@@ -1551,11 +1563,15 @@ getfsstat_callback(struct mount *mp, void *data)
                 * If MNT_NOWAIT or MNT_LAZY is specified, do not
                 * refresh the fsstat cache. MNT_NOWAIT or MNT_LAZY
                 * overrides MNT_WAIT.
+                *
+                * Ignore refresh error, user should have visibility.
+                * This can happen if a NFS mount goes bad (e.g. server
+                * revokes perms or goes down).
                 */
                if (((info->flags & (MNT_LAZY|MNT_NOWAIT)) == 0 ||
                    (info->flags & MNT_WAIT)) &&
                    (error = VFS_STATFS(mp, sp, info->td->td_ucred))) {
-                       return(0);
+                       /* ignore error */
                }
                sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK;
 
@@ -1642,18 +1658,22 @@ getvfsstat_callback(struct mount *mp, void *data)
                 * If MNT_NOWAIT or MNT_LAZY is specified, do not
                 * refresh the fsstat cache. MNT_NOWAIT or MNT_LAZY
                 * overrides MNT_WAIT.
+                *
+                * Ignore refresh error, user should have visibility.
+                * This can happen if a NFS mount goes bad (e.g. server
+                * revokes perms or goes down).
                 */
                if (((info->flags & (MNT_LAZY|MNT_NOWAIT)) == 0 ||
                    (info->flags & MNT_WAIT)) &&
                    (error = VFS_STATFS(mp, sp, info->td->td_ucred))) {
-                       return(0);
+                       /* ignore error */
                }
                sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK;
 
                if (((info->flags & (MNT_LAZY|MNT_NOWAIT)) == 0 ||
                    (info->flags & MNT_WAIT)) &&
                    (error = VFS_STATVFS(mp, vsp, info->td->td_ucred))) {
-                       return(0);
+                       /* ignore error */
                }
                vsp->f_flag = 0;
                if (mp->mnt_flag & MNT_RDONLY)
index b76b0d9..81f4844 100644 (file)
@@ -117,7 +117,7 @@ struct nlookupdata {
 #define NLC_HLINK              0x00001000      /* do hardlink checks */
 #define NLC_RENAME_SRC         0x00002000      /* do rename checks (source) */
 #define NLC_SHAREDLOCK         0x00004000      /* allow shared ncp & vp lock */
-#define NLC_UNUSED00008000     0x00008000
+#define NLC_IGNBADDIR          0x00008000      /* used by umount */
 #define NLC_NFS_RDONLY         0x00010000      /* set by nfs_namei() only */
 #define NLC_NFS_NOSOFTLINKTRAV 0x00020000      /* do not traverse softlnks */
 #define NLC_REFDVP             0x00040000      /* set ref'd/unlocked nl_dvp */