Coherent4.2.10/coh.386/sys5.c

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

/* $Header: /ker/coh.386/RCS/sys5.c,v 2.6 93/10/29 00:55:50 nigel Exp Locker: nigel $ */
/*
 * System calls introduced when going from COH 286 to COH 386.
 * $Log:	sys5.c,v $
 * Revision 2.6  93/10/29  00:55:50  nigel
 * R98 (aka 4.2 Beta) prior to removing System Global memory
 * 
 * Revision 2.5  93/08/19  03:26:55  nigel
 * Nigel's r83 (Stylistic cleanup)
 */

#include <kernel/proc_lib.h>
#include <sys/errno.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <ulimit.h>
#include <dirent.h>
#include <fcntl.h>
#include <stddef.h>
#include <limits.h>

#define	_KERNEL		1

#include <kernel/alloc.h>
#include <kernel/trace.h>
#include <kernel/reg.h>
#include <sys/uproc.h>
#include <sys/proc.h>
#include <sys/mmu.h>
#include <sys/buf.h>
#include <canon.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/utsname.h>
#include <sys/mount.h>
#include <ustat.h>
#include <sys/statfs.h>
#include <sys/sysi86.h>


time_t
utime ()
{
	return timer.t_time;
}


/*
 * Return an unique number.
 */

int
usysi86 (f, arg1)
int f;
int arg1;
{
	MOUNT *mp;
	struct filsys *fsp;
	extern void (*ndpEmFn)();
	int fpval;
	extern short ndpType;

	switch (f) {
	case SYI86UNEEK:
		if ((mp = getment (rootdev, 1)) == NULL)
			return -1;
		fsp = & mp->m_super;
		fsp->s_fmod = 1;
		return ++ fsp->s_unique;

	case SI86FPHW:
		/* 
		 * 2's bit: floating point ndp is present (80287/80387/80486dx)
		 * 1's bit (when 2's bit = 1): 80387/486dx is present
		 */

		if (! useracc (arg1, sizeof (int), 1)) {
			SET_U_ERROR (EFAULT, "sysi386:SI86FPHW");
			return -1;
		}
		if (ndpType <= 1) { /* no ndp */
			fpval = (ndpEmFn) ? FP_SW : FP_NO;
		} else
			fpval = (ndpType > 2) ? FP_387 : FP_287;

		putuwd (arg1, fpval);
		return 0;
	}

	set_user_error (EINVAL);
	return -1;
}


/*
 * sys_unknown - write name unknown to utsname struct
 */

#if	__USE_PROTO__
__LOCAL__ int sys_unknown (struct utsname * name)
#else
__LOCAL__ int
sys_unknown (name)
struct utsname	*name;
#endif
{
static	char	unknown[] = "UNKNOWN";

	if (! kucopy (unknown, name->sysname, sizeof (unknown)))
		return -1;
	if (! kucopy (unknown, name->nodename, sizeof (unknown)))
		return -1;
	return 0;
}


/*
 * uname - get name of the current operating system.
 */

extern char	version[];	/* Defined in main.c */
extern char 	release[];	/* Defined in main.c */

int
uname(name)
struct utsname	*name;
{
	register char	*rcp;			/**/
	register int	i;			/* Counter, loop index */
	BUF		*bp;			/* Read buffer */
	char		namebuf[SYS_NMLN];	/* System name */
	int		fl;			/* File length*/

	/* Check if *name is an available user area */
	if (! useracc ((char *) name, sizeof (struct utsname), 1)) {
		set_user_error (EFAULT);
		return -1;
	}
	/* Find the size of the version number */
	for (rcp = version, i = 0; *rcp != 0 && i < SYS_NMLN; i++, rcp++)
		/* DO NOTHING */ ;

	/* Write version number to user area */
	if (! kucopy (version, name->version, i))
		return -1;

	/* Find the size of the release number */
	for (rcp = release, i = 0 ; * rcp != 0 && i < SYS_NMLN ;
	     i ++, rcp ++)
		/* DO NOTHING */ ;

	/* Write release number to user area */
	if (! kucopy (release, name->release, i))
		return -1;

	/* Write "machine" to user area */
	if (! kucopy ("i386", name->machine, 4))
		return -1;

	/*
	 * We supposed that system name and nodename are in /etc/uucpname
	 * NIGEL: Set the global io segment to IOSYS so that ftoi () will use
	 * the right method of getting at the argument passed to it.
	 */

