static char rcsid[] = "$Header: sys_inode.c,v 820.1 86/12/04 19:58:30 root Exp $";
static char sccsid[]="%W% %Y% %Q% %G%";

/************************************************************************
*									*
*				Copyright 1984				*
*			VALID LOGIC SYSTEMS INCORPORATED		*
*									*
*	This listing contains confidential proprietary information	*
*	which is not to be disclosed to unauthorized persons without	*
*	written consent of an officer of Valid Logic Systems 		*
*	Incorporated.							*
*									*
*	The copyright notice appearing above is included to provide	*
*	statutory protection in the event of unauthorized or 		*
*	unintentional public disclosure.				*
*									*
************************************************************************/

/*	sys_inode.c	6.1	83/07/29	*/

#include "../h/param.h"
#include "../h/systm.h"
#include "../h/dir.h"
#include "../h/ioctl.h"
#include "../h/user.h"
#include "../h/inode.h"
#include "../h/proc.h"
#include "../h/fs.h"
#include "../h/conf.h"
#include "../h/buf.h"
#include "../h/mount.h"
#include "../h/file.h"
#include "../h/uio.h"
#include "../h/tty.h"
#include "../h/cmap.h"
#include "../h/stat.h"
#include "../h/kernel.h"
#if	defined(VALID_EFS) || defined(VALID_DFS)
#include "../vnet/vnet.h"
#include "../conn/conn.h"
#include "../rpc/rpc.h"
#include "../efs/efs.h"
#ifdef	VALID_DFS
#include "../dfs/dfs.h"
#endif	VALID_DFS
#endif	defined(VALID_EFS) || defined(VALID_DFS)

int	ino_rw(), ino_ioctl(), ino_select(), ino_close();
struct 	fileops inodeops =
	{ ino_rw, ino_ioctl, ino_select, ino_close };

ino_rw(fp, rw, uio)
	struct file *fp;
	enum uio_rw rw;
	struct uio *uio;
{
	register struct inode *ip = (struct inode *)fp->f_data;
	int error;

	if ((ip->i_mode&IFMT) == IFREG) {
#ifndef	DFS_NEW_RWIP
#ifdef BSDBUGFIX
		ilock(ip);
#else BSDBUGFIX
		ILOCK(ip);
#endif BSDBUGFIX
		if (fp->f_flag&FAPPEND && rw == UIO_WRITE)
			uio->uio_offset = fp->f_offset = ip->i_size;
		error = rwip(ip, uio, rw);
#ifdef BSDBUGFIX
		iunlock(ip);
#else BSDBUGFIX
		IUNLOCK(ip);
#endif BSDBUGFIX
	} else
		error = rwip(ip, uio, rw);
#else	DFS_NEW_RWIP
		if (fp->f_flag&FAPPEND && rw == UIO_WRITE)
			uio->uio_offset = fp->f_offset = ip->i_size;
		error = rwip(ip, uio, rw, -1/*serverSide to do ilock/iunlock*/);
	} else
		error = rwip(ip, uio, rw, 0/*No implicit ilock/iunlock*/);
#endif	DFS_NEW_RWIP
	return (error);
}

rdwri(rw, ip, base, len, offset, segflg, aresid)
	struct inode *ip;
	caddr_t base;
	int len, offset, segflg;
	int *aresid;
	enum uio_rw rw;
{
	struct uio auio;
	struct iovec aiov;
	int error;

	auio.uio_iov = &aiov;
	auio.uio_iovcnt = 1;
	aiov.iov_base = base;
	aiov.iov_len = len;
	auio.uio_resid = len;
	auio.uio_offset = offset;
	auio.uio_segflg = segflg;
#ifndef	DFS_NEW_RWIP
	error = rwip(ip, &auio, rw);
#else	DFS_NEW_RWIP
	error = rwip(ip, &auio, rw, 0/*No implicit ilock/iunlock*/);
#endif	DFS_NEW_RWIP
	if (aresid)
		*aresid = auio.uio_resid;
	else
		if (auio.uio_resid)
			error = EIO;
	return (error);
}

