/*
 * dfile.c - AIX file processing 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 structures
 */

/*
 * Local static variables
 */

static struct hsfile *HbyMPC = /* hash by MPC file buckets */
    (struct hsfile *)NULL;
static int HbyMPCCt = 0; /* HbyMPC entry count */

/*
 * Local definitions
 */

#define SFDIHASH                                                               \
    4094 /* Sfile hash by (device,inode) number                                \
          * pair bucket count (power of 2!) */
#define SFFSHASH                                                               \
    128 /* Sfile hash by file system device                                    \
         * number bucket count (power of 2!) */
#define SFHASHDEVINO(maj, min, ino, mod)                                       \
    ((int)(((int)((((int)(maj + 1)) * ((int)((min + 1)))) + ino) * 31415) &    \
           (mod - 1)))
/* hash for Sfile by major device,
 * minor device, and inode, modulo m
 * (m must be a power of 2) */
#define SFMPCHASH 1024 /* Sfile hash by MPC device number */
#define SFNMHASH                                                               \
    4096 /* Sfile hash by name bucket count                                    \
  (power of 2!) */
#define SFRDHASH                                                               \
    1024 /* Sfile hash by raw device number                                    \
          * bucket count (power of 2!) */
#define SFHASHRDEVI(maj, min, rmaj, rmin, ino, mod)                            \
    ((int)(((int)((((int)(maj + 1)) * ((int)((min + 1)))) +                    \
                  ((int)(rmaj + 1) * (int)(rmin + 1)) + ino) *                 \
            31415) &                                                           \
           (mod - 1)))
/* hash for Sfile by major device,
 * minor device, major raw device,
 * minor raw device, and inode, modulo
 * mod (mod must be a power of 2) */

/*
 * hashSfile() - hash Sfile entries for use in is_file_named() searches
 */

void hashSfile(struct lsof_context *ctx) {
    static int hs = 0;
    int i;
    struct sfile *s;
    struct hsfile *sh, *sn;
    /*
     * Do nothing if there are no file search arguments cached or if the
     * hashes have already been constructed.
     */
    if (!Sfile || hs)
        return;
    /*
     * Allocate hash buckets by (device,inode), file system device, MPC device,
     * and file name.
     */
    if (!(HbyFdi = (struct hsfile *)calloc((MALLOC_S)SFDIHASH,
                                           sizeof(struct hsfile)))) {
        (void)fprintf(
            stderr, "%s: can't allocate space for %d (dev,ino) hash buckets\n",
            Pn, SFDIHASH);
        Error(ctx);
    }
    if (!(HbyFrd = (struct hsfile *)calloc((MALLOC_S)SFRDHASH,
                                           sizeof(struct hsfile)))) {
        (void)fprintf(stderr,
                      "%s: can't allocate space for %d rdev hash buckets\n", Pn,
                      SFRDHASH);
        Error(ctx);
    }
    if (!(HbyFsd = (struct hsfile *)calloc((MALLOC_S)SFFSHASH,
                                           sizeof(struct hsfile)))) {
        (void)fprintf(stderr,
                      "%s: can't allocate space for %d file sys hash buckets\n",
                      Pn, SFFSHASH);
        Error(ctx);
    }
    if (!(HbyMPC = (struct hsfile *)calloc((MALLOC_S)SFMPCHASH,
                                           sizeof(struct hsfile)))) {
        (void)fprintf(stderr,
                      "%s: can't allocate space for %d MPC file hash buckets\n",
                      Pn, SFMPCHASH);
        Error(ctx);
    }
    if (!(HbyNm = (struct hsfile *)calloc((MALLOC_S)SFNMHASH,
                                          sizeof(struct hsfile)))) {
        (void)fprintf(stderr,
                      "%s: can't allocate space for %d name hash buckets\n", Pn,
                      SFNMHASH);
        Error(ctx);
    }
    hs++;
    /*
     * Scan the Sfile chain, building file, file system, MPC file, and file
     * name hash bucket chains.
     */
    for (s = Sfile; s; s = s->next) {
        for (i = 0; i < 4; i++) {
            if (i == 0) {
                if (!s->aname)
                    continue;
                sh = &HbyNm[hashbyname(s->aname, SFNMHASH)];
                HbyNmCt++;
            } else if (i == 1) {
                if (s->type) {
                    sh = &HbyFdi[SFHASHDEVINO(GET_MAJ_DEV(s->dev),
                                              GET_MIN_DEV(s->dev), s->i,
                                              SFDIHASH)];
                    HbyFdiCt++;
                } else {
                    sh = &HbyFsd[SFHASHDEVINO(
                        GET_MAJ_DEV(s->dev), GET_MIN_DEV(s->dev), 0, SFFSHASH)];
                    HbyFsdCt++;
                }
            } else if (i == 2) {
                if (s->type && (s->ch < 0) && (s->mode == S_IFCHR)) {
                    sh = &HbyMPC[SFHASHDEVINO(GET_MAJ_DEV(s->dev),
                                              GET_MIN_DEV(s->dev), 0,
                                              SFMPCHASH)];
                    HbyMPCCt++;
                } else
                    continue;
            } else if (i == 3) {
                if (s->type && (((s->mode == S_IFCHR) && (s->ch < 0)) ||
                                ((s->mode == S_IFBLK)))) {
                    sh = &HbyFrd[SFHASHRDEVI(
                        GET_MAJ_DEV(s->dev), GET_MIN_DEV(s->dev),
                        GET_MAJ_DEV(s->rdev), GET_MIN_DEV(s->rdev), s->i,
                        SFRDHASH)];
                    HbyFrdCt++;
                } else
                    continue;
            }
            if (!sh->s) {
                sh->s = s;
                sh->next = (struct hsfile *)NULL;
                continue;
            } else {
                if (!(sn = (struct hsfile *)malloc(
                          (MALLOC_S)sizeof(struct hsfile)))) {
                    (void)fprintf(stderr,
                                  "%s: can't allocate hsfile bucket for: %s\n",
                                  Pn, s->aname);
                    Error(ctx);
                }
                sn->s = s;
                sn->next = sh->next;
                sh->next = sn;
            }
        }
    }
}

