/*
 * 5799-WZQ (C) COPYRIGHT = NONE
 * LICENSED MATERIALS - PROPERTY OF IBM
 */
/* $Header:dfs_vfsops.c 12.0$ */
/* $ACIS:dfs_vfsops.c 12.0$ */
/* $Source: /ibm/acis/usr/sys/dfs/RCS/dfs_vfsops.c,v $ */

#ifndef lint
static char *rcsid = "$Header:dfs_vfsops.c 12.0$";
#endif
#ifdef DFS
#include "param.h"
#include "systm.h"
#include "dir.h"
#include "user.h"
#include "vfs.h"
#include "vnode.h"
#include "../specfs/snode.h"
#include "pathname.h"
#include "uio.h"
#include "dfs.h"
#include "buf.h"
#include "file.h"
#undef	NFS
#include "mount.h"
extern struct dnode *dfs_find();
extern struct dnode *dfs_table[];
extern struct vnode *dfs_make_vnode();
extern struct vnodeops dfs_vnodeops;
extern u_short dfs_unmap_fid();

/*
 * dfs vfs operations.
 */
int dfs_mount();
int dfs_unmount();
int dfs_root();
int dfs_statfs();
int dfs_sync();
int dfs_vget();
struct vnode *dfs_search();

#ifdef DEBUG
int dfs_debug = 0;/*0x10001;*/
extern char *dfs_mkstring();
#define DEBUGF(x,y)	if (x) (y)
#else
#define DEBUGF(x,y)
#endif

struct vfsops dfs_vfsops = {
	dfs_mount,
	dfs_unmount,
	dfs_root,
	dfs_statfs,
	dfs_sync,
	dfs_vget,
};


static struct dfs_mount_tab dos_mount_head = {0};

/*
 * dfs mount vfsop
 * Set up mount info record and attach it to vfs struct.
 */
