/* $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 */

#ident	"$Header: vfs_syscalls.c 1.48 90/09/14 $"

/*
 * vfs_syscalls.c
 *	Various virtual file-system system calls.
 */

/* $Log:	vfs_syscalls.c,v $
 */

#include "sys/signal.h"
#include "sys/types.h"
#include "sys/devsw.h"
#include "sys/param.h"
#include "sys/debug.h"
#include "sys/mutex.h"
#include "sys/user.h"
#include "sys/timer.h"
#include "sys/proc.h"
#include "sys/session.h"
#include "sys/errno.h"
#include "sys/file.h"
#include "sys/stat.h"
#include "sys/uio.h"
#include "sys/ioctl.h"
#include "sys/vfs.h"
#include "sys/pathname.h"
#include "sys/vnode.h"
#include "sys/ufsinode.h"
#include "sys/vmmeter.h"
#include "sys/vmparam.h"
#include "sys/var.h"

#include "sys/pte.h"
#include "sys/intctl.h"
#include "sys/plocal.h"

#ifdef DEBUG
int	fvhang_up=0;
#endif

/*
 * System call routines for operations on files other
 * than read, write and ioctl.  These calls manipulate
 * the per-process file table which references the
 * networkable version of normal UNIX inodes, called vnodes.
 *
 * Many operations take a pathname, which is read
 * into a kernel buffer by pn_get (see vfs_pathname.c).
 * After preparing arguments for an operation, a simple
 * operation proceeds:
 *
 *	error = lookupname(pname, seg, followlink, &dvp, &vp)
 *
 * where pname is the pathname operated on, seg is the segment that the
 * pathname is in (UIOSEG_USER or UIOSEG_KERNEL), followlink specifies
 * whether to follow symbolic links, dvp is a pointer to the vnode that
 * represents the parent directory of vp, the pointer to the vnode
 * referenced by the pathname. The lookupname routine fetches the
 * pathname string into an internal buffer using pn_get (vfs_pathname.c),
 * and iteratively running down each component of the path until the
 * the final vnode and/or it's parent are found. If either of the addresses
 * for dvp or vp are NULL, then it assumes that the caller is not interested
 * in that vnode.  Once the vnode or its parent is found, then a vnode
 * operation (e.g. VOP_OPEN) may be applied to it.
 *
 * One major change from SUN VFS for Dynix (multiprocessor) is that vnodes
 * are held locked at this level.  Eg, lookuppn() returns a locked vnode.
 * This is *much* simpler and more efficient in a multiprocessor.
 */

/*
 * Change current working directory (".").
 */
chdir()
{
	register struct a {
		char *dirnamep;
	} *uap;
	struct vnode *vp;

	uap = (struct a *)u.u_ap;
	u.u_error = chdirec(uap->dirnamep, &vp);
	if (u.u_error == 0) {
		VN_RELE(u.u_cdir);
		u.u_cdir = vp;
	}
}

/*
 * Change notion of root ("/") directory.
 */
chroot()
{
	register struct a {
		char *dirnamep;
	} *uap;
	struct vnode *vp;

	uap = (struct a *)u.u_ap;
	if (!suser())
		return;

	u.u_error = chdirec(uap->dirnamep, &vp);
	if (u.u_error == 0) {
		if (u.u_rdir != (struct vnode *)0)
			VN_RELE(u.u_rdir);
		u.u_rdir = vp;
	}
}

/*
 * Common code for chdir and chroot.
 * Translate the pathname and insist that it
 * is a directory to which we have execute access.
 * If it is replace u.u_[cr]dir with new vnode.
 */
chdirec(dirnamep, vpp)
	char *dirnamep;
	struct vnode **vpp;
{
	struct vnode *vp;		/* new directory vnode */
	register int error;

        error =
	    lookupname(dirnamep, UIOSEG_USER, FOLLOW_LINK,
			(struct vnode **)0, &vp);
	if (error)
		return (error);
	if (vp->v_type != VDIR) {
		error = ENOTDIR;
	} else {
		error = VOP_ACCESS(vp, VEXEC, u.u_cred);
	}
	if (error) {
		VN_PUT(vp);
	} else {
		VN_UNLOCKNODE(vp);
		*vpp = vp;
	}
	return (error);
}

