Whamcloud - gitweb
7da1ee6313ab6a86d27a7418724874d1c9fd41c7
[tools/e2fsprogs.git] / lib / ext2fs / test_io.c
1 /*
2  * test_io.c --- This is the Test I/O interface.
3  *
4  * Copyright (C) 1996 Theodore Ts'o.
5  *
6  * %Begin-Header%
7  * This file may be redistributed under the terms of the GNU Library
8  * General Public License, version 2.
9  * %End-Header%
10  */
11
12 #include <stdio.h>
13 #include <string.h>
14 #if HAVE_UNISTD_H
15 #include <unistd.h>
16 #endif
17 #include <fcntl.h>
18 #include <time.h>
19 #if HAVE_SYS_STAT_H
20 #include <sys/stat.h>
21 #endif
22 #if HAVE_SYS_TYPES_H
23 #include <sys/types.h>
24 #endif
25 #ifdef HAVE_SYS_PRCTL_H
26 #include <sys/prctl.h>
27 #else
28 #define PR_GET_DUMPABLE 3
29 #endif
30 #if (!defined(HAVE_PRCTL) && defined(linux))
31 #include <sys/syscall.h>
32 #endif
33
34 #include "ext2_fs.h"
35 #include "ext2fs.h"
36
37 /*
38  * For checking structure magic numbers...
39  */
40
41 #define EXT2_CHECK_MAGIC(struct, code) \
42           if ((struct)->magic != (code)) return (code)
43
44 struct test_private_data {
45         int     magic;
46         io_channel real;
47         int flags;
48         FILE *outfile;
49         unsigned long block;
50         int read_abort_count, write_abort_count;
51         void (*read_blk)(unsigned long block, int count, errcode_t err);
52         void (*write_blk)(unsigned long block, int count, errcode_t err);
53         void (*set_blksize)(int blksize, errcode_t err);
54         void (*write_byte)(unsigned long block, int count, errcode_t err);
55         void (*read_blk64)(unsigned long long block, int count, errcode_t err);
56         void (*write_blk64)(unsigned long long block, int count, errcode_t err);
57 };
58
59 static errcode_t test_open(const char *name, int flags, io_channel *channel);
60 static errcode_t test_close(io_channel channel);
61 static errcode_t test_set_blksize(io_channel channel, int blksize);
62 static errcode_t test_read_blk(io_channel channel, unsigned long block,
63                                int count, void *data);
64 static errcode_t test_write_blk(io_channel channel, unsigned long block,
65                                 int count, const void *data);
66 static errcode_t test_read_blk64(io_channel channel, unsigned long long block,
67                                int count, void *data);
68 static errcode_t test_write_blk64(io_channel channel, unsigned long long block,
69                                 int count, const void *data);
70 static errcode_t test_flush(io_channel channel);
71 static errcode_t test_write_byte(io_channel channel, unsigned long offset,
72                                  int count, const void *buf);
73 static errcode_t test_set_option(io_channel channel, const char *option,
74                                  const char *arg);
75 static errcode_t test_get_stats(io_channel channel, io_stats *stats);
76 static errcode_t test_discard(io_channel channel, unsigned long long block,
77                               unsigned long long count);
78
79 static struct struct_io_manager struct_test_manager = {
80         EXT2_ET_MAGIC_IO_MANAGER,
81         "Test I/O Manager",
82         test_open,
83         test_close,
84         test_set_blksize,
85         test_read_blk,
86         test_write_blk,
87         test_flush,
88         test_write_byte,
89         test_set_option,
90         test_get_stats,
91         test_read_blk64,
92         test_write_blk64,
93         test_discard,
94 };
95
96 io_manager test_io_manager = &struct_test_manager;
97
98 /*
99  * These global variable can be set by the test program as
100  * necessary *before* calling test_open
101  */
102 io_manager test_io_backing_manager = 0;
103 void (*test_io_cb_read_blk)
104         (unsigned long block, int count, errcode_t err) = 0;
105 void (*test_io_cb_write_blk)
106         (unsigned long block, int count, errcode_t err) = 0;
107 void (*test_io_cb_read_blk64)
108         (unsigned long long block, int count, errcode_t err) = 0;
109 void (*test_io_cb_write_blk64)
110         (unsigned long long block, int count, errcode_t err) = 0;
111 void (*test_io_cb_set_blksize)
112         (int blksize, errcode_t err) = 0;
113 void (*test_io_cb_write_byte)
114         (unsigned long block, int count, errcode_t err) = 0;
115
116 /*
117  * Test flags
118  */
119 #define TEST_FLAG_READ                  0x01
120 #define TEST_FLAG_WRITE                 0x02
121 #define TEST_FLAG_SET_BLKSIZE           0x04
122 #define TEST_FLAG_FLUSH                 0x08
123 #define TEST_FLAG_DUMP                  0x10
124 #define TEST_FLAG_SET_OPTION            0x20
125 #define TEST_FLAG_DISCARD               0x40
126
127 static void test_dump_block(io_channel channel,
128                             struct test_private_data *data,
129                             unsigned long block, const void *buf)
130 {
131         const unsigned char *cp;
132         FILE *f = data->outfile;
133         int     i;
134         unsigned long   cksum = 0;
135
136         for (i=0, cp = buf; i < channel->block_size; i++, cp++) {
137                 cksum += *cp;
138         }
139         fprintf(f, "Contents of block %lu, checksum %08lu: \n", block, cksum);
140         for (i=0, cp = buf; i < channel->block_size; i++, cp++) {
141                 if ((i % 16) == 0)
142                         fprintf(f, "%04x: ", i);
143                 fprintf(f, "%02x%c", *cp, ((i % 16) == 15) ? '\n' : ' ');
144         }
145 }
146
147 static void test_abort(io_channel channel, unsigned long block)
148 {
149         struct test_private_data *data;
150         FILE *f;
151
152         data = (struct test_private_data *) channel->private_data;
153         f = data->outfile;
154         test_flush(channel);
155
156         fprintf(f, "Aborting due to I/O to block %lu\n", block);
157         fflush(f);
158         abort();
159 }
160
161 static char *safe_getenv(const char *arg)
162 {
163         if ((getuid() != geteuid()) || (getgid() != getegid()))
164                 return NULL;
165 #if HAVE_PRCTL
166         if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 0)
167                 return NULL;
168 #else
169 #if (defined(linux) && defined(SYS_prctl))
170         if (syscall(SYS_prctl, PR_GET_DUMPABLE, 0, 0, 0, 0) == 0)
171                 return NULL;
172 #endif
173 #endif
174
175 #ifdef HAVE___SECURE_GETENV
176         return __secure_getenv(arg);
177 #else
178         return getenv(arg);
179 #endif
180 }
181
182 static errcode_t test_open(const char *name, int flags, io_channel *channel)
183 {
184         io_channel      io = NULL;
185         struct test_private_data *data = NULL;
186         errcode_t       retval;
187         char            *value;
188
189         if (name == 0)
190                 return EXT2_ET_BAD_DEVICE_NAME;
191         retval = ext2fs_get_mem(sizeof(struct struct_io_channel), &io);
192         if (retval)
193                 return retval;
194         memset(io, 0, sizeof(struct struct_io_channel));
195         io->magic = EXT2_ET_MAGIC_IO_CHANNEL;
196         retval = ext2fs_get_mem(sizeof(struct test_private_data), &data);
197         if (retval) {
198                 retval = EXT2_ET_NO_MEMORY;
199                 goto cleanup;
200         }
201         io->manager = test_io_manager;
202         retval = ext2fs_get_mem(strlen(name)+1, &io->name);
203         if (retval)
204                 goto cleanup;
205
206         strcpy(io->name, name);
207         io->private_data = data;
208         io->block_size = 1024;
209         io->read_error = 0;
210         io->write_error = 0;
211         io->refcount = 1;
212
213         memset(data, 0, sizeof(struct test_private_data));
214         data->magic = EXT2_ET_MAGIC_TEST_IO_CHANNEL;
215         if (test_io_backing_manager) {
216                 retval = test_io_backing_manager->open(name, flags,
217                                                        &data->real);
218                 if (retval)
219                         goto cleanup;
220         } else
221                 data->real = 0;
222         data->read_blk =        test_io_cb_read_blk;
223         data->write_blk =       test_io_cb_write_blk;
224         data->set_blksize =     test_io_cb_set_blksize;
225         data->write_byte =      test_io_cb_write_byte;
226         data->read_blk64 =      test_io_cb_read_blk64;
227         data->write_blk64 =     test_io_cb_write_blk64;
228
229         data->outfile = NULL;
230         if ((value = safe_getenv("TEST_IO_LOGFILE")) != NULL)
231                 data->outfile = fopen(value, "w");
232         if (!data->outfile)
233                 data->outfile = stderr;
234
235         data->flags = 0;
236         if ((value = safe_getenv("TEST_IO_FLAGS")) != NULL)
237                 data->flags = strtoul(value, NULL, 0);
238
239         data->block = 0;
240         if ((value = safe_getenv("TEST_IO_BLOCK")) != NULL)
241                 data->block = strtoul(value, NULL, 0);
242
243         data->read_abort_count = 0;
244         if ((value = safe_getenv("TEST_IO_READ_ABORT")) != NULL)
245                 data->read_abort_count = strtoul(value, NULL, 0);
246
247         data->write_abort_count = 0;
248         if ((value = safe_getenv("TEST_IO_WRITE_ABORT")) != NULL)
249                 data->write_abort_count = strtoul(value, NULL, 0);
250
251         *channel = io;
252         return 0;
253
254 cleanup:
255         if (io)
256                 ext2fs_free_mem(&io);
257         if (data)
258                 ext2fs_free_mem(&data);
259         return retval;
260 }
261
262 static errcode_t test_close(io_channel channel)
263 {
264         struct test_private_data *data;
265         errcode_t       retval = 0;
266
267         EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
268         data = (struct test_private_data *) channel->private_data;
269         EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
270
271         if (--channel->refcount > 0)
272                 return 0;
273
274         if (data->real)
275                 retval = io_channel_close(data->real);
276
277         if (data->outfile && data->outfile != stderr)
278                 fclose(data->outfile);
279
280         ext2fs_free_mem(&channel->private_data);
281         if (channel->name)
282                 ext2fs_free_mem(&channel->name);
283         ext2fs_free_mem(&channel);
284         return retval;
285 }
286
287 static errcode_t test_set_blksize(io_channel channel, int blksize)
288 {
289         struct test_private_data *data;
290         errcode_t       retval = 0;
291
292         EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
293         data = (struct test_private_data *) channel->private_data;
294         EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
295
296         if (data->real)
297                 retval = io_channel_set_blksize(data->real, blksize);
298         if (data->set_blksize)
299                 data->set_blksize(blksize, retval);
300         if (data->flags & TEST_FLAG_SET_BLKSIZE)
301                 fprintf(data->outfile,
302                         "Test_io: set_blksize(%d) returned %s\n",
303                         blksize, retval ? error_message(retval) : "OK");
304         channel->block_size = blksize;
305         return retval;
306 }
307
308
309 static errcode_t test_read_blk(io_channel channel, unsigned long block,
310                                int count, void *buf)
311 {
312         struct test_private_data *data;
313         errcode_t       retval = 0;
314
315         EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
316         data = (struct test_private_data *) channel->private_data;
317         EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
318
319         if (data->real)
320                 retval = io_channel_read_blk(data->real, block, count, buf);
321         if (data->read_blk)
322                 data->read_blk(block, count, retval);
323         if (data->flags & TEST_FLAG_READ)
324                 fprintf(data->outfile,
325                         "Test_io: read_blk(%lu, %d) returned %s\n",
326                         block, count, retval ? error_message(retval) : "OK");
327         if (data->block && data->block == block) {
328                 if (data->flags & TEST_FLAG_DUMP)
329                         test_dump_block(channel, data, block, buf);
330                 if (--data->read_abort_count == 0)
331                         test_abort(channel, block);
332         }
333         return retval;
334 }
335
336 static errcode_t test_write_blk(io_channel channel, unsigned long block,
337                                int count, const void *buf)
338 {
339         struct test_private_data *data;
340         errcode_t       retval = 0;
341
342         EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
343         data = (struct test_private_data *) channel->private_data;
344         EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
345
346         if (data->real)
347                 retval = io_channel_write_blk(data->real, block, count, buf);
348         if (data->write_blk)
349                 data->write_blk(block, count, retval);
350         if (data->flags & TEST_FLAG_WRITE)
351                 fprintf(data->outfile,
352                         "Test_io: write_blk(%lu, %d) returned %s\n",
353                         block, count, retval ? error_message(retval) : "OK");
354         if (data->block && data->block == block) {
355                 if (data->flags & TEST_FLAG_DUMP)
356                         test_dump_block(channel, data, block, buf);
357                 if (--data->write_abort_count == 0)
358                         test_abort(channel, block);
359         }
360         return retval;
361 }
362
363 static errcode_t test_read_blk64(io_channel channel, unsigned long long block,
364                                int count, void *buf)
365 {
366         struct test_private_data *data;
367         errcode_t       retval = 0;
368
369         EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
370         data = (struct test_private_data *) channel->private_data;
371         EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
372
373         if (data->real)
374                 retval = io_channel_read_blk64(data->real, block, count, buf);
375         if (data->read_blk64)
376                 data->read_blk64(block, count, retval);
377         if (data->flags & TEST_FLAG_READ)
378                 fprintf(data->outfile,
379                         "Test_io: read_blk64(%llu, %d) returned %s\n",
380                         block, count, retval ? error_message(retval) : "OK");
381         if (data->block && data->block == block) {
382                 if (data->flags & TEST_FLAG_DUMP)
383                         test_dump_block(channel, data, block, buf);
384                 if (--data->read_abort_count == 0)
385                         test_abort(channel, block);
386         }
387         return retval;
388 }
389
390 static errcode_t test_write_blk64(io_channel channel, unsigned long long block,
391                                int count, const void *buf)
392 {
393         struct test_private_data *data;
394         errcode_t       retval = 0;
395
396         EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
397         data = (struct test_private_data *) channel->private_data;
398         EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
399
400         if (data->real)
401                 retval = io_channel_write_blk64(data->real, block, count, buf);
402         if (data->write_blk64)
403                 data->write_blk64(block, count, retval);
404         if (data->flags & TEST_FLAG_WRITE)
405                 fprintf(data->outfile,
406                         "Test_io: write_blk64(%llu, %d) returned %s\n",
407                         block, count, retval ? error_message(retval) : "OK");
408         if (data->block && data->block == block) {
409                 if (data->flags & TEST_FLAG_DUMP)
410                         test_dump_block(channel, data, block, buf);
411                 if (--data->write_abort_count == 0)
412                         test_abort(channel, block);
413         }
414         return retval;
415 }
416
417 static errcode_t test_write_byte(io_channel channel, unsigned long offset,
418                                int count, const void *buf)
419 {
420         struct test_private_data *data;
421         errcode_t       retval = 0;
422
423         EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
424         data = (struct test_private_data *) channel->private_data;
425         EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
426
427         if (data->real && data->real->manager->write_byte)
428                 retval = io_channel_write_byte(data->real, offset, count, buf);
429         if (data->write_byte)
430                 data->write_byte(offset, count, retval);
431         if (data->flags & TEST_FLAG_WRITE)
432                 fprintf(data->outfile,
433                         "Test_io: write_byte(%lu, %d) returned %s\n",
434                         offset, count, retval ? error_message(retval) : "OK");
435         return retval;
436 }
437
438 /*
439  * Flush data buffers to disk.
440  */
441 static errcode_t test_flush(io_channel channel)
442 {
443         struct test_private_data *data;
444         errcode_t       retval = 0;
445
446         EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
447         data = (struct test_private_data *) channel->private_data;
448         EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
449
450         if (data->real)
451                 retval = io_channel_flush(data->real);
452
453         if (data->flags & TEST_FLAG_FLUSH)
454                 fprintf(data->outfile, "Test_io: flush() returned %s\n",
455                         retval ? error_message(retval) : "OK");
456
457         return retval;
458 }
459
460 static errcode_t test_set_option(io_channel channel, const char *option,
461                                  const char *arg)
462 {
463         struct test_private_data *data;
464         errcode_t       retval = 0;
465
466         EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
467         data = (struct test_private_data *) channel->private_data;
468         EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
469
470
471         if (data->flags & TEST_FLAG_SET_OPTION)
472                 fprintf(data->outfile, "Test_io: set_option(%s, %s) ",
473                         option, arg);
474         if (data->real && data->real->manager->set_option) {
475                 retval = (data->real->manager->set_option)(data->real,
476                                                            option, arg);
477                 if (data->flags & TEST_FLAG_SET_OPTION)
478                         fprintf(data->outfile, "returned %s\n",
479                                 retval ? error_message(retval) : "OK");
480         } else {
481                 if (data->flags & TEST_FLAG_SET_OPTION)
482                         fprintf(data->outfile, "not implemented\n");
483         }
484         return retval;
485 }
486
487 static errcode_t test_get_stats(io_channel channel, io_stats *stats)
488 {
489         struct test_private_data *data;
490         errcode_t       retval = 0;
491
492         EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
493         data = (struct test_private_data *) channel->private_data;
494         EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
495
496         if (data->real && data->real->manager->get_stats) {
497                 retval = (data->real->manager->get_stats)(data->real, stats);
498         }
499         return retval;
500 }
501
502 static errcode_t test_discard(io_channel channel, unsigned long long block,
503                               unsigned long long count)
504 {
505         struct test_private_data *data;
506         errcode_t       retval = 0;
507
508         EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
509         data = (struct test_private_data *) channel->private_data;
510         EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
511
512         if (data->real)
513                 retval = io_channel_discard(data->real, block, count);
514         if (data->flags & TEST_FLAG_DISCARD)
515                 fprintf(data->outfile,
516                         "Test_io: discard(%llu, %llu) returned %s\n",
517                         block, count, retval ? error_message(retval) : "OK");
518         return retval;
519 }