	{
		struct direct	dir;

		if (ftoi ("/etc/uucpname", 'r', IOSYS, NULL, & dir,
			  SELF->p_credp) != 0)
			return sys_unknown (name);
	}

	if ((fl = u.u_cdiri->i_size) == 0) {
		idetach (u.u_cdiri);
		return sys_unknown (name);
	}

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

	/* namebuf should be not more than SYS_NMLN - 1 characters long */
	fl = fl > SYS_NMLN ? SYS_NMLN : fl;
	memcpy (namebuf, bp->b_vaddr, fl);

	brelease (bp);
	idetach (u.u_cdiri);

	if (fl == 1 && namebuf [0] == '\n')
		return sys_unknown (name);

	for (rcp = namebuf, i = 0 ; i < fl ; rcp ++) {
		i ++;
		if (* rcp == '\n') {
			* rcp = 0;
			break;
		}
	}
	namebuf[i - 1] = 0;

	/* Write system name to user area */
	if (! kucopy (namebuf, name->sysname, i))
		return -1;

	/* Write system name to user area */
	if (! kucopy (namebuf, name->nodename, i))
		return -1;
	return 0;
}


/* 
 * u_ustat - get file system statistics. (Name ustat in use for stat s.c.)
 */

int
u_ustat(dev, buf)
dev_t	dev;
struct ustat	*buf;
{
	MOUNT	*mp;

	/* Check if buf is an available user area. */
	/* B_READ | B_WRITE is not implemented yet. */
	if (! useracc ((char *) buf, sizeof (struct ustat), 1)) {
		set_user_error (EFAULT);
		return -1;
	}

	/* Take mount filesystem, check if dev is mounted device */
	for (mp = mountp ; mp != NULL ; mp = mp->m_next)
		if (mp->m_dev == dev)
			break;

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

	/* Pickup information from superblock */
	/* Number of free blocks */
	if (! kucopy (& mp->m_super.s_tfree, & buf->f_tfree,
		     sizeof (mp->m_super.s_tfree)))
		return -1;

	/* Number of free inodes */
	if (! kucopy (& mp->m_super.s_tinode, & buf->f_tinode,
		      sizeof (mp->m_super.s_tinode)))
		return -1;

	/* File system name */
	if (! kucopy (mp->m_super.s_fname, buf->f_fname,  
		      sizeof (mp->m_super.s_fname)))
		return -1;

	/* File system pack name */
	if (! kucopy (mp->m_super.s_fpack, buf->f_fpack,  
		      sizeof (mp->m_super.s_fpack)))
		return -1;
	return 0;
}


/*
 * uname and ustat system calls.
 *
 * int uname(struct utsname *name)
 * Before lcall instruction 4(%esp) contains name, 8(%esp) contains
 * an unspecified value, and 12(%esp) contains the value 0.
 *
 * int ustat(int dev, struct ustat *buf)
 * Before lcall instruction 4(%esp) contains buf (REVERSE order of argument)
 * 8(%esp) contains dev, and 12(%esp) contains the value 2.
 */

int
uutssys (arg1, arg2, func)
caddr_t		arg1;
caddr_t		arg2;
int		func;
{
	switch (func) {	
	case 0: return uname ((struct utsname *) arg1);

	case 2: return u_ustat (arg2, arg1);

	default:
		set_user_error (EINVAL);
		return -1;
	}
}


/* Don't tell user process about last remaining 64k of RAM */
#define BRK_CUSHION	16

unsigned long
uulimit (cmd, newlimit)
int cmd;
unsigned long newlimit;
{
	int freeClicks;

	switch (cmd) {
	case UL_GETFSIZE:	/* Get max # of 512-byte blocks per file. */
		return u.u_bpfmax;
		break;

	case UL_SETFSIZE: /* Set max # of 512-byte blocks per file. */
		/* (only superuser may increase this) */
		if (newlimit <= u.u_bpfmax || super ()) {
			u.u_bpfmax = newlimit;
			return 0;
		}
		/* else super() will have set_user_error () to EPERM */
		break;

	case UL_GMEMLIM: /* Get max break value. */
		/* return (current brk value) + (amount of free space) */
		/* Don't report all free clicks - leave a cushion. */
		freeClicks = allocno () - BRK_CUSHION;
		if (freeClicks < 0)
			freeClicks = 0;
		return (unsigned long)
			(SELF->p_segl [SIPDATA].sr_base +
			 SELF->p_segl [SIPDATA].sr_segp->s_size + NBPC * freeClicks);

	case UL_GDESLIM:
		/* Return configured number of open files per process. */
		return NOFILE;
		break;

	default:
		set_user_error (EINVAL);
	}

	return -1UL;
}


