Busy the mount point which is the owner of the audit vnode, around

audit_record_write().  This is important so that VFS_STATFS() is not
done on the NULL or freed mp and the check for free space is
consistent with the vnode used for write.

Add vn_start_write() braces around VOP_FSYNC() calls on the audit vnode.

Move repeated code to fsync vnode and panic to the helper
audit_worker_sync_vp().

Reviewed by:	rwatson
Tested by:	pho
Sponsored by:	The FreeBSD Foundation
MFC after:	1 week
This commit is contained in:
Konstantin Belousov 2016-01-16 10:06:33 +00:00
parent b9d2b617ca
commit 27725229dc
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=294137

View file

@ -71,6 +71,8 @@ __FBSDID("$FreeBSD$");
#include <vm/uma.h>
#include <machine/stdarg.h>
/*
* Worker thread that will schedule disk I/O, etc.
*/
@ -98,6 +100,26 @@ static struct sx audit_worker_lock;
#define AUDIT_WORKER_LOCK() sx_xlock(&audit_worker_lock)
#define AUDIT_WORKER_UNLOCK() sx_xunlock(&audit_worker_lock)
static void
audit_worker_sync_vp(struct vnode *vp, struct mount *mp, const char *fmt, ...)
{
struct mount *mp1;
int error;
va_list va;
va_start(va, fmt);
error = vn_start_write(vp, &mp1, 0);
if (error == 0) {
VOP_LOCK(vp, LK_EXCLUSIVE | LK_RETRY);
(void)VOP_FSYNC(vp, MNT_WAIT, curthread);
VOP_UNLOCK(vp, 0);
vn_finished_write(mp1);
}
vfs_unbusy(mp);
vpanic(fmt, va);
va_end(va);
}
/*
* Write an audit record to a file, performed as the last stage after both
* preselection and BSM conversion. Both space management and write failures
@ -114,6 +136,7 @@ audit_record_write(struct vnode *vp, struct ucred *cred, void *data,
static struct timeval last_fail;
static int cur_lowspace_trigger;
struct statfs *mnt_stat;
struct mount *mp;
int error;
static int cur_fail;
long temp;
@ -123,15 +146,25 @@ audit_record_write(struct vnode *vp, struct ucred *cred, void *data,
if (vp == NULL)
return;
mnt_stat = &vp->v_mount->mnt_stat;
mp = vp->v_mount;
if (mp == NULL) {
error = EINVAL;
goto fail;
}
error = vfs_busy(mp, 0);
if (error != 0) {
mp = NULL;
goto fail;
}
mnt_stat = &mp->mnt_stat;
/*
* First, gather statistics on the audit log file and file system so
* that we know how we're doing on space. Consider failure of these
* operations to indicate a future inability to write to the file.
*/
error = VFS_STATFS(vp->v_mount, mnt_stat);
if (error)
error = VFS_STATFS(mp, mnt_stat);
if (error != 0)
goto fail;
/*
@ -246,13 +279,12 @@ audit_record_write(struct vnode *vp, struct ucred *cred, void *data,
*/
if (audit_in_failure) {
if (audit_q_len == 0 && audit_pre_q_len == 0) {
VOP_LOCK(vp, LK_EXCLUSIVE | LK_RETRY);
(void)VOP_FSYNC(vp, MNT_WAIT, curthread);
VOP_UNLOCK(vp, 0);
panic("Audit store overflow; record queue drained.");
audit_worker_sync_vp(vp, mp,
"Audit store overflow; record queue drained.");
}
}
vfs_unbusy(mp);
return;
fail_enospc:
@ -262,10 +294,8 @@ audit_record_write(struct vnode *vp, struct ucred *cred, void *data,
* space, or ENOSPC returned by the vnode write call.
*/
if (audit_fail_stop) {
VOP_LOCK(vp, LK_EXCLUSIVE | LK_RETRY);
(void)VOP_FSYNC(vp, MNT_WAIT, curthread);
VOP_UNLOCK(vp, 0);
panic("Audit log space exhausted and fail-stop set.");
audit_worker_sync_vp(vp, mp,
"Audit log space exhausted and fail-stop set.");
}
(void)audit_send_trigger(AUDIT_TRIGGER_NO_SPACE);
audit_suspended = 1;
@ -277,12 +307,12 @@ audit_record_write(struct vnode *vp, struct ucred *cred, void *data,
* lost, which may require an immediate system halt.
*/
if (audit_panic_on_write_fail) {
VOP_LOCK(vp, LK_EXCLUSIVE | LK_RETRY);
(void)VOP_FSYNC(vp, MNT_WAIT, curthread);
VOP_UNLOCK(vp, 0);
panic("audit_worker: write error %d\n", error);
audit_worker_sync_vp(vp, mp,
"audit_worker: write error %d\n", error);
} else if (ppsratecheck(&last_fail, &cur_fail, 1))
printf("audit_worker: write error %d\n", error);
if (mp != NULL)
vfs_unbusy(mp);
}
/*