/*
* dfile.c - Linux file processing functions for /proc-based lsof
*/
/*
* Copyright 1997 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 1997 Purdue Research Foundation.\nAll rights reserved.\n";
static char *rcsid = "$Id: dfile.c,v 1.8 2012/04/10 16:39:50 abe Exp abe $";
#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 *HbyFdi = /* hash by file (dev,ino) 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 SFDIHASH 4094 /* Sfile hash by (device,inode) number
* pair bucket count (power of 2!) */
#define SFFSHASH 1024 /* 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 mod
* (mod must be a 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) */
#define SFNMHASH 4096 /* Sfile hash by name bucket count
* (must be a power of 2!) */
/*
* hashSfile() - hash Sfile entries for use in is_file_named() searches
*/
void
hashSfile()
{
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, 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);
Exit(1);
}
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);
Exit(1);
}
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);
Exit(1);
}
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);
Exit(1);
}
hs++;
/*
* Scan the Sfile chain, building file, file system, raw device, and file
* name hash bucket chains.
*/
for (s = Sfile; s; s = s->next) {
for (i = 0; i < 3; i++) {
switch (i) {
case 0: /* hash by name */
if (!s->aname)
continue;
sh = &HbyNm[hashbyname(s->aname, SFNMHASH)];
HbyNmCt++;
break;
case 1: /* hash by device and inode, or file
* system device */
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++;
}
break;
case 2: /* hash by file's raw device */
if ((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;
}
/*
* Add hash to the bucket's chain, allocating new entries for
* all after the first.
*/
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);
Exit(1);
}
sn->s = s;
sn->next = sh->next;
sh->next = sn;
}
}
}
}
/*
* is_file_named() - is this file named?
*/
int
is_file_named(ty, p, mp, cd)
int ty; /* search type: 0 = only by device
* and inode
* 1 = by device and
* inode, or by file
* system device and
* path for NFS file
* systems
* 2 = only by path
*/
char *p; /* path name (device and inode are
* identified via *Lf) */
struct mounts *mp; /* NFS file system (NULL if not) */
int cd; /* character or block type file --
* VCHR or VBLK vnode, or S_IFCHR
* or S_IFBLK inode */
{
char *ep;
int f = 0;
struct mounts *smp;
struct sfile *s = (struct sfile *)NULL;
struct hsfile *sh;
size_t sz;
/*
* Check for a path name match, as requested.
*/
if ((ty == 2) && 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 file by device and inode number.
*/
if (!f && (ty < 2) && 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 && (ty == 1) && 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) && (s->dev == Lf->dev)) {
if (Lf->ntype != N_NFS) {
/*
* A non-NFS file matches to a non-NFS file system by
* device.
*/
if (!(smp = s->mp) || (smp->ty != N_NFS)) {
f = 1;
break;
}
} else {
/*
* An NFS file must also match to a file system by the
* the path name of the file system -- i.e., the first
* part of the file's path. This terrible, non-UNIX
* hack is forced on lsof by an egregious error in
* Linux NFS that can assign the same device number
* to two different NFS mounts.
*/
if (p && mp && mp->dirl && mp->dir && s->name
&& !strncmp(mp->dir, s->name, mp->dirl))
{
f = 1;
break;
}
}
}
}
}
/*
* Check for a character or block device match.
*/
if (!f && !ty && HbyFrdCt && cd
&& 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.
*/
switch (f) {
case 0:
return(0);
case 1:
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 (s->devnm) {
ep = endnm(&sz);
(void) snpf(ep, sz, " (%s)", s->devnm);
}
}
break;
case 2:
(void) strcpy(Namech, p);
break;
}
if (s)
s->f = 1;
return(1);
}
/*
* printdevname() - print character device name
*
* Note: this function should not be needed in /proc-based lsof, but
* since it is called by printname() in print.c, an ersatz one
* is provided here.
*/
int
printdevname(dev, rdev, f, nty)
dev_t *dev; /* device */
dev_t *rdev; /* raw device */
int f; /* 1 = follow with '\n' */
int nty; /* node type: N_BLK or N_chr */
{
char buf[128];
(void) snpf(buf, sizeof(buf), "%s device: %d,%d",
(nty == N_BLK) ? "BLK" : "CHR",
(int)GET_MAJ_DEV(*rdev), (int)GET_MIN_DEV(*rdev));
safestrprt(buf, stdout, f);
return(1);
}