/*
 *  Change the size of a file.
 */

int
uchsize (fd, size)
int fd;
long size;
{
	__fd_t	      *	fdp;
	INODE	      *	ip;

	if (size < 0) {
		set_user_error (EINVAL);
		return -1;
	}
	if ((fdp = fd_get (fd)) == NULL || (fdp->f_flag & IPW) == 0) {
		set_user_error (EBADF);
		return -1;
	}

	ip = fdp->f_ip;
	switch (ip->i_mode & IFMT) {
	case IFREG:
		if (size > (long) u.u_bpfmax * BSIZE) {
			set_user_error (EFBIG);
			return -1;
		}
		if (size == ip->i_size)
			break;
		ilock (ip, "uchsize () IFREG");
		if (size < ip->i_size)
			blclear (ip, __DIVIDE_ROUNDUP (ip->i_size, BSIZE));
		ip->i_size = size;
		imodcreat (ip);
		iunlock (ip);
		break;

	case IFPIPE:
		if (size > PIPE_MAX) {
			set_user_error (EFBIG);
			return -1;
		}

		ilock (ip, "uchsize () IFPIPE");

		if (! ip->i_par && ! ip->i_psr) {
			set_user_error (EPIPE);
			sendsig (SIGPIPE, SELF);
			iunlock (ip);
			return -1;
		}
		ip->i_pwx += (size - ip->i_pnc);
		if (size > ip->i_pnc) {
			if (ip->i_pwx >= PIPE_MAX)
				ip->i_pwx -= PIPE_MAX;
		} else if (size < ip->i_pnc) {
			if (ip->i_pwx < 0)
				ip->i_pwx += PIPE_MAX;
		}

		ip->i_pnc = size;
		imodcreat (ip);

		if (size > 0)
			pwake (ip, 2);	/* 2==IFWFW, see pipe.c	*/
		if (size < PIPE_MAX)
			pwake (ip, 1);	/* 1==IFWFR, see pipe.c	*/
		iunlock (ip);
		break;

	default:
		set_user_error (EBADF);
		return -1;
	}
	return 0;
}



/*
 * This is a copy of iucheck. The only one difference is that that allows
 * to remove a directory to a regular user.
 */

int
diucheck (dev, ino)
o_dev_t dev;
o_ino_t ino;
{
	if (inode_find (dev, ino, NULL) == NULL) {
		struct inode *ip;
		struct inode inode;

		ip = & inode;
		ip->i_dev = dev;
		ip->i_ino = ino;
		if (icopydm (ip) == 0)
			return 0;
	}
	return 1;
}


/*
 * Unlink the given directory.
 */

void
dunlink (np)
char *np;
{
	INODE	      *	ip;
	dev_t dev;
	IO		io;
	struct direct	dir;

	if (file_to_inode (np, 'u', 1, IOSYS, & io, & dir, SELF->p_credp) != 0)
		return;
 
	if (iaccess (u.u_pdiri, IPW, SELF->p_credp) == 0) {
		idetach (u.u_pdiri);
		return;
	}

	dev = u.u_pdiri->i_dev;
	if (diucheck (dev, u.u_cdirn) == 0) {
		idetach (u.u_pdiri);
		return;
	}

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

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

	if (ip->i_nlink > 0)
		-- ip->i_nlink;

	icreated (ip);	/* unlink - ctime */
	idetach (ip);
	return;
}


/*
 * Remove a directory.
 * path is a pointer to user area.
 */

