static char rcsid[] = "$Header: sys.c,v 86.2 86/07/25 15:55:28 bog Exp $";

/************************************************************************\
**									**
**				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
 *
 * Taken from Valid boot code.
 *
 * Bill O. Gallmeister 0586
 */

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

/*
 * Global variables: from here and elsewhere.
 */
int	sysdebug = 0;	/* System level debugging statements are enabled. */
struct io_fs iofs[NIOFS];	 /* Array of struct FS for file system access */
struct iob iob[NFILES];		/* available iob's (shared resource). */
#ifdef	SA_ADVANCED
struct io_inode ioinode[NFILES*4];/* Inodes for file access (global resource) */
int	ninodes = NFILES*4;
#else	SA_ADVANCED
struct io_inode ioinode[NFILES]; /* Inodes for file access (global resource) */
int	ninodes = NFILES;
#endif	SA_ADVANCED
extern int openfirst;	/* Flag to indicate fs structures need initializing. */

/*
 * Things from other lands that talk funny.
 */
#ifdef SA_ADVANCED
extern ino_t dcreate();
extern daddr_t alloc();
#endif SA_ADVANCED
extern ino_t dlook();
extern struct timeval time;	/* Our concept of time */
extern struct io_fs *openfs();	/* Used before defined. */


/*
 * Getf
 *
 * Get an available file descriptor/iob from the global array iob[].
 *
 * Returns the array index of the file descriptor allocated.
 * Doesn't return in case of an error -- _stop()s directly.
 */
int
getf()
{
	register int i;

	if (sysdebug)
		printf("getf()\n");

	/*
	 * Initialize file descriptors if you must (I.E. first time through).
	 */
	if (openfirst) {
		for (i = 0; i < NFILES; i++)	/* Initialize all files... */
			iob[i].i_flgs = 0;
		openfirst = 0;
	}

	/*
	 * Find an open file slot to use.
	 */
	for (i = 0; i < NFILES; i++)
		if (iob[i].i_flgs == 0)
			break;
	if (i == NFILES)
		_stop("No more file slots");

	/*
	 * Claim the file.
	 */
	iob[i].i_flgs |= F_ALLOC; /* "Mine, mine, mine!" -Yoda */

	return(i);
}


/*
 * Name_to_i
 *
 * Our very own little namei().
 *
 * Assumes the name is of the form DEV(unit,part)filename, as explained for
 * open().
 *
 * Returns the inode number associated with the name.  Returns -1 on error,
 * setting errno sometimes.
 */
ino_t
name_to_i(file,str,how,cmode)
	char *str;
	register struct iob *file;
	int how, cmode;
{
	register char *cp;
	register struct devsw *dp;
	int i, atol();
	register struct io_fs *fs;
	static int use_last;
	static dev_t lastfs;
	static daddr_t lastoff;

	/*
	 * Walk past devtype.  You better hit a left paren after devtype.
	 */
	for (cp = str; *cp && *cp != '('; cp++)
			;
	if (*cp == 0 && use_last) {
		if (sysdebug)
			printf ("Using device %x\n", lastfs);
		file->i_unit = (int) minor (lastfs);
		file->i_boff = (int) lastoff;
		file->i_dev = (int) major (lastfs);
		fs = openfs(file,"");
		file->i_fs = &fs->iof_fs;
		cp = str;
		goto find_file;
	}
	if (*cp != '(') {
		if (sysdebug)
			printf("Bad device type/unit number format\n");
		file->i_flgs = 0;
		errno = ENXIO;
		return (-1);
	}
	/* 
	 * Erase '(' (null-terminating str as a side-effect)
	 * and advance to next character.
	 */
	*cp = '\0'; 
	/*
	 * Search through "devsw" for a device type matching ours.
	 */
	for (dp = devsw; dp->dv_name; dp++) {
		/*
		 * Q: to case-insensitize or not to case-insensitize?
		 * A: for now, not.
		 */
		if (!strcmp(str, dp->dv_name))
			break;
	}
	if (! (dp->dv_name))
	{
		if (sysdebug)
			printf("Unknown device\n");
		file->i_flgs = 0;
		errno = ENXIO;
		return (-1);
	}
	*(cp++) = '(';
#ifdef INODE_LIVES_IN_IOB
	/*
	 * The iob structure file contains an inode structure which will
	 * statically hold the i_dev fopr the duration of its use.  This number
	 * is used as an index into devsw[].
	 */
	file->i_ino.i_dev = dp-devsw;	/* index into devsw array. */
#else  INODE_LIVES_IN_IOB
	/*
	 * Remember the i_dev until an inode is allocated.
	 * (by openi, which will then slap io->i_dev into the inodes i_dev,
	 * which will be used in all succeeding accesses of driver code.
	 */
	file->i_dev = dp-devsw; 	/* index into devsw array. */
#endif INODE_LIVES_IN_IOB
	/*
	 * The classical atoi() algorithm -- for ctlr/unit number.
	 */
	file->i_unit = 0;
	while (*cp >= '0' && *cp <= '9')
		file->i_unit = file->i_unit * 10 + *cp++ - '0';
	/*
	 * No real reason for this limitation except arbitrary nastiness.
	 */
	if (file->i_unit < 0 || file->i_unit > 255) {
		if (sysdebug)
			printf("Bad ctlr/unit specifier\n");
		file->i_flgs = 0;
		errno = ENXIO;
		return (-1);
	}
	/*
	 * Obtain partition/offset number.
	 */
	if (*cp++ != ',') {
badoff:
		if (sysdebug)
			printf("Missing offset specification\n");
		file->i_flgs = 0;
		errno = ENXIO;
		return (-1);
	}
	/*
	 * We cleverly know that atol() stops when 
	 * it hits non-numeric characters (and doesn't return an error).
	 *
	 * Crock alert (disks): i_boff gets the partition number going into
	 * devopen(), but the device routines will replace it with the
	 * real block offset for the file system. Yicch.
	 */
	file->i_boff = atol(cp);
	/*
	 * Walk out to the right parenthesis.
	 */
	for (;;) {
		if (*cp == ')')
			break;
		if (*cp++)
			continue;
		goto badoff;
	}
	devopen(file); /* Open up the partition */
	if (*++cp == '\0') {
		file->i_flgs |= how+1; /* XXX */
		file->i_cc = 0;
		file->i_offset = 0;
		/*
		 * XXX: we know 0 isn't a decent inode # -- but it COULD be.
		 */
		return (0);
	}

	fs = openfs(file,str);
	file->i_fs = &fs->iof_fs;
	lastfs = fs->iof_dev;
	lastoff = file->i_boff;
	use_last = 1;
find_file:
	/*
	 * Now the super block is incore: find the inode for the file hisself.
	 *
	 */
	if ((i = find(cp, file, how, cmode)) == 0) {
		file->i_flgs = 0;
		errno = ESRCH;
		return (-1);	/* invalid inode # */
	} else
		return i;	/* righteous inode # */
}

/*
 * Openi -- open up inode number n, in file system described by io.
 * Put read-in inode information into inode structure *ip.
 *
 * Returns > 0 on success;  or < 0 on failure.
 *
 * Implicit Effects: scribbles on io->i_buf, bn, cc, offset, &c.
 *
 * Notes to myself: If a global "read inodes" buffer were used, we would
 * avoid stepping on i_buf.  (But we'd still be tromping on the other stuff).
 */
