
static char rcsid[] = "$Header$";

/************************************************************************\
**									**
**				Copyright 1986				**
**			VALID LOGIC SYSTEMS INCORPORATED		**
**									**
**	This listing contains confidential proprietary information	**
**	which is not to be disclosed to unauthorized persons without	**
**	written consent of an officer of Valid Logic Systems 		**
**	Incoroporated.							**
**									**
**	The copyright notice appearing above is included to provide	**
**	statutory protection in the event of unauthorized or 		**
**	unintentional public disclosure.				**
**									**
\************************************************************************/

/*
 * Standalone I/O library - system support routines
 *
 * Bill O. Gallmeister 0586
 */

#include	"param.h"
#include	"inode.h"
#include	"fs.h"
#include	"dir.h"
#ifdef SA_ADVANCED
#include	"types.h"	/* For stat and buddies */
#include	"stat.h"	/* For stat and buddies */
#endif SA_ADVANCED
#include	"errno.h"	/* For righteous error numbers */
#include	"file.h"	/* For file I/O flags */
#include	"time.h"	/* For stat and buddies */
#include	"../machine/setjmp.h"
#include	"saio.h"

/*
 * Global variables: from here and elsewhere.
 */
extern int	sysdebug; /* System level debugging statements are enabled. */
extern struct io_fs iofs[];	 /* Array of struct FS for file system access */
extern struct io_inode ioinode[]; /* Inodes for file access (global resource) */
extern int openfirst;	/* Flag to indicate fs structures need initializing. */
extern struct iob iob[];		/* available iob's (shared resource). */
extern struct timeval time;	/* Our concept of time */

sbrk (incr)
{
	extern int end;
	static int sbrk_end;
	jmp_buf	local_jmp;
	extern int *nofault;
	int *oldfault;
	char *p;

	if (!sbrk_end) sbrk_end = (int) &end;
	if (!incr) return sbrk_end;
	oldfault = nofault;
	nofault = local_jmp;
	if (setjmp (nofault)) {
		nofault = oldfault;
		return -1;
	}
	p = (char *) (sbrk_end + incr - 1);
	if (incr > 0 && *p) ;		/* Force check if memory present */
	nofault = oldfault;
	sbrk_end += incr;
	if (incr > 0)
		return (sbrk_end-incr);
	return (sbrk_end);
}

#ifdef	SA_ADVANCED
/*
 * link system call
 */
link(target, linkname)
register char *target, *linkname;
{
	register struct inode *ip;
	register struct iob *tiob, *liob;
	register tdesc, ldesc;
	ino_t ino;

	errno = 0;
	/*
	 * We want to insure that NUL is not mistaken as '.'
	 * for the 'target'.
	 */
	if (!*target) {
		errno = EINVAL;
		return -1;
	}
	if (((tdesc = getf()) < 0) || (tdesc >= NFILES))
	{
		if (sysdebug)
			printf("link: getf() failed, returned 0x%x\n", tdesc);
		return (-1);
	}
	else
		tiob = &iob[tdesc];

	if (((ldesc = getf()) < 0) || (ldesc >= NFILES))
	{
		if (sysdebug)
			printf("link: getf() failed, returned 0x%x\n", ldesc);
		return (-1);
	}
	else
		liob = &iob[ldesc];

	tiob->i_flgs |= F_FILE;
	liob->i_flgs |= F_FILE;
	if ((ino = name_to_i(tiob, target, FOLLOW_SYMLINK | O_RDONLY, 0)) == -1)
		return -1;
	if (openi (ino, tiob) == -1)
		return -1;
	ip = tiob->i_ino;
	ip->i_nlink++;
	ip->i_flag |= ICHG;
	iupdat(tiob, &ip->i_mtime, &ip->i_mtime);
	name_to_i(liob, linkname, FOLLOW_SYMLINK | O_CREAT | CREATE_LINK, ip);
	if (errno) {
		ip->i_nlink--;
		ip->i_flag |= ICHG;
	}
	close(ldesc+3);
	close(tdesc+3);
	if (errno) return (-1);
	return (0);
}


/*
 * symlink -- make a symbolic link
 */
