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 |
|
---|
26 | #include "ext2_fs.h"
|
---|
27 | #include "ext2fs.h"
|
---|
28 |
|
---|
29 | /*
|
---|
30 | * For checking structure magic numbers...
|
---|
31 | */
|
---|
32 |
|
---|
33 | #define EXT2_CHECK_MAGIC(struct, code) \
|
---|
34 | if ((struct)->magic != (code)) return (code)
|
---|
35 |
|
---|
36 | struct test_private_data {
|
---|
37 | int magic;
|
---|
38 | io_channel real;
|
---|
39 | int flags;
|
---|
40 | FILE *outfile;
|
---|
41 | unsigned long block;
|
---|
42 | int read_abort_count, write_abort_count;
|
---|
43 | void (*read_blk)(unsigned long block, int count, errcode_t err);
|
---|
44 | void (*write_blk)(unsigned long block, int count, errcode_t err);
|
---|
45 | void (*set_blksize)(int blksize, errcode_t err);
|
---|
46 | void (*write_byte)(unsigned long block, int count, errcode_t err);
|
---|
47 | };
|
---|
48 |
|
---|
49 | static errcode_t test_open(const char *name, int flags, io_channel *channel);
|
---|
50 | static errcode_t test_close(io_channel channel);
|
---|
51 | static errcode_t test_set_blksize(io_channel channel, int blksize);
|
---|
52 | static errcode_t test_read_blk(io_channel channel, unsigned long block,
|
---|
53 | int count, void *data);
|
---|
54 | static errcode_t test_write_blk(io_channel channel, unsigned long block,
|
---|
55 | int count, const void *data);
|
---|
56 | static errcode_t test_flush(io_channel channel);
|
---|
57 | static errcode_t test_write_byte(io_channel channel, unsigned long offset,
|
---|
58 | int count, const void *buf);
|
---|
59 | static errcode_t test_set_option(io_channel channel, const char *option,
|
---|
60 | const char *arg);
|
---|
61 |
|
---|
62 | static struct struct_io_manager struct_test_manager = {
|
---|
63 | EXT2_ET_MAGIC_IO_MANAGER,
|
---|
64 | "Test I/O Manager",
|
---|
65 | test_open,
|
---|
66 | test_close,
|
---|
67 | test_set_blksize,
|
---|
68 | test_read_blk,
|
---|
69 | test_write_blk,
|
---|
70 | test_flush,
|
---|
71 | test_write_byte,
|
---|
72 | test_set_option
|
---|
73 | };
|
---|
74 |
|
---|
75 | io_manager test_io_manager = &struct_test_manager;
|
---|
76 |
|
---|
77 | /*
|
---|
78 | * These global variable can be set by the test program as
|
---|
79 | * necessary *before* calling test_open
|
---|
80 | */
|
---|
81 | io_manager test_io_backing_manager = 0;
|
---|
82 | void (*test_io_cb_read_blk)
|
---|
83 | (unsigned long block, int count, errcode_t err) = 0;
|
---|
84 | void (*test_io_cb_write_blk)
|
---|
85 | (unsigned long block, int count, errcode_t err) = 0;
|
---|
86 | void (*test_io_cb_set_blksize)
|
---|
87 | (int blksize, errcode_t err) = 0;
|
---|
88 | void (*test_io_cb_write_byte)
|
---|
89 | (unsigned long block, int count, errcode_t err) = 0;
|
---|
90 |
|
---|
91 | /*
|
---|
92 | * Test flags
|
---|
93 | */
|
---|
94 | #define TEST_FLAG_READ 0x01
|
---|
95 | #define TEST_FLAG_WRITE 0x02
|
---|
96 | #define TEST_FLAG_SET_BLKSIZE 0x04
|
---|
97 | #define TEST_FLAG_FLUSH 0x08
|
---|
98 | #define TEST_FLAG_DUMP 0x10
|
---|
99 | #define TEST_FLAG_SET_OPTION 0x20
|
---|
100 |
|
---|
101 | static void test_dump_block(io_channel channel,
|
---|
102 | struct test_private_data *data,
|
---|
103 | unsigned long block, const void *buf)
|
---|
104 | {
|
---|
105 | const unsigned char *cp;
|
---|
106 | FILE *f = data->outfile;
|
---|
107 | int i;
|
---|
108 | unsigned long cksum = 0;
|
---|
109 |
|
---|
110 | for (i=0, cp = buf; i < channel->block_size; i++, cp++) {
|
---|
111 | cksum += *cp;
|
---|
112 | }
|
---|
113 | fprintf(f, "Contents of block %lu, checksum %08lu: \n", block, cksum);
|
---|
114 | for (i=0, cp = buf; i < channel->block_size; i++, cp++) {
|
---|
115 | if ((i % 16) == 0)
|
---|
116 | fprintf(f, "%04x: ", i);
|
---|
117 | fprintf(f, "%02x%c", *cp, ((i % 16) == 15) ? '\n' : ' ');
|
---|
118 | }
|
---|
119 | }
|
---|
120 |
|
---|
121 | static void test_abort(io_channel channel, unsigned long block)
|
---|
122 | {
|
---|
123 | struct test_private_data *data;
|
---|
124 | FILE *f;
|
---|
125 |
|
---|
126 | data = (struct test_private_data *) channel->private_data;
|
---|
127 | f = data->outfile;
|
---|
128 | test_flush(channel);
|
---|
129 |
|
---|
130 | fprintf(f, "Aborting due to I/O to block %lu\n", block);
|
---|
131 | fflush(f);
|
---|
132 | abort();
|
---|
133 | }
|
---|
134 |
|
---|
135 | static errcode_t test_open(const char *name, int flags, io_channel *channel)
|
---|
136 | {
|
---|
137 | io_channel io = NULL;
|
---|
138 | struct test_private_data *data = NULL;
|
---|
139 | errcode_t retval;
|
---|
140 | char *value;
|
---|
141 |
|
---|
142 | if (name == 0)
|
---|
143 | return EXT2_ET_BAD_DEVICE_NAME;
|
---|
144 | retval = ext2fs_get_mem(sizeof(struct struct_io_channel), &io);
|
---|
145 | if (retval)
|
---|
146 | return retval;
|
---|
147 | memset(io, 0, sizeof(struct struct_io_channel));
|
---|
148 | io->magic = EXT2_ET_MAGIC_IO_CHANNEL;
|
---|
149 | retval = ext2fs_get_mem(sizeof(struct test_private_data), &data);
|
---|
150 | if (retval) {
|
---|
151 | retval = EXT2_ET_NO_MEMORY;
|
---|
152 | goto cleanup;
|
---|
153 | }
|
---|
154 | io->manager = test_io_manager;
|
---|
155 | retval = ext2fs_get_mem(strlen(name)+1, &io->name);
|
---|
156 | if (retval)
|
---|
157 | goto cleanup;
|
---|
158 |
|
---|
159 | strcpy(io->name, name);
|
---|
160 | io->private_data = data;
|
---|
161 | io->block_size = 1024;
|
---|
162 | io->read_error = 0;
|
---|
163 | io->write_error = 0;
|
---|
164 | io->refcount = 1;
|
---|
165 |
|
---|
166 | memset(data, 0, sizeof(struct test_private_data));
|
---|
167 | data->magic = EXT2_ET_MAGIC_TEST_IO_CHANNEL;
|
---|
168 | if (test_io_backing_manager) {
|
---|
169 | retval = test_io_backing_manager->open(name, flags,
|
---|
170 | &data->real);
|
---|
171 | if (retval)
|
---|
172 | goto cleanup;
|
---|
173 | } else
|
---|
174 | data->real = 0;
|
---|
175 | data->read_blk = test_io_cb_read_blk;
|
---|
176 | data->write_blk = test_io_cb_write_blk;
|
---|
177 | data->set_blksize = test_io_cb_set_blksize;
|
---|
178 | data->write_byte = test_io_cb_write_byte;
|
---|
179 |
|
---|
180 | data->outfile = NULL;
|
---|
181 | if ((value = getenv("TEST_IO_LOGFILE")) != NULL)
|
---|
182 | data->outfile = fopen(value, "w");
|
---|
183 | if (!data->outfile)
|
---|
184 | data->outfile = stderr;
|
---|
185 |
|
---|
186 | data->flags = 0;
|
---|
187 | if ((value = getenv("TEST_IO_FLAGS")) != NULL)
|
---|
188 | data->flags = strtoul(value, NULL, 0);
|
---|
189 |
|
---|
190 | data->block = 0;
|
---|
191 | if ((value = getenv("TEST_IO_BLOCK")) != NULL)
|
---|
192 | data->block = strtoul(value, NULL, 0);
|
---|
193 |
|
---|
194 | data->read_abort_count = 0;
|
---|
195 | if ((value = getenv("TEST_IO_READ_ABORT")) != NULL)
|
---|
196 | data->read_abort_count = strtoul(value, NULL, 0);
|
---|
197 |
|
---|
198 | data->write_abort_count = 0;
|
---|
199 | if ((value = getenv("TEST_IO_WRITE_ABORT")) != NULL)
|
---|
200 | data->write_abort_count = strtoul(value, NULL, 0);
|
---|
201 |
|
---|
202 | *channel = io;
|
---|
203 | return 0;
|
---|
204 |
|
---|
205 | cleanup:
|
---|
206 | ext2fs_free_mem(&io);
|
---|
207 | ext2fs_free_mem(&data);
|
---|
208 | return retval;
|
---|
209 | }
|
---|
210 |
|
---|
211 | static errcode_t test_close(io_channel channel)
|
---|
212 | {
|
---|
213 | struct test_private_data *data;
|
---|
214 | errcode_t retval = 0;
|
---|
215 |
|
---|
216 | EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
|
---|
217 | data = (struct test_private_data *) channel->private_data;
|
---|
218 | EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
|
---|
219 |
|
---|
220 | if (--channel->refcount > 0)
|
---|
221 | return 0;
|
---|
222 |
|
---|
223 | if (data->real)
|
---|
224 | retval = io_channel_close(data->real);
|
---|
225 |
|
---|
226 | if (data->outfile && data->outfile != stderr)
|
---|
227 | fclose(data->outfile);
|
---|
228 |
|
---|
229 | ext2fs_free_mem(&channel->private_data);
|
---|
230 | ext2fs_free_mem(&channel->name);
|
---|
231 | ext2fs_free_mem(&channel);
|
---|
232 | return retval;
|
---|
233 | }
|
---|
234 |
|
---|
235 | static errcode_t test_set_blksize(io_channel channel, int blksize)
|
---|
236 | {
|
---|
237 | struct test_private_data *data;
|
---|
238 | errcode_t retval = 0;
|
---|
239 |
|
---|
240 | EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
|
---|
241 | data = (struct test_private_data *) channel->private_data;
|
---|
242 | EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
|
---|
243 |
|
---|
244 | if (data->real)
|
---|
245 | retval = io_channel_set_blksize(data->real, blksize);
|
---|
246 | if (data->set_blksize)
|
---|
247 | data->set_blksize(blksize, retval);
|
---|
248 | if (data->flags & TEST_FLAG_SET_BLKSIZE)
|
---|
249 | fprintf(data->outfile,
|
---|
250 | "Test_io: set_blksize(%d) returned %s\n",
|
---|
251 | blksize, retval ? error_message(retval) : "OK");
|
---|
252 | channel->block_size = blksize;
|
---|
253 | return retval;
|
---|
254 | }
|
---|
255 |
|
---|
256 |
|
---|
257 | static errcode_t test_read_blk(io_channel channel, unsigned long block,
|
---|
258 | int count, void *buf)
|
---|
259 | {
|
---|
260 | struct test_private_data *data;
|
---|
261 | errcode_t retval = 0;
|
---|
262 |
|
---|
263 | EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
|
---|
264 | data = (struct test_private_data *) channel->private_data;
|
---|
265 | EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
|
---|
266 |
|
---|
267 | if (data->real)
|
---|
268 | retval = io_channel_read_blk(data->real, block, count, buf);
|
---|
269 | if (data->read_blk)
|
---|
270 | data->read_blk(block, count, retval);
|
---|
271 | if (data->flags & TEST_FLAG_READ)
|
---|
272 | fprintf(data->outfile,
|
---|
273 | "Test_io: read_blk(%lu, %d) returned %s\n",
|
---|
274 | block, count, retval ? error_message(retval) : "OK");
|
---|
275 | if (data->block && data->block == block) {
|
---|
276 | if (data->flags & TEST_FLAG_DUMP)
|
---|
277 | test_dump_block(channel, data, block, buf);
|
---|
278 | if (--data->read_abort_count == 0)
|
---|
279 | test_abort(channel, block);
|
---|
280 | }
|
---|
281 | return retval;
|
---|
282 | }
|
---|
283 |
|
---|
284 | static errcode_t test_write_blk(io_channel channel, unsigned long block,
|
---|
285 | int count, const void *buf)
|
---|
286 | {
|
---|
287 | struct test_private_data *data;
|
---|
288 | errcode_t retval = 0;
|
---|
289 |
|
---|
290 | EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
|
---|
291 | data = (struct test_private_data *) channel->private_data;
|
---|
292 | EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
|
---|
293 |
|
---|
294 | if (data->real)
|
---|
295 | retval = io_channel_write_blk(data->real, block, count, buf);
|
---|
296 | if (data->write_blk)
|
---|
297 | data->write_blk(block, count, retval);
|
---|
298 | if (data->flags & TEST_FLAG_WRITE)
|
---|
299 | fprintf(data->outfile,
|
---|
300 | "Test_io: write_blk(%lu, %d) returned %s\n",
|
---|
301 | block, count, retval ? error_message(retval) : "OK");
|
---|
302 | if (data->block && data->block == block) {
|
---|
303 | if (data->flags & TEST_FLAG_DUMP)
|
---|
304 | test_dump_block(channel, data, block, buf);
|
---|
305 | if (--data->write_abort_count == 0)
|
---|
306 | test_abort(channel, block);
|
---|
307 | }
|
---|
308 | return retval;
|
---|
309 | }
|
---|
310 |
|
---|
311 | static errcode_t test_write_byte(io_channel channel, unsigned long offset,
|
---|
312 | int count, const void *buf)
|
---|
313 | {
|
---|
314 | struct test_private_data *data;
|
---|
315 | errcode_t retval = 0;
|
---|
316 |
|
---|
317 | EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
|
---|
318 | data = (struct test_private_data *) channel->private_data;
|
---|
319 | EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
|
---|
320 |
|
---|
321 | if (data->real && data->real->manager->write_byte)
|
---|
322 | retval = io_channel_write_byte(data->real, offset, count, buf);
|
---|
323 | if (data->write_byte)
|
---|
324 | data->write_byte(offset, count, retval);
|
---|
325 | if (data->flags & TEST_FLAG_WRITE)
|
---|
326 | fprintf(data->outfile,
|
---|
327 | "Test_io: write_byte(%lu, %d) returned %s\n",
|
---|
328 | offset, count, retval ? error_message(retval) : "OK");
|
---|
329 | return retval;
|
---|
330 | }
|
---|
331 |
|
---|
332 | /*
|
---|
333 | * Flush data buffers to disk.
|
---|
334 | */
|
---|
335 | static errcode_t test_flush(io_channel channel)
|
---|
336 | {
|
---|
337 | struct test_private_data *data;
|
---|
338 | errcode_t retval = 0;
|
---|
339 |
|
---|
340 | EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
|
---|
341 | data = (struct test_private_data *) channel->private_data;
|
---|
342 | EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
|
---|
343 |
|
---|
344 | if (data->real)
|
---|
345 | retval = io_channel_flush(data->real);
|
---|
346 |
|
---|
347 | if (data->flags & TEST_FLAG_FLUSH)
|
---|
348 | fprintf(data->outfile, "Test_io: flush() returned %s\n",
|
---|
349 | retval ? error_message(retval) : "OK");
|
---|
350 |
|
---|
351 | return retval;
|
---|
352 | }
|
---|
353 |
|
---|
354 | static errcode_t test_set_option(io_channel channel, const char *option,
|
---|
355 | const char *arg)
|
---|
356 | {
|
---|
357 | struct test_private_data *data;
|
---|
358 | errcode_t retval = 0;
|
---|
359 |
|
---|
360 | EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
|
---|
361 | data = (struct test_private_data *) channel->private_data;
|
---|
362 | EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
|
---|
363 |
|
---|
364 |
|
---|
365 | if (data->flags & TEST_FLAG_SET_OPTION)
|
---|
366 | fprintf(data->outfile, "Test_io: set_option(%s, %s) ",
|
---|
367 | option, arg);
|
---|
368 | if (data->real && data->real->manager->set_option) {
|
---|
369 | retval = (data->real->manager->set_option)(data->real,
|
---|
370 | option, arg);
|
---|
371 | if (data->flags & TEST_FLAG_SET_OPTION)
|
---|
372 | fprintf(data->outfile, "returned %s\n",
|
---|
373 | retval ? error_message(retval) : "OK");
|
---|
374 | } else {
|
---|
375 | if (data->flags & TEST_FLAG_SET_OPTION)
|
---|
376 | fprintf(data->outfile, "not implemented\n");
|
---|
377 | }
|
---|
378 | return retval;
|
---|
379 | }
|
---|