/*
 * dnode1.h - NEXTSTEP AFS support
 */


/*
 * Copyright 1996 Purdue Research Foundation, West Lafayette, Indiana
 * 47907.  All rights reserved.
 *
 * Written by Victor A. Abell
 *
 * This software is not subject to any license of the American Telephone
 * and Telegraph Company or the Regents of the University of California.
 *
 * Permission is granted to anyone to use this software for any purpose on
 * any computer system, and to alter it and redistribute it freely, subject
 * to the following restrictions:
 *
 * 1. Neither the authors nor Purdue University are responsible for any
 *    consequences of the use of this software.
 *
 * 2. The origin of this software must not be misrepresented, either by
 *    explicit claim or by omission.  Credit to the authors and Purdue
 *    University must appear in documentation and sources.
 *
 * 3. Altered versions must be plainly marked as such, and must not be
 *    misrepresented as being the original software.
 *
 * 4. This notice may not be removed or altered.
 */


#ifndef lint
static char copyright[] =
"@(#) Copyright 1996 Purdue Research Foundation.\nAll rights reserved.\n";
static char *rcsid = "$Id: dnode1.c,v 1.2 96/03/08 14:04:25 abe Exp $";
#endif


#if	defined(HAS_AFS)
#include "lsof.h"

#include <rpc/xdr.h>
#define __XDR_INCLUDE__

#include <afs/stds.h>
#include <afs/param.h>
#include <afs/afsint.h>
#include <afs/vldbint.h>


/*
 * This is an emulation of the afs_rwlock_t definition that appears in
 * the AFS sources in afs/lock.h.
 */

struct afs_lock {

# if    HAS_AFS<304
    unsigned char d1[4];
# else  /* HAS_AFS>=304 */
    unsigned char d1[6];
# endif /* HAS_AFS<304 */

};
typedef struct afs_lock afs_lock_t;
typedef struct afs_lock afs_rwlock_t;


/*
 * This is an emulation of the afs_bozoLock_t definition that appears in
 * the AFS sources in afs/lock.h.
 */

struct afs_bozoLock {
    short d1;
    char d2[2];
    char *d3;
};
typedef struct afs_bozoLock afs_bozoLock_t;

#define	KERNEL
#include <afs/afs.h>
#undef	KERNEL


/*
 * Local function prototypes
 */

_PROTOTYPE(static struct volume *getvolume,(struct VenusFid *f, int *vols));
_PROTOTYPE(static int is_rootFid,(struct vcache *vc, int *rfid));


/*
 * alloc_vcache() - allocate space for vcache structure
 */

struct vnode *
alloc_vcache()
{
	return((struct vnode *)malloc(sizeof(struct vcache)));
}


/*
 * ckAFSsym() - check for missing X_AFS_* symbols in AFS name list file
 */

void
ckAFSsym(nl)
	struct nlist *nl;		/* copy of Nl[] when empty */
{
	char *path = AFSAPATHDEF;
	int i;

# if	defined(HASAOPT)
	if (AFSApath)
	    path = AFSApath;
# endif	/* defined(HASAOPT) */

/*
 * See if the alternate AFS name list file can be read.
 */
	if (!is_readable(path, 0)) {
	    if (!Fwarn)
		(void) fprintf(stderr,
		    "%s: WARNING: can't access AFS name list file: %s\n",
		    Pn, path);
	    return;
	}
/*
 * Read the AFS modload symbols and compare its non-zero values with
 * the non-zero values in Nl[].  Quit if there is any mis-match.
 */
	if (nlist(path, nl) < 0)
	    return;
	for (i = 0; Nl[i].n_un.n_name[0]; i++) {
	    if (!nl[i].n_value || !Nl[i].n_value)
		continue;
	    if (nl[i].n_value != Nl[i].n_value)
		return;
	}
/*
 * If any X_AFS_* symbol that doesn't have a value in Nl[] has one from
 * the AFS modload file, copy its modload value to Nl[].
 */
	if (!Nl[X_AFS_FID].n_value && nl[X_AFS_FID].n_value)
	    Nl[X_AFS_FID].n_value = nl[X_AFS_FID].n_value;
	if (!Nl[X_AFS_OPS].n_value && nl[X_AFS_OPS].n_value)
	    Nl[X_AFS_OPS].n_value = nl[X_AFS_OPS].n_value;
	if (!Nl[X_AFS_VOL].n_value && nl[X_AFS_VOL].n_value)
	    Nl[X_AFS_VOL].n_value = nl[X_AFS_VOL].n_value;
}