/*ARGSUSED*/
dfs_mount(vfsp, path, data)
	struct vfs *vfsp;
	char *path;
	caddr_t data;
{
	struct	buf *bp;
	struct vnode *devvp;
	struct vnode *svp;
	struct dfs_args args;
	struct dfs_fsinfo *bb;
	struct dfs_vfs_info *dfs;
	struct dfs_mount_tab *mp;
	int entry_size = 12;
	int ext_partition = 0;
	char *cp;
	int error,offset = 0;
	int i,tmp;
	dev_t dev;

	/*
	 * read block 0
	 * calculate the vfs parameters
	 */
	DEBUGF(dfs_debug & 1,printf("dfs_mount called\n"));

	/*
	 * Get arguments
	 */
	error = copyin(data, (caddr_t)&args, sizeof (struct dfs_args));
	if (error) {
		return (error);
	}

	/*
	 * determine dos extended partition useage
	 */
	for (cp = args.fspec; *cp; cp++) {
		if (*cp == '-') {
			*cp++ = 0;
			while (*cp) {
				if (*cp > '9' || *cp < '0') {
					return(EINVAL);
				}
				ext_partition = ext_partition*10 + (*cp - '0');
				cp++;
			}
			break;
		}
	}
			
	if (error = lookupname(args.fspec,UIO_USERSPACE,FOLLOW_LINK,
						(struct vnode **)0, &svp)) {
		return(error);
	}
	if (svp->v_type != VBLK) {
		VN_RELE(svp);
		return(ENOTBLK);
	}

	/*
	 * make a special (device) vnode for the filesystem
	 */
	dev = svp->v_rdev;
	devvp = VTOS(svp)->s_bdevvp;
	VN_HOLD(devvp);
	VN_RELE(svp);
	if (major(dev) >= nblkdev) {
		VN_RELE(devvp);
		return(ENXIO);
	}

	/*
	 * check to dos mount table
	 */
	for (mp = dos_mount_head.dmp_next; mp ; mp = mp->dmp_next) {
		if ((mp->dmp_dev == dev) && (mp->dmp_ext == ext_partition)) {
			VN_RELE(devvp);
			return(EBUSY);
		}
	}

	mp = (struct dfs_mount_tab *)kmem_alloc(sizeof(struct dfs_mount_tab));
	mp->dmp_last = &dos_mount_head;
	mp->dmp_next = dos_mount_head.dmp_next;
	mp->dmp_dev = dev;
	mp->dmp_ext = ext_partition;
	dos_mount_head.dmp_next = mp;

#ifdef DFS_RO
	if ((vfsp->vfs_flag & VFS_RDONLY) == 0) {
		dfs_free_mp(mp);
		VN_RELE(devvp);
		return(EPERM);
	}
#endif

	/*
	 * open the special device
	 */
	error = VOP_OPEN(&devvp,
		(vfsp->vfs_flag & VFS_RDONLY) ? FREAD : FREAD|FWRITE, u.u_cred);
	if (error) {
		VN_RELE(devvp);
		return (error);
	}


	/*
	 * get the bootblock
	 */
	bp = bread(devvp,0,DFS_BSIZE);
	if (bp->b_flags & B_ERROR) {
		brelse(bp);
		(void) VOP_CLOSE(devvp,((vfsp->vfs_flag & VFS_RDONLY) == 0),
								     u.u_cred);
		VN_RELE(devvp);
		dfs_free_mp(mp);

		return(EIO);
	}

	/*
	 * Search for the extented partition
	 */
	tmp = ext_partition;
	while (tmp--) {
		struct dfs_part_tab	*ptp = DFS_GET_PART(bp->b_un.b_addr);
		int flag;

		if (DFS_GET_MAGIC(bp->b_un.b_addr) != DFS_MAGIC) {
			DEBUGF(dfs_debug & 1,
			printf("dfs_mount: no magic in block 0 magic=0x%x\n",
					      DFS_GET_MAGIC(bp->b_un.b_addr)));
			brelse(bp);
			(void) VOP_CLOSE(devvp,
				((vfsp->vfs_flag & VFS_RDONLY) == 0),
								     u.u_cred);
			VN_RELE(devvp);
			dfs_free_mp(mp);
			return(ENODEV);
		}
		bb = (struct dfs_fsinfo *) bp->b_un.b_addr;
		if (bb->dfsb_jump[0] == 0xeb) {
			DEBUGF(dfs_debug & 1,
			printf("dfs_mount: no partition in block 0\n"));
			brelse(bp);
			(void) VOP_CLOSE(devvp,
				((vfsp->vfs_flag & VFS_RDONLY) == 0),
								     u.u_cred);
			VN_RELE(devvp);
			dfs_free_mp(mp);
			return(ENODEV);
		}

		DEBUGF(dfs_debug & 1,
		 printf("dfs_mount: byte 0 = %d, must have partition table\n",
							    bb->dfsb_jump[0]));
		for (i=0; i < DFS_NUMBER_PART; i++) {
			DEBUGF(dfs_debug & 2,
				printf("part %d is %d\n",i,ptp[i].sys_id));
			if (ptp[i].sys_id == DFS_DOS_EXT) {
				flag++;
				offset += dfs_get_long(ptp[i].rel_start);
				break;
			}
		}
		brelse(bp);
		if (!flag) {
		        DEBUGF(dfs_debug & 1,
			    printf("dfs_mount: no extended partition found\n"));
			(void) VOP_CLOSE(devvp,
				((vfsp->vfs_flag & VFS_RDONLY) == 0),
								     u.u_cred);
			VN_RELE(devvp);
			dfs_free_mp(mp);
			return(ENODEV);
		}
		DEBUGF(dfs_debug & 0x800,
					printf("current offset = %d\n",offset));
		bp = bread(devvp,offset,DFS_BSIZE);
		if (bp->b_flags & B_ERROR) {
			brelse(bp);
			(void) VOP_CLOSE(devvp,
				((vfsp->vfs_flag & VFS_RDONLY) == 0),
								     u.u_cred);
			VN_RELE(devvp);
			dfs_free_mp(mp);
			return(EIO);
		}
	}
		

	/*
	 * This must be either a boot block or a partition block
	 * in either case it has the same magic number.
	 */
	if (DFS_GET_MAGIC(bp->b_un.b_addr) != DFS_MAGIC) {
		DEBUGF(dfs_debug & 1,
			printf("dfs_mount: no magic in block 0 magic=0x%x\n",
					      DFS_GET_MAGIC(bp->b_un.b_addr)));
		brelse(bp);
		(void) VOP_CLOSE(devvp,((vfsp->vfs_flag & VFS_RDONLY) == 0),
								     u.u_cred);
		VN_RELE(devvp);
		dfs_free_mp(mp);
		return(ENODEV);
	}

	bb = (struct dfs_fsinfo *) bp->b_un.b_addr;
	if (bb->dfsb_jump[0] != 0xeb) {
		/*
		 * not a boot block, check for partition table
		 */
		struct dfs_part_tab	*ptp = DFS_GET_PART(bp->b_un.b_addr);
		int dos_offset = -1;

		DEBUGF(dfs_debug & 1,
		 printf("dfs_mount: byte 0 = %d, must have partition table\n",
							    bb->dfsb_jump[0]));
		for (i=0; i < DFS_NUMBER_PART; i++) {
			DEBUGF(dfs_debug & 2,
				printf("part %d is %d\n",i,ptp[i].sys_id));
			if ((ptp[i].sys_id == DFS_DOS_12) ||
					 (ptp[i].sys_id == DFS_DOS_16)) {
				if (ptp[i].sys_id == DFS_DOS_16) {
					entry_size = 16;
				}
				dos_offset = dfs_get_long(ptp[i].rel_start)
								    + offset;
				break;
			}
		}
		brelse(bp);
		if (dos_offset == -1) {
		        DEBUGF(dfs_debug & 1,
				printf("dfs_mount: no dos partition found\n"));
			(void) VOP_CLOSE(devvp,
				((vfsp->vfs_flag & VFS_RDONLY) == 0),
								     u.u_cred);
			VN_RELE(devvp);
			dfs_free_mp(mp);
			return(ENODEV);
		}
		DEBUGF(dfs_debug & 0x800,
					printf("dos offset = %d\n",dos_offset));
		bp = bread(devvp,dos_offset,DFS_BSIZE);
		if (bp->b_flags & B_ERROR) {
			brelse(bp);
			(void) VOP_CLOSE(devvp,
				((vfsp->vfs_flag & VFS_RDONLY) == 0),
								     u.u_cred);
			VN_RELE(devvp);
			dfs_free_mp(mp);
			return(EIO);
		}

		/*
		 * validate the boot block
		 */
		bb = (struct dfs_fsinfo *) bp->b_un.b_addr;
		if ((DFS_GET_MAGIC(bp->b_un.b_addr) != DFS_MAGIC) &&
						(bb->dfsb_jump[0] != 0xeb)) {
			DEBUGF(dfs_debug & 1,
			  printf("dfs_mount: no magic in block 0 magic=0x%x\n",
					      DFS_GET_MAGIC(bp->b_un.b_addr)));
			brelse(bp);
			(void) VOP_CLOSE(devvp,
				((vfsp->vfs_flag & VFS_RDONLY) == 0),
								     u.u_cred);
			VN_RELE(devvp);
			dfs_free_mp(mp);
			return(ENODEV);
		}
	}

	vfsp->vfs_data = (char *)kmem_alloc(sizeof(struct dfs_vfs_info));
	if ((vfsp->vfs_data) == 0) {
		brelse(bp);
		(void) VOP_CLOSE(devvp, ((vfsp->vfs_flag & VFS_RDONLY) == 0),
								     u.u_cred);
		VN_RELE(devvp);
		dfs_free_mp(mp);
		return(ENOSPC);	/* need a better error code */
	}
	dfs = VFSTODFS(vfsp);
	dfs->dfs_vrefcnt = 0;
	dfs->dfs_vdev = dev;
	dfs->dfs_vext = ext_partition;
	dfs->dfs_vdevvp =devvp;
	dfs->dfs_vfirst_cluster = DFS_FIRST_CLUSTER;
	dfs->dfs_vmnt = args.mnt_flags;
	dfs->dfs_vuid = args.def_uid;
	dfs->dfs_vgid = args.def_gid;
	dfs->dfs_vmp = mp;
	dfs->dfs_vfat_entry_size = entry_size;
	if (entry_size == 16) {
		dfs->dfs_veof = DFS_EOF_16;
		dfs->dfs_vmin_eof = DFS_MIN_EOF_16;
	} else {
		dfs->dfs_veof = DFS_EOF_12;
		dfs->dfs_vmin_eof = DFS_MIN_EOF_12;
	}
	dfs->dfs_vcluster_size = bb->dfsb_cluster_size;
	dfs->dfs_vfat_copies = bb->dfsb_fat_copies;
	bzero(dfs->dfs_vfat_bits,FAT_PAGES*sizeof(unsigned long));
	offset += dfs_get_word(bb->dfsb_hidden)+dfs_get_word(bb->dfsb_reserved);
	dfs->dfs_vfat_start = offset;
	dfs->dfs_vfat_pages = dfs_get_word(bb->dfsb_fat_size);
	offset += dfs->dfs_vfat_pages * bb->dfsb_fat_copies;
	dfs->dfs_vdir_start = offset;
	dfs->dfs_vdir_size = dfs_get_word(bb->dfsb_dir_entries) 
						* sizeof(struct dfs_dir);
	offset += dfs->dfs_vdir_size/DFS_BSIZE;
	dfs->dfs_vdata_start = offset;
	dfs->dfs_vfat_entries = ((dfs_get_word(bb->dfsb_device_size) - 
		dfs_get_word(bb->dfsb_reserved) - (dfs->dfs_vdir_size/DFS_BSIZE)
			- (dfs->dfs_vfat_pages * bb->dfsb_fat_copies))/ 
			dfs->dfs_vcluster_size) + DFS_FIRST_CLUSTER;
#ifdef DEBUG
	if (dfs_debug & 4) {
		char	name[9];
		printf("boot block = >\n");
		printf(" jmp = 0x%x 0x%x 0x%x\n",bb->dfsb_jump[0],
					bb->dfsb_jump[1],bb->dfsb_jump[2]);
		bcopy(bb->dfsb_oem,name,8);
		name[8] = 0;
		printf(" oem = %s\n",name);
		printf(" block size = %d\n",dfs_get_word(bb->dfsb_block_size));
		printf(" cluster size = %d\n",bb->dfsb_cluster_size);
		printf(" reserved = %d\n",dfs_get_word(bb->dfsb_reserved));
		printf(" FAT copies = %d\n",bb->dfsb_fat_copies);
		printf(" dir entries = %d\n",dfs_get_word(bb->dfsb_dir_entries));
		printf(" disk size = %d\n",dfs_get_word(bb->dfsb_device_size));
		printf(" media descriptor = 0x%x\n",bb->dfsb_media_descriptor);
		printf(" FAT size = %d\n",dfs_get_word(bb->dfsb_fat_size));
		printf(" sectors = %d\n",dfs_get_word(bb->dfsb_sectors));
		printf(" tracks = %d\n",dfs_get_word(bb->dfsb_tracks));
		printf(" hidden = %d\n",dfs_get_word(bb->dfsb_hidden));
	}
#endif DEBUG

	brelse(bp);
	dfs->dfs_vfat = (char *)kmem_alloc(dfs->dfs_vfat_pages * DFS_BSIZE);
	for (i=0; i < dfs->dfs_vfat_pages; i++) {
		bp = bread(devvp,dfs->dfs_vfat_start+i,DFS_BSIZE);
		if (bp->b_flags & B_ERROR) {
			kmem_alloc(dfs->dfs_vfat,
					dfs->dfs_vfat_pages * DFS_BSIZE);
			kmem_free((caddr_t)dfs, 
				(u_int)sizeof(struct dfs_vfs_info));
			brelse(bp);
			(void) VOP_CLOSE(devvp, 
				((vfsp->vfs_flag & VFS_RDONLY) == 0), u.u_cred);
			VN_RELE(devvp);
			dfs_free_mp(mp);
			return(EIO);
		}
		bcopy(bp->b_un.b_addr,dfs->dfs_vfat + i*DFS_BSIZE, DFS_BSIZE);
		brelse(bp);
	}
#ifdef DEBUG
	if (dfs_debug & 8) {
		printf("dfs vfs info => \n");
		printf(" fat * = 0x%x\n",dfs->dfs_vfat);
		printf(" dev vnode = 0x%x\n",dfs->dfs_vdevvp);
		printf(" reference count = %d\n",dfs->dfs_vrefcnt);
		printf(" mount flags = 0x%x\n",dfs->dfs_vmnt);
		printf(" mount dev = 0x%x\n",dfs->dfs_vdev);
		printf(" default uid = %d\n",dfs->dfs_vuid);
		printf(" default gid = %d\n",dfs->dfs_vgid);
		printf(" eof = 0x%x\n",dfs->dfs_veof);
		printf(" min eof = 0x%x\n",dfs->dfs_vmin_eof);
		printf(" first cluster = %d\n",dfs->dfs_vfirst_cluster);
		printf(" FAT entries = %d\n",dfs->dfs_vfat_entries);
		printf(" FAT ent size = %d\n",dfs->dfs_vfat_entry_size);
		printf(" cluster size = %d\n",dfs->dfs_vcluster_size);
		printf(" FAT copies = %d\n",dfs->dfs_vfat_copies);
		printf(" FAT start = %d\n",dfs->dfs_vfat_start);
		printf(" FAT pages = %d\n",dfs->dfs_vfat_pages);
		printf(" dir start = %d\n",dfs->dfs_vdir_start);
		printf(" dir size = %d\n",dfs->dfs_vdir_size);
		printf(" data start = %d\n",dfs->dfs_vdata_start);
	}
#endif DEBUG
	DEBUGF(dfs_debug & 1, printf("dfs_mount: mount complete\n"));
	return(0);
}

