Coherent4.2.10/coh.386/sys3.c

Compare this file to the similar file:
Show the results in this format:

/* $Header: /ker/coh.386/RCS/sys3.c,v 2.9 93/10/29 00:55:45 nigel Exp Locker: nigel $ */
/* (lgl-
 *	The information contained herein is a trade secret of Mark Williams
 *	Company, and  is confidential information.  It is provided  under a
 *	license agreement,  and may be  copied or disclosed  only under the
 *	terms of  that agreement.  Any  reproduction or disclosure  of this
 *	material without the express written authorization of Mark Williams
 *	Company or persuant to the license agreement is unlawful.
 *
 *	COHERENT Version 2.3.37
 *	Copyright (c) 1982, 1983, 1984.
 *	An unpublished work by Mark Williams Company, Chicago.
 *	All rights reserved.
 -lgl) */
/*
 * System calls (more filesystem related calls).
 *
 * $Log:	sys3.c,v $
 * Revision 2.9  93/10/29  00:55:45  nigel
 * R98 (aka 4.2 Beta) prior to removing System Global memory
 * 
 * Revision 2.8  93/09/13  08:03:55  nigel
 * Improved the synchronous write support to not flush the *entire* inode
 * cache, just write the relevant i-node.
 * 
 * Revision 2.7  93/09/02  18:08:35  nigel
 * Nigel's r85, minor edits only
 * 
 * Revision 2.6  93/08/19  10:37:38  nigel
 * r83 ioctl (), corefile, new headers
 * 
 * Revision 2.5  93/08/19  03:26:53  nigel
 * Nigel's r83 (Stylistic cleanup)
 */

#include <kernel/proc_lib.h>
#include <sys/stat.h>
#include <sys/errno.h>
#include <fcntl.h>
#include <stddef.h>

#define	_KERNEL		1

#include <kernel/trace.h>
#include <sys/uproc.h>
#include <sys/proc.h>
#include <sys/buf.h>
#include <sys/con.h>
#include <sys/fd.h>
#include <sys/filsys.h>
#include <sys/ino.h>
#include <sys/inode.h>
#include <sys/io.h>
#include <sys/mount.h>
#include <sys/file.h>

/*
 * Open the file `np' with the mode `mode'.
 */

