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