/*
 * dnode.c - AIX node reading functions for lsof
 */


/*
 * Copyright 1994 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 1994 Purdue Research Foundation.\nAll rights reserved.\n";
#endif


#include "lsof.h"


/*
 * Local definitions
 */

#if	AIXV<5000
#define	FL_NEXT	next
#else	/* AIXV>=5000 */
#define	FL_NEXT	fl_next
# if	!defined(ISVDEV)
#define ISVDEV(t) (((t)==VBLK)||((t)==VCHR)||((t)==VFIFO)||((t)==VMPC))
# endif	/* !defined(ISVDEV) */
#endif	/* AIXV<5000 */


# if	defined(HAS_NFS)
#  if	AIXV<4210
#include <nfs/rnode.h>
#  else	/* AIXV>=4210 */
#   if	AIXA<2
/*
 * Private rnode struct definitions for AIX 4.2.1 and above
 *
 * The rnode struct IBM ships in <nfs/rnode.h> doesn't match the one
 * the kernel uses.  The kernel's rnode struct definition comes from
 * <oncplus/nfs/rnode.h>, a header file IBM does not ship with AIX.
 *
 * The rnode64 struct is for AIX above 4.3.3 whose "width" is 64.
 * (See dnode.c for the method used to determine width.)
 */

struct rnode {
	caddr_t	r_d1[11];		/* dummies; links? */
	struct vnode r_vnode;		/* vnode for remote file */
	struct gnode r_gnode;		/* gnode for remote file */
	caddr_t r_d2[29];		/* dummies; rnode elements? */
	off_t r_size;			/* client's view of file size (long)*/
	struct vattr r_attr;		/* cached vnode attributes */
};

#    if	AIXV>4330
struct rnode64 {

#     if AIXV<5200
	caddr_t	r_d1[11];		/* dummies; links? */
#     else /* AIXV>=5200 */
#      if	AIXV<5300
	caddr_t	r_d1[12];		/* dummies; links? */
#      else	/* AIXV>=5300 */
	caddr_t r_d1[7];		/* dummies; links? */
#      endif	/* AIXV<5300 */
#     endif /* AIXV<5200 */

	struct vnode r_vnode;		/* vnode for remote file */
	struct gnode r_gnode;		/* gnode for remote file */

#     if	AIXV<5300
	caddr_t r_d2[15];		/* dummies; rnode elements? */
#     else	/* AIXV>=5300 */
	caddr_t r_d2[11];		/* dummies; rnode elements? */
#     endif	/* AIXV<5300 */

	off_t r_size;			/* client's view of file size (long)*/
	struct vattr r_attr;		/* cached vnode attributes */
};
#    endif	/* AIXV>4330 */
#   else	/* AIXA>=2 */
struct rnode {
	KA_T d1[7];			/* dummies */
	struct vnode r_vnode;		/* vnode for remote file */
	struct gnode r_gnode;		/* gnode for remote file */
	KA_T d2[19];			/* dummies */
	off_t r_size;			/* client's view of file size (long)*/
	struct vattr r_attr;		/* cached vnode attributes */
};
#   endif	/* AIXA<2 */
#  endif	/* AIXV<4210 */
# endif	/* defined(HAS_NFS) */


/*
 * isglocked() - is a gnode locked
 */

char
isglocked(ga)
	struct gnode *ga;		/* local gnode address */
{

	struct filock *cfp, f, *ffp;
	int l;

	if (!(ffp = ga->gn_filocks))
	    return(' ');
	cfp = ffp;

#if	AIXV>=4140
	do {
#endif	/* AIXV>=4140 */

	    if (kread((KA_T)cfp, (char *)&f, sizeof(f)))
		return(' ');

#if	AIXV>=4140
	    if (f.set.l_sysid || f.set.l_pid != (pid_t)Lp->pid)
		continue;
#endif	/* AIXV>=4140 */

	    if (f.set.l_whence == 0 && f.set.l_start == 0

#if	AIXV>=4200
	    &&  f.set.l_end == 0x7fffffffffffffffLL
#else	/* AIXV<4200 */
	    &&  f.set.l_end == 0x7fffffff
#endif	/* AIXV>=4200 */

	    )
		l = 1;
	    else
		l = 0;
	    switch (f.set.l_type & (F_RDLCK | F_WRLCK)) {

	    case F_RDLCK:
		return((l) ? 'R' : 'r');
	    case F_WRLCK:
		return((l) ? 'W' : 'w');
	    case (F_RDLCK + F_WRLCK):
		return('u');
	    }
	    return(' ');

#if	AIXV>=4140
	} while ((cfp = f.FL_NEXT) && cfp != ffp);
	return(' ');
#endif	/* AIXV>=4140 */

}


/*
 * process_node() - process vnode
 */

