#include "../h/local.h"

#ifdef  SCCS_ID
static char SCCS_ID [] = "@(#)sys3.c    	3.2	 01:53:36 - 81/12/10 ";
#endif  SCCS_ID

#include "../h/param.h"
#include "../h/systm.h"
#include "../h/mount.h"
#include "../h/ino.h"
#include "../h/reg.h"
#include "../h/buf.h"
#include "../h/filsys.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/inode.h"
#include "../h/file.h"
#include "../h/conf.h"
#include "../h/stat.h"

/*
 * 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;
	stat1(fp->f_inode, uap->sb, fp->f_flag&FPIPE? fp->f_un.f_offset: 0);
}

/*
 * 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, (off_t)0);
	iput(ip);
}

/*
 * The basic routine for fstat and stat:
 * get the inode and pass appropriate parts back.
 */
stat1(ip, ub, pipeadj)
register struct inode *ip;
struct stat *ub;
off_t pipeadj;
{
	register struct dinode *dp;
	register struct buf *bp;
#ifdef SVC6
	if (u.u_svc6) {
		struct v6stat ds;
		register i;

		iupdat(ip, &time, &time);
		ds.st6_dev = ip->i_dev;
		ds.st6_ino = ip->i_number;
		ds.st6_mode = ip->i_mode & ~(ILARG|DIALLOC|DILARG);
		if (ip->i_mode != 0)
			ds.st6_mode |= DIALLOC;
		if (ip->i_mode&ILARG)
			ds.st6_mode |= DILARG;
		ds.st6_nlink = ip->i_nlink;
		ds.st6_uid = ip->i_uid;
		ds.st6_gid = ip->i_gid;
		ds.st6_siz0 = 0;
		ds.st6_siz1 = ip->i_size - pipeadj;
		for (i = 0; i < 8; i++)
			ds.st6_addr[i] = ip->i_un.i_addr[i];
		bp = bread(ip->i_dev, itod(ip->i_number));
		dp = bp->b_un.b_dino;
		dp += itoo(ip->i_number);
		ds.st6_atm0 = ds.st6_mtm0 = 0;
		ds.st6_atime = dp->di_atime;
		ds.st6_mtime = dp->di_mtime;
		brelse(bp);
		if (copyout((caddr_t)&ds, (caddr_t)ub, sizeof(ds)) < 0)
			u.u_error = EFAULT;
	} else {
#endif  SVC6
	struct stat ds;

#ifdef  UCB_NODUPS
	iupdat(ip, &time, &time, 0);
#else   UCB_NODUPS
	iupdat(ip, &time, &time);
#endif  UCB_NODUPS
	/*
	 * first copy from inode table
	 */
	ds.st_dev = ip->i_dev;
	ds.st_ino = ip->i_number;
	ds.st_mode = ip->i_mode;
	ds.st_nlink = ip->i_nlink;
	ds.st_uid = ip->i_uid;
	ds.st_gid = ip->i_gid;
	ds.st_rdev = (dev_t)ip->i_un.i_rdev;
	ds.st_size = ip->i_size - pipeadj;
	/*
	 * next the dates in the disk
	 */
	bp = bread(ip->i_dev, itod(ip->i_number));
	dp = bp->b_un.b_dino;
	dp += itoo(ip->i_number);
	ds.st_atime = dp->di_atime;
	ds.st_mtime = dp->di_mtime;
#ifdef  V6
	ds.st_ctime = dp->di_mtime;
#else   V6
	ds.st_ctime = dp->di_ctime;
#endif  V6
	brelse(bp);
	if (copyout((caddr_t)&ds, (caddr_t)ub, sizeof(ds)) < 0)
		u.u_error = EFAULT;
#ifdef  SVC6
	}
#endif  SVC6
}

/*
 * the dup system call.
 */
dup()
{
	register struct file *fp;
	register struct a {
		int	fdes;
		int	fdes2;
	} *uap;
	register i, m;

	uap = (struct a *)u.u_ap;
	m = uap->fdes & ~077;
	uap->fdes &= 077;
	fp = getf(uap->fdes);
	if(fp == NULL)
		return;
	if ((m&0100) == 0) {
		if ((i = ufalloc()) < 0)
			return;
	} else {
		i = uap->fdes2;
		if (i<0 || i>=NOFILE) {
			u.u_error = EBADF;
			return;
		}
		u.u_r.r_val1 = i;
	}
	if (i!=uap->fdes) {
		if (u.u_ofile[i]!=NULL)
			closef(u.u_ofile[i]);
		u.u_ofile[i] = fp;
		fp->f_count++;
	}
}

/*
 * the mount system call.
 */