/*
 * getvolume() - get volume structure
 */

static struct volume *
getvolume(f, vols)
	struct VenusFid *f;		/* file ID pointer */
	int *vols;			/* afs_volumes status return */
{
	int i;
	static KA_T ka = 0;
	KA_T kh;
	static struct volume v;
	struct volume *vp;
	static int w = 0;

	if (!ka) {
	    if ((ka = (KA_T)Nl[X_AFS_VOL].n_value) == (KA_T)0) {
		if (!w && !Fwarn) {
		    (void) fprintf(stderr,
			"%s: WARNING: no kernel address for: %s\n",
			Pn, Nl[X_AFS_VOL].n_un.n_name);
		    (void) fprintf(stderr,
			"      This may hamper AFS node number reporting.\n");
		    w = 1;
		}
		*vols = 0;
		return((struct volume *)NULL);
	    }
	}
	*vols = 1;
	i = (NVOLS - 1) & f->Fid.Volume;
	kh = (KA_T)((char *)ka + (i * sizeof(struct volume *)));
	if (kread(kh, (char *)&vp, sizeof(vp)))
	    return((struct volume *)NULL);
	while (vp) {
	    if (kread((KA_T)vp, (char *)&v, sizeof(v)))
		return((struct volume *)NULL);
	    if (v.volume == f->Fid.Volume && v.cell == f->Cell)
		return(&v);
	    vp = v.next;
	}
	return((struct volume *)NULL);
}


/*
 * hasAFS() - test for AFS presence via vfs structure
 */

int
hasAFS(vp)
	struct vnode *vp;		/* vnode pointer */
{
	struct mounts *mp;
	int n;
	struct vfs v;
/*
 * If this vnode has a v_data pointer, then it probably isn't an AFS vnode;
 * return FALSE.
 *
 * If the vfs struct address of /afs is known and this vnode's v_vfsp matches
 * it, return TRUE.
 *
 * Read this vnode's vfs structure and see if it's device (fsid.val[0]) is
 * AFSDEV.  If it is, record the AFS vfs struct address and return TRUE.
 */
	if (AFSVfsp && !vp->v_data && vp->v_vfsp == AFSVfsp)
	    return(1);
	if (vp->v_data
	||  !vp->v_vfsp
	||  kread((KA_T)vp->v_vfsp, (char *)&v, sizeof(v))
	||  v.vfs_data)
	    return(0);
	if (v.vfs_fsid.val[0] == AFSDEV) {
	    AFSVfsp = vp->v_vfsp;
	    return(1);
	}
/*
 * Search the local mount table for /afs devices or a match on device number.
 * Count /afs devices and skip a device number test for them.  A match on
 * device number for non-AFS devices produces a FALSE return.
 */
	for (mp = Mtab, n = 0; mp; mp = mp->next) {
	    if (mp->dev == AFSDEV
	    &&  mp->dir && strcmp(mp->dir, "/afs") == 0
	    &&  mp->fsname && strcmp(mp->fsname, "AFS") == 0)
		n++;
	    else if (mp->dev == (dev_t)v.vfs_fsid.val[0])
		return(0);
	}
/*
 * If there is exactly one /afs device, assume its vfs struct address is
 * the one for this vnode, record it, and return TRUE.
 */
	if (n == 1) {
	    AFSVfsp = vp->v_vfsp;
	    return(1);
	}
	return(0);
}


/*
 * is_rootFid() - is the file ID the root file ID
 *
 * return: 0	= is not root file ID
 *	   1	= is root file ID
 *	   rfid = 0 if root file ID structure address not available
 *		  1 if root file ID structure address available
 */