int
uopen (np, oflag, mode)
char *np;
int		oflag;
mode_t		mode;
{
	int f;
	struct inode * ip;
	fd_t fd;
	int cflag;	/* Flag is set if we create a file.  */
	IO		io;
	struct direct	dir;

	cflag = 0;	/* Nothing created so far.  */

	/*
	 * NIGEL: As reported numerous times by customers, this stupid code
	 * will create a file before looking to see whether it can open a file
	 * descriptor. In fact, any error in here will leave a new file around
	 * despite the error return.
	 *
	 * Do it right; allocate the resources first!
	 */

	if ((fd = fd_alloc ()) == ERROR_FD)
		return -1;


	/* Determine read or write status for fdopen.  */

	switch (oflag & 3) {
	case O_RDONLY:
		f = IPR;
		break;

	case O_WRONLY:
		f = IPW;
		break;

	case O_RDWR:
		f = IPR | IPW;
		break;

	default:
		SET_U_ERROR (EINVAL, "bad oflag");
		goto done;
	}

	/* Process the O_CREAT flag.  */
	if ((oflag & O_CREAT) != 0) {

		if (ftoi (np, 'c', IOUSR, & io, & dir, SELF->p_credp) != 0)
			goto done;

		/* If it didn't exist, but its parent did, then make it.  */
		if ((ip = u.u_cdiri) == NULL) {
			if ((ip = imake ((mode & ~ IFMT) | IFREG,
					 (o_dev_t) 0, & io, & dir,
					 SELF->p_credp)) == NULL)
				goto done;

			cflag = 1;	/* Note that we just created a file.  */
		} else {	/* The file already exists.  */
			/*
			 * Exclusive O_CREAT on existing file should fail.
			 */

			if ((oflag & O_EXCL) != 0) {
				idetach (ip);
				SET_U_ERROR (EEXIST,
					 "exclusive creat on existing file");
				goto done;
			}

			/*
			 * Do not write to a read only file system;
			 * never write to a directory;
			 * always write to block and character special devices.
			 */

			switch (ip->i_mode & IFMT) {
			case IFBLK:
			case IFCHR:
				break;

			case IFDIR:
				idetach (ip);
				SET_U_ERROR (EISDIR, "<open: EISDIR>");
				goto done;

			default:
				if (getment (ip->i_dev, 1) == NULL) {
					idetach (ip);
					SET_U_ERROR (EROFS,
						"Could not fetch mount entry");
					goto done;
				}
			}
		} /* Did the file exist?  */

	} else { /* O_CREAT was not set--just reference the file.  */

		if (ftoi (np, 'r', IOUSR, NULL, & dir, SELF->p_credp) != 0)
			goto done;

		ip = u.u_cdiri;	/* This must be the inode we wanted.  */
	}

	/*
	 * ASSERTION: We probably have an inode for an existing file.
	 * If we don't, the ip will be NULL and iaccess() will fail (as
	 * desired.)
	 */

	/*
	 * Only check permissions on a pre-existing file.
	 */

	if (0 == cflag && iaccess (ip, f, SELF->p_credp) == 0) {
		idetach (ip);
		goto done;
	}

	/*
	 * ASSERTION: We have an inode for a file we
	 * have valid permissions on.
	 */


	/*
	 * LOUIS:
	 * According to the POSIX and SVID specs, this alternate handling
	 * of O_EXCL is bogus.  O_EXCL only has meaning if used with
	 * O_CREAT  It is now being removed for the first time in
	 * Coherent history (even though it was broken long before!)
	 * and is #ifdef'ed out in case it breaks just a few too many
	 * utilities.
	 */

#if 0
	if ((ip->i_flag & IFEXCL) != 0) {
		idetach (ip);
		SET_U_ERROR (EEXIST, "open: file already open O_EXCL");
		goto done;	/* Somebody else has an exclusive open.  */
	}

	/*
	 * If requesting exclusive open, fail if someone else has it open.
	 */

	if ((oflag & O_EXCL) != 0) {
		if (ip->i_refc != 1) {
			idetach (ip);
			SET_U_ERROR (EEXIST, "<open: O_EXCL but already open>");
			goto done;
		}

		/* Mark this open inode as exclusive.  */

		/* This had read &= IFEXCL -- I think
		 * that was a mistake -- LOUIS
		 */
		ip->i_flag &= IFEXCL;
	}
#endif

	if ((oflag & O_NDELAY) != 0)
		f |= IPNDLY;
	if ((oflag & O_NONBLOCK) != 0)
		f |= IPNONBLOCK;
	if ((oflag & O_APPEND) != 0)
		f |= IPAPPEND;
	if ((oflag & O_SYNC) != 0)
		f |= IPSYNC;
#if 0
	if ((oflag & O_EXCL) != 0)
		f |= IPEXCL;
#endif

	if ((oflag & O_NOCTTY) != 0)
		f |= IPNOCTTY;

	/*
	 * NIGEL: special hack for FS debugging.
	 */

	if ((oflag & O_TRACE) != 0)
		itrace (ip);

	if (fd_init (fd, ip, f) < 0) {
		idetach (ip);
		goto done;
	}

	/*
	 * Change our notion of what inode we are working with in case the
	 * open created a new one.
	 */

	ip = fd_get (fd)->f_ip;


	/*
	 * If requested, truncate the file.
	 */

	if ((oflag & O_TRUNC) != 0 && (ip->i_mode & IFPIPE) != IFPIPE &&
	    cflag == 0) {
		if (iaccess (ip, IPW, SELF->p_credp) != 0) {
			itruncate (ip);
		} else {
			idetach (ip);
			goto done;
		}
	}

#if	0
	/*
	 * This is code to test where a bug in BBx/4 is showing up; their
	 * self-tests fail on O_APPEND for some unknown reason; this didn't
	 * help.
	 */

	if ((oflag & O_APPEND) != 0)
		fd_get (fd)->f_seek = ip->i_size;
#endif

	iunlock (ip);

done:
	return (fd = fd_finish (fd)) == ERROR_FD ? -1 : fd;
}


