/*
 * 5799-WZQ (C) COPYRIGHT IBM CORPORATION 1988
 * LICENSED MATERIALS - PROPERTY OF IBM
 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
 */
/* $Header:afs_gfsops.c 12.1$ */
/* $ACIS:afs_gfsops.c 12.1$ */
/* $Source: /ibm/acis/usr/sys/afs/RCS/afs_gfsops.c,v $ */

#ifndef lint
static char *rcsid = "$Header:afs_gfsops.c 12.1$";
#endif

#ifdef	vax
#include "../h/param.h"
#include "../h/systm.h"
#include "../h/user.h"
#include "../h/uio.h"
#include "../h/file.h"
#include "../h/stat.h"
#include "../h/fs_types.h"
#include "../h/mount.h"
#include "../h/kernel.h"
#include "../h/socket.h"
#include "../h/ioctl.h"
#include "../h/vfs.h"
#include "../h/vnode.h"
#include "../net/if.h"
#include "../netinet/in.h"
#include "../h/mbuf.h"
#include "../rpc/types.h"
#include "../rpc/xdr.h"

#include "../afs/osi.h"
#define RFTP_INTERNALS 1
#include "../afs/r.h"
#include "../afs/rftp.h"
#include "../afs/lock.h"
#include "../afs/volerrors.h"
#include "../afsint/rvice.h"
#include "../afsint/rvaux.h"
#include "../afs/afs.h"


/*
 * Convert between vnode types and gnode formats
 */

enum vtype gftovt_tab[] = {
	VNON, VCHR, VDIR, VBLK, VREG, VLNK, VSOCK, VFIFO, VBAD
};
int vttogf_tab[] = {
	0, GFREG, GFDIR, GFBLK, GFCHR, GFLNK, GFSOCK,GFPORT, GFMT
};

/*
 * afs vfs operations.
 */
struct mount *afs_gmount();
extern int afs_statfs();

struct gnode	*ufs_gget();
struct gnode	*ufs_galloc();
int	ufs_gfree(), ufs_fcntl(), ufs_seek();
struct fs_data	*afs_getfsdata();
extern int afs_strategy();	/* defined in afs_vnodeops.c */

int afs_gumount();
int afs_sbupdate();
struct gnode *afs_namei();
int afs_glink();
int afs_unlink();
struct gnode *afs_gmkdir();
int afs_grmdir();
struct gnode *afs_makenode();
int afs_grename();
int afs_readdir();
int afs_rele();
int afs_syncgp();
int afs_trunc();
int afs_getval();
int afs_rwgp();
int afs_stat();
int afs_lock();
int afs_unlock();
int afs_update();
int afs_gopen();
int afs_gclose();
int afs_select();
int afs_greadlink();
int afs_gsymlink();
struct fs_data *afs_getfsdata();
int afs_gbmap();

struct mount_ops AFS_mount_ops = {
	afs_gumount,
	afs_sbupdate,	/* NOOP for afs */
	0,		/* find a usage for gget? */
	afs_getfsdata,
};

struct gnode_ops AFS_gnode_ops = {
	afs_namei,
	afs_glink,
	afs_unlink,
	afs_gmkdir,
	afs_grmdir,
	afs_makenode,
	afs_grename,
	afs_readdir,
	afs_rele,
	afs_syncgp,
	afs_trunc,
	afs_getval,
	afs_rwgp,
	0,		/* find a usage or rlock? */
	ufs_seek,	/* use standard UFS seek */
	afs_stat,
	afs_lock,
	afs_unlock,
	afs_update,
	afs_gopen,
	afs_gclose,
	afs_select,
	afs_greadlink,
	afs_gsymlink,
	0,		/* no usage for fcntl yet */
	0,		/* find a usage for freegn? */
	afs_gbmap
};
struct mount_ops *afs_mount_ops = &AFS_mount_ops;
struct gnode_ops *afs_ops = &AFS_gnode_ops;

/*
 * afs_mount is called from the generic mount system call
 */