void
removedir(path, iPathLen)
char	*path;	/* Directory name */
int	iPathLen;	/* Path length */
{
	char		*buf;
	char		*cpbuf,		/* internal file_name buffer */
			*cppath;	/* user file_name buffer */

	/*
	 * Allocate kernel buffer. We need extra space for '/', '.', '..' 
	 * and 0
	 */

	if ((buf = kalloc (iPathLen + 4)) == NULL) {
		SET_U_ERROR (ENOSPC, "rmdir: out of kernel space");
		return;
	}
	cpbuf = buf;
	cppath = path;

	/* Copy path to the kernel buffer. */
	while ((* cpbuf = getubd (cppath)) != 0) {
		cppath ++;
		cpbuf ++;
	}
	* cpbuf++ = '/';
	* cpbuf++ = '.';
	* cpbuf = 0;

	dunlink (buf);
	if (get_user_error ()) {
		kfree (buf);
		return;
	}

	* cpbuf ++ = '.';
	* cpbuf = 0;

	dunlink (buf);
	if (get_user_error ()) {
		/* We have to link '.' back here. */
		kfree (buf);
		return;
	}

	buf [iPathLen] = 0;

	dunlink (buf);
	if (get_user_error ()) {
		/* We have to link '.' and '..' back here. */
		kfree (buf);
		return;
	}
	kfree (buf);
	return;
}


/* 
 * Check if directory is empty.
 */

int
isdirempty (ip)
struct inode *ip;
{
	char	*cp;
	int		count;
	BUF		*bp;

	for (count = 0 ; count < ip->i_size ; count += BSIZE) {
		if ((bp = vread (ip, count)) == NULL) 
			break;

		for (cp = (char *) bp->b_vaddr ; 
		     cp < (char *) bp->b_vaddr + BSIZE ; cp += 16) {

			if (* cp == 0 && cp [1] == 0) 
				continue;
			if (cp [2] != '.')
				goto bad;
			if (cp [3] == 0)
				continue;
			if (cp [3] != '.' || cp [4] != 0)
				goto bad;
		}

		brelease (bp);
	}
	return 1;

bad:
	brelease (bp);
	return 0;
}


/*
 * Remove a directory.
 */

int
urmdir (path)
char * path;
{
	int		iPathLen;	/* Size of the string */
	extern int	strUserAcc ();
	struct direct	dir;
	
	/* Check if path points to a valid user buffer.*/
	if ((iPathLen = strUserAcc (path, 0)) < 0) {
		set_user_error (EFAULT);
		return -1;
	}

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

	/* Check if path is a directory */
	if ((u.u_cdiri->i_mode & IFMT) != IFDIR) {
		SET_U_ERROR (ENOTDIR, "rmdir: no such directory");
		idetach (u.u_cdiri);
		return -1;
	}

	/* We have to check if directory is empty */
	if (! isdirempty (u.u_cdiri)) {
		SET_U_ERROR (EEXIST, "rmdir: directory is not empty");
		idetach (u.u_cdiri);
		return -1;
	}
	idetach (u.u_cdiri);
	removedir (path, iPathLen);
	return 0;
}


/*
 * SysV compatible mkdir() system call.
 *
 *	Create a directory of the given "path" and "mode", if possible.
 *	Creating the directory is straight forward.  Trying to clean
 *	up in case we run out of inodes or freee blocks in the process 
 * 	is not trivial.
 *	This system call was implemented in very press time.
 * 	Vlad 6-04-92
 */

