
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <math.h>
#include <time.h>
#include <ftw.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/kd.h>
#include <linux/errno.h>

#include "Ltpfs.h"

#define M_2PI (M_PI*2)
#define MAXN 4096
#define MAXFSIZE 1024 * 192
#define FILE_CREATE_COUNT 256
#define FAIL 0
#define SUCCESS 1
#define MAXNUM   5000
#define BUFFSIZE 8192
#define AVEFSIZE (MAXFSIZE/2)
#define POOLDISKSPACE (AVEFSIZE*128)
#define MAXERROR  1024
#define FILES_ONLY 0x01
#define ALL        0x00

// Globals

char wbuf[MAXFSIZE];
int startc = 0;
int showchar[] = { 124, 47, 45, 92, 124, 47, 45, 92 };

int nullFileHandle;
int openlog[2] = { 0, 0 };

int cFileCount, dFileCount, errorCount;
static int disk_space_pool = 0;
char rootPath[BUFFSIZE];

int LTP_fs_open_block_device(void);
int do_fs_thump_tests(char *path);
int do_create_file_test(char *path);
int makedir(char *dir1);
int changedir(char *dir);
int do_random_access_test(int maxNum);
int do_random_create_delete(int maxNum);
int create_file(char *filename);
int delete_file(char *filename);
int gen_random_file_size(int min, int max);
int open_read_close(char *fname);
int create_or_delete(char *fname);
int do_tree_cleanup(char *path, int flag);
int cleanup_files(char *file, struct stat *statBuff, int flag);
int cleanup_dirs(char *file, struct stat *statBuff, int flag);

int ltp_block_dev_handle = 0;	/* handle to LTP Test block device */
int ltp_fileHandle = 0;
char *fileBuf;

int main(int argc, char **argv)
{

	ltpdev_cmd_t cmd = { 0, 0 };
	int rc, i, tmpHandle;
	struct stat statBuf;

	printf("[%s] - Running test program\n", argv[0]);

	rc = LTP_fs_open_block_device();

	if (!rc) {

		ltp_block_dev_handle = open(LTP_FS_DEVICE_NAME, O_RDWR);

		if (ltp_block_dev_handle < 0) {
			printf
			    ("ERROR: Open of device %s failed %d errno = %d\n",
			     LTP_FS_DEVICE_NAME, ltp_block_dev_handle, errno);
		} else {
			rc = ioctl(ltp_block_dev_handle, LTPAIODEV_CMD, &cmd);

			printf("return from AIO ioctl %d \n", rc);

			rc = ioctl(ltp_block_dev_handle, LTPBIODEV_CMD, &cmd);

			printf("return from BIO ioctl %d \n", rc);
		}

	} else {
		printf("ERROR: Create/open block device failed\n");
	}

	ltp_fileHandle =
	    open("/tmp/testfile", O_CREAT | O_RDWR | O_SYNC | FASYNC,
		 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);

	if (ltp_fileHandle > 0) {

		tmpHandle = open("/usr/include/ctype.h", O_RDONLY);

		if (tmpHandle > 0) {

			rc = fstat(tmpHandle, &statBuf);

			if (!rc) {
				fileBuf = malloc(statBuf.st_size);

				if (fileBuf) {

					read(tmpHandle, fileBuf,
					     statBuf.st_size);
					close(tmpHandle);
					write(ltp_fileHandle, fileBuf,
					      statBuf.st_size);

					for (i = 0; i < 100; i++) {
						read(ltp_fileHandle, fileBuf,
						     statBuf.st_size * i);
						write(ltp_fileHandle, fileBuf,
						      statBuf.st_size * i);
					}
				}

			}

		} else {
			printf("ERROR: Create/open file failed\n");
		}
	}

	printf("*** Starting FileSystem thump tests....****\n");
	printf("*** Please be patient, this may take a little while... ***\n");

	for (i = 1; i < argc; i++) {
		printf("Running test %d of %d on FileSystem %s \n", i, argc - 1,
		       argv[i]);
		if (strcmp(argv[i], "|") != 0) {
			strcpy(rootPath, argv[i]);
			rc = do_fs_thump_tests(argv[i]);
			if (rc != 0 && rc != ENOSPC) {
				printf
				    ("ERROR: Failed on FileSystem %s with errno %d \n",
				     argv[i], rc);
			}
		} else {
			printf("Test Program complete..\n");
			break;
		}

	}

	printf("Test Program complete..\n");

	return 0;
}

