/*
 * 5799-WZQ (C) COPYRIGHT IBM COPORATION 1986
 * LICENSED MATERIALS - PROPERTY OF IBM
 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
 */
/* $Header:kern_descrip.c 12.0$ */
/* $ACIS:kern_descrip.c 12.0$ */
/* $Source: /ibm/acis/usr/sys/sys/RCS/kern_descrip.c,v $ */

#if !defined(lint) && !defined(NO_RCS_HDRS)
static char *rcsid = "$Header:kern_descrip.c 12.0$";
#endif

/*
 * Copyright (c) 1982, 1986 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 *
 *	@(#)kern_descrip.c	7.1 (Berkeley) 6/5/86
 */

#include "param.h"
#include "systm.h"
#include "dir.h"
#include "user.h"
#include "kernel.h"
#ifndef VFS
#include "inode.h"
#include "proc.h"
#include "file.h"
#endif !VFS
#include "socket.h"
#include "socketvar.h"
#ifndef VFS
#include "mount.h"
#else !VFS
#include "../h/vfs.h"
#include "vnode.h"
#include "proc.h"
#include "file.h"
#include "fcntl.h"
#endif !VFS
#include "stat.h"

#include "ioctl.h"
#ifdef VICE
#include "../h/remote.h"
#endif

int uf_exclose = UF_EXCLOSE;

/*
 * Descriptor management.
 */

/*
 * TODO:
 *	eliminate u.u_error side effects
 */

/*
 * System calls on descriptors.
 */
getdtablesize()
{

	u.u_r.r_val1 = NOFILE;
}

getdopt()
{

}

setdopt()
{

}

dup()
{
	register struct a {
		int	i;
	} *uap = (struct a *) u.u_ap;
	struct file *fp;
	int j;

	if (uap->i &~ 077) { uap->i &= 077; dup2(); return; }	/* XXX */

	GETF(fp, uap->i);
	j = ufalloc(0);
	if (j < 0)
		return;
	dupit(j, fp, u.u_pofile[uap->i] &~ uf_exclose);
}

dup2()
{
	register struct a {
		int	i, j;
	} *uap = (struct a *) u.u_ap;
	register struct file *fp;

	GETF(fp, uap->i);
	if (uap->j < 0 || uap->j >= NOFILE) {
		u.u_error = EBADF;
		return;
	}
	u.u_r.r_val1 = uap->j;
	if (uap->i == uap->j)
		return;
	if (u.u_ofile[uap->j]) {
#ifdef VFS
		/* Release all System-V style record locks, if any */
		(void) vno_lockrelease(u.u_ofile[uap->j]);	/* errors? */
#endif VFS
		if (u.u_pofile[uap->j] & UF_MAPPED)
			munmapfd(uap->j);
		closef(u.u_ofile[uap->j]);
#ifndef VFS
		if (u.u_error)
			return;
#else !VFS
		/*
		 * Even if an error occurred when calling the close routine
		 * for the vnode or the device, the file table entry has
		 * had its reference count decremented anyway.  As such,
		 * the descriptor is closed, so there's not much point
		 * in worrying about errors; we might as well pretend
		 * the "close" succeeded.
		 */
		u.u_error = 0;
#endif !VFS
	}
	dupit(uap->j, fp, u.u_pofile[uap->i] &~ uf_exclose);
}

dupit(fd, fp, flags)
	int fd;
	register struct file *fp;
	register int flags;
{

	u.u_ofile[fd] = fp;
	u.u_pofile[fd] = flags;
	fp->f_count++;
	if (fd > u.u_lastfile)
		u.u_lastfile = fd;
}

/*
 * The file control system call.
 */