static int
is_rootFid(vc, rfid)
	struct vcache *vc;		/* vcache structure */
	int *rfid;			/* root file ID pointer status return */
{
	int err;
	static int f = 0;		/* rootFID structure status:
					 *     -1 = unavailable
					 *	0 = not yet accessed
					 *	1 = available */
	static struct VenusFid r;
	static int w = 0;

	switch (f) {
	case -1:
	    if (vc->v.v_flag & VROOT) {
		*rfid = 1;
		return(1);
	    }
	    *rfid = 0;
	    return(0);
	case 0:
	    if (!Nl[X_AFS_FID].n_value) {
		err = 1;

rfid_unavailable:

		if (!w && !Fwarn) {
		    (void) fprintf(stderr,
			"%s: WARNING: %s: %s\n", Pn,
			err ? "no kernel address" : "can't read from kernel",
			Nl[X_AFS_VOL].n_un.n_name);
		    (void) fprintf(stderr,
			"      This may hamper AFS node number reporting.\n");
		    w = 1;
		}
		f = -1;
		if (vc->v.v_flag & VROOT) {
		    *rfid = 1;
		    return(1);
		}
		*rfid = 0;
		return(0);
	    }
	    if (kread((KA_T)Nl[X_AFS_FID].n_value, (char *)&r, sizeof(r))) {
		err = 0;
		goto rfid_unavailable;
	    }
	    f = 1;
	    /* fall through */
	case 1:
	    *rfid = 1;
	    if (vc->fid.Fid.Unique == r.Fid.Unique
	    &&  vc->fid.Fid.Vnode == r.Fid.Vnode
	    &&  vc->fid.Fid.Volume == r.Fid.Volume
	    &&  vc->fid.Cell == r.Cell)
		return(1);
	}
	*rfid = 0;
	return(0);
}


/*
 * readafsnode() - read AFS node
 */

int
readafsnode(va, v, an)
	caddr_t va;			/* kernel vnode address */
	struct vnode *v;		/* vnode buffer pointer */
	struct afsnode *an;		/* afsnode recipient */
{
	char *cp;
	KA_T ka;
	int len, rfid, vols;
	struct vcache *vc;
	struct volume *vp;

	cp = ((char *)v + sizeof(struct vnode));
	ka = (KA_T)((char *)va + sizeof(struct vnode));
	len = sizeof(struct vcache) - sizeof(struct vnode);
	if (kread(ka, cp, len)) {
	    (void) sprintf(Namech,
		"vnode at %#x: can't read vcache remainder from %#x", va, ka);
	    enter_nm(Namech);
	    return(1);
	}
	vc = (struct vcache *)v;
	an->dev = AFSDEV;
	an->size = (unsigned long)vc->m.Length;
/*
 * Manufacture the "inode" number.
 */
	if (vc->mvstat == 2) {
	    if ((vp = getvolume(&vc->fid, &vols))) {
		an->inode = (unsigned long)(vp->mtpoint.Fid.Vnode +
					    (vp->mtpoint.Fid.Volume << 16));
		if (an->inode == (unsigned long)0) {
		    if (is_rootFid(vc, &rfid))
			an->ino_st = 1;
		    else if (rfid) {
			an->inode = (unsigned long)2;
			an->ino_st = 1;
		    } else
			an->ino_st = 0;
		} else
		    an->ino_st = 1;
	    } else {
		if (vols) {
		    an->inode = (unsigned long)2;
		    an->ino_st = 1;
		} else {
		    if (v->v_flag & VROOT) {
			an->inode = (unsigned long)0;
			an->ino_st = 1;
		    } else
			an->ino_st = 0;
		}
	    }
	} else {
	    an->inode = (unsigned long)((vc->fid.Fid.Vnode
		      +			(vc->fid.Fid.Volume << 16))
		      & 0x7fffffff);
	    an->ino_st = 1;
	}
	return(0);
}
#endif	/* defined(HAS_AFS) */
