From 1af46206e167f00e035b1a82ebe15665df633ac6 Mon Sep 17 00:00:00 2001 From: Matthew Dillon Date: Tue, 26 Mar 2019 19:33:03 -0700 Subject: [PATCH] kernel - Fix df and unmount of bad NFS volumes * 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 | 45 ++++++++++++++++++++++++++++++++--------- sys/kern/vfs_syscalls.c | 42 ++++++++++++++++++++++++++++---------- sys/sys/nlookup.h | 2 +- 3 files changed, 68 insertions(+), 21 deletions(-) diff --git a/sys/kern/vfs_nlookup.c b/sys/kern/vfs_nlookup.c index c3c6fd56b6..2b0b2ba402 100644 --- a/sys/kern/vfs_nlookup.c +++ b/sys/kern/vfs_nlookup.c @@ -73,6 +73,24 @@ 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; diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c index 1ec320d50a..8ebab8b46a 100644 --- a/sys/kern/vfs_syscalls.c +++ b/sys/kern/vfs_syscalls.c @@ -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) diff --git a/sys/sys/nlookup.h b/sys/sys/nlookup.h index b76b0d9177..81f4844c01 100644 --- a/sys/sys/nlookup.h +++ b/sys/sys/nlookup.h @@ -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 */ -- 2.41.0