struct mount *
afs_gmount(special, path, flag, mp, ops)
caddr_t special, path;
int flag;
struct mount *mp;
char *ops;
{
    extern struct vfs *afs_globalVFS;
    extern int afs_root();
    struct gnode *rootgp = NULL;	/* root gnode */
    struct vfs *vfsp;			/* nfs-like vfs handle */
    int error;

  
    vfsp = MP_TO_VFSP(mp);
    mp->m_bufp = (struct buf *) (NODEV);	/* to reserve this slot */
    mp->m_dev =	getpdev();			/* should be NODEV? */
    mp->iostrat	= afs_strategy;			/* set it immediately */
    mp->m_ops = afs_mount_ops;
    mp->m_fstype = GT_AFS;
    mp->m_flags = (flag ? M_RONLY : 0);
    afs_globalVFS = vfsp;
    afs_globalVFS->vfs_data = (char *)mp;
    mp->m_bsize = 8192;
    error = afs_root(vfsp, &rootgp);
#ifdef	AFSDEBUG
    afs_dp("Leaving afs_mount: mp=%x, m_dev=%x, m_ops=%x, err=%d\n", mp, mp->m_dev, mp->m_ops, error);
#endif
    if (error) goto mount_error;
    /*
      * check_mountp will verify local mount point exists and is
      * ok to mount on.
      */

    if (!(check_mountp(mp, path)))
	goto mount_error;    
    mp->m_rootgp = (struct gnode *) rootgp;
    return(mp);
mount_error:
    if (rootgp) {
	GRELE((struct gnode *)rootgp);
    }
    return((struct mount *)NULL);
}


/* Not used yet; don't umount afs */
afs_gumount(mp, force)
register struct mount *mp;
register int force;	    /* XXX ignored */
{
    register struct gnode *gp = mp->m_gnodp;
    register struct gnode *rgp = mp->m_rootgp;

    afs_rele(rgp);
    gp->g_flag &= ~GMOUNT;
    GRELE(gp);
    return(0);
}


afs_sbupdate(mp)
register struct mount *mp;
{
    return 0;
}


afs_gget(){}		/* Currently unused */



int afs_glink(gp, ndp)
register struct gnode *gp;
register struct nameidata *ndp;
{
    u.u_error = afs_link(gp, ndp->ni_pdir, ndp->ni_cp, u.u_cred);
    afs_rele(gp);
    gput(ndp->ni_pdir);
    return(u.u_error);
}

afs_unlink(gp, ndp)
register struct gnode *gp;
register struct nameidata *ndp;
{
    u.u_error = afs_remove(ndp->ni_pdir, ndp->ni_cp, u.u_cred);
    return(u.u_error);
}


/*
 * GET_FILENAME leaves a pointer to the last component of a pathname
 * in cp.  It is used to patch up an awkwardness in the GFS mkdir
 * interface; GFS passes us the full pathname of the directory to
 * be created, but we must send only the last component over the wire.
 */
#define GET_FILENAME(pnamep, cp) \
	{ (cp) = &(pnamep)[strlen(pnamep)]; \
	  while (((cp) > (pnamep)) && (*(cp) != '/')) (cp)--; \
	  if (*(cp) == '/') (cp)++; \
	}

struct gnode *
afs_gmkdir(pgp, name, mode)
register struct gnode *pgp;
register char *name;
register int mode;
{
    struct vattr va;
    struct gnode *gp;
    register char *cp;

    GET_FILENAME(name, cp);
    vattr_null(&va);
    va.va_type = VDIR;
    va.va_mode = (mode & 0777) & ~u.u_cmask;
    u.u_error = afs_mkdir(pgp, cp, &va, &gp, u.u_cred);
    gput(pgp);
    return(u.u_error ? NULL : gp);
}


afs_grmdir(gp, ndp)
register struct gnode *gp;
register struct nameidata *ndp;
{
    if (gp == ndp->ni_pdir) {
	u.u_error = EINVAL;
	afs_rele(gp);
	gput(ndp->ni_pdir);
	return(u.u_error);
    }
    if ((gp->g_mode & GFMT) != GFDIR)
	u.u_error = ENOTDIR;
    if (gp->g_dev != ndp->ni_pdir->g_dev)
	u.u_error = EBUSY;
    if (!u.u_error)
	u.u_error = afs_rmdir(ndp->ni_pdir, ndp->ni_cp, u.u_cred);
    gput(gp);
    gput(ndp->ni_pdir);
    return(u.u_error);
}

struct gnode *
afs_makenode(mode,dev, ndp)
register int mode;
register dev_t dev;
struct nameidata *ndp;
{
	struct vattr _va;
	register struct vattr *va = &_va;
	struct gnode *vpp;

	/*
	 * replication of the gross mknod hack
	 */

	vattr_null(va);