/*
 * Open system call.
 */
open()
{
	register struct a {
		char *fnamep;
		int fmode;
		int cmode;
	} *uap;

	uap = (struct a *)u.u_ap;
	copen(uap->fnamep, (int)(uap->fmode - FOPEN), uap->cmode);
}

/*
 * Creat system call.
 */
creat()
{
	register struct a {
		char *fnamep;
		int cmode;
	} *uap;

	uap = (struct a *)u.u_ap;
	copen(uap->fnamep, FWRITE|FCREAT|FTRUNC, uap->cmode);
}

/*
 * copen()
 *	Common code for open, creat.
 */

copen(pnamep, filemode, createmode)
	char	*pnamep;
	int	filemode;
	int	createmode;
{
	register struct file *fp;
	struct	vnode	*vp;
	int	i;

	/*
	 * add this test to be system V and POSIX compliant
	 */
	if ((filemode&(FREAD|FWRITE)) == 0)  {
		u.u_error = EINVAL;
		return;
	}

	/*
	 * If the line has been vhangup()ed then return an error as if the
	 * a modem disconnect had occured.
	 */
	if (filemode&FHUPED) {
		u.u_error = EIO;
		return;
	}

	/*
	 * Allocate a user file descriptor and file table entry.
	 *	allocate here, BEFORE calling vn_open because
	 *	vn_open may have side effect of creating a file.
	 *	This is hard to undo if later we run out of file
	 *	table entries.
	 */

	fp = falloc();
	if (fp == NULL) {
		return;
	}
	i = u.u_r.r_val1;	/* Save in case need to unwind */
	u.u_openfp = fp;	/* for benefit of cloneopen */

	/*
	 * Open the vnode.
	 * We pass &filemode to allow vn_open to change these flags.
	 *
	 *	System V doesnn't allow sticky text on file create
	 */

	u.u_error = vn_open(pnamep, UIOSEG_USER, &filemode,
			((createmode & 06777) & ~u.u_cmask), &vp);
	if (u.u_error) {
		ofile_install(u.u_ofile_tab, i, (struct file *)NULL);
		ffree(fp);
		return;
	}

	/*
	 * FMASK includes open flags:
	 *	FREAD, FWRITE, FNDELAY, FAPPEND, FSYNC, and FRCACH.
	 *
	 * we aren't effected by filemode changes in vn_open
	 * (which may turn off FCREAT, FTRUNC, and FEXC)L.
	 */ 

	fp->f_flag = filemode & FMASK;
	fp->f_vnode = vp;

	/*
	 * Truncate if required.
	 */

	if ((filemode & FTRUNC) && (vp->v_type != VFIFO) &&
				   (vp->v_type != VCHR) &&
				   (vp->v_type != VBLK) &&
				   (vp->v_type != VDIR)) {
		struct vattr vattr;

		filemode &= ~FTRUNC;
		vattr_null(&vattr);
		vattr.va_size = 0;
		u.u_error = VOP_SETATTR(vp, &vattr, u.u_cred);
	}

	VN_UNLOCKNODE(vp);

	/*
	 * VOP_OPEN(), which calls routines for special files,
	 * must be done after node is unlocked to prevent deadlock
	 * if we hang in a driver routine.
	 */

	if (u.u_error == 0)
		u.u_error = VOP_OPEN(&vp, filemode, u.u_cred);
	/*
	 * If the file we opened was a STREAMS clone device then the vnode
	 * that we gave VOP_OPEN (vp) is no longer valid.  Do NOT reference 
	 * vp if VOP_OPEN succeeded.
	 */
	if (u.u_error == 0) {
		ofile_install(u.u_ofile_tab, i, fp);
		return;
	}

	/*
	 * Unwind if anything failed above.
	 */

	ofile_install(u.u_ofile_tab, i, (struct file *) NULL);
	ffree(fp);
	VN_RELE(vp);
	return;
}