int
umkdir (path, mode)
char	*path;
int	mode;
{
	INODE 		*dmknod();		/* make directory node */
	INODE 		*pip;			/* parent inode pointer */
	char 		*cp_path, 
			*cpb_path, 
			*cp_parent;
	char		*bufpath, *bufparent, *mark;

	/* Check if path points to a valid user buffer.*/
	if (strUserAcc (path, 0) < 0) {
		set_user_error (EFAULT);
		return -1;
	}

	/*
	 * Create a local copies of "path" which we can use to build up
	 * the required directory links:
	 *	path/. -- bufdot
	 *	path/..-- bufdotdot
	 * Verify that the given path is not too long.
	 */

	cp_path = path;

	cpb_path = bufpath = kalloc ((PATH_MAX + 5) * sizeof (char));
	if (cpb_path == NULL) {
		set_user_error (EAGAIN);
		return -1;
	}

	cp_parent = bufparent = kalloc ((PATH_MAX + 5) * sizeof (char));
	if (cp_parent == NULL) {
		kfree (cpb_path);
		set_user_error (EAGAIN);
		return -1;
	}

	while ((* cpb_path = getubd (cp_path)) != 0) {
		* cp_parent ++ = * cpb_path;
		++ cp_path;
		if (++ cpb_path >= & bufpath [PATH_MAX - 3]) {
			SET_U_ERROR (ENOENT, "sys5: mkdir: path too long");
			kfree (bufpath);
			kfree (bufparent);
			return -1;
		}
	}

	if ((pip = dmknod (path, mode, IOUSR)) == NULL) {
		kfree (bufpath);
		kfree (bufparent);
		return -1;
	}

	/* 
	 * Temporarily change parent to the 'dot' directory
	 */

	mark = cp_parent;
	* cp_parent ++ = '/';
	* cp_parent ++ = '.';
	* cp_parent ++ = 0;
	do_link (bufpath, bufparent, IOSYS, 0);

	/*
	 * Get rid of dot entry.
	 */
	* (cp_parent = mark) = 0;

	if (get_user_error ()) {
		set_user_error (do_unlink (bufpath, IOSYS));
		kfree (bufparent);
		kfree (bufpath);
		return -1;
	}

	/*
	 * Now really find the parent
	 */

	while (-- cp_parent >= bufparent) {
		if (* cp_parent == '/') {
			* ++ cp_parent = 0;
			break;
		}
	}

	if (cp_parent < bufparent) {
		* ++ cp_parent = '.';
		* ++ cp_parent = 0;
	}

	/*
	 * We can hack into the pathname now.
	 */

	mark = cpb_path;
	* cpb_path ++ = '/';
	* cpb_path ++ = '.';
	* cpb_path ++ = '.';
	* cpb_path ++ = 0;


	do_link (bufparent, bufpath, IOSYS, 0);
	* (cpb_path = mark) = 0;

	if (get_user_error ()) {
		/* 
	 	 * Temporarily change parent to the 'dot' directory
	  	 */

		mark = cp_parent;
		*cp_parent++ = '/';
		*cp_parent++ = '.';
		*cp_parent++ = 0;

		set_user_error (do_unlink (bufparent, IOSYS));
		set_user_error (do_unlink (bufpath, IOSYS));
	}

	kfree (bufpath);
	kfree (bufparent);
	return 0;
}


/*
 * Create a directory.
 *
 * We cannot use original ulink because it makes the directories only
 * for superuser.
 */

INODE *
dmknod (np, mode, space)
char	*np;	/* Direcotory name */
int	mode;
int		space;
{
	INODE *ip;
	IO		io;
	struct direct	dir;

	/* If ftoi returns nonzero, get_user_error () has been set. */
	if (ftoi (np, 'c', space, & io, & dir, SELF->p_credp) != 0)
		return NULL;

	if (u.u_cdiri != NULL) {
		SET_U_ERROR (EEXIST, "dmknod: path already exists");
		idetach (u.u_cdiri);
		return NULL;
	}
	if ((ip = imake (mode | S_IFDIR, (o_dev_t) 0, & io, & dir,
			 SELF->p_credp)) != NULL)
		idetach (ip);

	return u.u_pdiri;	/* grab ptr to parent inode */
}


/*
 * Read `n' bytes from the directory `fd' using the buffer `bp'.
 */

int
dirio(fd, bp, n, offset)
int fd;
struct direct	*bp;
unsigned 	n;
off_t		*offset;
{
	__fd_t	      *	fdp;
	IO		io;

	/* Check file descriptor */
	if ((fdp = fd_get (fd)) == NULL ||
	    (fdp->f_flag & IPR) == 0 ||
	    (fdp->f_ip->i_mode & IFMT) != IFDIR) {
		set_user_error (EBADF);
		return 0;
	}

	ilock (fdp->f_ip, "dirio ()");

	io.io_seg = IOSYS;
	io.io_seek = fdp->f_seek;
	io.io.vbase = (caddr_t) 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;

	iread (fdp->f_ip, & io);
	iaccessed (fdp->f_ip);		/* read - atime */

	n -= io.io_ioc;
	* offset = fdp->f_seek;
	fdp->f_seek += n;

	iunlock (fdp->f_ip);
	return n;
}


/*
 * Get directory entry in file system independent format.
 */

