/*
 * Copyright (c) 2004 Robbert Haarman
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

#include <linux/module.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/fs.h>

#define ROOT_INO 1

void tutorfs_read_inode(struct inode *inode) {
        ino_t ino = inode->i_ino;

	/* Defaults */
	inode->i_mode = S_IRUGO | S_IWUSR;
        inode->i_nlink = 1;
       	inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
       	inode->i_blocks = 0;
       	inode->i_blksize = inode->i_sb->s_blocksize;

	if(ino == ROOT_INO) {
		/* Special case for the root directory */
		inode->i_mode |= S_IFDIR | S_IXUGO;
        	inode->i_nlink = 2;
                inode->i_fop = &simple_dir_operations;
                inode->i_op = &simple_dir_inode_operations;
	}
}

static void tutorfs_put_super(struct super_block *sb) {
        return;
}

static struct super_operations tutorfs_ops = {
        .read_inode     = tutorfs_read_inode,
        .put_super      = tutorfs_put_super,
};

int tutorfs_fill_sb(struct super_block *sb, void *data, int silent) {
        struct inode *root;

        sb->s_op = &tutorfs_ops;

        sb->s_blocksize = 4096;
        sb->s_blocksize_bits = 12;

        root = iget(sb, ROOT_INO);
        sb->s_root = d_alloc_root(root);
        if(!sb->s_root) return -EINVAL;

        /* Indicate success */
        return 0;
}

/* Account for incompatible change in VFS interface between Linux 2.6.17 and 2.6.18 */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)
struct super_block *tutorfs_get_sb(struct file_system_type *fs_type,
	int flags, const char *dev_name, void *data)
{
        return get_sb_nodev(fs_type, flags, data, tutorfs_fill_sb);
}
#else
static int tutorfs_get_sb(struct file_system_type *fs_type,
	int flags, const char *dev_name, void *data, struct vfsmount *mnt)
{
	return get_sb_nodev(fs_type, flags, data, tutorfs_fill_sb, mnt);
}
#endif

static struct file_system_type tutorfs_type = {
        .name           = "tutorfs",
        .get_sb         = tutorfs_get_sb,
        .kill_sb        = kill_anon_super
};

int __init tutorfs_init(void) {
	register_filesystem(&tutorfs_type);
	return 0;
}

void __exit tutorfs_exit(void) {
}

module_init(tutorfs_init);
module_exit(tutorfs_exit);