	switch (mode & GFMT) {

	    case GFREG:
		va->va_type = VREG;
		break;

	    case GFCHR:
		va->va_type = VCHR;
		printf("afs_makenode: making chr file '%s'\n",ndp->ni_cp);
		break;

	    case GFBLK:
		va->va_type = VBLK;
		printf("afs_makenode: making blk file '%s'\n",ndp->ni_cp);
		break;

	    case GFPORT:
		va->va_type = VFIFO;
		printf("afs_makenode: making fifo file '%s'\n",ndp->ni_cp);
		break;

	    case GFSOCK:
		va->va_type = VSOCK;
		printf("afs_makenode: making sock file '%s'\n",ndp->ni_cp);
		break;

	default:
		u.u_error = EINVAL;
		goto bad;
	}

	va->va_mode = (mode & ~u.u_cmask) & 0xffff;
	va->va_size = 0;
	va->va_rdev = dev;

	u.u_error = afs_create(ndp->ni_pdir, ndp->ni_cp, va, NONEXCL, mode, &vpp, u.u_cred);

bad:
	gput(ndp->ni_pdir);	
	if (u.u_error)
		return(NULL);
	else {
		afs_lock(vpp);	
		return(vpp);
	}
}

afs_grename(gp, ssd, srcndp, tsd, targetndp, flag)
register struct gnode *gp;
struct gnode *ssd, *tsd;
register struct nameidata *srcndp, *targetndp;
{
    register struct gnode *tgp;
    register char *tcp, *scp;

    scp = srcndp->ni_cp;
    afs_unlock(srcndp->ni_pdir);
    if (srcndp->ni_pdir != gp)
	afs_unlock(gp);

    if (!strcmp(scp, ".") || !strcmp(scp, "..")) {
	u.u_error = EINVAL;
	goto free_src;
    }

    tgp = GNAMEI(targetndp);
    if(u.u_error)
	goto free_src;
    tcp = targetndp->ni_cp;
    if(!strcmp(tcp, ".") || !strcmp(tcp, "..")) {
	u.u_error = EINVAL;
	goto free_targ;
    }

    if (gp->g_mp != targetndp->ni_pdir->g_mp) {
	u.u_error = EXDEV;
	goto free_targ;
    }

    if (ISREADONLY(gp->g_mp)) {
	u.u_error = EROFS;
	goto free_targ;
    }

    u.u_error = afs_rename(srcndp->ni_pdir, scp, targetndp->ni_pdir,
			    tcp, u.u_cred);

free_targ:
	if(tgp)	gput(tgp);
	gput(targetndp->ni_pdir);

free_src:	
	afs_rele(gp);
	afs_rele(srcndp->ni_pdir);

	return(u.u_error);
}

    

afs_rele(gp)
register struct gnode *gp;
{
    struct gnode *vp;

    if(gp->g_count <= 0)
	panic("afs_rele: zero count");
    if(gp->g_count == 1)
	afs_inactive(gp, u.u_cred);


/* Check the count again before freeing up the gnode, in case
* another reference has been created (we may block in the
* afs_inactive call).
* We have to worry about this because the gnode is still in the
* cache during the period that we are making it inactive.
*/
	if (--gp->g_count == 0) {
/*
FIX THIS TOO, PLEASE....
		freegnode(gp);
*/
	}
}

afs_syncgp(gp, cred)
register struct gnode *gp;
register struct ucred *cred;
{
    return(u.u_error = afs_fsync(gp, cred));
}

afs_trunc(gp, newsize, cred)
register struct gnode *gp;
register unsigned newsize;
register struct ucred *cred;
{
    struct vattr va;

    vattr_null(&va);
    va.va_size = newsize;
    return(u.u_error = afs_setattr(gp, &va, cred));
}