/*
 * vfs operations
 */

dfs_unmount(vfsp)
	struct vfs *vfsp;
{
	struct dfs_vfs_info *dfs = VFSTODFS(vfsp);

	DEBUGF(dfs_debug & 1,printf("dfs_unmount called\n"));
	if (dfs->dfs_vrefcnt) {
		DEBUGF(dfs_debug & 1, printf("dfs_umount busy\n"));
		return (EBUSY);
	}
		
	(void) VOP_CLOSE(dfs->dfs_vdevvp,((vfsp->vfs_flag & VFS_RDONLY) == 0),
								     u.u_cred);
	VN_RELE(dfs->dfs_vdevvp);

	kmem_free(dfs->dfs_vfat, dfs->dfs_vfat_pages * DFS_BSIZE);
	kmem_free((caddr_t)dfs, (u_int)sizeof(struct dfs_vfs_info));
	dfs_free_mp(dfs->dfs_vmp);
	DEBUGF(dfs_debug & 1,printf("dfs_unmount successful\n"));
	return(0);
}

dfs_free_mp(mp)
	struct dfs_mount_tab *mp;
{
	mp->dmp_last->dmp_next = mp->dmp_next;
	if (mp->dmp_next) {
		mp->dmp_next->dmp_last = mp->dmp_last;
	}
	kmem_free((caddr_t)mp,sizeof(struct dfs_mount_tab));
}

