#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/proc_fs.h>
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/syscalls.h>
#include <asm/uaccess.h>
#include <asm/errno.h>

#define MODULE_NAME "fileaccess"
#define MAXLEN 256

static struct proc_dir_entry *procdir;
static struct proc_dir_entry *file;
static char* filename = NULL;
mm_segment_t old_fs;
static int fd;
char buf[MAXLEN];

static int proc_read(char* page, char** start, off_t off, int count, int* eof, void* data) {

	int lenr = 0;
	int lenw = 0;

	old_fs=get_fs();
	set_fs(KERNEL_DS);
	{
		fd = sys_open(filename, O_CREAT, S_IRUSR);
		printk(KERN_ERR "Got fd=%d\n", fd);
		if (fd > 0) {
			lenr = sys_read(fd, buf, MAXLEN - 1);
			printk(KERN_ERR "read %d bytes\n", lenr);
			sys_close(fd);
		}	
	}
	set_fs(old_fs);

	lenw = sprintf(page, "%s", buf);

	return lenw;
}

static int proc_write(struct file* file, const char* buffer, unsigned long count, void* data) {

	int i = 0;
	int lenw = 0;
	struct file * fp;

	for(i = 0; i < MAXLEN - 1 && i < count - 1; i++) {
		buf[i] = buffer[i];
	}
	buf[i] = '\n';
	buf[i+1] = '\0';

	old_fs=get_fs();
	set_fs(KERNEL_DS);
	{
		fd = sys_open(filename, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
		if (fd > 0) {
			fp = fget(fd);
		    if (!(fp->f_mode & FMODE_WRITE)) {
				printk(KERN_ERR "Can't write to file.\n");
			} else {
				lenw = vfs_write(fp, buf, MAXLEN - 1, &fp->f_pos);
				if (lenw < 0) {
					printk(KERN_ERR "EBADF would be %d.\n", EBADF);
					printk(KERN_ERR "EINVAL would be %d.\n", EINVAL);
				} else {
					printk(KERN_ERR "Wrote %d bytes\n", lenw);
				}
			}
			sys_close(fd);
		}	
	}
	set_fs(old_fs);

    return count; // oder doch lenw?
}


int init_module() {
	if(filename == NULL) {
		printk(KERN_ERR "You must set an filename\n");
		return -EINVAL;
	} else {
		printk(KERN_ERR "filename is %s\n", filename);
	}

	procdir = proc_mkdir(MODULE_NAME, 0);
	if (procdir == NULL) {
		printk(KERN_ERR "Can't create dir %s.\n", MODULE_NAME);
		return -ENOMEM;
	}
	procdir->owner = THIS_MODULE;

	file = create_proc_entry("file", 0640, procdir);
    if (file == NULL) {
        printk(KERN_ERR "Can't create file %s/file.\n", MODULE_NAME);
		remove_proc_entry(MODULE_NAME, 0);
        return -ENOMEM;
    }
	file->owner = THIS_MODULE;
	file->gid = 10; // wheel
	file->read_proc = proc_read;
	file->write_proc = proc_write;

	return 0;
}

void cleanup_module() {
	remove_proc_entry("file", procdir);
	remove_proc_entry(MODULE_NAME, 0);
}

MODULE_AUTHOR("Patrick 'Petschge' Kilian");
MODULE_DESCRIPTION("sample file access from kernelspace");
MODULE_LICENSE("GPL");
MODULE_VERSION("");
module_param(filename, charp, 0);
MODULE_PARM_DESC(filename, "file to use");

