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 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