/* @(#)sys3.c	1.4 */
#include "sys/param.h"
#include "sys/types.h"
#include "sys/sysmacros.h"
#include "sys/systm.h"
#include "sys/mount.h"
#include "sys/ino.h"
#include "sys/buf.h"
#include "sys/filsys.h"
#include "sys/dir.h"
#include "sys/signal.h"
#include "sys/user.h"
#include "sys/errno.h"
#include "sys/inode.h"
#include "sys/file.h"
#include "sys/conf.h"
#include "sys/stat.h"
#include "sys/ttold.h"
#include "sys/var.h"
#ifdef UCB_NET
#include "sys/termio.h"
#include "net/misc.h"
#include "net/socketvar.h"
#endif

/*
 * the fstat system call.
 */
fstat()
{
	register struct file *fp;
	register struct a {
		int	fdes;
		struct stat *sb;
	} *uap;

	uap = (struct a *)u.u_ap;
	fp = getf(uap->fdes);
	if(fp == NULL)
		return;
#ifdef UCB_NET
	if (fp->f_flag & FSOCKET)
		u.u_error = sostat((struct socket *)fp->f_socket, uap->sb);
	else
#endif
	stat1(fp->f_inode, uap->sb);
}

/*
 * the stat system call.
 */
stat()
{
	register struct inode *ip;
	register struct a {
		char	*fname;
		struct stat *sb;
	} *uap;

	uap = (struct a *)u.u_ap;
	ip = namei(uchar, 0);
	if(ip == NULL)
		return;
	stat1(ip, uap->sb);
	iput(ip);
}

/*
 * The basic routine for fstat and stat:
 * get the inode and pass appropriate parts back.
 */
stat1(ip, ub)
register struct inode *ip;
struct stat *ub;
{
	register struct dinode *dp;
	register struct buf *bp;
	register struct stat *dsp;
	struct stat ds;

	if(ip->i_flag&(IACC|IUPD|ICHG))
		iupdat(ip, &time, &time);
	/*
	 * first copy from inode table
	 */
	dsp = &ds;
	dsp->st_dev = brdev(ip->i_dev);
	dsp->st_ino = ip->i_number;
	dsp->st_mode = ip->i_mode;
	dsp->st_nlink = ip->i_nlink;
	dsp->st_uid = ip->i_uid;
	dsp->st_gid = ip->i_gid;
	dsp->st_rdev = (dev_t)ip->i_rdev;
	dsp->st_size = ip->i_size;
	/*
	 * next the dates in the disk
	 */
	bp = bread(ip->i_dev, FsITOD(ip->i_dev, ip->i_number));
	dp = bp->b_un.b_dino;
	dp += FsITOO(ip->i_dev, ip->i_number);
	dsp->st_atime = dp->di_atime;
	dsp->st_mtime = dp->di_mtime;
	dsp->st_ctime = dp->di_ctime;
	brelse(bp);
	if (copyout((caddr_t)dsp, (caddr_t)ub, sizeof(ds)) < 0)
		u.u_error = EFAULT;
}

/*
 * the dup system call.
 */
dup()
{
	register struct file *fp;
	int i;
	struct a {
		int	fdes;
	} *uap;

	uap = (struct a *)u.u_ap;
	fp = getf(uap->fdes);
	if(fp == NULL)
		return;
	if ((i = ufalloc(0)) < 0)
		return;
	u.u_ofile[i] = fp;
	fp->f_count++;
}

/*
 * the file control system call.
 */
fcntl()
{
	register struct file *fp;
	register struct a {
		int	fdes;
		int	cmd;
		int	arg;
	} *uap;
	register i;
	register struct user *up;

	up = &u;
	uap = (struct a *)up->u_ap;
	fp = getf(uap->fdes);
	if (fp == NULL)
		return;
	switch(uap->cmd) {
	case 0:
		i = uap->arg;
		if (i < 0 || i > NOFILE) {
			up->u_error = EINVAL;
			return;
		}
		if ((i = ufalloc(i)) < 0)
			return;
		up->u_ofile[i] = fp;
		fp->f_count++;
		break;

	case 1:
		up->u_rval1 = up->u_pofile[uap->fdes];
		break;

	case 2:
		up->u_pofile[uap->fdes] = uap->arg;
		break;

	case 3:
		up->u_rval1 = fp->f_flag+FOPEN;
		break;

	case 4:
		fp->f_flag &= (FREAD|FWRITE);
		fp->f_flag |= (uap->arg-FOPEN) & ~(FREAD|FWRITE);
		break;

	default:
		up->u_error = EINVAL;
	}
}

