1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (c) 2000-2001 Silicon Graphics, Inc.
13 * Run a fully automatic, random test of the directory routines.
15 * Given an input file of a list of filenames (one per line)
16 * It does a number of iterations of operations
17 * chosen pseudo-randomly in certain percentages:
19 * deleting (unlink) and
21 * on a pseudo-randomly chosen filename (from input file).
23 * The percentage thresholds for operation selection change
24 * every <number-of-names> iterations.
26 * If had 100 names then:
28 * 1-100: pct_remove = 33; pct_create = 33;
29 * 101-200: pct_remove = 60; pct_create = 20;
30 * 201-300: pct_remove = 20; pct_create = 60;
31 * 301-400: pct_remove = 33; pct_create = 33;
32 * 401-500: pct_remove = 60; pct_create = 20;
33 * 501-600: pct_remove = 20; pct_create = 60;
36 * op > (pct_remove + pct_create) => auto_lookup(ip);
37 * op > pct_remove => auto_create(ip);
38 * t => auto_remove(ip);
40 * Each iteration an op is chosen as shown above
41 * and a filename is randomly chosen.
43 * The operation is done and any error codes are
44 * verified considering whether file exists (info.exists)
45 * or not. The stat(3) call also compares inode number.
49 #define DOT_COUNT 100 /* print a '.' every X operations */
58 char *table_data; /* char string storage for info table */
60 int good_adds, good_rms, good_looks, good_tot; /* ops that suceeded */
61 int bad_adds, bad_rms, bad_looks, bad_tot; /* ops that failed */
66 int auto_lookup(struct info *);
67 int auto_create(struct info *);
68 int auto_remove(struct info *);
75 printf("usage: nametest [-l srcfile] [-i iterations] [-s seed] [-z] [-v] [-c]\n");
80 main(int argc, char *argv[])
83 int totalnames, iterations, zeroout;
84 int zone, op, pct_remove=0, pct_create=0, ch, i, retval, fd;
89 linedots = zeroout = verbose = mixcase = 0;
90 seed = (int)time(NULL) % 1000;
93 while ((ch = getopt(argc, argv, "l:i:s:zvc")) != EOF) {
95 case 'l': sourcefile = optarg; break;
96 case 's': seed = atoi(optarg); break;
97 case 'i': iterations = atoi(optarg); break;
98 case 'z': zeroout++; break;
99 case 'v': verbose++; break;
100 case 'c': mixcase++; break;
101 default: usage(); break;
106 * Read in the source file.
108 if (stat64(sourcefile, &statb) < 0) {
113 if ((table_data = malloc(statb.st_size)) == NULL) {
117 if ((fd = open(sourcefile, O_RDONLY)) < 0) {
121 if (read(fd, table_data, statb.st_size) < 0) {
128 * Allocate space for the info table and fill it in.
132 * Add up number of lines in file
133 * and replace '\n' by '\0'
136 for (c = table_data, i = 0; i < statb.st_size; c++, i++) {
143 printf("no names found in input file\n");
147 table = (struct info *)calloc(totalnames+1, sizeof(struct info));
153 * Copy over names from file (in <table_data>) into name fields
154 * of info structures in <table>.
157 ip->name = c = table_data;
158 for (i = 0; i < totalnames; ) {
168 * Check table of names.
169 * Name are of files and not commands.
171 * ??? I guess use of an input file with commands
172 * has been done before ???
173 * "touch fred" => "fred"
177 for (ip = table, i = 0; i < totalnames; ip++, i++) {
178 if (strncmp(ip->name, "touch ", strlen("touch ")) == 0) {
179 /* make name skip over "touch " string */
180 ip->name += strlen("touch ");
181 ip->namelen -= strlen("touch ");
182 } else if (strncmp(ip->name, "rm ", strlen("rm ")) == 0) {
183 printf("bad input file, \"rm\" cmds not allowed\n");
185 } else if (strncmp(ip->name, "ls ", strlen("ls ")) == 0) {
186 printf("bad input file, \"ls\" cmds not allowed\n");
192 * Run random transactions against the directory.
195 printf("Seed = %d (use \"-s %d\" to re-execute this test)\n", seed, seed);
198 for (i = 0; i < iterations; i++) {
200 * The distribution of transaction types changes over time.
201 * At first we have an equal distribution which gives us
202 * a steady state directory of 50% total size.
203 * Later, we have an unequal distribution which gives us
204 * more creates than removes, growing the directory.
205 * Later still, we have an unequal distribution which gives
206 * us more removes than creates, shrinking the directory.
208 if ((i % totalnames) == 0) {
211 case 0: pct_remove = 20; pct_create = 60; break;
212 case 1: pct_remove = 33; pct_create = 33; break;
213 case 2: pct_remove = 60; pct_create = 20; break;
218 * Choose an operation based on the current distribution.
220 ip = &table[ random() % totalnames ];
222 if (op > (pct_remove + pct_create)) {
223 retval = auto_lookup(ip);
224 } else if (op > pct_remove) {
225 retval = auto_create(ip);
227 retval = auto_remove(ip);
230 /* output '.' every DOT_COUNT ops
231 * and output '\n" every 72 dots
233 if ((i % DOT_COUNT) == 0) {
234 if (linedots++ == 72) {
244 printf("creates: %6d OK, %6d EEXIST (%6d total, %2d%% EEXIST)\n",
245 good_adds, bad_adds, good_adds + bad_adds,
247 ? (bad_adds*100) / (good_adds+bad_adds)
249 printf("removes: %6d OK, %6d ENOENT (%6d total, %2d%% ENOENT)\n",
250 good_rms, bad_rms, good_rms + bad_rms,
252 ? (bad_rms*100) / (good_rms+bad_rms)
254 printf("lookups: %6d OK, %6d ENOENT (%6d total, %2d%% ENOENT)\n",
255 good_looks, bad_looks, good_looks + bad_looks,
256 (good_looks+bad_looks)
257 ? (bad_looks*100) / (good_looks+bad_looks)
259 good_tot = good_looks + good_adds + good_rms;
260 bad_tot = bad_looks + bad_adds + bad_rms;
261 printf("total : %6d OK, %6d w/error (%6d total, %2d%% w/error)\n",
262 good_tot, bad_tot, good_tot + bad_tot,
264 ? (bad_tot*100) / (good_tot+bad_tot)
268 * If asked to clear the directory out after the run,
269 * remove everything that is left.
273 for (ip = table, i = 0; i < totalnames; ip++, i++) {
277 retval = unlink(ip->name);
279 if (errno == ENOENT) {
280 printf("\"%s\"(%llu) not removed, should have existed\n", ip->name, (unsigned long long)ip->inumber);
282 printf("\"%s\"(%llu) on remove: ", ip->name, (unsigned long long)ip->inumber);
287 if ((good_rms % DOT_COUNT) == 0) {
292 printf("\ncleanup: %6d removes\n", good_rms);
297 char *get_name(struct info *ip)
299 static char path[PATH_MAX];
305 /* pick a random character to change case in path */
306 strcpy(path, ip->name);
307 p = strrchr(path, '/');
310 p += random() % strlen(p);
319 auto_lookup(struct info *ip)
324 retval = stat64(get_name(ip), &statb);
328 if (ip->exists == 0) {
329 printf("\"%s\"(%llu) lookup, should not exist\n",
330 ip->name, (unsigned long long)statb.st_ino);
332 } else if (ip->inumber != statb.st_ino) {
333 printf("\"%s\"(%llu) lookup, should be inumber %llu\n",
334 ip->name, (unsigned long long)statb.st_ino,
335 (unsigned long long)ip->inumber);
337 } else if (verbose) {
338 printf("\"%s\"(%llu) lookup ok\n",
339 ip->name, (unsigned long long)statb.st_ino);
341 } else if (errno == ENOENT) {
344 if (ip->exists == 1) {
345 printf("\"%s\"(%llu) lookup, should exist\n",
346 ip->name, (unsigned long long)ip->inumber);
348 } else if (verbose) {
349 printf("\"%s\"(%llu) lookup ENOENT ok\n",
350 ip->name, (unsigned long long)ip->inumber);
354 printf("\"%s\"(%llu) on lookup: ",
355 ip->name, (unsigned long long)ip->inumber);
362 auto_create(struct info *ip)
367 retval = open(get_name(ip), O_RDWR|O_EXCL|O_CREAT, 0666);
372 if (stat64(ip->name, &statb) < 0) {
376 if (ip->exists == 1) {
377 printf("\"%s\"(%llu) created, but already existed as inumber %llu\n", ip->name, (unsigned long long)statb.st_ino, (unsigned long long)ip->inumber);
379 } else if (verbose) {
380 printf("\"%s\"(%llu) create new ok\n",
381 ip->name, (unsigned long long)statb.st_ino);
384 ip->inumber = statb.st_ino;
385 } else if (errno == EEXIST) {
388 if (ip->exists == 0) {
389 if (stat64(ip->name, &statb) < 0) {
393 printf("\"%s\"(%llu) not created, should not exist\n",
394 ip->name, (unsigned long long)statb.st_ino);
396 } else if (verbose) {
397 printf("\"%s\"(%llu) not created ok\n",
398 ip->name, (unsigned long long)ip->inumber);
403 printf("\"%s\"(%llu) on create: ",
404 ip->name, (unsigned long long)ip->inumber);
411 auto_remove(struct info *ip)
415 retval = unlink(get_name(ip));
419 if (ip->exists == 0) {
420 printf("\"%s\"(%llu) removed, should not have existed\n",
421 ip->name, (unsigned long long)ip->inumber);
423 } else if (verbose) {
424 printf("\"%s\"(%llu) remove ok\n",
425 ip->name, (unsigned long long)ip->inumber);
429 } else if (errno == ENOENT) {
432 if (ip->exists == 1) {
433 printf("\"%s\"(%llu) not removed, should have existed\n",
434 ip->name, (unsigned long long)ip->inumber);
436 } else if (verbose) {
437 printf("\"%s\"(%llu) not removed ok\n",
438 ip->name, (unsigned long long)ip->inumber);
443 printf("\"%s\"(%llu) on remove: ",
444 ip->name, (unsigned long long)ip->inumber);