/*
 * dnode.c - Linux node 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: dnode.c,v 1.6 96/01/11 14:41:05 abe Exp $";
#endif


#include "lsof.h"


/*
 * Local definitions
 */

#define	OFFSET_MAX	((off_t)0x7fffffff)	/* this is defined in
						 * .../src/fs/locks.c and not
						 * in a header file */

/*
 * process_inode() - process inode
 */

void
process_inode(fi, fop)
	struct inode *fi;		/* inode kernel space address 
					 * from file structure */
	struct file_operations *fop;	/* inode kernel file operations pointer
					 * from file structure */
{
	dev_t dev;
	char dev_ch[32];
	struct inode is;
	struct file_lock lk;
	short lt;
	struct mounts *mp;
	char *pd, *tn;
	umode_t type;

/*
 * Read inode.
 */
	if (readinode(fi, &is)) {
		enter_nm(Namech);
		return;
	}

#if	defined(HASNCACHE)
/*
 * Save inode address if HASNCACHE defined.
 */
	Lf->na = (unsigned long)fi;
#endif	/* defined(HASNCACHE) */

/*
 * Process node type.
 */
	type = is.i_mode & S_IFMT;
	switch (type) {
	case S_IFBLK:
		Ntype = N_BLK;
		break;
	case S_IFCHR:
		Ntype = N_CHR;
		Lf->is_chr_dev = 1;
		break;
	case S_IFIFO:
		Ntype = N_FIFO;
		if (Nl[X_RDPOPS].n_type
		&&  (unsigned long)fop == Nl[X_RDPOPS].n_value)
			pd = "<-";
		else if (Nl[X_WRPOPS].n_type
		     &&  (unsigned long)fop == Nl[X_WRPOPS].n_value)
			pd = "->";
		else
			pd = NULL;
		break;
	case S_IFSOCK:
		process_isock(&is);
		return;
	}
	if (Ntype == N_REGLR) {
		if ((Nl[X_NFSDOPS].n_type &&
		     (unsigned long)is.i_op == Nl[X_NFSDOPS].n_value)
		||  (Nl[X_NFSFOPS].n_type &&
		     (unsigned long)is.i_op == Nl[X_NFSFOPS].n_value)
		||  (Nl[X_NFSYOPS].n_type &&
		     (unsigned long)is.i_op == Nl[X_NFSYOPS].n_value))
			Ntype = N_NFS;
	}
/*
 * Obtain lock information.
 */
	if (is.i_flock
	&&  kread((KA_T)is.i_flock, (char *)&lk, sizeof(lk)) == 0) {

#if	LINUXV<1300
		if (lk.fl_whence == 0
#else	/* LINUXV>=1300 */
		if (lk.fl_flags == 0
#endif	/* LINUXV<1300 */

		&&  lk.fl_start == 0
		&&  (lk.fl_end == 0 || lk.fl_end == OFFSET_MAX))
			lt = 1;
		else
			lt = 0;
		switch (lk.fl_type & (F_RDLCK | F_WRLCK)) {
		case F_RDLCK:
			Lf->lock = lt ? 'R' : 'r';
			break;
		case F_WRLCK:
			Lf->lock = lt ? 'W' : 'w';
		}
	}
/*
 * Determine the device.
 */
	switch (Ntype) {
	case N_FIFO:
		if (pd) {
			(void) sprintf(dev_ch, "0x%08x", is.u.pipe_i.base);
			enter_dev_ch(dev_ch);
			break;
		}
		/* fall through */
	default:
		Lf->dev = dev = (Ntype == N_CHR) ? is.i_rdev : is.i_dev;
		Lf->dev_def = 1;
	}
/*
 * Determine the inode number.
 */
	switch (Ntype) {
	case N_FIFO:
		if (pd)
			break;
		/* fall through */
	default:
		Lf->inode = (unsigned long)is.i_ino;
		Lf->inp_ty = 1;
	}
/*
 * Determine the file size.
 */
	if (Foffset)
		Lf->off_def = 1;
	else {
		switch (Ntype) {
		case N_FIFO:
			if (is.i_pipe) {
				Lf->sz = (unsigned long)is.u.pipe_i.len;
				Lf->sz_def = 1;
			} else if (!Fsize)
				Lf->off_def = 1;
			break;
		default:
			if (type == S_IFCHR)
				Lf->off_def = 1;
			else {
				Lf->sz = (unsigned long)is.i_size;
				Lf->sz_def = 1;
			}
		}
	}
/*
 * Format the type name.
 */
	switch (type) {
	case S_IFBLK:
		tn = "BLK";
		break;
	case S_IFCHR:
		tn = "CHR";
		break;
	case S_IFDIR:
		tn = "DIR";
		break;
	case S_IFIFO:
		tn = "FIFO";
		break;
	case S_IFREG:
		tn = "REG";
		break;
	case S_IFLNK:
		tn = "LINK";
		break;
	default:
		if (type > 9999)
			(void) sprintf(Lf->type, "*%03d", type % 1000);
		else
			(void) sprintf(Lf->type, "%04d", type);
		(void) strcpy(Namech, "unknown inode type");
		tn = NULL;
	}
	if (tn)
		(void) strcpy(Lf->type, tn);
/*
 * Record an NFS file selection.
 */
	if (Ntype == N_NFS && Fnfs)
		Lf->sf |= SELNFS;
/*
 * Look up names.
 */
	switch (Ntype) {
	case N_FIFO:
		if (pd) {
			(void) strcpy(Lf->iproto, "PIPE");
			Lf->inp_ty = 2;
			(void) strcpy(Namech, pd);
			break;
		}
		/* fall through */
	case N_BLK:
	case N_NFS:
	case N_REGLR:
		if (Lf->dev_def) {
			for (mp = Mtab; mp; mp = mp->next) {
				if (dev == mp->dev) {
					Lf->fsdir = mp->dir;
					Lf->fsdev = mp->fsname;

#if	defined(HASFSINO)
					Lf->fs_ino = mp->inode;
#endif	/* defined(HASFSINO) */

					break;
				}
			}
		}
		break;
	}
/*
 * If this is a IFCHR file and it's missing an inode number, try to
 * supply one.
 */
	if (Lf->inp_ty == 0 && type == S_IFCHR && Lf->dev_def)
		find_ch_ino();
/*
 * Test for specified file.
 */
	if (Sfile && is_file_named(NULL, type))
		Lf->sf |= SELNM;
/*
 * Enter name characters.
 */
	if (Namech[0])
		enter_nm(Namech);
}
