/*
 * dfile.c - Solaris 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 "lsof.h"


/*
 * Local structures
 */

struct hsfile {
	struct sfile *s;		/* the Sfile table address */
	struct hsfile *next;		/* the next hash bucket entry */
};


/*
 * Local static variables
 */

static struct hsfile *HbyCd =		/* hash by clone buckets */
	(struct hsfile *)NULL;
static int HbyCdCt = 0;			/* HbyCd entry count */
static struct hsfile *HbyFdi =		/* hash by file buckets */
	(struct hsfile *)NULL;
static int HbyFdiCt = 0;		/* HbyFdi entry count */
static struct hsfile *HbyFrd =		/* hash by file raw device buckets */
	(struct hsfile *)NULL;
static int HbyFrdCt = 0;		/* HbyFrd entry count */
static struct hsfile *HbyFsd =		/* hash by file system buckets */
	(struct hsfile *)NULL;
static int HbyFsdCt = 0;		/* HbyFsd entry count */
static struct hsfile *HbyNm =		/* hash by name buckets */
	(struct hsfile *)NULL;
static int HbyNmCt = 0;			/* HbyNm entry count */


/*
 * Local definitions
 */

#define	SFCDHASH	1024		/* Sfile hash by clone device */
#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	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) */


#if	solaris<20500
/*
 * get_max_fd() - get maximum file descriptor plus one
 */

int
get_max_fd()
{
	struct rlimit r;

	if (getrlimit(RLIMIT_NOFILE, &r))
	    return(-1);
	return(r.rlim_cur);
}
#endif	/* solaris<20500 */


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

void
hashSfile()
{
	int cmaj, hvc, i;
	static int hs = 0;
	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;
/*
 * Preset the clone major device for Solaris.
 */
	if (HaveCloneMaj) {
	    cmaj = CloneMaj;
	    hvc = 1;
	} else
	    hvc = 0;
/*
 * Allocate hash buckets by clone device, (device,inode), file system device,
 * and file name.
 */
	if (hvc) {
	    if (!(HbyCd = (struct hsfile *)calloc((MALLOC_S)SFCDHASH,
						  sizeof(struct hsfile))))
	    {
		(void) fprintf(stderr,
		    "%s: can't allocate space for %d clone hash buckets\n",
		    Pn, SFCDHASH);
		Error();
	    }
	}
	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();
	}
	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();
	}
	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();
	}
	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();
	}
	hs++;
