cmd/xfs/stress/001 1.6 Renamed to cmd/xfstests/001
[xfstests-dev.git] / src / nametest.c
1 /*
2  * Copyright (c) 2000 Silicon Graphics, Inc.  All Rights Reserved.
3  * 
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of version 2 of the GNU General Public License as
6  * published by the Free Software Foundation.
7  * 
8  * This program is distributed in the hope that it would be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11  * 
12  * Further, this software is distributed without any warranty that it is
13  * free of the rightful claim of any third person regarding infringement
14  * or the like.  Any license provided herein, whether implied or
15  * otherwise, applies only to this software file.  Patent licenses, if
16  * any, provided herein do not apply to combinations of this program with
17  * other software, or any other product whatsoever.
18  * 
19  * You should have received a copy of the GNU General Public License along
20  * with this program; if not, write the Free Software Foundation, Inc., 59
21  * Temple Place - Suite 330, Boston MA 02111-1307, USA.
22  * 
23  * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
24  * Mountain View, CA  94043, or:
25  * 
26  * http://www.sgi.com 
27  * 
28  * For further information regarding this notice, see: 
29  * 
30  * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
31  */
32  
33 #include "global.h"
34
35 /*
36  * nametest.c
37  *
38  * Run a fully automatic, random test of the directory routines.
39  *
40  * Given an input file of a list of filenames (one per line)
41  * It does a number of iterations of operations
42  * chosen pseudo-randomly in certain percentages: 
43  *   creating (open), 
44  *   deleting (unlink) and 
45  *   looking up (stat)
46  * on a pseudo-randomly chosen filename (from input file). 
47  *
48  * The percentage thresholds for operation selection change
49  * every <number-of-names> iterations. 
50  * e.g.
51  * If had 100 names then:
52  * iterations:
53  * 1-100:      pct_remove = 33; pct_create = 33;  
54  * 101-200:    pct_remove = 60; pct_create = 20;
55  * 201-300:    pct_remove = 20; pct_create = 60;
56  * 301-400:    pct_remove = 33; pct_create = 33;  
57  * 401-500:    pct_remove = 60; pct_create = 20;
58  * 501-600:    pct_remove = 20; pct_create = 60;
59  * etc...
60  *
61  * op > (pct_remove + pct_create) => auto_lookup(ip);
62  * op > pct_remove                => auto_create(ip);
63  * t                              => auto_remove(ip);
64  *
65  * Each iteration an op is chosen as shown above
66  * and a filename is randomly chosen.
67  *
68  * The operation is done and any error codes are
69  * verified considering whether file exists (info.exists)
70  * or not. The stat(3) call also compares inode number. 
71  */
72
73
74 #define DOT_COUNT       100     /* print a '.' every X operations */
75
76 struct info {
77         ino64_t inumber;
78         char    *name;
79         short   namelen;
80         short   exists;
81 } *table;
82
83 char *table_data;       /* char string storage for info table */
84
85 int good_adds, good_rms, good_looks, good_tot;  /* ops that suceeded */
86 int bad_adds, bad_rms, bad_looks, bad_tot;      /* ops that failed */
87
88 int verbose;
89
90 int     auto_lookup(struct info *);
91 int     auto_create(struct info *);
92 int     auto_remove(struct info *);
93
94 void    usage(void);
95
96 void
97 usage(void)
98 {
99         printf("usage: nametest [-l srcfile] [-i iterations] [-s seed] [-z] [-v]\n");
100         exit(1);
101 }
102
103 int
104 main(int argc, char *argv[])
105 {
106         char *sourcefile, *c;
107         int totalnames, iterations, zeroout;
108         int zone, op, pct_remove, pct_create, ch, i, retval, fd;
109         struct stat64 statb;
110         struct info *ip;
111         int seed, linedots;
112
113         linedots = zeroout = verbose = 0;
114         seed = (int)time(NULL) % 1000;
115         iterations = 100000;
116         sourcefile = "input";
117         while ((ch = getopt(argc, argv, "l:i:s:zv")) != EOF) {
118                 switch (ch) {
119                 case 'l':       sourcefile = optarg;            break;
120                 case 's':       seed = atoi(optarg);            break;
121                 case 'i':       iterations = atoi(optarg);      break;
122                 case 'z':       zeroout++;                      break;
123                 case 'v':       verbose++;                      break;
124                 default:        usage();                        break;
125                 }
126         }
127
128         /*
129          * Read in the source file.
130          */
131         if (stat64(sourcefile, &statb) < 0) {
132                 perror(sourcefile);
133                 usage();
134                 return 1;
135         }
136         if ((table_data = malloc(statb.st_size)) == NULL) {
137                 perror("calloc");
138                 return 1;
139         }
140         if ((fd = open(sourcefile, O_RDONLY)) < 0) {
141                 perror(sourcefile);
142                 return 1;
143         }
144         if (read(fd, table_data, statb.st_size) < 0) {
145                 perror(sourcefile);
146                 return 1;
147         }
148         close(fd);
149
150         /*
151          * Allocate space for the info table and fill it in.
152          */
153
154         /* 
155          * Add up number of lines in file 
156          * and replace '\n' by '\0'
157          */
158         totalnames = 0;
159         for (c = table_data, i = 0; i < statb.st_size; c++, i++) {
160                 if (*c == '\n') {
161                         *c = 0;
162                         totalnames++;
163                 }
164         }
165         if (!totalnames) {
166                 printf("no names found in input file\n");
167                 return 1;
168         }
169         
170         table = (struct info *)calloc(totalnames+1, sizeof(struct info));
171         if (table == NULL) {
172                 perror("calloc");
173                 return 1;
174         }
175         /*
176          * Copy over names from file (in <table_data>) into name fields 
177          * of info structures in <table>.
178          */
179         ip = table;
180         ip->name = c = table_data;
181         for (i = 0; i < totalnames;  ) {
182                 if (*c++ == 0) {
183                         ip++;
184                         ip->name = c;
185                         i++;
186                 } else {
187                         ip->namelen++;
188                 }
189         }
190         /*
191          * Check table of names.
192          * Name are of files and not commands.
193          *
194          * ??? I guess use of an input file with commands
195          *     has been done before ???
196          * "touch fred" => "fred"
197          * "rm fred" => error
198          * "ls fred" => error
199          */
200         for (ip = table, i = 0; i < totalnames; ip++, i++) {
201                 if (strncmp(ip->name, "touch ", strlen("touch ")) == 0) {
202                         /* make name skip over "touch " string */
203                         ip->name += strlen("touch ");
204                         ip->namelen -= strlen("touch ");
205                 } else if (strncmp(ip->name, "rm ", strlen("rm ")) == 0) {
206                         printf("bad input file, \"rm\" cmds not allowed\n");
207                         return 1;
208                 } else if (strncmp(ip->name, "ls ", strlen("ls ")) == 0) {
209                         printf("bad input file, \"ls\" cmds not allowed\n");
210                         return 1;
211                 }
212         }
213
214         /*
215          * Run random transactions against the directory.
216          */
217         zone = -1;
218         printf("Seed = %d (use \"-s %d\" to re-execute this test)\n", seed, seed);
219         srandom(seed);
220
221         for (i = 0; i < iterations; i++) {
222                 /*
223                  * The distribution of transaction types changes over time.
224                  * At first we have an equal distribution which gives us
225                  * a steady state directory of 50% total size.
226                  * Later, we have an unequal distribution which gives us
227                  * more creates than removes, growing the directory.
228                  * Later still, we have an unequal distribution which gives
229                  * us more removes than creates, shrinking the directory.
230                  */
231                 if ((i % totalnames) == 0) {
232                         zone++;
233                         switch(zone % 3) {
234                         case 0: pct_remove = 20; pct_create = 60; break;
235                         case 1: pct_remove = 33; pct_create = 33; break;
236                         case 2: pct_remove = 60; pct_create = 20; break;
237                         }
238                 }
239
240                 /*
241                  * Choose an operation based on the current distribution.
242                  */
243                 ip = &table[ random() % totalnames ];
244                 op = random() % 100;
245                 if (op > (pct_remove + pct_create)) {
246                         retval = auto_lookup(ip);
247                 } else if (op > pct_remove) {
248                         retval = auto_create(ip);
249                 } else {
250                         retval = auto_remove(ip);
251                 }
252
253                 /* output '.' every DOT_COUNT ops
254                  * and output '\n" every 72 dots
255                  */
256                 if ((i % DOT_COUNT) == 0) {
257                         if (linedots++ == 72) {
258                                 linedots = 0;
259                                 write(1, "\n", 1);
260                         }
261                         write(1, ".", 1);
262                         fflush(stdout);
263                 }
264         }
265         printf("\n");
266
267         printf("creates: %6d OK, %6d EEXIST  (%6d total, %2d%% EEXIST)\n",
268                          good_adds, bad_adds, good_adds + bad_adds,
269                          (good_adds+bad_adds)
270                                  ? (bad_adds*100) / (good_adds+bad_adds)
271                                  : 0);
272         printf("removes: %6d OK, %6d ENOENT  (%6d total, %2d%% ENOENT)\n",
273                          good_rms, bad_rms, good_rms + bad_rms,
274                          (good_rms+bad_rms)
275                                  ? (bad_rms*100) / (good_rms+bad_rms)
276                                  : 0);
277         printf("lookups: %6d OK, %6d ENOENT  (%6d total, %2d%% ENOENT)\n",
278                          good_looks, bad_looks, good_looks + bad_looks,
279                          (good_looks+bad_looks)
280                                  ? (bad_looks*100) / (good_looks+bad_looks)
281                                  : 0);
282         good_tot = good_looks + good_adds + good_rms;
283         bad_tot = bad_looks + bad_adds + bad_rms;
284         printf("total  : %6d OK, %6d w/error (%6d total, %2d%% w/error)\n",
285                          good_tot, bad_tot, good_tot + bad_tot,
286                          (good_tot + bad_tot)
287                                  ? (bad_tot*100) / (good_tot+bad_tot)
288                                  : 0);
289
290         /*
291          * If asked to clear the directory out after the run,
292          * remove everything that is left.
293          */
294         if (zeroout) {
295                 good_rms = 0;
296                 for (ip = table, i = 0; i < totalnames; ip++, i++) {
297                         if (!ip->exists)
298                                 continue;
299                         good_rms++;
300                         retval = unlink(ip->name);
301                         if (retval < 0) {
302                                 if (errno == ENOENT) {
303                                         printf("\"%s\"(%lu) not removed, should have existed\n",
304                                                       ip->name, ip->inumber);
305                                 } else {
306                                         printf("\"%s\"(%lu) on remove: ",
307                                                       ip->name, ip->inumber);
308                                         perror("unlink");
309                                 }
310                         }
311
312                         if ((good_rms % DOT_COUNT) == 0) {
313                                 write(1, ".", 1);
314                                 fflush(stdout);
315                         }
316                 }
317                 printf("\ncleanup: %6d removes\n", good_rms);
318         }
319         return 0;
320 }
321
322 int
323 auto_lookup(struct info *ip)
324 {
325         struct stat64 statb;
326         int retval;
327
328         retval = stat64(ip->name, &statb);
329         if (retval >= 0) {
330                 good_looks++;
331                 retval = 0;
332                 if (ip->exists == 0) {
333                         printf("\"%s\"(%lu) lookup, should not exist\n",
334                                       ip->name, statb.st_ino);
335                         retval = 1;
336                 } else if (ip->inumber != statb.st_ino) {
337                         printf("\"%s\"(%lu) lookup, should be inumber %lu\n",
338                                       ip->name, statb.st_ino, ip->inumber);
339                         retval = 1;
340                 } else if (verbose) {
341                         printf("\"%s\"(%lu) lookup ok\n",
342                                 ip->name, statb.st_ino);
343                 }
344         } else if (errno == ENOENT) {
345                 bad_looks++;
346                 retval = 0;
347                 if (ip->exists == 1) {
348                         printf("\"%s\"(%lu) lookup, should exist\n",
349                                       ip->name, ip->inumber);
350                         retval = 1;
351                 } else if (verbose) {
352                         printf("\"%s\"(%lu) lookup ENOENT ok\n",
353                                 ip->name, ip->inumber);
354                 }
355         } else {
356                 retval = errno;
357                 printf("\"%s\"(%lu) on lookup: ", ip->name, ip->inumber);
358                 perror("stat64");
359         }
360         return(retval);
361 }
362
363 int
364 auto_create(struct info *ip)
365 {
366         struct stat64 statb;
367         int retval;
368
369         retval = open(ip->name, O_RDWR|O_EXCL|O_CREAT, 0666);
370         if (retval >= 0) {
371                 close(retval);
372                 good_adds++;
373                 retval = 0;
374                 if (stat64(ip->name, &statb) < 0) {
375                         perror("stat64");
376                         exit(1);
377                 }
378                 if (ip->exists == 1) {
379                         printf("\"%s\"(%lu) created, but already existed as inumber %lu\n",
380                                       ip->name, statb.st_ino, ip->inumber);
381                         retval = 1;
382                 } else if (verbose) {
383                         printf("\"%s\"(%lu) create new ok\n",
384                                 ip->name, statb.st_ino);
385                 }
386                 ip->exists = 1;
387                 ip->inumber = statb.st_ino;
388         } else if (errno == EEXIST) {
389                 bad_adds++;
390                 retval = 0;
391                 if (ip->exists == 0) {
392                         if (stat64(ip->name, &statb) < 0) {
393                                 perror("stat64");
394                                 exit(1);
395                         }
396                         printf("\"%s\"(%lu) not created, should not exist\n",
397                                       ip->name, statb.st_ino);
398                         retval = 1;
399                 } else if (verbose) {
400                         printf("\"%s\"(%lu) not created ok\n",
401                                 ip->name, ip->inumber);
402                 }
403                 ip->exists = 1;
404         } else {
405                 retval = errno;
406                 printf("\"%s\"(%lu) on create: ", ip->name, ip->inumber);
407                 perror("creat");
408         }
409         return(retval);
410 }
411
412 int
413 auto_remove(struct info *ip)
414 {
415         int retval;
416
417         retval = unlink(ip->name);
418         if (retval >= 0) {
419                 good_rms++;
420                 retval = 0;
421                 if (ip->exists == 0) {
422                         printf("\"%s\"(%lu) removed, should not have existed\n",
423                                       ip->name, ip->inumber);
424                         retval = 1;
425                 } else if (verbose) {
426                         printf("\"%s\"(%lu) remove ok\n",
427                                 ip->name, ip->inumber);
428                 }
429                 ip->exists = 0;
430                 ip->inumber = 0;
431         } else if (errno == ENOENT) {
432                 bad_rms++;
433                 retval = 0;
434                 if (ip->exists == 1) {
435                         printf("\"%s\"(%lu) not removed, should have existed\n",
436                                       ip->name, ip->inumber);
437                         retval = 1;
438                 } else if (verbose) {
439                         printf("\"%s\"(%lu) not removed ok\n",
440                                 ip->name, ip->inumber);
441                 }
442                 ip->exists = 0;
443         } else {
444                 retval = errno;
445                 printf("\"%s\"(%lu) on remove: ", ip->name, ip->inumber);
446                 perror("unlink");
447         }
448         return(retval);
449 }