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