/*
 * Create a pipe.  Notice, we must do the IPR fdopen with IPNDLY so that
 * we don't block waiting for the writer we are about to create.  Then
 * after we are done, we ufcntl() to turn off the IPNDLY on fd1.
 */

int
upipe (fdp)
short fdp[2];
{
	INODE *ip;
	fd_t fd1;
	fd_t fd2;

	if ((ip = pmake (0)) == NULL)
		return -1;

	if ((fd1 = fd_open (ip, IPR | IPNDLY)) != ERROR_FD) {
		/*
		 * We assume that fd_open () will not switch inodes on us.
		 */

		ASSERT (fd_get (fd1)->f_ip == ip);

		ip->i_refc ++;
		if ((fd2 = fd_open (ip, IPW)) != ERROR_FD) {

			ASSERT (fd_get (fd2)->f_ip == ip);

			iunlock (ip);
			u.u_rval2 = fd2;
			ufcntl (fd1, F_SETFL, 0);
			return fd1;
		}

		-- ip->i_refc;
		iunlock (ip);
		fd_close (fd1);
		return -1;
	}
	idetach (ip);
	return -1;
}


/*
 * Read or write `n' bytes from the file number `fd' using the buffer
 * `bp'.  If `do_write' is nonzero, write, else read.
 */

#if	__USE_PROTO__
__LOCAL__ int sysio (int fd, caddr_t bp, size_t n, int do_write)
#else
__LOCAL__ int
sysio (fd, bp, n, do_write)
int fd;
caddr_t bp;
size_t n;
int do_write;
#endif
{
	__fd_t	      *	fdp;
	int type;
	IO		io;

	if ((fdp = fd_get(fd)) == NULL ||
	    (fdp->f_flag & (do_write ? IPW : IPR)) == 0) {
		set_user_error (EBADF);
		return -1;
	}


	/*
	 * When reading (writing into user memory), buffer may NOT be in text
	 * segment.  When writing (reading from user memory), buffer may
	 * be in text segment.
	 */

	if (! useracc (bp, n, ! do_write)) {
		set_user_error (EFAULT);
		return -1;
	}

	type = fdp->f_ip->i_mode & IFMT;
	if (type != IFCHR)
		ilock (fdp->f_ip, "sysio ()");

	/* Writes in append mode are forced to end of file. */
	if ((fdp->f_flag & IPAPPEND) != 0 && do_write)
		fdp->f_seek = fdp->f_ip->i_size;

	if (do_write && (fdp->f_ip->i_mode & IFMT) == IFREG) {
		long maxbyte = (long) u.u_bpfmax * BSIZE;
		if (maxbyte <= fdp->f_seek)
			n = 0;
		else if ((long) n > maxbyte - fdp->f_seek)
			n = (unsigned) (maxbyte - fdp->f_seek);
	}

	io.io_seg = IOUSR;
	io.io_seek = fdp->f_seek;
	io.io.vbase = bp;
	io.io_ioc  = n;
	io.io_flag = 0;

	if ((fdp->f_flag & IPNDLY) != 0)
		io.io_flag |= IONDLY;
	if ((fdp->f_flag & IPNONBLOCK) != 0)
		io.io_flag |= IONONBLOCK;

	if (do_write) {
		iwrite (fdp->f_ip, & io);
	} else {
		iread (fdp->f_ip, & io);
		iaccessed (fdp->f_ip);		/* read - atime */
	}
	n -= io.io_ioc;
	fdp->f_seek += n;

	if (type != IFCHR) {
		/* Was this inode opened for synchronous writes?  */
		if ((fdp->f_flag & IPSYNC) != 0)
			icopymd (fdp->f_ip);

		iunlock (fdp->f_ip);
	}

	return n;
}


/*
 * Read `n' bytes into the buffer `bp' from file number `fd'.
 */

int
uread(fd, bp, n)
int fd;
caddr_t bp;
size_t n;
{
	return sysio (fd, bp, n, 0);
}