#ifndef	DFS_NEW_RWIP
rwip(ip, uio, rw)
#else	DFS_NEW_RWIP
rwip(ip, uio, rw, implicitLock)
#endif	DFS_NEW_RWIP
	register struct inode *ip;
	register struct uio *uio;
	enum uio_rw rw;
#ifdef	DFS_NEW_RWIP
	register implicitLock;	/* !0 ==> serverSide to do ilock()/iunlock() */
#endif	DFS_NEW_RWIP
{
	dev_t dev = (dev_t)ip->i_rdev;
	struct buf *bp;
	struct fs *fs;
	daddr_t lbn, bn;
	register int n, on, type;
	int size;
	long bsize;
	extern int mem_no;
	int error = 0;

	if (rw != UIO_READ && rw != UIO_WRITE)
		panic("rwip");
	if (rw == UIO_READ && uio->uio_resid == 0)
		return (0);

#ifdef	VALID_DFS
	if (ip->i_host) {
		/*
		 * Agent for a remote inode.
		 * Make a remote rwip call.
		 */
#ifndef	DFS_NEW_RWIP
		return dfsClient_rwip(ip, uio, rw);
#else	DFS_NEW_RWIP
		return dfsClient_rwip(ip, uio, rw, implicitLock);
#endif	DFS_NEW_RWIP
	}
#endif	VALID_DFS

	if (uio->uio_offset < 0 &&
	    ((ip->i_mode&IFMT) != IFCHR || mem_no != major(dev)))
		return (EINVAL);
	if (rw == UIO_READ)
		ip->i_flag |= IACC;
	type = ip->i_mode&IFMT;
	if (type == IFCHR) {
		if (rw == UIO_READ)
			error = (*cdevsw[major(dev)].d_read)(dev, uio);
		else {
			ip->i_flag |= IUPD|ICHG;
			error = (*cdevsw[major(dev)].d_write)(dev, uio);
		}
		return (error);
	}
	if (uio->uio_resid == 0)
		return (0);
	if (rw == UIO_WRITE && type == IFREG &&
	    uio->uio_offset + uio->uio_resid >
	      u.u_rlimit[RLIMIT_FSIZE].rlim_cur) {
		psignal(u.u_procp, SIGXFSZ);
		return (EFBIG);
	}
	if (type != IFBLK) {
		dev = ip->i_dev;
		fs = ip->i_fs;
		bsize = fs->fs_bsize;
	} else
		bsize = BLKDEV_IOSIZE;
	do {
		lbn = uio->uio_offset / bsize;
		on = uio->uio_offset % bsize;
		n = MIN((unsigned)(bsize - on), uio->uio_resid);
		if (type != IFBLK) {
			if (rw == UIO_READ) {
				int diff = ip->i_size - uio->uio_offset;
				if (diff <= 0)
					return (0);
				if (diff < n)
					n = diff;
			}
			bn = fsbtodb(fs,
			    bmap(ip, lbn, rw == UIO_WRITE ? B_WRITE: B_READ, (int)(on+n)));
			if (u.u_error || rw == UIO_WRITE && (long)bn<0)
				return (u.u_error);
			if (rw == UIO_WRITE && uio->uio_offset + n > ip->i_size &&
			   (type == IFDIR || type == IFREG || type == IFLNK))
				ip->i_size = uio->uio_offset + n;
			size = blksize(fs, ip, lbn);
		} else {
			bn = lbn * (BLKDEV_IOSIZE/DEV_BSIZE);
			rablock = bn + (BLKDEV_IOSIZE/DEV_BSIZE);
			rasize = size = bsize;
		}
		if (rw == UIO_READ) {
			if ((long)bn<0) {
				bp = geteblk(size);
				clrbuf(bp);
			} else if (ip->i_lastr + 1 == lbn)
				bp = breada(dev, bn, size, rablock, rasize);
			else
				bp = bread(dev, bn, size);
			ip->i_lastr = lbn;
		} else {
			int i, count;
			extern struct cmap *mfind();

			count = howmany(size, DEV_BSIZE);
			for (i = 0; i < count; i += CLSIZE)
				if (mfind(dev, bn + i))
					munhash(dev, bn + i);
			if (n == bsize) 
				bp = getblk(dev, bn, size);
			else
				bp = bread(dev, bn, size);
		}
		n = MIN(n, size - bp->b_resid);
		if (bp->b_flags & B_ERROR) {
			error = EIO;
			brelse(bp);
			goto bad;
		}
		u.u_error =
		    uiomove(bp->b_un.b_addr+on, n, rw, uio);
		if (rw == UIO_READ) {
			if (n + on == bsize || uio->uio_offset == ip->i_size)
				bp->b_flags |= B_AGE;
			brelse(bp);
		} else {
			if ((ip->i_mode&IFMT) == IFDIR)
				bwrite(bp);
			else if (n + on == bsize) {
				bp->b_flags |= B_AGE;
				bawrite(bp);
			} else
				bdwrite(bp);
			ip->i_flag |= IUPD|ICHG;
			if (u.u_ruid != 0)
				ip->i_mode &= ~(ISUID|ISGID);
		}
	} while (u.u_error == 0 && uio->uio_resid > 0 && n != 0);
	if (error == 0)				/* XXX */
		error = u.u_error;		/* XXX */
bad:
	return (error);
}

