/*
 * traditional disk filesystem
 */
#include "sys/param.h"
#include "sys/systm.h"
#include "sys/ino.h"
#include "sys/buf.h"
#include "sys/filsys.h"
#include "sys/mount.h"
#include "sys/dir.h"
#include "sys/user.h"
#include "sys/inode.h"
#include "sys/file.h"
#include "sys/conf.h"
#include "sys/vlimit.h"
#include "sys/stat.h"
#include "sys/filio.h"
#include "sys/cmap.h"
#include "sys/stream.h"
#include "sys/log.h"

int fsput(), fsupdat(), fsread(), fswrite(), fstrunc(), fsstat();
int fsnami(), fsmount(), fsioctl(), fsdirread();
struct inode *fsopen();
struct fstypsw fsfs =
fsinit(fsput, fsupdat, fsread, fswrite, fstrunc, fsstat,
	fsnami, fsmount, fsioctl, fsopen, fsdirread);

extern struct mount fsmtab[];
extern int fscnt;

#define isblind(ip) (((ip)->i_mode&(IFMT|ICCTYP))==(IFDIR|IBLIND))

/*
 * silly mount/unmount routine for fs type 0
 */

fsmount(sip, ip, flag, mnt, fstyp)
register struct inode *ip, *sip;
int flag, mnt, fstyp;
{

	if (!suser(1))
		return;
	if (mnt)
		fson(sip, ip, flag, fstyp);
	else
		fsoff(ip, fstyp);
}

/*
 * mount a type 0 filesystem (sip on ip)
 * maintain the mount table for vm code's sake
 *
 * magic to help booting:
 * if rootdir == NULL, we are being called from iinit, so
 *	- allow s_valid == 0 on bitmapped filesystems so root may be mounted
 *	- call clkinit to set the clock from the super-block time
 */

fson(sip, ip, ronly, fstyp)
register struct inode *ip, *sip;
int ronly, fstyp;
{
	dev_t dev;
	register struct mount *mp;
	struct mount *emp;
	struct inode *rip;
	register struct filsys *fp;
	struct buf *bp;
	struct inode pi;	/* primer */

	if ((sip->i_mode&IFMT) != IFBLK) {
		u.u_error = EINVAL;
		return;
	}
	if (ip->i_fstyp == fstyp && ip->i_dev == sip->i_un.i_rdev) {
		u.u_error = EBUSY;
		return;
	}
	emp = NULL;
	for (mp = &fsmtab[fscnt-1]; mp >= fsmtab; mp--) {
		if (mp->m_dev == NULL)
			emp = mp;
		else if (mp->m_dev == sip)
			goto hopeless;
	}
	if (emp == NULL)
		goto hopeless;
	emp->m_dev = sip;
	dev = (dev_t)sip->i_un.i_rdev;
	bp = bread(dev, SUPERB);
	if(u.u_error) {
		emp->m_dev = NULL;
		brelse(bp);
		return;
	}
	bp->b_flags |= B_LOCKED;
	brelse(bp);
	fp = bp->b_un.b_filsys;
	fp->s_ilock = 0;
	fp->s_flock = 0;
	fp->s_ronly = ronly & 1;
	fp->s_nbehind = 0;
	fp->s_lasti = 1;
	if(fp->s_cylsize == 0)
		fp->s_cylsize = 40;	/* transition hack */
	if(fp->s_aspace == 0)
		fp->s_aspace = 4;	/* likewise */
	if(BITFS(dev) && !fp->s_valid && !fp->s_ronly && rootdir != NULL)
		goto failed;			/* NOT IMPLEMENTED */
	if(BITFS(dev) && fp->U.N.S_flag) {
		if(fsbiton(dev, fp))
			goto failed;
	/* hack until pjw fixes chuck */
	} else if (BITFS(dev) && fp->s_isize + BITMAP*BITCELL < fp->s_fsize)
		if(fsbiton(dev, fp))
			goto failed;
	pi.i_dev = dev;
	pi.i_fstyp = fstyp;
	pi.i_un.i_bufp = bp;
	pi.i_ceil = 0;
	if ((rip = iget(&pi, dev, ROOTINO)) == NULL)
		goto failed;
	if (rip->i_count == 1 && fsiread(&pi, rip) < 0)
		goto failed;
	if (rip->i_count != 1) {	/* already mounted */
		iput(rip);
		goto failed;
	}
	emp->m_mroot = rip;
	ip->i_mroot = rip;
	rip->i_mpoint = ip;
	ip->i_count++;
	sip->i_count++;
	prele(rip);
	if (rootdir == NULL)
		clkinit(fp->s_time);
	return;
failed:
	bp->b_flags &= ~B_LOCKED;
	emp->m_dev = NULL;
hopeless:
	if (u.u_error == 0)
		u.u_error = EBUSY;
}