/*
 * Create a special (or regular) file.
 */
mknod()
{
	register struct a {
		char		*pnamep;
		int		fmode;
		int		dev;
	} *uap;

	struct vnode *vp;
	struct vattr vattr;
	enum vtype vtype;

	uap = (struct a *)u.u_ap;
	vtype = IFTOVT(uap->fmode);

	/*
	 * Must be super user, unless creating a FIFO
	 */
	 
	if (vtype != VFIFO && !suser())
		return;

	/*
	 * Allow super user to mknod a regular file
	 */

	if (vtype == VNON)
		vtype = VREG;

	/*
	 * Setup desired attributes and vn_create the file.
	 */
	vattr_null(&vattr);
	vattr.va_type = vtype;
	vattr.va_mode = (uap->fmode & 07777) & ~u.u_cmask;
	if (vtype == VDIR){
		u.u_error = vn_create(uap->pnamep, UIOSEG_USER, &vattr, EXCL, VWRITE, &vp);
		if (u.u_error == 0)
			VN_PUT(vp);
		return;
	}

	switch (vattr.va_type) {

	case VBAD:
	case VCHR:
	case VBLK:
		vattr.va_rdev = uap->dev;
		break;
	default:
		break;
	}

	u.u_error = vn_create(uap->pnamep, UIOSEG_USER, &vattr, EXCL, 0, &vp);
	if (u.u_error == 0)
		VN_PUT(vp);
}

/*
 * Make a directory.
 */
mkdir()
{
	struct a {
		char	*dirnamep;
		int	dmode;
	} *uap;

	struct vnode *vp;
	struct vattr vattr;

	uap = (struct a *)u.u_ap;
	vattr_null(&vattr);
	vattr.va_type = VDIR;
	vattr.va_mode = (uap->dmode & 0777) & ~u.u_cmask;

	u.u_error = vn_create(uap->dirnamep, UIOSEG_USER, &vattr, EXCL, 0, &vp);
	if (u.u_error == 0)
		VN_PUT(vp);
}

/*
 * make a hard link
 */
link()
{
	register struct a {
		char	*from;
		char	*to;
	} *uap;

	uap = (struct a *)u.u_ap;
	u.u_error = vn_link(uap->from, uap->to, UIOSEG_USER);
}

/*
 * rename or move an existing file
 */
rename()
{
	register struct a {
		char	*from;
		char	*to;
	} *uap;

	uap = (struct a *)u.u_ap;
	u.u_error = vn_rename(uap->from, uap->to, UIOSEG_USER);
}

/*
 * Create a symbolic link.
 * Similar to link or rename except target
 * name is passed as string argument, not
 * converted to vnode reference.
 */
symlink()
{
	register struct a {
		char	*target;
		char	*linkname;
	} *uap;

	struct vnode *dvp;
	struct vattr vattr;
	struct pathname tpn;
	struct pathname lpn;

	uap = (struct a *)u.u_ap;
	u.u_error = pn_get(uap->linkname, UIOSEG_USER, &lpn);
	if (u.u_error)
		return;
	u.u_error = lookuppn(&lpn, NO_FOLLOW, &dvp, (struct vnode **)0);
	if (u.u_error) {
		pn_free(&lpn);
		return;
	}
	if (dvp->v_vfsp->vfs_flag & VFS_RDONLY) {
		u.u_error = EROFS;
		goto out;
	}
	u.u_error = pn_get(uap->target, UIOSEG_USER, &tpn);
	vattr_null(&vattr);
	vattr.va_mode = 0777 & ~u.u_cmask;
	if (u.u_error == 0) {
		u.u_error =
		   VOP_SYMLINK(dvp, lpn.pn_path, &vattr, tpn.pn_path, u.u_cred);
		pn_free(&tpn);
	}
out:
	pn_free(&lpn);
	VN_RELE(dvp);
}

/*
 * Unlink (i.e. delete) a file.
 */