/*
 * character special i/o control
 */
ioctl()
{
	register struct file *fp;
	register struct inode *ip;
	register struct a {
		int	fdes;
		int	cmd;
		int	arg;
	} *uap;
	register dev_t dev;
#ifdef UCB_NET
	register unsigned fmt;
#endif

	uap = (struct a *)u.u_ap;
	if ((fp = getf(uap->fdes)) == NULL)
		return;
#ifdef UCB_NET
	if (fp->f_flag & FSOCKET) {
		soioctl((struct socket *)fp->f_socket, uap->cmd, (caddr_t)uap->arg);
		return;
	}
#endif
	ip = fp->f_inode;
#ifdef UCB_NET
	fmt = ip->i_mode & IFMT;
	if (fmt != IFCHR) {
		if (uap->cmd==FIONREAD && (fmt == IFREG || fmt == IFDIR
			|| fmt == IFIFO)) {
			off_t nread;

			if ((ip->i_mode&IFMT)==IFIFO)
				nread = ip->i_size;
			else
				nread = ip->i_size - fp->f_offset;
			if (copyout((caddr_t)&nread, (caddr_t)uap->arg,
				 sizeof(off_t)))
				u.u_error = EFAULT;
		} else if (uap->cmd == FIONBIO /*|| uap->cmd == FIOASYNC*/)
			return;
		else
			u.u_error = ENOTTY;
		return;
	}
#else
	if ((ip->i_mode&IFMT) != IFCHR) {
		u.u_error = ENOTTY;
		return;
	}
#endif UCB_NET
	dev = (dev_t)ip->i_rdev;
	(*cdevsw[(short)major(dev)].d_ioctl)(minor(dev),uap->cmd,uap->arg,fp->f_flag);
}

/*
 * old stty and gtty
 */
stty()
{
	register struct a {
		int	fdes;
		int	arg;
		int	narg;
	} *uap;

	uap = (struct a *)u.u_ap;
	uap->narg = uap->arg;
	uap->arg = TIOCSETP;
	ioctl();
}

gtty()
{
	register struct a {
		int	fdes;
		int	arg;
		int	narg;
	} *uap;

	uap = (struct a *)u.u_ap;
	uap->narg = uap->arg;
	uap->arg = TIOCGETP;
	ioctl();
}

/*
 * the mount system call.
 */
