/*
 * Directory hashing
 *
 * John Buck, Fred Richter PINY Jan. 1982
 *
 * The rountines in this file provide a system
 * call to hash commonly used, (big), directories
 * like /bin, /usr/bin.  It keeps a 511 position byte
 * map containing approximate (often exact) block
 * numbers at which the hashed file names may
 * be found easily.  It also will, 9/10 times return
 * a ENOENT type response right away without reading
 * a directory (see nami.c) if it finds that the
 * name in question is not in the hash table for that
 * directory.
 *
 * The hash table is kept up to date in wdir() (iget.c)
 * so that any new files created in a hashed directory will
 * be put into the table so they dont turn up missing.
 *
 * No provision is made for deleting a file in a hashed
 * directory.  This is not a problem, since 
 *	a) there is a system call to rehash a directory
 *	b) things in /bin /usr/bin do not get deleted often
 *	c) who cares if they do? worst case: we do an
 *	   entire search for something that is not there
 *	   anyway (like we used to!)
 *
 * Each hashed directory robs a disk buffer (geteblk())
 * therefore, it is often unwise to hash things
 * 'for the hell of it', unless, of course you have
 * buffers to burn.
 */
#include	"sys/param.h"
#include	"sys/types.h"
#include	"sys/systm.h"
#include	"sys/errno.h"
#include	"sys/signal.h"
#include	"sys/dir.h"
#include	"sys/buf.h"
#include	"sys/user.h"
#include	"sys/inode.h"
#include	"sys/hash.h"
#include	"sys/var.h"


syshash()
{
	register struct inode *ip;
	register struct hash *hp;
	register struct hash *savhp;
	struct a {
		char	*dname;
		int	hmode;
	} *uap;

	if(!suser())
		return;
	ip = namei(uchar, 0);
	if(ip == NULL)
		return;
	if((ip->i_mode & IFMT) != IFDIR){
		u.u_error = ENOTDIR;
		iput(ip);
		return;
	}
	uap = (struct a *)u.u_ap;
	savhp = NULL;
	for(hp = &hash[0]; hp < (struct hash *)v.ve_hash; hp++){
		if(hp->h_iptr == ip){
			if(uap->hmode == 0){
				hp->h_iptr = 0;
				brelse(hp->h_bufp);
				iput(ip);
				iput(ip);
				return;
			}
			else	{
				hashit(hp);
				iput(ip);
				return;
			}
		}
		if(hp->h_iptr == 0 && savhp == NULL)
			savhp = hp;
	}
	if(uap->hmode == 0){
		u.u_error = EINVAL;
		iput(ip);
		return;
	}

	if(savhp == NULL){
		u.u_error = EBUSY;
		iput(ip);
		return;
	}
	savhp->h_iptr = ip;
	savhp->h_bufp = getablk(1);
	hashit(savhp);
	prele(ip);
	return;
}

hashit(hp)
struct hash *hp;
{
	struct buf *bp;
	register struct inode *ip;
	register char *ap;
	register char *cp;
	daddr_t bn;
	short	bno;
	short hval;

	ip = hp->h_iptr;
	clear(paddr(hp->h_bufp), BSIZE);
	ap = (char *)paddr(hp->h_bufp);
	u.u_offset = 0;
	u.u_segflg = 1;
	bp = NULL;
	bno = 0;

eloop:		/* Can you guess where this is from ? */
	if(u.u_offset >= ip->i_size){	/* End of directory hash */
		if(bp)
			brelse(bp);
		return;
	}
	if((u.u_offset & BMASK) == 0){
		if(bp)
			brelse(bp);
		bn = bmap(ip, (daddr_t)(u.u_offset >> BSHIFT), B_READ);
		if(u.u_error)
			return;
		if(bn < 0){
			u.u_error = EIO;
			return;
		}
		bp = bread(ip->i_dev, bn);
		if(u.u_error){
			brelse(bp);
			return;
		}
		if(++bno >= 128)
			bno = 127;
	}
	copyio(paddr(bp) + (uint)(u.u_offset & BMASK), &u.u_t[0],
		sizeof (struct direct), U_RKD);
	u.u_offset += sizeof (struct direct);
	if(((struct direct *)(&u.u_t[0]))->d_ino == 0)
		goto eloop;
	hval = hkey(&(((struct direct *)(&u.u_t[0]))->d_name[0]));
	if(ap[hval] == 0)
		ap[hval] = bno;
	goto eloop;
}

hkey(n)
char *n;
{
	register char *s;
	register unsigned h;
	register char *t;

	h = 0;
	s = n;
	t = s + DIRSIZ;
	while(s < t && *s)
		h += h + (*s++);
	return(h % 511);
}

inhash(ip)
register struct inode *ip;
{
	register struct hash *hp;
	register char *ap;

	for(hp = &hash[0]; hp < v.ve_hash; hp++){
               if(hp->h_iptr == ip){
			ap = (char *)paddr(hp->h_bufp);
			return(ap[hkey(u.u_dent.d_name)]);
		}
	}
	return(1);
}

addhash(ip)
register struct inode *ip;
{
	register struct hash *hp;
	short hval;
	char bno;
	register char *ap;

	for(hp = &hash[0]; hp < v.ve_hash; hp++){
		if(hp->h_iptr == ip){
			bno = (u.u_offset >> 9) + 1;
			ap = (char *)paddr(hp->h_bufp);
			hval = hkey(u.u_dent.d_name);
			if(ap[hval] == 0 || bno < ap[hval])
				ap[hval] = bno;
			return;
		}
	}
	return;
}