int
openi(n, io)
	register ino_t n;	/* inode number */
	register struct iob *io;
{
	register struct dinode *dp;
	register int cc, i;
	register struct fs *fs = io->i_fs;

	if (openfirst)
		for (cc=0; cc<ninodes; cc++)
			(ioinode[cc].ioi_inuse) = 0; /* Not in use */
	else
		/*
		 * Close the inode currently being used by this iob.
		 * (We're rolling over it anyways -- all inodes are
		 * opened up using this code...and every opened-up
		 * inode should be closed again.  It makes sense to catch
		 * all necessary closei's here, and the only inodes that aren't
		 * closed are those for files not closed before a given
		 * standalone utility exits...which is the programmer's
		 * fault.
		 *
		 * Note that closei is a no-op if the iob doesn't point at any
		 * inode.
		 *
		 * Notes to myself: we could catch and close ALL files
		 * when exit() is called.
		 */
		closei(io);
	/*
	 * Find an available inode.
	 */
	for (cc=0; cc<ninodes; cc++)
		if ((ioinode[cc].ioi_inode.i_number == n) &&
		    (ioinode[cc].ioi_inuse) &&
		    (ioinode[cc].ioi_inode.i_fs == fs))
			/*
			 * The inode for which we seek is already open.
			 */
			break;
	if (cc >= NFILES)
		for (cc=0; cc<NFILES; cc++)
		    if (! ioinode[cc].ioi_inuse) /* Found an unused inode. */
			break;
	
	if (cc >= NFILES)	/* Still:  no free inodes left! */
	{
		printf("Openi: no more free inodes left!\n");
		return(-1);
	}

	io->i_ino = &(ioinode[cc].ioi_inode);

	if (! ioinode[cc].ioi_inuse)
	{
		/*
		 * First time use of this inode: get data from disk.
		 */
#ifndef INODE_LIVES_IN_IOB
		io->i_ino->i_dev = io->i_dev;	/* index into devsw array. */
#endif  INODE_LIVES_IN_IOB
		io->i_bn = fsbtodb(fs, itod(fs, n)) + io->i_boff;
		io->i_cc = fs->fs_bsize;
		io->i_ma = io->i_buf;	/* Is this area always available? */
		i = devread(io);
		dp = (struct dinode *)io->i_buf;
		/*
		 * Fill in the necessary blanks. A slight waste of
		 * time if devread croaked.  But who cares.
		 */
		io->i_ino->i_ic = dp[itoo(fs, n)].di_ic;
		io->i_ino->i_number = n;
		io->i_ino->i_fs = fs;
	}

	ioinode[cc].ioi_inuse++;  /* One more file accessing this inode */
	return (cc);
}

int
closei(io)
	register struct iob *io;
{
	register int i, retval = 0;

	if (sysdebug>1)
		printf("closei(io=0x%x, ino=0x%x, dev=0x%x)\n",
			(int)io,io->i_ino ? io->i_ino->i_number : -1,
			io->i_ino ? io->i_ino->i_dev : -1);
	if (io->i_ino == NULL)	/* No inode to close. */
		return (0);

#ifdef	SA_ADVANCED
	if (iupdat(io, &time, &time) != 0) {
		if (sysdebug)
			printf("closei: iupdat() blew up!\n");
		retval = -1;
	}
#endif	SA_ADVANCED

	/*
	 * Find our inode.
	 */
	for (i=0; i<ninodes; i++)
		if (&(ioinode[i].ioi_inode) == io->i_ino)
			/*
			 * Found our inode.
			 */
			break;

	if (&(ioinode[i].ioi_inode) != io->i_ino)
		_stop("closei: couldn't find inode");
	if (--ioinode[i].ioi_inuse < 0)
		_stop("closei: inode accessed count is negative!");

	/* Clear inode pointer in iob. */
	io->i_ino = NULL;

	return(retval);
}
	

/*
 * Openfs -- open up the file system accessed by the given iob.
 * The superblock is put into a global area (iofs) and a pointer returned.
 *
 * Returns a valid fs pointer on success.  Returns NULL on error.
 *
 * Implicit Effects: Steps on io->i_bn, cc, offset, &c. (not i_buf)
 */
struct io_fs *
openfs(io,name)
register struct iob *io;
char *name;	/* name mounted on. (Standalone syntax) */
{
	register int i,j;
	register struct io_fs *file_systems = iofs;
	register struct fs *fs;
	register dev_t dev = makedev (io->i_dev, io->i_unit);
	int blks;
	caddr_t space;
	int size;

	/*
	 * Initialize fs structures if you must.
	 */

	if (openfirst)
		for (i=0; i<NIOFS; i++)
			(file_systems[i].iof_inuse) = 0; /* Not in use */
	else
	{
		/*
		 * Search for incore fs table for the superblock we will be
		 * using.  If it is there, we needn't read it in again.
		 */
		for (i=0; i<NIOFS; i++)
			if ((file_systems[i].iof_dev == dev)
				&& (file_systems[i].iof_inuse))
					break;
	}
 
	/*
	 * If we just initialized the fs structures (firstopen),
	 * then i == NIOFS+1.
	 */
	for (i=0; i<NIOFS; i++)
		if ((file_systems[i].iof_dev==dev) && 
			(file_systems[i].iof_inuse))
				break;
	if (i >= NIOFS)
	{
		/*
		 * Did not find an incore FS structure matching 
		 * our device/partition.
		 */
		for (i=0; i<NIOFS; i++)
			if (! file_systems[i].iof_inuse)
				break;
		if (i >= NIOFS)
		{
			printf("open: too many file systems open, max is %d.\n",
				NIOFS);
			return (NULL);
		}
		/*
		 * Grab the allocated file system structure.
		 */
		fs = (&file_systems[i].iof_fs);
		file_systems[i].iof_inuse++;
		file_systems[i].iof_dev   = dev;

		/* Multibus address: start of fs structure */
		io->i_ma = (char *)fs;
		io->i_cc = SBSIZE;
		io->i_bn = SBLOCK + io->i_boff;
		if (devread(io) < 0) {
			errno = io->i_error;
			if (sysdebug)
				printf("super block read error\n");
			return (NULL);
		}
		if (sysdebug>3) {
		printf ("fs_sblkno=0x%x, fs_cblkno=0x%x, fs_iblkno=0x%x,",
			fs->fs_sblkno, fs->fs_cblkno, fs->fs_iblkno);
		printf ("fs_dblkno=0x%x, fs_cgoffset=0x%x, fs_cgmask=0x%x\n",
			fs->fs_dblkno, fs->fs_cgoffset, fs->fs_cgmask);
		printf ("fs_size=0x%x, fs_dsize=0x%x, fs_ncg=0x%x,",
			fs->fs_size, fs->fs_dsize, fs->fs_ncg);
		printf ("fs_bsize=0x%x, fs_fsize=0x%x, fs_frag=0x%x\n",
			fs->fs_bsize, fs->fs_fsize, fs->fs_frag);
		printf ("fs_minfree=0x%x, fs_rotdelay=0x%x, fs_rps=0x%x,",
			fs->fs_minfree, fs->fs_rotdelay, fs->fs_rps);
		printf ("fs_bmask=0x%x, fs_fmask=0x%x, fs_bshift=0x%x\n",
			fs->fs_bmask, fs->fs_fmask, fs->fs_bshift);
		printf ("fs_fshift=0x%x, fs_maxcontig=0x%x, fs_maxbpg=0x%x,",
			fs->fs_fshift, fs->fs_maxcontig, fs->fs_maxbpg);
		printf ("fs_fragshift=0x%x, fs_fsbtodb=0x%x, fs_sbsize=0x%x\n",
			fs->fs_fragshift, fs->fs_fsbtodb, fs->fs_sbsize);
		printf ("fs_csmask=0x%x, fs_csshift=0x%x, fs_nindir=0x%x,",
			fs->fs_csmask, fs->fs_csshift, fs->fs_nindir);
		printf ("fs_inopb=0x%x, fs_nspf=0x%x, fs_csaddr=0x%x\n",
			fs->fs_inopb, fs->fs_nspf, fs->fs_csaddr);
		printf ("fs_cssize=0x%x, fs_cgsize=0x%x, fs_ntrak=0x%x,",
			fs->fs_cssize, fs->fs_cgsize, fs->fs_ntrak);
		printf ("fs_nsect=0x%x, fs_spc=0x%x, fs_ncyl=0x%x\n",
			fs->fs_nsect, fs->fs_spc, fs->fs_ncyl);
		printf ("fs_cpg=0x%x, fs_ipg=0x%x, fs_fpg=0x%x\n",
			fs->fs_cpg, fs->fs_ipg, fs->fs_fpg);
		}
		/* Copy in name for later diagnostics. */
		strcpy(file_systems[i].iof_fs.fs_fsmnt, name);
		/*
		 * blks is ``how many cyl. grp. summaries can you fit
		 * in a fragment?''
		 */
		blks = howmany(fs->fs_cssize, fs->fs_fsize);
		/*
		 * Allocate wired-down space tor cyl. grp. summary area.
		 */
		space = (caddr_t) malloc((int)fs->fs_cssize);
		if (space == 0) {
			if (sysdebug)
				printf("openfs: out of memory asking for %x bytes\n",
					fs->fs_cssize);
			return (NULL);
		}
		/*
		 * I is incremented by the number of fragments in a block of
		 * the file system.
		 */
		for (j = 0; j < blks; j += fs->fs_frag) {
			size = fs->fs_bsize; /* A block. */
			if (j + fs->fs_frag > blks)
				/*
				 * Final iteration of loop. Size to read is
				 * only what is left over at end of fs.
				 */
				size = (blks - j) * fs->fs_fsize;
			/*
			 * Read in cyl. grp. summary.
			 */
			io->i_ma = (char *)space;
			io->i_cc = size;
			io->i_bn = fsbtodb(fs, fs->fs_csaddr + j) + io->i_boff;
			if (devread(io) < 0) {
				errno = io->i_error;
				if (sysdebug)
					printf("super block read error\n");
				return (NULL);
			}
			fs->fs_csp[j / fs->fs_frag] = (struct csum *)space;
			space += size;
		}
		if (time.tv_sec < fs->fs_time)
			time.tv_sec = fs->fs_time;
	}
	return (&file_systems[i]);
}

