/*
 *  linux/fs/ifs/file.c
 *
 *  Written 1992,1993 by Werner Almesberger
 *
 *  IFS regular file handling primitives
 */

#include <asm/segment.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/ifs_fs.h>


static int ifs_lseek(struct inode *inode,struct file *filp,off_t offset,
    int origin);
static int ifs_open(struct inode *inode,struct file *filp);
static void ifs_release(struct inode *inode,struct file *filp);


#define C_ ,
#define IFS_F_WRAPPER(name,args,argl,retnone) \
  static int ifs_##name(struct inode *inode,struct file *filp args) \
  { \
    int retval; \
  \
    if (!IFS_F(filp)->open) \
      return -EBADF; \
    if (!IFS_F(filp)->open->f_op->name) \
      return retnone; \
    IFS_F(filp)->open->f_pos = filp->f_pos; \
    retval = IFS_F(filp)->open->f_op->name(IFS_F(filp)->open->f_inode, \
      IFS_F(filp)->open argl); \
    filp->f_pos = IFS_F(filp)->open->f_pos; \
    return retval; \
  }


IFS_F_WRAPPER(read,C_ char *buffer C_ int size,C_ buffer C_ size,-EINVAL)
IFS_F_WRAPPER(write,C_ char *buffer C_ int size,C_ buffer C_ size,-EINVAL)
IFS_F_WRAPPER(select,C_ int flag C_ select_table *table,C_ flag C_ table,1)
IFS_F_WRAPPER(ioctl,C_ unsigned int op C_ unsigned int arg,C_ op C_ arg,-EINVAL)
IFS_F_WRAPPER(mmap,C_ unsigned long addr C_ size_t length C_ int mask C_
    unsigned long offset,C_ addr C_ length C_ mask C_ offset,-ENODEV)
IFS_F_WRAPPER(fsync,,,0) /* ? */


static struct file_operations ifs_file_operations = {
	ifs_lseek,		/* lseek */
	ifs_read,		/* read */
	ifs_write,		/* write */
	NULL,			/* readdir - bad */
	ifs_select,		/* select */
	ifs_ioctl,		/* ioctl */
	ifs_mmap,		/* mmap */
	ifs_open,		/* open */
	ifs_release,		/* release */
	ifs_fsync		/* fsync */
};

struct inode_operations ifs_file_inode_operations = {
	&ifs_file_operations,	/* default file operations */
	NULL,			/* create */
	NULL,			/* lookup */
	NULL,			/* link */
	NULL,			/* unlink */
	NULL,			/* symlink */
	NULL,			/* mkdir */
	NULL,			/* rmdir */
	NULL,			/* mknod */
	NULL,			/* rename */
	NULL,			/* readlink */
	NULL,			/* follow_link */
	NULL,			/* bmap */
	ifs_truncate,		/* truncate */
	NULL			/* permission */
};


static int ifs_lseek(struct inode *inode,struct file *filp,off_t offset,
    int origin)
{
	int retval;

	if (!IFS_F(filp)->open)
		return -EBADF;
	if (IFS_F(filp)->open->f_op->lseek) {
    		retval = IFS_F(filp)->open->f_op->lseek(IFS_F(filp)->open->
		    f_inode,IFS_F(filp)->open,offset,origin);
		if (retval >= 0)
			filp->f_pos = retval;
		return retval;
	}
	retval = origin ? origin == 1 ? filp->f_pos+offset :
	    IFS_F(filp)->open->f_inode->i_size-offset : offset;
	if (retval < 0)
		return -EINVAL;
	filp->f_reada = 0;
	return filp->f_pos = retval;
}


static int ifs_open(struct inode *inode,struct file *filp)
{
	struct inode *use;
	int retval,n;

Dprintk("ifs_open\n");
	if (((filp->f_mode & 2) || (IFS_SB(inode->i_sb)->flags & IFS_FF_CACHE))
	  && !IFS_NTH(inode,0)) {
		retval = ifs_clone_file(inode);
		if (retval)
			return retval;
	}
	use = NULL;
	for (n = 0; n < IFS_LAYERS(inode); n++)
		if (IFS_NTH(inode,n)) {
			use = IFS_NTH(inode,n);
			break;
		}
	if (!use) /* bogus inode */
		return -ENOENT;
	return ifs_open_file(use,&IFS_F(filp)->open,filp->f_flags);
}


static void ifs_release(struct inode *inode,struct file *filp)
{
	if (!IFS_F(filp)->open)
		printk("ifs_release: open is NULL\n");
	else {
		ifs_close_file(IFS_F(filp)->open);
		IFS_F(filp)->open = NULL;
	}
}


void ifs_truncate(struct inode *inode)
{
	if (IFS_CAN_INODE(inode,0,truncate))
		IFS_DO_INODE(inode,0,truncate,(IFS_NTH(inode,0)));
}
