/*
 * dmnt.c - Solaris mount support 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 static definitions
 */

static char *getmntdev(struct lsof_context *ctx, char *o, int l, struct stat *s,
                       char *f);

/*
 * getmntdev() - get mount entry's device number
 */

static char *getmntdev(struct lsof_context *ctx, /* context */
                       char *o,                  /* start of device option */
                       int l,          /* length of device keyword (not
                                        * including `=') */
                       struct stat *s, /* pointer to stat buffer to create */
                       char *f)        /* file system type */
{
    char *opte;

    memset((char *)s, 0, sizeof(struct stat));
    if (!(opte = x2dev(o + l + 1, &s->st_dev)))
        return ((char *)NULL);

#if solaris >= 70000 && L_BITSMAJOR != L_BITSMAJOR32
    /*
     * If this is a Solaris 7 system with a 64 bit kernel, convert the 32 bit
     * device number to a 64 bit device number.
     */
    s->st_dev = (((s->st_dev >> L_BITSMINOR32) & L_MAXMAJ32) << L_BITSMINOR) |
                (s->st_dev & L_MAXMIN32);
#endif /* solaris>=70000 && L_BITSMAJOR!=L_BITSMAJOR32 */

    s->st_mode = S_IFDIR | 0777;

#if defined(HASFSTYPE)
    if (f) {
        (void)strncpy(s->st_fstype, f, sizeof(s->st_fstype));
        s->st_fstype[sizeof(s->st_fstype) - 1] = '\0';
    }
#endif /* defined(HASFSTYPE) */

    return (opte);
}

/*
 * readmnt() - read mount table
 */

struct mounts *readmnt(struct lsof_context *ctx) {
    int devl, ignore;
    char *cp, *dir, *fs;
    char *dn = (char *)NULL;
    char *ln;
    FILE *mfp;
    struct mounts *mtp;
    char *dopt, *dopte;
    struct stat sb;
    struct mnttab me;
    struct mnttab *mp;

#if defined(HASPROCFS)
    int procfs = 0;
#endif /* defined(HASPROCFS) */

    unsigned char stat;
    char *zopt;

#if defined(HASZONES)
    int zwarn = 0;
#endif /* definesd(HASZONES) */