/*
 * Return a status structure for the given file name.
 */

int
ustat(np, stp)
char *np;
struct stat *stp;
{
	struct stat	stat;
	struct direct	dir;

	if (ftoi (np, 'r', IOUSR, NULL, & dir, SELF->p_credp) != 0)
		return -1;

	istat (u.u_cdiri, & stat);
	idetach (u.u_cdiri);

	if (kucopy (& stat, stp, sizeof (stat)) != sizeof (stat)) {
		set_user_error (EFAULT);
		return -1;
	}
	return 0;
}


/*
 * Write out all modified buffers, inodes and super blocks to disk.
 */

int
usync()
{
	MOUNT * mp;
	static __DUMB_GATE syngate = __GATE_DECLARE ("sync");

	__GATE_LOCK (syngate, "lock : usync ()");
	for (mp = mountp ; mp != NULL ; mp = mp->m_next)
		msync (mp);
	bsync ();
	__GATE_UNLOCK (syngate);
	return 0;
}


/*
 * Set the mask for file access.
 */

int
uumask (mask)
int mask;
{
	int		omask;

	omask = u.u_umask;
	u.u_umask = mask & 0777;
	return omask;
}


/*
 * Unmount the given device.
 */

int
uumount(sp)
char *sp;
{
	INODE *ip;
	MOUNT *mp;
	MOUNT **mpp;
	dev_t rdev;
	int mode;
	struct direct	dir;

	if (ftoi (sp, 'r', IOUSR, NULL, & dir, SELF->p_credp) != 0)
		return -1;

	u.u_cdiri = u.u_cdiri;
	if (iaccess (u.u_cdiri, IPR | IPW, SELF->p_credp) == 0) {
		idetach (u.u_cdiri);
		return -1;
	}

	rdev = u.u_cdiri->i_rdev;
	mode = u.u_cdiri->i_mode;

	idetach (u.u_cdiri);
	if ((mode & IFMT) != IFBLK) {
		set_user_error (ENOTBLK);
		return -1;
	}

	for (mpp = & mountp ; (mp = * mpp) != NULL ; mpp = & mp->m_next)
		if (mp->m_dev == rdev)
			break;

	if (mp == NULL) {
		set_user_error (EINVAL);
		return -1;
	}

	msync (mp);
	for (ip = inode_table ; ip < inode_table_end ; ip ++) {
		if (ip->i_dev == rdev && ip->i_refc > 0) {
			set_user_error (EBUSY);
			return -1;
		}
	}
	ASSERT (ip == inode_table_end);

	for (ip = inode_table ; ip < inode_table_end ; ip ++) {
		if (ip->i_dev == rdev) {
			ASSERT (! ilocked (ip));
#if	0
			cmn_err (CE_NOTE, "umount ino = %d size = %ld",
				 ip->i_ino, ip->i_size);
#endif
			ip->i_ino = 0;
		}
	}
	ASSERT (ip == inode_table_end);

	bflush (rdev);
	dclose (rdev, mp->m_flag ? IPR : IPR | IPW, DFBLK, NULL);
	* mpp = mp->m_next;
	mp->m_ip->i_flag &= ~ IFMNT;

	ldetach (mp->m_ip);
	kmem_free (mp, sizeof (* mp));
	return 0;
}


/*
 * Internal version of unlink () called by uunlink () and umkdir ().
 */

int
do_unlink (path, space)
caddr_t		path;
int		space;
{
	INODE * ip;
	dev_t dev;
	IO		io;
	struct direct	dir;
	unsigned	olderror;

	/*
	 * We start by clearing u_error because we are called from umkdir ()
	 * in a situation where the active error number is not relevant to
	 * us. We return the old error number so that umkdir () can restore
	 * the error number it wants easily.
	 */

	olderror = get_user_error ();
	set_user_error (0);

	if (ftoi (path, 'u', space, & io, & dir, SELF->p_credp) != 0)
		return olderror;

	if (iaccess (u.u_pdiri, IPW, SELF->p_credp) == 0) {
		set_user_error (EACCES);
		idetach (u.u_pdiri);
		return olderror;
	}
	dev = u.u_pdiri->i_dev;

	if (iucheck (dev, u.u_cdirn) == 0) {
		idetach (u.u_pdiri);
		return olderror;
	}

	idirent (0, & io, & dir);
	idetach (u.u_pdiri);

	if ((ip = iattach (dev, u.u_cdirn)) == NULL)
		return 0;

	if (ip->i_nlink > 0)
		-- ip->i_nlink;
	icreated (ip);	/* unlink - ctime */
	idetach (ip);
	return olderror;
}


