/*
 * 5799-WZQ (C) COPYRIGHT IBM CORPORATION 1988
 * LICENSED MATERIALS - PROPERTY OF IBM
 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
 */
/* $Header:inodestuff.c 12.2$ */
/* $ACIS:inodestuff.c 12.2$ */
/* $Source: /ibm/acis/usr/sys/afs/RCS/inodestuff.c,v $ */

#ifndef lint
static char *rcsid = "$Header:inodestuff.c 12.2$";
#endif

#include "../h/types.h"
#include "../h/param.h"
#include "../h/systm.h"
#include "../h/time.h"
#include "../h/kernel.h"
#include "../h/socket.h"
#include "../h/socketvar.h"
#include "../h/protosw.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/file.h"
#include "../h/uio.h"
#include "../h/vfs.h"
#include "../h/vnode.h"
#include "../ufs/inode.h"
#include "../afs/auxinode.h"
#include "../h/buf.h"
#include "../ufs/mount.h"
#include "../netinet/in.h"
#include "../h/mbuf.h"
#include "../afs/osi.h"
#include "../afs/lock.h"



/* All this below will be more organized soon... scout honors */
#ifdef	vax
/* get an existing gnode.*/
struct gnode *
igetinode(dev, gnode)
	dev_t dev;
	gno_t gnode;
{
	register struct mount *mp;
	register struct gnode *gp;

	GETMP(mp, dev);
	if (mp == NULL || (mp == (struct mount *) MSWAPX)) {
		u.u_error = ENXIO;
		return;
	}
	gp = gfs_gget(dev, mp, gnode, 0);
	if (gp == NULL) {
	    u.u_error = ENOENT;		/* Well... */
	    return;
	}
	if (gp->g_mode == 0) {
	    /* Not an allocated inode */
	    gforget(gp);	    
	    u.u_error = ENOENT;
	    return;
	}
	if (gp->g_nlink == 0 || (gp->g_mode&GFMT) != GFREG) {
	    gput(gp);
	    u.u_error = ENOENT;
	    return;
	}
	return gp;
}


/*
 * Really horrible fudge to allow us to drop an inode we got with iget but
 * which isn't really allocated.
 */
gforget(gp)
struct gnode *gp;
{
   extern struct gnode *gfreeh, **gfreet;

	gp->g_flag = 0;
	if (gfreeh) {
		*gfreet = gp;
		gp->g_freeb = gfreet;
	} else {
		gfreeh = gp;
		gp->g_freeb = &gfreeh;
	}
	gp->g_freef = NULL;
	gfreet = &gp->g_freef;
}

/*
 * icreate system call -- create an inode
 */
icreate()
{
	register struct a {
		int	dev;
		int	near_gnode;
		int	param1;
		int	param2;
		int	param3;
		int	param4;
	} *uap = (struct a *) u.u_ap;
	register struct gnode *gp, *newgp;
	register struct mount *mp;

	if (!suser())
		return;
	GETMP(mp, (dev_t)uap->dev);
	if (mp == NULL || (mp == (struct mount *) MSWAPX)) {
		u.u_error = EINVAL;
		return;
	}

	gp = gfs_gget((dev_t)uap->dev, mp, 2, 0);
	if (gp == NULL) {
		u.u_error = ENOENT; /\comment{* Well... */}
		return;
	}
	newgp = GALLOC(gp, 0, 0);
	gput(gp);
	if  (newgp == NULL)
	    return;
	newgp->g_flag |= GACC|GUPD|GCHG;
	newgp->g_nlink = 1;
	newgp->g_uid = 0;
	newgp->g_gid = -2;
	newgp->g_mode = GFREG;
	G_TO_I(newgp)->di_vicemagic = VICEMAGIC;
	G_TO_I(newgp)->di_vicep1 = uap->param1;
	G_TO_I(newgp)->di_vicep2 = uap->param2;
	G_TO_I(newgp)->di_vicep3 = uap->param3;
	G_TO_I(newgp)->di_vicep4 = uap->param4; 
	u.u_r.r_val1 = newgp->g_number;
	gput(newgp);
	return;
}


/*
 * iopen system call -- open an inode for reading/writing
 * Restricted to super user.
 * Any IFREG files.
 */
