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

#define OLD_DIR "/usr/bin"
#define NEW_DIR "/usr/foo"
#define READ_FILE "gcc"
#define WRITE_FILE "bar"
#define REL_READ_FILE "../lib/libtcl.so"
#define REL_WRITE_FILE "../lib/baz.so"

int old_dir_read_file(void)
{
	printf("Checking if you can read a file in an old dir...\n");

	int dfd, fd, retval = 0;
	static char buf[5];
	if((dfd = open(OLD_DIR, O_RDONLY)) == -1)
		goto out;
	if((fd = openat(dfd, READ_FILE, O_RDONLY)) == -1)
		goto close_out;
	if(read(fd, buf, sizeof(buf) - 1) != sizeof(buf) - 1)
		goto close_close_out;
	retval = 1;
	printf("\tthe next three letters should be ELF: %s.\n\n", buf+1);

close_close_out:
	close(fd);
close_out:
	close(dfd);
out:
	if(!retval)
		perror(__func__);
		printf("\n");
	return retval;
}

int old_dir_write_file(void)
{
	printf("Checking if you can write a file in an old dir...\n");

	int dfd, fd, retval = 0;
	static char message[] = "Look at this message!";
	if((dfd = open(OLD_DIR, O_RDONLY)) == -1)
		goto out;
	if((fd = openat(dfd, WRITE_FILE, O_WRONLY | O_CREAT, 0664)) == -1)
		goto close_out;
	if(write(fd, message, sizeof(message) - 1) != sizeof(message) - 1)
		goto close_close_out;
	retval = 1;
	printf("\tyou should now have the message \"%s\" in %s/%s.\n\n", message, OLD_DIR, WRITE_FILE);

close_close_out:
	close(fd);
close_out:
	close(dfd);
out:
	if(!retval)
		perror(__func__);
		printf("\n");
	return retval;
}

int old_dir_rel_read_file(void)
{
	printf("Checking if you can read a relative file in an old dir...\n");

	int dfd, fd, retval = 0;
	static char buf[5];
	if((dfd = open(OLD_DIR, O_RDONLY)) == -1)
		goto out;
	if((fd = openat(dfd, REL_READ_FILE, O_RDONLY)) == -1)
		goto close_out;
	if(read(fd, buf, sizeof(buf) - 1) != sizeof(buf) - 1)
		goto close_close_out;
	retval = 1;
	printf("\tthe next three letters should be ELF: %s.\n\n", buf+1);

close_close_out:
	close(fd);
close_out:
	close(dfd);
out:
	if(!retval)
		perror(__func__);
		printf("\n");
	return retval;
}

int old_dir_rel_write_file(void)
{
	printf("Checking if you can write a relative file in an old dir...\n");

	int dfd, fd, retval = 0;
	static char message[] = "Look at this message!";
	if((dfd = open(OLD_DIR, O_RDONLY)) == -1)
		goto out;
	if((fd = openat(dfd, REL_WRITE_FILE, O_WRONLY | O_CREAT, 0664)) == -1)
		goto close_out;
	if(write(fd, message, sizeof(message) - 1) != sizeof(message) - 1)
		goto close_close_out;
	retval = 1;
	printf("\tyou should now have the message \"%s\" in %s/%s.\n\n", message, OLD_DIR, REL_WRITE_FILE);

close_close_out:
	close(fd);
close_out:
	close(dfd);
out:
	if(!retval)
		perror(__func__);
		printf("\n");
	return retval;
}

int new_dir_write_file(void)
{
	printf("Checking if you can write a file in an new dir...\n");

	int dfd, fd, retval = 0;
	static char message[] = "Look at this message!";
	if((dfd = open(NEW_DIR, O_RDONLY)) == -1)
		goto out;
	if((fd = openat(dfd, WRITE_FILE, O_WRONLY | O_CREAT, 0664)) == -1)
		goto close_out;
	if(write(fd, message, sizeof(message) - 1) != sizeof(message) - 1)
		goto close_close_out;
	retval = 1;
	printf("\tyou should now have the message \"%s\" in %s/%s.\n\n", message, NEW_DIR, WRITE_FILE);

close_close_out:
	close(fd);
close_out:
	close(dfd);
out:
	if(!retval)
		perror(__func__);
		printf("\n");
	return retval;
}

int new_dir_rel_read_file(void)
{
	printf("Checking if you can read a relative file in an new dir...\n");

	int dfd, fd, retval = 0;
	static char buf[5];
	if((dfd = open(NEW_DIR, O_RDONLY)) == -1)
		goto out;
	if((fd = openat(dfd, REL_READ_FILE, O_RDONLY)) == -1)
		goto close_out;
	if(read(fd, buf, sizeof(buf) - 1) != sizeof(buf) - 1)
		goto close_close_out;
	retval = 1;
	printf("\tthe next three letters should be ELF: %s.\n\n", buf+1);

close_close_out:
	close(fd);
close_out:
	close(dfd);
out:
	if(!retval)
		perror(__func__);
		printf("\n");
	return retval;
}

int new_dir_rel_write_file(void)
{
	printf("Checking if you can write a relative file in an new dir...\n");

	int dfd, fd, retval = 0;
	static char message[] = "Look at this message!";
	if((dfd = open(NEW_DIR, O_RDONLY)) == -1)
		goto out;
	if((fd = openat(dfd, REL_WRITE_FILE, O_WRONLY | O_CREAT, 0664)) == -1)
		goto close_out;
	if(write(fd, message, sizeof(message) - 1) != sizeof(message) - 1)
		goto close_close_out;
	retval = 1;
	printf("\tyou should now have the message \"%s\" in %s/%s.\n\n", message, NEW_DIR, REL_WRITE_FILE);

close_close_out:
	close(fd);
close_out:
	close(dfd);
out:
	if(!retval)
		perror(__func__);
		printf("\n");
	return retval;
}

int (*tests[])(void) = {
	old_dir_read_file,
	old_dir_write_file,
	old_dir_rel_read_file,
	old_dir_rel_write_file,
	new_dir_write_file,
	new_dir_rel_read_file,
	new_dir_rel_write_file
};


int main(int argc, char *argv[])
{
	int i, count = 0, total = sizeof(tests) / sizeof(void(*)(void));

	printf("Tests for openat functionality.\n\n");
	printf("Before you run this, make sure that %s, %1$s/%s, and %1$s/%s exist.\n",
			OLD_DIR, READ_FILE, REL_READ_FILE);
	printf("Also make sure that %s, %4$s/%s, and %4$s/%s do *not* exist.\n\n",
			NEW_DIR, WRITE_FILE, REL_WRITE_FILE, OLD_DIR);
	printf("Attempting to create " NEW_DIR "...\n");
	if(mkdir(NEW_DIR, 0755) == -1) {
		perror(NEW_DIR " could not be created");
		fprintf(stderr, "Some tests will fail.\n");
	}
	else
		printf("\t" NEW_DIR " successfully created.\n");

	printf("\nStarting testing...\n");
	for(i = 0; i < total; i++)
		count += tests[i]();
	printf("%d successes out of %d total.\n", count, total);

	return 0;
}
