#include "../afs/param.h"       /* Should be always first */
#include "../afs/sysincludes.h" /* Standard vendor system headers */
#include "../afs/afsincludes.h" /* Afs-based standard headers */
#include "../h/syscall.h"
#include "../afs/auxinode.h"
#undef i_vicemagic
#undef i_vicep1
#undef i_vicep2
#undef i_vicep3
#undef i_vicep4
#include <linux/fs.h>
#include <linux/ext2_fs.h>
#include <linux/ext2_fs_i.h>
#include <linux/stat.h>
#include <linux/mount.h>
#include <linux/kdev_t.h>
#include <fcntl.h>
#include <unistd.h>
extern long afs_debug;
 
#ifndef EXT2_VICEMAGIC_FL
#define EXT2_VICEMAGIC_FL 0x40000000
#endif

#ifdef __powerpc__

int afs_syscall_iopen(dev_t dev, unsigned long ino, long mode) {
  return ENOSYS;
}

int afs_syscall_icreate(dev_t dev, unsigned long near_ino, long vol, long
                        vnode, long uniq, long datav) {
  return ENOSYS;
}

int afs_syscall_ireadwrite(dev_t dev, unsigned long ino, long vol,
			   long offset, unsigned char *buffaddr,
                           unsigned long count, long rw) {
  return ENOSYS;
}

int afs_syscall_iincdec(dev_t dev, unsigned long ino, long vol, long count) {
  return ENOSYS;
}

#else
/* Sign flipping of return codes occurs in these functions due to the way
   afs_syscall_call deals with return values. Thus positive return values denote errors, while negative return values refer to IO completion counts or fd's */

/* This can only be called if we (a) believe the inode is unallocated
                                 (b) didn't modify it.
   It's probably not SMP safe. */
static void iforget(struct inode *inode) {
  if (!inode)
    return;
  if (inode->i_dirt) {
    printk("Iforget: inode is dirty! trying iput....\n");
    iput(inode);
    return;
  }
  if (inode->i_nlink) {
    printk("Iforget: inode has links! trying iput....\n");
    iput(inode);
    return;
  }
  if (inode->i_lock) {
    printk("Iforget: inode is locked! trying iput....\n");
    iput(inode);
    return;
  }
  if (!inode->i_count) {
    printk("VFS: iforget: trying to free free inode\n");
    printk("VFS: device %s, inode %lu, mode=0%07o\n",
           kdevname(inode->i_rdev), inode->i_ino, inode->i_mode);
    return;
  }
  if (inode->i_count>1) {
    inode->i_count--;
    printk("Iforget: Someone else has this inode!\n");
    return; /* Uh oh. If this can happen, so can the worse case:
               An allocated inode with only one ref gets passed
               in here, and we don't free it correctly */
  }
  inode->i_count--;
  clear_inode(inode);
}

static struct inode  *igetinode(dev_t dev, unsigned long ino, int *err) {
  struct vfsmount *tmp;

  *err=0;
  
  tmp=lookup_vfsmnt(to_kdev_t(dev));
  if (tmp)
    return iget(tmp->mnt_sb,ino);
  *err=-ENODEV;
  return NULL;
}

  

int afs_syscall_icreate(dev_t dev, unsigned long near_ino, long vol, long
                        vnode, long uniq, long datav) {

  struct inode *near_inode,*ninode;
  int err;
  unsigned long ino;
  
  if (afs_debug & 0x10) printk("icreate(%d,%d,%d,%d,%d,%d)\n",dev, near_ino, vol, vnode, uniq, datav);
  if (!fsuser())
    return EPERM;
  if (near_ino == 0)
    near_ino=EXT2_ROOT_INO; /* XXX */
  near_inode=igetinode(dev,near_ino,&err);
  if (!near_inode) {
    if (afs_debug & 0x10) printk("igetinode FAILS\n");
    if (err)
      if (afs_debug & 0x10) printk("Error %d\n", err);
    return err ? -err : ENOENT;
  }
  if (!near_inode->i_nlink) {
    iforget(near_inode);
    return ENOENT;
  }  
  ninode=ext2_new_inode(near_inode, S_IFREG, &err);
  if (!ninode) {
    if (afs_debug & 0x10) printk("Error %d creating new inode\n", err);
    iput(near_inode);
    return -err;
  }  
  ninode->u.ext2_i.i_flags |= EXT2_VICEMAGIC_FL;
  ninode->u.ext2_i.i_vicep1=vol;
  ninode->u.ext2_i.i_vicep2=vnode;
  ninode->u.ext2_i.i_vicep3=uniq;
  ninode->u.ext2_i.i_version=datav;
  ninode->i_gid=-2;
  ninode->i_uid=0;
  ninode->i_op=&ext2_file_inode_operations;
  ino=ninode->i_ino;
  iput(ninode);
  iput(near_inode);
  if (afs_debug & 0x10) printk("Newly created inode had ino %d\n", ino);
  return -ino;
}