int
ugetdents(fd, bp, n)
int 		fd;	/* File descriptor to an open directory */
char 		*bp;	/* Buffer where entries should be read */
unsigned 	n;	/* Number of bytes to be read */
{
	struct direct	r_dir;	
	unsigned	bytes;	/* Number of bytes */
	struct dirent	sd;
	ino_t		inode;	/* Inode number */
	unsigned short	ofnm;	/* Offset to file name in dirent */
	char		*cw, 
			*cr;
	int		minbuf;	/* Minimum possible size of the bp */
	int		i, mod;
	int		entry;
	char		ends[3] = "";
	int		total = 0;

	cw = bp;

	ofnm = sizeof (sd.d_ino) + sizeof (sd.d_off) + sizeof (sd.d_reclen);

	/*
	 * Find minimum possible size of bp. It should be enough to contain
	 * the header of dirent, file name + 0, and be on a sizeof(long)
	 * boundary.
	 */

	entry = ofnm + DIRSIZ + 1;
	mod = entry % sizeof(long);
	minbuf = entry + (mod ? sizeof(long) - mod : 0); 

	/* Is user buffer available? */
	if (! useracc (bp, n, 1) || n < minbuf) {
		set_user_error (EFAULT);
		return -1;
	}

	while(n - (cw - bp) >= minbuf) {
		/*
		 * Read next entry from the directory. 
		 * inode == 0 for rm(ed) entries 
		 */

		do {
			if ((bytes = dirio (fd, & r_dir,
					    sizeof (struct direct), 
					    & sd.d_off)) == 0)
				return total;

			inode = r_dir.d_ino;
		} while (! inode);

		/* Find the size of file name */
		for (cr = r_dir.d_name, i = 0 ; * cr != 0 && i < DIRSIZ ; 
		     i ++, cr ++)
			/* DO NOTHING */ ;

		/* Copy file name */
		if (! kucopy (r_dir.d_name, cw + ofnm, i))
			return -1;

		/* Write 0 */
		putubd (cw + ofnm + i ++, 0);

		/* Round up to long boundary */
		if ((mod = (ofnm + i) % sizeof(long)) != 0)
			if (! kucopy (ends, cw + ofnm + i, sizeof
				      (long) - mod))
				return -1;

		sd.d_ino = r_dir.d_ino;
		sd.d_reclen = ofnm + i;		/* Size of directory entry */
		if (mod)
			sd.d_reclen += sizeof (long) - mod;

		if (! kucopy(& sd, cw, ofnm))
			return -1;

		total += sd.d_reclen;
		cw += sd.d_reclen;
	}
	return total;
}


/* 
 * Get file system information by file name.
 */

int
ustatfs(path, stfs, len, fstyp)
char		*path;	/* File name */
struct statfs	*stfs;	/* Pointer to a user structure */
int		len;	/* Size of the structure */
int		fstyp;	/* File system type */
{
	struct filsys	*statmount();	/* Get mp for mounted device */
	struct filsys	*statunmount();	/* Get mp for unmounted device */
	struct filsys	*sb;		/* Pointer to superblock */
	int		count = 0;	/* Number of copied bytes */
	short		systype = 1;	/* System type */
	long		bsize = BSIZE;	/* Block size */
	long		frsize = 0;	/* Fragment size */

	/* Check if stfs is an available user area. */
	if (! useracc ((char *) stfs, len, 1)) {
		SET_U_ERROR (EFAULT, "ustatfs 0");
		return -1;
	}

	/* Filesystem type is 1 for 512 bytes blocks. */
	count += sizeof (systype);
	if (count > len) {
		SET_U_ERROR (EFAULT, "ustatfs 1");
		return -1;
	}
	if (! kucopy (& systype, & stfs->f_fstyp, sizeof (systype)))
		return -1;
	
	/* Block size */
	count += sizeof (bsize);
	if (count > len) {
		SET_U_ERROR (EFAULT, "ustatfs 2");
		return -1;
	}
	if (! kucopy (& bsize, & stfs->f_bsize, sizeof (bsize)))
		return -1;

	/* Fragment size. */
	count += sizeof (int);
	if (count > len) {
		SET_U_ERROR (EFAULT, "ustatfs 3");
		return -1;
	}
	if (! kucopy (& frsize, & stfs->f_frsize, sizeof (frsize)))
		return -1;

	if (! fstyp) {
		if ((sb = statmount (-1, path)) == NULL)
			return -1;
		devinfo (sb, stfs, len, & count);
	} else {
		if ((sb = statunmount (-1, path)) == NULL)
			return -1;
		devinfo (sb, stfs, len, & count);
		kfree (sb);
	}
	return 0;
}


