Coherent4.2.10/coh.386/fd.c

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

/* $Header: /ker/coh.386/RCS/fd.c,v 2.6 93/10/29 00:55:07 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) */
/*
 * File descriptor routines.
 *
 * $Log:	fd.c,v $
 * Revision 2.6  93/10/29  00:55:07  nigel
 * R98 (aka 4.2 Beta) prior to removing System Global memory
 * 
 * Revision 2.5  93/08/19  03:26:25  nigel
 * Nigel's r83 (Stylistic cleanup)
 * 
 * Revision 2.4  93/07/26  15:09:56  nigel
 * Nigel's R80
 * 
 * Revision 1.5  93/04/14  10:06:26  root
 * r75
 * 
 * Revision 1.3  92/06/10  12:52:39  hal
 * First record locking changes.
 */

#include <common/ccompat.h>
#include <kernel/proc_lib.h>
#include <sys/debug.h>
#include <sys/errno.h>
#include <sys/kmem.h>
#include <fcntl.h>
#include <stddef.h>

#define	_KERNEL		1

#include <kernel/param.h>
#include <sys/uproc.h>
#include <sys/fd.h>
#include <sys/inode.h>


/*
 * Operations for converting from tagged to untagged "pointer to system file
 * table entry" and vice versa.
 */

enum {
	TAGMASK		= sizeof (int) - 1
};

#define	MAKE_TAGGED_FD(untag,flag) \
	(ASSERT (((int) (untag) & TAGMASK) == 0), \
	 ASSERT (((flag) & ~ TAGMASK) == 0), \
	 (tagfd_t) ((unsigned long) (untag) + (flag)))

#define	GET_UNTAGGED_FD(tagged) \
	((__fd_t *) ((unsigned long) (tagged) & ~ TAGMASK))

#define	GET_TAGGED_FLAG(tagged) \
	((int) (tagged) & TAGMASK)

/*
 * Given a file number, return the file descriptor. Now that file descriptors
 * stored in the U area have their low bits tagged to store flag information
 * that is FD-specific (gotta avoid that space crunch in the U area), this
 * interface deals with masking out any extra stuff and returning a plain
 * pointer.
 */

#if	__USE_PROTO__
__fd_t * fd_get (fd_t fd)
#else
__fd_t *
fd_get (fd)
fd_t		fd;
#endif
{
	__fd_t	      *	fdp;

	if (fd >= NOFILE || (fdp = GET_UNTAGGED_FD (u.u_filep [fd])) == NULL)
		return NULL;
	return fdp;
}


/*
 * Return tag bits from file descriptor.
 */

#if	__USE_PROTO__
int fd_get_flags (fd_t fd)
#else
int
fd_get_flags (fd)
fd_t		fd;
#endif
{
	if (fd >= NOFILE || u.u_filep [fd] == NULL)
		return -1;
	return GET_TAGGED_FLAG (u.u_filep [fd]);
}


/*
 * This function allows clients to set the flag bits.
 */

#if	__USE_PROTO__
int fd_set_flags (fd_t fd, int flags)
#else
int
fd_set_flags (fd, flags)
fd_t		fd;
int		flags;
#endif
{
	if (fd >= NOFILE || u.u_filep [fd] == NULL)
		return -1;

	if ((flags & ~ TAGMASK) != 0) {
		set_user_error (EINVAL);
		return -1;
	}

	u.u_filep [fd] = MAKE_TAGGED_FD (GET_UNTAGGED_FD (u.u_filep [fd]),
					 flags);
	return 0;
}


/*
 * This function performs something similar to the F_DUPFD function of
 * fcntl ()... this functionality is needed several places internally.
 */

#if	__USE_PROTO__
fd_t fd_dup (fd_t old, fd_t base)
#else
fd_t
fd_dup (old, base)
fd_t		old;
fd_t		base;
#endif
{
	while (base < NOFILE)
		if (u.u_filep [base] == NULL) {
			__fd_t	      *	fdp = fd_get (old);

			if (fdp == NULL)
				return ERROR_FD;
			u.u_filep [base] = MAKE_TAGGED_FD (fdp, 0);
			fdp->f_refc ++;
			return base;
		} else
			base ++;

	set_user_error (EMFILE);
	return ERROR_FD;
}


/*
 * This function finds a free slot in the process file descriptor table and
 * returns the index.
 */

#if	__USE_PROTO__
fd_t fd_get_free (void)
#else
fd_t
fd_get_free ()
#endif
{
	int		i;

	for (i = 0 ; i < sizeof (u.u_filep) / sizeof (* u.u_filep) ; i ++) {
		if (u.u_filep [i] == NULL)
			return i;
	}

	return ERROR_FD;
}


/*
 * This function finds a free slot in the process file descriptor table and
 * fills it in with a partially initialised entry.
 *
 * This function returns a file descriptor number on success, -1 on failure.
 */

#if	__USE_PROTO__
fd_t fd_alloc (void)
#else
fd_t
fd_alloc ()
#endif
{
	fd_t		fd;

	if ((fd = fd_get_free ()) == ERROR_FD) {
		set_user_error (EMFILE);
	} else {
		__fd_t	      *	filep;

		if ((filep = kmem_alloc (sizeof (__fd_t),
					 KM_NOSLEEP)) == NULL) {
			/*
			 * Insufficient resources!
			 */

			set_user_error (ENFILE);
			return ERROR_FD;
		}

		u.u_filep [fd] = MAKE_TAGGED_FD (filep, 0);

		filep->f_flag = 0;
		filep->f_refc = 0;
		filep->f_seek = 0;
		filep->f_ip = NULL;
	}

	return fd;
}