/*
 * is_file_named() - is file named?
 */

int is_file_named(struct lsof_context *ctx, /* context */
                  char *p,       /* path name; NULL = search by device
                                  * and inode (from *Lf) */
                  enum vtype ty, /* vnode type */
                  chan_t ch,     /* gnode channel */
                  int ic)        /* is clone file (4.1.4 and above) */
{
    int dmaj, dmin, maj, min, rmaj, rmin;
    static int dsplit = 0;
    char *ep;
    int f = 0;
    struct sfile *s;
    struct hsfile *sh;
    size_t sz;
    /*
     * Split the device numbers into their major and minor numbers.
     *
     * THis is necessitated by 64 bit AIX architectures, which store two
     * different types of device numbers in 64 bit dev_t's.  The two types can't
     * be compared directly, but must be compared by extracting their major and
     * minor numbers and comparing them.
     */
    readdev(ctx, 0);
    if (!dsplit) {
        dmaj = GET_MAJ_DEV(DevDev);
        dmin = GET_MIN_DEV(DevDev);
        dsplit = 1;
    }
    if (Lf->dev_def) {
        maj = GET_MAJ_DEV(Lf->dev);
        min = GET_MIN_DEV(Lf->dev);
    }
    if (Lf->rdev_def) {
        rmaj = GET_MAJ_DEV(Lf->rdev);
        rmin = GET_MIN_DEV(Lf->rdev);
    }

#if AIXV >= 4140
    /*
     * Check for a clone match.
     */
    if (ic && HbyFdiCt && CloneMaj >= 0 &&
        (Lf->dev_def && (maj = dmaj) && (min == dmin)) && Lf->rdev_def &&
        (Lf->inp_ty == 1 || Lf->inp_ty == 3)) {
        for (sh = &HbyFdi[SFHASHDEVINO(CloneMaj, rmaj, Lf->inode, SFDIHASH)];
             sh; sh = sh->next) {
            if ((s = sh->s) && (GET_MAJ_DEV(s->rdev) == CloneMaj) &&
                (GET_MIN_DEV(s->rdev) == rmaj) && (s->i == Lf->inode)) {
                f = 3;
                break;
            }
        }
    }
#endif /* AIXV>=4140 */

    /*
     * Check for a path name match, as requested.
     */
    if (!f && p && HbyNmCt) {
        for (sh = &HbyNm[hashbyname(p, SFNMHASH)]; sh; sh = sh->next) {
            if ((s = sh->s) && strcmp(p, s->aname) == 0) {
                f = 2;
                break;
            }
        }
    }
    /*
     * Check for a regular AIX multiplexed file, matching the channel if
     * it was supplied by the caller.
     */
    if (!f && HbyMPCCt && ty == VMPC &&
        (Lf->dev_def && (maj == dmaj) && (min == dmin)) && Lf->rdev_def) {
        for (sh = &HbyMPC[SFHASHDEVINO(rmaj, rmin, 0, SFMPCHASH)]; sh;
             sh = sh->next) {
            if ((s = sh->s) && (GET_MAJ_DEV(s->dev) == rmaj) &&
                (GET_MIN_DEV(s->dev) == rmin) && (s->ch < 0 || ch == s->ch)) {
                f = 1;
                break;
            }
        }
    }
    /*
     * Check for a regular file.
     */
    if (!f && HbyFdiCt && Lf->dev_def && (Lf->inp_ty == 1 || Lf->inp_ty == 3)) {
        for (sh = &HbyFdi[SFHASHDEVINO(maj, min, Lf->inode, SFDIHASH)]; sh;
             sh = sh->next) {
            if ((s = sh->s) && (maj == GET_MAJ_DEV(s->dev)) &&
                (min == GET_MIN_DEV(s->dev)) && (Lf->inode == s->i)) {
                f = 1;
                break;
            }
        }
    }
    /*
     * Check for a file system.
     */
    if (!f && HbyFsdCt && Lf->dev_def) {
        for (sh = &HbyFsd[SFHASHDEVINO(maj, min, 0, SFFSHASH)]; sh;
             sh = sh->next) {
            if ((s = sh->s) && (maj == GET_MAJ_DEV(s->dev)) &&
                (min == GET_MIN_DEV(s->dev))) {
                f = 1;
                break;
            }
        }
    }
    /*
     * Check for a character or block device file.
     */
    if (!f && HbyFrdCt && ((ty == VCHR) || (ty == VBLK)) &&
        (Lf->dev_def && (maj == dmaj) && (min == dmin)) && Lf->rdev_def &&
        (Lf->inp_ty == 1 || Lf->inp_ty == 3)) {
        for (sh = &HbyFrd[SFHASHRDEVI(maj, min, rmaj, rmin, Lf->inode,
                                      SFRDHASH)];
             sh; sh = sh->next) {
            if ((s = sh->s) && (GET_MAJ_DEV(s->rdev) == rmaj) &&
                (GET_MIN_DEV(s->rdev) == rmin) &&
                (((ty == VCHR) && (s->mode == S_IFCHR) && (s->ch < 0)) ||
                 ((ty == VBLK) && (s->mode == S_IFBLK)))) {
                f = 1;
                break;
            }
        }
    }
    /*
     * Convert the name if a match occurred.
     */
    if (f) {
        if (f == 2) {
            (void)snpf(Namech, Namechl, "%s", p);

#if AIXV >= 4140
        } else if (f == 3 && ClonePtc >= 0 && (maj == ClonePtc)) {
            (void)snpf(Namech, Namechl, "%s/%d", s->name, min);

#endif /* AIXV>=4140 */

        } else if (s->type) {

            /*
             * If the search argument isn't a file system, propagate it
             * to Namech[]; otherwise, let printname() compose the name.
             */
            (void)snpf(Namech, Namechl, "%s", s->name);
            if (ty == VMPC && s->ch < 0) {
                ep = endnm(ctx, &sz);
                (void)snpf(ep, sz, "/%d", ch);
            }
            if (s->devnm) {
                ep = endnm(ctx, &sz);
                (void)snpf(ep, sz, " (%s)", s->devnm);
            }
        }
        s->f = 1;
        return (1);
    }
    return (0);
}

