QA source updates and some associated test changes
[xfstests-dev.git] / src / fill2.c
1 /*
2  * Copyright (c) 2000-2003 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
34 /*
35  * fill2:
36  *   Derived from fill.c, adds command line options, block boundary marking
37  *   and allows me to tweak to suit fill2fs without breaking other qa tests.
38  *   This version marks block boundaries (512, 1k, 4k and 16k) within the file
39  *   using characters guaranteed not to appear anywhere else, this may allow
40  *   simple checks in future which can inspect these offsets and ensure one of
41  *   of the four characters is present. Also fixes off-by-one error in fill.c
42  *   and is more careful about checking when write operations fail - this is 
43  *   needed by fill2fs to ensure that the number of bytes written is accurately
44  *   determined.
45  */
46
47 #include <xfs/libxfs.h>
48
49 #define N(x) (sizeof(x)/sizeof(x[0]))
50
51 /* function prototypes */
52 static void illegal(char *, char *);
53 static void reqval(char, char * [], int);
54 static void respec(char, char * [], int);
55 static void unknown(char, char *);
56 static void usage(void);
57 static char *progname;
58
59 char *dopts[] =         { "nbytes", "linelength", "seed", "file", NULL };
60 enum                    { D_NBYTES, D_LINELENGTH, D_SEED, D_FILE, D_ISSET, D_NUM };
61 int dflags[D_NUM] =     { 0 };
62 char *bopts[] =         { "512", "1k", "4k", "16k" };
63 enum                    { B_512, B_1K, B_4K, B_16K, B_ISSET, B_NUM };
64 int bflags[B_NUM] =     { 0 };
65
66
67 /*
68  * block boundaries
69  *
70  */
71
72 /* block boundaries which should be flagged in the output file */
73 /* flag is the character that should be used to indicate each type */
74 /* of block boundary */
75
76
77 struct s_block_type {
78     int mark;
79     int size;
80     char flag;
81 };
82
83 static struct s_block_type btypes[] = {
84     { 0, 512, '!' },    /* 512 */
85     { 0, 1024, '"' },   /* 1k */
86     { 0, 4096, '#' },   /* 4k */
87     { 0, 16384, '$' },  /* 16k */
88 };
89
90 /*
91  * main
92  *
93  */
94
95 int
96 main(int argc, char **argv)
97 {
98     int                 status = 0;     /* return status */
99     int                 c;              /* current option character */
100     char                *p;             /* for getsubopt calls */
101     long                nbytes;         /* total number of bytes to write */
102     int                 dlen;           /* length of normal output data line */
103     const char          *dseed = NULL;  /* input string for seeding rand */
104     unsigned int        seed;           /* seed for output data */    
105     char                *dfile = NULL;  /* where to write output */
106
107     FILE                *f;             /* output file */
108     char                *dbuf;          /* output line buffer */
109     char                bbuf[50];       /* block boundary string */
110     char                *active = NULL; /* active buffer to copy out of */
111     size_t              hlen;           /* header length (bytes+key) in output */
112                                         /* lines */
113     char                *hptr;          /* pointer to end of header */
114     char                *ptr;           /* current position to copy from */
115     int                 blktype = 0;    /* current block boundary type */
116     int                 boundary;       /* set if current output char lies on */
117                                         /* block boundary */
118     long                i;
119     int                 j;
120     int                 l = 0;
121
122
123     /* defaults */
124     progname = basename(argv[0]);
125     nbytes = 1024 * 1024;
126     dlen = 73;  /* includes the trailing newline */
127
128     while ((c = getopt(argc, argv, "d:b:")) != EOF) {
129         switch (c) {
130         case 'd':
131             if (dflags[D_ISSET])
132                 respec('d', NULL, 0);
133             dflags[D_ISSET] = 1;
134
135             p = optarg;
136             while (*p != '\0') {
137                 char *value;
138                 switch (getsubopt(&p, (constpp)dopts, &value)) {
139                 case D_NBYTES:
140                     if (! value) reqval('d', dopts, D_NBYTES);
141                     if (dflags[D_NBYTES]) respec('d', dopts, D_NBYTES);
142                     dflags[D_NBYTES] = 1;
143                     nbytes = strtol(value, &ptr, 10);
144                     if (ptr == value) illegal(value, "d nbytes");
145                     break;
146
147                 case D_LINELENGTH:
148                     if (! value) reqval('d', dopts, D_LINELENGTH);
149                     if (dflags[D_LINELENGTH]) respec('d', dopts, D_LINELENGTH);
150                     dflags[D_LINELENGTH] = 1;
151                     dlen = (int) strtol(value, &ptr, 10) + 1;
152                     if (ptr == value) illegal(value, "d linelength");
153                     break;
154
155                 case D_SEED:
156                     if (! value) reqval('d', dopts, D_SEED);
157                     if (dflags[D_SEED]) respec('D', dopts, D_SEED);
158                     dflags[D_SEED] = 1;
159                     dseed = value;
160                     break;
161
162                 case D_FILE:
163                     if (! value) reqval('d', dopts, D_FILE);
164                     if (dflags[D_FILE]) respec('d', dopts, D_FILE);
165                     dflags[D_FILE] = 1;
166                     dfile = value;
167                     break;
168                     
169                 default:
170                     unknown('d', value);
171                 }
172             }
173             break;
174
175         case 'b':
176             if (bflags[B_ISSET])
177                 respec('b', NULL, 0);
178             bflags[B_ISSET] = 1;
179
180             p = optarg;
181             while (*p != '\0') {
182                 char *value;
183                 switch (getsubopt(&p, (constpp)bopts, &value)) {
184                 case B_512:
185                     if (value) illegal(value, "b 512");
186                     if (bflags[B_512]) respec('b', bopts, B_512);
187                     bflags[B_512] = 1;
188                     btypes[0].mark = 1;
189                     break;
190
191                 case B_1K:
192                     if (value) illegal(value, "b 1k");
193                     if (bflags[B_1K]) respec('b', bopts, B_1K);
194                     bflags[B_1K] = 1;
195                     btypes[1].mark = 1;
196                     break;
197
198                 case B_4K:
199                     if (value) illegal(value, "b 4k");
200                     if (bflags[B_4K]) respec('b', bopts, B_4K);
201                     bflags[B_4K] = 1;
202                     btypes[2].mark = 1;
203                     break;
204
205                 case B_16K:
206                     if (value) illegal(value, "b 16k");
207                     if (bflags[B_16K]) respec('b', bopts, B_16K);
208                     bflags[B_16K] = 1;
209                     btypes[3].mark = 1;
210                     break;
211
212                 default:
213                     unknown('b', value);
214                     break;
215                 }
216             }
217             break;
218
219         case '?':
220             usage();
221         }
222     }
223
224     if (dflags[D_FILE] && optind != argc)
225         usage();
226
227     if (! dflags[D_FILE] && argc - optind != 1)
228         usage();
229
230     if (! dflags[D_FILE])
231         dfile = argv[optind];
232
233     if ((f = fopen(dfile, "w")) == NULL) {
234         fprintf(stderr, "fill2: cannot create \"%s\": %s\n",
235                 dfile, strerror(errno));
236         status = 1;
237         goto cleanup;
238     }
239
240     if (! dflags[D_SEED]) 
241         dseed = dfile;
242
243     /* seed is an ascii string */
244     seed = 0;
245     i = 0;
246     while (dseed[i]) {
247         seed <<= 8;
248         seed |= dseed[i];
249         i++;
250     }
251     srand(seed);
252
253
254     /* normal data line format: XXXXXXXXXXXX CCCC...CCC CCCCCCCCCCCC...CCC */
255     /* block boundary format: CXXXXXXX */
256
257     dbuf = (char *) malloc(dlen + 1);
258     hlen = 12+1+strlen(dseed)+1;
259     assert(hlen < dlen);
260     hptr = dbuf + hlen;
261
262     for (i = 0; i < nbytes; i++) {
263
264         
265         /* is this a block boundary? check largest to smallest */
266         boundary = 0;
267         for (j = N(btypes) - 1; j >= 0; j--) {
268             if (btypes[j].mark) {
269                 /* first time through or just before a block boundary? */
270                 if (i == 0 || i % btypes[j].size == btypes[j].size - 1) {
271                     boundary = 1;
272                     blktype = j;
273                     break;
274                 }
275             }
276         }
277
278
279         /* are there block boundaries to check? */
280         /* is this the first time through? */
281         /* block boundaries take priority over other output */
282         if (bflags[B_ISSET] && (i == 0 || boundary)) {
283             sprintf(bbuf, "%s%c%07ld\n",
284                     i ? "\n" : "",
285                     btypes[blktype].flag,
286                     /* remember i is one less than block boundary */
287                     i ? (i+1) / btypes[blktype].size : 0);
288             active = bbuf;
289             ptr = bbuf;
290         }
291         /* are we at the start of a new line? */
292         else if (i == 0
293                  || (active == bbuf && *ptr == '\0')
294                  || (active == dbuf && l == dlen))
295         {
296             sprintf(dbuf, "%012ld %s ", i, dseed);
297             assert(*hptr == '\0');
298             for (ptr = hptr; ptr != dbuf + dlen - 1; ptr++) {
299                 /* characters upto 126 '~' are used */
300                 /* do not use !"#$ - leave free for use as block markers */
301                 *ptr = 37 + (rand() % (127 - 37)); 
302             }
303             *ptr++ = '\n';
304             *ptr = '\0';
305             assert(ptr == dbuf + dlen);
306             active = dbuf;
307             ptr = dbuf;
308             l = 0;
309         }
310         else {
311             /* continue copying from the active buffer */
312         }
313
314         /* output one new character from current buffer */
315         if (fputc(*ptr++, f) == EOF) {
316             fprintf(stderr, "fill2: could not write character to \"%s\": %s\n",
317                     dfile, strerror(errno));
318             status = 1;
319             goto cleanup;
320         }
321         if (active == dbuf) l++;
322
323     }
324
325  cleanup:
326
327     /* close file and flush buffers - check if this fails */
328     if (fclose(f) != 0) {
329         fprintf(stderr, "fill2: fclose() on \"%s\" failed: %s\n",
330                 dfile, strerror(errno));
331         status = 1;
332     }
333     exit(status);
334 }
335
336
337
338 /*
339  * suboption checking functions
340  *
341  */
342
343 static void
344 illegal(char *value, char *opt)
345 {
346     fprintf(stderr, "%s: Error: Illegal value \"%s\" for -%s option\n",
347             progname, value, opt);
348     usage();
349 }
350 static void
351 reqval(char opt, char *tab[], int idx)
352 {
353     fprintf(stderr, "%s: Error: -%c %s option requires a value\n",
354             progname, opt, tab[idx]);
355     usage();
356 }
357 static void
358 respec(char opt, char *tab[], int idx)
359 {
360     fprintf(stderr, "%s: Error: ", progname);
361     fprintf(stderr, "-%c ", opt);
362     if (tab) fprintf(stderr, "%s ", tab[idx]);
363     fprintf(stderr, "option respecified\n");
364     usage();
365 }
366 static void
367 unknown(char opt, char *s)
368 {
369     fprintf(stderr, "%s: Error: Unknown option -%c %s\n", progname, opt, s);
370     usage();
371 }
372 static void
373 usage(void)
374 {
375     fprintf(stderr, "Usage: %s [options] filename\n"
376 "Options:\n"
377 "  -d [nbytes=num,linelength=num,   output data properties\n"
378 "      seed=string,file=name]\n"
379 "  -b [512,1k,4k,16k]               where to mark block boundaries\n", progname);
380     exit(2);
381 }