fcntl()
{
	register struct file *fp;
	register struct a {
		int	fdes;
		int	cmd;
		int	arg;
	} *uap;
	register i;
	register char *pop;
#ifdef VFS
	struct flock ld;
	register int oldwhence;
#endif

	uap = (struct a *)u.u_ap;
	GETF(fp, uap->fdes);
	pop = &u.u_pofile[uap->fdes];
	switch(uap->cmd) {
	case F_DUPFD:
		i = uap->arg;
		if (i < 0 || i >= NOFILE) {
			u.u_error = EINVAL;
			return;
		}
		if ((i = ufalloc(i)) < 0)
			return;
		dupit(i, fp, *pop &~ uf_exclose);
		break;

	case F_GETFD:
		u.u_r.r_val1 = *pop & 1;
		break;

	case F_SETFD:
		*pop = (*pop &~ 1) | (uap->arg & 1);
		break;

	case F_GETFL:
		u.u_r.r_val1 = fp->f_flag+FOPEN;
		break;

	case F_SETFL:
		fp->f_flag &= FCNTLCANT;
		fp->f_flag |= (uap->arg-FOPEN) &~ FCNTLCANT;
		u.u_error = fset(fp, FNDELAY, fp->f_flag & FNDELAY);
		if (u.u_error)
			break;
		u.u_error = fset(fp, FASYNC, fp->f_flag & FASYNC);
		if (u.u_error)
			(void) fset(fp, FNDELAY, 0);
		break;

	case F_GETOWN:
		u.u_error = fgetown(fp, &u.u_r.r_val1);
		break;

	case F_SETOWN:
		u.u_error = fsetown(fp, uap->arg);
		break;

#ifdef VFS
		/* System-V Record-locking (lockf() maps to fcntl()) */
	case F_GETLK:
	case F_SETLK:
	case F_SETLKW:
		/* First off, allow only vnodes here */
		if (fp->f_type != DTYPE_VNODE) {
			u.u_error = EBADF;
			return;
		}
		/* get flock structure from user-land */
		if (u.u_error = copyin((caddr_t) uap->arg, (caddr_t) &ld,
		    sizeof (ld))) {
			return;
		}

		/* *** NOTE ***
		 * The SVID does not say what to return on file access errors!
		 * Here, EBADF is returned, which is compatible with S5R3
		 * and is less confusing than EACCES
		 */
		/* check access permissions */
		if (uap->cmd != F_GETLK) {
			switch (ld.l_type) {
			case F_RDLCK:
				if (!(fp->f_flag & FREAD)) {
					u.u_error = EBADF;
					return;
				}
				break;

			case F_WRLCK:
				if (!(fp->f_flag & FWRITE)) {
					u.u_error = EBADF;
					return;
				}
				break;

			case F_UNLCK:
				break;

			default:
				u.u_error = EINVAL;
				return;
			}
		}

		/* convert offset to start of file */
		oldwhence = ld.l_whence;	/* save to renormalize later */
		if (u.u_error = rewhence(&ld, fp, 0))
			return;

		/* convert negative lengths to positive */
		if (ld.l_len < 0) {
			ld.l_start += ld.l_len;		/* adjust start point */
			ld.l_len = -(ld.l_len);		/* absolute value */
		}

		/* check for validity */
		if (ld.l_start < 0) {
			u.u_error = EINVAL;
			return;
		}

		if ((uap->cmd != F_GETLK) && (ld.l_type != F_UNLCK)) {
			/* If any locking is attempted, mark file locked
			 * to force unlock on close.
			 * Also, since the SVID specifies that the FIRST
			 * close releases all locks, mark process to
			 * reduce the search overhead in vno_lockrelease().
			 */
			*pop |= UF_FDLOCK;
			u.u_procp->p_flag |= SLKDONE;
		}

		/*
		 * Dispatch out to vnode layer to do the actual locking.
		 * Then, translate error codes for SVID compatibility
		 */
		switch (u.u_error = VOP_LOCKCTL((struct vnode *)fp->f_data,
		    &ld, uap->cmd, fp->f_cred)) {
		case 0:
			break;		/* continue, if successful */
		case EWOULDBLOCK:
			u.u_error = EACCES;	/* EAGAIN ??? */
			return;
		default:
			return;		/* some other error code */
		}

		/* if F_GETLK, return flock structure to user-land */
		if (uap->cmd == F_GETLK) {
			/* per SVID, change only 'l_type' field if unlocked */
			if (ld.l_type == F_UNLCK) {
				if (u.u_error = copyout((caddr_t) &ld.l_type,
				    (caddr_t)&((struct flock*)uap->arg)->l_type,
				    sizeof (ld.l_type))) {
					return;
				}
			} else {
				if (u.u_error = rewhence(&ld, fp, oldwhence))
					return;
				if (u.u_error = copyout((caddr_t) &ld,
				    (caddr_t) uap->arg, sizeof (ld))) {
					return;
				}
			}
		}
		break;
#endif VFS

	default:
		u.u_error = EINVAL;
	}
}

fset(fp, bit, value)
	struct file *fp;
	int bit, value;
{

	if (value)
		fp->f_flag |= bit;
	else
		fp->f_flag &= ~bit;
	return (fioctl(fp, (int)(bit == FNDELAY ? FIONBIO : FIOASYNC),
	    (caddr_t)&value));
}

fgetown(fp, valuep)
	struct file *fp;
	int *valuep;
{
	int error;

	switch (fp->f_type) {

	case DTYPE_SOCKET:
		*valuep = ((struct socket *)fp->f_data)->so_pgrp;
		return (0);

	default:
		error = fioctl(fp, (int)TIOCGPGRP, (caddr_t)valuep);
		*valuep = -*valuep;
		return (error);
	}
}

