/*
 * 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 "common.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
 */

enum lsof_lock_mode isglocked(struct lsof_context *ctx, /* context */
                              struct gnode *ga) /* local gnode address */
{

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

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

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

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

#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 ? LSOF_LOCK_READ_FULL : LSOF_LOCK_READ_PARTIAL;
        case F_WRLCK:
            return (l) ? LSOF_LOCK_WRITE_FULL : LSOF_LOCK_WRITE_PARTIAL;
        case (F_RDLCK + F_WRLCK):
            return LSOF_LOCK_READ_WRITE;
        }
        return LSOF_LOCK_NONE;

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

/*
 * process_node() - process vnode
 */

void process_node(struct lsof_context *ctx, /* context */
                  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];
    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(ctx, "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(ctx);
        }
    }
    /*
     * Read the vnode.
     */
    if (readvnode(ctx, va, v)) {
        enter_nm(ctx, Namech);
        return;
    }

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

    /*
     * Read the gnode.
     */
    if (!v->v_gnode || readgnode(ctx, (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(ctx, 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(ctx, Namech);
            return;
        }
        /*
         * Read the special node.
         */
        if (!g.gn_data ||
            kread(ctx, (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(ctx, Namech);
            return;
        }
        /*
         * Read the PFS gnode and its inode and vnode.
         */
        if (sn.sn_pfsgnode) {
            if (Selinet) {
                Lf->sf = SELEXCLF;
                return;
            }
            if (readgnode(ctx, (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(ctx, Namech);
                return;
            }
            if (!g.gn_data || readlino(ctx, &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(ctx, Namech);
                return;
            }
            ins = 1;
            if (!g.gn_vnode || readvnode(ctx, (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(ctx, 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(ctx, (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(ctx, &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(ctx, 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(ctx, qp, (char *)&q, sizeof(q)))
                                break;
                            if (!(ka = (KA_T)q.q_qinfo) ||
                                kread(ctx, ka, (char *)&qi, sizeof(qi)))
                                continue;
                            if (!(ka = (KA_T)qi.qi_minfo) ||
                                kread(ctx, ka, (char *)&mi, sizeof(mi)))
                                continue;
                            if (!(ka = (KA_T)mi.mi_idname) ||
                                kread(ctx, ka, mn, sizeof(mn) - 1) ||
                                !(ml = (int)strlen(mn)) || !strcmp(mn, "sth"))
                                continue;
                            if (!strcmp(mn, "xtiso") && (xp = (KA_T)q.q_ptr) &&
                                !kread(ctx, 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(ctx, (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(ctx, Namech);
                return;
            }
            /*
             * Otherwise, read the devnode and its gnode.
             */
        } else {
            if (!sn.sn_devnode ||
                kread(ctx, (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(ctx, 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(ctx, print_kptr(va, (char *)NULL, 0));
    } else {
        if (!(vfs = readvfs(ctx, 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(ctx, 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(ctx, &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(ctx, (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(ctx, 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(ctx, (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(ctx, 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(ctx, (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(ctx, 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(ctx, &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(ctx, 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(ctx, 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(ctx, 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(ctx, 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(ctx, 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.
     */
    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:
        break;
    case N_CHR:
    case N_MPC:
        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;
            }
        }
        break;
    }
    /*
     * Record link count.
     */
    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:
        Lf->type = LSOF_FILE_VNODE_VNON;
        Lf->dev = dev;
        Lf->dev_def = devs;
        Lf->rdev = rdev;
        Lf->rdev_def = rdevs;
        break;
    case VREG:
    case VDIR:
        Lf->type = (type == VREG) ? LSOF_FILE_VNODE_VREG : LSOF_FILE_VNODE_VDIR;
        Lf->dev = dev;
        Lf->dev_def = devs;
        Lf->rdev = rdev;
        Lf->rdev_def = rdevs;
        break;
    case VBLK:
        Lf->type = LSOF_FILE_VNODE_VBLK;
        Lf->dev = dev;
        Lf->dev_def = devs;
        Lf->rdev = rdev;
        Lf->rdev_def = rdevs;
        Ntype = N_BLK;
        break;
    case VCHR:
        Lf->type = LSOF_FILE_VNODE_VCHR;
        Lf->dev = dev;
        Lf->dev_def = devs;
        Lf->rdev = rdev;
        Lf->rdev_def = rdevs;
        Ntype = N_CHR;
        break;
    case VLNK:
        Lf->type = LSOF_FILE_VNODE_VLNK;
        Lf->dev = dev;
        Lf->dev_def = devs;
        Lf->rdev = rdev;
        Lf->rdev_def = rdevs;
        break;

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

    case VBAD:
        Lf->type = LSOF_FILE_VNODE_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;
        }
        Lf->type = LSOF_FILE_VNODE_VFIFO;
        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;
        Lf->type = LSOF_FILE_VNODE_VMPC;
        break;
    default:
        Lf->dev = dev;
        Lf->dev_def = devs;
        Lf->rdev = rdev;
        Lf->rdev_def = rdevs;
        Lf->type = LSOF_FILE_UNKNOWN_RAW;
        Lf->unknown_file_type_number = type;
    }
    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(ctx);
#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(ctx);
    /*
     * Test for specified file.
     */
    if (Sfile && is_file_named(ctx, NULL, type, g.gn_chan, ic))
        Lf->sf |= SELNM;
    /*
     * Enter name characters.
     */
    if (Namech[0])
        enter_nm(ctx, Namech);
}

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

void process_shmt(struct lsof_context *ctx, /* context */
                  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.
     */
    Lf->type = LSOF_FILE_SHARED_MEM_TRANSPORT;
    if (!sa || kread(ctx, (KA_T)sa, (char *)&mn, sizeof(mn))) {
        (void)snpf(Namech, Namechl, "can't read shmtnode: %s",
                   print_kptr(sa, (char *)NULL, 0));
        enter_nm(ctx, Namech);
        return;
    }
    enter_dev_ch(ctx, print_kptr(sa, (char *)NULL, 0));
    /*
     * If buffer size is less than free bytes, enable offset display. Otherwise
     * set the file size as buffer size less free bytes.
     */
    if (mn.free <= mn.sz) {
        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(ctx, (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(ctx, Namech);
}
#endif /* AIXV>=4200 */

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

int readlino(struct lsof_context *ctx, /* context */
             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(ctx, ga, li));
#endif /* defined(HAS_JFS2) */

    /*
     * Read a "standard" inode.
     */
    if (readinode(ctx, (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);
}