unlink()
{
	struct a {
		char	*pnamep;
	} *uap;

	uap = (struct a *)u.u_ap;
	u.u_error = vn_remove(uap->pnamep, UIOSEG_USER, FILE);
}

/*
 * Remove a directory.
 */
rmdir()
{
	struct a {
		char	*dnamep;
	} *uap;

	uap = (struct a *)u.u_ap;
	u.u_error = vn_remove(uap->dnamep, UIOSEG_USER, DIRECTORY);
}


/*
 * pipe() - create a pipe on the rootdev.
 *
 *	later, change to use the pipedev.
 */

pipe()
{
	register struct file *rf;
	register struct file *wf;
	struct vnode *vp;
	int r;

	/*
	 * allocate two file table entries 
	 *	First one is the "read" entry.
	 *	The second is the "write" entry.`
	 *
	 * Allocate file table entires first because
	 * it's easier to deallocate file table entries
	 * on failure of the VOP_PIPE than to
	 * free the vnode on failure of an falloc.
	 * This is because the vnode created by
	 * VOP_PIPE has TWO references.
	 *
	 * Is there a problem with dangling references here?
	 *	(see comment in old socket-pipe code).
	 * 
	 */
	rf = falloc();
	if (rf == (struct file *)NULL)
		return;
	r = u.u_r.r_val1;
	rf->f_flag = FREAD;
	wf = falloc();
	if (wf == (struct file *)NULL) {
		/*
		 * Allocation of the write file table entry failed.
		 * Must free the read file table entry.
		 */
		ofile_install(u.u_ofile_tab, r, (struct file *)NULL);
		ffree(rf);
		return;
	}
	wf->f_flag = FWRITE;
	u.u_r.r_val2 = u.u_r.r_val1;
	u.u_r.r_val1 = r;
	/*
	 * Create the vnode for the pipe.
	 *	call a special specnode operation for it.
	 */
	u.u_error = pipesp(&vp);
/*
	u.u_error = VOP_PIPE(rootdir, &vp);
*/
	if (u.u_error) {
		/*
		 * return BOTH file table entries.
		 */
		ofile_install(u.u_ofile_tab, u.u_r.r_val1, (struct file *)NULL);
		ofile_install(u.u_ofile_tab, u.u_r.r_val2, (struct file *)NULL);
		ffree(rf);
		ffree(wf);
		return;
	}
	/*
	 * pipesp returns a locked vnode with a reference
	 * count of two.  So, all that remains is to
	 * set up the file pointers to the vnode and unlock
	 * the vnode.
	 */
	ofile_install(u.u_ofile_tab, u.u_r.r_val1, rf);
	ofile_install(u.u_ofile_tab, u.u_r.r_val2, wf);
	rf->f_vnode = vp;
	wf->f_vnode = vp;
	VN_UNLOCKNODE(vp);
	return;

}

/*
 * get directory entries in a file system independent format
 *
 *	Note that auio.count, and changes in the values of auio.offset
 *	and auio.resid reflect changes in two TOTALLY different things now:
 *		offset is change in offset in the directory we're
 *		accessing.
 *
 *		resid and count reflect bytes transferred to user
 *		space AFTER translating directory data into the
 *		new format.
 */
getdents()
{
	register struct a {
		int	fd;
		char	*buf;
		int 	count;
	} *uap = (struct a *)u.u_ap;

			struct file *fp;
	register	struct vnode *vp;
			struct uio auio;
			struct iovec aiov;

	u.u_error = getvnodefp(uap->fd, &fp);
	if (u.u_error)
		return;
	vp = fp->f_vnode;
	if (vp->v_type != VDIR) {
		u.u_error = ENOTDIR;
		return;
	}
	aiov.iov_base = uap->buf;
	aiov.iov_len = uap->count;
	auio.uio_iov = &aiov;
	auio.uio_iovcnt = 1;
	auio.uio_segflg = UIOSEG_USER;
	auio.uio_resid = uap->count;
	VN_LOCKNODE(vp);
	auio.uio_offset = fp->f_offset;
	u.u_error = VOP_GETDENTS(vp, &auio, fp->f_cred);
	if (u.u_error) {
		VN_UNLOCKNODE(vp);
		return;
	}
	fp->f_offset = auio.uio_offset;
	VN_UNLOCKNODE(vp);
	u.u_r.r_val1 = uap->count - auio.uio_resid;
}

