Fix getfsstat(2) with MNT_WAIT to not skip filesystems that are in the

process of being unmounted.  Previously it would skip them, even if the
unmount eventually failed eg due to the filesystem being busy.

This behaviour broke autounmountd(8) - if you tried to manually unmount
a mounted filesystem, using 'automount -u', and the autounmountd attempted
to refresh the filesystem list in that very moment, it would conclude that
the filesystem got unmounted and not try to unmount it afterwards.

Reviewed by:	kib@
Tested by:	pho@
MFC after:	1 month
Differential Revision:	https://reviews.freebsd.org/D8030
This commit is contained in:
trasz 2016-11-02 09:43:19 +00:00
parent 8381908c9b
commit 2c0de38912

View File

@ -446,16 +446,19 @@ kern_getfsstat(struct thread *td, struct statfs **buf, size_t bufsize,
size_t *countp, enum uio_seg bufseg, int flags) size_t *countp, enum uio_seg bufseg, int flags)
{ {
struct mount *mp, *nmp; struct mount *mp, *nmp;
struct statfs *sfsp, *sp, sb; struct statfs *sfsp, *sp, sb, *tofree;
size_t count, maxcount; size_t count, maxcount;
int error; int error;
restart:
maxcount = bufsize / sizeof(struct statfs); maxcount = bufsize / sizeof(struct statfs);
if (bufsize == 0) if (bufsize == 0) {
sfsp = NULL; sfsp = NULL;
else if (bufseg == UIO_USERSPACE) tofree = NULL;
} else if (bufseg == UIO_USERSPACE) {
sfsp = *buf; sfsp = *buf;
else /* if (bufseg == UIO_SYSSPACE) */ { tofree = NULL;
} else /* if (bufseg == UIO_SYSSPACE) */ {
count = 0; count = 0;
mtx_lock(&mountlist_mtx); mtx_lock(&mountlist_mtx);
TAILQ_FOREACH(mp, &mountlist, mnt_list) { TAILQ_FOREACH(mp, &mountlist, mnt_list) {
@ -464,8 +467,8 @@ kern_getfsstat(struct thread *td, struct statfs **buf, size_t bufsize,
mtx_unlock(&mountlist_mtx); mtx_unlock(&mountlist_mtx);
if (maxcount > count) if (maxcount > count)
maxcount = count; maxcount = count;
sfsp = *buf = malloc(maxcount * sizeof(struct statfs), M_TEMP, tofree = sfsp = *buf = malloc(maxcount * sizeof(struct statfs),
M_WAITOK); M_TEMP, M_WAITOK);
} }
count = 0; count = 0;
mtx_lock(&mountlist_mtx); mtx_lock(&mountlist_mtx);
@ -480,9 +483,24 @@ kern_getfsstat(struct thread *td, struct statfs **buf, size_t bufsize,
continue; continue;
} }
#endif #endif
if (vfs_busy(mp, MBF_NOWAIT | MBF_MNTLSTLOCK)) { if (flags == MNT_WAIT) {
nmp = TAILQ_NEXT(mp, mnt_list); if (vfs_busy(mp, MBF_MNTLSTLOCK) != 0) {
continue; /*
* If vfs_busy() failed, and MBF_NOWAIT
* wasn't passed, then the mp is gone.
* Furthermore, because of MBF_MNTLSTLOCK,
* the mountlist_mtx was dropped. We have
* no other choice than to start over.
*/
mtx_unlock(&mountlist_mtx);
free(tofree, M_TEMP);
goto restart;
}
} else {
if (vfs_busy(mp, MBF_NOWAIT | MBF_MNTLSTLOCK) != 0) {
nmp = TAILQ_NEXT(mp, mnt_list);
continue;
}
} }
if (sfsp && count < maxcount) { if (sfsp && count < maxcount) {
sp = &mp->mnt_stat; sp = &mp->mnt_stat;