fsoff(mip, fstyp)
register struct inode *mip;
{
	dev_t dev;
	register struct mount *mp;
	register struct inode *rip;
	struct buf *bp;
	struct filsys *fp;

	varused(fstyp);
	rip = mip->i_mroot;
	for (mp = &fsmtab[fscnt-1]; mp >= fsmtab; mp--)
		if (mp->m_mroot == rip)
			break;
	if (mp < fsmtab)
		panic("umount mp");
	plock(rip);
	xumount(rip);
	update();	/* silly */
	if (rip->i_count > 1 || ifsbusy(rip)) {
		u.u_error = EBUSY;
		prele(rip);
		return;
	}
	plock(mip);
	mip->i_mroot = NULL;
	iput(mip);
	dev = rip->i_dev;
	if ((bp = getblk(dev, SUPERB)) != rip->i_un.i_bufp)
		panic("umount");
	iput(rip);
	bp->b_flags &= ~(B_LOCKED|B_ASYNC);	/* ~async needed? */
	if (bp->b_un.b_filsys->s_ronly)
		brelse(bp);
	else {
		if (BITFS(dev)) {
			fp = bp->b_un.b_filsys;
			fp->s_valid = 1;
			if(fp->U.N.S_flag)
				fsbitoff(dev, fp, 1);
		}
		bwrite(bp);
	}
	mpurge(mp - fsmtab);
	idec(mp->m_dev);
	mp->m_dev = NULL;
}

fsbiton(dev, fp)
dev_t dev;
register struct filsys *fp;
{
	register int i, nf;
	register struct buf **bpp;
	register struct buf *bp;

	/* two chuck should do */
	fp->U.N.S_flag = 1;	/* bit map not in superblock */
	nf = fp->U.N.S_bsize = BSIZE(dev) * NBBY;
	if (fp->s_ronly)
		return (0);
	nf = (fp->s_fsize + nf - 1) / nf;
	bpp = fp->U.N.S_blk;
	for(i = 0; i < nf; i++)
		bpp[i] = 0;
	for(i = 0; i < nf; i++) {
		bp = bread(dev, fp->s_fsize-nf+i);
		if(u.u_error) {
			brelse(bp);
			goto failed;
		}
		bp->b_flags |= B_LOCKED;
		brelse(bp);
		bpp[i] = bp;
	}
	return(0);
failed:
	fsbitoff(dev, fp, 0);
	return(1);
}

fsbitoff(dev, fp, wrtflg)
dev_t dev;
struct filsys *fp;
{
	register struct buf **bpp;
	register struct buf *bp;
	register int i, nf;

	if (fp->s_ronly)
		return;
	nf = fp->U.N.S_bsize;
	nf = (fp->s_fsize + nf-1)/nf;
	bpp = fp->U.N.S_blk;
	for(i = 0; i < nf; i++)
		if(bpp[i]) {
			if ((bp = getblk(dev, fp->s_fsize - nf + i)) != bpp[i])
				panic("fsbitoff");
			bp->b_flags &= ~B_LOCKED;
			if(wrtflg)
				bwrite(bp);
			bpp[i] = 0;	/* safety first */
		}
}

/*
 * fill in inode ip from the disk copy
 * in filesystem fip
 * ip is an inode fresh from iget
 */
