/*
 * Copyright (C) 2004 Red Hat, UK
 *
 * This file is released under the GPL
 */

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <signal.h>

#define HASH_MULT 2654435387U
#define CHUNKSIZE 4096

static unsigned seed;

static inline unsigned pattern(void)
{
	unsigned r = seed;
	seed *= HASH_MULT;
	return r;
}

void usage(FILE *fp)
{

	fprintf(fp, "Usage:\n");
	fprintf(fp, "\n");
	fprintf(fp, "hashdd of=<file> seed=<num> [osync=<num>]\n");
	fprintf(fp, "hashdd if=<file> seed=<num> [osync=<num>]\n");
}

static int io(int fd, int write_p, void *buffer, size_t size)
{
	ssize_t n = 0;
	size_t total = 0;

	while (size) {
		do
			n = write_p ? write(fd, buffer, size) : read(fd, buffer, size);

		while ((n < 0) && ((errno == EINTR) || (errno == EAGAIN)));

		if (n <= 0)
			break;

		total += n;
		buffer += n;
	}

	return (total ? total : 0);
}

static void fill_array(unsigned *array, unsigned count)
{
	unsigned i;

	for (i = 0; i < count; i++)
		array[i] = pattern();
}

static int valid_array(unsigned *array, unsigned count)
{
	unsigned i;

	for (i = 0; i < count; i++)
		if (array[i] != pattern()) {
			fprintf(stderr, "found invalid data at offset %u\n",
				i * sizeof(*array));
			return 0;
		}

	return 1;
}

static int read_file(char *filename)
{
	int fd;
	size_t n;
	unsigned buffer[1024 * 256];

	fd = open(filename, O_RDONLY);
	if (fd < 0) {
		fprintf(stderr, "Error opening '%s': %s\n",
			filename, strerror(errno));
		return 0;
	}

	while (1) {
		n = io(fd, 0, buffer, sizeof(buffer));
		if (!n)
			break;

		if (!valid_array(buffer, n / sizeof(*buffer)))
			return 0;
	}

	return 1;
}

static int write_file(char *filename)
{
	int fd;
	size_t n, size;
	unsigned buffer[1024 * 256];

	fd = open(filename, O_CREAT | O_WRONLY);
	if (fd < 0) {
		fprintf(stderr, "Error opening '%s': %s\n",
			filename, strerror(errno));
		return 0;
	}

	size = sizeof(buffer) / sizeof(*buffer);
	while (1) {
		fill_array(buffer, size);

		n = io(fd, 1, buffer, sizeof(buffer));
		if (n != sizeof(buffer))
			break;
	}
	fsync(fd);
	close(fd);

	return 1;
}

int main(int argc, char **argv)
{
	int error = 0;
	char *filename = NULL;
	char act[10];
	char action = 'r';
	int i, count;
	struct stat st;

	if(argc != 3) {
		usage(stderr);
		return 1;
	}

	for(i = 1; i < argc; i++) {
		count = strcspn(argv[i], "=");
		count++;
		if(count < 2 || count > 8) {
			error = 1;
			goto end;
		}
		memset(act, 0, sizeof(char) * 10);
		strncpy(act, argv[i], count);
		if(!strcmp(act, "if=")) {
			filename = &(argv[i][count]);
			action = 'r';
		}
		else if(!strcmp(act, "of=")) {
			filename = &(argv[i][count]);
			action = 'w';
		}
		else if(!strcmp(act, "seed="))
			seed = atoi(&(argv[i][count]));
/*      else if(!strcmp(act, "osync="))
	sync = atoi(&(argv[i][count]));*/
		else {
			usage(stderr);
			return 1;
		}
	}

	if((error = stat(filename, &st)) != 0) {
		if(errno != ENOENT) {
			fprintf(stderr, "Unable to stat %s: %s\n", filename, strerror(errno));
			return 1;
		}
	}
	else if(!S_ISREG(st.st_mode) && !S_ISBLK(st.st_mode)) {
		fprintf(stderr, "<filename> must be a regular file or a block device\n");
		return 1;
	}

	if(seed <= 1) {
		fprintf(stderr, "seed value must be greater than 1\n");
		error = 1;
		goto end;
	}

	if(action == 'w')
		error = !write_file(filename);

	else if (action == 'r')
		error = !read_file(filename);

	else {
		usage(stderr);
		return 1;
	}

 end:
	return error;
}