/*
 * Scan the Sfile chain, building file, file system, 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->mode == S_IFCHR) || (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;
		} else {
		    if (!hvc || (GET_MAJ_DEV(s->rdev) != cmaj))
			continue;
		    sh = &HbyCd[SFHASHDEVINO(0, GET_MIN_DEV(s->rdev), 0,
					     SFCDHASH)];
		    HbyCdCt++;
		}
		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();
		    }
		    sn->s = s;
		    sn->next = sh->next;
		    sh->next = sn;
		}
	    }
	}
}


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

int
is_file_named(p, nt, vt, ps)
	char *p;			/* path name; NULL = search by device
					 * and inode (from *Lf) */
	int nt;				/* node type -- e.g., N_* */
	enum vtype vt;			/* vnode type */
	int ps;				/* print status: 0 = don't copy name
					 * to Namech */
{
	char *ep;
	int f = 0;
	struct sfile *s;
	struct hsfile *sh;
	size_t sz;
/*
 * Check for a path name match, as requested.
 */
	if (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 Solaris clone file.
 */
	if (!f && HbyCdCt && nt == N_STREAM && Lf->dev_def && Lf->rdev_def
	&&  (Lf->dev == DevDev))
	{
	    for (sh = &HbyCd[SFHASHDEVINO(0, GET_MAJ_DEV(Lf->rdev), 0,
					  SFCDHASH)];
		 sh;
		 sh = sh->next)
	    {
		if ((s = sh->s) && (GET_MAJ_DEV(Lf->rdev)
				==  GET_MIN_DEV(s->rdev)))
		{
		    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(GET_MAJ_DEV(Lf->dev),
					   GET_MIN_DEV(Lf->dev),
					   Lf->inode,
					   SFDIHASH)];
		 sh;
		 sh = sh->next)
	    {
		if ((s = sh->s) && (Lf->dev == s->dev)
		&&  (Lf->inode == s->i)) {
		    f = 1;
		    break;
		}
	    }
	}
/*
 * Check for a file system match.
 */
	if (!f && HbyFsdCt && Lf->dev_def) {
	    for (sh = &HbyFsd[SFHASHDEVINO(GET_MAJ_DEV(Lf->dev),
					   GET_MIN_DEV(Lf->dev), 0, SFFSHASH)];
		 sh;
		 sh = sh->next)
	    {
		if ((s = sh->s) && Lf->dev == s->dev) {
		    f = 1;
		    break;
		}
	    }
	}
/*
 * Check for a character or block device match.
 */
	if (!f && HbyFrdCt
	&&  ((vt = VCHR) || (vt = VBLK))
	&&  Lf->dev_def && (Lf->dev == DevDev)
	&&  Lf->rdev_def
	&& (Lf->inp_ty == 1 || Lf->inp_ty == 3))
	{
	    for (sh = &HbyFrd[SFHASHRDEVI(GET_MAJ_DEV(Lf->dev),
					  GET_MIN_DEV(Lf->dev),
					  GET_MAJ_DEV(Lf->rdev),
					  GET_MIN_DEV(Lf->rdev),
					  Lf->inode, SFRDHASH)];
		 sh;
		 sh = sh->next)
	    {
		if ((s = sh->s) && (s->dev == Lf->dev)
		&&  (s->rdev == Lf->rdev) && (s->i == Lf->inode))
		{
		    f = 1;
		    break;
		}
	    }
	}
/*
 * Convert the name if a match occurred.
 */
	if (f) {
	    if (f == 2) {
		if (ps)
		    (void) snpf(Namech, Namechl, "%s", p);
	    } else {
		if (ps && 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 (s->devnm) {
			ep = endnm(&sz);
			(void) snpf(ep, sz, " (%s)", s->devnm);
		    }
		}
	    }
	    s->f = 1;
	    return(1);
	}
	return(0);
}


#if	defined(HASPRINTDEV)
/*
 * print_dev() - print device
 */

char *
print_dev(lf, dev)
	struct lfile *lf;		/* file whose device is to be printed */
	dev_t *dev;			/* device to be printed */
{
	static char buf[128];
/*
 * Avoid the Solaris major() and minor() functions from makedev(3C) to get
 * printable major/minor numbers.
 *
 * We would like to use the L_MAXMAJ definition from <sys/sysmacros.h> all
 * the time, but it's not always correct in all versions of Solaris.
 */
	(void) snpf(buf, sizeof(buf), "%d,%d", (int)((*dev >> L_BITSMINOR) &

#if	solaris>=20501
	    L_MAXMAJ
#else	/* solaris<20501 */
	    0x3fff
#endif	/* solaris>=20501 */

	    ), (int)(*dev & L_MAXMIN));
	return(buf);
}
#endif	/* defined(HASPRINTDEV) */


#if	defined(HAS_V_PATH)

/*
 * Local definitions
 */

#define	VPRDLEN	((MAXPATHLEN + 7)/8)	/* v_path read length increment */


/*
 * print_v_path() - print path name from vnode's v_path pointer
 */

extern int
print_v_path(lf)
	struct lfile *lf;		/* local file structure */
{
	char buf[MAXPATHLEN+1];
	unsigned char del = 0;
	unsigned char aperr = 0;

# if	defined(HASMNTSTAT)
	struct stat sb;
# endif	/* defined(HASMNTSTAT) */

# if	defined(HASVXFS) && defined(HASVXFSRNL)
	if (lf->is_vxfs && (lf->inp_ty == 1) && lf->fsdir) {
	    if (print_vxfs_rnl_path(lf))
		return(1);
	}
# endif	/* defined(HASVXFS) && defined(HASVXFSRNL) */

	(void) read_v_path((KA_T)lf->V_path, buf, (size_t)sizeof(buf));
	if (buf[0]) {

# if	defined(HASMNTSTAT)
	    if (!lf->mnt_stat && lf->dev_def && (lf->inp_ty == 1)) {

	    /*
	     * No problem was detected in applying stat(2) to this mount point.
	     * If the device and inode for the file are known, it is probably
	     * safe and worthwhile to apply stat(2) to the v_path.
	     */
		if (!statsafely(buf, &sb)) {

		/*
		 * The stat(2) succeeded.  See if the device and inode match.
		 * If they both don't match, ignore the v_path.
		 */
		    if ((lf->dev != sb.st_dev)
		    ||  (lf->inode != (INODETYPE)sb.st_ino)
		    ) {
			return(0);
		    }
		} else {

		/*
		 * The stat(2) failed.
		 *
		 * If the error reply is ENOENT and the -X option hasn't been
		 * specified, ignore the v_path.
		 *
		 * If the error reply is ENOENT, the -X option has been
		 * specified and the file's link count is zero, report the
		 * v_path with the "(deleted)" notation.
		 *
		 * If the error reply is EACCES or EPERM, report the v_path,
		 * followed by "(?)", because lsof probably lacks permission
		 * to apply stat(2) to v_path.
		 */
		    switch (errno) {
		    case EACCES:
		    case EPERM:
			aperr = 1;
			break;
		    case ENOENT:

# if	defined(HASXOPT)
			if (Fxopt && lf->nlink_def && !lf->nlink) {
			    del = 1;
			    break;
			}
# endif	/* defined(HASXOPT) */

			return(0);
		    default:
			return(0);
		    }
		}
	    }
# endif	/* defined(HASMNTSTAT) */

	/*
	 * Print the v_path.
	 */
	    safestrprt(buf, stdout, 0);
	    if (del)
		safestrprt(" (deleted)", stdout, 0);
	    else if (aperr)
		safestrprt(" (?)", stdout, 0);
	    return(1);
	}
	return(0);
}


/*
 * read_v_path() - read path name from vnode's v_path pointer
 */

extern void
read_v_path(ka, rb, rbl)
	KA_T ka;			/* kernel path address */
	char *rb;			/* receiving buffer */
	size_t rbl;			/* receiving buffer length */
{
	char *ba;
	size_t rl, tl;

	*rb = '\0';
	if (!ka)
	    return;
	for (ba = rb, tl = 0;
	     tl < (rbl - 1);
	     ba += rl, ka += (KA_T)((char *)ka + rl), tl += rl
	) {

	/*
	 * Read v_path VPRDLEN bytes at a time until the local buffer is full
	 * or a NUL byte is reached.
	 */
	    if ((rl = rbl - 1 - tl) > VPRDLEN)
		rl = VPRDLEN;
	    else if (rl < 1) {
		*(rb + rbl - 1) = '\0';
		break;
	    }
	    if (!kread(ka, ba, rl)) {
		*(ba + rl) = '\0';
		if (strchr(ba, '\0') < (ba + rl))
		    break;
	    } else {

	    /*
	     * Can't read a full buffer load; try reducing the length one
	     * byte at a time until it reaches zero.  Stop here, since it
	     * has been established that no more bytes can be read.
	     */
		for (rl--; rl > 0; rl--) {
		    if (!kread(ka, ba, rl)) {
			*(ba + rl) = '\0';
			break;
		    }
		}
		if (rl <= 0)
		    *ba = '\0';
		break;
	    }
	}
}
#endif	/* defined(HAS_V_PATH) */


/*
 * process_file() - process file
 */

void
process_file(fp)
	KA_T fp;		/* kernel file structure address */
{
	struct file f;
	int flag;

#if	defined(FILEPTR)
	FILEPTR = &f;
#endif	/* defined(FILEPTR) */

	if (kread(fp, (char *)&f, sizeof(f))) {
	    (void) snpf(Namech, Namechl, "can't read file struct from %s",
		print_kptr(fp, (char *)NULL, 0));
	    enter_nm(Namech);
	    return;
	}
	Lf->off = (SZOFFTYPE)f.f_offset;

	if (f.f_count) {

	/*
	 * Construct access code.
	 */
	    if ((flag = (f.f_flag & (FREAD | FWRITE))) == FREAD)
		Lf->access = 'r';
	    else if (flag == FWRITE)
		Lf->access = 'w';
	    else if (flag == (FREAD | FWRITE))
		Lf->access = 'u';

#if	defined(HASFSTRUCT)
	/*
	 * Save file structure values.
	 */
	    if (Fsv & FSV_CT) {
		Lf->fct = (long)f.f_count;
		Lf->fsv |= FSV_CT;
	    }
	    if (Fsv & FSV_FA) {
		Lf->fsa = fp;
		Lf->fsv |= FSV_FA;
	    }
	    if (Fsv & FSV_FG) {
		Lf->ffg = (long)f.f_flag;
		Lf->fsv |= FSV_FG;
	    }
	    if (Fsv & FSV_NI) {
		Lf->fna = (KA_T)f.f_vnode;
		Lf->fsv |= FSV_NI;
	    }
#endif	/* defined(HASFSTRUCT) */

	/*
	 * Solaris file structures contain a vnode pointer.  Process it.
	 */
	    process_node((KA_T)f.f_vnode);
	    return;
	}
	enter_nm("no more information"); }


#if	defined(HASIPv6)
/*
 * gethostbyname2() -- an RFC2133-compatible get-host-by-name-two function
 *                     to get AF_INET and AF_INET6 addresses from host names,
 *                     using the RFC2553-compatible getipnodebyname() function
 */

extern struct hostent *
gethostbyname2(nm, prot)
	const char *nm; 		/* host name */
	int prot;			/* protocol -- AF_INET or AF_INET6 */
{
	int err;
	static struct hostent *hep = (struct hostent *)NULL;

	if (hep)
	    (void) freehostent(hep);
	return((hep = getipnodebyname(nm, prot, 0, &err)));
}
#endif	/* defined(HASIPv6) */
