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