ino_ioctl(fp, com, data)
	struct file *fp;
	register int com;
	caddr_t data;
{
	register struct inode *ip = ((struct inode *)fp->f_data);
#ifndef	VALID_DFS
	register int fmt = ip->i_mode & IFMT;
	dev_t dev;
#endif	VALID_DFS

#ifdef	VALID_DFS
	u.u_error = ip->i_host ? 
		dfsClient_ioctl(ip, com, data, fp->f_offset, fp->f_flag) :
		ioctl1(ip, com, data, fp->f_offset, fp->f_flag);
}

ioctl1(ip, com, data, offset, flag)
	register struct inode * ip;
{
	register int fmt = ip->i_mode & IFMT;
	dev_t dev;
#endif	VALID_DFS

	switch (fmt) {

	case IFREG:
	case IFDIR:
		if (com == FIONREAD) {
#ifdef VALID_DFS
			*(off_t *)data = ip->i_size - offset;
#else VALID_DFS
			*(off_t *)data = ip->i_size - fp->f_offset;
#endif VALID_DFS
			return (0);
		}
		if (com == FIONBIO || com == FIOASYNC)	/* XXX */
			return (0);			/* XXX */
		/* fall into ... */

	default:
		return (ENOTTY);

	case IFCHR:
		dev = ip->i_rdev;
		u.u_r.r_val1 = 0;
#ifdef INTERMEDSIG
		if (((u.u_procp->p_flag&SOUSIG) == 0 ||
		    (u.u_procp->p_flag&SNUSIG)) &&
			    setjmp(&u.u_qsave)) {
#else INTERMEDSIG
		if ((u.u_procp->p_flag&SOUSIG) == 0 && setjmp(&u.u_qsave)) {
#endif INTERMEDSIG
			u.u_eosys = RESTARTSYS;
			return (0);
		}
		return ((*cdevsw[major(dev)].d_ioctl)(dev, com, data,
#ifdef VALID_DFS
		    flag));
#else VALID_DFS
		    fp->f_flag));
#endif VALID_DFS
	}
}

ino_select(fp, which)
	struct file *fp;
	int which;
{
	register struct inode *ip = (struct inode *)fp->f_data;

	switch (ip->i_mode & IFMT) {

	default:
		return (1);		/* XXX */

	case IFCHR:
		return
		    (*cdevsw[major(ip->i_rdev)].d_select)(ip->i_rdev, which);
	}
}

#ifdef notdef
ino_clone()
{

	return (EOPNOTSUPP);
}
#endif