iopen()
{
	register struct a {
		int	dev;
		int	inode;
		int	usermode;
	} *uap = (struct a *) u.u_ap;
	register struct file *fp;
	register struct gnode *gp;
	extern struct fileops gnodeops;

	if (!suser())
		return;
	gp = ggetinode((dev_t)uap->dev, (gno_t)uap->inode);
	if (u.u_error)
		return;
	if (gp->g_gid != -2 && G_TO_I(gp)->di_vicemagic != VICEMAGIC) {
	    gput(gp);
	    u.u_error = EPERM;
	    return;
	}
	fp = falloc();
	if (fp == NULL) {
		gput(gp);
		return;
	}
	gfs_unlock(gp);
	fp->f_flag = (uap->usermode-FOPEN)&FMASK;
	fp->f_type = DTYPE_VNODE;
	fp->f_ops = &gnodeops;
	fp->f_data = (caddr_t)gp;
	return;
}


/*
 * Support for iinc() and idec() system calls--increment or decrement
 * count on inode.
 * Restricted to super user.
 * Only VICEMAGIC type inodes.
 */
iincdec(amount)
	int amount;
{
	register struct a {
		int	dev;
		int	inode;
		long	inode_p1;
	} *uap = (struct a *) u.u_ap;
	register struct gnode *gp;


	if (!suser())
		return;
	gp = ggetinode((dev_t)uap->dev, (gno_t)uap->inode);
	if (u.u_error)
		return;
	if (G_TO_I(gp)->di_vicemagic != VICEMAGIC)
		u.u_error = EPERM;
	else if (G_TO_I(gp)->di_vicep1 != uap->inode_p1)
	    u.u_error = ENXIO;
	else
		gp->g_nlink += amount;
	if (gp->g_nlink == 0)
		gp->g_vicemagic = 0;
	gp->g_flag |= GCHG;
	gput(gp);
}

iinc() {
    iincdec(1);
}

idec() {
    iincdec(-1);
}


/*
 * Support for iread/iwrite system calls.
 * Restricted to super user.
 * Only inodes with owner and group == -1.
 * NB:  VICEMAGIC inodes default to this owner and group.
 */
ireadwrite(rw)
{
	register struct a {
		int		dev;
		int		inode;
		long		inode_p1;
		unsigned int 	offset;
		char		*cbuf;
		unsigned int 	count;
	} *uap = (struct a *) u.u_ap;
	register struct gnode *gp;
	unsigned int resid;
	daddr_t	db[NDADDR], ib[NIADDR];
	int size;
	if (!suser())
		return;
	gp = ggetinode((dev_t)uap->dev, (gno_t)uap->inode);
	if (u.u_error)
		return;
	if (gp->g_gid != -2 && G_TO_I(gp)->di_vicemagic != VICEMAGIC) {
		gput(gp);
		u.u_error = EPERM;
		return;
	}
	if (G_TO_I(gp)->di_vicep1 != uap->inode_p1) {
	        gput(gp);
	        u.u_error = ENXIO;
		return;
	}
	bcopy(G_TO_I(gp)->di_db, db, sizeof db);
	bcopy(G_TO_I(gp)->di_ib, ib, sizeof ib);
	size = gp->g_size;
	resid = 0;
	gfs_unlock(gp);
	u.u_error = rdwri(
	    rw, gp, (caddr_t) uap->cbuf, uap->count, uap->offset, 0, &resid);
	u.u_r.r_val1 = uap->count - resid;
	if (size == gp->g_size && bcmp(G_TO_I(gp)->di_db,db,sizeof db) == 0
	    && bcmp(G_TO_I(gp)->di_ib,ib,sizeof ib) == 0) {
		gp->g_flag &= ~(GUPD|GACC|GCHG);
	}
	gput(gp);
}

iread()
{
	ireadwrite(UIO_READ);
}

iwrite()
{
    	ireadwrite(UIO_WRITE);
}


#else	vax

 struct fs *
 trygetfs(dev)
 	dev_t dev;
 {
 	register struct mount *mp;
 
 	mp = getmp(dev);
 	if (mp == NULL) {
 		return(NULL);
 	}
 	return (mp->m_bufp->b_un.b_fs);
 }

#ifdef	sun

/* get an existing inode.  Common code for iopen, iread/write, iinc/dec. */
/* Also used by rmt_remote to support passing of inode number from venus */
struct inode *
igetinode(dev, inode)
	dev_t dev;
	ino_t inode;
{
	register struct inode *ip;
	register struct fs *fs;
	fs = trygetfs(dev);
	if (fs == NULL) {
		u.u_error = ENXIO;
		return;
	}
	ip = iget(dev, fs, inode);
	if (ip == NULL) {
	    u.u_error = ENOENT;		/* Well... */
	    return;
	}
	if (ip->i_mode == 0) {
	    /* Not an allocated inode */
	    iforget(ip);	    
	    u.u_error = ENOENT;
	    return;
	}
	if (ip->i_nlink == 0 || (ip->i_mode&IFMT) != IFREG) {
	    iput(ip);
	    u.u_error = ENOENT;
	    return;
	}
	return ip;
}


