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); 69 70static 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 83io_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 */ 89io_manager test_io_backing_manager = 0; 90void (*test_io_cb_read_blk) 91 (unsigned long block, int count, errcode_t err) = 0; 92void (*test_io_cb_write_blk) 93 (unsigned long block, int count, errcode_t err) = 0; 94void (*test_io_cb_set_blksize) 95 (int blksize, errcode_t err) = 0; 96void (*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 109static 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 129static 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 143static 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 164static 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 234cleanup: 235 if (io) 236 ext2fs_free_mem(&io); 237 if (data) 238 ext2fs_free_mem(&data); 239 return retval; 240} 241 242static 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 267static 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 289static 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 316static 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 343static 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 */ 367static 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 386static 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