/*
 *  linux/fs/ifs/inode.c
 *
 *  Written 1992,1993 by Werner Almesberger
 */

#include <asm/segment.h>

#include <linux/fs.h>
#include <linux/ifs_fs.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/locks.h>
#include <linux/stat.h>


void ifs_put_inode(struct inode *inode)
{
	int n;

Dprintk("ifs_put_inode\n");
	if (IFS_I(inode)->next)
		IFS_I(IFS_I(inode)->next)->prev = IFS_I(inode)->prev;
	if (IFS_I(inode)->prev)
		IFS_I(IFS_I(inode)->prev)->next = IFS_I(inode)->next;
	else IFS_SB(inode->i_sb)->ifs_inodes = IFS_I(inode)->next;
	if (IFS_I(inode)->parent)
		iput(IFS_I(inode)->parent);
	for (n = 0; n < IFS_LAYERS(inode); n++)
		if (IFS_NTH(inode,n))
			iput(IFS_NTH(inode,n));
/* no inode caching ... (would be useless anyway) */
	clear_inode(inode);
}


void ifs_put_super(struct super_block *sb)
{
Dprintk("ifs_put_super\n");
	lock_super(sb);
	sb->s_dev = 0;
	unlock_super(sb);
	return;
}


void ifs_write_super(struct super_block *sb)
{
Dprintk("ifs_write_super\n");
	sb->s_dirt = 0;
	if (!(IFS_NTH(sb->s_mounted,0)->i_sb->s_flags & MS_RDONLY))
		IFS_OP_SB(sb->s_mounted,0,write_super,(IFS_NTH(sb->s_mounted,0)
		    ->i_sb));
	/* lower layers are always read-only */
}


static struct super_operations ifs_sops = { 
	ifs_read_inode,
	ifs_notify_change,
	ifs_write_inode,
	ifs_put_inode,
	ifs_put_super,
	ifs_write_super,
	ifs_statfs
};


struct super_block *ifs_read_super(struct super_block *s,void *data,int silent)
{
	struct ifs_mpar *mpar;
	struct inode *layers[IFS_MAX_LAYERS];
	int layer,retval,i,j;

	mpar = (struct ifs_mpar *) data;
	if (mpar->magic != IFS_MOUNT_MAGIC) {
		printk("Bad magic number. Please use smount.\n");
		return NULL;
	}
	s->s_magic = IFS_SUPER_MAGIC;
	IFS_SB(s)->flags = mpar->flags;
	if ((IFS_SB(s)->layers = mpar->layers) >
	    IFS_MAX_LAYERS) {
		printk("Too many layers\n");
		return NULL;
	}
	for (layer = 0; layer < IFS_SB(s)->layers; layer++) {
		retval = namei(mpar->names[layer],&layers[layer]);
		if (retval) {
			while (--layer >= 0)
				iput(layers[layer]);
			printk("namei returns %d\n",retval);
			return NULL;
		}
	}
	for (i = 0; i < layer; i++)
		for (j = i+1; j < layer; j++)
			if (layers[i] == layers[j]) {
				while (--layer >= 0)
					iput(layers[layer]);
				printk("IFS: layer %d and %d are identical.\n",
				    i,j);
				return NULL;
			}
	IFS_SB(s)->ifs_inodes = NULL;
	IFS_SB(s)->ifs_ino = IFS_MIN_INO;
	IFS_SB(s)->ino_wait = NULL;
	IFS_SB(s)->ino_lock = 0;
	/* set up enough so that it can read an inode */
	s->s_op = &ifs_sops;
	if (!(s->s_mounted = iget(s,IFS_ANY_INO))) {
		printk("get root inode failed\n");
		return NULL;
	}
Dprintk("initializing root\n");
	ifs_init_inode(s->s_mounted,layers);
	IFS_I(s->s_mounted)->parent = NULL;
	return s;
}


void ifs_statfs(struct super_block *sb,struct statfs *buf)
{
	struct statfs sum,temp;
	int n;
	unsigned short old_fs;

	old_fs = get_fs();
	set_fs(get_ds());
	IFS_DO_SB(sb->s_mounted,0,statfs,(IFS_NTH(sb->s_mounted,0)->i_sb,
	    &sum));
	sum.f_type = sb->s_magic;
	for (n = 1; n < IFS_SB(sb)->layers; n++) {
		IFS_DO_SB(sb->s_mounted,n,statfs,(IFS_NTH(sb->s_mounted,n)
		    ->i_sb,&temp));
		sum.f_blocks += (temp.f_blocks*temp.f_bsize)/sum.f_bsize;
		sum.f_files += temp.f_files;
	}
	set_fs(old_fs);
	memcpy_tofs(buf,&sum,sizeof(sum));
}


void ifs_read_inode(struct inode *inode)
{
Dprintk("ifs_read_inode\n");
	inode->i_mode = 0;
	IFS_I(inode)->pop_lock = 0;
	IFS_I(inode)->pop_wait = NULL;
}


void ifs_write_inode(struct inode *inode)
{
Dprintk("ifs_write_inode\n");
	if (IFS_IS_RO(inode))
		return;
	inode->i_dirt = 0;
	IFS_OP_SB(inode,0,write_inode,(IFS_NTH(inode,0)));
	/* lower layers are always read-only */
}


int ifs_notify_change(int flags,struct inode *inode)
{
	int retval;

Dprintk("ifs_notify_change (0x%X,0x%X)\n",flags,(int) inode);
	if (flags && IFS_IS_RO(inode)) {
		inode->i_uid = IFS_I(inode)->val_uid;
		inode->i_gid = IFS_I(inode)->val_gid;
		inode->i_mode = IFS_I(inode)->val_mode;
		inode->i_size = IFS_I(inode)->val_size;
		return -EROFS;
	}
	if (inode->i_uid == IFS_I(inode)->val_uid && inode->i_gid ==
	    IFS_I(inode)->val_gid)
		flags &= ~NOTIFY_UIDGID;
	if (inode->i_mode == IFS_I(inode)->val_mode)
		flags &= ~NOTIFY_MODE;
	if (inode->i_size == IFS_I(inode)->val_size || !(S_ISREG(inode->i_mode)
	    || S_ISDIR(inode->i_mode)))
		flags &= ~NOTIFY_SIZE;
	if (!flags)
		return 0;
	if (!IFS_NTH(inode,0)) {
		retval = ifs_clone_file(inode);
		if (retval)
			return retval;
	}
	IFS_NTH(inode,0)->i_uid = IFS_I(inode)->val_uid = inode->i_uid;
	IFS_NTH(inode,0)->i_gid = IFS_I(inode)->val_gid = inode->i_gid;
	IFS_NTH(inode,0)->i_mode = IFS_I(inode)->val_mode = inode->i_mode;
	IFS_NTH(inode,0)->i_size = IFS_I(inode)->val_size = inode->i_size;
	if (!IFS_CAN_SB(inode,0,notify_change))
		return 0;
	return IFS_DO_SB(inode,0,notify_change,(flags,IFS_NTH(inode,0)));
}