ino_stat(ip, sb)
	register struct inode *ip;
	register struct stat *sb;
{

	IUPDAT(ip, &time, &time, 0);
	/*
	 * Copy from inode table
	 */
	sb->st_dev = ip->i_dev;
	sb->st_ino = ip->i_number;
	sb->st_mode = ip->i_mode;
	sb->st_nlink = ip->i_nlink;
	sb->st_uid = ip->i_uid;
	sb->st_gid = ip->i_gid;
	sb->st_rdev = (dev_t)ip->i_rdev;
	sb->st_size = ip->i_size;
	sb->st_atime = ip->i_atime;
	sb->st_spare1 = 0;
	sb->st_mtime = ip->i_mtime;
	sb->st_spare2 = 0;
	sb->st_ctime = ip->i_ctime;
	sb->st_spare3 = 0;
	/* this doesn't belong here */
	if ((ip->i_mode&IFMT) == IFBLK)
		sb->st_blksize = BLKDEV_IOSIZE;
	else if ((ip->i_mode&IFMT) == IFCHR)
		sb->st_blksize = MAXBSIZE;
#ifdef	VALID_DFS
	else if (ip->i_host)
		/*
		 * BUG: blksize should be determined from the remote fs.
		 */
		sb->st_blksize = MAXBSIZE;
#endif	VALID_DFS
	else
		sb->st_blksize = ip->i_fs->fs_bsize;
	sb->st_blocks = ip->i_blocks;
#ifndef	VALID_DFS
	sb->st_spare4[0] = sb->st_spare4[1] = 0;
#else	VALID_DFS
	sb->st_host[0] = ip->i_host->node.host.high;
	sb->st_host[1] = ip->i_host->node.host.low;
#endif	VALID_DFS
	return (0);
}

ino_close(fp)
	register struct file *fp;
{
	register struct inode *ip = (struct inode *)fp->f_data;
	register struct mount *mp;
	int flag, mode;
	dev_t dev;
	register int (*cfunc)();
#ifdef BSDBUGFIX
	struct file *savedfp = fp;	/* since fp is demolished below */
#endif BSDBUGFIX
#ifdef VALID_DFS
	struct connection * host;
#endif VALID_DFS

	if (fp->f_flag & (FSHLOCK|FEXLOCK))
		ino_unlock(fp, FSHLOCK|FEXLOCK);
	flag = fp->f_flag;
#ifdef	VALID_DFS
	host = ip->i_host;
#endif	VALID_DFS
	dev = (dev_t)ip->i_rdev;
	mode = ip->i_mode & IFMT;
	ilock(ip);
#ifdef	DFS_WCOUNT
	/*
	 * This is the clientSide.
	 *
	 * ino_close() is the best marriage of necessity with opportunity
	 * to manage
	 *		(struct inode *)ip->i_wcount, i_flag&IOPENWRITE,
	 * and		(dfs_remote_t *)rp->wcount
	 *
	 * We update the refreshed/ilock-d (agent) inode gotten
	 * from the serverSide, twiddle the agent, and then
	 * let iput() transport the updates to back to the serverSide.
	 *
	 * Since we cannot decrement the (dfs_remote_t *)rp->wcount
	 * from here, we will have that work done on the serverSide .
	 */
	if (ip->i_flag & IOPENWRITE)
		if (fp->f_flag & FWRITE)	/* This machine had it FWRITE */
			if (ip->i_wcount > 0) {
				if (--ip->i_wcount <= 0)
					ip->i_flag &= ~IOPENWRITE;
				/*
				 * Server side iput()==>iupdate() will
				 * handle the (dfs_remote_t *)rp->wcount update.
				 */
			} else	DFS_DEBUG(4,("ino_close/E: ip=0x%X i_wcount would go negative!\n", ip));
#endif	DFS_WCOUNT
	iput(ip);
	fp->f_count = 0;			/* XXX Should catch */
#ifdef	VALID_DFS
	if (host)
		return;
#endif	VALID_DFS
	switch (mode) {

	case IFCHR:
		cfunc = cdevsw[major(dev)].d_close;
#ifdef VALID_EFS
		/*
		 * If the file was on the extended file
		 * system then a special close must be done
		 * over the net.
		 */
		if (efs_isRemoteFile(fp)) {
			(*cfunc)(dev, flag, fp);
			return;
		}
#endif VALID_EFS
		break;

	case IFBLK:
		/*
		 * We don't want to really close the device if it is mounted
		 */
/* MOUNT TABLE SHOULD HOLD INODE */
		for (mp = mount; mp < &mount[NMOUNT]; mp++)
			if (mp->m_bufp != NULL && mp->m_dev == dev)
				return;
		cfunc = bdevsw[major(dev)].d_close;
		break;

	default:
		return;
	}

	/*
	 * Check that another inode for the same device isn't active.
	 * This is because the same device can be referenced by
	 * two different inodes.
	 */
	for (fp = file; fp < fileNFILE; fp++) {
		if (fp->f_type == DTYPE_SOCKET)		/* XXX */
			continue;
		if (fp->f_count && (ip = (struct inode *)fp->f_data) &&
		    ip->i_rdev == dev && (ip->i_mode&IFMT) == mode)
			return;
	}
	if (mode == IFBLK) {
		/*
		 * On last close of a block device (that isn't mounted)
		 * we must invalidate any in core blocks, so that
		 * we can, for instance, change floppy disks.
		 */
		bflush(dev);
		binval(dev);
	}
#ifdef BSDBUGFIX
	(*cfunc)(dev, flag, savedfp);
#else BSDBUGFIX
	(*cfunc)(dev, flag, fp);
#endif BSDBUGFIX
}