/*
 * print_dev() - print device
 */

char *print_dev(struct lfile *lf, /* file whose device to be printed */
                dev_t *dev)       /* pointer to device to be printed */
{
    static char buf[128];
    int maj = GET_MAJ_DEV(*dev);
    int min = GET_MIN_DEV(*dev);

#if AIXV >= 3200
    if (*dev & SDEV_REMOTE) {
        (void)snpf(buf, sizeof(buf), "NFS,%d", (min & ~SDEV_REMOTE));
        return (buf);
    }
#endif /* AIXV>=3200 */

    (void)snpf(buf, sizeof(buf), "%d,%d", maj, min);
    return (buf);
}

/*
 * readvfs() - read vfs structure
 */

struct l_vfs *readvfs(struct lsof_context *ctx, /* context */
                      struct vnode *vn)         /* vnode */
{
    struct gfs g;
    void *mp;
    char *s1, *s2;
    uint ul;
    struct vfs v;
    struct vmount *vm;
    struct l_vfs *vp;

    if (!vn->v_vfsp)
        return ((struct l_vfs *)NULL);
    for (vp = Lvfs; vp; vp = vp->next) {
        if ((KA_T)vn->v_vfsp == vp->addr)
            return (vp);
    }
    if (!(vp = (struct l_vfs *)malloc((MALLOC_S)sizeof(struct l_vfs)))) {
        (void)fprintf(stderr, "%s: PID %d, no space for vfs\n", Pn, Lp->pid);
        Error(ctx);
    }
    vp->dir = (char *)NULL;
    vp->fsname = (char *)NULL;
    /*
     * Read the vfs structure.
     */
    if (kread(ctx, (KA_T)vn->v_vfsp, (char *)&v, sizeof(v))) {

    vfs_exit:
        (void)free((FREE_P *)vp);
        return ((struct l_vfs *)NULL);
    }
    /*
     * Locate AIX mount information.
     */
    if (!v.vfs_gfs || kread(ctx, (KA_T)v.vfs_gfs, (char *)&g, sizeof(g)))
        goto vfs_exit;
    if (!v.vfs_mdata ||
        kread(ctx,
              (KA_T)((char *)v.vfs_mdata + offsetof(struct vmount, vmt_length)),
              (char *)&ul, sizeof(ul)))
        goto vfs_exit;
    if (!(mp = (void *)malloc((MALLOC_S)ul))) {
        (void)fprintf(stderr, "%s: PID %d, no space for mount data\n", Pn,
                      Lp->pid);
        Error(ctx);
    }
    if (kread(ctx, (KA_T)v.vfs_mdata, (char *)mp, (int)ul)) {
        (void)free((FREE_P *)mp);
        goto vfs_exit;
    }
    vm = (struct vmount *)mp;
    vp->vmt_flags = vm->vmt_flags;
    vp->vmt_gfstype = vm->vmt_gfstype;

#if AIXV >= 3200
    if ((vp->vmt_flags & MNT_REMOTE)

#    if defined(HAS_SANFS) && defined(MNT_SANFS)
        && (vp->vmt_gfstype != MNT_SANFS)
#    endif /* defined(HAS_SANFS) && defined(MNT_SANFS) */

    ) {
        vp->dev = 0x80000000 | vm->vmt_vfsnumber;
#    if AIXA >= 1
        vp->dev |= 0x8000000000000000;
#    endif /* AIXA>=1 */
    } else
#endif /* AIXV>=3200 */

#if defined(HAS_AFS)
        if (vm->vmt_gfstype == MNT_AFS)
        vp->dev = AFSDEV;
    else
#endif /* defined(HAS_AFS) */

#if AIXA > 1
        if (vm->vmt_gfstype == MNT_PROCFS) {

        /*
         * !!!DEBUG!!!   !!!DEBUG!!!   !!!DEBUG!!!   !!!DEBUG!!!   !!!DEBUG!!!
         *
         * The following *hack* is required to make the vmount structure's
         * device number match what stat(2) errnoneously returns in ia64
         * AIX >= 5.
         *
         * REMOVE THIS CODE WHEN STAT(2) IS FIXED!!!
         */
        vp->dev = (dev_t)(vm->vmt_fsid.fsid_dev & 0x7fffffffffffffff);
        /*
         * !!!DEBUG!!!   !!!DEBUG!!!   !!!DEBUG!!!   !!!DEBUG!!!   !!!DEBUG!!!
         */

    } else
#endif /* AIXA>1 */