/*
 * Tell whether the indicated file allows unsigned offsets.  This
 * function is currently only used in one place, but it's pretty
 * ugly in-line, so here it is instead.
 */
static int
is_unsoff(fp)
	struct file *fp;
{
	if (!fp->f_vnode)
		return(0);
	if (!fp->f_vnode->v_rdevp)
		return(0);
	if (!(fp->f_vnode->v_rdevp->d_flags & D_UNSOFF))
		return(0);
	return(1);
}

/*
 * Seek on file.  Only hard operation
 * is seek relative to end which must
 * apply to vnode for current file size.
 * 
 * Note: lseek(0, 0, L_XTND) costs much more than it did before.
 */
lseek()
{
	register struct a {
		int	fd;
		off_t	off;
		int	sbase;
	} *uap;
	struct file *fp;
	off_t	new_offset;

	uap = (struct a *)u.u_ap;
	if ((u.u_error = getvnodefp(uap->fd, &fp)) != 0)
		return;

	if (fp->f_vnode->v_type == VFIFO) {
		u.u_error = ESPIPE;
		return;
	}

	switch (uap->sbase) {

	case SEEK_CUR:
		new_offset = uap->off + fp->f_offset;
		break;

	case SEEK_END: {
		struct vattr vattr;

		VN_LOCKNODE(fp->f_vnode);
		u.u_error =
		    VOP_GETATTR(fp->f_vnode, &vattr, u.u_cred);
		VN_UNLOCKNODE(fp->f_vnode);
		if (u.u_error)
			return;
		new_offset = uap->off + vattr.va_size;
		break;
	}

	case SEEK_SET:
		new_offset = uap->off;
		break;

	default:
		u.u_error = EINVAL;
		psignal(u.u_procp, (int)SIGSYS);
		return;
	}

	if (new_offset < 0) {
		if (!is_unsoff(fp)) {
			u.u_error = EINVAL;
			return;
		}
	}

	u.u_r.r_off = fp->f_offset = new_offset;
}

/*
 * Determine accessibility of file, by
 * reading its attributes and then checking
 * against our protection policy.
 */
saccess()
{
	register struct a {
		char	*fname;
		int	fmode;
	} *uap;

	struct vnode *vp;
	register ushort mode;
	struct ucred *savcred = (struct ucred *)NULL;

	uap = (struct a *)u.u_ap;

	/*
	 * If necessary, use the real uid and gid to check access.
	 */
	if (u.u_uid != u.u_ruid || u.u_gid != u.u_rgid) {
		savcred = u.u_cred;
		u.u_cred = crdup(u.u_cred);
		u.u_uid = u.u_ruid;
		u.u_gid = u.u_rgid;
	}

	/*
	 * Lookup file
	 */
	u.u_error = lookupname(uap->fname, UIOSEG_USER, FOLLOW_LINK,
				(struct vnode **)0, &vp);
	if (u.u_error) {
		if (savcred) {
			crfree(u.u_cred);
			u.u_cred = savcred;
		}
		return;
	}

	mode = 0;
	/*
	 * fmode == 0 means only check for exist
	 */
	if (uap->fmode) {
		if (uap->fmode & R_OK)
			mode |= VREAD;
		if (uap->fmode & W_OK) {
			if (vp->v_vfsp->vfs_flag & VFS_RDONLY) {
				u.u_error = EROFS;
				goto out;
			}
			mode |= VWRITE;
		}
		if (uap->fmode & X_OK)
			mode |= VEXEC;
		u.u_error = VOP_ACCESS(vp, mode, u.u_cred);
	}

	/*
	 * release the vnode and restore the uid and gid.
	 */
out:
	VN_PUT(vp);
	if (savcred) {
		crfree(u.u_cred);
		u.u_cred = savcred;
	}
}

