/*	$NetBSD: sys.c,v 1.14 1996/06/18 06:10:35 mycroft Exp $	*/

/*
 * Ported to boot 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
 *
 * Mach Operating System
 * Copyright (c) 1992, 1991 Carnegie Mellon University
 * All Rights Reserved.
 * 
 * Permission to use, copy, modify and distribute this software and its
 * documentation is hereby granted, provided that both the copyright
 * notice and this permission notice appear in all copies of the
 * software, derivative works or modified versions, and any portions
 * thereof, and that both notices appear in supporting documentation.
 * 
 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
 * 
 * Carnegie Mellon requests users of this software to return to
 * 
 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
 *  School of Computer Science
 *  Carnegie Mellon University
 *  Pittsburgh PA 15213-3890
 * 
 * any improvements or extensions that they make and grant Carnegie Mellon
 * the rights to redistribute these changes.
 */

#include "boot.h"
#include <sys/dirent.h>
#include <sys/reboot.h>

#undef MAXBSIZE
#define	MAXBSIZE	16384
char mapbuf[MAXBSIZE], iobuf[MAXBSIZE], fsbuf[SBSIZE];
int mapblock = 0;
char pathname[MAXPATHLEN + 1];

void bcopy(), pcpy();

read(buffer, count)
	char *buffer;
	int count;
{
	_read(buffer, count, bcopy);
}

xread(buffer, count)
	char *buffer;
	int count;
{
	_read(buffer, count, pcpy);
}

_read(buffer, count, copy)
	char *buffer;
	int count;
	void (*copy)();
{
	int logno, off, size;
	int cnt2;

	while (count) {
		off = blkoff(fs, poff);
		logno = lblkno(fs, poff);
		cnt2 = size = blksize(fs, &inode, logno);
		bnum = fsbtodb(fs, block_map(logno)) + boff;
		cnt = cnt2;
		iodest = iobuf;
		devread();
		size -= off;
		if (size > count)
			size = count;
		copy(iodest + off, buffer, size);
		buffer += size;
		count -= size;
		poff += size;
	}
}

find(path)
	char *path;
{
	char *rest, ch;
	int block, off, loc, ino = ROOTINO, parent;
	struct dirent *dp;
	int nlinks = 0;
	
loop:
	iodest = iobuf;
	cnt = fs->fs_bsize;
	bnum = fsbtodb(fs, ino_to_fsba(fs,ino)) + boff;
	devread();
	bcopy(&((struct dinode *)iodest)[ino_to_fsbo(fs,ino)],
	      &inode.i_din,
	      sizeof(struct dinode));
	if ((inode.i_mode & IFMT) == IFLNK) {
		int link_len = inode.i_size;
		int len = strlen(path);
		
		if (link_len + len > MAXPATHLEN ||
		    ++ nlinks > MAXSYMLINKS)
			return 0;
		bcopy(path, &pathname[link_len], len + 1);
		if (link_len < fs->fs_maxsymlinklen)
			bcopy(inode.i_shortlink, pathname, link_len);
		else {
			poff = 0;
			read(pathname,link_len);
		}
		path = pathname;
		if (*pathname == '/')
			ino = ROOTINO;
		else
			ino = parent;
		goto loop;
	}
	if (!*path)
		return 1;
	while (*path == '/')
		path++;
	if (!inode.i_size || ((inode.i_mode & IFMT) != IFDIR))
		return 0;
	parent = ino;
	for (rest = path; (ch = *rest) && ch != '/'; rest++);
	*rest = 0;
	loc = 0;
	do {
		if (loc >= inode.i_size)
			return 0;
		if (!(off = blkoff(fs, loc))) {
			int cnt2;
			block = lblkno(fs, loc);
			cnt2 = blksize(fs, &inode, block);
			bnum = fsbtodb(fs, block_map(block)) + boff;
			cnt = cnt2;
			iodest = iobuf;
			devread();
		}
		dp = (struct dirent *)(iodest + off);
		if (dp->d_reclen < 8) {
			printf("directory corrupted (possible geometry mismatch)\n");
			return 0;
		}
		loc += dp->d_reclen;
	} while (!dp->d_fileno || strcmp(path, dp->d_name));
	ino = dp->d_fileno;
	*(path = rest) = ch;
	goto loop;
}

block_map(file_block)
	int file_block;
{
	if (file_block < NDADDR)
		return(inode.i_db[file_block]);
	if ((bnum = fsbtodb(fs, inode.i_ib[0]) + boff) != mapblock) {
		iodest = mapbuf;
		cnt = fs->fs_bsize;
		devread();
		mapblock = bnum;
	}
	return (((int *)mapbuf)[(file_block - NDADDR) % NINDIR(fs)]);
}

openrd()
{
	char **devp, *cp = name;
	/*******************************************************\
	* If bracket given look for preceding device name	*
	\*******************************************************/
	while (*cp && *cp!='(')
		cp++;
	if (!*cp) {
		cp = name;
	} else {
		if (cp != name) {
			*cp = '\0';
			for (devp = devs; *devp; devp++)
				if (!strcmp(name, *devp))
					break;
			if (!*devp) {
				printf("Unknown device\n");
				return 1;
			}
			maj = devp-devs;
		}
		/*******************************************************\
		* Look inside brackets for unit number, and partition	*
		\*******************************************************/
		cp++;
		if (*cp >= '0' && *cp <= '9')
			if ((unit = *cp++ - '0') > 1) {
				printf("Bad unit\n");
				return 1;
			}
		if (!*cp || (*cp == ',' && !*++cp))
			return 1;
		if (*cp >= 'a' && *cp <= 'p')
			part = *cp++ - 'a';
		while (*cp && *cp++!=')') ;
		if (!*cp)
			return 1;
	}
	if (maj == 1) {
		dosdev = unit | 0x80;
		unit = 0;
	} else if (maj == 0 || maj == 4)
		dosdev = unit | 0x80;
	else if (maj == 2)
		dosdev = unit;
	inode.i_dev = dosdev;
	/***********************************************\
	* Now we know the disk unit and part,		*
	* Load disk info, (open the device)		*
	\***********************************************/
	if (devopen())
		return 1;

	/***********************************************\
	* Load Filesystem info (mount the device)	*
	\***********************************************/
	iodest = (char *)(fs = (struct fs *)fsbuf);
	cnt = SBSIZE;
	bnum = SBLOCK + boff;
	devread();

	/*
	 * Deal with old file system format.  This is borrowed from
	 * ffs_oldfscompat() in ufs/ffs/ffs_vfsops.c.
	 */
	if (fs->fs_inodefmt < FS_44INODEFMT) {			/* XXX */
		fs->fs_qbmask = ~fs->fs_bmask;			/* XXX */
		fs->fs_qfmask = ~fs->fs_fmask;			/* XXX */
	}							/* XXX */

	/***********************************************\
	* Find the actual FILE on the mounted device	*
	\***********************************************/
	if (!find(cp))
		return 1;

	poff = 0;
	name = cp;
	return 0;
}