fsiread(fip, ip)
register struct inode *fip, *ip;
{
	register struct buf *bp;
	daddr_t bno;

	if (ip->i_number <= 0) {
		u.u_error = EINVAL;
		iput(ip);
		return (-1);
	}
	bno = itod(fip->i_dev, (ino_t)ip->i_number);
	if (bno >= getfs(fip)->s_isize) {
		u.u_error = EINVAL;
		iput(ip);
		return (-1);
	}
	bp = bread(fip->i_dev, bno);
	if((bp->b_flags&B_ERROR) != 0) {
		brelse(bp);
		iput(ip);
		return(-1);
	}
	iexpand(ip, bp->b_un.b_dino+itoo(fip->i_dev, (ino_t)ip->i_number));
	brelse(bp);
	ip->i_un.i_bufp = fip->i_un.i_bufp;
	ip->i_un.i_lastr = 0;
	return (0);
}

extern int tzero0;
extern daddr_t devnulldev;	/* see mem.c */
extern daddr_t devlogdev;	/* see log.c */
extern daddr_t devfd0dev;	/* see fd.c */
#define isdevnull(ip) (devnulldev==ip->i_un.i_rdev||major(devlogdev)==major(ip->i_un.i_rdev)||major(devfd0dev)==major(ip->i_un.i_rdev))

iexpand(ip, dp)
register struct inode *ip;
register struct dinode *dp;
{
	register char *p1, *p2;
	register int i;

	ip->i_mode = dp->di_mode;
	ip->i_nlink = dp->di_nlink;
	ip->i_uid = dp->di_uid;
	ip->i_gid = dp->di_gid;
	ip->i_size = dp->di_size;
	p1 = (char *)ip->i_un.i_addr;
	p2 = (char *)dp->di_addr;
	for(i=0; i<NADDR; i++) {
		*p1++ = *p2++;
		*p1++ = *p2++;
		*p1++ = *p2++;
		*p1++ = 0;
	}
	/*
	 * labels: for regular files, etc: use value on disk, if legit.
	 * for everything else use default from iget: labelbot & RIGID
	 */
	switch(jl_flag(ip->i_lab)) {
	default:
		ip->i_flag |= ICHG;
		labCPMOVX(labelno, ip->i_lab);
		break;
	case L_YES:
	case L_NO:
	case L_BITS:
		i = ip->i_mode & IFMT;
		if (i==IFREG || i==IFDIR || i==IFLNK) {
			/* can a disk file have label Y ? */
			labdtoj(&dp->di_label, ip->i_lab);
		} else if(i==IFCHR && isdevnull(ip)) {
			labCPMOVX(labelyes, ip->i_lab);
		} else if(i==IFCHR && labEQ(labelno, ip->i_lab)
		    && (tzero0 & 0200)) {
			labCPMOVX(labelbot, ip->i_lab);
		} else if(i!=0) {
			labCPMOVX(labelno, ip->i_lab);
		}
		break;
	}
	setpoison(ip, dp->di_label.lb_junk);
	logpoison(ip);
}

fsput(ip)
struct inode *ip;
{
	if (ip->i_nlink == 0) {
		fstrunc(ip);
		ifree(ip);
		ip->i_mode = 0;
		ip->i_flag |= IUPD|ICHG;
	}
	if ((ip->i_flag&(IUPD|IACC|ICHG)) != 0)
		fsiupdat(ip, &time, &time, 0);
}

fsupdat(ip, ta, tm, waitfor)
register struct inode *ip;
time_t *ta, *tm;
{
	fsiupdat(ip, ta, tm, waitfor);
	if (ip->i_un.i_bufp->b_un.b_filsys->s_fmod)
		fsfupdat(ip);
}

/*
 * update the inode on disk
 * if the super-block is dirty, write it too
 */
extern struct jlabel *labelnox;