    if (Lmi || Lmist)
        return (Lmi);
    devl = strlen(MNTOPT_DEV);
    /*
     * Open access to the mount table and read mount table entries.
     */
    if (!(mfp = fopen(MNTTAB, "r"))) {
        (void)fprintf(stderr, "%s: can't access %s\n", Pn, MNTTAB);
        return (0);
    }
    for (mp = &me; getmntent(mfp, mp) == 0;) {

        /*
         * Skip loop-back mounts, since they are aliases for legitimate file
         * systems and there is no way to determine that a vnode refers to a
         * loop-back alias.
         */
        if (strcmp(mp->mnt_fstype, MNTTYPE_LO) == 0)
            continue;
        /*
         * Save pointers to the directory and file system names for later use.
         *
         * Check the file system name.  If it doesn't begin with a `/'
         * but contains a `:' not followed by a '/', ignore this entry.
         */
        dir = mp->mnt_mountp;
        fs = mp->mnt_special;
        if (*fs != '/' && (cp = strchr(fs, ':')) && *(cp + 1) != '/')
            continue;
        /*
         * Check for a "ignore" type (SunOS) or "ignore" option (Solaris).
         */
        if (hasmntopt(mp, MNTOPT_IGNORE))
            ignore = 1;
        else
            ignore = 0;
        /*
         * Interpolate a possible symbolic directory link.
         */
        if (dn)
            (void)free((FREE_P *)dn);
        if (!(dn = mkstrcpy(dir, (MALLOC_S *)NULL))) {

        no_space_for_mount:

            (void)fprintf(stderr, "%s: no space for mount ", Pn);
            safestrprt(fs, stderr, 0);
            (void)fprintf(stderr, " (");
            safestrprt(dir, stderr, 0);
            (void)fprintf(stderr, ")\n");
            Error(ctx);
        }
        if (!(ln = Readlink(ctx, dn))) {
            if (!Fwarn) {
                (void)fprintf(stderr,
                              "      Output information may be incomplete.\n");
            }
            continue;
        }
        if (ln != dn) {
            (void)free((FREE_P *)dn);
            dn = ln;
        }
        if (*dn != '/')
            continue;
        /*
         * Stat() the directory.
         *
         * Avoid the stat() if the mount entry has an "ignore" option and
         * try to use the mount entry's device number instead.
         */
        dopt = hasmntopt(mp, MNTOPT_DEV);
        if (ignore) {
            if (!dopt || !(dopte = getmntdev(ctx, dopt, devl, &sb,

#if defined(HASFSTYPE)
                                             mp->mnt_fstype
#else  /* !defined(HASFSTYPE) */
                                             (char *)NULL
#endif /* defined(HASFSTYPE) */

                                             )))
                continue;
            stat = 1;
        } else if (statsafely(ctx, dn, &sb)) {
            if (dopt) {
                if (!(dopte = getmntdev(ctx, dopt, devl, &sb,

#if defined(HASFSTYPE)
                                        mp->mnt_fstype
#else  /* !defined(HASFSTYPE) */
                                        (char *)NULL
#endif /* defined(HASFSTYPE) */

                                        )))
                    dopt = (char *)NULL;
            } else
                dopte = (char *)NULL;
            if (!Fwarn) {

#if defined(HASZONES)
                if ((zopt = hasmntopt(mp, "zone")) && dopte)
                    zwarn++;
#else  /* !defined(HASZONES) */
                zopt = (char *)NULL;
#endif /* defined(HASZONES) */

                if (!zopt || !dopte) {
                    (void)fprintf(stderr, "%s: WARNING: can't stat() ", Pn);
                    safestrprt(mp->mnt_fstype, stderr, 0);
                    (void)fprintf(stderr, " file system ");
                    safestrprt(dir, stderr, 1);
                    (void)fprintf(
                        stderr,
                        "      Output information may be incomplete.\n");
                    if (dopte) {
                        (void)fprintf(stderr,
                                      "      assuming \"%.*s\" from %s\n",
                                      (int)(dopte - dopt), dopt, MNTTAB);
                    }
                }
            }
            if (!dopt)
                continue;
            stat = 1;
        } else
            stat = 0;
        /*
         * Allocate and fill a local mount structure.
         */
        if (!(mtp = (struct mounts *)malloc(sizeof(struct mounts))))
            goto no_space_for_mount;

#if defined(HASFSTYPE)
        if (!(mtp->fstype = mkstrcpy(sb.st_fstype, (MALLOC_S *)NULL)))
            goto no_space_for_mount;
#endif /* defined(HASFSTYPE) */

        mtp->dir = dn;
        dn = (char *)NULL;
        mtp->next = Lmi;
        mtp->dev = sb.st_dev;
        mtp->rdev = sb.st_rdev;
        mtp->inode = (INODETYPE)sb.st_ino;
        mtp->mode = sb.st_mode;

#if solaris >= 80000
        mtp->nlink = sb.st_nlink;
        mtp->size = sb.st_size;
#endif /* solaris>=80000 */

#if defined(HASMNTSTAT)
        mtp->stat = stat;
#endif /* defined(HASMNTSTAT) */

#if defined(HASPROCFS)
        if (strcmp(sb.st_fstype, HASPROCFS) == 0) {

            /*
             * Save information on exactly one proc file system.
             */
            if (procfs)
                Mtprocfs = (struct mounts *)NULL;
            else {
                procfs = 1;
                Mtprocfs = mtp;
            }
        }
#endif /* defined(HASPROCFS) */

        /*
         * Interpolate a possible file system (mounted-on) device name link.
         */
        if (!(dn = mkstrcpy(fs, (MALLOC_S *)NULL)))
            goto no_space_for_mount;
        mtp->fsname = dn;
        ln = Readlink(ctx, dn);
        dn = (char *)NULL;
        /*
         * Stat() the file system (mounted-on) name and add file system
         * information to the local mount table entry.
         */
        if (!ln || statsafely(ctx, ln, &sb))
            sb.st_mode = 0;
        mtp->fsnmres = ln;
        mtp->fs_mode = sb.st_mode;
        Lmi = mtp;

#if defined(HAS_AFS)
        /*
         * If an AFS device number hasn't yet been defined, look for it.
         */
        if (!AFSdevStat && mtp->dir && strcmp(mtp->dir, "/afs") == 0 &&
            mtp->fsname && strcmp(mtp->fsname, "AFS") == 0) {
            AFSdev = mtp->dev;
            AFSdevStat = 1;
        }
#endif /* defined(HAS_AFS) && solaris>=20600 */
    }
    (void)fclose(mfp);

#if defined(HASZONES)
    /*
     * If some zone file systems were encountered, issue a warning.
     */
    if (!Fwarn && zwarn) {
        (void)fprintf(stderr, "%s: WARNING: can't stat() %d zone file system%s",
                      Pn, zwarn, (zwarn == 1) ? "" : "s");
        (void)fprintf(stderr, "; using dev= option%s\n",
                      (zwarn == 1) ? "" : "s");
    }
#endif /* defined(HASZONES) */

    /*
     * Clean up and return local mount info table address.
     */
    if (dn)
        (void)free((FREE_P *)dn);
    Lmist = 1;
    return (Lmi);
}

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

struct l_vfs *readvfs(struct lsof_context *ctx, /* context */
                      KA_T ka,          /* vfs structure kernel address, if
                                         * must be read from kernel */
                      struct vfs *la,   /* local vfs structure address, non-
                                         * NULL if already read from kernel */
                      struct vnode *lv) /* local vnode */
{
    struct vfs *v, tv;
    struct l_vfs *vp;

    if (!ka && !la)
        return ((struct l_vfs *)NULL);
    for (vp = Lvfs; vp; vp = vp->next) {
        if (ka == vp->addr)
            return (vp);
    }
    if (!(vp = (struct l_vfs *)malloc(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;

#if defined(HASFSINO)
    vp->fs_ino = 0;
#endif /* defined(HASFSINO) */

    /*
     * Read vfs structure from kernel, if necessary.
     */
    if (la)
        v = la;
    else {
        v = &tv;
        if (kread(ctx, (KA_T)ka, (char *)v, sizeof(tv))) {
            (void)free((FREE_P *)vp);
            return ((struct l_vfs *)NULL);
        }
    }

#if defined(HAS_AFS)
    /*
     * Fake the device number for an AFS device.
     */
    if (v->vfs_fstype == AFSfstype) {
        if (!AFSdevStat)
            (void)readmnt(ctx);
        v->vfs_dev = AFSdevStat ? AFSdev : 0;
    }
#endif /* defined(HAS_AFS) */

    /*
     * Complete mount information.
     */

    (void)completevfs(ctx, vp, (dev_t *)&v->vfs_dev);
    vp->next = Lvfs;
    vp->addr = ka;
    Lvfs = vp;
    return (vp);
}