void
process_node(va)
	KA_T va;			/* vnode kernel space address */
{
	struct cdrnode c;
	dev_t dev, rdev;
	int devs = 0;
	struct gnode g;
	struct l_ino i;
	int ic = 0;
	int ins = 0;
	struct vfs *la = NULL;
	int rdevs = 0;
	size_t sz;
	char tbuf[32], *ty;
	enum vtype type;
	struct l_vfs *vfs;
	static struct vnode *v = (struct vnode *)NULL;

#if	AIXV>=3200
	struct devnode dn;
	struct gnode pg;
	struct specnode sn;
	struct fifonode f;
#endif	/* AIXV>=3200 */

#if	defined(HAS_AFS)
	static int afs = 0;		/* AFS test status: -1 = no AFS
					 *		     0 = not tested
					 *		     1 = AFS present */
	struct afsnode an;
#endif	/* defined(HAS_AFS) */

#if	defined(HAS_NFS)
	struct vattr nfs_attr;
	int nfss = 0;
	static struct rnode r;
	static char *rp = (char *)&r;
	static int rsz = sizeof(r);

# if	AIXV>4330 && AIXA<2
	static struct rnode64 r64;
# endif	/* AIXV>4330 && AIXA<2 */

# if	AIXA<2
	static int width = -1;
# else	/* AIXA>=2 */
	static width = 64;
# endif	/* AIXA<2 */
#endif	/* defined(HAS_NFS) */

#if	defined(HAS_SANFS)
	struct sanfs_node {	/* DEBUG */

	/*
	 * This is a DEBUG version of the SANFS node structure.  When IBM makes
	 * the SANFS header files available in /usr/include, this definition
	 * will be removed.
	 */
	    u_long san_d1[20];	/* DEBUG */
	    struct gnode san_gnode;	/* DEBUG */
	    u_long san_d2[128];	/* DEBUG */
	} san;
	int sans = 0;
#endif	/* defined(HAS_SANFS) */

#if	AIXV>=4140
	struct clone *cl;
	KA_T ka;
	struct module_info mi;
	int ml, nx;
	char mn[32];
	struct queue q;
	struct qinit qi;
	KA_T qp, xp;
	int ql;
	struct sth_s {			/* stream head */
	    KA_T *dummy;		/* dummy */
	    KA_T *sth_wq;		/* write queue */
	} sh;
	struct xticb {			/* XTI control block */
	    int d1;
	    long d2;
	    int d3;
	    struct socket *xti_so;	/* socket pointer */
	} xt;
#endif	/* AIXV>=4140 */


/*
 * Read the vnode.
 */
	if (!va) {
	    enter_nm("no vnode address");
	    return;
	}
	if (!v) {

	/*
	 * Allocate space for the vnode or AFS vcache structure.
	 */

#if	defined(HAS_AFS)
	    v = alloc_vcache();
#else	/* !defined(HAS_AFS) */
	    v = (struct vnode *)malloc((MALLOC_S)sizeof(struct vnode));
#endif	/* defined(HAS_AFS) */

	    if (!v) {
		(void) fprintf(stderr, "%s: can't allocate %s space\n", Pn,

#if	defined(HAS_AFS)
			       "vcache"
#else	/* !defined(HAS_AFS) */
			       "vnode"
#endif	/* defined(HAS_AFS) */

			      );
		Error();
	    }
	}
/*
 * Read the vnode.
 */
	if (readvnode(va, v)) {
	    enter_nm(Namech);
	    return;
	}

#if	defined(HASFSTRUCT)
	Lf->fsv |= FSV_NI;
	Lf->fna = va;
#endif	/* defined(HASFSTRUCT) */

/*
 * Read the gnode.
 */
	if (!v->v_gnode || readgnode((KA_T)v->v_gnode, &g)) {
	    if (Selinet) {
		Lf->sf = SELEXCLF;
		return;
	    }
	    (void) snpf(Namech, Namechl, "vnode at %s has no gnode\n",
		print_kptr(va, (char *)NULL, 0));
	    enter_nm(Namech);
	    return;
	}

#if	AIXV>=3200

/*
 * Under AIX 3.2 and above, if the vnode type is ISVDEV, then there is a
 * special node and a fifonode or devnode.  Behind them are the "real"
 * gnode, inode and vnode.
 */
	if (ISVDEV(g.gn_type)) {
	    switch (g.gn_type) {
	    case VBLK:
		Ntype = N_BLK;
		break;
	    case VCHR:
		Ntype = N_CHR;
		break;
	    case VFIFO:
		Ntype = N_FIFO;
		break;
	    case VMPC:
		Ntype = N_MPC;
		break;
	    default:
		(void) snpf(Namech, Namechl, "vnode at %s: unknown ISVDEV(%#x)",
		    print_kptr(va, (char *)NULL, 0), g.gn_type);
		enter_nm(Namech);
		return;
	    }
	/*
	 * Read the special node.
	 */
	    if (!g.gn_data || kread((KA_T)g.gn_data, (char *)&sn, sizeof(sn))) {
		if (Selinet) {
		    Lf->sf = SELEXCLF;
		    return;
		}
		(void) snpf(Namech, Namechl,
		    "vnode at %s: can't read specnode (%s)",
		    print_kptr(va, tbuf, sizeof(tbuf)),
		    print_kptr((KA_T)g.gn_data, (char *)NULL, 0));
		enter_nm(Namech);
		return;
	   }
	/*
	 * Read the PFS gnode and its inode and vnode.
	 */
	    if (sn.sn_pfsgnode) {
		if (Selinet) {
		    Lf->sf = SELEXCLF;
		    return;
		}
		if (readgnode((KA_T)sn.sn_pfsgnode, &g)) {
		    (void) snpf(Namech, Namechl,
			"vnode at %s: can't read pfsgnode (%s)",
			print_kptr(va, tbuf, sizeof(tbuf)),
			print_kptr((KA_T)sn.sn_pfsgnode, (char *)NULL, 0));
		    enter_nm(Namech);
		    return;
		}
		if (!g.gn_data || readlino(&g, &i)) {
		    (void) snpf(Namech, Namechl,
			"pfsgnode at %s: can't read inode (%s)",
			print_kptr((KA_T)sn.sn_pfsgnode, tbuf, sizeof(tbuf)),
			print_kptr((KA_T)g.gn_data, (char *)NULL, 0));
		    enter_nm(Namech);
		    return;
		}
		ins = 1;
		if (!g.gn_vnode || readvnode((KA_T)g.gn_vnode, v)) {
		    (void) snpf(Namech, Namechl,
			"pfsgnode at %s: can't read vnode (%s)",
			print_kptr((KA_T)sn.sn_pfsgnode, tbuf, sizeof(tbuf)),
			print_kptr((KA_T)g.gn_vnode, (char *)NULL, 0));
		    enter_nm(Namech);
		    return;
		}
	    } else {
		(void) zeromem((char *)&i, sizeof(i));

#if	AIXV>=4140
	    /*
	     * See if this is a clone device, connected to a stream.
	     *
	     *     the clone major device number must be known;
	     *     the specnode must have a devnode pointer;
	     *     and the devnode must have a stream head pointer.
	     */
		if (CloneMaj >= 0
		&&  sn.sn_devnode
		&&  kread((KA_T)sn.sn_devnode, (char *)&dn, sizeof(dn)) == 0
		&&  (ka = (KA_T)dn.dv_pdata))
		{

# if	defined(HASDCACHE)

process_clone_again:

# endif	/* defined(HASDCACHE) */

		    for (cl = Clone; cl; cl = cl->next) {
			if (GET_MAJ_DEV(g.gn_rdev) == GET_MIN_DEV(cl->cd.rdev))
			{

# if	defined(HASDCACHE)
			    if (DCunsafe && !cl->cd.v && !vfy_dev(&cl->cd))
				goto process_clone_again;
# endif	/* defined(HASDCACHE) */

			/*
			 * Identify this file as a clone.  Save the clone
			 * device inode number as the file's inode number.
			 */
			    ic = 1;
			    Lf->inode = cl->cd.inode;
			    Lf->inp_ty = 1;
			    if (ClonePtc >= 0
			    &&  GET_MAJ_DEV(g.gn_rdev) == ClonePtc) {
				if (Selinet) {
				    Lf->sf = SELEXCLF;
				    return;
				}
			    /*
			     * If this is a /dev/ptc stream, enter the device
			     * name and the channel.
			     */
			        (void) snpf(Namech, Namechl, "%s/%d",
				    cl->cd.name, (int)GET_MIN_DEV(g.gn_rdev));
				break;
			    }
			/*
			 * If this isn't a /dev/ptc stream, collect the names
			 * of the modules on the stream.  Ignore the stream
			 * head and look for an "xtiso" module.  Limit the
			 * module depth to 25.
			 */
			    (void) snpf(Namech, Namechl, "STR:%s", cl->cd.name);
			    nx = (int) strlen(Namech);
			    if (!kread(ka, (char *)&sh, sizeof(sh)))
				qp = (KA_T)sh.sth_wq;
			    else
				qp = (KA_T)NULL;
			    for (mn[sizeof(mn) - 1] = '\0', ql = 0;
				 qp && (ql < 25);
				 ql++, qp = (KA_T)q.q_next)
			    {

			    /*
			     * Read the queue structure.  If it can't be read,
			     * end module name collection.
			     *
			     * The queue structure should lead to a qinfo
			     * structure, and the qinfo structure should lead
			     * to a module_info structure, where the module
			     * name should be found.  If there's no queue
			     * structure.
			     *
			     * If the qinfo or module_info structures can't be
			     * read, skip to the next queue structure.
			     */
				if (kread(qp, (char *)&q, sizeof(q)))
				    break;
				if (!(ka = (KA_T)q.q_qinfo)
				||  kread(ka, (char *)&qi, sizeof(qi)))
				    continue;
				if (!(ka = (KA_T)qi.qi_minfo)
				||  kread(ka, (char *)&mi, sizeof(mi)))
				    continue;
				if (!(ka = (KA_T)mi.mi_idname)
				||  kread(ka, mn, sizeof(mn) - 1)
				||  !(ml = (int) strlen(mn))
				||  !strcmp(mn, "sth"))
				    continue;
				if (!strcmp(mn, "xtiso")
				&&  (xp = (KA_T)q.q_ptr)
				&&  !kread(xp, (char *)&xt, sizeof(xt))
				&&  (ka = (KA_T)xt.xti_so)) {

				/*
				 * The xtiso module's private queue pointer
				 * leads to an xticb with a non-NULL socket
				 * pointer.  Process the stream as a socket.
				 */
				    Namech[0] = '\0';
				    Lf->inp_ty = 0;
				    (void) process_socket(ka);
				    return;
				}
			    /*
			     * Save the module name in Mamech[] as a "->"
			     * prefixed chain, beginning with "STR:<device>".
			     */
				if ((nx + ml + 2) > (Namechl - 1))
				    continue;
				(void) snpf(&Namech[nx], Namechl, "->%s", mn);
				nx += (ml + 2);
			    }
			    break;
			}
		    }
		}
#endif	/* AIXV>=4140 */

		if (Selinet) {
		    Lf->sf = SELEXCLF;
		    return;
		}
	    }
	/*
	 * If it's a FIFO, read its fifonode.
	 */
	    if (Ntype == N_FIFO) {
		if (!sn.sn_fifonode ||readfifonode((KA_T)sn.sn_fifonode, &f)) {
		    (void) snpf(Namech, Namechl,
			"vnode at %s: can't read fifonode (%s)",
			print_kptr(va, tbuf, sizeof(tbuf)),
			print_kptr((KA_T)sn.sn_fifonode, (char *)NULL, 0));
		    enter_nm(Namech);
		    return;
		}
	/*
	 * Otherwise, read the devnode and its gnode.
	 */
	    } else {
		if (!sn.sn_devnode
		|| kread((KA_T)sn.sn_devnode,(char *)&dn,sizeof(dn))) {
		    (void) snpf(Namech, Namechl,
			"vnode at %s: can't read devnode (%s)",
			print_kptr(va, tbuf, sizeof(tbuf)),
			print_kptr((KA_T)sn.sn_devnode, (char *)NULL, 0));
		    enter_nm(Namech);
		    return;
		}
		g = dn.dv_gnode;
	    }
	}
#endif	/* AIXV>=3200 */

/*
 * Read the AIX virtual file system structure.
 */
	if (Ntype != N_AFS && g.gn_rdev == NODEVICE) {
	    vfs = (struct l_vfs *)NULL;
	    enter_dev_ch(print_kptr(va, (char *)NULL, 0));
	} else {
	    if (!(vfs = readvfs(v))) {
		(void) snpf(Namech, Namechl, "can't read vfs for %s at %s",
		    print_kptr(va, tbuf, sizeof(tbuf)),
		    print_kptr((KA_T)v->v_vfsp, (char *)NULL, 0));
		enter_nm(Namech);
		return;
	    }
	}
/*
 * Set special node types: NFS, PROC or SANFS.
 */

#if	defined(MNT_REMOTE)
	if (vfs && vfs->vmt_flags & MNT_REMOTE) {
	    switch(vfs->vmt_gfstype) {

# if	defined(HAS_NFS)
	    case MNT_NFS:

#  if	defined(MNT_NFS3)
	    case MNT_NFS3:
#  endif	/* defined(MNT_NFS3) */

#  if	defined(MNT_NFS4)
	    case MNT_NFS4:
#  endif	/* defined(MNT_NFS4) */

#  if	defined(HAS_AFS)
		if (!AFSVfsp || (KA_T)v->v_vfsp != AFSVfsp)
#  endif	/* defined(HAS_AFS) && defined(HAS_NFS) */

		    Ntype = N_NFS;
# endif	/* defined(HAS_NFS) */
		break;

# if	defined(HAS_SANFS) && defined(MNT_SANFS)
	    case MNT_SANFS:
		Ntype = N_SANFS;
		break;
# endif	/* defined(HAS_SANFS) && defined(MNT_SANFS) */

	    }
	}
#endif	/* defined(MNT_REMOTE) */

#if	defined(HASPROCFS)
	if (vfs && (vfs->vmt_gfstype == MNT_PROCFS))
	    Ntype = N_PROC;
#endif	/* defined(HASPROCFS) */

/*
 * Get the lock status.
 */
	Lf->lock = isglocked(&g);
	switch (Ntype) {

#if	defined(HAS_NFS)
/*
 * Read an NFS rnode.
 */
	case N_NFS:

# if	AIXA<2
	    if (width == -1) {

	    /*
	     * Establish the architecture's bit width and set NFS rnode
	     * access parameters accordingly.
	     */

#  if	AIXV<=4330
		width = 32;
#  else	/* AIXV>4330 */
		if (__KERNEL_64()) {
		    width = 64;
		    rp = (char *)&r64;
		    rsz = sizeof(r64);
		} else if (__KERNEL_32()) {
		    width = 32;
		} else {
		    if (!Fwarn)
			(void) fprintf(stderr,
			    "%s: WARNING: unknown kernel bit size\n", Pn);
		    width = -2;
		}
#  endif	/* AIXV<-4330 */

	    }
# endif	/* AIXA<2 */

	    if (width > 0) {
		if (!g.gn_data || kread((KA_T)g.gn_data, rp, rsz)) {
		    (void) snpf(Namech, Namechl,
			"remote gnode at %s has no rnode",
			print_kptr((KA_T)v->v_gnode, (char *)NULL, 0));
		    enter_nm(Namech);
		    return;
		}

# if	AIXV<=4330 || AIXA>=2
		nfs_attr = r.r_attr;
		nfss = 1;
# else	/* AIXV>4330 && AIXA<2 */
		switch (width) {
		case 32:
		    nfs_attr = r.r_attr;
		    nfss = 1;
		    break;
		case 64:
		    nfs_attr = r64.r_attr;
		    nfss = 1;
		    break;
		}
# endif	/* AIXV<=4330 || AIXA>=2 */

	    }
	    break;
#endif	/* defined(HAS_NFS) */

#if	defined(HAS_SANFS)
/*
 * Read SANFS node and associated structures.
 */
	case N_SANFS:
	    if (!g.gn_data
	    ||  kread((KA_T)g.gn_data, &san, sizeof(san))
	    ) {
		(void) snpf(Namech, Namechl, "gnode at %s has no SANFS node",
		    print_kptr((KA_T)v->v_gnode, (char *)NULL, 0));
		enter_nm(Namech);
		return;
	    }
	/*
	 * DEBUG: this code is insufficient.  It can't be completed until IBM
	 * makes the SANFS header files available in /usr/include.  There are
	 * apparently two node structures following the SANFS node and file
	 * attributes (size, etc.) are in the second structure.
	 */
	    sans = 1;
	    break;
#endif	/* defined(HAS_SANFS) */

/*
 * Read N_REGLR nodes.
 */
	case N_REGLR:
	    if (vfs && vfs->vmt_gfstype == MNT_CDROM) {

	    /*
	     * Read a CD-ROM cdrnode.
	     */
		if (!g.gn_data || readcdrnode((KA_T)g.gn_data, &c)) {
		    (void) snpf(Namech, Namechl, "gnode at %s has no cdrnode",
			print_kptr((KA_T)v->v_gnode, (char *)NULL, 0));
		    enter_nm(Namech);
		    return;
		}
		(void) zeromem((char *)&i, sizeof(i));
		i.number = (INODETYPE)c.cn_inumber;
		i.size = (off_t)c.cn_size;
		i.number_def = i.size_def = 1;
	    /*
	     * Otherwise, read the inode.
	     */

	    } else if (g.gn_data) {
		if (readlino(&g, &i)) {
		    (void) snpf(Namech, Namechl,
			"gnode at %s can't read inode: %s",
			print_kptr((KA_T)v->v_gnode, tbuf, sizeof(tbuf)),
			print_kptr((KA_T)g.gn_data, (char *)NULL, 0));
		    enter_nm(Namech);
		    return;
		}
		ins = 1;
	    }

#if	defined(HAS_AFS)
	    else {

	    /*
	     * See if this is an AFS node.
	     */
		if (AFSVfsp && (KA_T)v->v_vfsp == AFSVfsp)
		    Ntype = N_AFS;
		else if (v->v_vfsp) {
		    switch (afs) {
		    case -1:
			break;
		    case 0:
			if (!hasAFS(v)) {
			    afs = 1;
			    break;
			}
			afs = 1;
			Ntype = N_AFS;
			break;
		    case 1:
			if ((KA_T)v->v_vfsp == AFSVfsp)
			    Ntype = N_AFS;
		     }
		}
	    /*
	     * If this is an AFS node, read the afsnode.
	     */
		if (Ntype == N_AFS) {
		    if (readafsnode(va, v, &an))
			return;
		} else {
		    (void) snpf(Namech, Namechl, "gnode at %s has no inode",
			print_kptr((KA_T)v->v_gnode, (char *)NULL, 0));
		    enter_nm(Namech);
		    return;
		}
	    }
#else	/* !defined(HAS_AFS) */

	    else {
		(void) snpf(Namech, Namechl, "gnode at %s has no inode",
		    print_kptr((KA_T)v->v_gnode, (char *)NULL, 0));
		enter_nm(Namech);
		return;
	    }
#endif	/* defined(HAS_AFS) */

	}
/*
 * Get device and type for printing.
 */

#if	defined(HAS_NFS)
	if (Ntype == N_NFS) {
	    if (vfs) {
		dev = vfs->dev;
		devs = 1;
	    }
	} else
#endif	/* defined(HAS_NFS) */

#if	defined(HAS_AFS)
	if (Ntype == N_AFS) {
	    dev = an.dev;
	    devs = 1;
	} else
#endif	/* defined(HAS_AFS) */

#if	defined(HASPROCFS)
	if (Ntype == N_PROC) {

/* WARNING!!!   WARNING!!!   The following hack should be removed ASAP!!! */
	    dev = vfs ? (vfs->dev & 0x7fffffffffffffff) : 0;
/* WARNING!!!   WARNING!!!   The above hack should be removed ASAP!!! */

	    devs = 1;
	}
	else
#endif	/* defined(HASPROCFS) */

#if	defined(HAS_SANFS)
	if ((Ntype == N_SANFS) && vfs) {
	    dev = vfs->dev;
	    devs = 1;
	}
	else
#endif	/* defined(HAS_SANFS) */

	{
	    if (vfs) {
		dev = vfs->dev;
		devs = 1;
	    }
	    rdev = g.gn_rdev;
	    rdevs = 1;
	}

#if	AIXV>=3200
	if (Ntype == N_MPC)
	    type = VMPC;
	else
#endif	/* AIXV>=3200 */

	    type = g.gn_type;
/*
 * Obtain the inode number.
 */
	switch (Ntype) {

#if	defined(HAS_AFS)
	case N_AFS:
	    if (an.ino_st) {
		Lf->inode = (INODETYPE)an.inode;
		Lf->inp_ty = 1;
	    }
	    break;
#endif	/* defined(HAS_AFS) */

#if	defined(HAS_NFS)
	case N_NFS:
	    if (nfss) {
		Lf->inode = (INODETYPE)nfs_attr.va_serialno;
		Lf->inp_ty = 1;
	    }
	    break;
#endif	/* defined(HAS_NFS) */

#if	defined(HAS_SANFS)
	case N_SANFS:
	    if (sans) {

	    /*
	     * DEBUG: this code is insufficient.  It can't be completed until
	     * IBM makes the SANFS header files available in /usr/include.
	     */
		/* Lf->inode = ???	DEBUG */
		Lf->inp_ty = 1;
	    }
	    break;
#endif	/* defined(HAS_SANFS) */

# if	AIXV>=3200
	case N_BLK:
	case N_CHR:
	case N_FIFO:
	case N_MPC:
# endif	/* AIXV>=3200 */

	case N_REGLR:
	    if (ins) {
		Lf->inode = (INODETYPE)i.number;
		Lf->inp_ty = i.number_def;
	    }
	}
/*
 * Obtain the file size.
 */
	if (Foffset)
	    Lf->off_def = 1;
	else {
	    switch (Ntype) {

#if	defined(HAS_AFS)
	    case N_AFS:
		Lf->sz = (SZOFFTYPE)an.size;
		Lf->sz_def = 1;
		break;
#endif	/* defined(HAS_AFS) */

#if	AIXV>=3200
	    case N_FIFO:
		Lf->sz = (SZOFFTYPE)f.ff_size;
		Lf->sz_def = 1;
		break;
#endif	/* AIXV>=3200 */

#if	defined(HAS_NFS)
	    case N_NFS:
		if (nfss) {
		    Lf->sz = (SZOFFTYPE)nfs_attr.va_size;
		    Lf->sz_def = 1;
		}
		break;
#endif	/* defined(HAS_NFS) */

#if	defined(HAS_SANFS)
	    case N_SANFS:
		if (sans) {

		/*
	 	 * DEBUG: this code is insufficient.  It can't be completed
		 * until IBM makes the SANFS header files available in
		 * /usr/include.
		 */
		    /* Lf->sz = (SZOFFTYPE)???	DEBUG */
		    Lf->sz_def = 1;
		}
		break;
#endif	/* defined(HAS_SANFS) */

#if	 AIXV>=3200
	    case N_BLK:
		if (!Fsize)
		    Lf->off_def = 1;
		break;
	    case N_CHR:
	    case N_MPC:
		if (!Fsize)
		    Lf->off_def = 1;
		break;
#endif	/* AIXV>=3200 */

	    case N_REGLR:
		if (type == VREG || type == VDIR) {
		    if (ins) {
			Lf->sz = (SZOFFTYPE)i.size;
			Lf->sz_def = i.size_def;
		    }
		} else if (((type == VBLK) || (type == VCHR) || (type == VMPC))
		       &&  !Fsize)
		    Lf->off_def = 1;
		break;
	    }
	}
/*
 * Record link count.
 */
	if (Fnlink) {
	    switch(Ntype) {

#if	defined(HAS_AFS)
	    case N_AFS:
		Lf->nlink = an.nlink;
		Lf->nlink_def = an.nlink_st;
		break;
#endif	/* defined(HAS_AFS) */

#if	defined(HAS_NFS)
	    case N_NFS:
		if (nfss) {
		    Lf->nlink = (long)nfs_attr.va_nlink;
		    Lf->nlink_def = 1;
		}
		break;
#endif	/* defined(HAS_NFS) */

#if	defined(HAS_SANFS)
	    case N_SANFS:
		if (sans) {

		/*
	 	 * DEBUG: this code is insufficient.  It can't be completed
		 * until IBM makes the SANFS header files available in
		 * /usr/include.
		 */
		    /* Lf->nlink = (long)???	DEBUG */
		    Lf->nlink_def = 1;
		}
		break;
#endif	/* defined(HAS_SANFS) */

#if	AIXV>=3200
	    case N_BLK:
	    case N_CHR:
	    case N_FIFO:
	    case N_MPC:
#endif	/* AIXV>=3200 */

	    case N_REGLR:
		if (ins) {
		    Lf->nlink = (long)i.nlink;
		    Lf->nlink_def = i.nlink_def;
		}
		break;
	    }
	    if (Nlink && Lf->nlink_def && (Lf->nlink < Nlink))
		Lf->sf |= SELNLINK;
	}

#if	defined(HAS_NFS)
/*
 * Record an NFS file selection.
 */
	if (Ntype == N_NFS && Fnfs)
	    Lf->sf |= SELNFS;
#endif	/* defined(HAS_NFS) */

/*
 * Save the file system names.
 */
	if (vfs) {
	    Lf->fsdir = vfs->dir;
	    Lf->fsdev = vfs->fsname;
	}
/*
 * Save the device numbers and their states.
 *
 * Format the vnode type.
 */
	switch (type) {

	case VNON:
	    ty ="VNON";
	    Lf->dev = dev;
	    Lf->dev_def = devs;
	    Lf->rdev = rdev;
	    Lf->rdev_def = rdevs;
	    break;
	case VREG:
	case VDIR:
	    ty = (type == VREG) ? "VREG" : "VDIR";
	    Lf->dev = dev;
	    Lf->dev_def = devs;
	    Lf->rdev = rdev;
	    Lf->rdev_def = rdevs;
	    break;
	case VBLK:
	    ty = "VBLK";
	    Lf->dev = dev;
	    Lf->dev_def = devs;
	    Lf->rdev = rdev;
	    Lf->rdev_def = rdevs;
	    Ntype = N_BLK;
	    break;
	case VCHR:
	    ty = "VCHR";
	    Lf->dev = dev;
	    Lf->dev_def = devs;
	    Lf->rdev = rdev;
	    Lf->rdev_def = rdevs;
	    Ntype = N_CHR;
	    break;
	case VLNK:
	    ty = "VLNK";
	    Lf->dev = dev;
	    Lf->dev_def = devs;
	    Lf->rdev = rdev;
	    Lf->rdev_def = rdevs;
	    break;

#if	defined(VSOCK)
	case VSOCK:
	    ty = "SOCK";
	    Lf->dev = dev;
	    Lf->dev_def = devs;
	    Lf->rdev = rdev;
	    Lf->rdev_def = rdevs;
	    break;
#endif

	case VBAD:
	    ty = "VBAD";
	    Lf->dev = dev;
	    Lf->dev_def = devs;
	    Lf->rdev = rdev;
	    Lf->rdev_def = rdevs;
	    break;
	case VFIFO:
	    if (!Lf->dev_ch || Lf->dev_ch[0] == '\0') {
		Lf->dev = dev;
		Lf->dev_def = devs;
		Lf->rdev = rdev;
		Lf->rdev_def = rdevs;
	    }
	    ty = "FIFO";
	    break;
	case VMPC:
	    Lf->rdev = g.gn_rdev;
	    Lf->rdev_def = 1;
	    if (vfs) {
		Lf->dev = vfs->dev;
		Lf->dev_def = 1;
	    }
	    Lf->ch = g.gn_chan;

#if	AIXV<3200
	    Lf->inp_ty = 0;
#endif	/* AIXV<3200 */

	    Ntype = N_CHR;
	    ty = "VMPC";
	    break;
	default:
	    Lf->dev = dev;
	    Lf->dev_def = devs;
	    Lf->rdev = rdev;
	    Lf->rdev_def = rdevs;
	    (void) snpf(Lf->type, sizeof(Lf->type), "%04o", (type & 0xfff));
	    ty = (char *)NULL;
	}
	if (ty)
	    (void) snpf(Lf->type, sizeof(Lf->type), "%s", ty);
	Lf->ntype = Ntype;

#if	defined(HASBLKDEV)
/*
 * If this is a VBLK file and it's missing an inode number, try to
 * supply one.
 */
	if ((Lf->inp_ty == 0) && (type == VBLK))
	    find_bl_ino();
#endif	/* defined(HASBLKDEV) */

/*
 * If this is a VCHR file and it's missing an inode number, try to
 * supply one.
 */
	if ((Lf->inp_ty == 0) && (type == VCHR))
	    find_ch_ino();
/*
 * Test for specified file.
 */
	if (Sfile && is_file_named(NULL, type, g.gn_chan, ic))
	    Lf->sf |= SELNM;
/*
 * Enter name characters.
 */
	if (Namech[0])
	    enter_nm(Namech);
}