/*
 * Unlink the given file.
 */

int
uunlink (np)
char *np;
{
	(void) do_unlink (np, IOUSR);
	return 0;
}


/*
 * Set file times.
 */

int
uutime (np, utime)
char *np;
time_t utime [2];
{
	struct {
		time_t		_time [2];
	} stime;
	struct direct	dir;

	if (ftoi (np, 'r', IOUSR, NULL, & dir, SELF->p_credp) != 0)
		return -1;

	if (iprotected (u.u_cdiri)) {
		set_user_error (EROFS);
		idetach (u.u_cdiri);
		return -1;
	}

	if (owner (u.u_cdiri->i_uid)) {
		iaccmodcreat (u.u_cdiri);	/* utime - atime/mtime/ctime */
		if (utime != NULL) {
			if (ukcopy (utime, & stime,
				    sizeof (stime)) != sizeof (stime)) {
				set_user_error (EFAULT);
			} else {
				u.u_cdiri->i_atime = stime._time [0];
				u.u_cdiri->i_mtime = stime._time [1];
			}
		}
	}
	idetach (u.u_cdiri);
	return 0;
}


/*
 * Write `n' bytes from buffer `bp' on file number `fd'.
 */

int
uwrite(fd, bp, n)
int fd;
caddr_t bp;
unsigned n;
{
	return sysio (fd, bp, n, 1);
}


/*
 *
 * int
 * useracc(base, count, writeUsr) -- determine user accessibility
 * caddr_t base;
 * int count;
 * int writeUsr;
 *
 *	Input:	base  = offset in user data space of the region to be accessed.
 *		count = size of access region in bytes.
 *		writeUsr = 0 if read access to be checked, else write
 *
 *	Action:	Verify user has desired access mode into specified region.
 *
 *	Return:	0 = permission denied.
 *		1 = access allowed.
 *
 *	Notes:	Mode is ignored for now, but is required for compatibility
 *		with System V, and future protected mode extensions.
 */

int
useracc(base, count, writeUsr)
register char *base;
int writeUsr, count;
{
	if (base + count >= base) {
		int		ret;

		ret = accdata (base, count) || accstack (base, count) ||
			accShm (base, count);
		if (! writeUsr)
			ret = ret || acctext (base, count);

		return ret;
	}
	return 0;
}


/*
 * strUserAcc(str, writeUsr) - Check user accessibility of 0 terminated string.
 *
 * char	*str;		null-terminated string,
 * int	writeUsr;	0 if read access to be checked, else write.
 *
 * Returns string size on success (without 0), -1 otherwise.
 *
 * It is interface to useracc() when count is not known.
 */

int
strUserAcc(str, writeUsr)
char	*str;
int	writeUsr;
{
	register char	*ch;

	if (! useracc (str, 1, writeUsr))
		return -1;

	for (ch = str ; * ch != 0 ; ch ++) 
		if (! useracc (ch + 1, 1, writeUsr)) 
			return -1;

	return ch - str;
}

/*
 * "Safe" ukcopy and kucopy - use useracc to check user address supplied.
 */

int
kucopyS(kernel, user, n)
caddr_t kernel;
caddr_t user;
size_t n;
{
	if (useracc (user, n, 1))
		return kucopy (kernel, user, n);
	else {
		set_user_error (EFAULT);
		return 0;
	}
}

int
ukcopyS(user, kernel, n)
caddr_t kernel;
caddr_t user;
size_t n;
{
	if (useracc (user, n, 0))
		return ukcopy (user, kernel, n);
	else {
		set_user_error (EFAULT);
		return 0;
	}
}