int afs_syscall_iopen(dev_t dev, unsigned long ino, long mode) {
  struct inode *inode;
  struct file *f;
  int error,flag,fd;
  
  if (afs_debug & 0x10) printk("iopen(%d,%d,%d)\n",dev, ino,mode);
  if (!fsuser())
    return EPERM;
  fd=get_unused_fd();
  if (fd < 0)
    return -fd;
  f = get_empty_filp();
  if (!f) {
    put_unused_fd(fd);
    return ENFILE;
  }
  f->f_flags = flag = mode;
  f->f_mode = (flag+1) & O_ACCMODE;
  inode=igetinode(dev,ino,&error);
  if (!inode) {
    error=-error;
    if (afs_debug & 0x10) printk("igetinode FAILS\n");
    if (error)
      if (afs_debug & 0x10) printk("Error %d\n", error);
    if (!error)
      error=ENOENT;
    goto cleanup_file;
  }
  if (!inode->i_nlink) {
    if (afs_debug & 0x10) printk("inode %d unallocated\n", ino);
    iforget(inode);
    error=ENOENT;
    goto cleanup_file;
  }
  if (f->f_mode & FMODE_WRITE) {
    error = get_write_access(inode);
    if (error) {      
      if (afs_debug & 0x10) printk("Error %d getting write access\n", error);
      error=-error;
      goto cleanup_inode;
    }    
  }
  f->f_inode = inode;
  f->f_pos = 0;
  f->f_reada = 0;
  f->f_op = NULL;
  if (inode->i_op)
    f->f_op = inode->i_op->default_file_ops;
  if (f->f_op && f->f_op->open) {
                error = f->f_op->open(inode,f);
                if (error) {
                  if (afs_debug & 0x10) printk("Error %d doing fs open\n", error);
                  goto cleanup_all;
                }                
  }
  f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
  current->files->fd[fd] = f;
  if (afs_debug & 0x10) printk("iopen fd = %d\n", fd);
  return -fd;
  
cleanup_all:
  if (f->f_mode & FMODE_WRITE)
    put_write_access(inode);
cleanup_inode:
  iput(inode);
cleanup_file:
  f->f_count--;
  put_unused_fd(fd);
  return error;
}