#ifdef SA_ADVANCED
/*
 * Write the given fs structure (io->i_fs) out to disk.
 * This routine is routinely used to refresh superblock structures on disk
 * as we allocate new blocks, etcetera.
 *
 * Returns 0, or -1 on error.
 *
 * Implicitly: alters i_bn, cc, ma, offset &c.
 */
writefs(io)
register struct iob *io;
{
	register struct fs *fs = io->i_fs;
	int blks;
	caddr_t space;
	int i, size;

	/* Multibus address: start of fs structure */
	io->i_ma = (char *)(fs);
	io->i_cc = SBSIZE;
	io->i_bn = SBLOCK + io->i_boff;
#ifdef notdef
	if (sysdebug)
	{
		printf("going to devwrite super block:\n");
		printf("   io->i_flgs= 0x%x\n",io->i_flgs);
		printf("   io->i_unit= 0x%x\n",io->i_unit);
		printf("   io->i_boff= 0x%x\n",io->i_boff);
		printf("   io->i_cyloff= 0x%x\n",io->i_cyloff);
		printf("   io->i_bn= 0x%x\n",io->i_bn);
		printf("   io->i_ma= 0x%x\n",io->i_ma);
		printf("   io->i_cc= 0x%x\n",io->i_cc);
		printf("   io->i_error= 0x%x\n",io->i_error);
		printf("   io->i_errcnt= 0x%x\n",io->i_errcnt);
		printf("   io->i_errblk= 0x%x\n",io->i_errblk);
	}
#endif notdef
	if (devwrite(io) < 0) {
		errno = io->i_error;
		if (sysdebug)
			printf("super block write error\n");
		return (-1);
	}
	blks = howmany(fs->fs_cssize, fs->fs_fsize);
	space = (caddr_t)fs->fs_csp[0];
	for (i = 0; i < blks; i += fs->fs_frag) {
		size = fs->fs_bsize;
		if (i + fs->fs_frag > blks)
			size = (blks - i) * fs->fs_fsize;
		io->i_ma = (char *)(space);
		io->i_cc = size;
		io->i_bn = fsbtodb(fs, fs->fs_csaddr + i) + io->i_boff;
		space += size;
		if (devwrite(io) < 0) {
			errno = io->i_error;
			if (sysdebug)
				printf("super block write error\n");
			return (-1);
		}
	}
	return (0);
}
#endif SA_ADVANCED

/*
 * Find -- find a file on disk, given its UNIX pathname.
 *
 * PATH is a UNIX filename.
 * FILE is an already opened filesystem to look for the file name in.
 * FLAG is just that; it determines whether we are CREATING, LOOKING UP,
 * or DELETING the searched-for file, as well as whether to follow symlinks
 * or not.  FLAG values live in saio.h; unlike namei(), the flag is all a
 * bitmap.
 *
 * Returns inode number found on success, 0 on failure.
 *
 * Typical usage is to apply "find" to get an inode to diddle with,
 * then to use openi and buddies to mess up the file.
 *
 * Find() will allocate an inode if CREATE is specified in flag, using
 * creation mode mode. All blocks associated with that inode will be freed.
 *
 * NOTES to myself:
 *
 *	Openi() allocates an inode on the fly.  It needs to know, therefore,
 *	which device it is accessing.  Earlier, this unit # was computed in
 *	a moderately heinous fashion and stored in the appropriate iob's 
 *	i_ino structure, which was a struct inode, not a struct inode *.
 *	The solution is to incorporate a field in the iob structure, i_dev,
 *	which will store this information. Capiche?
 *
#ifndef INODE_LIVES_IN_IOB
 *	Done: io->i_dev and ancillary code to support it is in place.
#endif  INODE_LIVES_IN_IOB
 *
 */
int
find(filename, file, flag, mode)
	register char *filename;
	struct iob *file;
	int flag, mode;
{
	register int n = 0, db, len;
	char c, buffer[LITTLEBSIZE]; /* Workspace */
	register char *q, *path = buffer;
	struct iob last; /* last inode we read (parent) */
#ifdef SA_ADVANCED
	register struct inode *ip;	/* Used when trailing symlinks. */
#endif SA_ADVANCED

	if (sysdebug)
		printf("find.\n");

	if (filename==NULL || *filename=='\0') {
		if (sysdebug)
			printf("null path\n");
		return (0);
	}

	if ((len = strlen(filename)) > LITTLEBSIZE) {
		printf("Find: name \"%s\" too long to be believed.\n",filename);
		errno = ENAMETOOLONG;
		return (0);
	}
	strncpy(path,filename,len+1);  /* Put path into workspace */
	bclear((caddr_t)&last, sizeof(last)); /* Clear out parent inode */

	if (sysdebug>1)
		printf ("find (%s):\n", path);
	if (openi((ino_t) ROOTINO, file) < 0) {
		if (sysdebug)
			printf("root inode not found\n");
		/*
		 * Notes to myself:
		 *
		 * At some point the root inode has to be created.
		 * In UNIX, it must be at mkfs time, because you can mount a
		 * mkfs'd file system and cd right into it.
		 */
		return (0);
	}

	/*
	 * Walk down the path until it trickles out.
	 */
	while (*path) {
		/*
		 * Ignore extra "/"s
		 */
		while (*path == '/')
			path++;
		/*
		 * Walk 'q' out to the end of the path or another "/",
		 * whichever comes first.
		 */
		q = path;
		while(*q != '/' && *q != '\0')
			q++;
		/*
		 * Null-terminate that segment of path, remembering the
		 * original character in the string.
		 */
		c = *q;
		*q = '\0';

		/*
		 * Find the inode with name path (nulled out at position q).
		 */
		if ((n = dlook(path, file)) != 0) {
			if (c == '\0')
				break;
			/*
			 * Replace the inode we WERE using 
			 * with the next one down the path.
			 * Remember the parent inode.
			 */
			last = *file;
			if (openi(n, file) < 0)
				return (0);
			/*
			 * Repair the path string.
			 */
			*q = c;

#ifdef SA_ADVANCED
			/*
			 * Check for symbolic links.
			 */
			ip = file->i_ino;
			if (((ip->i_mode & IFMT) == IFLNK) && 
				(flag&FOLLOW_SYMLINK))
			{
				/* 
				 * Need to find out exactly which file this
				 * link is linked to. (...sigh...)
				 */
				if (ip->i_size + strlen(q) >= LITTLEBSIZE)
				{
					if (sysdebug)
						printf("Pathname too long.\n");
					errno = ENAMETOOLONG;
					return(0);
				}
				/* Move current pathname out of the way */
				ovbcopy(q,&buffer[ip->i_size+1],strlen(q));
				db = sbmap (file, 0, READ); /* block number 0 */
				if (! db)
				{
					if (sysdebug)
						printf("symlink corrupted\n");
					errno = ENXIO; /* Maybe misleading? */
					return (0);
				}
				file->i_bn = fsbtodb(file->i_fs,db) 
					+ file->i_boff;
				file->i_ma = file->i_buf;
				file->i_cc = blksize(file->i_fs, ip, 0);
				if (devread(file) < 0)
				{
					if (sysdebug)
						printf("Can't read link.\n");
					errno = file->i_error;
					return (0);
				}
				bcopy (file->i_buf, buffer, ip->i_size);
				/* Find symlink inode... */
				/*
				 * We have two cases here that are 
				 * actually fairly similar. If the
				 * symlink is a rooted pathname, we cat
				 * the rest of the path onto it, read in the 
				 * root inode, and continue.  If the symlink
				 * is relative to our position, then we read 
				 * in the parent directory inode, cat the rest 
				 * of the path onto the symlink name, 
				 * and continue.
				 */
				q = buffer;
				q[ip->i_size] = '/'; /* To fully cat names */
				if (q[0] == '/')
				{
					/* We're rooted again. */
					if (openi((ino_t) ROOTINO, file) < 0)
					{
					   if (sysdebug)
					     printf("can't read root inode\n");
					   return (0);
					}
				} else {
					/*
					 * Relative to where we are.
					 * Back off to parent inode.
					 */
					*file = last;
				}
			}
#endif SA_ADVANCED

			/*
			 * Move along to the next component of the path.
			 */
			path = q;
			continue;
		} else {
			/*
			 * I see no *path of ANY KIND!
			 */
			if (sysdebug==1)
				printf("%s not found\n", path);
			/*
			 * Repair the path string.
			 */
			*q = c;
			n = 0;	/* Assume a null return value */

			break;
		}
	}
#ifdef SA_ADVANCED
	if (*path) {
		/*
		 * Inode not found.
		 * Do we want to create an inode for the file?
		 */
		 if (n == 0 && (flag & O_CREAT)) {
			/*
			 * If we are at the end of the path,
			 * then we can create the file.
			 */
			for (q = path; (*q) && (*q != '/'); q++)
				; /* Nothing */
			if (*q) {
				/*
				 * Hit a '/' with something after it.
				 * All directories ancestral to
				 * new file must exist.  User loses.
				 */
				if (sysdebug)
					printf("find: no such directory %s.\n", filename);
			} else
				/*
				 * Try and create the inode.
				 */
				n = dcreate(path, file, flag, mode);
		} else if (flag & DELETE) {
			n = dirempty (path, file, n);
		}

	} 
#endif SA_ADVANCED
	return (n);
}