fsiupdat(ip, ta, tm, waitfor)
register struct inode *ip;
time_t *ta, *tm;
{
	register struct buf *bp;
	struct dinode *dp;
	register char *p1, *p2;
	register int i;

	if (ip->i_un.i_bufp->b_un.b_filsys->s_ronly)
		return;
	bp = bread(ip->i_dev, itod(ip->i_dev, ip->i_number));
	if (bp->b_flags & B_ERROR) {
		brelse(bp);
		return;
	}
	dp = bp->b_un.b_dino;
	dp += itoo(ip->i_dev, ip->i_number);
	dp->di_mode = ip->i_mode;
	dp->di_nlink = ip->i_nlink;
	dp->di_uid = ip->i_uid;
	dp->di_gid = ip->i_gid;
	dp->di_size = ip->i_size;
	p1 = (char *)dp->di_addr;
	p2 = (char *)ip->i_un.i_addr;
	for(i=0; i<NADDR; i++) {
		*p1++ = *p2++;
		*p1++ = *p2++;
		*p1++ = *p2++;
		if(*p2++)
			printf("ino %d dev #%x addr #%x\n", ip->i_number, ip->i_dev, ip->i_un.i_addr[i]);
	}
	i = ip->i_mode & IFMT;
	if (i==IFREG || i==IFDIR || i==IFLNK) {
		labjtod(ip->i_lab, &dp->di_label);
	} else {
		labjtod(labelnox, &dp->di_label);
	}
	dp->di_label.lb_junk = LOG_AC(ip);
	if(ip->i_flag&IACC)
		dp->di_atime = *ta;
	if(ip->i_flag&IUPD)
		dp->di_mtime = *tm;
	if(ip->i_flag&ICHG)
		dp->di_ctime = time;
	ip->i_flag &= ~(IUPD|IACC|ICHG);
	if (waitfor)
		bwrite(bp);
	else
		bdwrite(bp);
}

/*
 * write the super-block
 */

fsfupdat(ip)
struct inode *ip;
{
	register struct filsys *fp;
	struct buf *bp;

	fp = ip->i_un.i_bufp->b_un.b_filsys;
	if (fp->s_fmod == 0 || fp->s_ilock || fp->s_flock || fp->s_ronly)
		return;
	bp = getblk(ip->i_dev, SUPERB);
	if (bp->b_un.b_filsys != fp)
		panic("fsfupdat");
	fp->s_fmod = 0;
	fp->s_time = time;
	bwrite(bp);
}

fsstat(ip, ub)
register struct inode *ip;
struct stat *ub;
{
	register struct dinode *dp;
	register struct buf *bp;
	struct stat ds;

	/*
	 * first copy from inode table
	 */
	ds.st_dev = ip->i_dev;
	ds.st_ino = ip->i_number;
	ds.st_rdev = (dev_t)ip->i_un.i_rdev;

	if(jl_flag(ip->i_lab) == L_YES) {
		ds.st_nlink = 0;
		ds.st_mode = (ip->i_mode & IFMT) | 0666;
		ds.st_uid = 0;
		ds.st_gid = 0;
		ds.st_size = 0;
		ds.st_atime = time;
		ds.st_mtime = time;
		ds.st_ctime = time;
	} else {
		ds.st_nlink = ip->i_nlink;
		ds.st_mode = ip->i_mode;
		ds.st_uid = ip->i_uid;
		ds.st_gid = ip->i_gid;
		ds.st_size = ip->i_size;
		/*
		 * next the dates in the disk
		 */
		bp = bread(ip->i_dev, itod(ip->i_dev, ip->i_number));
		dp = bp->b_un.b_dino;
		dp += itoo(ip->i_dev, ip->i_number);
		ds.st_atime = dp->di_atime;
		ds.st_mtime = dp->di_mtime;
		ds.st_ctime = dp->di_ctime;
		brelse(bp);
	}
	if (copyout((caddr_t)&ds, (caddr_t)ub, sizeof(ds)) < 0)
		u.u_error = EFAULT;
}