struct dummy_a {
	char *fname;
	struct stat *ub;
};

/*
 * Get attributes from file or file descriptor.
 * Argument says whether to follow links, and is
 * passed through in flags.
 */
stat()
{
	struct dummy_a *uap;

	uap = (struct dummy_a *)u.u_ap;
	u.u_error = stat1(uap, FOLLOW_LINK);
}

lstat()
{
	struct dummy_a *uap;

	uap = (struct dummy_a *)u.u_ap;
	u.u_error = stat1(uap, NO_FOLLOW);
}

stat1(uap, follow)
	register struct dummy_a *uap;
	enum symfollow follow;
{
	struct vnode *vp;
	struct stat sb;
	register int error;

	error =
	    lookupname(uap->fname, UIOSEG_USER, follow,
			(struct vnode **)0, &vp);
	if (error)
		return (error);
	error = vno_stat(vp, &sb);
	VN_PUT(vp);
	if (error)
		return (error);
	return (copyout((caddr_t)&sb, (caddr_t)uap->ub, sizeof(sb)));
}

/*
 * Read contents of symbolic link.
 */
readlink()
{
	register struct a {
		char	*name;
		char	*buf;
		int	count;
	} *uap;

	struct vnode *vp;
	struct iovec aiov;
	struct uio auio;

	uap = (struct a *)u.u_ap;
	u.u_error =
	    lookupname(uap->name, UIOSEG_USER, NO_FOLLOW,
			(struct vnode **)0, &vp);
	if (u.u_error)
		return;
	if (vp->v_type != VLNK) {
		u.u_error = EINVAL;
		goto out;
	}
	aiov.iov_base = uap->buf;
	aiov.iov_len = uap->count;
	auio.uio_iov = &aiov;
	auio.uio_iovcnt = 1;
	auio.uio_offset = 0;
	auio.uio_segflg = UIOSEG_USER;
	auio.uio_resid = uap->count;
	u.u_error = VOP_READLINK(vp, &auio, u.u_cred);
out:
	VN_PUT(vp);
	u.u_r.r_val1 = uap->count - auio.uio_resid;
	u.u_ioch += u.u_r.r_val1;
}

/*
 * Change mode of file given path name.
 */
chmod()
{
	register struct a {
		char	*fname;
		int	fmode;
	} *uap;

	struct vattr vattr;

	uap = (struct a *)u.u_ap;
	vattr_null(&vattr);
	vattr.va_mode = uap->fmode & 07777;
	u.u_error = namesetattr(uap->fname, FOLLOW_LINK, &vattr);
}


/*
 * Change ownership of file given file name.
 */
chown()
{
	register struct a {
		char	*fname;
		int	uid;
		int	gid;
	} *uap;

	struct vattr vattr;

	uap = (struct a *)u.u_ap;
	vattr_null(&vattr);
	vattr.va_uid = (uid_t)uap->uid;
	vattr.va_gid = (gid_t)uap->gid;
	u.u_error = namesetattr(uap->fname, NO_FOLLOW,  &vattr);
}


/*
 * Set access/modify times on named file.
 */
utime()
{
	register struct a {
		char	*fname;
		time_t *tptr;
	} *uap;

	time_t tv[2];
	struct vattr vattr;

	uap = (struct a *)u.u_ap;
	vattr_null(&vattr);
	if (uap->tptr != NULL) {
		u.u_error = copyin((caddr_t)uap->tptr,(caddr_t)tv, sizeof(tv));
		if (u.u_error)
			return;
		vattr.va_atime.tv_sec = tv[0];
		vattr.va_atime.tv_usec = 0;
		vattr.va_mtime.tv_sec = tv[1];
		vattr.va_mtime.tv_usec = 0;
	} else {
		/*
		 * You can change the mod/acc times if
	 	 * (1) you are su or owner,   or
	 	 * (2) you have write access &&
	 	 *     uap->tptr is NULL.
		 * We need to let the fs-specific setattr routine
		 * know that uap->tptr was NULL. We do it by setting
		 * tv_sec to 0 and tv_usec to -1.
		 */
		vattr.va_atime.tv_sec = 0;
		vattr.va_mtime.tv_sec = 0;
	}
	u.u_error = namesetattr(uap->fname, FOLLOW_LINK, &vattr);
}