int do_fs_thump_tests(char *path)
{
	int rc = 0;

	printf("Changing to directory %s \n", path);

	changedir(path);

	cFileCount = 0;
	dFileCount = 0;

	rc |= do_create_file_test(path);
	rc |= do_random_access_test(MAXNUM);
	rc |= do_tree_cleanup(path, FILES_ONLY);
	rc |= do_random_create_delete(MAXNUM);
	rc |= do_tree_cleanup(path, ALL);

	return rc;

}

int do_tree_cleanup(char *path, int flag)
{

	if (flag == FILES_ONLY) {
		printf("Cleaning up test files...\n");
		ftw(path, (void *)cleanup_files, MAXNUM);
	} else {
		printf("Cleaning up everything in the test directory...\n");
		ftw(path, (void *)cleanup_files, MAXNUM);
		ftw(path, (void *)cleanup_dirs, MAXNUM);
	}

	return 0;
}

int cleanup_files(char *file, struct stat *statBuff, int flag)
{
	int rc = 0;

	if (flag == FTW_F) {
		if (unlink(file)) {
			printf("ERROR:%d removing file %s\n", errno, file);
		}
	}

	return rc;
}

int cleanup_dirs(char *file, struct stat *statBuff, int flag)
{
	int rc = 0;

	//printf("%s:Cleaning up directory %s \n", __FUNCTION__, file);

	if (strcmp(rootPath, file) == 0) {
		return 0;
	}

	if (flag == FTW_F) {
		if (unlink(file)) {
			printf("ERROR:%d removing file %s\n", errno, file);
		}
	} else if (flag == FTW_D) {
		changedir(file);
		ftw(file, (void *)cleanup_dirs, MAXNUM);
		rmdir(file);

	} else {
		printf("No idea what we found here\n");
	}

	return rc;
}

int do_create_file_test(char *path)
{
	int i = 0;
	int j = 0;
	int k = 0;
	int l = 0;
	int rc = 0;

	char dir1[MAXN];
	char dir2[MAXN];
	char dir3[MAXN];
	char filename[MAXN];

	time_t t;

	int maxfiles = 0xFFFFFF;

	time(&t);

	srandom((unsigned int)getpid() ^
		(((unsigned int)t << 16) | (unsigned int)t >> 16));

	printf("Creating files...\n");

	for (i = 0; i < FILE_CREATE_COUNT; i++) {

		sprintf(dir1, "%2.2x", i);

		makedir(dir1);

		changedir(dir1);

		for (j = 0; j < FILE_CREATE_COUNT; j++) {

			sprintf(dir2, "%2.2x", j);

			makedir(dir2);

			changedir(dir2);

			for (k = 0; k < FILE_CREATE_COUNT; k++) {

				sprintf(dir3, "%2.2x", k);
				makedir(dir3);
				changedir(dir3);

				for (l = 0; l < FILE_CREATE_COUNT; l++) {
					sprintf(filename, "%s%s%s%2.2x", dir1,
						dir2, dir3, l);
					rc = create_file(filename);
					if (rc != 0 || maxfiles < dFileCount++) {
						if (rc != ENOSPC) {
							printf
							    ("ERROR: failed error:%d creating all the test files ! \n",
							     errno);
							printf
							    ("ERROR2: rc:%d -- dFileCount:%d \n",
							     rc, dFileCount);
						}
						goto end;
					}
				}
				changedir("../");
			}
			changedir("../");
		}
		changedir("../");
	}
end:
	fprintf(stderr, "\nTotal create files: %d\n", cFileCount);
	printf("Done\n");
	return rc;
}

int makedir(char *dir1)
{
	if (mkdir(dir1, S_IRWXU) < 0) {
		perror(dir1);
		return (errno);
	}
	return 0;
}

int changedir(char *dir)
{
	if (chdir(dir) < 0) {
		perror(dir);
		return (errno);
	}

	return 0;
}

int create_file(char *filename)
{
	int fileHandle;
	int randomsize;

	if ((fileHandle = creat(filename, S_IRWXU)) < 0) {

		fprintf(stderr, "\nERROR line %d: Total create files: %d\n",
			__LINE__, cFileCount);
		perror(filename);
		return (errno);
	}

	if ((randomsize = gen_random_file_size(0, MAXFSIZE)) < 0) {
		randomsize = MAXFSIZE;
	}
	if (write(fileHandle, wbuf, randomsize) < 0) {

		fprintf(stderr, "\nERROR:%d line%d: Total create files: %d\n",
			errno, __LINE__, cFileCount);
		close(fileHandle);

		perror(filename);
		return (errno);
	}

	cFileCount++;
	close(fileHandle);
	return 0;
}