/*
 * This function performs the second half of the file open process by filling
 * in the inode member of the file table entry and requesting that the inode be
 * opened.
 *
 * This function returns -1 on error, 0 on success.
 */

#if	__USE_PROTO__
int fd_init (fd_t fd, struct inode * ip, int mode)
#else
int
fd_init (fd, ip, mode)
fd_t		fd;
struct inode  *	ip;
int		mode;
#endif
{
	__fd_t	      *	filep;
	INODE	      *	newip;

	if ((filep = fd_get (fd)) == NULL)
		return -1;

	ASSERT (ilocked (ip));

	newip = iopen (ip, mode);

	if (get_user_error () != 0)
		return -1;

	if (newip == NULL) {
		/*
		 * This should only happen when there is some kind of internal
		 * error. Still, we cope with it. iopen () should have still
		 * made sure the "ip" was locked, BTW.
		 */

		set_user_error (ENXIO);
		return -1;
	}

	if (newip != ip) {
		/*
		 * In the case of something like a STREAMS clone operation,
		 * iopen () will have returned a new inode but not locked it,
		 * and left the old inode locked. Transfer over to the new
		 * inode.
		 */

		idetach (ip);
		ilock (newip, "fd_init ()");
	}
	ASSERT (ilocked (newip));

	filep->f_ip = newip;
	filep->f_flag = mode;
	filep->f_refc = 1;

	return 0;
}


/*
 * This function finalises an open; normally this does nothing, but if there
 * has been an error, this code will take care of deallocating the entry.
 *
 * This function returns the file descriptor number on success, or -1 on error.
 */

#if	__USE_PROTO__
fd_t fd_finish (fd_t fd)
#else
fd_t
fd_finish (fd)
fd_t		fd;
#endif
{
	__fd_t	      *	filep;

	if ((filep = fd_get (fd)) == NULL)
		return ERROR_FD;

	if (filep->f_refc == 0) {
		/*
		 * The open never really succeeded, release resources.
		 */

		kmem_free (filep, sizeof (* filep));
		u.u_filep [fd] = NULL;
		fd = ERROR_FD;
	}

	return fd;
}


/*
 * Given an inode, and a mode containing permission flags, open the
 * inode with the appropriate permissions and return a file descriptor
 * containing it.
 */

#if	__USE_PROTO__
fd_t fd_open (INODE * ip, int mode)
#else
fd_t
fd_open (ip, mode)
INODE	      *	ip;
int		mode;
#endif
{
	int		fd;

	if ((fd = fd_alloc ()) != ERROR_FD) {
		fd_init (fd, ip, mode);
		fd = fd_finish (fd);
	}

	return fd;
}


/*
 * Given an existing system file table directory, allocate a file table entry
 * and return the index.
 */

#if	__USE_PROTO__
int fd_recv (fd_t fd, __fd_t * fdp)
#else
int
fd_recv (fd, fdp)
fd_t		fd;
__fd_t	      *	fdp;
#endif
{
	ASSERT (fd != ERROR_FD);
	ASSERT (fdp != NULL);

	u.u_filep [fd] = MAKE_TAGGED_FD (fdp, 0);
	fdp->f_refc ++;
	return 0;
}


/*
 * Close the given file number.
 */

#if	__USE_PROTO__
void fd_close (fd_t fd)
#else
void
fd_close (fd)
fd_t		fd;
#endif
{
	__fd_t	      *	fdp;
	static	struct flock	flock = { F_UNLCK, 0, 0, 0 };

	if (fd >= NOFILE ||
	    (fdp = GET_UNTAGGED_FD (u.u_filep [fd])) == NULL) {
		set_user_error (EBADF);
		return;
	}

	if (fdp->f_ip->i_rl != NULL)
		rlock (fdp, F_SETLK, & flock);	/* delete all record locks */

	u.u_filep[fd] = NULL;

	ASSERT (fdp->f_refc != 0);

	if (-- fdp->f_refc == 0) {
		do {
			set_user_error (0);
			iclose (fdp->f_ip, fdp->f_flag);
		} while (get_user_error () == EINTR);
		kmem_free (fdp, sizeof (* fdp));
	}
}


/*
 * Assuming we have made a copy of the user area, increment the reference
 * count of all open files.  (used in fork).
 */

#if	__USE_PROTO__
void fd_dup_all (void)
#else
void
fd_dup_all ()
#endif
{
	int		i;

	for (i = 0 ; i < NOFILE ; i ++)
		if (u.u_filep [i] != NULL)
			GET_UNTAGGED_FD (u.u_filep [i])->f_refc ++;
}


/*
 * Close all open files in the current process.
 */

#if	__USE_PROTO__
void fd_close_all (void)
#else
void
fd_close_all ()
#endif
{
	int		fd;

	for (fd = 0 ; fd < NOFILE ; fd ++)
		if (u.u_filep [fd] != NULL)
			fd_close (fd);
}