/*
 * Sbmap -- map a block number into a disk block by stumbling around in the
 * tender white underbelly of disk indirect blocks.
 *
 * IO is the inode/file/iob structure relating to the file you're dealing with.
 * BN is a block number RELATIVE TO THE START OF THE FILE.
 * RWFLG indicates READ or WRITE (so we know if we should allocate new blocks.
 *	legal values are READ or WRITE, from "saio.h".
 *
 * Warning:  the Surgeon General has determined that perusing heinous indirect
 * block code without the aid of Tequila or Night Train may be dangerous to
 * your mental balance and your near neighbors. We recommend Jose Cuervo in
 * the CAL-OSHA-approved "SLAMMER" format.  But seriously, folks.  Understand
 * the machinations of the UNIX (UNIX is a registered mumble gargle...)
 * file system before you try and decipher this beast.
 *
 * Returns 0 on failure, or the disk block number on success.
 *
 * see sys/ufs_bmap for the UNIX ancestor to this routine.
 *
 */
daddr_t
sbmap(ioin, bn, rwflg, size)
	struct iob *ioin;
	register daddr_t bn;
	int rwflg, size;
{
	register struct inode *ip;
	register int i, j;
	register daddr_t nb, *bap, blkno;
	int sh, pref, osize, nsize;
	register struct fs *fs = ioin->i_fs;
	daddr_t lbn;
	struct iob ibuf;
	register struct iob *io = &ibuf;

	if (sysdebug>1)
		printf("sbmap(0x%x, 0x%x, 0x%x, 0x%x)\n",ioin,bn,rwflg,size);

	/* Copy in file data to workspace. */
	bcopy(ioin, io, sizeof *ioin );

	ip = io->i_ino;
	if (bn < 0) {
		errno = EFBIG;
		if (sysdebug)
			printf("bn negative\n");
		return ((daddr_t)0);
	}

#ifdef SA_ADVANCED
	/*
	 * If this write will extend the file into a new block and the
	 * current last block is just a fragment of a whole block,
	 * then we must extend the current last block (fragment)
	 * to be a whole block (block).
	 */
	/* nb is calculated to be the lbn of the last block in file */
	if ((rwflg == WRITE) && ((nb = lblkno(fs, ip->i_size)) < NDADDR) && 
		(nb < bn))
	{
		if (sysdebug>1)
			printf("\textending last block...\n");
		/* Old size of last block. */
		osize = blksize(fs, ip, nb);
		/* If old size is some nonzero number of fragments... */
		if ((osize < fs->fs_bsize) && (osize > 0))
		{
			/*
			 * Reallocate the fragment to be an entire block.
			 */
			if ((blkno=realloccg(io, ip->i_db[nb],
				blkpref(ip,nb, (int)nb, &ip->i_db[0]),
				osize, (int)fs->fs_bsize)) < 0)
					return (-1);
#ifdef	LATER
			ip->i_size = (nb + 1) * fs->fs_bsize;
#endif	LATER
			ip->i_db[nb] = dbtofsb(fs,blkno);
			ip->i_flag |= IUPD|ICHG;
		}
	}
#endif SA_ADVANCED

	/*
	 * blocks 0..NDADDR are direct blocks.
	 */
	if(bn < NDADDR)
	{
		if (sysdebug>1)
			printf("\tbn < NDADDR...\n");
		nb = ip->i_db[bn];
		if (rwflg == READ)
			return (nb);	/* 0 if no block lives there. */
#ifdef SA_ADVANCED
		else /* writing */
		{
			if (sysdebug>1)
				printf("\twriting...\n");
			if ((nb == 0) || (ip->i_size < (bn + 1) * fs->fs_bsize))
			{
				if (nb != 0)
				{
					/* 
					 * Consider the need to 
					 * reallocate a fragment.
					 */
					osize = fragroundup(fs, 
						blkoff(fs, ip->i_size));
					nsize = fragroundup(fs, size);
					if (nsize <= osize)
						return(nb); /* Yates. */
					blkno = realloccg(io, nb,
					 blkpref(ip, bn, (int)bn, &ip->i_db[0]),
					 osize, nsize);
				}
				else
				{
					nsize = fragroundup(fs, size);
					blkno = sa_alloc(io,
					 blkpref(ip, bn, (int)bn, &ip->i_db[0]),
					 nsize);
				}
				if (blkno == -1)
					return (0);
				nb = dbtofsb(fs, blkno);
				ip->i_db[bn] = nb;
				ip->i_flag |= IUPD|ICHG;
			}
			return(nb);
		}
#endif SA_ADVANCED
	}

	/*
	 * addresses NIADDR have single and double indirect blocks.
	 * the first step is to determine how many levels of indirection.
	 */
	lbn = bn;	/* Save block number for later calculations. */
	sh = 1;
	bn -= NDADDR;
	for (j = NIADDR; j > 0; j--) {
		if (sysdebug>1)
			printf("\tbn > NDADDR...\n");
		/*
		 * NINDIR is the number of indirects in a block.
		 */
		sh *= NINDIR(io->i_fs);
		/*
		 * Is (bn) within the first {NINDIR, NINDIR-squared, ...}
		 * indirect blocks?
		 */
		if (bn < sh)
			/*
			 * Yes. Then j (actually NIADDR - j) will give us
			 * the number of the indirect block to read to find
			 * where block bn REALLY is.
			 *
			 * And (sh) represents the number of blocks 
			 * encompassed by this indirect block.
			 */
			break;
		/*
		 * Subtract that number from (bn) to prepare 
		 * for the next comparison.
		 */
		bn -= sh;
	}
	if (j == 0) {
		/*
		 * No indirect block can hold a number so big as bn.
		 * (Misleading error message: bn has been smallified 
		 * by the time we print it out.)
		 */
		errno = EFBIG;
		if (sysdebug)
			printf("bn = 0x%x is too large\n", bn);
		return ((daddr_t)0);
	}

	/*
	 * fetch the first indirect block address from the inode.
	 */
	nb = ip->i_ib[NIADDR - j];
	if (nb == 0)
	{
		if (rwflg == READ)
		{
			/*
			 * Bogus bn (for reading) because
			 * that indirect block does not exist.
			 */
			if (sysdebug)
				printf("sbmap: (no i-block %d) bn void 0x%x\n",
					NIADDR - j, bn);
			return ((daddr_t)0);
		}
#ifdef SA_ADVANCED
		/* Writing... */
		pref = blkpref(ip, lbn, 0, (daddr_t *)0);
		if ((blkno = sa_alloc(io, pref, (int)fs->fs_bsize)) < 0)
			return ((daddr_t)0);
		nb = dbtofsb(fs, blkno);
		ip->i_ib[NIADDR - j] = nb;
		blknos[j] = nb;
		bclear (b[j], io->i_fs->fs_bsize);
		ip->i_flag |= IUPD|ICHG;
#endif SA_ADVANCED
	}

	/*
	 * Fetch through the indirect blocks.
	 */
	/* For each indirect block... */
	for (; j <= NIADDR; j++) {
		/* ...if blknos[] doesn't already contain the block...   */
		/* (note: blknos[] is a map of what is incore (I think)) */
		if (blknos[j] != nb) {
			/* ...read in the block... */
			/* Disk block */
			io->i_bn = fsbtodb(io->i_fs, nb) + io->i_boff;
			/* 
			 * Read disk block directly into b[] 
			 * incore buffer area. 
			 */
			/* Multibus address */
			io->i_ma = b[j];
			/* Character count */
			io->i_cc = io->i_fs->fs_bsize;
			if (devread(io) != io->i_fs->fs_bsize) {
				/*
				 * Disk stomach ache... 
				 * we couldn't eat the whole thing.
				 */
				if (io->i_error)
					errno = io->i_error;
				if (sysdebug)
					printf("bn %D: read error\n", io->i_bn);
				return ((daddr_t)0);
			}
			/* ...and remember that the block is now incore. */
			blknos[j] = nb;
		}
		/* Point bap at the block itself. */
		bap = (daddr_t *)b[j];
		/* 
		 * Back (sh) off one level of magnitude, 
		 * making it smaller than (bn). 
		 */
		sh /= NINDIR(io->i_fs);
		/* 
		 * (bn / sh) gives us an offset into the indirect block.
		 * Mod NINDIR gives us a range guarantee. I think, 
		 * due to the mathematics used to compute bn and sh, 
		 * that the modular division is unnecessary, 
		 * but I wouldn't bet my life on it. To be determined.
		 */
		i = (bn / sh) % NINDIR(io->i_fs);
		/* Fetch next indirect block. */
		if(bap[i] == 0)
		{
			if (rwflg == READ)
			{
				if (sysdebug)
					printf("bn void %D\n",bn);
				return ((daddr_t)0);
			}
#ifdef SA_ADVANCED
			/* Writing, so allocate a block... */
			if (pref == 0)
				if (j < NIADDR)
					pref = blkpref(ip,lbn,0,(daddr_t*)0);
				else
					pref = blkpref(ip,lbn,i,&bap[0]);
			if ((blkno = sa_alloc(io,pref, (int)fs->fs_bsize)) < 0)
				return ((daddr_t)(0));
			bap[i] = dbtofsb(fs, blkno);
			io->i_bn = fsbtodb(io->i_fs, nb) + io->i_boff;
			/* Multibus address */
			io->i_ma = b[j];
			/* Character count */
			io->i_cc = io->i_fs->fs_bsize;
			if (devwrite(io) < 0) /* Refresh error. */
			{
				if (sysdebug)
					printf("sbmap: refresh error.\n");
				errno = io->i_error;
				return((daddr_t)0);
			}
#endif SA_ADVANCED
		}
		nb = bap[i];
	}
	return (nb);
}

