DragonFlyBSD Kernel Audit
DF-2632 / naturalprobe.c
← back to finding ↓ download raw
/*
 * DF-2632 — natural-name negative-control probe.
 *
 * (a) replicates hammer2_dirhash() in userland (hammer2_subr.c:178-229)
 *     and reports the max number of natural names sharing one 64K
 *     collision window (key & ~HAMMER2_DIRHASH_LOMASK, LOMASK=0x7FFF);
 * (b) creates the natural-named files on the hammer2 mount with periodic
 *     sync(), proving (or refuting) that natural names cannot make any
 *     window dense enough to trip the flusher overlap panic.
 *
 * usage: naturalprobe <dir> <count-sequential> <count-random> [batch]
 */
#include <fcntl.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

static uint32_t tbl[256];

static void
mk_tbl(void)
{
	uint32_t i, j, c;
	for (i = 0; i < 256; ++i) {
		c = i;
		for (j = 0; j < 8; ++j)
			c = (c >> 1) ^ (0x82F63B78 & (-(c & 1)));
		tbl[i] = c;
	}
}

static uint32_t
icrc32(const char *b, size_t n)
{
	uint32_t r = 0xFFFFFFFF;
	size_t i;
	for (i = 0; i < n; ++i)
		r = (r >> 8) ^ tbl[(r ^ (uint8_t)b[i]) & 0xFF];
	return r ^ 0xFFFFFFFF;
}

/* replica of hammer2_dirhash() for delimiter-free names */
static uint64_t
dirhash(const char *name, size_t len)
{
	uint64_t key = 0;
	uint32_t crcx;

	crcx = icrc32(name, len);	/* single segment: m32 == crc */
	crcx |= 0x80000000U;
	key |= (uint64_t)crcx << 32;
	crcx = icrc32(name, len);
	crcx = crcx ^ (crcx << 16);
	key |= crcx & 0xFFFF0000U;
	key |= 0x8000U;
	return key;
}

struct went {
	uint64_t win;
	long cnt;
};

static int
cmp_win(const void *a, const void *b)
{
	uint64_t x = ((const struct went *)a)->win;
	uint64_t y = ((const struct went *)b)->win;
	return (x < y) ? -1 : (x > y) ? 1 : 0;
}

static uint64_t
xorshift64(uint64_t *s)
{
	uint64_t x = *s;
	x ^= x << 13;
	x ^= x >> 7;
	x ^= x << 17;
	*s = x;
	return x;
}

int
main(int argc, char **argv)
{
	const char *dir;
	long nseq, nrand, batch = 512;
	char name[64], path[512];
	struct went *w;
	long nw = 0, i, created = 0, errors = 0, maxwin = 0, dupwin = 0;
	uint64_t seed = 0x9E3779B97F4A7C15ULL;
	FILE *dev;

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

	mk_tbl();
	if (icrc32("123456789", 9) != 0xE3069283u) {
		fprintf(stderr, "crc32c self-test FAILED\n");
		return 2;
	}

	/* real randomness for the random-name half */
	dev = fopen("/dev/urandom", "r");
	if (dev) {
		if (fread(&seed, sizeof(seed), 1, dev) != 1)
			seed = 0x12345678DEADBEEFULL;
		fclose(dev);
	}

	w = calloc(nseq + nrand, sizeof(*w));
	if (!w) { perror("calloc"); return 2; }

	for (i = 0; i < nseq; ++i) {
		snprintf(name, sizeof(name), "f%07ld", i);
		w[nw++].win = dirhash(name, strlen(name)) & ~0x7FFFULL;
	}
	for (i = 0; i < nrand; ++i) {
		static const char hex[] = "0123456789abcdef";
		uint64_t r = xorshift64(&seed);
		int k;
		for (k = 0; k < 32; ++k) {
			name[k] = hex[(r >> (k % 60)) & 15];
			if ((k & 15) == 15)
				r = xorshift64(&seed);
		}
		name[32] = 0;
		w[nw++].win = dirhash(name, strlen(name)) & ~0x7FFFULL;
	}
	qsort(w, nw, sizeof(*w), cmp_win);
	for (i = 0; i < nw; ) {
		long j = i;
		while (j < nw && w[j].win == w[i].win)
			++j;
		if (j - i > maxwin)
			maxwin = j - i;
		if (j - i > 1)
			++dupwin;
		i = j;
	}
	printf("names=%ld windows=%ld max-per-window=%ld windows-with->1=%ld\n",
	    nw, nw, maxwin, dupwin);
	fflush(stdout);
	free(w);

	setvbuf(stdout, NULL, _IONBF, 0);
	seed ^= 0xA5A5A5A5A5A5A5A5ULL;
	for (i = 0; i < nseq; ++i) {
		snprintf(name, sizeof(name), "f%07ld", i);
		snprintf(path, sizeof(path), "%s/%s", dir, name);
		if (open(path, O_CREAT | O_EXCL | O_WRONLY, 0644) < 0)
			++errors;
		else
			++created;
		if (created % batch == 0 && created) {
			printf("created=%ld errors=%ld\n", created, errors);
			sync();
		}
	}
	for (i = 0; i < nrand; ++i) {
		static const char hex[] = "0123456789abcdef";
		uint64_t r = xorshift64(&seed);
		int k;
		for (k = 0; k < 32; ++k) {
			name[k] = hex[(r >> (k % 60)) & 15];
			if ((k & 15) == 15)
				r = xorshift64(&seed);
		}
		name[32] = 0;
		snprintf(path, sizeof(path), "%s/%s", dir, name);
		if (open(path, O_CREAT | O_EXCL | O_WRONLY, 0644) < 0)
			++errors;
		else
			++created;
		if (created % batch == 0 && created) {
			printf("created=%ld errors=%ld\n", created, errors);
			sync();
		}
	}
	sync();
	printf("DONE created=%ld errors=%ld\n", created, errors);
	return (errors == 0) ? 0 : 1;
}