/*
 * Really horrible fudge to allow us to drop an inode we got with iget but
 * which isn't really allocated.
 */
iforget(ip)
struct inode *ip;
{
   extern struct inode *ifreeh, **ifreet;

	ip->i_flag = 0;
	if (ifreeh) {
		*ifreet = ip;
		ip->i_freeb = ifreet;
	} else {
		ifreeh = ip;
		ip->i_freeb = &ifreeh;
	}
	ip->i_freef = NULL;
	ifreet = &ip->i_freef;
}

/*
 * icreate system call -- create an inode
 */
icreate()
{
	register struct a {
		int	dev;
		int	near_inode;
		int	param1;
		int	param2;
		int	param3;
		int	param4;
	} *uap = (struct a *) u.u_ap;
	register struct inode *ip, *newip;
	struct fs *fs;

	if (!suser())
		return;
	fs = trygetfs((dev_t)uap->dev);
	if (fs == NULL)
		return;
	/* Shouldn t be doing following line */
	ip = iget((dev_t)uap->dev, fs, 2);	/* AN example inode from this
						   file system, just to
						   satisfy ialloc */
	if (ip == NULL) {
		u.u_error = ENOENT; /* Well... */
		return;
	}
	newip = ialloc(ip, 0, 0);
	iput(ip);
	if  (newip == NULL)
	    return;
	imark(newip, IACC|IUPD|ICHG);
	newip->i_nlink = 1;
	newip->i_uid = 0;
	newip->i_gid = -2;
	newip->i_mode = IFREG;
	newip->i_vnode.v_type = VREG;
	newip->i_vicemagic = VICEMAGIC;
	newip->i_vicep1 = uap->param1;
	newip->i_vicep2 = uap->param2;
	newip->i_vicep3 = uap->param3;
	newip->i_vicep4 = uap->param4; 
	u.u_r.r_val1 = newip->i_number;
	iput(newip);
	return;
}


/*
 * iopen system call -- open an inode for reading/writing
 * Restricted to super user.
 * Any IFREG files.
 */
iopen()
{
	register struct a {
		int	dev;
		int	inode;
		int	usermode;
	} *uap = (struct a *) u.u_ap;
	register struct file *fp;
	register struct inode *ip;
	struct vnode *vp;
	extern struct fileops vnodefops;

	if (!suser())
		return;
	ip = igetinode((dev_t)uap->dev, (ino_t)uap->inode);
	if (u.u_error)
		return;
	if (ip->i_gid != -2 && ip->i_vicemagic != VICEMAGIC) {
	    iput(ip);
	    u.u_error = EPERM;
	    return;
	}
	fp = falloc();
	if (fp == NULL) {
		iput(ip);
		return;
	}
	iunlock(ip);
	fp->f_flag = (uap->usermode-FOPEN)&FMASK;
	fp->f_type = DTYPE_VNODE;
	fp->f_ops = &vnodefops;
	vp = ITOV(ip);
	fp->f_data = (caddr_t)vp;
	return;
}


/*
 * Support for iinc() and idec() system calls--increment or decrement
 * count on inode.
 * Restricted to super user.
 * Only VICEMAGIC type inodes.
 */
iincdec(amount)
	int amount;
{
	register struct a {
		int	dev;
		int	inode;
		long	inode_p1;
	} *uap = (struct a *) u.u_ap;
	register struct inode *ip;

	if (!suser())
		return;
	ip = igetinode((dev_t)uap->dev, (ino_t)uap->inode);
	if (u.u_error)
		return;
	if (ip->i_vicemagic != VICEMAGIC)
		u.u_error = EPERM;
	else if (ip->i_vicep1 != uap->inode_p1)
	    u.u_error = ENXIO;
	else
		ip->i_nlink += amount;
	if (ip->i_nlink == 0)
		ip->i_vicemagic = 0;
	ip->i_flag |= ICHG;
	iput(ip);
}

iinc() {
    iincdec(1);
}

idec() {
    iincdec(-1);
}


