02faf70e62609e92817fb34cf941c8a7d34a34e8
[xfstests-dev.git] / src / nametest.c
1 /*
2  * Copyright (c) 2000-2001 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=0, pct_create=0, 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\"(%llu) not removed, should have existed\n", ip->name, (unsigned long long)ip->inumber);
304                                 } else {
305                                         printf("\"%s\"(%llu) on remove: ", ip->name, (unsigned long long)ip->inumber);
306                                         perror("unlink");
307                                 }
308                         }
309
310                         if ((good_rms % DOT_COUNT) == 0) {
311                                 write(1, ".", 1);
312                                 fflush(stdout);
313                         }
314                 }
315                 printf("\ncleanup: %6d removes\n", good_rms);
316         }
317         return 0;
318 }
319
320 int
321 auto_lookup(struct info *ip)
322 {
323         struct stat64 statb;
324         int retval;
325
326         retval = stat64(ip->name, &statb);
327         if (retval >= 0) {
328                 good_looks++;
329                 retval = 0;
330                 if (ip->exists == 0) {
331                         printf("\"%s\"(%llu) lookup, should not exist\n",
332                                 ip->name, (unsigned long long)statb.st_ino);
333                         retval = 1;
334                 } else if (ip->inumber != statb.st_ino) {
335                         printf("\"%s\"(%llu) lookup, should be inumber %llu\n",
336                                 ip->name, (unsigned long long)statb.st_ino,
337                                 (unsigned long long)ip->inumber);
338                         retval = 1;
339                 } else if (verbose) {
340                         printf("\"%s\"(%llu) lookup ok\n",
341                                 ip->name, (unsigned long long)statb.st_ino);
342                 }
343         } else if (errno == ENOENT) {
344                 bad_looks++;
345                 retval = 0;
346                 if (ip->exists == 1) {
347                         printf("\"%s\"(%llu) lookup, should exist\n",
348                                 ip->name, (unsigned long long)ip->inumber);
349                         retval = 1;
350                 } else if (verbose) {
351                         printf("\"%s\"(%llu) lookup ENOENT ok\n",
352                                 ip->name, (unsigned long long)ip->inumber);
353                 }
354         } else {
355                 retval = errno;
356                 printf("\"%s\"(%llu) on lookup: ",
357                         ip->name, (unsigned long long)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\"(%llu) created, but already existed as inumber %llu\n", ip->name, (unsigned long long)statb.st_ino, (unsigned long long)ip->inumber);
380                         retval = 1;
381                 } else if (verbose) {
382                         printf("\"%s\"(%llu) create new ok\n",
383                                 ip->name, (unsigned long long)statb.st_ino);
384                 }
385                 ip->exists = 1;
386                 ip->inumber = statb.st_ino;
387         } else if (errno == EEXIST) {
388                 bad_adds++;
389                 retval = 0;
390                 if (ip->exists == 0) {
391                         if (stat64(ip->name, &statb) < 0) {
392                                 perror("stat64");
393                                 exit(1);
394                         }
395                         printf("\"%s\"(%llu) not created, should not exist\n",
396                                 ip->name, (unsigned long long)statb.st_ino);
397                         retval = 1;
398                 } else if (verbose) {
399                         printf("\"%s\"(%llu) not created ok\n",
400                                 ip->name, (unsigned long long)ip->inumber);
401                 }
402                 ip->exists = 1;
403         } else {
404                 retval = errno;
405                 printf("\"%s\"(%llu) on create: ",
406                         ip->name, (unsigned long long)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\"(%llu) removed, should not have existed\n",
423                                 ip->name, (unsigned long long)ip->inumber);
424                         retval = 1;
425                 } else if (verbose) {
426                         printf("\"%s\"(%llu) remove ok\n",
427                                 ip->name, (unsigned long long)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\"(%llu) not removed, should have existed\n",
436                                 ip->name, (unsigned long long)ip->inumber);
437                         retval = 1;
438                 } else if (verbose) {
439                         printf("\"%s\"(%llu) not removed ok\n",
440                                 ip->name, (unsigned long long)ip->inumber);
441                 }
442                 ip->exists = 0;
443         } else {
444                 retval = errno;
445                 printf("\"%s\"(%llu) on remove: ",
446                         ip->name, (unsigned long long)ip->inumber);
447                 perror("unlink");
448         }
449         return(retval);
450 }