/*
 * Dlook - look through a directory inode's blocks for filename s.
 *
 * Returns the inode number for s upon success. Returns 0 on failure.
 */
ino_t
dlook(s, io)
	char *s;
	register struct iob *io;
{
	register struct direct *dp;
	register struct inode *ip;
	struct dirstuff dirp;
	int len;

	if (sysdebug)
		printf("dlook(\"%s\")\n", s ? s : "NULL");
	if (s == NULL || *s == '\0')
		return (0);
	ip = io->i_ino;
	if ((ip->i_mode&IFMT) != IFDIR) {
		if (sysdebug)
			printf("not a directory\n");
		return (0);
	}
	if (ip->i_size == 0) {
		if (sysdebug)
			printf("zero length directory\n");
		return (0);
	}
	len = strlen(s);
	dirp.loc = 0;
	dirp.io = io;
	for (dp = readdir(&dirp,LOOKUP,0); dp != NULL;
	    dp = readdir(&dirp,LOOKUP,0)) {
		if (sysdebug==1)
			printf("r ");
		if(dp->d_ino == 0)
			continue;
#ifdef	SA_ADVANCED
		if (sysdebug==1)
#endif	SA_ADVANCED
		printf ("%d\t %s\n", dp->d_ino, dp->d_name);
		if (dp->d_namlen == len && !strncmp(s, dp->d_name, len)) {
			if (sysdebug>1)
				printf("\ndlook found, returning 0x%x\n",
					dp->d_ino);
			return (dp->d_ino);
		}
	}
	if (sysdebug==1)
		printf("\n");
	return (0);
}

#ifdef SA_ADVANCED
/*
 * Dcreate - create a new inode and righteous directory entry.
 *
 * Used in the initial creation of files.
 * Returns the number of the inode created, or 0 on failure.
 */
ino_t
dcreate(filename, io, how, mode)
	char *filename;	/* Name of file to create. */
	register struct iob *io;	/* file system to create it in. */
	int mode;		/* Mode of file being created.  */
{
	register struct direct *dp;
	register struct inode *ip;
	struct iob newiob;
	struct dirstuff dirp;
	int len,i;

	if (sysdebug)
		printf("dcreate(\"%s\")", filename ? filename : "NULL");
	if (filename == NULL || *filename == '\0')
		/*
		 * Nothing to create.
		 */
		return (0);
	ip = io->i_ino;	/* Current inode */
	if ((ip->i_mode&IFMT) != IFDIR) {
		if (sysdebug)
			printf("dcreate \"%s\": invalid directory\n",filename);
		return (0);
	}
	if (how & CREATE_LINK && ip->i_dev != ((struct inode *)mode)->i_dev) {
		if (sysdebug)
			printf ("dcreate \"%s\": cross device link\n", filename);
		return (0);
	}
	/*
	 * I believe that even an empty directory has a non-zero size.
	 */
	if (ip->i_size == 0) {
		if (sysdebug)
			printf("zero length directory\n");
		return (0);
	}
	len = strlen(filename);
	dirp.loc = 0;
	dirp.io = io;
	if ((dp = readdir(&dirp,CREATE,len +
	    (sizeof (struct direct) - (MAXNAMLEN+1)))) != NULL) {
    
		if (sysdebug>1)
			printf("r dp = {(ino)0x%x, (rlen)0x%x, (nlen)0x%x}\n",
				dp->d_ino, dp->d_reclen,dp->d_namlen);
		/*
		 * This dir struct, read in from the directory on disk,
		 * is either available, or has enough room for an extra entry.
		 */
		dirp.loc -= dp->d_reclen;
		i = dp->d_reclen;
		if (dp->d_ino != 0)
		{
			/*
			 * We're looking at a direct. struct in use, but 
			 * there is enough room at the end for our new 
			 * struct to be squeezed in.  Split the structure
			 * into two.
			 */
			dp->d_reclen = DIRSIZ(dp);
			dirp.loc += dp->d_reclen;
			i -= dp->d_reclen;
			dp = (struct direct *)(((caddr_t)(dp))+DIRSIZ(dp));
			dp->d_reclen = i;
		}
	} else {
		/*
		 * No space available in this directory.  We must allocate a 
		 * new block. All directories live in multiples of DIRBLKSIZ. 
		 *
		 * (Note: we should first try to compact the space in the 
		 * current block AT LEAST and see if we can squeeze out enough 
		 * bits that way, before we give up and snag another disk 
		 * block.  For later bored enhancers, ha ha.)
		 *
		 * Writing out the entry will automagically
		 * expand the file size, we hope.
		 */
		if (sysdebug>1)
			printf("\n");
		dirp.loc = howmany (ip->i_size, DIRBLKSIZ) * DIRBLKSIZ;
		dp = (struct direct *)(io->i_buf + blkoff (io->i_fs, dirp.loc));
		bclear (dp, DIRBLKSIZ);
		dp->d_reclen = DIRBLKSIZ;

		if (sysdebug>1)
			printf ("Expanded into new block dirp.loc = %d, i_size = %d\n",
			dirp.loc, ip->i_size);
	}
	/*
	 * make_node() will dereference ip and change it to the allocated
	 * inode #,  therefore we must save the directory inode pointer
	 */
	bclear (&newiob, sizeof newiob);
	newiob.i_flgs = io->i_flgs;
	newiob.i_unit = io->i_unit;
	newiob.i_dev = io->i_dev;
	newiob.i_boff = io->i_boff;
	newiob.i_fs = io->i_fs;

	/*
	 * Enter new directory entry:  first, come up with a new inode.
	 */
	if (how & CREATE_LINK) {
		dp->d_ino = ((struct inode *)mode)->i_number;
	} else {
		if ((dp->d_ino = maknode(&newiob, mode)) < 0) {
			if (sysdebug)
				printf("dcreate: can't snag inode.\n");
			return (0);
		}
		if (closei (&newiob)) {
			if (sysdebug)
				printf("dcreate: closei failed.\n");
			return (0);
		}
	}
	/*
	 * Fill in the other blanks; d_reclen has already been set.
	 */
	dp->d_namlen = len;
	strncpy(dp->d_name,filename,len+1);
	/*
	 * Update this block on disk.
	 */
	if (ino_write(io,dirp.loc + DIRSIZ(dp) - 1) < 0) {
		if (sysdebug)
			printf("dcreate: ino_write failed.\n");
		return(0);
	}
	if (ip->i_size < (dirp.loc + DIRSIZ(dp))) {
		ip->i_size = dirp.loc + DIRSIZ(dp);
		ip->i_flag |= ICHG;
		newiob.i_ino = ip;
		if (iupdat(&newiob, &time, &time) != 0) {
			if (sysdebug)
				printf("dcreate: iupdat() blew up!\n");
			return 0;
		}
		if (sysdebug>1)
			printf ("Expanding directory size to 0x%x\n",
				io->i_ino->i_size);
	}
	dirp.loc = 0;
	while ((dp = readdir(&dirp,LOOKUP,len)) != NULL) {
		if (len == dp->d_namlen && !strncmp (filename, dp->d_name, len))
			return(dp->d_ino);
	}
	_stop ("Can't find new entry\n");
	return (0);
}