int afs_syscall_ireadwrite(dev_t dev, unsigned long ino, long vol, long offset, unsigned char *buffaddr,
                           unsigned long count, long rw) {
  struct inode *inode;  
  int error;
  struct uio auio;
  struct iovec iov;
  struct AFS_UCRED *cred;
  int retval;
  struct file *f;

  if (afs_debug & 0x10) printk("irw(%d,%d,%d,%d,0x%x,%d,%d)\n",dev, ino, vol, offset, buffaddr, count, rw);
  if (!fsuser())
    return EPERM;
  error = verify_area(rw ? VERIFY_READ : VERIFY_WRITE, buffaddr, count);
  if (error) {
    if (afs_debug & 0x10) printk("Bad Address!\n");
    return -error;
  }
  inode=igetinode(dev,ino,&error);
  if (!inode) {
    error=-error;
    if (afs_debug & 0x10) printk("igetinode FAILS\n");
    if (error)
      if (afs_debug & 0x10) printk("Error %d\n", error);
    if (!error)
      error=ENOENT;
    return error;
  }
  if (!inode->i_nlink) {
    if (afs_debug & 0x10) printk("inode %d unallocated\n", ino);
    iforget(inode);
    return ENOENT;
  }
  if (!(inode->u.ext2_i.i_flags & EXT2_VICEMAGIC_FL)) {
    if (afs_debug & 0x10) printk("Not a vice inode\n");
    error=EPERM;
    goto cleanup_inode;    
  }
  if (inode->u.ext2_i.i_vicep1 != vol) {
    if (afs_debug & 0x10) printk("Vice inode for other vol %d\n", inode->u.ext2_i.i_vicep1 );
    error=ENXIO;
    goto cleanup_inode;
  }
  f=get_empty_filp();
  if (!f) {
    error=EMFILE;
    goto cleanup_inode;
  }
  if (rw) {
    error = get_write_access(inode);
    if (error) {
      error=-error;
      if (afs_debug & 0x10) printk("Error %d getting write access\n", error);
      goto cleanup_file;
    }    
  }
  if (rw && (afs_debug & 0x20)) {
    char hex[16*3+1];
    char tmpbuf[4];
    int i;
    hex[0]=0;
    for (i=0;i < count && i< 16; i++) {
      sprintf(tmpbuf, "%x ", get_user(&buffaddr[i]));
      strcat(hex,tmpbuf);
    }
    printk(hex);
  }
/*  afs_linux_buf2uio(buffaddr, count, offset, 0,  UIO_USERSPACE, &auio, &iov);
  if (afs_debug & 0x10) printk("About to do I/O\n");
  error=-(afs_linux_vop_rdwr(inode, &auio, rw, 0, NULL));
*/
  f->f_inode = inode;
  f->f_op = inode->i_op->default_file_ops;
  f->f_pos = offset;
  if (rw)
      error = f->f_op->write(inode, f, buffaddr, count);
  else {
      error = f->f_op->read(inode, f, buffaddr, count);
  }
  error=-error;
  if (error > 0) {
    if (afs_debug & 0x10) printk("Error %d doing I/O\n", error);
  } else {
    if (afs_debug & 0x10) 
       if (rw) printk("Wrote %d bytes to inode %d\n", -error, ino);
       else printk("Read %d bytes from inode %d\n", -error, ino);
  }
  if (rw) {
    put_write_access(inode);
  }
cleanup_file:
  f->f_count--;
cleanup_inode:
  iput(inode);
  return error;
}


int afs_syscall_iincdec(dev_t dev, unsigned long ino, long vol, long count) {

  struct inode *inode;
  int error,flag,fd;
  
  if (afs_debug & 0x10) printk("iincdec(%d,%d,%d,%d)\n",dev, ino, vol, count);
  if (!fsuser())
    return EPERM;
  inode=igetinode(dev,ino,&error);
  if (!inode) {
    error=-error;
    if (afs_debug & 0x10) printk("igetinode FAILS\n");
    if (error)
      if (afs_debug & 0x10) printk("Error %d\n", error);
    return error ? error : ENOENT;
  }  
  if (!inode->i_nlink) {
    if (afs_debug & 0x10) printk("inode %d unallocated\n", ino);
    iforget(inode);
    return ENOENT;
  }
  if (!(inode->u.ext2_i.i_flags & EXT2_VICEMAGIC_FL)) {
    if (afs_debug & 0x10) printk("Not a vice inode\n");
    iput(inode);
    return EPERM;
  }
  if (inode->u.ext2_i.i_vicep1 != vol) {
    if (afs_debug & 0x10) printk("Vice inode for other vol %d\n", inode->u.ext2_i.i_vicep1 );
    iput(inode);
    return ENXIO;
  }
  if (count < -inode->i_nlink) {
    printk("iincdec: Would make nlink negative\n");
    iput(inode);
    return EINVAL;
  }
  inode->i_nlink += count;
  inode->i_dirt=1;
  if (afs_debug & 0x10) printk("iincdec: Link count for %d now %d\n", ino, inode->i_nlink);
  if (!inode->i_nlink) {
    if (afs_debug & 0x10) printk("iincdec: marking inode %d as unused \n", ino);
    inode->u.ext2_i.i_flags &= ~ EXT2_VICEMAGIC_FL;
  }
  iput(inode);
  return 0;
}
#endif
