This driver can be loaded as an overlay on top of a real block device, and can then be used to generate block-level failures for certain transfer requests. Specifically, a rule-based system allows the user to introduce (overt and silent) data corruption and errors. It exposes itself through /dev/fbd, and a file system can be mounted on top of it. The new fbdctl(8) tool can be used to control the driver; see ``man fbdctl'' for details. It also comes with a test set, located in test/fbdtest.
		
			
				
	
	
		
			191 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			191 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* Simple block pattern reader/writer for testing FBD */
 | 
						|
 | 
						|
#include <stdlib.h>
 | 
						|
#include <stdio.h>
 | 
						|
#include <string.h>
 | 
						|
#include <unistd.h>
 | 
						|
#include <fcntl.h>
 | 
						|
 | 
						|
#define BLOCK_SIZE 4096	/* set to match root FS to prevent partial I/O */
 | 
						|
 | 
						|
static int flush_buf(int fd, char *buf, size_t size, size_t write_size)
 | 
						|
{
 | 
						|
	ssize_t r;
 | 
						|
 | 
						|
	while (write_size <= size) {
 | 
						|
		if ((r = write(fd, buf, write_size)) != write_size) {
 | 
						|
			if (r < 0)
 | 
						|
				perror("write");
 | 
						|
			else
 | 
						|
				fprintf(stderr, "short write (%d < %d)\n",
 | 
						|
					r, write_size);
 | 
						|
 | 
						|
			return EXIT_FAILURE;
 | 
						|
		}
 | 
						|
 | 
						|
		sync();
 | 
						|
 | 
						|
		buf += write_size;
 | 
						|
		size -= write_size;
 | 
						|
	}
 | 
						|
 | 
						|
	return EXIT_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
static int write_pattern(int fd, char *pattern, int write_size)
 | 
						|
{
 | 
						|
	char *buf, *ptr;
 | 
						|
	size_t size;
 | 
						|
	int r, count, nblocks;
 | 
						|
 | 
						|
	/* Only write sizes that are a multiple or a
 | 
						|
	 * divisor of the block size, are supported.
 | 
						|
	 */
 | 
						|
	nblocks = write_size / BLOCK_SIZE;
 | 
						|
	if (!nblocks) nblocks = 1;
 | 
						|
	size = nblocks * BLOCK_SIZE;
 | 
						|
 | 
						|
	if ((buf = malloc(size)) == NULL) {
 | 
						|
		perror("malloc");
 | 
						|
 | 
						|
		return EXIT_FAILURE;
 | 
						|
	}
 | 
						|
 | 
						|
	count = 0;
 | 
						|
 | 
						|
	do {
 | 
						|
		ptr = &buf[count * BLOCK_SIZE];
 | 
						|
 | 
						|
		switch (*pattern) {
 | 
						|
		case 'A':
 | 
						|
		case 'B':
 | 
						|
		case 'C':
 | 
						|
		case 'D':
 | 
						|
		case 'U':
 | 
						|
			memset(ptr, *pattern, BLOCK_SIZE);
 | 
						|
			break;
 | 
						|
 | 
						|
		case '0':
 | 
						|
			memset(ptr, 0, BLOCK_SIZE);
 | 
						|
			break;
 | 
						|
 | 
						|
		case '\0':
 | 
						|
			memset(ptr, 0, BLOCK_SIZE);
 | 
						|
			ptr[0] = 'E';
 | 
						|
			ptr[1] = 'O';
 | 
						|
			ptr[2] = 'F';
 | 
						|
		}
 | 
						|
 | 
						|
		if (++count == nblocks) {
 | 
						|
			if ((r = flush_buf(fd, buf, size, write_size)) !=
 | 
						|
					EXIT_SUCCESS) {
 | 
						|
				free(buf);
 | 
						|
 | 
						|
				return r;
 | 
						|
			}
 | 
						|
 | 
						|
			count = 0;
 | 
						|
		}
 | 
						|
	} while (*pattern++);
 | 
						|
 | 
						|
	if (count > 0)
 | 
						|
		r = flush_buf(fd, buf, count * BLOCK_SIZE, write_size);
 | 
						|
	else
 | 
						|
		r = EXIT_SUCCESS;
 | 
						|
 | 
						|
	free(buf);
 | 
						|
 | 
						|
	return r;
 | 
						|
}
 | 
						|
 | 
						|
static int read_pattern(int fd)
 | 
						|
{
 | 
						|
	char buf[BLOCK_SIZE];
 | 
						|
	unsigned int i, val;
 | 
						|
	ssize_t r;
 | 
						|
 | 
						|
	for (;;) {
 | 
						|
		memset(buf, '?', sizeof(buf));
 | 
						|
 | 
						|
		if ((r = read(fd, buf, sizeof(buf))) != sizeof(buf)) {
 | 
						|
			putchar('#');
 | 
						|
 | 
						|
			if (!r) break; /* stop at hard EOF */
 | 
						|
 | 
						|
			lseek(fd, sizeof(buf), SEEK_CUR);
 | 
						|
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
 | 
						|
		if (buf[0] == 'E' && buf[1] == 'O' && buf[2] == 'F') {
 | 
						|
			for (i = 3; i < sizeof(buf); i++)
 | 
						|
				if (buf[i] != 0) break;
 | 
						|
 | 
						|
			if (i == sizeof(buf)) break;
 | 
						|
		}
 | 
						|
 | 
						|
		for (i = 1; i < sizeof(buf); i++)
 | 
						|
			if (buf[i] != buf[0]) break;
 | 
						|
 | 
						|
		if (i == sizeof(buf)) {
 | 
						|
			switch (buf[0]) {
 | 
						|
			case 'A':
 | 
						|
			case 'B':
 | 
						|
			case 'C':
 | 
						|
			case 'D':
 | 
						|
			case 'U':
 | 
						|
			case '?':
 | 
						|
				printf("%c", buf[0]);
 | 
						|
				break;
 | 
						|
 | 
						|
			case '\0':
 | 
						|
				printf("0");
 | 
						|
				break;
 | 
						|
 | 
						|
			default:
 | 
						|
				printf("X");
 | 
						|
			}
 | 
						|
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
 | 
						|
		for (i = val = 0; i < sizeof(buf); i++)
 | 
						|
			val += buf[i];
 | 
						|
 | 
						|
		printf("%c", 'a' + val % 26);
 | 
						|
	}
 | 
						|
 | 
						|
	printf("\n");
 | 
						|
 | 
						|
	return EXIT_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
int main(int argc, char **argv)
 | 
						|
{
 | 
						|
	int fd, r;
 | 
						|
 | 
						|
	if (argc < 2) {
 | 
						|
		fprintf(stderr, "usage: %s <device> [pattern [writesz]]\n",
 | 
						|
			argv[0]);
 | 
						|
 | 
						|
		return EXIT_FAILURE;
 | 
						|
	}
 | 
						|
 | 
						|
	fd = open(argv[1], (argc > 2) ? O_WRONLY : O_RDONLY);
 | 
						|
	if (fd < 0) {
 | 
						|
		perror("open");
 | 
						|
 | 
						|
		return EXIT_FAILURE;
 | 
						|
	}
 | 
						|
 | 
						|
	if (argc > 2)
 | 
						|
		r = write_pattern(fd, argv[2],
 | 
						|
			argv[3] ? atoi(argv[3]) : BLOCK_SIZE);
 | 
						|
	else
 | 
						|
		r = read_pattern(fd);
 | 
						|
 | 
						|
	close(fd);
 | 
						|
 | 
						|
	return r;
 | 
						|
}
 |