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