/*
 * DF-5784 leakloop: drain a directory via readdir(3).  On the crafted
 * image the wrapped-range dirent makes directory iteration never reach
 * EOF (iteration key wraps below key_beg), so this drains entries until
 * the entry cap is hit.  On the control image each pass EOFs after the
 * 3 standard entries.
 *
 * Prints progress with the live kernel chain counter so the leak rate is
 * visible in the run log.
 */
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/sysctl.h>

static long
chain_allocs(void)
{
	long v = -1;
	size_t l = sizeof(v);
	if (sysctlbyname("vfs.hammer2.chain_allocs", &v, &l, NULL, 0) < 0)
		return (-1);
	return (v);
}

int
main(int argc, char **argv)
{
	const char *path;
	long cap, total = 0, passes = 0;
	DIR *d;
	struct dirent *de;

	if (argc < 3) {
		fprintf(stderr, "usage: leakloop <dir> <entry-cap>\n");
		return (1);
	}
	path = argv[1];
	cap = atol(argv[2]);

	d = opendir(path);
	if (d == NULL) {
		perror("opendir");
		return (1);
	}
	printf("start chain_allocs=%ld\n", chain_allocs());
	for (;;) {
		rewinddir(d);
		while ((de = readdir(d)) != NULL) {
			if (++total % 100000 == 0)
				printf("entries=%ld chain_allocs=%ld\n",
				    total, chain_allocs());
			if (total >= cap)
				goto done;
		}
		if (++passes % 100 == 0)
			printf("passes=%ld total=%ld chain_allocs=%ld\n",
			    passes, total, chain_allocs());
		if (passes > 10000000)
			goto done;
	}
done:
	printf("DONE entries=%ld passes=%ld chain_allocs=%ld\n",
	    total, passes, chain_allocs());
	closedir(d);
	return (0);
}