int delete_file(char *filename)
{
	struct stat buf;
	int st;

	st = stat(filename, &buf);

	if (st < 0) {
		errorCount++;
		printf("ERROR line %d: Getting file stats %s \n", __LINE__,
		       filename);
		return (-1);
	}

	disk_space_pool += buf.st_size;

	if (unlink(filename) < 0) {
		errorCount++;
		printf("ERROR line %d: Removing file %s \n", __LINE__,
		       filename);
		return (-1);
	}

	dFileCount++;
	return 0;
}

int LTP_fs_open_block_device()
{
	dev_t devt;
	struct stat statbuf;
	int rc;

	if (ltp_block_dev_handle == 0) {

		/* check for the /dev/LTPFSTest subdir, and create if it does not exist.
		 *
		 * If devfs is running and mounted on /dev, these checks will all pass,
		 * so a new node will not be created.
		 */
		devt = makedev(LTPMAJOR, 0);

		rc = stat(LTP_FS_DEV_NODE_PATH, &statbuf);

		if (rc) {
			if (errno == ENOENT) {
				/* dev node does not exist. */
				rc = mkdir(LTP_FS_DEV_NODE_PATH,
					   (S_IFDIR | S_IRWXU | S_IRGRP |
					    S_IXGRP | S_IROTH | S_IXOTH));
			} else {
				printf
				    ("ERROR: Problem with LTP FS dev directory.  Error code from stat() is %d\n\n",
				     errno);
			}

		} else {
			if (!(statbuf.st_mode & S_IFDIR)) {
				rc = unlink(LTP_FS_DEV_NODE_PATH);
				if (!rc) {
					rc = mkdir(LTP_FS_DEV_NODE_PATH,
						   (S_IFDIR | S_IRWXU | S_IRGRP
						    | S_IXGRP | S_IROTH |
						    S_IXOTH));
				}
			}
		}

		/*
		 * Check for the /dev/ltp-fs/block_device node, and create if it does not
		 * exist.
		 */
		rc = stat(LTP_FS_DEVICE_NAME, &statbuf);
		if (rc) {
			if (errno == ENOENT) {
				/* dev node does not exist */
				rc = mknod(LTP_FS_DEVICE_NAME,
					   (S_IFBLK | S_IRUSR | S_IWUSR |
					    S_IRGRP | S_IWGRP), devt);
			} else {
				printf
				    ("ERROR:Problem with LTP FS block device node directory.  Error code form stat() is %d\n\n",
				     errno);
			}

		} else {
			/*
			 * /dev/ltp-fs/block_device exists.  Check to make sure it is for a
			 * block device and that it has the right major and minor.
			 */
			if ((!(statbuf.st_mode & S_IFBLK)) ||
			    (statbuf.st_rdev != devt)) {

				/* Recreate the dev node. */
				rc = unlink(LTP_FS_DEVICE_NAME);
				if (!rc) {
					rc = mknod(LTP_FS_DEVICE_NAME,
						   (S_IFBLK | S_IRUSR | S_IWUSR
						    | S_IRGRP | S_IWGRP), devt);
				}
			}
		}

	}

	return rc;
}

int gen_random_file_size(int min, int max)
{
	double u1, u2, z;
	int i;
	int ave;
	int range;
	int ZZ;
	if (min >= max) {
		return (-1);
	}
	range = max - min;
	ave = range / 2;
	for (i = 0; i < 10; i++) {
		u1 = ((double)(random() % 1000000)) / 1000000;
		u2 = ((double)(random() % 1000000)) / 1000000;
		z = sqrt(-2.0 * log(u1)) * cos(M_2PI * u2);
		ZZ = min + (ave + (z * (ave / 4)));
		if (ZZ >= min && ZZ < max) {
			return (ZZ);
		}
	}
	return (-1);
}

