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