afs_getval(gp)
register struct gnode *gp;
{
    struct vattr vattr;
    int locked = 0;

    /* don't hold gnode locked while going over the net */
    /* it should be locked now, but check in case there is */
    /* a way to get here with it unlocked */
    if (gp->g_flag & GLOCKED) {
	locked = 1;
	gfs_unlock(gp);
    }

    u.u_error = afs_getattr(gp, &vattr, u.u_cred);
    /*  XXXXXXXX More to do -- CAREFUL XXXXXXXXXXXX */
    gp->g_number = vattr.va_nodeid;
    gp->g_mode = vattr.va_mode;
    gp->g_nlink = vattr.va_nlink;
    gp->g_uid = vattr.va_uid;
    gp->g_gid = vattr.va_gid;
    gp->g_rdev = (dev_t)vattr.va_rdev;
    gp->g_size = vattr.va_size;
    gp->g_atime.tv_sec = vattr.va_atime.tv_sec;
    gp->g_atime.tv_usec = vattr.va_atime.tv_usec;
    gp->g_mtime.tv_sec = vattr.va_mtime.tv_sec;
    gp->g_mtime.tv_usec = vattr.va_mtime.tv_usec;
    gp->g_ctime.tv_sec = vattr.va_ctime.tv_sec;
    gp->g_ctime.tv_usec = vattr.va_ctime.tv_usec;
/*
FIX THIS PLEASE....
    gp->g_mp->m_fs_data->fd_otsize = vattr.va_blocksize;
*/
    gp->g_blocks = vattr.va_blocks;

    /* lock gnode again if it was locked coming in */
    /* (and hope it wasn't changed in any important way!!) */
    if (locked)
	gfs_lock(gp);

    return(u.u_error);
}

afs_rwgp(gp, uiop, rw, ioflag, cred)
register struct gnode *gp;
register struct uio *uiop;
enum uio_rw rw;
int ioflag;
struct ucred *cred;
{  
    return(afs_rdwr(gp, uiop, rw, ioflag, cred));
}


afs_stat(gp, sb)
register struct gnode *gp;
register struct stat *sb;
{
    struct vattr vattr;

    if (u.u_error = afs_getattr(gp, &vattr, u.u_cred)) {
	bzero(sb, sizeof(struct stat));
	return(u.u_error);
    }
    sb->st_dev = vattr.va_fsid;
    sb->st_ino = vattr.va_nodeid;
    sb->st_mode = vattr.va_mode;
    sb->st_nlink = vattr.va_nlink;
    sb->st_uid = vattr.va_uid;
    sb->st_gid = vattr.va_gid;
    sb->st_rdev = (dev_t)vattr.va_rdev;
    sb->st_size = vattr.va_size;
    sb->st_atime = vattr.va_atime.tv_sec;
    sb->st_spare1 = vattr.va_atime.tv_usec;
    sb->st_mtime = vattr.va_mtime.tv_sec;
    sb->st_spare2 = vattr.va_mtime.tv_usec;
    sb->st_ctime = vattr.va_ctime.tv_sec;
    sb->st_spare3 = vattr.va_ctime.tv_usec;
    sb->st_blksize = vattr.va_blocksize;
    sb->st_blocks = vattr.va_blocks;
    sb->st_spare4[0] = sb->st_spare4[1] = 0;
    return (0);
}


afs_lock(gp)	    /* XXXXXXXX */
	register struct gnode *gp;
{
	if (gp->g_count <= 0)
		panic("afs_lock: unrefed gnode");
	while (gp->g_flag & GLOCKED) {
		gp->g_flag |= GWANT;
		sleep((caddr_t)gp, PINOD);
	}
	gp->g_flag |= GLOCKED;
}

afs_unlock(gp)	    /* XXXXXXXXX */
	register struct gnode *gp;
{
	if (!(gp->g_flag & GLOCKED))
		panic("afs_unlock: locked gnode isn't");
	gp->g_flag &= ~GLOCKED;
	if (gp->g_flag&GWANT) {
		gp->g_flag &= ~GWANT;
		wakeup((caddr_t)gp);
	}
}


afs_update(gp, atime, mtime, wait, cred)
register struct gnode *gp;
register struct timeval	*atime, *mtime;
register int	wait;
register struct	ucred	*cred;
{
    struct vattr	va;	
    register int error;

/*  Implement wait for? */
	
    vattr_null(&va);

    if (gp->g_flag & GCHG) {
	if(gp->g_flag & GCID) {
	    va.va_uid = gp->g_uid;
	    va.va_gid = gp->g_gid;
	}
	if(gp->g_flag & GCMODE)
	    va.va_mode = gp->g_mode;
/*	    Should this be in there too??
	if (gp-_flag & GCHLNK)
	    va.va_nlink = gp->g_nlink;
*/
  
	gp->g_flag &= ~(GCID | GCMODE | GCLINK | GCHG);
    }

    if (gp->g_flag & GACC) {
	gp->g_flag &= ~GACC;
	/*
	 * fixes 2002 /dev/tty* problem when 
	 * pointer is uninitialized
	 */
	if (atime != NULL) {
	    va.va_atime.tv_sec = atime->tv_sec;
	    va.va_atime.tv_usec = atime->tv_usec;
	} else {
	    va.va_atime.tv_sec = time.tv_sec;
	    va.va_atime.tv_usec = time.tv_usec;
	}
    }
    if (gp->g_flag & GUPD) {
	gp->g_flag &= ~GUPD;
	/*
	 * fixes 2002 /dev/tty* problem when 
	 * pointer is uninitialized
	 */
	if (mtime != NULL) {
	    va.va_mtime.tv_sec = mtime->tv_sec;
	    va.va_mtime.tv_usec = mtime->tv_usec;
	} else {
	    va.va_mtime.tv_sec = time.tv_sec;
	    va.va_mtime.tv_usec = time.tv_usec;
	}
    }

    error = afs_setattr(gp, &va, cred);
    return(error);
}