smount()
{
	register struct user *up;
	register dev_t dev;
	register struct inode *ip;
	register struct mount *mp;
	struct mount *smp;
	register struct filsys *fp;
	struct inode *bip = NULL;
	register struct a {
		char	*fspec;
		char	*freg;
		int	ronly;
	} *uap;

	up = &u;
	uap = (struct a *)up->u_ap;
	if(!suser())
		return;
	ip = namei(uchar, 0);
	if(ip == NULL)
		return;
	if((ip->i_mode&IFMT) != IFBLK)
		up->u_error = ENOTBLK;
	dev = (dev_t)ip->i_rdev;
	if(bmajor(dev) >= bdevcnt)
		if (!up->u_error)
			up->u_error = ENXIO;
	if (up->u_error)
		goto out;
	bip = ip;
	up->u_dirp = (caddr_t)uap->freg;
	ip = namei(uchar, 0);
	if(ip == NULL) {
		iput(bip);
		return;
	}
	if ((ip->i_mode&IFMT) != IFDIR) {
		up->u_error = ENOTDIR;
		goto out;
	}
	if (ip->i_count != 1)
		goto out;
	if (ip->i_number == ROOTINO)
		goto out;
	smp = NULL;
	for(mp = &mount[0]; mp < (struct mount *)v.ve_mount; mp++) {
		if(mp->m_flags != MFREE) {
			if (brdev(dev) == brdev(mp->m_dev))
				goto out;
		} else
		if(smp == NULL)
			smp = mp;
	}
	mp = smp;
	if(mp == NULL)
		goto out;
	mp->m_flags = MINTER;
	mp->m_dev = brdev(dev);
	(*bdevsw[(short)bmajor(dev)].d_open)(minor(dev),
		uap->ronly ?  (FREAD | FKERNEL) : (FREAD | FWRITE | FKERNEL));
	if(up->u_error)
		goto out1;
	mp->m_bufp = geteblk();
	fp = mp->m_bufp->b_un.b_filsys;
	up->u_offset = SUPERBOFF;
	up->u_count = sizeof(struct filsys);
	up->u_base = (caddr_t)fp;
	up->u_segflg = 1;
	readi(bip);
	if (up->u_error) {
		brelse(mp->m_bufp);
		goto out1;
	}
	mp->m_inodp = ip;
	mp->m_flags = MINUSE;
	if (fp->s_magic != FsMAGIC)
		fp->s_type = Fs1b;	/* assume old file system */
	if (fp->s_type == Fs2b)
		mp->m_dev |= Fs2BLK;
#if FsTYPE == 4
	if (fp->s_type == Fs4b)
		mp->m_dev |= Fs4BLK;
#endif
	if (brdev(pipedev) == brdev(mp->m_dev))
		pipedev = mp->m_dev;
	fp->s_ilock = 0;
	fp->s_flock = 0;
	fp->s_ninode = 0;
	fp->s_inode[0] = 0;
	fp->s_ronly = uap->ronly & 1;
	if (mp->m_mount = iget(mp->m_dev, ROOTINO))
		prele(mp->m_mount);
	else {
		brelse(mp->m_bufp);
		goto out1;
	}
	ip->i_flag |= IMOUNT;
	iput(bip);
	prele(ip);
	return;

out1:
	mp->m_flags = MFREE;
out:
	if (bip != NULL)
		iput(bip);
	if (up->u_error == 0)
		up->u_error = EBUSY;
	iput(ip);
}

/*
 * the umount system call.
 */
sumount()
{
	register dev_t dev;
	register struct inode *ip;
	register struct mount *mp;
	register struct a {
		char	*fspec;
	};

	if(!suser())
		return;
	dev = getmdev();
	if(u.u_error)
		return;
	for(mp = &mount[0]; mp < (struct mount *)v.ve_mount; mp++)
		if(mp->m_flags == MINUSE && brdev(dev) == brdev(mp->m_dev))
			goto found;
	u.u_error = EINVAL;
	return;

found:
	dev = mp->m_dev;
	(void) xumount(dev);	/* remove unused sticky files from text table */
	update();
	if (mp->m_mount) {
		plock(mp->m_mount);
		iput(mp->m_mount);
		mp->m_mount = NULL;
	}
	for(ip = &inode[0]; ip < (struct inode *)v.ve_inode; ip++)
		if(ip->i_number != 0 && dev == ip->i_dev) {
			u.u_error = EBUSY;
			return;
		}
	(*bdevsw[(short)bmajor(dev)].d_close)(minor(dev), FKERNEL);
	binval(dev);
	ip = mp->m_inodp;
	ip->i_flag &= ~IMOUNT;
	plock(ip);
	iput(ip);
	brelse(mp->m_bufp);
	mp->m_bufp = NULL;
	mp->m_flags = MFREE;
}

/*
 * Common code for mount and umount.
 * Check that the user's argument is a reasonable
 * thing on which to mount, and return the device number if so.
 */
dev_t
getmdev()
{
	dev_t dev;
	register struct inode *ip;

	ip = namei(uchar, 0);
	if(ip == NULL)
		return(NODEV);
	if((ip->i_mode&IFMT) != IFBLK)
		u.u_error = ENOTBLK;
	dev = (dev_t)ip->i_rdev;
	if(bmajor(dev) >= bdevcnt)
		u.u_error = ENXIO;
	iput(ip);
	return(dev);
}