/*
 * find root of dfs
 */
int
dfs_root(vfsp, vpp)
	struct vfs *vfsp;
	struct vnode **vpp;
{
        struct  dnode *dp;
	struct	dfs_vfs_info *dfs = VFSTODFS(vfsp);

	DEBUGF(dfs_debug & 1,printf("dfs_root called\n"));
        if (dp = dfs_find(dfs_put_start(DFS_ROOT_FID), vfsp)) {
		*vpp = DTOV(dp);
		VN_HOLD(*vpp);
		DEBUGF(dfs_debug & 1,printf("dfs_root found in free list\n"));
                return(0);
        }
        dp=(struct dnode *)kmem_alloc(sizeof(struct dnode));
        bzero(dp,sizeof(struct dnode));
        bcopy(" ** ROOT **",dp->dfs_dir.dfs_name,DFS_MAX_NAME_LENGTH);
        dp->dfs_parent = 0;
        dp->dfs_dir_offset = 0;
	dp->dfs_dir.dfs_attributes = DFS_DIRECTORY;
	dp->dfs_dir.dfs_start = dfs_put_start(DFS_ROOT_FID);
	dp->dfs_dir.dfs_size = dfs_put_size(0);
        VN_INIT(DTOV(dp),vfsp,VDIR,dfs->dfs_vdev);
	*vpp = DTOV(dp);
        (*vpp)->v_op = &dfs_vnodeops;
	(*vpp)->v_flag |= VROOT;
        dfs->dfs_vrefcnt++;
        dfs_save(dp);
	DEBUGF(dfs_debug & 1,printf("dfs_root allocated\n"));
        return(0);
}