afs_gopen(gp, ioflag)
struct gnode *gp;
int ioflag;
{
    return(afs_open(&gp, ioflag, u.u_cred));
}

afs_gclose(gp, ioflag)
struct gnode *gp;
int ioflag;
{
    return(afs_close(gp, ioflag, u.u_cred));
}


afs_select(gp, which, cred)
struct gnode *gp;
int	which;
struct	ucred	*cred;
{
    return(EOPNOTSUPP);
}

afs_greadlink(gp, uio)
register struct gnode *gp;
register struct uio *uio;
{
    return(u.u_error = afs_readlink(gp, uio, u.u_cred));
}


afs_gsymlink(ndp, to)
register struct nameidata *ndp;
register char *to;
{
    struct vattr va;

    vattr_null(&va);
    va.va_mode = (0777 & ~u.u_cmask) & 0xffff;
    afs_unlock(ndp->ni_pdir);
    u.u_error = afs_symlink(ndp->ni_pdir, ndp->ni_cp, &va, to, u.u_cred);
    afs_rele(ndp->ni_pdir);
    return(u.u_error);
}

struct fs_data *
afs_getfsdata(mp)
register struct mount *mp;
{
    register struct fs_data *fsdata = mp->m_fs_data;
    struct statfs statfs;

    u.u_error = afs_statfs(mp, &statfs);

    if (!u.u_error) {
	fsdata->fd_fstype = GT_AFS;
	fsdata->fd_gtot = 0;
	fsdata->fd_gfree = 0;
	fsdata->fd_btot = statfs.f_blocks;
	fsdata->fd_bfree = statfs.f_bfree;
	fsdata->fd_bfreen = statfs.f_bavail;
/*	fsdata->fd_mtsize = fsdata->fd_otsize = MP_TO_MIP(mp)->mi_stsize; */
    }
	
    return(fsdata);
}

afs_gbmap(gp, gbn, rw, size, sync)
register struct	gnode *gp;  /* gnode */
register daddr_t gbn;	    /* virtual block */
int rw,	size, sync;	    /* ignore for afs */
{
    daddr_t lbn;    

    afs_bmap(gp,gbn,(struct gnode **)0, &lbn);
    return((int)lbn);
}

/*
 * The routine afs_namei performs pathname lookup in remote filesystems,
 * with some side effects. Note it's trying to imitate what Sun's lookupname()
 * does.  Unlike the ufs namei, we leave a pointer to the last pathname component
 * in ndp->ni_cp.  Other AFS code depends on this.
 */ 