int do_random_access_test(int maxNum)
{
	int r;
	char fname[1024];
	time_t t;
	int i;

	printf("Running random access test...\n");
	changedir(rootPath);

	if (maxNum < 1 || maxNum > MAXNUM) {
		printf("out of size %d\n", maxNum);
		return 1;
	}

	time(&t);
	srandom((unsigned int)getpid() ^
		(((unsigned int)t << 16) | (unsigned int)t >> 16));

	if ((nullFileHandle = open("/dev/null", O_WRONLY)) < 0) {
		perror("/dev/null");
		return (errno);
	}

	/* 00/00/00/00 */
	for (i = 0; i < maxNum; i++) {

		r = random() % maxNum;

		sprintf(fname, "00/%2.2x/%2.2x/00%2.2x%2.2x%2.2x",
			((r >> 16) & 0xFF),
			((r >> 8) & 0xFF),
			((r >> 16) & 0xFF), ((r >> 8) & 0xFF), (r & 0xFF));

		open_read_close(fname);
	}
	close(nullFileHandle);
	printf("Success:\t%d\nFail:\t%d\n", openlog[SUCCESS], openlog[FAIL]);
	return 0;
}

int open_read_close(char *fname)
{
	int fileHandle, fileHandle2;
	char buffer[BUFFSIZE];
	int c;

	if ((fileHandle = open(fname, O_RDONLY | O_SYNC | O_ASYNC)) < 0) {
		openlog[FAIL]++;
		printf("ERROR:opening file %s failed %d \n", fname, errno);
		return (errno);
	}

	if ((fileHandle2 = open(fname, O_RDONLY | O_SYNC | O_ASYNC)) < 0) {
		openlog[FAIL]++;
		printf("ERROR:2nd opening file %s failed %d \n", fname, errno);
		return (errno);
	}

	openlog[SUCCESS]++;

	while ((c = read(fileHandle, buffer, BUFFSIZE)) > 0) {
		if (write(nullFileHandle, buffer, c) < 0) {
			perror("/dev/null");
			printf("Opened\t %d\nUnopend:\t%d\n", openlog[SUCCESS],
			       openlog[FAIL]);
			close(fileHandle2);
			close(fileHandle);
			return (errno);
		}
		if ((c = read(fileHandle2, buffer, BUFFSIZE)) > 0) {
			if (write(nullFileHandle, buffer, c) < 0) {
				perror("/dev/null");
				printf("Opened\t %d\nUnopend:\t%d\n",
				       openlog[SUCCESS], openlog[FAIL]);
				close(fileHandle2);
				close(fileHandle);
				return (errno);
			}
		}
	}

	if (c < 0) {
		perror(fname);
		printf("Opened\t %d\nUnopend:\t%d\n", openlog[SUCCESS],
		       openlog[FAIL]);
		return (errno);
	}

	close(fileHandle2);
	close(fileHandle);
	return 0;
}

int create_or_delete(char *fname)
{
	int r, rc;

	r = (random() & 1);

	/* create */
	if ((create_file(fname) == 0)) {
		rc = delete_file(fname);
	} else {
		printf("Error: %d creating random file \n", errno);
	}

	if ((errorCount > dFileCount || errorCount > cFileCount)
	    && (errorCount > MAXERROR)) {
		fprintf(stderr, "Too many errors -- Aborting test\n");
		fprintf(stderr, "Total create files: %d\n", cFileCount);
		fprintf(stderr, "Total delete files: %d\n", dFileCount);
		fprintf(stderr, "Total error       : %d\n", errorCount);
		return (MAXERROR);
	}

	return 0;
}

int do_random_create_delete(int maxNum)
{
	int r, rc = 0;
	char fname[1024];
	time_t t;
	int i;

	printf("Running random create/delete test...\n");

	if (maxNum < 1 || maxNum > MAXNUM) {
		printf("MAX out of size %d\n", maxNum);
		return (maxNum);
	}

	time(&t);
	srandom((unsigned int)getpid() ^
		(((unsigned int)t << 16) | (unsigned int)t >> 16));

	/* 00/00/00/00 */
	for (i = 0; i < maxNum && rc != MAXERROR; i++) {
		r = random() % maxNum;
		sprintf(fname, "00/%2.2x/%2.2x/00%2.2x%2.2x%2.2x",
			((r >> 16) & 0xFF),
			((r >> 8) & 0xFF),
			((r >> 16) & 0xFF), ((r >> 8) & 0xFF), (r & 0xFF));

		rc = create_or_delete(fname);
	}

	fprintf(stderr, "Total create files: %d\n", cFileCount);
	fprintf(stderr, "Total delete files: %d\n", dFileCount);
	fprintf(stderr, "Total error       : %d\n", errorCount);
	return (rc);
}