/*
 * Dirempty - delete an inode and righteous directory entry.
 *
 * Returns -1, sets errno on failure
 */
dirempty (filename, io, ino)
	char *filename;	/* Name of file to create. */
	register struct iob *io;	/* file system to create it in. */
	register	ino;	/* Inode number of directory entry */
{
	register struct direct *dp;
	register struct inode *ip, *ipp = 0;
	struct iob newiob;
	struct dirstuff dirp;
	int len,i;

	if (sysdebug)
		printf("dirempty(\"%s\",ino=0x%x)",
			filename ? filename : "NULL", ino);
	if (filename == NULL || *filename == '\0')
		/*
		 * Nothing to delete.
		 */
		return (-1);
	if (ino == 0) /* Couldn't find it, doesn't exist */
		return -1;
	ip = io->i_ino;	/* Current inode */
	if ((ip->i_mode&IFMT) != IFDIR) {
		if (sysdebug)
			printf("dirempty \"%s\": invalid directory\n",filename);
		return (0);
	}
	/*
	 * I believe that even an empty directory has a non-zero size.
	 */
	if (ip->i_size == 0) {
		errno = ENXIO;
		if (sysdebug)
			printf("zero length directory\n");
		return (0);
	}
	len = strlen(filename);
	dirp.loc = 0;
	dirp.io = io;
	while ((dp = readdir(&dirp,LOOKUP,len)) != NULL) {
		if (sysdebug>1)
			printf("r\n");
		if (len == dp->d_namlen && !strncmp (filename, dp->d_name, len))
		{
			if (sysdebug>1)
				printf("readdir returned dp=0x%x, ino# 0x%x\n",
					dp,dp->d_ino);
			break;
		}
	}
	/*
	 * I am not sure, given the circumstances
	 * under which we call dirempty, that we will ever NOT find the
	 * desired filename.  But it is, of course, possible.
	 */
	if (!(len == dp->d_namlen && !strncmp (filename, dp->d_name, len)))
	{
		if (sysdebug)
			printf("dirempty: file \"%s\" not found.\n",
				filename);
		errno = ENOENT;	/* no such file or directory */
		return(-1);
	}
	/*
	 * After a successful sequence of readdirs, dirp.loc contains the
	 * offset to the NEXT directory structure to be read.
	 * We want the offset to THIS directory structure.
	 */
	dirp.loc -= dp->d_reclen;
	if (sysdebug>1)
		printf("dirempty: parent inode @0x%x, #0x%x\n",
			io->i_ino, io->i_ino->i_number);
	if (dp->d_ino != ino) {
		errno = ENXIO;
		if (sysdebug)
			printf ("dirempty: inodes #'s don't match (want)%d vs (read)%d\n",
				ino, dp->d_ino);
	}
	/* Scrub out parent directory entry, anyways. */
	dp->d_ino = 0;
	bclear (dp->d_name, dp->d_namlen);
	if (ino_write (io, dirp.loc) < 0) {	/* Write changed directory */
		if (sysdebug)
			printf ("dirempty: ino_write failed\n");
		return(-1);
	}
	/* Open file to be deleted. */
	if (openi (ino, io) < 0) {
		if (sysdebug)
			printf ("dirempty: openi failed\n");
		return -1;
	}
	ip = io->i_ino;	/* Inode of file being deleted. */
	if ((ip->i_mode&IFMT) == IFDIR) {
		errno = ENXIO;
		if (sysdebug)
			printf("dirempty \"%s\": can't delete directory\n",
				filename);
	}
	else
	{
		/*
		 * Note one less access link;
		 * however, if any at all are left don't blast file from disk.
		 */
		ip->i_nlink --;
		if (!(ip->i_nlink)) 
		{
			ip->i_mode = 0;	/* clear inode flags */
			itrunc (io, 0);
			ifree (io, ip->i_mode);
		}
		ip->i_flag |= ICHG;
	}
ret:
	closei (io);
	return -1;

}
#endif SA_ADVANCED

/*
 * Readdir - get next entry in a directory.
 *
 * Dirp points to some directory information.
 * Mode may be either LOOKUP, indicating to look for next nonempty directory 
 * entry, or CREATE, signifying to look for len free bytes (in which to put 
 * a new directory entry, we assume).
 *
 * Returns address of the right struct direct on success;
 * returns 0 on failure.
 *
 * If CREATE, the struct direct returned is the one which has at least 
 * len free bytes.  Nothing is modified in this routine.
 *
 * Notes to myself:
 *	Somewhere in here we loop infinitely. (080186 -- sorry to leave you
 *	on this note)
 */