/*
 * statmount - get superblock for mounted file system.
 * fd - file descriptor or -1, path -  file name or NULL.
 */

struct filsys *
statmount(fd, path)
int	fd;
char	*path;
{	
	MOUNT	*mp;		/* Pointer to device */
	dev_t		device;		/* Mounted device */

	/* Find the device */
	if (path) {	/* Find ip by file name */
		struct direct	dir;

		if (ftoi (path, 'r', IOUSR, NULL, & dir, SELF->p_credp))
			return NULL;

		device = u.u_cdiri->i_dev;
		idetach (u.u_cdiri);
	} else {		/* Find ip by file descriptor */
		__fd_t	      *	fdp;

		if ((fdp = fd_get (fd)) == NULL || (fdp->f_flag & IPR) == 0) {
			SET_U_ERROR (EBADF, "statmount 1");
			return NULL;
		}
		device = fdp->f_ip->i_dev;
	}	

	/* Take mount filesystem, check if dev is mounted device */
	for (mp = mountp; mp != NULL; mp = mp->m_next)
		if (mp->m_dev == device)
			break;

	if (mp == NULL) {
		set_user_error (EINVAL);
		return NULL;
	}
	return & mp->m_super;
}


/*
 * devinfo() write system information to user area
 */

int
devinfo (sb, stfs, len, count)
struct filsys	*sb;	/* File name */
struct statfs	*stfs;	/* Pointer to a user structure */
int		len;	/* Size of the structure */
int		*count;
{
	long		inode;

	/* Total number of blocks */
	* count += sizeof (sb->s_fsize);
	if (* count > len)
		return -1;

	if (! kucopy (& sb->s_fsize, & stfs->f_blocks, sizeof (sb->s_fsize)))
		return -1;

	/* Count of free blocks */
	* count += sizeof (sb->s_tfree);
	if (* count > len)
		return -1;
	if (! kucopy (& sb->s_tfree, & stfs->f_bfree, sizeof (sb->s_tfree)))
		return -1;

	/* Total number of file inodes */
	* count += sizeof (inode);
	if (* count > len)
		return -1;

	inode = (long) (sb->s_isize - INODEI) * INOPB;
	if (! kucopy (& inode, & stfs->f_files, sizeof (inode)))
		return -1;

	/* Number of free inodes */
	* count += sizeof(inode);
	if (* count > len)
		return -1;
	inode = sb->s_tinode;
	if (! kucopy (& inode, & stfs->f_ffree, sizeof (inode)))
		return -1;

	/* Volume name */
	* count += sizeof (sb->s_fname);
	if (* count > len)
		return -1;
	if (! kucopy (sb->s_fname, stfs->f_fname, sizeof (sb->s_fpack)))
		return -1;

	/* Pack name */
	* count += sizeof(sb->s_fpack);
	if (* count > len)
		return -1;
	if (! kucopy (sb->s_fpack, stfs->f_fpack, sizeof (sb->s_fpack)))
		return -1;
	return 0;
}

/*
 * statunmount - get superblock for unmounted file system.
 * fd - file descriptor or -1, path -  file name or NULL.
 */

struct filsys *
statunmount(fd, path)
int	fd;	/* File descriptor */
char	*path;	/* File name */
{
	MOUNT	*mp;
	dev_t	rdev;
	int		mode;
	BUF		*bp;
	struct filsys	*sb;

	/* Find the device */
	if (path) {	/* Find ip by file name */
		struct direct	dir;

		if (ftoi (path, 'r', IOUSR, NULL, & dir, SELF->p_credp)) 
			return NULL;

		mode = u.u_cdiri->i_mode;
		rdev = u.u_cdiri->i_rdev;
		idetach (u.u_cdiri);
	} else {		/* Find ip by file descriptor */
		__fd_t		*fdp;

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

		ilock (fdp->f_ip, "statunmount ()");

		mode = fdp->f_ip->i_mode;
		rdev = fdp->f_ip->i_rdev;

		iunlock (fdp->f_ip);
	}	

	/* Check for block special device */
	if ((mode & IFMT) != IFBLK) {
		set_user_error (ENOTBLK);
		return NULL;
	}

	/* Check if device is mounted device */
	for (mp = mountp; mp != NULL; mp = mp->m_next) {
		if (mp->m_dev == rdev) {
			set_user_error (EBUSY);
			return NULL;
		}
	}

	(void) dopen (rdev, IPR, DFBLK, NULL);
	if (get_user_error ()) 
		return NULL;
	
	/*
 	 * NIGEL: Modified for new dclose ().
	 */

	bp = bread (rdev, (daddr_t) SUPERI, BUF_SYNC);
	dclose (rdev, IPR, DFBLK, NULL);

	if (bp == NULL)
		return NULL;

	if ((sb = kalloc (sizeof (struct filsys))) == NULL)
		return NULL;

	memcpy (sb, bp->b_vaddr, sizeof (struct filsys));
	/*
	 * We must invalidate the buffer or the next try will
	 * use this buffer even if the disk has changed (e.g.,
	 * for a floppy disk).
	 */
	buf_invalidate(bp);

	brelease (bp);

	cansuper (sb);		/* canonicalize superblock */
	if (tstf (sb) == 0) {	/* check for consistency */
		kfree (sb);
		set_user_error (EINVAL);
		return NULL;
	}
	return sb;
}



