use non-zero data
[tridge/junkcode.git] / fstest-aio.c
1 /* filesystem verification tool, designed to detect data corruption on a filesystem
2
3    tridge@samba.org, March 2002
4  */
5 #define _GNU_SOURCE
6
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <unistd.h>
10 #include <sys/stat.h>
11 #include <sys/wait.h>
12 #include <sys/types.h>
13 #include <sys/stat.h>
14 #include <sys/mman.h>
15 #include <dirent.h>
16 #include <fcntl.h>
17 #include <errno.h>
18 #include <libaio.h>
19
20 /* variables settable on the command line */
21 static int loop_count = 100;
22 static int num_files = 1;
23 static int file_size = 1024*1024;
24 static int block_size = 1024;
25 static char *base_dir = ".";
26 static int use_mmap;
27 static int use_sync;
28
29 typedef unsigned char uchar;
30
31 #ifndef MIN
32 #define MIN(a,b) ((a)<(b)?(a):(b))
33 #endif
34
35 static void fatal(char * msg)
36 {
37         perror(msg);
38         exit(1);
39 }
40
41 static ssize_t a_pread(int fd, void *buf, size_t count, off_t offset)
42 {
43         struct io_event ev;
44         struct iocb iocb;
45         struct iocb *iocbp = &iocb;
46         io_context_t io_ctx;
47
48         memset(&io_ctx, 0, sizeof(io_ctx));
49
50         if (io_setup(1, &io_ctx) != 0) {
51                 fatal("io_setup failed");
52         }
53
54         io_prep_pread(&iocb, fd, buf, count, offset);
55
56         if (io_submit(io_ctx, 1, &iocbp) != 1) {
57                 fatal("io_submit failed");
58         }
59
60         if (io_getevents(io_ctx, 1, 1, &ev, NULL) != 1) {
61                 fatal("io_getevents failed");
62         }
63
64         io_destroy(io_ctx);
65
66         return iocb.u.c.nbytes;
67 }
68
69 static void *x_malloc(int size)
70 {
71         void *ret = malloc(size);
72         if (!ret) {
73                 fprintf(stderr,"Out of memory for size %d!\n", size);
74                 exit(1);
75         }
76         return ret;
77 }
78
79
80 /* generate a buffer for a particular child, fnum etc. Just use a simple buffer
81    to make debugging easy 
82 */
83 static void gen_buffer(uchar *buf, int loop, int child, int fnum, int ofs)
84 {
85         uchar v = (loop+child+fnum+(ofs/block_size)) % 256;
86         memset(buf, v, block_size);
87 }
88
89 /* 
90    check if a buffer from disk is correct
91 */
92 static void check_buffer(uchar *buf, int loop, int child, int fnum, int ofs)
93 {
94         uchar *buf2;
95
96         buf2 = x_malloc(block_size);
97
98         gen_buffer(buf2, loop, child, fnum, ofs);
99         
100         if (memcmp(buf, buf2, block_size) != 0) {
101                 int i, j;
102                 for (i=0;buf[i] == buf2[i] && i<block_size;i++) ;
103                 fprintf(stderr,"Corruption in child %d fnum %d at offset %d\n",
104                         child, fnum, ofs+i);
105
106                 printf("Correct:   ");
107                 for (j=0;j<MIN(20, block_size-i);j++) {
108                         printf("%02x ", buf2[j+i]);
109                 }
110                 printf("\n");
111
112                 printf("Incorrect: ");
113                 for (j=0;j<MIN(20, block_size-i);j++) {
114                         printf("%02x ", buf[j+i]);
115                 }
116                 printf("\n");
117                 exit(1);
118         }
119
120         free(buf2);
121 }
122
123 /*
124   create a file with a known data set for a child
125  */
126 static void create_file(const char *dir, int loop, int child, int fnum)
127 {
128         uchar *buf;
129         int size, fd;
130         char fname[1024];
131
132         buf = x_malloc(block_size);
133         sprintf(fname, "%s/file%d", dir, fnum);
134         fd = open(fname, O_RDWR|O_CREAT|O_TRUNC | (use_sync?O_SYNC:0), 0644);
135         if (fd == -1) {
136                 perror(fname);
137                 exit(1);
138         }
139                 
140         if (!use_mmap) {
141                 for (size=0; size<file_size; size += block_size) {
142                         gen_buffer(buf, loop, child, fnum, size);
143                         if (pwrite(fd, buf, block_size, size) != block_size) {
144                                 fprintf(stderr,"Write failed at offset %d\n", size);
145                                 exit(1);
146                         }
147                 }
148         } else {
149                 char *p;
150                 if (ftruncate(fd, file_size) != 0) {
151                         perror("ftruncate");
152                         exit(1);
153                 }
154                 p = mmap(NULL, file_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
155                 if (p == (char *)-1) {
156                         perror("mmap");
157                         exit(1);
158                 }
159                 for (size=0; size<file_size; size += block_size) {
160                         gen_buffer(p+size, loop, child, fnum, size);
161                 }
162                 munmap(p, file_size);
163         }
164
165         free(buf);
166         close(fd);
167 }
168
169 /* 
170    check that a file has the right data
171  */
172 static void check_file(const char *dir, int loop, int child, int fnum)
173 {
174         uchar *buf;
175         int size, fd;
176         char fname[1024];
177
178         buf = x_malloc(block_size);
179
180         sprintf(fname, "%s/file%d", dir, fnum);
181         fd = open(fname, O_RDONLY);
182         if (fd == -1) {
183                 perror(fname);
184                 exit(1);
185         }
186
187         for (size=0; size<file_size; size += block_size) {
188                 if (a_pread(fd, buf, block_size, size) != block_size) {
189                         fprintf(stderr,"read failed at offset %d\n", size);
190                         exit(1);
191                 }
192                 check_buffer(buf, loop, child, fnum, size);
193         }
194
195         free(buf);
196         close(fd);
197 }
198
199 /* 
200    revsusive directory traversal - used for cleanup
201    fn() is called on all files/dirs in the tree
202  */
203 void traverse(const char *dir, int (*fn)(const char *))
204 {
205         DIR *d;
206         struct dirent *de;
207
208         d = opendir(dir);
209         if (!d) return;
210
211         while ((de = readdir(d))) {
212                 char fname[1024];
213                 struct stat st;
214
215                 if (strcmp(de->d_name,".") == 0) continue;
216                 if (strcmp(de->d_name,"..") == 0) continue;
217
218                 sprintf(fname, "%s/%s", dir, de->d_name);
219                 if (lstat(fname, &st)) {
220                         perror(fname);
221                         continue;
222                 }
223
224                 if (S_ISDIR(st.st_mode)) {
225                         traverse(fname, fn);
226                 }
227
228                 fn(fname);
229         }
230
231         closedir(d);
232 }
233
234 /* the main child function - this creates/checks the file for one child */
235 static void run_child(int child)
236 {
237         int i, loop;
238         char dir[1024];
239
240         sprintf(dir, "%s/child%d", base_dir, child);
241
242         /* cleanup any old files */
243         if (remove(dir) != 0 && errno != ENOENT) {
244                 printf("Child %d cleaning %s\n", child, dir);
245                 traverse(dir, remove);
246                 remove(dir);
247         }
248
249         if (mkdir(dir, 0755) != 0) {
250                 perror(dir);
251                 exit(1);
252         }
253
254         for (loop = 0; loop < loop_count; loop++) {
255                 printf("Child %d loop %d\n", child, loop);
256                 for (i=0;i<num_files;i++) {
257                         create_file(dir, loop, child, i);
258                 }
259                 for (i=0;i<num_files;i++) {
260                         check_file(dir, loop, child, i);
261                 }
262         }
263
264         /* cleanup afterwards */
265         printf("Child %d cleaning up %s\n", child, dir);
266         traverse(dir, remove);
267         remove(dir);
268
269         exit(0);
270 }
271
272 static void usage(void)
273 {
274         printf("\n"
275 "Usage: fstest [options]\n"
276 "\n"
277 " -n num_children       set number of child processes\n"
278 " -f num_files          set number of files\n"
279 " -s file_size          set file sizes\n"
280 " -b block_size         set block (IO) size\n"
281 " -p path               set base path\n"
282 " -l loops              set loop count\n"
283 " -m                    use mmap\n"
284 " -S                    use synchronous IO\n"
285 " -h                    show this help message\n");
286 }
287
288 /* main program */
289 int main(int argc, char *argv[])
290 {
291         int c;
292         extern char *optarg;
293         extern int optind;
294         int num_children = 1;
295         int i, status, ret;
296
297         while ((c = getopt(argc, argv, "n:s:f:p:l:b:Shm")) != -1) {
298                 switch (c) {
299                 case 'n':
300                         num_children = strtol(optarg, NULL, 0);
301                         break;
302                 case 'b':
303                         block_size = strtol(optarg, NULL, 0);
304                         break;
305                 case 'f':
306                         num_files = strtol(optarg, NULL, 0);
307                         break;
308                 case 's':
309                         file_size = strtol(optarg, NULL, 0);
310                         break;
311                 case 'p':
312                         base_dir = optarg;
313                         break;
314                 case 'm':
315                         use_mmap = 1;
316                         break;
317                 case 'S':
318                         use_sync = 1;
319                         break;
320                 case 'l':
321                         loop_count = strtol(optarg, NULL, 0);
322                         break;
323                 case 'h':
324                         usage();
325                         exit(0);
326                 default:
327                         usage();
328                         exit(1);
329                 }
330         }
331
332         argc -= optind;
333         argv += optind;
334
335         /* round up the file size */
336         if (file_size % block_size != 0) {
337                 file_size = (file_size + (block_size-1)) / block_size;
338                 file_size *= block_size;
339                 printf("Rounded file size to %d\n", file_size);
340         }
341
342         printf("num_children=%d file_size=%d num_files=%d loop_count=%d block_size=%d\nmmap=%d sync=%d\n",
343                num_children, file_size, num_files, loop_count, block_size, use_mmap, use_sync);
344
345         printf("Total data size %.1f Mbyte\n",
346                num_files * num_children * 1.0e-6 * file_size);
347
348         /* fork and run run_child() for each child */
349         for (i=0;i<num_children;i++) {
350                 if (fork() == 0) {
351                         run_child(i);
352                         exit(0);
353                 }
354         }
355
356         ret = 0;
357
358         /* wait for children to exit */
359         while (waitpid(0, &status, 0) == 0 || errno != ECHILD) {
360                 if (WEXITSTATUS(status) != 0) {
361                         ret = WEXITSTATUS(status);
362                         printf("Child exited with status %d\n", ret);
363                 }
364         }
365
366         if (ret != 0) {
367                 printf("fstest failed with status %d\n", ret);
368         }
369
370         return ret;
371 }