blob: 74b30c8c2cd88b37d1226b7eea3cbfb2776d99ab [file] [log] [blame]
/*
* dfile.c - AIX 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";
static char *rcsid = "$Id: dfile.c,v 1.13 2005/08/08 19:46:38 abe Exp $";
#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 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 *HbyMPC = /* hash by MPC file buckets */
(struct hsfile *)NULL;
static int HbyMPCCt = 0; /* HbyMPC 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 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 SFMPCHASH 1024 /* Sfile hash by MPC device number */
#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) */
/*
* 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, MPC 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 (!(HbyMPC = (struct hsfile *)calloc((MALLOC_S)SFMPCHASH,
sizeof(struct hsfile))))
{
(void) fprintf(stderr,
"%s: can't allocate space for %d MPC file hash buckets\n",
Pn, SFMPCHASH);
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, MPC file, 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->ch < 0) && (s->mode == S_IFCHR))
{
sh = &HbyMPC[SFHASHDEVINO(GET_MAJ_DEV(s->dev),
GET_MIN_DEV(s->dev),
0,
SFMPCHASH)];
HbyMPCCt++;
} else
continue;
} else if (i == 3) {
if (s->type
&& (((s->mode == S_IFCHR) && (s->ch < 0))
|| ((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;
}
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 file named?
*/
int
is_file_named(p, ty, ch, ic)
char *p; /* path name; NULL = search by device
* and inode (from *Lf) */
enum vtype ty; /* vnode type */
chan_t ch; /* gnode channel */
int ic; /* is clone file (4.1.4 and above) */
{
int dmaj, dmin, maj, min, rmaj, rmin;
static int dsplit = 0;
char *ep;
int f = 0;
struct sfile *s;
struct hsfile *sh;
size_t sz;
/*
* Split the device numbers into their major and minor numbers.
*
* THis is necessitated by 64 bit AIX architectures, which store two different
* types of device numbers in 64 bit dev_t's. The two types can't be compared
* directly, but must be compared by extracting their major and minor numbers
* and comparing them.
*/
readdev(0);
if (!dsplit) {
dmaj = GET_MAJ_DEV(DevDev);
dmin = GET_MIN_DEV(DevDev);
dsplit = 1;
}
if (Lf->dev_def) {
maj = GET_MAJ_DEV(Lf->dev);
min = GET_MIN_DEV(Lf->dev);
}
if (Lf->rdev_def) {
rmaj = GET_MAJ_DEV(Lf->rdev);
rmin = GET_MIN_DEV(Lf->rdev);
}
#if AIXV>=4140
/*
* Check for a clone match.
*/
if (ic
&& HbyFdiCt
&& CloneMaj >= 0
&& (Lf->dev_def && (maj = dmaj) && (min == dmin))
&& Lf->rdev_def
&& (Lf->inp_ty == 1 || Lf->inp_ty == 3))
{
for (sh=&HbyFdi[SFHASHDEVINO(CloneMaj, rmaj, Lf->inode, SFDIHASH)];
sh;
sh = sh->next)
{
if ((s = sh->s)
&& (GET_MAJ_DEV(s->rdev) == CloneMaj)
&& (GET_MIN_DEV(s->rdev) == rmaj)
&& (s->i == Lf->inode))
{
f = 3;
break;
}
}
}
#endif /* AIXV>=4140 */
/*
* Check for a path name match, as requested.
*/
if (!f && 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 AIX multiplexed file, matching the channel if
* it was supplied by the caller.
*/
if (!f && HbyMPCCt && ty == VMPC
&& (Lf->dev_def && (maj == dmaj) && (min == dmin))
&& Lf->rdev_def)
{
for (sh = &HbyMPC[SFHASHDEVINO(rmaj, rmin, 0, SFMPCHASH)];
sh;
sh = sh->next)
{
if ((s = sh->s)
&& (GET_MAJ_DEV(s->dev) == rmaj)
&& (GET_MIN_DEV(s->dev) == rmin)
&& (s->ch < 0 || ch == s->ch)) {
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(maj, min, Lf->inode, SFDIHASH)];
sh;
sh = sh->next)
{
if ((s = sh->s)
&& (maj == GET_MAJ_DEV(s->dev))
&& (min == GET_MIN_DEV(s->dev))
&& (Lf->inode == s->i))
{
f = 1;
break;
}
}
}
/*
* Check for a file system.
*/
if (!f && HbyFsdCt && Lf->dev_def) {
for (sh = &HbyFsd[SFHASHDEVINO(maj, min, 0, SFFSHASH)];
sh;
sh = sh->next)
{
if ((s = sh->s)
&& (maj == GET_MAJ_DEV(s->dev))
&& (min == GET_MIN_DEV(s->dev))
) {
f = 1;
break;
}
}
}
/*
* Check for a character or block device file.
*/
if (!f && HbyFrdCt
&& ((ty == VCHR) || (ty == VBLK))
&& (Lf->dev_def && (maj == dmaj) && (min == dmin))
&& Lf->rdev_def
&& (Lf->inp_ty == 1 || Lf->inp_ty == 3))
{
for (sh = &HbyFrd[SFHASHRDEVI(maj, min, rmaj, rmin,
Lf->inode, SFRDHASH)];
sh;
sh = sh->next)
{
if ((s = sh->s)
&& (GET_MAJ_DEV(s->rdev) == rmaj)
&& (GET_MIN_DEV(s->rdev) == rmin)
&& (((ty == VCHR) && (s->mode == S_IFCHR) && (s->ch < 0))
|| ((ty == VBLK) && (s->mode == S_IFBLK))))
{
f = 1;
break;
}
}
}
/*
* Convert the name if a match occurred.
*/
if (f) {
if (f == 2) {
(void) snpf(Namech, Namechl, "%s", p);
#if AIXV>=4140
} else if (f == 3 && ClonePtc >= 0 && (maj == ClonePtc)) {
(void) snpf(Namech, Namechl, "%s/%d", s->name, min);
#endif /* AIXV>=4140 */
} else 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 (ty == VMPC && s->ch < 0) {
ep = endnm(&sz);
(void) snpf(ep, sz, "/%d", ch);
}
if (s->devnm) {
ep = endnm(&sz);
(void) snpf(ep, sz, " (%s)", s->devnm);
}
}
s->f = 1;
return(1);
}
return(0);
}
/*
* print_dev() - print device
*/
char *
print_dev(lf, dev)
struct lfile *lf; /* file whose device to be printed */
dev_t *dev; /* pointer to device to be printed */
{
static char buf[128];
int maj = GET_MAJ_DEV(*dev);
int min = GET_MIN_DEV(*dev);
#if AIXV>=3200
if (*dev & SDEV_REMOTE) {
(void) snpf(buf, sizeof(buf), "NFS,%d", (min & ~SDEV_REMOTE));
return(buf);
}
#endif /* AIXV>=3200 */
(void) snpf(buf, sizeof(buf), "%d,%d", maj, min);
return(buf);
}
/*
* readvfs() - read vfs structure
*/
struct l_vfs *
readvfs(vn)
struct vnode *vn; /* vnode */
{
struct gfs g;
void *mp;
char *s1, *s2;
uint ul;
struct vfs v;
struct vmount *vm;
struct l_vfs *vp;
if (!vn->v_vfsp)
return((struct l_vfs *)NULL);
for (vp = Lvfs; vp; vp = vp->next) {
if ((KA_T)vn->v_vfsp == vp->addr)
return(vp);
}
if (!(vp = (struct l_vfs *)malloc((MALLOC_S)sizeof(struct l_vfs)))) {
(void) fprintf(stderr, "%s: PID %d, no space for vfs\n",
Pn, Lp->pid);
Exit(1);
}
vp->dir = (char *)NULL;
vp->fsname = (char *)NULL;
/*
* Read the vfs structure.
*/
if (kread((KA_T)vn->v_vfsp, (char *)&v, sizeof(v))) {
vfs_exit:
(void) free((FREE_P *)vp);
return((struct l_vfs *)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)((char *)v.vfs_mdata
+ offsetof(struct vmount, vmt_length)),
(char *)&ul, sizeof(ul)))
goto vfs_exit;
if (!(mp = (void *)malloc((MALLOC_S)ul))) {
(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)
# if defined(HAS_SANFS) && defined(MNT_SANFS)
&& (vp->vmt_gfstype != MNT_SANFS)
# endif /* defined(HAS_SANFS) && defined(MNT_SANFS) */
) {
vp->dev = 0x80000000 | vm->vmt_vfsnumber;
# if AIXA>=1
vp->dev |= 0x8000000000000000;
# endif /* AIXA>=1 */
} else
#endif /* AIXV>=3200 */
#if defined(HAS_AFS)
if (vm->vmt_gfstype == MNT_AFS)
vp->dev = AFSDEV;
else
#endif /* defined(HAS_AFS) */
#if AIXA>1
if (vm->vmt_gfstype == MNT_PROCFS) {
/*
* !!!DEBUG!!! !!!DEBUG!!! !!!DEBUG!!! !!!DEBUG!!! !!!DEBUG!!!
*
* The following *hack* is required to make the vmount structure's
* device number match what stat(2) errnoneously returns in ia64
* AIX >= 5.
*
* REMOVE THIS CODE WHEN STAT(2) IS FIXED!!!
*/
vp->dev = (dev_t)(vm->vmt_fsid.fsid_dev & 0x7fffffffffffffff);
/*
* !!!DEBUG!!! !!!DEBUG!!! !!!DEBUG!!! !!!DEBUG!!! !!!DEBUG!!!
*/
}
else
#endif /* AIXA>1 */
vp->dev = (dev_t)vm->vmt_fsid.fsid_dev;
if ((s1 = vmt2dataptr(vm, VMT_STUB))) {
if (!(vp->dir = mkstrcpy(s1, (MALLOC_S *)NULL))) {
readvfs_aix1:
(void) fprintf(stderr, "%s: PID %d, readvfs, no space\n",
Pn, Lp->pid);
Exit(1);
}
} else
vp->dir = (char *)NULL;
s1 = vmt2dataptr(vm, VMT_HOST);
if (!(s2 = vmt2dataptr(vm, VMT_OBJECT)) || *s1 == '\0')
s2 = g.gfs_name;
if (!s1 && !s2)
vp->fsname = (char *)NULL;
else {
if (vm->vmt_flags & MNT_REMOTE) {
if (!(vp->fsname = mkstrcat(s1 ? s1 : "",
-1,
(s1 && *s1) ? ":" : "",
-1, s2, -1,
(MALLOC_S *)NULL)))
goto readvfs_aix1;
} else {
if (!(vp->fsname = mkstrcpy(s2, (MALLOC_S *)NULL)))
goto readvfs_aix1;
}
}
(void) free((FREE_P *)mp);
vp->next = Lvfs;
vp->addr = (KA_T)vn->v_vfsp;
#if defined(HAS_AFS)
if (!AFSVfsp && vm->vmt_gfstype == MNT_AFS)
AFSVfsp = (KA_T)vn->v_vfsp;
#endif /* defined(HAS_AFS) */
Lvfs = vp;
return(vp);
}