/* 
 * Get file system information by file descriptor
 */

int
ufstatfs (fildes, stfs, len, fstyp)
int		fildes;	/* File descriptor */
struct statfs	*stfs;	/* Pointer to a user structure */
int		len;	/* Size of the structure */
int		fstyp;	/* File system type */
{	
	struct filsys	*statmount();	/* Get mp for mounted device */
	struct filsys	*statunmount();	/* Get mp for unmounted device */
	struct filsys	*sb;		/* Pointer to superblock */
	int		count = 0;	/* Number of copied bytes */
	short		systype = 1;	/* System type */
	long		bsize = BSIZE;	/* Block size */
	long		frsize = 0;	/* Fragment size */

	/* Check if stfs is an available user area. */
	if (! useracc ((char *) stfs, len)) {
		set_user_error (EFAULT);
		return -1;
	}

	/* Filesystem type is 1 for 512 bytes blocks. */
	count += sizeof (systype);
	if (count > len) {
		SET_U_ERROR (EFAULT, "ufstatfs 0");
		return -1;
	}
	if (! kucopy (& systype, & stfs->f_fstyp, sizeof (systype)))
		return -1;
	
	/* Block size */
	count += sizeof (bsize);
	if (count > len) {
		SET_U_ERROR (EFAULT, "ufstatfs 1");
		return -1;
	}
	if (! kucopy (& bsize, & stfs->f_bsize, sizeof (bsize)))
		return -1;

	/* Fragment size. */
	count += sizeof (int);
	if (count > len) {
		SET_U_ERROR (EFAULT, "ufstatfs 2");
		return -1;
	}
	if (! kucopy (& frsize, & stfs->f_frsize, sizeof (frsize)))
		return -1;

	if (! fstyp) {
		if ((sb = statmount (fildes, NULL)) == NULL)
			return -1;
		if (devinfo (sb, stfs, len, & count) != 0)
			return -1;
	} else {
		if ((sb = statunmount (fildes, NULL)) == NULL)
			return -1;
		if (devinfo (sb, stfs, len, & count) != 0) {
			kfree (sb);
			return -1;
		}
		kfree (sb);
	}
	return 0;
}


/*
 * Check superblock for consistency.
 */

int
tstf(fp)
struct filsys *fp;
{
	daddr_t *dp;
	ino_t *ip;
	ino_t maxinode;

	maxinode = (fp->s_isize - INODEI) * INOPB + 1;
	if (fp->s_isize >= fp->s_fsize)
		return 0;
	if (fp->s_tfree < fp->s_nfree ||
	    fp->s_tfree >= fp->s_fsize - fp->s_isize + 1)
		return 0;
	if (fp->s_tinode < fp->s_ninode || fp->s_tinode >= maxinode - 1)
		return 0;

	for (dp = fp->s_free ; dp < fp->s_free + fp->s_nfree ; dp += 1)
		if (* dp < fp->s_isize || * dp >= fp->s_fsize)
			return 0;

	for (ip = fp->s_inode ; ip < fp->s_inode + fp->s_ninode ; ip += 1)
		if (* ip < 1 || * ip > maxinode)
			return 0;
	return 1;
}

/* the following calls are not in the BCS */

int
uadmin()
{
	set_user_error (EINVAL);
	return -1;
}