| /* |
| * 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"; |
| static char *rcsid = "$Id: dfile.c,v 1.21 2009/03/25 19:22:16 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 *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); |
| Exit(1); |
| } |
| } |
| 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, 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); |
| Exit(1); |
| } |
| 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) */ |