struct direct *
readdir(dirp,mode,len)
	register struct dirstuff *dirp;
	int mode, len;
{
	register struct direct *dp;
	register struct iob *io;
	daddr_t lbn, d;
	int off;

	if (sysdebug)
		printf("readdir(dirp={0x%x,0x%x},mode=0x%x,len=0x%x)\n",
			dirp->loc,dirp->io,mode,len);

	io = dirp->io;
	for(;;)		/* Gee.  Good spot for an infinite loop... */
	{
		if (dirp->loc >= io->i_ino->i_size)
		{
			/* No more entries */
			if (sysdebug>1)
				printf("loc > i_size 0x%x\n",io->i_ino->i_size);
			/* loc shouldn't go GREATER than i_size!!! */
			if ((dirp->loc > io->i_ino->i_size) && (sysdebug))
				printf("readdir: loc 0x%x > i_size 0x%x\n",
					dirp->loc, io->i_ino->i_size);
			return (NULL);
		}
		off = blkoff(io->i_fs, dirp->loc);
		if (off == 0) {
			/* Need to read in this block */
			lbn = lblkno(io->i_fs, dirp->loc);
			d = sbmap(io, lbn, READ);
			if (sysdebug>1) printf("dlook: sbmap -> 0x%x ", d);
			if (d == 0)
			{
				/* 
				 * Error: there should be a block if the 
				 * directory says there's something there.
				 */
				if (sysdebug)
					printf("readdir: loc 0x%x (lbn 0x%x) unmapped\n", dirp->loc, lbn);
				return NULL;
			}
			/* Starting disk address */
			io->i_bn = fsbtodb(io->i_fs, d) + io->i_boff;
			if (sysdebug>1) printf("Mapped to phys block 0x%x\n",
				io->i_bn);
			/* Multibus address */
			io->i_ma = io->i_buf;
			/* Character count */
			io->i_cc = blksize(io->i_fs, io->i_ino, lbn);
			/* Read it. */
			if (devread(io) < 0) {
				errno = io->i_error;
				if (sysdebug)
					printf("bn 0x%x: read error\n", io->i_bn);
				return (NULL);
			}
		}
		/* Where the directory entry should be */
		dp = (struct direct *)(io->i_buf + off);
		/* Advance the pointer. */
		dirp->loc += dp->d_reclen;
				
		if (sysdebug)
			printf("Looking at entry= {0x%x, 0x%x, 0x%x, \"%s\"} ",
				dp->d_ino, dp->d_reclen, dp->d_namlen,
				dp->d_ino ? dp->d_name : "NULLL");
		if (dp->d_reclen == 0) {
			_stop("readdir: Corrupted directory\n");
		}
		switch(mode)
		{
			default:
				printf("Readdir: bad mode %d.\n",mode);
				return(NULL);
				break;
			case LOOKUP:
				if (dp->d_ino == 0)
					/* No inode home */
					continue;
				return(dp);
				break;
#ifdef SA_ADVANCED
			case CREATE:
				if (dp->d_ino == 0) {
					if (dp->d_reclen >= len)
						return(dp);
				} else {
					/*
					if (dp->d_reclen - DIRSIZ(dp) >= len)
					*/

					/* 
					 * Above will fail when dp->d_reclen
					 * equals exactly the length of the
					 * current directory entry + len
					 * since we need to len + 1 space
					 * to store NULL TERMINATED name
					 *
					 *  3 Feb 1987		BW
					 */
					if (dp->d_reclen - DIRSIZ(dp) > len)
						return(dp);
					else continue;
				}
				break;
#endif SA_ADVANCED
		}
	}
}

#ifdef SA_ADVANCED
/*
 * Ino_write:
 *
 * Write a block of the file system's block size onto the disk.
 * The information is located in dirp->io.i_buf.
 *
 * Returns 0 normally, and -1 on failure.
 *
 */
ino_write(io, loc_in_file)
	register struct iob *io;
	register int loc_in_file;
{
	register daddr_t lbn, d;
	register struct fs *fs = io->i_fs;
	register osize, size;

	lbn = lblkno(fs, loc_in_file);
	if (sysdebug)
		printf("ino_write: io 0x%x loc 0x%x -> lbn 0x%x (i_size 0x%x)\n",
			io, loc_in_file, lbn, io->i_ino->i_size);
	if (loc_in_file > io->i_ino->i_size) {
		osize = io->i_ino->i_size;
		io->i_ino->i_size = loc_in_file;
		size = blksize (fs, io->i_ino, lbn);
		io->i_ino->i_size = osize;
	} else
		size = blksize (fs, io->i_ino, lbn);
	/* Sbmap will allocate a new block if such is required. */
	d = sbmap(io, lbn, WRITE, size);
	if (d == 0)
	{
		/* sbmap failed. */
		if (sysdebug)
			printf("ino_write: loc 0x%x (lbn 0x%x) unmapped\n",
				loc_in_file, lbn);
		return (-1);
	}
	/* Starting disk address */
	io->i_bn = fsbtodb(fs, d) + io->i_boff;
	/* Multibus address */
	io->i_ma = io->i_buf;
	/* Character count */
	io->i_cc = size;
	/* Write the block. */
	if (devwrite(io) < 0) {
		errno = io->i_error;
		if (sysdebug)
			printf("bn 0x%x: write error\n", io->i_bn);
		return (-1);
	}
	return (0);
}
#endif SA_ADVANCED

/*
 * Fillbuf 
 *
 * Buffered read routine.
 *
 * fill the given buffer (*ptr) with (count) bytes from file (fdes).
 */
fillbuf(fdesc, ptr, count)
	int fdesc;
	char *ptr;
	int count;
{
	register struct iob *io;
	register struct fs *fs;
	register char *p;
	int c, lbn, off, size, diff;


	if (fdesc >= 0 && fdesc <= 2)
	{
		if (sysdebug)
			printf("Invalid parameters to fillbuf.\n");
		return(-1);
	}
	fdesc -= 3;
	if (fdesc < 0 || fdesc >= NFILES ||
	    ((io = &iob[fdesc])->i_flgs&F_ALLOC) == 0) {
		errno = EBADF;
		return (-1);
	}
	if (sysdebug)
		printf ("fillbuf: offset %x ptr %x count %x\n", io->i_offset,
		    ptr, count);
	p = io->i_ma;
	if (io->i_cc <= 0) {	/* Number of characters in core. */
		if ((io->i_flgs & F_FILE) != 0) {
			/*
			 * Real live file, Daddy.
			 */
			/*
			 * Bytes between R/W pointer and EOF
			 */
			diff = io->i_ino->i_size - io->i_offset;
			if (diff <= 0)
				/* No bytes to read. */
				return (-1);	/* should the (-1) be EOF? */
			fs = io->i_fs;
			lbn = lblkno(fs, io->i_offset);
			io->i_bn = fsbtodb(fs, sbmap(io, lbn, READ))
				+ io->i_boff;
	
			off = blkoff(fs, io->i_offset);
			size = blksize(fs, io->i_ino, lbn);
		} else {
			/*
			 * F_FILE implies the file is an opened one (has
			 * passed through the gauntlet of the open() call).
			 * When will this code be executed?
			 */
			io->i_bn = io->i_offset / DEV_BSIZE;
			off = 0;
			size = DEV_BSIZE;
		}
		/*
		 * Eh?
		 *
		 * if we're reading more bytes than the block size of the
		 * device, read directly into the user buffer (*ptr).
		 * Else read into the iobuf's own buffer struct, later
		 * to copy them bytes to the user's ptr.  Kind of readahead:
		 * read a block (whatever THAT is is thought out above with
		 * the "size = " code), at a time, then just dole out that
		 * block in smaller pieces if the user is reading little
		 * bits at a time.
		 */
		if (count >= size)
			io->i_ma = ptr;
		else
			io->i_ma = io->i_buf;
		io->i_cc = size;
		if (sysdebug>1)
			printf ("Reading block %x into addr %x, size %x\n", 
			    io->i_bn, io->i_ma, io->i_cc);
		if (devread(io) < 0) {
			errno = io->i_error;
			return (-1);
		}
		if ((io->i_flgs & F_FILE) != 0) {
			if (io->i_offset - off + size >= io->i_ino->i_size)
				io->i_cc = diff + off;
			io->i_cc -= off;
		}
		p = &io->i_buf[off];
		if (count < size)
		 {
			if (sysdebug>1)
				printf ("Copying %x bytes from %x to %x\n",
				    count, io->i_ma, ptr);
			bcopy (io->i_ma, ptr, count);
			io->i_cc -= count;
			io->i_ma += count;
			size = off = count;
		 }
		else
		 {
			off = size;
			io->i_cc = 0;
		 }
	}
	else
	 {
		if (count >= io->i_cc)
			count = io->i_cc;
		bcopy (io->i_ma, ptr, count);
		io->i_cc -= count;
		io->i_ma += count;
		size = off = count;
	 }
	io->i_offset += off;
	if(sysdebug>1)
		printf ("fillbuf returning %x\n", count >= size ? size : count);
	return count >= size ? size : count;
}

#ifdef SA_ADVANCED

/*
 * Writebuf
 *
 * Buffered write.
 * returns count of characters written, -1 on error.
 */