/*
 * Get file system statistics.
 */
int
dfs_statfs(vfsp, sbp)
	register struct vfs *vfsp;
	struct statfs *sbp;
{
	struct dfs_vfs_info *dfs = VFSTODFS(vfsp);
	int	tcluster;
	int	free_clust = 0;
	int	total_clust = 0;

	DEBUGF(dfs_debug & 1,printf("dfs_statfs called\n"));
	for (tcluster = dfs->dfs_vfirst_cluster;
			tcluster < dfs->dfs_vfat_entries; tcluster++) {
		if (dfs_get_cluster(dfs,tcluster) == DFS_FREE) {
			free_clust++;
		}
		total_clust++;
	}
	DEBUGF(dfs_debug & 2,
	 printf("total_clust = %d, free = %d, first_clust = %d fat_ent = %d\n",
		   total_clust,free_clust,dfs->dfs_vfirst_cluster,
							dfs->dfs_vfat_entries));
	sbp->f_bsize = DFS_BSIZE;
	sbp->f_blocks = total_clust * dfs->dfs_vcluster_size;
	sbp->f_bfree = sbp->f_bavail = free_clust * dfs->dfs_vcluster_size;

	/*
	 * for the future, these could be stats on the root dir.
	 */
	sbp->f_files = -1;
	sbp->f_ffree = -1;
	bcopy((caddr_t)&vfsp->vfs_fsid,
		    (caddr_t)&sbp->f_fsid, sizeof (fsid_t));
	DEBUGF(dfs_debug & 1,printf("dfs_statfs completed\n"));
	return(0);
}