symlink(target, linkname)
register char	*target;
register char	*linkname;
{
	register struct iob *io;
	register struct inode *ip;
	register char *tp;
	register c, nc, fdesc;
	register ino_t ino;


	errno = 0;
	/*
	 * We want to insure that NUL is not mistaken as '.'
	 * for the 'target'.
	 */
	if (!*target) {
		errno = EINVAL;
		return -1;
	}
	nc = strlen (target);
	if (((fdesc = getf()) < 0) || (fdesc >= NFILES))
	{
		if (sysdebug)
			printf("symlink: getf() failed, returned 0x%x\n", fdesc);
		return (-1);
	}
	else
		io = &iob[fdesc];

	io->i_flgs |= F_FILE;
	if ((ino = name_to_i(io, linkname, FOLLOW_SYMLINK | O_CREAT | O_EXCL,
	    IFLNK | 0777)) == -1)
		return -1;
	if (openi (ino, io) == -1)
		return -1;
	if (errno)
		return -1;
	io->i_offset = 0;
	io->i_cc = 0;
	io->i_flgs |= F_WRITE;
	if (writebuf(fdesc+3, target, nc) == -1)
		printf("Error in writebuf\n");
	ip = io->i_ino;
	if (close (fdesc+3) == -1)
		printf("Error in closebuf\n");
	printf ("Leaving symlink ino = %d, i_number= %d, i_size = %d, i_db = %d\n",
		ino, ip->i_number, ip->i_size, ip->i_db[0]);
	return (0);
}

/*
 * Unlink system call.
 * Hard to avoid races here, especially
 * in unlinking directories.
 */
unlink(name)
register char	*name;
{
	register struct iob *io;
	register struct inode *ip;
	register char *tp;
	register c, nc, fdesc;
	register ino_t ino;


	if (sysdebug) printf("unlink(\"%s\")\n",name);
	errno = 0;
	/*
	 * We want to insure that NUL is not mistaken as '.'
	 * for the 'target'.
	 */
	if (!*name) {
		errno = EINVAL;
		return -1;
	}
	if (((fdesc = getf()) < 0) || (fdesc >= NFILES))
	{
		if (sysdebug)
			printf("unlink: getf() failed, returned 0x%x\n", fdesc);
		return (-1);
	}
	else
		io = &iob[fdesc];

	io->i_flgs |= F_FILE;
	ino = name_to_i(io, name, DELETE | LOOKUP, 0);
	if (errno)
		return -1;
	close (fdesc+3);
	return 0;
}

/*
 * Stat system call.  This version follows links.
 */
stat(fname, ub)
register char	*fname;
register struct stat *ub;
{

	stat1(fname, ub, 1);
}

/*
 * Lstat system call.  This version does not follow links.
 */
lstat(fname, ub)
register char	*fname;
register struct stat *ub;
{

	stat1(fname, ub, 0);
}

stat1(name, ub, follow)
register char	*name;
register struct stat *ub;
register int follow;
{
	register struct inode *ip;
	register struct iob *io;
	register fdesc;
	register ino_t ino;

	errno = 0;
	/*
	 * We want to insure that NUL is not mistaken as '.'
	 * for the 'name'.
	 */
	if (!*name) {
		errno = EINVAL;
		return -1;
	}
	if (((fdesc = getf()) < 0) || (fdesc >= NFILES))
	{
		if (sysdebug)
			printf("stat1: getf() failed, returned 0x%x\n", fdesc);
		return (-1);
	}
	else
		io = &iob[fdesc];

	io->i_flgs |= F_FILE;
	if ((ino = name_to_i(io, name, (follow ? FOLLOW_SYMLINK : 0)
	    | O_RDONLY, 0)) == -1)
		return -1;
	if (openi (ino, io) == -1)
		return -1;
	ip = io->i_ino;
	(void) ino_stat(ip, ub);
	close (fdesc + 3);
	return 0;
}

/*
 * Return target name of a symbolic link
 */
readlink(name, buf, count)
register char	*name;
register char	*buf;
register int	count;
{
	register struct inode *ip;
	register struct iob *io;
	register fdesc;
	register ino_t ino;
	int resid;

	errno = 0;
	/*
	 * We want to insure that NUL is not mistaken as '.'
	 * for the 'name'.
	 */
	if (!*name) {
		errno = EINVAL;
		return -1;
	}
	if (((fdesc = getf()) < 0) || (fdesc >= NFILES))
	{
		if (sysdebug)
			printf("readlink: getf() failed, returned 0x%x\n", fdesc);
		return (-1);
	}
	else
		io = &iob[fdesc];

	io->i_flgs |= F_FILE | F_READ;
	if ((ino = name_to_i(io, name, FOLLOW_SYMLINK | O_RDONLY, 0)) == -1)
		return -1;
	if (openi (ino, io) == -1)
		return -1;
	ip = io->i_ino;

	if ((ip->i_mode&IFMT) != IFLNK) {
		errno = ENXIO;
		resid = -1;
	} else {
		resid = ip->i_size > count ? count : ip->i_size;
		fillbuf (fdesc+3, buf, resid);
	}
	close (fdesc+3);
	return resid;
}

/*
 * Change mode of a file given a file descriptor.
 */
fchmod(fd, fmode)
int	fd;
int	fmode;
{
	register struct iob *io;
	register struct inode *ip;

	errno = 0;
	if (fd >= 0 && fd <= 2)
		return 0;
	io = &iob[fd-3];
	if ((io->i_flgs & (F_ALLOC|F_FILE)) == 0 || (ip = io->i_ino) == 0) {
		errno = EBADF;
		return -1;
	}
	chmod1(ip, fmode);
}

