#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <fcntl.h>

#define COUNT (1024 * 1024 * 1024)
#define BS 2048
#define RBS 4096
#define DATA 0

double GetTime()
{
	double t;
	struct timeval tv;
	struct timezone tz;

	gettimeofday(&tv, &tz);
	t = tv.tv_sec;
	t += tv.tv_usec / 1000000.0;

	return(t);
}

int WriteFile(char *file)
{
	int fd;
	char buf[BS];
	unsigned long i;
	double start, end, rate;
	unsigned long count;
	int w, x;
	char tmp[256];

	count = 1 + (int) (((double)COUNT) * rand() / (RAND_MAX + 1.0));

	fd = open(file, O_WRONLY | O_CREAT, 0666);
	if(fd == -1)
	{
		perror(file);
		return(0);
	}

	for(i = 0; i < BS; i++)
		buf[i] = DATA;

	/* printf("*** Writing %lu bytes to %s in %d byte chunks\n", count, file, BS); */

	start = GetTime();
	for(i = 0; i < count; )
	{
		x = BS;
		if(i + BS > count)
			x = count - i;
		/*sleep(1);*/
		w = write(fd, buf, x);
		if(w == -1)
		{
			sprintf(tmp, "write: %s block %lu", file, i);
			perror(tmp);
			exit(-1);
			close(fd);
			return(0);
		}
		i += w;
	}
	end = GetTime();

	rate = count / 1024.0 / 1024.0;
	rate /= (end - start);
	fprintf(stderr, "*** Wrote %lu bytes in %0.2lf seconds, %0.2lf MB/s\n", count, (end - start), rate);

	close(fd);

	return(1);
}

int VerifyFile(char *file)
{
	struct stat statbuf;
	size_t length, i;
	int fd;
	double start, end, rate;
	char buf[RBS];
	char readBuf[RBS];
	int r;
	char tmp[256];

	if(stat(file, &statbuf) == -1)
	{
		perror(file);
		return(0);
	}

	length = statbuf.st_size;
	fd = open(file, O_RDONLY);
	if(fd == -1)
	{
		perror(file);
		return(0);
	}

	for(i = 0; i < RBS; i++)
		buf[i] = DATA;

	/* printf("*** Verifying %s, with %d byte reads\n", file, RBS); */
	start = GetTime();

	for(i = 0; i < length; )
	{
		/*sleep(1);*/
		r = read(fd, readBuf, RBS);
		if(r == -1)
		{
			sprintf(tmp, "read: %s block %lu", file, i);
			perror(tmp);
			exit(-1);
			close(fd);
			return(0);
		}
		else if(r == 0)
			break;
		i += r;

		if(memcmp(readBuf, buf, r))
		{
			fprintf(stderr, "%s: Invalid data at block %lu\n", file, i);
			close(fd);
			return(0);
		}
	}

	end = GetTime();

	rate = length / 1024.0 / 1024.0;
	rate /= (end - start);
	fprintf(stderr, "*** Read %lu bytes in %0.2lf seconds, %0.2lf MB/s\n", i, (end - start), rate);

	close(fd);

	return(1);
}

int main(int argc, char **argv)
{
	int i;
	int children;
	char *f;
	char file[32];
	struct stat statbuf;
	struct rusage ru;
	unsigned int s;

	if(argc != 3)
	{
		fprintf(stderr, "usage: %s <prefix> <count>\n", argv[0]);
		fprintf(stderr, "\tprefix is a path and filename to where %s should create\nits file(s)\n", argv[0]);
		fprintf(stderr, "\tcount is how many children to run (use 0 to test speed)\n", argv[0]);
		return(-1);
	}

	children = atoi(argv[2]);

	fprintf(stderr, "*** Starting %d children\n", children);

	for(i = 0; i < children; i++)
	{
		if(fork() != 0)
			break;
	}

	getrusage(RUSAGE_SELF, &ru);
	srand(time(NULL) + ru.ru_majflt + ru.ru_utime.tv_usec + getpid() + getuid());


	f = argv[1];
	sprintf(file, "%s%d", f, i);

	fprintf(stderr, "*** Started %s for %s\n", argv[0], file);

	while(1)
	{
		if(stat(file, &statbuf) == -1)
		{
			/* no file */
			WriteFile(file);
		}
		else if(VerifyFile(file) == 1)
			unlink(file);
	}
	return(0);
}