smount()
{
	dev_t dev;
	register struct inode *ip;
	register struct mount *mp;
	struct mount *smp;
	register struct filsys *fp;
	struct buf *bp;
	register struct a {
		char	*fspec;
		char	*freg;
		int	ronly;
	} *uap;

	uap = (struct a *)u.u_ap;
	dev = getmdev();
#ifdef  CGL_LOCAL       /* only suser can mount */
	if(u.u_error || !suser())
#else   CGL_LOCAL
	if(u.u_error)
#endif  CGL_LOCAL
		return;
	u.u_dirp = (caddr_t)uap->freg;
	ip = namei(uchar, 0);
	if(ip == NULL)
		return;
	if(ip->i_count!=1 || (ip->i_mode&(IFBLK&IFCHR))!=0)
		goto out;
	smp = NULL;
	for(mp = &mount[0]; mp < mountNMOUNT; mp++) {
		if(mp->m_bufp != NULL) {
			if(dev == mp->m_dev)
				goto out;
		} else
		if(smp == NULL)
			smp = mp;
	}
	mp = smp;
	if(mp == NULL)
		goto out;
	(*bdevsw[major(dev)].d_open)(dev, !uap->ronly);
	if(u.u_error)
		goto out;
	bp = bread(dev, SUPERB);
	if(u.u_error) {
		brelse(bp);
		goto out1;
	}
	mp->m_inodp = ip;
	mp->m_dev = dev;
#ifdef  UCB_MOUNT
	if (((mp-mount)%NSBPFSB) == 0 && mp->m_caddr == NULL) {
		mp->m_bufp = geteblk();
		mount1(mp);
	} else
		mp->m_bufp = (struct buf *)1;
#else   UCB_MOUNT
	mp->m_bufp = geteblk();
#endif  UCB_MOUNT
#ifdef  UCB_MOUNT
	bcopy((caddr_t)bp->b_un.b_addr, mp->m_caddr, SBSIZE);
	fp = mp->m_caddr;
#else   UCB_MOUNT
	bcopy((caddr_t)bp->b_un.b_addr, mp->m_bufp->b_un.b_addr, BSIZE);
	fp = mp->m_bufp->b_un.b_filsys;
#endif  UCB_MOUNT
	fp->s_ilock = 0;
	fp->s_flock = 0;
	fp->s_ronly = uap->ronly & 1;
#ifdef  CGL_ISRCH
	fp->s_lasti = 1;
	fp->s_nbehind = 0;
#endif  CGL_ISRCH
#ifdef  UCB_FSNAM
	{ char *cp;
	  u.u_dirp = (caddr_t)uap->freg;
	  for (cp = fp->s_fsmnt; cp < &fp->s_fsmnt[sizeof(fp->s_fsmnt)-1]; )
		if ((*cp++ = uchar()) == 0)
			u.u_dirp--;     /* get 0 again */
	  *cp = 0;
	}
#endif  UCB_FSNAM
	brelse(bp);
	ip->i_flag |= IMOUNT;
	prele(ip);
	return;

out:
	u.u_error = EBUSY;
out1:
	iput(ip);
}

/*
 * the umount system call.
 */
sumount()
{
	dev_t dev;
	register struct inode *ip;
	register struct mount *mp;
	struct buf *bp;
	register struct a {
		char	*fspec;
	};

	dev = getmdev();
#ifdef  CGL_LOCAL       /* only suser can umount */
	if(u.u_error || !suser())
#else   CGL_LOCAL
	if(u.u_error)
#endif  CGL_LOCAL
		return;
	xumount(dev);	/* remove unused sticky files from text table */
	update();
	for(mp = &mount[0]; mp < mountNMOUNT; mp++)
		if(mp->m_bufp != NULL && dev == mp->m_dev)
			goto found;
	u.u_error = EINVAL;
	return;

found:
	for(ip = &inode[0]; ip < inodeNINODE; ip++)
		if(ip->i_number != 0 && dev == ip->i_dev) {
			u.u_error = EBUSY;
			return;
		}
	(*bdevsw[major(dev)].d_close)(dev, 0);
#ifdef  UCB_UMOUNT
	{
		struct buf *dp;         /* Klein & McConnell fix */
		dp = bdevsw[major(dev)].d_tab;
		for (bp=dp->b_forw; bp!=dp; bp=bp->b_forw) {
			spl6();
			if (bp->b_dev == dev) {
#ifdef  UCB_BHASH
				bunhash(bp);
#endif  UCB_BHASH
				bp->b_dev = NODEV;
			}
			spl0();
		}
	}
#endif UCB_UMOUNT
	ip = mp->m_inodp;
	ip->i_flag &= ~IMOUNT;
	plock(ip);
	iput(ip);
	bp = mp->m_bufp;
	mp->m_bufp = NULL;
#ifndef UCB_MOUNT
	brelse(bp);
#endif  UCB_MOUNT
}

/*
 * 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_un.i_rdev;
	if(major(dev) >= nblkdev)
		u.u_error = ENXIO;
	iput(ip);
	return(dev);
}

#ifdef  UCB_MOUNT
/*
 * distribute a buffer's address through
 * NSBPFSB succeeding mount table entries
 */
mount1(mp)
register struct mount *mp;
{
	register a, i;

	a = mp->m_bufp->b_un.b_addr;
	for (i=0; i<NSBPFSB; i++)
		(mp++)->m_caddr = a + sizeof(struct filsys) * i;
}
#endif  UCB_MOUNT