#if	defined(HASPRIVFILETYPE)
/*
 * process_shmt() -- process shared memory transport file
 */

void
process_shmt(sa)
	KA_T sa;			/* shared memory transport node struct
					 * address ??? */
{
	struct shmtnode {		/* shared memory transport node
					 * struct ??? */

	    struct shmtnode *peer;	/* peer shmtnode struct */
	    caddr_t d1[2];		/* dummy to fill space */
	    int sz;			/* buffer size */
	    caddr_t d2[3];		/* dyummy to fill space */
	    int free;			/* free bytes in buffer */
	    caddr_t d3[17];		/* dummy to fill space */
	    pid_t pid;			/* process ID */
	} mn, pn;
/*
 * Ignore this file if only Internet files are selected.
 */
	if (Selinet) {
	    Lf->sf |= SELEXCLF;
	    return;
	}
/*
 * Set type to " SMT" and put shmtnode structure address in device column.
 */
	(void) snpf(Lf->type, sizeof(Lf->type), " SMT");
	if (!sa || kread((KA_T)sa, (char *)&mn, sizeof(mn))) {
	    (void) snpf(Namech, Namechl, "can't read shmtnode: %s",
		print_kptr(sa, (char *)NULL, 0));
	    enter_nm(Namech);
	    return;
	}
	enter_dev_ch(print_kptr(sa, (char *)NULL, 0));
/*
 * If offset display has been requested or if buffer size less free bytes is
 * negative, enable offset display.  Otherwise set the  file size as buffer
 * size less free bytes.
 */
	if (Foffset || mn.free > mn.sz)
	    Lf->off_def = 1;
	else {
	    Lf->sz = (SZOFFTYPE)(mn.sz - mn.free);
	    Lf->sz_def = 1;
	}
/*
 * If there is a peer, read its shmtnode structure.
 */
	if (!mn.peer)
	    (void) snpf(Namech, Namechl, "->(unknown)");
	else {
	    if (kread((KA_T)mn.peer, (char *)&pn, sizeof(pn)))
		(void) snpf(Namech, Namechl, "can't read peer shmtnode: %s",
		    print_kptr((KA_T)mn.peer, (char *)NULL, 0));
	    else {
		if (pn.pid)
		    (void) snpf(Namech, Namechl, "->%s (PID %d)",
			print_kptr((KA_T)mn.peer, (char *)NULL, 0), pn.pid);
		else
		    (void) snpf(Namech, Namechl, "->%s",
			print_kptr((KA_T)mn.peer, (char *)NULL, 0));
	    }
	}
	enter_nm(Namech);
}
#endif	/* AIXV>=4200 */