writebuf(fdesc, ptr, count)
	int fdesc;
	char *ptr;
	register int count;
{
	register struct iob *io;
	register struct fs *fs;
	int c, off, buffersize, osize, size, diff;
	register int lbn, nbytes;

	c = count;	/* Remember how much we were writing. */

	if (fdesc >= 0 && fdesc <= 2)
	{
		if (sysdebug)
			printf("Invalid parameters to writebuf.\n");
		return(-1);
	}
	fdesc -= 3;	/* To get the REAL file descriptor (what a crock)... */
	if (fdesc < 0 || fdesc >= NFILES ||
	    ((io = &iob[fdesc])->i_flgs&F_ALLOC) == 0) {
		errno = EBADF;
		return (-1);
	}
	if (sysdebug>2)
	{
		register i;
		printf ("writebuf: offset %x ptr %x count %x, 0x%x in buffer\n",
			io->i_offset, ptr, count, io->i_cc);
#define isprint(c)	(((c)>=' ') && ((c)<='~'))
		if (sysdebug>3) for (i=0; i < count; i++)
			printf("%x(%c) ", ptr[i],isprint(ptr[i]) ? ptr[i] : '\0');
		putchar('\n');
	}
	fs = io->i_fs;
	/*
	 * To be sure we are pointed in the right direction.
	 */
	io->i_ma = io->i_buf;
	/*
	 * Buffersize figuring is insufficient.
	 */
	buffersize = (fs->fs_bsize > MAXBSIZE) ? MAXBSIZE : fs->fs_bsize;
	while (count)
	{
		/*
		 * Clearing shouldn't make a difference -- but might.
		 */
		if (io->i_cc == 0)
			bclear (io->i_buf, buffersize);
		/*
		 * Transfer only as many bytes as are needed to make up a
		 * block of the "right" (I.E., file system) size.
		 */
		nbytes = (io->i_cc + count > buffersize) ? 
				(buffersize - io->i_cc) : (count);
		if (sysdebug>2)
			printf("bcopy(from=0x%x, to=0x%x, nbytes=0x%x)\n",
				ptr, io->i_ma+io->i_cc, nbytes);
		bcopy (ptr, io->i_ma + io->i_cc, nbytes);
		ptr += nbytes;
		io->i_cc += nbytes;
		io->i_offset += nbytes;
		count -= nbytes;
		/*
		 * io->i_cc is the count of buffered characters.
		 * If it is going to go over the blk size of the file system,
		 * then we write.
		 */
		if (io->i_cc == buffersize) {	/* Time to write. */
			if (sysdebug>1) printf("Time to write...");
			if ((io->i_flgs & F_FILE) != 0) {
				/*
				* Real live file, Daddy.
				*/
				lbn = lblkno(fs, io->i_offset - io->i_cc);
				osize = io->i_ino->i_size;
				if (io->i_offset > io->i_ino->i_size)
					io->i_ino->i_size = io->i_offset;
				size = blksize(fs, io->i_ino, lbn);
				io->i_ino->i_size = osize;
				io->i_bn = fsbtodb(fs, sbmap(io, lbn, WRITE, 
					size)) + io->i_boff;
			} else {
				/*
				 * ``Special file''.  I don't think we want to
				 * even go through buffering in this case.
				 */
				io->i_bn = io->i_offset / DEV_BSIZE;
				/* offset w/in current block */
				off = 0;
				size = DEV_BSIZE;
			}
			if (sysdebug>1)
				printf ("Write blk %x from addr %x, size %x\n", 
					io->i_bn, io->i_ma, io->i_cc);
			io->i_ma = io->i_buf;
			if (devwrite(io) < 0) {
				errno = io->i_error;
				return (-1);
			}
			if (iupdat(io, &time, &time) != 0) {
				if (sysdebug)
					printf("writebuf: iupdat() blew up!\n");
				return 0;
			}
			io->i_cc = 0;
		}
		else if (io->i_cc > buffersize)
		{
			/* Should never occur. */
			printf("writebuf explosion: i_cc > buffersize\n");
			printf("i_cc= 0x%x, i_bn= 0x%x, buffersize= 0x%x, i_offset= 0x%x\n",
				io->i_cc,io->i_bn,buffersize,io->i_offset);
			return (-1);
		}
		if (io->i_offset > io->i_ino->i_size) {
			io->i_ino->i_size = io->i_offset;
			io->i_ino->i_flag |= ICHG;
		}
	}
	return (c);
}

/*
 * Closebuf
 *
 * Close a read/write buffered file.
 * Flushes buffer is file is open for writing; no problem if file is 
 * open for reading only.
 *
 * Returns 0 normally; sets errno and returns -1 on error.
 *
 * Caution! Written on the fly.  Probably deadly.
 */
closebuf(fdesc)
	int fdesc;
{
	register struct iob *io;
	register struct fs *fs;
	int count, off, buffersize, size, diff;
	register int lbn, nbytes;

	if (fdesc >= 0 && fdesc <= 2)
	{
		if (sysdebug)
			printf("Invalid parameters to closebuf.\n");
		return(-1);
	}
	fdesc -= 3;	/* To get the REAL file descriptor (what a crock)... */
	if (fdesc < 0 || fdesc >= NFILES ||
	    ((io = &iob[fdesc])->i_flgs&F_ALLOC) == 0) {
		errno = EBADF;
		return (-1);
	}
	if (((io->i_flgs & F_FILE) == 0) || ((io->i_flgs & F_WRITE) == 0))
	{
		/*
		 * Special files and files not open for writing
		 * need no flushing.
		 */
		return(0);
	}

	if (sysdebug>1)
		printf ("closebuf: offset %x, 0x%x in buffer\n",
			io->i_offset, io->i_cc);
	if (io->i_cc == 0)	/* Nothing to flush */
		return (0);
	fs = io->i_fs;
	count = io->i_cc;	/* Remember character count. */
	io->i_ma = io->i_buf;
	/*
	 * Buffersize figuring is insufficient.
	 */
	buffersize = (fs->fs_bsize > MAXBSIZE) ? MAXBSIZE : fs->fs_bsize;
	/*
	 * io->i_cc is the count of buffered characters.
	 * We want to write them all out.
	 */
	lbn = lblkno(fs, io->i_offset - io->i_cc);
	io->i_bn = fsbtodb(fs, sbmap(io, lbn, WRITE, 
		blksize(fs, io->i_ino, lbn))) + io->i_boff;
	/* offset w/in current block (SB zero). */
	off = blkoff(fs, io->i_offset);
	size = blksize(fs, io->i_ino, lbn);
	io->i_cc = size;
	if (sysdebug>1)
	{
		register i;
		printf ("Write blk 0x%x from addr 0x%x, size 0x%x, to i_offset 0x%x\n", io->i_bn, io->i_ma, io->i_cc, io->i_offset);
		if (sysdebug>3) for (i=0; i<io->i_cc; i++)
			printf("%x(%c) ", io->i_buf[i],isprint(io->i_buf[i]) ? io->i_buf[i] : '\0');
		putchar('\n');
	}
	if (devwrite(io) < 0) {
		errno = io->i_error;
		return (-1);
	}
	return 0;
}

/*
 * Ino_stat - copy stuff from a struct inode to a struct stat.
 * Used for the stat() family of system calls.
 *
 * Always returns 0.
 */
int
ino_stat(ip, sb)
	register struct inode *ip;
	register struct stat *sb;
{
	/*
	 * Copy from inode table.
	 */
	sb->st_dev = ip->i_dev;
	sb->st_ino = ip->i_number;
	sb->st_mode = ip->i_mode;
	sb->st_nlink = ip->i_nlink;
	sb->st_uid = ip->i_uid;
	sb->st_gid = ip->i_gid;
	sb->st_rdev = (dev_t)ip->i_rdev;
	sb->st_size = ip->i_size;
	sb->st_atime = ip->i_atime;
	sb->st_spare1 = 0;
	sb->st_mtime = ip->i_mtime;
	sb->st_spare2 = 0;
	sb->st_ctime = ip->i_ctime;
	sb->st_spare3 = 0;
	if ((ip->i_mode&IFMT) == IFBLK) /* Block special file */
		sb->st_blksize = BLKDEV_IOSIZE;
	else if ((ip->i_mode&IFMT) == IFCHR) /* Character special file */
		sb->st_blksize = MAXBSIZE;
	else	/* Ordinary file */
		sb->st_blksize = ip->i_fs->fs_bsize;
	sb->st_blocks = ip->i_blocks;
	sb->st_spare4[0] = sb->st_spare4[1] = 0;
	return (0);
}

#endif SA_ADVANCED