        vp->dev = (dev_t)vm->vmt_fsid.fsid_dev;
    if ((s1 = vmt2dataptr(vm, VMT_STUB))) {
        if (!(vp->dir = mkstrcpy(s1, (MALLOC_S *)NULL))) {

        readvfs_aix1:
            (void)fprintf(stderr, "%s: PID %d, readvfs, no space\n", Pn,
                          Lp->pid);
            Error(ctx);
        }
    } else
        vp->dir = (char *)NULL;
    s1 = vmt2dataptr(vm, VMT_HOST);
    if (!(s2 = vmt2dataptr(vm, VMT_OBJECT)) || *s1 == '\0')
        s2 = g.gfs_name;
    if (!s1 && !s2)
        vp->fsname = (char *)NULL;
    else {
        if (vm->vmt_flags & MNT_REMOTE) {
            if (!(vp->fsname =
                      mkstrcat(s1 ? s1 : "", -1, (s1 && *s1) ? ":" : "", -1, s2,
                               -1, (MALLOC_S *)NULL)))
                goto readvfs_aix1;
        } else {
            if (!(vp->fsname = mkstrcpy(s2, (MALLOC_S *)NULL)))
                goto readvfs_aix1;
        }
    }
    (void)free((FREE_P *)mp);
    vp->next = Lvfs;
    vp->addr = (KA_T)vn->v_vfsp;

#if defined(HAS_AFS)
    if (!AFSVfsp && vm->vmt_gfstype == MNT_AFS)
        AFSVfsp = (KA_T)vn->v_vfsp;
#endif /* defined(HAS_AFS) */

    Lvfs = vp;
    return (vp);
}