/*
 * readlino() -- read local inode
 */

int
readlino(ga, li)
	struct gnode *ga;			/* gnode address */
	struct l_ino *li;			/* local inode receiver */
{
	struct inode i;				/* "regular" inode */

#if	defined(HAS_JFS2)
	static struct vnodeops *j2va = (struct vnodeops *)NULL;
						/* j2_vnops address */
	static int j2vas = 0;			/* j2nl[] status */
#endif	/* defined(HAS_JFS2) */

	zeromem((char *)li, sizeof(struct l_ino));
	if (!ga || !ga->gn_data)
	    return(0);

#if	defined(HAS_JFS2)
	if (!j2vas) {

	/*
	 * Get the j2_vnops address once.
	 */
	    struct nlist j2nl[] = {
		{ "j2_vnops"	},
		{ (char *)NULL	}
	    };

	    if (nlist(N_UNIX, j2nl) == 0)
		j2va = (struct vnodeops *)j2nl[0].n_value;
	    if (!j2va && !Fwarn) {
		(void) fprintf(stderr,
		    "%s: WARNING: can't identify jfs2 files\n", Pn);
	    }
	    j2vas = 1;
	}
/*
 * If this system has jfs2, see if this gnode's operation structure pointer
 * references j2_vnops.
 */
	if (ga->gn_ops && j2va && (ga->gn_ops == j2va))
	    return(readj2lino(ga, li));
#endif	/* defined(HAS_JFS2) */

/*
 * Read a "standard" inode.
 */
	if (readinode((KA_T)ga->gn_data, &i))
	    return(1);
	li->dev = i.i_dev;
	li->nlink = i.i_nlink;
	li->number = (INODETYPE)i.i_number;
	li->size = i.i_size;
	li->dev_def = li->nlink_def = li->number_def = li->size_def = 1;
	return(0);
}
