/*
 * dmnt.c - AIX 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";
static char *rcsid = "$Id: dmnt.c,v 1.8 96/03/08 14:02:22 abe Exp $";
#endif


#include "lsof.h"


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

int
readmnt()
{
	int err = 0;
	char *dir, *fs, *h, *ty;
	int fsl;
	struct mounts *mtp;
	int nm;
	struct stat sb;
	unsigned sz;
	struct vmount *v, *vt;
/*
 * Read the table of vmount structures.
 */
	for (sz = sizeof(struct vmount);;) {
		if ((vt = (struct vmount *)malloc(sz)) == NULL) {
			(void) fprintf(stderr,
				"%s: no space for vmount table\n", Pn);
			return(0);
		}
		nm = mntctl(MCTL_QUERY, sz, vt);
		if (nm > 0) {
			if (vt->vmt_revision != VMT_REVISION) {
				(void) fprintf(stderr,
					"%s: stale file system, rev %d != %d\n",
					Pn, vt->vmt_revision, VMT_REVISION);
				return(0);
			}
			break;
		}
		if (nm == 0) {
			sz = (unsigned)vt->vmt_revision;
			(void) free((FREE_P *)vt);
		} else {
			(void) fprintf(stderr, "%s: mntctl error: %s\n",
				Pn, strerror(errno));
			return(0);
		}
	}
/*
 * Scan the vmount structures and build Mtab.
 */
	for (v = vt; nm--; v = (struct vmount *)((char *)v + v->vmt_length)) {
		dir = (char *)vmt2dataptr(v, VMT_STUB);
		fs = (char *)vmt2dataptr(v, VMT_OBJECT);
		h = (char *)vmt2dataptr(v, VMT_HOST);
		fsl = strlen(fs);
		if (v->vmt_flags & MNT_REMOTE)
			fsl += strlen(h) + 1;
                if (statsafely(dir, &sb)) {
		    err = 2;
		    if (!Fwarn) {

		    /*
		     * Issue stat() failure warning.
		     */
			switch(v->vmt_gfstype) {

#if	defined(HAS_AFS)
			case MNT_AFS:
				ty = "afs";
				break;
#endif	/* defined(HAS_AFS) */

			case MNT_AIX:
				ty = "oaix";
				break;
			case MNT_NFS:
				ty = "nfs";
				break;
			case MNT_JFS:
				ty = "jfs";
				break;
			case MNT_CDROM:
				ty = "cdrom";
				break;
			default:
				ty = "unknown";
			}
			(void) fprintf(stderr,
			    "%s: WARNING: can't stat() %s file system %s\n",
			    Pn, ty, dir);
			(void) fprintf(stderr,
			    "      Output information may be incomplete.\n");
		    }
		/*
		 * Assemble alternate device number and mode flags.
		 */
		    (void) bzero((char *)&sb, sizeof(sb));
		    if (v->vmt_flags & MNT_REMOTE)
			sb.st_dev = (dev_t)(SDEV_REMOTE | v->vmt_vfsnumber);
		    else {

#if	defined(HAS_AFS)
			if (v->vmt_gfstype == MNT_AFS)
			    sb.st_dev = AFSDEV;
			else
#endif	/* defined(HAS_AFS) */

			    sb.st_dev = (dev_t)v->vmt_fsid.val[0];
		    }
		    if (!Fwarn)
			(void) fprintf(stderr,
			    "      assuming \"dev=%#lx\" from mount table\n",
			    sb.st_dev);
		    sb.st_mode = S_IFDIR | 0777;
		}
		if ((mtp = (struct mounts *)malloc(sizeof(struct mounts)))
		== NULL) {
			err = 1;
			break;
		}
		if ((mtp->dir = (char *)malloc(strlen(dir) + 1)) == NULL) {
			err = 1;
			break;
		}
		(void) strcpy(mtp->dir, dir);
		if ((mtp->fsname = (char *)malloc(fsl + 1)) == NULL) {
			err = 1;
			break;
		}
		if (v->vmt_flags & MNT_REMOTE)
			(void) sprintf(mtp->fsname, "%s:%s", h, fs);
		else
			(void) strcpy(mtp->fsname, fs);
		mtp->dev = sb.st_dev;
		mtp->inode = sb.st_ino;
		mtp->mode = sb.st_mode;
		mtp->rdev = sb.st_rdev;
		mtp->next = Mtab;
		Mtab = mtp;
        }
/*
 * Handle errors.
 */
	(void) free((FREE_P *)vt);
	switch(err) {
	case 1:
		(void) fprintf(stderr, "%s: no space for mount at %s (%s)\n",
			Pn, fs, dir);
		exit(1);
	case 2:
		return(1);
	}
	return(1);
}


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

struct l_vfs *
readvfs(vn)
	struct vnode *vn;		/* vnode */
{
	struct gfs g;
	void *mp;
	char *s1, *s2;
	unsigned slen;
	u_long ul;
	struct vfs v;
	struct vmount *vm;
	struct l_vfs *vp;

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

vfs_exit:
		(void) free((FREE_P *)vp);
		return(NULL);
	}
/*
 * Locate AIX mount information.
 */
	if (!v.vfs_gfs || kread((KA_T)v.vfs_gfs, (char *)&g, sizeof(g)))
		goto vfs_exit;
	if (!v.vfs_mdata
	||  kread((KA_T)v.vfs_mdata + (KA_T)sizeof(u_long), (char *)&ul,
		  sizeof(ul))
	)
		goto vfs_exit;
	if ((mp = (void *)malloc((size_t) ul)) == NULL) {
		(void) fprintf(stderr, "%s: PID %d, no space for mount data\n",
			Pn, Lp->pid);
		exit(1);
	}
	if (kread((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)
		vp->dev = 0x80000000 | vm->vmt_vfsnumber;
	else
#endif	/* _AIXV>=3200 */

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

			vp->dev = (dev_t)vm->vmt_fsid.fsid_dev;
	if ((s1 = vmt2dataptr(vm, VMT_STUB)) != NULL) {
		if ((vp->dir = (char *)malloc(strlen(s1) + 1)) == NULL) {

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

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

	Lvfs = vp;
	return(vp);
}