fsetown(fp, value)
	struct file *fp;
	int value;
{

	if (fp->f_type == DTYPE_SOCKET) {
		((struct socket *)fp->f_data)->so_pgrp = value;
		return (0);
	}
	if (value > 0) {
		struct proc *p = pfind(value);
		if (p == 0)
			return (ESRCH);
		value = p->p_pgrp;
	} else
		value = -value;
	return (fioctl(fp, (int)TIOCSPGRP, (caddr_t)&value));
}

fioctl(fp, cmd, value)
	struct file *fp;
	int cmd;
	caddr_t value;
{

	return ((*fp->f_ops->fo_ioctl)(fp, cmd, value));
}

close()
{
	struct a {
		int	i;
	} *uap = (struct a *)u.u_ap;
	register int i = uap->i;
	register struct file *fp;
	register u_char *pf;

	GETF(fp, i);

#ifdef VFS
	/* Release all System-V style record locks, if any */
	(void) vno_lockrelease(fp);	/* WHAT IF error returned? */
#endif VFS
	pf = (u_char *)&u.u_pofile[i];
	if (*pf & UF_MAPPED)
		munmapfd(i);
	u.u_ofile[i] = NULL;
	while (u.u_lastfile >= 0 && u.u_ofile[u.u_lastfile] == NULL)
		u.u_lastfile--;
	*pf = 0;
	closef(fp);
	/* WHAT IF u.u_error ? */
}

fstat()
{
	register struct file *fp;
	register struct a {
		int	fdes;
		struct	stat *sb;
	} *uap;
	struct stat ub;
#ifdef VICE
	extern int rmt_syscall();
	extern int rmt_fstat();
#endif

	uap = (struct a *)u.u_ap;
	GETF(fp, uap->fdes);
	switch (fp->f_type) {

#ifndef VFS
	case DTYPE_INODE:
		u.u_error = ino_stat((struct inode *)fp->f_data, &ub);
#else !VFS
	case DTYPE_VNODE:
		u.u_error = vno_stat((struct vnode *)fp->f_data, &ub);
#endif !VFS
		break;

	case DTYPE_SOCKET:
		u.u_error = soo_stat((struct socket *)fp->f_data, &ub);
		break;
#ifdef VICE
	case DTYPE_REMOTE:
		rmt_syscall(((struct inode *)fp->f_data)->i_rmt_dev,
				rmt_fstat, fp, &ub);
		break;
#endif

	default:
		panic("fstat");
		/*NOTREACHED*/
	}
	if (u.u_error == 0)
		u.u_error = copyout((caddr_t)&ub, (caddr_t)uap->sb,
		    sizeof (ub));
}

/*
 * Allocate a user file descriptor.
 */
ufalloc(i)
	register int i;
{

	for (; i < NOFILE; i++)
		if (u.u_ofile[i] == NULL) {
			u.u_r.r_val1 = i;
			u.u_pofile[i] = 0;
			if (i > u.u_lastfile)
				u.u_lastfile = i;
			return (i);
		}
	u.u_error = EMFILE;
	return (-1);
}

ufavail()
{
	register int i, avail = 0;

	for (i = 0; i < NOFILE; i++)
		if (u.u_ofile[i] == NULL)
			avail++;
	return (avail);
}

struct	file *lastf;
/*
 * Allocate a user file descriptor
 * and a file structure.
 * Initialize the descriptor
 * to point at the file structure.
 */
struct file *
falloc()
{
	register struct file *fp;
	register i;

	i = ufalloc(0);
	if (i < 0)
		return (NULL);
	if (lastf == 0)
		lastf = file;
	for (fp = lastf; fp < fileNFILE; fp++)
		if (fp->f_count == 0)
			goto slot;
	for (fp = file; fp < lastf; fp++)
		if (fp->f_count == 0)
			goto slot;
	tablefull("file");
	u.u_error = ENFILE;
	return (NULL);
slot:
	u.u_ofile[i] = fp;
	fp->f_count = 1;
	fp->f_data = 0;
	fp->f_offset = 0;
#ifdef VFS
	crhold(u.u_cred);
	fp->f_cred = u.u_cred;
#endif VFS
	lastf = fp + 1;
	return (fp);
}

#ifdef VICE
/* (falloc should really call this routine) */
/*
 * Allocate a file structure.
 */
