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