fstrunc(ip)
register struct inode *ip;
{
	register i;
	daddr_t bn;
	struct inode itmp;

	i = ip->i_mode & IFMT;
	if (i!=IFREG && i!=IFDIR && i!=IFLNK)
		return;
	/*
	 * Clean inode on disk before freeing blocks
	 * to insure no duplicates if system crashes.
	 */
	itmp = *ip;
	itmp.i_size = 0;
	for (i = 0; i < NADDR; i++)
		itmp.i_un.i_addr[i] = 0;
	itmp.i_flag |= ICHG|IUPD;
	fsiupdat(&itmp, &time, &time, 1);
	ip->i_flag &= ~(IUPD|IACC|ICHG);

	/*
	 * Now return blocks to free list... if machine
	 * crashes, they will be harmless MISSING blocks.
	 */
	for(i=NADDR-1; i>=0; i--) {
		bn = ip->i_un.i_addr[i];
		if(bn == (daddr_t)0)
			continue;
		ip->i_un.i_addr[i] = (daddr_t)0;
		switch(i) {

		default:
			free(ip, bn);
			break;

		case NADDR-3:
			tloop(ip, bn, 0, 0);
			break;

		case NADDR-2:
			tloop(ip, bn, 1, 0);
			break;

		case NADDR-1:
			tloop(ip, bn, 1, 1);
		}
	}
	ip->i_size = 0;
	/*
	 * Inode was written and flags updated above.
	 * No need to modify flags here.
	 */
}

tloop(fip, bn, f1, f2)
register struct inode *fip;
daddr_t bn;
{
	register i;
	register struct buf *bp;
	register daddr_t *bap;
	daddr_t nb;

	bp = NULL;
	for(i=NINDIR(fip->i_dev)-1; i>=0; i--) {
		if(bp == NULL) {
			bp = bread(fip->i_dev, bn);
			if (bp->b_flags & B_ERROR) {
				brelse(bp);
				return;
			}
			bap = bp->b_un.b_daddr;
		}
		nb = bap[i];
		if(nb == (daddr_t)0)
			continue;
		if(f1) {
			brelse(bp);
			bp = NULL;
			tloop(fip, nb, f2, 0);
		} else
			free(fip, nb);
	}
	if(bp != NULL)
		brelse(bp);
	free(fip, bn);
}

fsioctl(ip, cmd, cmarg, flag)
register struct inode *ip;
caddr_t cmarg;
{
	int fmt;
	register dev_t dev;

	fmt = ip->i_mode & IFMT;
	if (fmt != IFCHR) {
		if (cmd==FIONREAD && (fmt == IFREG || fmt == IFDIR)) {
			off_t nread = ip->i_size; /* - fp->f_offset */
			if (copyout((caddr_t)&nread, cmarg, sizeof(off_t)))
				u.u_error = EFAULT;
		} else
			u.u_error = ENOTTY;
		return;
	}
	dev = ip->i_un.i_rdev;
	u.u_r.r_val1 = 0;
	(*cdevsw[major(dev)]->d_ioctl)(dev, cmd, cmarg, flag);
}

struct inode *
fsopen(ip, rw)
register struct inode *ip;
{
	dev_t dev;
	register unsigned int maj;

	dev = (dev_t)ip->i_un.i_rdev;
	maj = major(dev);

	switch(ip->i_mode&IFMT) {
	case IFCHR:
		if(maj >= nchrdev || cdevsw[maj] == NULL)
			goto bad;
		if (cdevsw[maj]->qinfo)		/* stream device */
			return(stopen(cdevsw[major(dev)]->qinfo, dev, rw, ip));
		(*cdevsw[maj]->d_open)(dev, rw);
		break;

	case IFBLK:
		if(maj >= nblkdev || bdevsw[maj] == NULL)
			goto bad;
		(*bdevsw[maj]->d_open)(dev, rw);
	}
	if (u.u_error) {
		iput(ip);
		return (NULL);
	}
	return (ip);

bad:
	u.u_error = ENXIO;
	iput(ip);
	return(NULL);
}