struct file *
file_alloc()
{
	register struct file *fp;
	register i;

	if (lastf == 0)
		lastf = file;
	for (fp = lastf; fp < fileNFILE; fp++)
		if (fp->f_count == 0)
			goto slot;
	for (fp = file; fp < lastf; fp++)
		if (fp->f_count == 0)
			goto slot;
	tablefull("file");
	u.u_error = ENFILE;
	return (NULL);
slot:
	fp->f_count = 1;
	fp->f_data = 0;
	fp->f_offset = 0;
	lastf = fp + 1;
	return (fp);
}
#endif

/*
 * Convert a user supplied file descriptor into a pointer
 * to a file structure.  Only task is to check range of the descriptor.
 * Critical paths should use the GETF macro.
 */
struct file *
getf(f)
	register int f;
{
	register struct file *fp;

	if ((unsigned)f >= NOFILE || (fp = u.u_ofile[f]) == NULL) {
		u.u_error = EBADF;
		return (NULL);
	}
	return (fp);
}

/*
 * Internal form of close.
 * Decrement reference count on file structure.
 */
closef(fp)
	register struct file *fp;
{
#ifdef VICE
	struct inode *ip;
#endif

	if (fp == NULL)
		return;
#ifdef VICE
	if (fp->f_flag & FFILL) {
		ip = (struct inode *)(fp->f_data);
		ip->i_flag &= ~IFILLING;	/* time to go */
		if ((ip->i_flag) & IFILLWAIT) {
			wakeup(&ip->i_flag);
		}
	}
#endif
	if (fp->f_count > 1) {
		fp->f_count--;
		return;
	}
#ifdef VFS
	if (fp->f_count < 1)		/* check this; sxn */
		panic("closef: count < 1");
#endif VFS

	(*fp->f_ops->fo_close)(fp);
#ifdef VFS
	crfree(fp->f_cred);
#endif VFS
	fp->f_count = 0;
}

#ifdef VFS
/*
 * Normalize SystemV-style record locks
 */
rewhence(ld, fp, newwhence)
	struct flock *ld;
	struct file *fp;
	int newwhence;
{
	struct vattr va;
	register int error;

	/* if reference to end-of-file, must get current attributes */
	if ((ld->l_whence == 2) || (newwhence == 2)) {
		if (error = VOP_GETATTR((struct vnode *)fp->f_data, &va,
		    u.u_cred))
			return(error);
	}

	/* normalize to start of file */
	switch (ld->l_whence) {
	case 0:
		break;
	case 1:
		ld->l_start += fp->f_offset;
		break;
	case 2:
		ld->l_start += va.va_size;
		break;
	default:
		return(EINVAL);
	}

	/* renormalize to given start point */
	switch (ld->l_whence = newwhence) {
	case 1:
		ld->l_start -= fp->f_offset;
		break;
	case 2:
		ld->l_start -= va.va_size;
		break;
	}
	return(0);
}
#endif VFS

/*
 * Apply an advisory lock on a file descriptor.
 */
flock()
{
	register struct a {
		int	fd;
		int	how;
	} *uap = (struct a *)u.u_ap;
	register struct file *fp;

	GETF(fp, uap->fd);
#ifdef VICE
	if (fp->f_type ==  DTYPE_REMOTE) {
		extern rmt_simple_file();
	    	rmt_syscall(((struct inode *)fp->f_data)->i_rmt_dev,
				rmt_simple_file, RT_flock, fp, uap->how);
		return;
	} else
#endif VICE
#ifndef VFS
	if (fp->f_type != DTYPE_INODE) {
#else !VFS
	if (fp->f_type != DTYPE_VNODE) {
#endif !VFS
		u.u_error = EOPNOTSUPP;
		return;
	}
	if (uap->how & LOCK_UN) {
#ifndef VFS
		ino_unlock(fp, FSHLOCK|FEXLOCK);
#else !VFS
		vno_bsd_unlock(fp, FSHLOCK|FEXLOCK);
#endif !VFS
		return;
	}
#ifndef VFS
	if ((uap->how & (LOCK_SH | LOCK_EX)) == 0) {
#else !VFS
	/* check for valid lock type */
	if ((uap->how & (LOCK_SH | LOCK_EX)) == 0) {
#endif !VFS
		u.u_error = EINVAL;
		return;
	}
	if (uap->how & LOCK_EX)
		uap->how &= ~LOCK_SH;
	/* avoid work... */
	if ((fp->f_flag & FEXLOCK) && (uap->how & LOCK_EX) ||
	    (fp->f_flag & FSHLOCK) && (uap->how & LOCK_SH))
		return;
#ifndef VFS
	u.u_error = ino_lock(fp, uap->how);
#else !VFS
	u.u_error = vno_bsd_lock(fp, uap->how);
#endif !VFS
}
