/*
 * random_exercise.c --- Test program which exercises an ext2
 * 	filesystem.  It creates a lot of random files in the current
 * 	directory, while holding some files open while they are being
 * 	deleted.  This exercises the orphan list code, as well as
 * 	creating lots of fodder for the ext3 journal.
 * 
 * Copyright (C) 2000 Theodore Ts'o.
 *
 * %Begin-Header%
 * This file may be redistributed under the terms of the GNU Public
 * License.
 * %End-Header%
 */

#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define MAXFDS	128

struct state {
	char	name[16];
	int	state;
	int	isdir;
};

#define STATE_CLEAR	0
#define STATE_CREATED	1
#define STATE_DELETED	2

struct state state_array[MAXFDS];

#define DATA_SIZE 65536

char data_buffer[DATA_SIZE];

void clear_state_array()
{
	int	i;

	for (i = 0; i < MAXFDS; i++)
		state_array[i].state = STATE_CLEAR;
}

int get_random_fd()
{
	int	fd;

	while (1) {
		fd = ((int) random()) % MAXFDS;
		if (fd > 2)
			return fd;
	}
}

unsigned int get_inode_num(int fd)
{
	struct stat st;

	if (fstat(fd, &st) < 0) {
		perror("fstat");
		return 0;
	}
	return st.st_ino;
}


void create_random_file()
{
	char template[16] = "EX.XXXXXX";
	int	fd;
	int	isdir = 0;
	int	size;
	
	mktemp(template);
	isdir = random() & 1;
	if (isdir) {
		if (mkdir(template, 0700) < 0)
			return;
		fd = open(template, O_RDONLY, 0600);
		printf("Created temp directory %s, fd = %d\n",
		       template, fd);
	} else {
		size = random() & (DATA_SIZE-1);		
		fd = open(template, O_CREAT|O_RDWR, 0600);
		write(fd, data_buffer, size);
		printf("Created temp file %s, fd = %d, size=%d\n",
		       template, fd, size);
	}
	state_array[fd].isdir = isdir;
	if (fd < 0)
		return;
	state_array[fd].isdir = isdir;
	state_array[fd].state = STATE_CREATED;
	strcpy(state_array[fd].name, template);
}

void truncate_file(int fd)
{
	int	size;

	size = random() & (DATA_SIZE-1);

	if (state_array[fd].isdir)
		return;

	ftruncate(fd, size);
	printf("Truncating temp file %s, fd = %d, ino=%u, size=%d\n",
	       state_array[fd].name, fd, get_inode_num(fd), size);
}


void unlink_file(int fd)
{
	char *filename = state_array[fd].name;
	
	printf("Deleting %s, fd = %d, ino = %u\n", filename, fd,
	       get_inode_num(fd));

	if (state_array[fd].isdir)
		rmdir(filename);
	else
		unlink(filename);
	state_array[fd].state = STATE_DELETED;
}

void close_file(int fd)
{
	char *filename = state_array[fd].name;
	
	printf("Closing %s, fd = %d, ino = %u\n", filename, fd,
	       get_inode_num(fd));
	
	close(fd);
	state_array[fd].state = STATE_CLEAR;
}


main(int argc, char **argv)
{
	int	i, fd;

	memset(data_buffer, 0, sizeof(data_buffer));
	sprintf(data_buffer, "This is a test file created by the "
		"random_exerciser program\n");
	
	for (i=0; i < 100000; i++) {
		fd = get_random_fd();
		switch (state_array[fd].state) {
		case STATE_CLEAR:
			create_random_file();
			break;
		case STATE_CREATED:
			if ((state_array[fd].isdir == 0) &&
			    (random() & 2))
				truncate_file(fd);
			else
				unlink_file(fd);
			break;
		case STATE_DELETED:
			close_file(fd);
			break;
		}
	}
}