/*
 * Support for iread/iwrite system calls.
 * Restricted to super user.
 * Only inodes with owner and group == -1.
 * NB:  VICEMAGIC inodes default to this owner and group.
 */
ireadwrite(rw)
{
	register struct a {
		int		dev;
		int		inode;
		long		inode_p1;
		unsigned int 	offset;
		char		*cbuf;
		unsigned int 	count;
	} *uap = (struct a *) u.u_ap;
	register struct inode *ip;
	unsigned int resid;
	daddr_t	db[NDADDR], ib[NIADDR];
	int size;
	if (!suser())
		return;
	ip = igetinode((dev_t)uap->dev, (ino_t)uap->inode);
	if (u.u_error)
		return;
	if (ip->i_gid != -2 && ip->i_vicemagic != VICEMAGIC) {
		iput(ip);
		u.u_error = EPERM;
		return;
	}
	if (ip->i_vicep1 != uap->inode_p1) {
	        iput(ip);
	        u.u_error = ENXIO;
		return;
	}
	bcopy(ip->i_db, db, sizeof db);
	bcopy(ip->i_ib, ib, sizeof ib);
	size = ip->i_size;
	resid = 0;
	iunlock(ip);
	u.u_error = rdwri(
	    rw, ip, (caddr_t) uap->cbuf, uap->count, uap->offset, 0, &resid);
	u.u_r.r_val1 = uap->count - resid;
	if (size == ip->i_size && bcmp(ip->i_db,db,sizeof db) == 0
	    && bcmp(ip->i_ib,ib,sizeof ib) == 0) {
		/* Don t write out the inode if it hasn t really changed.
		   We don t care about inode dates in file server files */
		ip->i_flag &= ~(IUPD|IACC|ICHG);
	}
	VN_RELE(ITOV(ip));
}

iread()
{
	ireadwrite(UIO_READ);
}

iwrite()
{
    	ireadwrite(UIO_WRITE);
}

#endif sun  

#ifdef	ibm032

extern struct fs *trygetfs();

/*
 * Really horrible fudge to allow us to drop an inode we got with iget but
 * which isn't really allocated.
 */
iforget(ip)
struct inode *ip;
{
   extern struct inode *ifreeh, **ifreet;

	ip->i_flag = 0;
	if (ifreeh) {
		*ifreet = ip;
		ip->i_freeb = ifreet;
	} else {
		ifreeh = ip;
		ip->i_freeb = &ifreeh;
	}
	ip->i_freef = NULL;
	ifreet = &ip->i_freef;
}

/* get an existing inode.  Common code for iopen, iread/write, iinc/dec. */
/* Also used by rmt_remote to support passing of inode number from venus */
struct inode *
igetinode(dev, inode)
	dev_t dev;
	ino_t inode;
{
	register struct inode *ip;
	register struct fs *fs;
	fs = trygetfs(dev);
	if (u.u_error)
		return (struct inode *) 0;
	/* Note:  should optimize here--annoying that iget immediately does
	   another getfs... */
	ip = iget(dev, fs, inode);
	if (ip == NULL) {
	    u.u_error = ENOENT;	 /* Well... */
	    return (struct inode *) 0;
	}
	if (ip->i_mode == 0) {
	    /* Not an allocated inode */
	    iforget(ip);
	    u.u_error = ENOENT;
	    return (struct inode *) 0;
	}
	if (ip->i_nlink == 0 || (ip->i_mode&IFMT) != IFREG) {
	    iput(ip);
	    u.u_error = ENOENT;
	    return (struct inode *) 0;
	}
	return ip;
}

/*
 * icreate system call -- create an inode
 */
icreate()
{
	register struct a {
		int	dev;
		int	near_inode;
		int	param1;
		int	param2;
		int	param3;
		int	param4;
	} *uap = (struct a *) u.u_ap;
	register struct inode *ip, *newip;
	struct fs *fs;
	if (!suser())
		return;
	fs = trygetfs((dev_t)uap->dev);
	if (u.u_error)
		return;
	/* Shouldn t be doing following line */
	ip = iget((dev_t)uap->dev, fs, 2);	/* AN example inode from this
						   file system, just to
						   satisfy ialloc */
	if (u.u_error)
		return;
	newip = ialloc(ip, 0, 0);
        iput(ip);
	if  (u.u_error)
	    return;
	newip->i_flag |= IACC|IUPD|ICHG;
	newip->i_nlink = 1;
	newip->i_uid = 0;
	newip->i_gid = -2;
	newip->i_mode = IFREG;
	newip->i_vnode.v_type = VREG;
	newip->i_vicemagic = VICEMAGIC;
	newip->i_vicep1 = uap->param1;
	newip->i_vicep2 = uap->param2;
	newip->i_vicep3 = uap->param3;
	newip->i_vicep4 = uap->param4;
	u.u_r.r_val1 = newip->i_number;
	iput(newip);
	return;
}

