
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/malloc.h>
#include <linux/module.h>
#include <linux/locks.h>

static char *file="Ala ma kota\n";
#define FILESIZE 12

static char *filename="ala.txt";
#define NAMESIZE 7


#define ROOT 0
#define PLIK 1
#define IFS_SUPER_MAGIC 0xabcd4321


void ifs_read_inode(struct inode * inode);
void ifs_put_super(struct super_block *sb);
void ifs_put_super(struct super_block *sb);
void ifs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz);
void ifs_read_inode(struct inode * inode);
int ifs_lookup(struct inode * dir, const char * name, int len,
	struct inode ** result);
int ifs_readdir(struct inode * inode, struct file * file,
	void * dirent, filldir_t filldir);
int ifs_read(struct inode * inode, struct file * file,char * buf, int count);

struct super_operations ifs_sops = { 
	ifs_read_inode,
	NULL,
	NULL,
	NULL,
	ifs_put_super,
	NULL,
	ifs_statfs,
	NULL
};


static struct file_operations ifs_file_file_ops = {
	read: ifs_read
};

struct inode_operations ifs_file_inode_ops = {
	default_file_ops: &ifs_file_file_ops
};


struct file_operations ifs_dir_file_ops = {
	readdir: ifs_readdir
};

struct inode_operations ifs_dir_inode_ops = {
	default_file_ops: &ifs_dir_file_ops,
	lookup: ifs_lookup
};

void ifs_put_super(struct super_block *sb)
{
	iput(sb->s_mounted);
	lock_super(sb);
	sb->s_dev = 0;
	unlock_super(sb);
	MOD_DEC_USE_COUNT;
}

void ifs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
{
	struct statfs tmp;

	tmp.f_type = IFS_SUPER_MAGIC;
	tmp.f_bsize = 1024;
	tmp.f_blocks = 0;
	tmp.f_bfree = 0;
	tmp.f_bavail = 0;
	tmp.f_files = 0;
	tmp.f_ffree = 0;
	tmp.f_namelen = 14;
	memcpy_tofs(buf, &tmp, bufsiz);
}

void ifs_read_inode(struct inode * inode)
{
	inode->i_op = NULL;
	inode->i_mode = 0;
	inode->i_uid = 0;
	inode->i_gid = 0;
	inode->i_size = 0;
	inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
	inode->i_blocks = 0;
	inode->i_blksize = 1024;
	switch (inode->i_ino) {
		case ROOT: 
		  inode->i_op=&ifs_dir_inode_ops;
		  inode->i_nlink=2;
		  inode->i_mode=S_IFDIR | S_IRUGO | S_IXUGO;
		  break;
		case PLIK:
		  inode->i_mode=S_IFREG | S_IRUGO;
		  inode->i_op=&ifs_file_inode_ops;
	          inode->i_nlink=1;
	          inode->i_size=FILESIZE;
	          inode->u.generic_ip=(void *)file;
		  break;
        }
}


struct super_block *ifs_read_super(struct super_block *s,void *data, 
				    int silent)
{
	lock_super(s);
	s->s_blocksize = 1024;
	s->s_blocksize_bits = 10;
	s->s_magic = IFS_SUPER_MAGIC;
	s->s_op = &ifs_sops;
	s->s_flags |= MS_RDONLY;
	unlock_super(s);
	if (!(s->s_mounted=iget(s,ROOT))) {
		s->s_dev = 0;
		printk("get root inode failed\n");
		return NULL;
	}
	MOD_INC_USE_COUNT;
	return s;
}

int ifs_lookup(struct inode * dir, const char * name, int len,
	struct inode ** result)
{
	struct super_block * sb=dir->i_sb;
	*result = NULL;
	if (!dir || !S_ISDIR(dir->i_mode))
		return -ENOENT;
	if (!len || (name[0] == '.' && (len == 1 ||
	    (name[1] == '.' && len == 2)))) {
		*result = dir;
		return 0;
	}
	iput(dir);
	if ((len==NAMESIZE) && !memcmp(name,filename,NAMESIZE)) {
		*result=iget(sb,PLIK);
		return 0;
	}
	return -ENOENT;
}

int ifs_readdir(struct inode * inode, struct file * file,
	void * dirent, filldir_t filldir)
{
	int quit=0;
	if (!inode || !S_ISDIR(inode->i_mode))
		return -EBADF;
	while(quit>=0) {
		switch((int)file->f_pos) {
			case 0: quit=filldir(dirent,".",1,0,inode->i_ino);
				break;
			case 1: quit=filldir(dirent,"..",2,1,inode->i_ino);
				break;
			case 2: quit=filldir(dirent,filename,NAMESIZE,2,PLIK);
				break;
		}
		file->f_pos++;
		if (file->f_pos>2)
		   quit=-1;
	}
	return 0;
}

int ifs_read(struct inode * inode, struct file * file,char * buf, int count)
{
	int maxcount=inode->i_size - file->f_pos;
	if (count<0)
		return -EINVAL;
	if (count>maxcount)
	   count=maxcount;
	if (count>0) {
		memcpy_tofs(buf,(char *)inode->u.generic_ip+file->f_pos,count);
		file->f_pos+=count;
	}
	return count;
}

struct file_system_type ifs_type = {ifs_read_super,"ifs",0,NULL };

int init_module() 
{
	if (register_filesystem(&ifs_type)<0) {
		printk("Cannot register ifs filesystem");
		return -1;
	}
	return 0;	
}

void cleanup_module() 
{
	if (unregister_filesystem(&ifs_type)<0)
		printk("Cannot unregister ifs filesystem");
}