fsread(ip)
register struct inode *ip;
{
	struct buf *bp;
	dev_t dev;
	daddr_t lbn, bn;
	off_t diff;
	register int on, type;
	register unsigned n;

	dev = (dev_t)ip->i_un.i_rdev;
	type = ip->i_mode&IFMT;
	if (type==IFCHR) {
		(*cdevsw[major(dev)]->d_read)(dev);
		return;
	}
	if (isblind(ip)!=0 || Lsign(u.u_offset)<0) {	/* and not IFCHR */
		u.u_error = EINVAL;
		return;
	}
	if (type != IFBLK) {	/* if offset >= size, avoid overflow */
		if (Lsign(Lladd(u.u_offset, -ip->i_size))>=0)
			return;
		dev = ip->i_dev;
	}
	do {
		lbn = bn = Lshift(u.u_offset, BSHIFT(dev));
		on = Ltol(u.u_offset) & BMASK(dev);
		n = MIN((unsigned)(BSIZE(dev)-on), u.u_count);
		if (type!=IFBLK) {
			diff = ip->i_size - Ltol(u.u_offset);
			if (diff <= 0)
				return;
			if (diff < n)
				n = diff;
			bn = bmap(ip, bn, B_READ);
			if (u.u_error)
				return;
		} else
			rablock = bn+1;
		if ((long)bn<0) {
			bp = geteblk();
			clrbuf(bp);
		} else if (ip->i_un.i_lastr+1==lbn)
			bp = breada(dev, bn, rablock);
		else
			bp = bread(dev, bn);
		ip->i_un.i_lastr = lbn;
		n = MIN(n, BSIZE(dev)-bp->b_resid);
		if (n!=0)
			iomove(bp->b_un.b_addr+on, n, B_READ);
		brelse(bp);
	} while(u.u_error==0 && u.u_count!=0 && n!=0);
}

fsdirread(ip, len)
struct inode *ip;
{	struct buf *bp = 0, *outb;
	dev_t dev;
	daddr_t bn, lbn;
	int on, type, j, m, new = 0;
	char *p, *lp, *lastp;
	struct direct *dp;
	char ncvt[24];

	if(isblind(ip)!=0 || Lsign(u.u_offset)<0) {
		u.u_error = EINVAL;
		return;
	}
	type = ip->i_mode & IFMT;
	if(type != IFDIR) {
		u.u_error = ENOTDIR;
		return;
	}
	dev = ip->i_dev;
	outb = geteblk();	/* to hold the stuff for the user */
	if(len > BSIZE(dev))
		len = BSIZE(dev);
	p = lp = outb->b_un.b_addr;
	lastp = p + len;
loop:
	if(new <= 0) {	/* get some goo */
		lbn = bn = Lshift(u.u_offset, BSHIFT(dev));
		on = Ltol(u.u_offset) & BMASK(dev);
		if(ip->i_size <= Ltol(u.u_offset))
			goto done;
		bn = bmap(ip, bn, B_READ);
		if(u.u_error)
			return;
		if((long)bn < 0) {	/* holes in dirs? */
			u.u_offset = Lladd(u.u_offset, BSIZE(dev)-on);
			goto loop;	/* try the next block */
		}
		if(bp)
			brelse(bp);
		if(ip->i_un.i_lastr + 1 == lbn)
			bp = breada(dev, bn, rablock);
		else
			bp = bread(dev, bn);
		ip->i_un.i_lastr = lbn;
		new = BSIZE(dev) - on;
		if(new > BSIZE(dev) - bp->b_resid)	/* io error? */
			new = BSIZE(dev) - bp->b_resid;	/* or paranoia */
		if(new > ip->i_size - Ltol(u.u_offset))
			new = ip->i_size - Ltol(u.u_offset);
		dp = (struct direct *) (bp->b_un.b_addr + on);
	}
	if(dp->d_ino == 0)
		goto incr;
	for(m = dp->d_ino, j = sizeof(ncvt)-1; m; j--) {
		ncvt[j] = m%10 + '0';
		m /= 10;
	}
	for(++j; j < sizeof(ncvt) && p < lastp; )
		*p++ = ncvt[j++];
	if(j != sizeof(ncvt) || p >= lastp)
		goto early;
	*p++ = '\t';
	for(j = 0; j < DIRSIZ && dp->d_name[j] && p < lastp; j++)
		*p++ = dp->d_name[j];
	if(p >= lastp)
		goto early;
	*p++ = 0;
incr:
	lp = p;
	new -= sizeof(*dp);
	u.u_offset = Lladd(u.u_offset, sizeof(*dp));
	dp++;
	if(p < lastp)
		goto loop;
done:
	j = lp - outb->b_un.b_addr;
	if(j > 0)
		if(copyout((caddr_t)outb->b_un.b_addr, u.u_base, j))
			u.u_error = EFAULT;
	if(bp)
		brelse(bp);
	brelse(outb);
	u.u_r.r_val1 = j;
	return;
early:
	*lp = 0;
	if(lp > outb->b_un.b_addr)
		goto done;
	u.u_error = ENOSPC;
	if(bp)
		brelse(bp);
	brelse(outb);
}

