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