/*
 * Truncate a file given its path name.
 */
truncate()
{
	register struct a {
		char	*fname;
		int	length;
	} *uap;

	struct vattr vattr;

	uap = (struct a *)u.u_ap;
	if (uap->length < 0) {
		u.u_error = EINVAL;
		return;
	}
	vattr_null(&vattr);
	vattr.va_size = uap->length;
	u.u_error = namesetattr(uap->fname, FOLLOW_LINK, &vattr);
}

/*
 * Truncate a file given a file descriptor.
 */
ftruncate()
{
	register struct a {
		int	fd;
		int	length;
	} *uap;

	register struct vnode *vp;
	struct file *fp;

	uap = (struct a *)u.u_ap;
	if (uap->length < 0) {
		u.u_error = EINVAL;
		return;
	}
	u.u_error = getvnodefp(uap->fd, &fp);
	if (u.u_error)
		return;
	vp = fp->f_vnode;
	if ((fp->f_flag & FWRITE) == 0) {
		u.u_error = EINVAL;
	} else if (vp->v_vfsp->vfs_flag & VFS_RDONLY) {
		u.u_error = EROFS;
	} else {
		struct vattr vattr;

		vattr_null(&vattr);
		vattr.va_size = uap->length;
		VN_LOCKNODE(vp);
		u.u_error = VOP_SETATTR(vp, &vattr, fp->f_cred);
		VN_UNLOCKNODE(vp);
	}
}

/*
 * Common routine for modifying attributes
 * of named files.
 */
namesetattr(fnamep, followlink, vap)
	char *fnamep;
	enum symfollow followlink;
	struct vattr *vap;
{
	struct vnode *vp;
	register int error;

	error =
	    lookupname(fnamep, UIOSEG_USER, followlink,
			(struct vnode **)0, &vp);
	if (error)
		return (error);	
	if (vp->v_vfsp->vfs_flag & VFS_RDONLY)
		error = EROFS;
	else
		error = VOP_SETATTR(vp, vap, u.u_cred);
	VN_PUT(vp);
	return (error);
}

/*
 * Set file creation mask.
 */
umask()
{
	register struct a {
		int mask;
	} *uap;

	uap = (struct a *)u.u_ap;
	u.u_r.r_val1 = u.u_cmask;
	u.u_cmask = uap->mask & 0777;
}

/*
 * vhangup()
 *	Revoke access to the specified tty by all processes.
 *
 * Used by the super-user in init to give ``clean'' terminals at login.
 * Also used by (eg) rlogind, window managers, etc, to render old file
 * descriptors on pseudo-ttys useless when session starts.
 */

vhangup()
{
	register struct a {
		char	*fnamp;
	} *uap;
	struct	vnode	*vp;
	int	filemode;

	uap = (struct a *) u.u_ap;
	filemode = FREAD;
#ifdef DEBUG
	if (fvhang_up)
		printf("fvhangup: on 0x%x\n", uap->fnamp);
#endif
	if (!suser())
		return;

	u.u_error = vn_open(uap->fnamp, UIOSEG_USER, &filemode, 0600, &vp);
	if (u.u_error)
		return;
	VN_UNLOCKNODE(vp); /* don't need lock */
	if (vp->v_type != VCHR)
		u.u_error = ENOTTY;
	/*
	 * if no one else has the file open then just return.
	 * otherwise set all the "file" flags to FHUPED.
	 */
	if (cado(vp)) {
#ifdef DEBUG
		if (fvhang_up)
			printf("fvhangup:done - no others\n");
#endif
		VN_RELE(vp);
		return;
	}
	/*
	 * some one else has it open so inform the driver as to whats 
	 * happening.
	 * Note a read that is already in progress may be unnoticed by the
	 * driver - so be it.
	 */
	VOP_VHANGUP(vp); 	/* STRHUP device */

	VN_RELE(vp);	/* should be closed by now */
#ifdef DEBUG
	if (fvhang_up)
		printf("fvhangup:vnode 0x%x done\n", vp);
#endif
}