/*
 * Place an advisory lock on an inode.
 */
ino_lock(fp, cmd)
	register struct file *fp;
	int cmd;
{
	register int priority = PLOCK;
	register struct inode *ip = (struct inode *)fp->f_data;

	if (cmd & LOCK_EX)
		priority++;
	/*
	 * If there's a exclusive lock currently applied
	 * to the file, or someone waiting to get a
	 * exclusive lock, then we've gotta wait for the
	 * lock with everyone else.
	 */
again:
	while (ip->i_flag & (IEXLOCK|ILWAIT)) {
		if (cmd & LOCK_NB)
			return (EWOULDBLOCK);
		/*
		 * If we're holding an exclusive
		 * lock, then release it.
		 */
		if (fp->f_flag & FEXLOCK) {
			ino_unlock(fp, FEXLOCK);
			goto again;
		}
		ip->i_flag |= ILWAIT;
		sleep((caddr_t)&ip->i_exlockc, priority);
	}
	if (cmd & LOCK_EX) {
		cmd &= ~LOCK_SH;
		/*
		 * Must wait for any shared locks to finish
		 * before we try to apply a exclusive lock.
		 */
		while (ip->i_flag & ISHLOCK) {
			if (cmd & LOCK_NB)
				return (EWOULDBLOCK);
			/*
			 * If we're holding a shared
			 * lock, then release it.
			 */
			if (fp->f_flag & FSHLOCK) {
				ino_unlock(fp, FSHLOCK);
				goto again;
			}
			ip->i_flag |= ILWAIT;
			sleep((caddr_t)&ip->i_shlockc, PLOCK);
		}
	}
	if (fp->f_flag & (FSHLOCK|FEXLOCK))
		panic("ino_lock");
	if (cmd & LOCK_SH) {
		ip->i_shlockc++;
		ip->i_flag |= ISHLOCK;
		fp->f_flag |= FSHLOCK;
	}
	if (cmd & LOCK_EX) {
		ip->i_exlockc++;
		ip->i_flag |= IEXLOCK;
		fp->f_flag |= FEXLOCK;
	}
	return (0);
}

/*
 * Unlock a file.
 */
