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