struct gnode *
afs_namei(ndp)
register struct nameidata *ndp;
{
    struct gnode *gpp, *gp;
    register char *slash, *ptr, *cp, *ncp;
    int lockparent, flag, i;
    char name[AFS_MAXNAMLEN+1];
    struct uio _auio;
    register struct uio *auio = &_auio;
    struct iovec _aiov;
    register struct iovec *aiov = &_aiov;
    struct mount *mpp;
    int name_len;
	


	afs_dp("Entering afs_namei to look up %s\n", ndp->ni_cp);
	flag = ndp->ni_nameiop & (LOOKUP | CREATE | DELETE);
	lockparent = ndp->ni_nameiop & LOCKPARENT;
	gp = ndp->ni_pdir;
	afs_unlock(gp);

#ifdef AFSDEBUG
	if (gp == NULL) panic("afs_namei: no parent");
	if ((flag != LOOKUP) && (flag != CREATE) && (flag != DELETE))
		panic("afs_namei: bad flags");
#endif AFSDEBUG
	
	cp = ndp->ni_cp;
	while (*cp == '/')
		cp++;

	while(*cp) {

		/*
		 * Find the next pathname component, move it into name[].
		 */

		for (i = 0; cp[i] != 0 && cp[i] != '/'; i++) {
			if (i == AFS_MAXNAMLEN)
				u.u_error = ENAMETOOLONG;
			if (cp[i] & 0200)
				u.u_error = EINVAL;
			if (u.u_error) {
				afs_rele(gp);
				return(NULL);
			}
			name[i] = cp[i];
		}

		name_len = i + 1;
		name[i] = '\0';
		ncp = cp + i;

		/*
		 * If we're at the root of a filesystem and the next
		 * component is ".." then we just bounce back to caller.
		 */

		if((name[0] == '.') && (name[1] == '.') && !name[2]
		&& ((gp == gp->g_mp->m_rootgp) || (gp == u.u_rdir))) {
			afs_rele(gp);
			/*
			 * Necessary for no ..
 			 * Check if at the root directory (/) 
 			 * If so, set gp to itself
			 */
 			if (gp->g_mp->m_gnodp != (struct gnode *) NULL)
 				gp = gp->g_mp->m_gnodp;
			gfs_lock(gp);
			gp->g_count++;
			gp->g_flag |= GINCOMPLETE;
			ndp->ni_pdir = gp;
			/*
			 * Check if at the root of the
			 * filesystem.  If so, strip off ..
			 */
			if (gp == gp->g_mp->m_rootgp || gp == u.u_rdir)
				ndp->ni_cp = ncp;
			else
				ndp->ni_cp = cp;
			return(gp);
		}
		
		/*
		 * Now look up the current pathname component.
		 */

		u.u_error = afs_lookup(gp, name, &gpp, u.u_cred);

		/*
		 * Take appropriate action if the lookup fails.
		 * Negate the error if the file doesn't exist and
		 * the operation is create.
		 */

		if (u.u_error) {
			if ((flag == CREATE) && (cp[i] == '\0') &&
			(u.u_error == ENOENT) && !access(gp, GWRITE)) {
				u.u_error = 0;
				afs_lock(gp);
				ndp->ni_pdir = gp;
				ndp->ni_cp = cp;
			}
			else {
				afs_rele(gp);
			}
			return(NULL);
		}

		/*
		 * The lookup has succeeded.  If we've hit a mount point
		 * traverse it if it's type AFS, else bounce back to
		 * caller.
		 *
		 * XXX Should check that unmount is not in progress on fs.
		 */

		if (gpp->g_flag & GMOUNT) {
			mpp = gpp->g_mpp;
			if (mpp->m_fstype != GT_AFS) {
				afs_rele(gp);
				afs_rele(gpp);
				gp = mpp->m_rootgp;
				gfs_lock(gp);
				gp->g_count++;
				ndp->ni_cp = ncp;
				ndp->ni_pdir = gp;
				gp->g_flag |= GINCOMPLETE;
				return(gp);
			}
			else {
				afs_rele(gp);
				gp = gpp;
				gpp = mpp->m_rootgp;
				gpp->g_count++;
			}
		}

		/*
		 * Check for symbolic links
		 */
		afs_dp("afs_namei: check for slink mode 0x%x GFLNK 0x%x flag 0x%x\n",gpp->g_mode&GFMT, GFLNK, ndp->ni_nameiop&FOLLOW);

		if ((gpp->g_mode & GFMT) == GFLNK && ((ndp->ni_nameiop & FOLLOW) || *ncp == '/')) {
			u_int pathlen = strlen(ncp) + 1;
			/* g_size is used quite often often by the GFS layer implying the correct gnode size.
			 * Since we don't want to feel the AFS related code with g_size's we refer to the
			 * equivalent m.Length and hope for the best...
			 */
			gpp->g_size = ((struct vcache *)gpp)->m.Length;
			if (gpp->g_size + pathlen >= MAXPATHLEN - 1) {
				afs_dp("afs_namei: slink name too long\n");
				u.u_error = ENAMETOOLONG;
				afs_rele(gp);
				afs_rele(gpp);
				return (NULL);
			}

			if (++ndp->ni_slcnt > MAXSYMLINKS) {
				afs_dp("afs_namei: too many slinks\n");
				u.u_error = ELOOP;
				afs_rele(gp);
				afs_rele(gpp);
				return (NULL);
			}

			aiov->iov_base = ndp->ni_dirp;
			aiov->iov_len = auio->uio_resid = gpp->g_size;
			auio->uio_iov = aiov;
			auio->uio_iovcnt = 1;
			auio->uio_segflg = UIO_SYSSPACE;
			auio->uio_offset = 0;

			ovbcopy(ncp, ndp->ni_dirp + gpp->g_size, pathlen);

			u.u_error = afs_readlink((struct vnode *)gpp,
				auio, u.u_cred);
			if (u.u_error) {
			    afs_dp("afs_namei: afs_readlink returned error\n");
			    afs_rele(gp);
			    afs_rele(gpp);
			    return (NULL);
			}

			cp = ndp->ni_dirp;
			afs_rele(gpp);
afs_dp("cp=%s\n", cp);
			if (*cp == '/') {
			    afs_dp("afs_namei: slink name starts with slash\n");
			    afs_rele(gp);
			    while (*cp == '/')
				cp++;
			    if ((gp = u.u_rdir) == NULL)
				gp = rootdir;
			    afs_lock(gp);
			    gp->g_count++;
			    ndp->ni_cp = cp;
			    ndp->ni_pdir = gp;
			    gp->g_flag |= GINCOMPLETE;
			    return(gp);
			}
			else {
			    afs_dp("afs_namei: slink name does not start with slash\n");
				continue;
			}
		}

		/*
		 * The lookup has succeeded and we haven't hit a mount
		 * point.  Handle the case where this is the last component
		 * of the pathname.
		 */

		while (*ncp == '/')
		    ncp++;
		if (*ncp == '\0') {
		    /*
		     ** Not applicable to AFS! **
		     if (ndp->ni_nameiop & NOCACHE)
			 dnlc_purge_vp(gpp);	    */
		    if ((flag == DELETE) && access(gp, GWRITE))  {
			afs_rele(gp);
			afs_rele(gpp);
				return(NULL);
			}
			ndp->ni_cp = cp;
			if (lockparent) {
				if (gp != gpp)
					afs_lock(gp);
				ndp->ni_pdir = gp;
			}
			else {
				afs_rele(gp);
			}
			/*
			 * Copy in the last component name in
			 * ni_dent for accounting.
			 */
			bcopy(ndp->ni_cp, ndp->ni_dent.d_name, name_len);
			ndp->ni_dent.d_namlen = name_len;

			afs_lock(gpp);
			gpp->g_flag &= ~GINCOMPLETE;
			return(gpp);
		}

		/*
		 * The lookup has succeeded, but this is not the last
		 * component of the pathname.
		 */

		afs_rele(gp);
		gp = gpp;
		cp = ncp;
	}

	/*
	 * "Temporary" hack for handling null pathnames, which denote the
	 * starting directory by convention.  If the path is null and the
	 * operation is a create or delete we return EISDIR for hysterical
	 * reasons.  Note: we can also get here if the pathname resolves
	 * to an afs mount point.
	 */
	if (flag != LOOKUP) {
		if (gp == gp->g_mp->m_rootgp) {
			if (flag == CREATE)
			        if ((gp->g_mode & GFMT) == GFDIR)
				        u.u_error = EEXIST;
			        else {
				  	afs_lock(gp);
					gp->g_flag &= ~GINCOMPLETE;
					return(gp);
				}
			else
				u.u_error = EBUSY;
		} else
			u.u_error = EISDIR;
		afs_rele(gp);
		return(NULL);
	}

	afs_lock(gp);
	gp->g_flag &= ~GINCOMPLETE;
	return(gp);
}



printgnode(gp)
struct gnode *gp;
{
    afs_dp("g_count=%d, g_flag=%X, g_dev=%x, gr_dev=%x, g_blocks=%D, g_shlocks=%d, g_exlocks=%d\n", gp->g_count, gp->g_flag, gp->g_dev, gp->g_rdev, gp->g_blocks, gp->g_shlockc, gp->g_exlockc);
    afs_dp("g_number=%x, g_id=%X, g_mode=%x, g_nlink=%x, g_uid=%x, g_gid=%x\n", gp->g_number, gp->g_id, gp->g_mode, gp->g_nlink, gp->g_uid, gp->g_gid);
    if (gp->g_mp) afs_dp("g_mp=%x ", gp->g_mp);
    if (gp->g_ops) afs_dp("g_ops=%x",gp->g_ops);
    afs_dp("\n");
}

#endif	vax