ino_unlock(fp, kind)
	register struct file *fp;
	int kind;
{
	register struct inode *ip = (struct inode *)fp->f_data;
	int flags;

	kind &= fp->f_flag;
	if (ip == NULL || kind == 0)
		return;
	flags = ip->i_flag;
	if (kind & FSHLOCK) {
#ifdef	BSDBUGFIX
		/*
		 * It is possible that the user invoked flock()
		 * with LOCK_UN specified, upon a file that is NOT locked.
		 * This latter implies ino_unlock(fp, FSHLOCK|FEXLOCK)
		 * for which a panic() is inappropriate...
		 * merely breaking his knuckles is sufficient.
		 */
		if ((flags & ISHLOCK) == 0)
			u.u_error = EINVAL;
		else
#else	BSDBUGFIX
		if ((flags & ISHLOCK) == 0)
			panic("ino_unlock: SHLOCK");
#endif	BSDBUGFIX
		if (--ip->i_shlockc == 0) {
			ip->i_flag &= ~ISHLOCK;
			if (flags & ILWAIT)
				wakeup((caddr_t)&ip->i_shlockc);
		}
		fp->f_flag &= ~FSHLOCK;
	}
	if (kind & FEXLOCK) {
#ifdef	BSDBUGFIX
		/*
		 * It is possible that the user invoked flock()
		 * with LOCK_UN specified, upon a file that is NOT locked.
		 * This latter implies ino_unlock(fp, FSHLOCK|FEXLOCK)
		 * for which a panic() is inappropriate...
		 * merely breaking his knuckles is sufficient.
		 */
		if ((flags & IEXLOCK) == 0)
			u.u_error = EINVAL;
		else
#else	BSDBUGFIX
		if ((flags & IEXLOCK) == 0)
			panic("ino_unlock: EXLOCK");
#endif	BSDBUGFIX
		if (--ip->i_exlockc == 0) {
			ip->i_flag &= ~(IEXLOCK|ILWAIT);
			if (flags & ILWAIT)
				wakeup((caddr_t)&ip->i_exlockc);
		}
		fp->f_flag &= ~FEXLOCK;
	}
}

/*
 * Openi called to allow handler
 * of special files to initialize and
 * validate before actual IO.
 */
#ifdef VALID_EFS
openi(ip, openFlags, fileMode)
	int	openFlags;	/* Flags from user's system call	*/
	int	fileMode;	/* File mode as in chmod(2)		*/
#else VALID_EFS
openi(ip, openFlags)
	int	openFlags;	/* Flags from user's system call	*/
#endif VALID_EFS
	register struct inode *ip;
{
	dev_t dev = (dev_t)ip->i_rdev;
	register int maj = major(dev);

	switch (ip->i_mode&IFMT) {

	case IFCHR:
#ifdef	VALID_DFS
		if (ip->i_host) 
			return dfsClient_openi(ip, openFlags, fileMode);
#endif	VALID_DFS
		if ((u_int)maj >= nchrdev)
			return (ENXIO);
#ifdef VALID_EFS
		return ((*cdevsw[maj].d_open)(dev, openFlags, fileMode));
#else VALID_EFS
		return ((*cdevsw[maj].d_open)(dev, openFlags));
#endif VALID_EFS

	case IFBLK:
#ifdef	VALID_DFS
		if (ip->i_host) 
			return dfsClient_openi(ip, openFlags, fileMode);
#endif	VALID_DFS
		if ((u_int)maj >= nblkdev)
			return (ENXIO);
		return ((*bdevsw[maj].d_open)(dev, openFlags));
	}
	return (0);
}

/*
 * Revoke access the current tty by all processes.
 * Used only by the super-user in init
 * to give ``clean'' terminals at login.
 */
vhangup()
{

	if (!suser())
		return;
	if (u.u_ttyp == NULL)
		return;
	forceclose(u.u_ttyd);
	if ((u.u_ttyp->t_state) & TS_ISOPEN)
		gsignal(u.u_ttyp->t_pgrp, SIGHUP);
}

forceclose(dev)
	dev_t dev;
{
	register struct file *fp;
	register struct inode *ip;

	for (fp = file; fp < fileNFILE; fp++) {
		if (fp->f_count == 0)
			continue;
		if (fp->f_type != DTYPE_INODE)
			continue;
		ip = (struct inode *)fp->f_data;
		if (ip == 0)
			continue;
		if ((ip->i_mode & IFMT) != IFCHR)
			continue;
		if (ip->i_rdev != dev)
			continue;
#ifdef	VALID_DFS
#ifdef	notyet
		if (ip->i_host != host)
			continue;
#endif	notyet
#endif	VALID_DFS
		fp->f_flag &= ~(FREAD|FWRITE);
	}
}
