blob: a4cdd599a76142d0c5f38e98afc74f0e0eb59ff2 [file] [log] [blame]
/*
* Creates a bunch of files in the specified directory with the same
* hashvalue on-disk.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <limits.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/time.h>
#define rol32(x,y) (((x) << (y)) | ((x) >> (32 - (y))))
/*
* Implement a simple hash on a character string.
* Rotate the hash value by 7 bits, then XOR each character in.
* This is implemented with some source-level loop unrolling.
*/
static uint32_t xfs_da_hashname(const char *name, int namelen)
{
uint32_t hash;
/*
* Do four characters at a time as long as we can.
*/
for (hash = 0; namelen >= 4; namelen -= 4, name += 4)
hash = (name[0] << 21) ^ (name[1] << 14) ^ (name[2] << 7) ^
(name[3] << 0) ^ rol32(hash, 7 * 4);
/*
* Now do the rest of the characters.
*/
switch (namelen) {
case 3:
return (name[0] << 14) ^ (name[1] << 7) ^ (name[2] << 0) ^
rol32(hash, 7 * 3);
case 2:
return (name[0] << 7) ^ (name[1] << 0) ^ rol32(hash, 7 * 2);
case 1:
return (name[0] << 0) ^ rol32(hash, 7 * 1);
default: /* case 0: */
return hash;
}
}
static int is_invalid_char(char c)
{
return (c <= ' ' || c > '~' || c == '/' || (c >= 'A' && c <= 'Z'));
}
static char random_char(void)
{
char c;
/* get a character of "0"-"9" or "a"-"z" */
c = lrand48() % 36;
return (c >= 10) ? c + 87 : c + 48;
}
static int generate_names(const char *path, long amount, uint32_t desired_hash)
{
char **names;
char fullpath[PATH_MAX];
char *filename;
long count;
long i;
uint32_t base_hash;
uint32_t hash;
names = malloc(amount * sizeof(char *));
if (!names) {
fprintf(stderr, "genhashnames: malloc(%lu) failed!\n",
amount * sizeof(char *));
return 1;
}
strcpy(fullpath, path);
filename = fullpath + strlen(fullpath);
if (filename[-1] != '/')
*filename++ = '/';
for (count = 0; count < amount; count++) {
for (;;) {
base_hash = 0;
for (i = 0; i < 6; i++) {
filename[i] = random_char();
base_hash = filename[i] ^ rol32(base_hash, 7);
}
while (i < 200) {
filename[i] = random_char();
base_hash = filename[i] ^ rol32(base_hash, 7);
i++;
hash = rol32(base_hash, 3) ^ desired_hash;
filename[i] = (hash >> 28) |
(random_char() & 0xf0);
if (is_invalid_char(filename[i]))
continue;
filename[i + 1] = (hash >> 21) & 0x7f;
if (is_invalid_char(filename[i + 1]))
continue;
filename[i + 2] = (hash >> 14) & 0x7f;
if (is_invalid_char(filename[i + 2]))
continue;
filename[i + 3] = (hash >> 7) & 0x7f;
if (is_invalid_char(filename[i + 3]))
continue;
filename[i + 4] = (hash ^ (filename[i] >> 4))
& 0x7f;
if (is_invalid_char(filename[i + 4]))
continue;
break;
}
if (i < NAME_MAX)
break;
}
filename[i + 5] = '\0';
if (xfs_da_hashname(filename, i + 5) != desired_hash) {
fprintf(stderr, "genhashnames: Hash mismatch!\n");
return 1;
}
for (i = 0; i < count; i++) {
if (strcmp(fullpath, names[i]) == 0)
break;
}
if (i == count) {
names[count] = strdup(fullpath);
puts(fullpath);
} else
count--;
}
return 0;
}
static void usage(void)
{
fprintf(stderr, "Usage: genhashnames <directory> <amount> "
"[seed] [hashvalue]\n");
exit(1);
}
int main(int argc, char **argv)
{
long seed;
uint32_t desired_hash;
if (argc < 3 || argc > 5)
usage();
if (argc >= 4)
seed = strtol(argv[3], NULL, 0);
else {
struct timeval tv;
gettimeofday(&tv, NULL);
seed = tv.tv_usec / 1000 + (tv.tv_sec % 1000) * 1000;
}
srand48(seed);
/*
* always generate hash from random so if hash is specified, random
* sequence is the same as a randomly generated hash of the same value.
*/
desired_hash = (uint32_t)mrand48();
if (argc >= 5)
desired_hash = (uint32_t)strtoul(argv[4], NULL, 0);
fprintf(stderr, "seed = %ld, hash = 0x%08x\n", seed, desired_hash);
return generate_names(argv[1], strtol(argv[2], NULL, 0), desired_hash);
}