/*
 * Flush any pending I/O, invalidate all our buffers, re-read the fat.
 * this attempts to try to keep in sync with what os/2 believes the world
 * looks like.. NOTE: there are still race and buffered conditions (all
 * referenced vnode's still have the directory 
 */
int
dfs_sync(vfsp)
	struct vfs *vfsp;
{
	struct dfs_vfs_info *dfs = VFSTODFS(vfsp);
	struct vnode *devvp = dfs->dfs_vdevvp;
	struct buf *bp;
	int i;

	DEBUGF(dfs_debug & 1,printf("dfs_sync called\n"));

	/* invalidate currently read buffers */
	binvalfree(devvp);

	/* reread the fat */
	for (i=0; i < dfs->dfs_vfat_pages; i++) {
		int	pi = i / (sizeof(unsigned long)*8);
		int	si = i % (sizeof(unsigned long)*8);

		dfs_want_fat(vfsp,pi,1 << si);
		bp = bread(devvp,dfs->dfs_vfat_start+i,DFS_BSIZE);
		if (bp->b_flags & B_ERROR) {
			brelse(bp);
			return(EIO);
		}
		bcopy(bp->b_un.b_addr,dfs->dfs_vfat + i*DFS_BSIZE, DFS_BSIZE);
		brelse(bp);
	}

	/* refresh the vnode cached information */
	for (i=0; i < DFS_HASH_SIZE; i++) {
		struct dnode *hashp = dfs_table[i];
		while (hashp) {
			if (DTOV(hashp)->v_flag & VROOT) {
				hashp = hashp->dfs_next;
				continue;
			}
			dlock(hashp->dfs_parent);
			dfs_read(hashp->dfs_parent,hashp->dfs_dir_offset,
				sizeof(struct dfs_dir),VTODir(hashp),0);
			dunlock(hashp->dfs_parent);
			hashp = hashp->dfs_next;
		}
	}

	DEBUGF(dfs_debug & 1,printf("dfs_sync completed\n"));
	return(0);
}

dfs_vget(vfsp, vpp, fidp)
	struct vfs *vfsp;
	struct vnode **vpp;
	struct fid *fidp;
{
	struct dfs_fid *dfid = (struct dfs_fid *)fidp;
	struct dnode *dp;
	struct vnode *vp;
	int	error,fid = dfs_unmap_fid(dfid->dfid_start);

	/*
	 * we can always find the root vnode
	 */
	DEBUGF(dfs_debug & 0x10000,printf("dfs_vget called %x %x\n",fid,
							dfid->dfid_start));
	if (fid == dfs_put_start(DFS_ROOT_FID)) {
		DEBUGF(dfs_debug & 0x10000,printf("dfs_vget getting root\n"));
		return(dfs_root(vfsp,vpp));
	}
	/*
	 * look the vnode up in the cache
	 */
        if (dp = dfs_find(fid, vfsp)) {
		DEBUGF(dfs_debug & 0x10000,printf("dfs_vget found in cache\n"));
		*vpp = DTOV(dp);
		VN_HOLD(*vpp);
                return(0);
        }
	/*
	 * we need to really look it up now, but for now we just don't find it.
	 */
	DEBUGF(dfs_debug & 0x10000,printf("dfs_vget searching directory\n"));
	error = dfs_root(vfsp,&vp);
	if (error) {
		*vpp = NULL;
		return(0);
	}
	*vpp = dfs_search(vp,fid);
	if (!*vpp) {
		DEBUGF(dfs_debug & 0x10000,printf("dfs_vget not found\n"));
	} 
	DEBUGF(dfs_debug & 0x10000,printf("dfs_vget done\n"));
	return(0);
}

