DragonFlyBSD Kernel Audit
DF-2628 / drainfill.c
← back to finding ↓ download raw
/*
 * DF-2628 drain filler: single process, no threads.  Writes 256K chunks
 * to <dir>/drainfile in one tight loop until write() fails, then keeps
 * retrying (each retry re-runs the enospace precheck, keeping the
 * per-tick cache hot while attempting to consume any space that appears).
 * Prints progress on stderr.
 *
 * usage: drainfill <dir>
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <sys/stat.h>
#include <signal.h>
#include <stdlib.h>

static void
bye(int sig __unused)
{
	_exit(0);
}

int
main(int argc, char **argv)
{
	char path[512], buf[262144];
	int fd;
	long long bw = 0, last = 0;
	time_t t0 = time(NULL);

	if (argc < 2) { fprintf(stderr, "usage: %s <dir> [seconds]\n", argv[0]); return 64; }
	if (argc >= 3) {
		signal(SIGALRM, bye);
		alarm(atoi(argv[2]));
	}
	snprintf(path, sizeof(path), "%s/drainfile", argv[1]);
	{	/* incompressible data: hammer2 auto-compresses (lz4) and
		 * does not allocate for all-zero data; random-ish content
		 * forces real allocation. */
		size_t i;
		uint32_t st = 0x12345678u ^ (uint32_t)(bw >> 9);
		for (i = 0; i < sizeof(buf); i += 4) {
			st ^= st << 13; st ^= st >> 17; st ^= st << 5;
			((uint32_t *)buf)[i / 4] = st;
		}
	}
	fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0644);
	if (fd < 0) { perror(path); return 1; }
	for (;;) {
		ssize_t r = write(fd, buf, sizeof(buf));
		if (r < 0) {
			if (errno == ENOSPC) {
				fprintf(stderr, "ENOSPC at %lld bytes "
				    "(%lds), retry-loop\n", bw,
				    (long)(time(NULL) - t0));
				/* keep retrying: consumes space that may
				 * reappear (bulkfree) */
				usleep(2000);
				continue;
			}
			perror("write");
			return 1;
		}
		bw += r;
		if (r < (ssize_t)sizeof(buf)) {
			fprintf(stderr, "short write %zd at %lld\n", r, bw);
		}
		if (bw - last > (32LL << 20)) {
			last = bw;
			fprintf(stderr, "drained %lld MB (%lds)\n",
			    bw >> 20, (long)(time(NULL) - t0));
		}
	}
	return 0;
}