/* @(#)alloc.c	1.4 */#include "sys/param.h"#include "sys/types.h"#include "sys/sysmacros.h"#include "sys/systm.h"#include "sys/mount.h"#include "sys/filsys.h"#include "sys/fblk.h"#include "sys/buf.h"#include "sys/inode.h"#include "sys/file.h"#include "sys/ino.h"#include "sys/dir.h"#include "sys/signal.h"#include "sys/user.h"#include "sys/errno.h"#include "sys/var.h"/* * alloc will obtain the next available free disk block from the free list * of the specified device. * The super block has up to NICFREE remembered free blocks; * the last of these is read to obtain NICFREE more . . . * * no space on dev x/y -- when the free list is exhausted. */struct buf *alloc(dev)dev_t dev;{	register daddr_t bno;	register struct filsys *fp;	register struct buf *bp;	fp = getfs(dev);	while(fp->s_flock)		(void) sleep((caddr_t)&fp->s_flock, PINOD);	do {		if (fp->s_nfree <= 0)			goto nospace;		bno = fp->s_free[--fp->s_nfree];		if (bno == 0)			goto nospace;	} while (badblock(fp, bno, dev));	if (fp->s_nfree <= 0) {		fp->s_flock++;		bp = bread(dev, bno);		if (u.u_error == 0) {			fp->s_nfree = (bp->b_un.b_fblk)->df_nfree;			bcopy((caddr_t)(bp->b_un.b_fblk)->df_free,			    (caddr_t)fp->s_free, sizeof(fp->s_free));		}		brelse(bp);		/*		 * Prevent "dups in free"		 */		bp = getblk(dev, SUPERB);		bcopy((caddr_t)fp, bp->b_un.b_addr, sizeof(struct filsys));		fp->s_fmod = 0;		fp->s_time = time;		bwrite(bp);		fp->s_flock = 0;		wakeup((caddr_t)&fp->s_flock);	}	if (fp->s_nfree <= 0 || fp->s_nfree > NICFREE) {		prdev("Bad free count", dev);		goto nospace;	}	bp = getblk(dev, bno);	clrbuf(bp);	if (fp->s_tfree) fp->s_tfree--;	fp->s_fmod = 1;	return(bp);nospace:	fp->s_nfree = 0;	fp->s_tfree = 0;	delay(v.v_hz<<2);	prdev("no space", dev);	u.u_error = ENOSPC;	return(NULL);}/* * place the specified disk block back on the free list of the * specified device. */free(dev, bno)dev_t dev;daddr_t bno;{	register struct filsys *fp;	register struct buf *bp;	fp = getfs(dev);	fp->s_fmod = 1;	while(fp->s_flock)		(void) sleep((caddr_t)&fp->s_flock, PINOD);	if (badblock(fp, bno, dev))		return;	if (fp->s_nfree <= 0) {		fp->s_nfree = 1;		fp->s_free[0] = 0;	}	if (fp->s_nfree >= NICFREE) {		fp->s_flock++;		bp = getblk(dev, bno);		(bp->b_un.b_fblk)->df_nfree = fp->s_nfree;		bcopy((caddr_t)fp->s_free,			(caddr_t)(bp->b_un.b_fblk)->df_free,			sizeof(fp->s_free));		fp->s_nfree = 0;		bwrite(bp);		fp->s_flock = 0;		wakeup((caddr_t)&fp->s_flock);	}	fp->s_free[fp->s_nfree++] = bno;	fp->s_tfree++;	fp->s_fmod = 1;}/* * Check that a block number is in the range between the I list * and the size of the device. * This is used mainly to check that a * garbage file system has not been mounted. * * bad block on dev x/y -- not in range */badblock(fp, bn, dev)register struct filsys *fp;daddr_t bn;dev_t dev;{	if (bn < fp->s_isize || bn >= fp->s_fsize) {		prdev("bad block", dev);		return(1);	}	return(0);}/* * Allocate an unused I node on the specified device. * Used with file creation. * The algorithm keeps up to NICINOD spare I nodes in the * super block. When this runs out, a linear search through the * I list is instituted to pick up NICINOD more. */struct inode *ialloc(dev, mode, nlink)dev_t dev;{	register struct filsys *fp;	register struct inode *ip;	register i;	register struct buf *bp;	struct dinode *dp;	ino_t ino;	daddr_t adr;	fp = getfs(dev);loop:	while(fp->s_ilock)		(void) sleep((caddr_t)&fp->s_ilock, PINOD);	if (fp->s_ninode > 0	    && (ino = fp->s_inode[--fp->s_ninode])) {		ip = iget(dev, ino);		if (ip == NULL)			return(NULL);		if (ip->i_mode == 0) {			/* found inode: update now to avoid races */			ip->i_mode = mode;			ip->i_nlink = nlink;			ip->i_flag |= IACC|IUPD|ICHG|ISYN;			ip->i_uid = u.u_uid;			ip->i_gid = u.u_gid;			ip->i_size = 0;			for (i=0; i<NADDR; i++)				ip->i_addr[i] = 0;			if (fp->s_tinode) fp->s_tinode--;			fp->s_fmod = 1;			iupdat(ip, &time, &time);			return(ip);		}		/*		 * Inode was allocated after all.		 * Look some more.		 */		iupdat(ip, &time, &time);		iput(ip);		goto loop;	}	fp->s_ilock++;	fp->s_ninode = NICINOD;	ino = FsINOS(dev, fp->s_inode[0]);	for(adr = FsITOD(dev, ino); adr < fp->s_isize; adr++) {		bp = bread(dev, adr);		if (u.u_error) {			brelse(bp);			ino += FsINOPB(dev);			continue;		}		dp = bp->b_un.b_dino;		for(i=0; i<FsINOPB(dev); i++,ino++,dp++) {			if (fp->s_ninode <= 0)				break;			if (dp->di_mode == 0)				fp->s_inode[--fp->s_ninode] = ino;		}		brelse(bp);		if (fp->s_ninode <= 0)			break;	}	fp->s_ilock = 0;	wakeup((caddr_t)&fp->s_ilock);	if (fp->s_ninode > 0) {		fp->s_inode[fp->s_ninode-1] = 0;		fp->s_inode[0] = 0;	}	if (fp->s_ninode != NICINOD) {		fp->s_ninode = NICINOD;		goto loop;	}	fp->s_ninode = 0;	prdev("Out of inodes", dev);	u.u_error = ENOSPC;	fp->s_tinode = 0;	return(NULL);}/* * Free the specified I node on the specified device. * The algorithm stores up to NICINOD I nodes in the super * block and throws away any more. */ifree(dev, ino)dev_t dev;ino_t ino;{	register struct filsys *fp;	fp = getfs(dev);	fp->s_tinode++;	if (fp->s_ilock)		return;	fp->s_fmod = 1;	if (fp->s_ninode >= NICINOD) {		if (ino < fp->s_inode[0])			fp->s_inode[0] = ino;		return;	}	fp->s_inode[fp->s_ninode++] = ino;}/* * getfs maps a device number into a pointer to the incore super block. * The algorithm is a linear search through the mount table. * A consistency check of the in core free-block and i-node counts. * * bad count on dev x/y -- the count *	check failed. At this point, all *	the counts are zeroed which will *	almost certainly lead to "no space" *	diagnostic * panic: no fs -- the device is not mounted. *	this "cannot happen" */struct filsys *getfs(dev)dev_t dev;{	register struct mount *mp;	register struct filsys *fp;	for(mp = &mount[0]; mp < (struct mount *)v.ve_mount; mp++)	if (mp->m_flags == MINUSE && mp->m_dev == dev) {		fp = mp->m_bufp->b_un.b_filsys;		if (fp->s_nfree > NICFREE || fp->s_ninode > NICINOD) {			prdev("bad count", dev);			fp->s_nfree = 0;			fp->s_ninode = 0;		}		return(fp);	}	panic("no fs");	return(NULL);}/* * update is the internal name of 'sync'. It goes through the disk * queues to initiate sandbagged IO; goes through the I nodes to write * modified nodes; and it goes through the mount table to initiate modified * super blocks. */update(){	register struct inode *ip;	register struct mount *mp;	register struct filsys *fp;	register struct user *up;	static struct inode uinode;	if (uinode.i_flag)		return;	up = &u;	uinode.i_flag++;	uinode.i_mode = IFBLK;	for(mp = &mount[0]; mp < (struct mount *)v.ve_mount; mp++)		if (mp->m_flags == MINUSE) {			fp = mp->m_bufp->b_un.b_filsys;			if (fp->s_fmod==0 || fp->s_ilock!=0 ||			   fp->s_flock!=0 || fp->s_ronly!=0)				continue;			fp->s_fmod = 0;			fp->s_time = time;			uinode.i_rdev = mp->m_dev;			up->u_error = 0;			up->u_offset = SUPERBOFF;			up->u_count = sizeof(struct filsys);			up->u_base = (caddr_t)fp;			up->u_segflg = 1;			up->u_fmode = FWRITE|FSYNC;			writei(&uinode);		}	for(ip = &inode[0]; ip < (struct inode *)v.ve_inode; ip++)		if ((ip->i_flag&ILOCK)==0 && ip->i_count!=0		&& (ip->i_flag&(IACC|IUPD|ICHG))) {			ip->i_flag |= ILOCK;			ip->i_count++;			iupdat(ip, &time, &time);			iput(ip);		}	bflush(NODEV);	uinode.i_flag = 0;}