/*
 * cado()
 *	Cease and desist order - try to stop more I/O.
 *	Search for references to a particular dev in the file table.
 *	For each one found, make it useless for read and write and open.
 *	return true if any files found.
 */

static
cado(vp)
	register struct vnode *vp;
{
	register struct file *fp;
	register struct vnode *vp1;
	register spl_t	s;
	dev_t		rdev;
	int		found;

	rdev = vp->v_rdev;
#ifdef DEBUG
	if (fvhang_up)
		printf("cado: on dev 0x%x vnode:0x%x v_count:%d\n",
					rdev, vp, vp->v_count);
#endif
	found=0;
	s = p_lock(&file_list, SPLFS);
	for (fp = file; fp < v.ve_file; fp++) {
		if (fp->f_count == 0)
			continue;
		vp1=fp->f_vnode;
		if (vp1 && ((vp1 == vp) || (vp1->v_rdev == rdev))) {
			found++;
#ifdef DEBUG
			if (fvhang_up)
				printf("cado: fp#%d found vp:0x%x vp1:0x%x\n",
				fp-file, vp, vp1);
#endif
			/*
			 * Force NDELAY semantics.
			 */
			(void) p_lock(&fp->f_mutex, SPLFS);
			vp1=fp->f_vnode;
			if (vp1 && ((vp1 == vp) || (vp1->v_rdev == rdev))) {
				fp->f_flag |= FNDELAY|FHUPED;
			}
			v_lock(&fp->f_mutex, SPLFS);
		}
	}
	v_lock(&file_list, s);
	if (found == 0) {
#ifdef DEBUG
		if (fvhang_up)
			printf("cado: none found\n");
#endif
		u.u_error = ESRCH;
		return(u.u_error);
	}
#ifdef DEBUG
	if (fvhang_up)
		printf("cado:exit dev 0x%x vnode:0x%x v_count:%d\n",
					rdev, vp, vp->v_count);
#endif
	return (0);
}


/*
 * getvnodefp()
 *	Get the file structure entry for the file descrpitor, but make sure
 *	its a vnode.
 */

int
getvnodefp(fd, fpp)
	int fd;
	struct file **fpp;
{
	register struct file *fp;

	GETFP(fp, fd);
	if (fp == NULL)
		return EBADF;
	*fpp = fp;
	return (0);
}

/*
 * check_rw_fd()
 *	Check if a writable file-descriptor exists for a given vnode.
 *
 * Used to insure don't cache a text (or allow exec) if the file can change.
 * Returns 0 for no such file-descriptor, else error (ETXTBSY).
 *
 * Caller holds locked vnode.
 */

check_rw_fd(vp)
	register struct vnode *vp;
{
	register struct file *fp;

	/*
	 * Scan file-table to check for writing use of same vnode.  This
	 * needs no locking of file-table or entries, since we hold
	 * locked vnode, and copen() arranges that file-table entry is
	 * filled out before unlocking vnode.  This can race and miss a
	 * concurrent close, but this is ok.  Can also race with other
	 * use of same text going away, but locked inode here also avoids
	 * problems.
	 *
	 * This check assumes if v_count==1 (caller has only ref) the
	 * file-system doesn't increment v_count until after holding vnode
	 * (soft) locked.  See ufs_inode.c:iget() for example.
	 */

	for (fp = file; fp < v.ve_file; fp++) {
		if (fp->f_count > 0
		&&  fp->f_vnode == vp
		&&  (fp->f_flag & FWRITE))
			return (ETXTBSY);
	}

	return (0);				/* no RW fd's */
}