/*
 * iopen system call -- open an inode for reading/writing
 * Restricted to super user.
 * Any IFREG files.
 */
iopen()
{
	register struct a {
		int	dev;
		int	inode;
		int	usermode;
	} *uap = (struct a *) u.u_ap;
	register struct file *fp;
	register struct inode *ip;
	register struct vnode *vp;
	extern struct fileops vnodefops;

	if (!suser())
		return;
	ip = igetinode((dev_t)uap->dev, (ino_t)uap->inode);
	if (u.u_error)
		return;
	if (ip->i_gid != (gid_t)-2 && ip->i_vicemagic != VICEMAGIC) {
	    iput(ip);
	    u.u_error = EPERM;
	    return;
	}
	fp = falloc();
	if (fp == NULL) {
		iput(ip);
		return;
	}
	iunlock(ip);
	fp->f_flag = (uap->usermode-FOPEN)&FMASK;
	fp->f_type = DTYPE_VNODE;
	fp->f_ops = &vnodefops;
	vp=&(ip->i_vnode);
	fp->f_data = (caddr_t)vp;
	return;
}

/*
 * Support for iinc() and idec() system calls--increment or decrement
 * count on inode.
 * Restricted to super user.
 * Only VICEMAGIC type inodes.
 */
iincdec(amount)
	int amount;
{
	register struct a {
		int	dev;
		int	inode;
		long	inode_p1;
	} *uap = (struct a *) u.u_ap;
	register struct inode *ip;
	if (!suser())
		return;
	ip = igetinode((dev_t)uap->dev, (ino_t)uap->inode);
	if (u.u_error)
		return;
	if (ip->i_vicemagic != VICEMAGIC)
		u.u_error = EPERM;
	else if (ip->i_vicep1 != uap->inode_p1)
	    u.u_error = ENXIO;
	else
		ip->i_nlink += amount;
	if (ip->i_nlink == 0)
		ip->i_vicemagic = 0;
	ip->i_flag |= ICHG;
	iput(ip);
}

iinc() {
    iincdec(1);
}

idec() {
    iincdec(-1);
}

/*
 * Support for iread/iwrite system calls.
 * Restricted to super user.
 * Only inodes with owner and group == -1.
 * NB:  VICEMAGIC inodes default to this owner and group.
 */
ireadwrite(rw)
{
	register struct a {
		int		dev;
		int		inode;
		long		inode_p1;
		unsigned int 	offset;
		char		*cbuf;
		unsigned int 	count;
	} *uap = (struct a *) u.u_ap;
	register struct inode *ip;
	unsigned int resid;
	daddr_t	db[NDADDR], ib[NIADDR];
	int size;
	if (!suser())
		return;
	ip = igetinode((dev_t)uap->dev, (ino_t)uap->inode);
	if (u.u_error)
		return;
	if (ip->i_gid != (gid_t)-2 && ip->i_vicemagic != VICEMAGIC) {
		iput(ip);
		u.u_error = EPERM;
		return;
	}
	if (ip->i_vicep1 != uap->inode_p1) {
	        iput(ip);
	        u.u_error = ENXIO;
		return;
	}
	bcopy(ip->i_db, db, sizeof db);
	bcopy(ip->i_ib, ib, sizeof ib);
	size = ip->i_size;
	resid = 0;
	iunlock(ip);
	u.u_error = rdwri(
	    rw, ip, (caddr_t) uap->cbuf, uap->count, uap->offset, 0, &resid);
	u.u_r.r_val1 = uap->count - resid;
	ilock(ip);
	if (size == ip->i_size && bcmp(ip->i_db,db,sizeof db) == 0
	    && bcmp(ip->i_ib,ib,sizeof ib) == 0) {
		/* Don t write out the inode if it hasn t really changed.
		   We don t care about inode dates in file server files */
		ip->i_flag &= ~(IUPD|IACC|ICHG);
	}
	iput(ip);
}

iread()
{
	ireadwrite(UIO_READ);
}

iwrite()
{
    	ireadwrite(UIO_WRITE);
}

#endif	ibm032

#endif	vax