fswrite(ip)
register struct inode *ip;
{
	struct buf *bp;
	dev_t dev;
	daddr_t bn;
	register int on, type;
	register unsigned n;
	register int i;

	dev = (dev_t)ip->i_un.i_rdev;
	type = ip->i_mode&IFMT;
	if (type==IFCHR) {
		if((*u.u_pofilep & SAFETOWRITE) || labWRITE()) {
			ip->i_flag |= IUPD|ICHG;
			(*cdevsw[major(dev)]->d_write)(dev);
		}
		return;
	}
	if (Lsign(u.u_offset) < 0) {		/* and not IFCHR */
		u.u_error = EINVAL;
		return;
	}
	if (u.u_count == 0)
		return;
	if ((ip->i_mode&IFMT)==IFREG &&
	    (Lsign(Luadd(u.u_offset, u.u_count))!=0 || 
	     Ltol(u.u_offset) + u.u_count > u.u_limit[LIM_FSIZE])) {
		u.u_error = EMFILE;
		return;
	}
	if (type != IFBLK)
		dev = ip->i_dev;
	do {
		if((ip->i_mode & ICCTYP) == IAPPEND)
			u.u_offset = ltoL(ip->i_size);
		bn = Lshift(u.u_offset, BSHIFT(dev));
		on = Ltol(u.u_offset) & BMASK(dev);
		n = MIN((unsigned)(BSIZE(dev)-on), u.u_count);
		if (type!=IFBLK) {
			/*
			 * Early security check (see iomove()) done BEFORE
			 * possible block alloc in bmap(), for consistency.
			 */
			if((*u.u_pofilep & SAFETOWRITE) || labWRITE())
				;
			else
				break;
			bn = bmap(ip, bn, B_WRITE);
			if((long)bn<0)
				return;
		}
		i = getfsx(dev);
		if (bn && mfind(i, bn))
			munhash(i, bn);
		if(n == BSIZE(dev)) 
			bp = getblk(dev, bn);
		else
			bp = bread(dev, bn);
		iomove(bp->b_un.b_addr+on, n, B_WRITE);
		if(u.u_error != 0)
			brelse(bp);
		else {
			if ((ip->i_mode&IFMT) == IFDIR &&
			    ((struct direct *)(bp->b_un.b_addr+on))->d_ino == 0)
				bwrite(bp);	/* consistency */
			else if ((n+on) == BSIZE(dev))
				bawrite(bp);
			else
				bdwrite(bp);
		}
		if(Ltol(u.u_offset) > ip->i_size &&
		   (type==IFDIR || type==IFREG || type==IFLNK))
			ip->i_size = Ltol(u.u_offset);
		ip->i_flag |= IUPD|ICHG;
	} while(u.u_error==0 && u.u_count!=0);
}

/*
 * Getfsx returns the index in the file system
 * table of the specified device.  The swap device
 * is also assigned a pseudo-index.  The index may
 * be used as a compressed indication of the location
 * of a block, recording
 *	<getfsx(dev),blkno>
 * rather than
 *	<dev, blkno>
 * provided the information need remain valid only
 * as long as the file system is mounted.
 *
 * only the vm code calls this.
 */
getfsx(dev)
	dev_t dev;
{
	register struct mount *mp;

	if (dev == swapdev)
		return (MSWAPX);
	for (mp = &fsmtab[fscnt-1]; mp >= fsmtab; mp--) {
		if (mp->m_dev == NULL)
			continue;
		if (mp->m_dev->i_un.i_rdev == dev)
			return (mp - fsmtab);
	}
	return (-1);
}