/*
 * Change the mode on a file.
 * Inode must be locked before calling.
 */
chmod1(ip, mode)
register struct inode *ip;
register int mode;
{

	ip->i_mode &= ~07777;
	ip->i_mode |= mode&07777;
	ip->i_flag |= ICHG;
}

utime(name, tptr)
char	*name;
long	tptr[];
{
	register struct inode *ip;
	register struct iob *io;
	register fdesc;
	ino_t ino;
	struct	timeval tv[2];

	errno = 0;
	/*
	 * We want to insure that NUL is not mistaken as '.'
	 * for the 'target'.
	 */
	if (!*name) {
		errno = EINVAL;
		return -1;
	}
	if (((fdesc = getf()) < 0) || (fdesc >= NFILES))
	{
		if (sysdebug)
			printf("link: getf() failed, returned 0x%x\n", fdesc);
		return (-1);
	}
	else
		io = &iob[fdesc];

	io->i_flgs |= F_FILE;
	if ((ino = name_to_i(io, name, FOLLOW_SYMLINK | O_RDONLY, 0)) == -1) {
		goto err;
	}
	if (openi (ino, io) == -1) {
		goto err;
	}
	ip = io->i_ino;

	if (errno == 0) {
		tv[0].tv_sec = tptr[0];
		tv[1].tv_sec = tptr[1];
		tv[0].tv_usec = tv[1].tv_usec = 0;
		ip->i_flag |= IACC|IUPD|ICHG;
		iupdat(io, &tv[0], &tv[1]);
	}
err:
	close (fdesc+3);
	if (errno)
		return -1;
	return (0);
}

/*
 * A virgin directory (no blushing please).
 */
struct dirtemplate mastertemplate = {
	0, 12, 1, ".",
	0, DIRBLKSIZ - 12, 2, ".."
};

/*
 * Mkdir system call
 */
mkdir(name, dmode)
register char	*name;
register int	dmode;
{
	register struct inode *ip, *dp;
	struct dirtemplate dirtemplate;
	register struct iob *io;
	register fdesc;
	ino_t ino;

if( sysdebug )
	printf( "mkdir: name is %s, dmode is 0%o\n", name, dmode );

	errno = 0;
	bcopy (&mastertemplate, &dirtemplate, sizeof mastertemplate);
	/*
	 * We want to insure that NUL is not mistaken as '.'
	 * for the 'target'.
	 */
	if (!*name) {
		errno = EINVAL;
		return -1;
	}
	if (((fdesc = getf()) < 0) || (fdesc >= NFILES))
	{
		if (sysdebug)
			printf("link: getf() failed, returned 0x%x\n", fdesc);
		return (-1);
	}
	else
		io = &iob[fdesc];

	io->i_flgs |= F_FILE;
	dmode &= 0777;
	dmode |= IFDIR;
	if ((ino=name_to_i(io, name, FOLLOW_SYMLINK | O_EXCL | O_CREAT, dmode))
	    == -1) {
		errno = EEXIST;
		printf ("name_to_i failed for '%s'\n", name);
		return -1;
	}
	else
		if (sysdebug)
			printf("name_to_i returns ino# 0x%x to mkdir\n",ino);
	/*
	 * Bump link count in parent directory
	 * to reflect work done below.  Should
	 * be done before reference is created
	 * so reparation is possible if we crash.
	 */
	dp = io->i_ino;		/* parent inode of directory being built */
	dp->i_nlink++;		/* one more link - the subdirectory */
	dp->i_flag |= ICHG;	/* name_to_i changed the inode */
	/* fill in blanks in template directory */
	dirtemplate.dotdot_ino = dp->i_number;
	if (openi (ino, io) == -1)	/* open new directory */
		return -1;
	ip = io->i_ino;
	if (errno)
		return -1;
	ip->i_flag |= IACC|IUPD|ICHG;
	ip->i_nlink = 2;
	ip->i_uid = 0;
	ip->i_gid = dp->i_gid;
if( sysdebug )
	printf( "mkdir: about to call iupdat -  ip is 0x%x, ip->i_fs is 0x%x, ip->i_fs->fs_ronly  is %d\n", ip, ip->i_fs, ip->i_fs->fs_ronly );
	iupdat(io, &time, &time);	/* update inode -- NOT data blocks */

	/*
	 * Initialize directory data with "."
	 * and ".." from static template.
	 */
	dirtemplate.dot_ino = ip->i_number;
	io->i_offset = 0;
	io->i_cc = 0;
	io->i_flgs |= F_WRITE;
	writebuf(fdesc+3, (caddr_t)&dirtemplate, sizeof (dirtemplate));
	close (fdesc+3);
	return (0);
}

#endif	SA_ADVANCED
