2 * Generates files or directories with hash collisions on a XFS filesystem
3 * Copyright (C) 2014 Hannes Frederic Sowa
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
35 } touch_mode = ILLEGAL;
37 static bool one_hash = false;
39 static uint32_t rol32(uint32_t word, unsigned int shift)
41 return (word << shift) | (word >> (32 - shift));
44 static uint32_t xfs_da_hashname(const uint8_t *name, int namelen)
48 for (hash = 0; namelen >= 4; namelen -= 4, name += 4)
49 hash = (name[0] << 21) ^ (name[1] << 14) ^ (name[2] << 7) ^
50 (name[3] << 0) ^ rol32(hash, 7 * 4);
55 "misbalanced input buffer to xfs_da_hashname - "
56 "overlapping %d bytes\n", namelen);
63 static uint8_t gen_rand(void)
66 while (!(r = rand()));
70 static uint8_t buffer[252+1] = {0};
72 static void gen_name(void)
78 for (idx = 0; idx < 252-4; idx+=4) {
79 buffer[idx + 0] = gen_rand();
80 buffer[idx + 1] = gen_rand();
81 buffer[idx + 2] = gen_rand();
82 buffer[idx + 3] = gen_rand();
85 hash = rol32(xfs_da_hashname(buffer, 248), 7 * 4);
91 buffer[idx + 3] = last & 0x7fU;
92 buffer[idx + 2] = (last >> 7) & 0x7fU;
93 buffer[idx + 1] = (last >> 14) & 0x7fU;
94 buffer[idx + 0] = ((last >> 21) & 0xffU);
96 if (memchr(buffer, '.', sizeof(buffer)) ||
97 memchr(buffer, '/', sizeof(buffer)))
101 /* very poor - can be improved later */
102 static bool done = false;
103 static uint32_t filter;
106 filter = xfs_da_hashname(buffer, 252);
111 if (filter != xfs_da_hashname(buffer, 252))
116 static int touch(const char *buffer)
118 if (touch_mode == DIRECTORY) {
119 if (mkdir(buffer, S_IRWXU)) {
120 /* ignore if directory is already present */
123 perror("mkdir with random directory name");
126 } else if (touch_mode == FILENAME) {
127 int fd = creat(buffer, S_IRWXU);
129 /* ignore duplicate files */
132 perror("creat with random directory name");
136 perror("close is leaking a file descriptor");
144 static void do_seed(void)
147 if (gettimeofday(&tv, NULL)) {
148 perror("gettimeofday");
151 srand(tv.tv_sec ^ tv.tv_usec ^ getpid());
154 static void usage_and_exit(const char *pname)
156 fprintf(stderr, "usage: %s [-d] [-f] [-n num] [-s] directory\n"
157 "\t-f\tcreate files (the default)\n"
158 "\t-d\tcreate directories\n"
159 "\t-n num\tcreate num directories or files (default 200000)\n"
160 "\t-s\tonly generate one hash\n"
161 "\tdirectory\tthe directory to chdir() to\n",
166 int main(int argc, char **argv)
168 const char allopts[] = "hsdfn:";
169 int c, orig_cycles, errors = 0, cycles = 200000;
171 while ((c = getopt(argc, argv, allopts)) != -1) {
174 if (touch_mode != ILLEGAL)
175 usage_and_exit(argv[0]);
176 touch_mode = DIRECTORY;
179 if (touch_mode != ILLEGAL)
180 usage_and_exit(argv[0]);
181 touch_mode = FILENAME;
185 if (sscanf(optarg, "%d", &cycles) != 1 ||
187 fputs("could not parse number of iterations", stderr);
195 usage_and_exit(argv[0]);
200 if (argc <= optind || touch_mode == ILLEGAL)
201 usage_and_exit(argv[0]);
203 if (chdir(argv[optind])) {
208 orig_cycles = cycles;
214 errors += touch((char *)buffer);
218 fprintf(stderr, "creating %d %s caused %d errors\n",
219 orig_cycles, touch_mode == FILENAME ? "files" : "directories",