struct vnode *
dfs_search(vp,fid)
	struct vnode *vp;
	u_short	fid;
{
	struct dfs_dir *ddp = VTODir(vp);
	struct	buf *bp;
	struct	dfs_dir *lpd;
	struct	vnode	*pvp;
	int	count,offset,error;
	struct dnode *dp = VTOD(vp);

	if (!dfs_is_directory(ddp)) {
		VN_RELE(vp);
		return(0);
	}

	dlock(vp);
	bp = geteblk(DFS_BSIZE);
	count = dfs_get_dir_size(vp,ddp);
	dp->dfs_last_index = 0;
	dp->dfs_dir_index = DFS_BSIZE;
	dp->dfs_last_dir = (struct dfs_dir *)bp->b_un.b_addr;
	while (vp) {
	   DEBUGF(dfs_debug & 0x20000,
		 printf("resuming search in directory <%s> for file id = %d\n"
				    ,dfs_mkstring(VTODir(vp)->dfs_name),fid));

	   for (offset = dp->dfs_last_index; offset < count; 
							offset += DFS_BSIZE) {
		int	i;

		error = dfs_read(vp,offset,DFS_BSIZE,bp->b_un.b_addr,0);
		if (error) {
			brelse(bp);
			dunlock(vp);
			VN_RELE(vp);
			return(0);
		}
		for (lpd = dp->dfs_last_dir, i = dp->dfs_dir_index; 
		  i > 0 && lpd->dfs_flag; lpd++, i -= sizeof(struct dfs_dir)) {
			if (dfs_is_volume(lpd)) {
				continue;
			}
			if (lpd->dfs_flag == DFS_DIR_ERASED) {
				continue;
			}
			/*
			 * have we found it ?
			 */
			if (lpd->dfs_start == fid) {
				struct vnode *nvp;
				nvp = dfs_make_vnode(vp,lpd,fid,
					(caddr_t)lpd - bp->b_un.b_addr+offset);
				brelse(bp);
				dunlock(vp);
				return(nvp);
			}
			/* maybe it's in this directory
			 */
			if (dfs_is_directory(lpd) && lpd->dfs_name[0] != '.') {
				struct vnode *dvp;

				dvp = dfs_make_vnode(vp,lpd,lpd->dfs_start,
					(caddr_t)lpd - bp->b_un.b_addr+offset);
				dunlock(vp);
				dlock(dvp);
				dp->dfs_last_index = offset;
				dp->dfs_last_dir = lpd + 1;
				dp->dfs_dir_index = i - sizeof(struct dfs_dir);
				VN_RELE(vp);	/*dvp is still referencing vp */
				vp = dvp;
				dp = VTOD(vp);
				dp->dfs_last_index = 0;
				dp->dfs_last_dir = 
					(struct dfs_dir *)bp->b_un.b_addr;
				dp->dfs_dir_index = DFS_BSIZE;
				offset = (-DFS_BSIZE);
				i = 0;
				count = dfs_get_dir_size(vp,VTODir(vp));
	   			DEBUGF(dfs_debug & 0x10000,
		 	    printf("searching directory <%s> for file id = %d\n"
				    ,dfs_mkstring(VTODir(vp)->dfs_name),fid));
			}
		}
		/* no directory entries after the first notused... */
		if (i > 0) 
			break;
	   }
	   pvp = dp->dfs_parent;
	   dunlock(vp);
	   if (pvp) {
		VN_HOLD(pvp);
		dlock(pvp);
	   	dp = VTOD(pvp);
	   }
	   VN_RELE(vp);
	   vp = pvp;
	}
	brelse(bp);

	/*
	 * it doesn't exist....
	 */
	return(0);
}
#endif DFS
