diff options
Diffstat (limited to 'lib/rnch.c')
-rw-r--r-- | lib/rnch.c | 811 |
1 files changed, 811 insertions, 0 deletions
diff --git a/lib/rnch.c b/lib/rnch.c new file mode 100644 index 0000000..0bdb7db --- /dev/null +++ b/lib/rnch.c @@ -0,0 +1,811 @@ +/* + * rnch.c -- Sun format name cache functions for lsof library + */ + + +/* + * 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. + */ + + +#include "../machine.h" + +#if defined(HASNCACHE) && defined(USE_LIB_RNCH) + +# if !defined(lint) +static char copyright[] = +"@(#) Copyright 1997 Purdue Research Foundation.\nAll rights reserved.\n"; +static char *rcsid = "$Id: rnch.c,v 1.11 2008/10/21 16:13:23 abe Exp $"; +# endif /* !defined(lint) */ + +#include "../lsof.h" + + +/* + * rnch.c - read Sun format (struct ncache) name cache + * + * This code is effective only when HASNCACHE is defined. + */ + +/* + * The caller must: + * + * #include the relevant header file -- e.g., <sys/dnlc.h>. + * + * Define X_NCSIZE as the nickname for the kernel cache size variable, + * or, if X_NCSIZE is undefined, define FIXED_NCSIZE as the size of the + * kernel cache. + * + * Define X_NCACHE as the nickname for the kernel cache address and + * define ADDR_NCACHE if the address is the address of the cache, + * rather than the address of a pointer to it. + * + * Define NCACHE_NXT if the kernel's name cache is a linked list, starting + * at the X_NCACHE address, rather than a table, starting at that address. + * + * Define any of the following casts that differ from their defaults: + * + * NCACHE_SZ_CAST cast for X_NCACHE (default int) + * + * The caller may: + * + * Define NCACHE_DP as the name of the element in the + * ncache structure that contains the + * parent vnode pointer. + * + * Default: dp + * + * Define NCACHE_NAME as the name of the element in the + * ncache structure that contains the + * name. + * + * Default: name + * + * Define NCACHE_NAMLEN as the name of the element in the + * ncache structure that contains the + * name length. + * + * Deafult: namlen + * + * Define NCACHE_NEGVN as the name of the name list element + * whose value is a vnode address to + * ignore when loading the kernel name + * cache. + * + * Define NCACHE_NODEID as the name of the element in the + * ncache structure that contains the + * vnode's capability ID. + * + * Define NCACHE_PARID as the name of the element in the + * ncache structure that contains the + * parent vnode's capability ID. + * + * Define NCACHE_VP as the name of the element in the + * ncache structure that contains the + * vnode pointer. + * + * Default: vp + * + * Note: if NCACHE_NODEID is defined, then NCACHE_PARID must be defined. + * + * + * The caller must: + * + * Define this prototype for ncache_load(): + * + * _PROTOTYPE(void ncache_load,(void)); + */ + + +/* + * Local static values + */ + +static int Mch; /* name cache hash mask */ + +# if !defined(NCACHE_NC_CAST) +#define NCACHE_SZ_CAST int +# endif /* !defined(NCACHE_NC_CAST) */ + +static NCACHE_SZ_CAST Nc = 0; /* size of name cache */ +static int Nch = 0; /* size of name cache hash pointer + * table */ +struct l_nch { + KA_T vp; /* vnode address */ + KA_T dp; /* parent vnode address */ + struct l_nch *pa; /* parent Ncache address */ + +# if defined(NCACHE_NODEID) + unsigned long id; /* node's capability ID */ + unsigned long did; /* parent node's capability ID */ +# endif /* defined(NCACHE_NODEID) */ + + char *nm; /* name */ + int nl; /* name length */ +}; + +static struct l_nch *Ncache = (struct l_nch *)NULL; + /* the local name cache */ +static struct l_nch **Nchash = (struct l_nch **)NULL; + /* Ncache hash pointers */ +static int Ncfirst = 1; /* first-call status */ + +# if defined(NCACHE_NEGVN) +static KA_T NegVN = (KA_T)NULL; /* negative vnode address */ +static int NegVNSt = 0; /* NegVN status: 0 = not loaded */ +# endif /* defined(NCACHE_NEGVN) */ + +# if defined(NCACHE_NODEID) +_PROTOTYPE(static struct l_nch *ncache_addr,(unsigned long i, KA_T v)); +#define ncachehash(i,v) Nchash+(((((int)(v)>>2)+((int)(i)))*31415)&Mch) +# else /* !defined(NCACHE_NODEID) */ +_PROTOTYPE(static struct l_nch *ncache_addr,(KA_T v)); +#define ncachehash(v) Nchash+((((int)(v)>>2)*31415)&Mch) +# endif /* defined(NCACHE_NODEID) */ + +_PROTOTYPE(static int ncache_isroot,(KA_T va, char *cp)); + +#define DEFNCACHESZ 1024 /* local size if X_NCSIZE kernel value < 1 */ +#define LNCHINCRSZ 64 /* local size increment */ + +# if !defined(NCACHE_DP) +#define NCACHE_DP dp +# endif /* !defined(NCACHE_DP) */ + +# if !defined(NCACHE_NAME) +#define NCACHE_NAME name +# endif /* !defined(NCACHE_NAME) */ + +# if !defined(NCACHE_NAMLEN) +#define NCACHE_NAMLEN namlen +# endif /* !defined(NCACHE_NAMLEN) */ + +# if !defined(NCACHE_VP) +#define NCACHE_VP vp +# endif /* !defined(NCACHE_VP) */ + + +/* + * ncache_addr() - look up a node's local ncache address + */ + +static struct l_nch * + +# if defined(NCACHE_NODEID) +ncache_addr(i, v) +# else /* !defined(NCACHE_NODEID) */ +ncache_addr(v) +# endif /* defined(NCACHE_NODEID) */ + +# if defined(NCACHE_NODEID) + unsigned long i; /* capability ID */ +# endif /* defined(NCACHE_NODEID) */ + + KA_T v; /* vnode's address */ +{ + struct l_nch **hp; + +# if defined(NCACHE_NODEID) + for (hp = ncachehash(i, v); *hp; hp++) +# else /* !defined(NCACHE_NODEID) */ + for (hp = ncachehash(v); *hp; hp++) +# endif /* defined(NCACHE_NODEID) */ + + { + +# if defined(NCACHE_NODEID) + if ((*hp)->vp == v && (*hp)->id == i) +# else /* !defined(NCACHE_NODEID) */ + if ((*hp)->vp == v) +# endif /* defined(NCACHE_NODEID) */ + + return(*hp); + } + return((struct l_nch *)NULL); +} + + +/* + * ncache_isroot() - is head of name cache path a file system root? + */ + +static int +ncache_isroot(va, cp) + KA_T va; /* kernel vnode address */ + char *cp; /* partial path */ +{ + char buf[MAXPATHLEN]; + int i; + MALLOC_S len; + struct mounts *mtp; + struct stat sb; + struct vnode v; + static int vca = 0; + static int vcn = 0; + static KA_T *vc = (KA_T *)NULL; + + if (!va) + return(0); +/* + * Search the root vnode cache. + */ + for (i = 0; i < vcn; i++) { + if (va == vc[i]) + return(1); + } +/* + * Read the vnode and see if it's a VDIR node with the VROOT flag set. If + * it is, then the path is complete. + * + * If it isn't, and if the file has an inode number, search the mount table + * and see if the file system's inode number is known. If it is, form the + * possible full path, safely stat() it, and see if it's inode number matches + * the one we have for this file. If it does, then the path is complete. + */ + if (kread((KA_T)va, (char *)&v, sizeof(v)) + || v.v_type != VDIR || !(v.v_flag & VROOT)) { + + /* + * The vnode tests failed. Try the inode tests. + */ + if (Lf->inp_ty != 1 || !Lf->inode + || !Lf->fsdir || (len = strlen(Lf->fsdir)) < 1) + return(0); + if ((len + 1 + strlen(cp) + 1) > sizeof(buf)) + return(0); + for (mtp = readmnt(); mtp; mtp = mtp->next) { + if (!mtp->dir || !mtp->inode) + continue; + if (strcmp(Lf->fsdir, mtp->dir) == 0) + break; + } + if (!mtp) + return(0); + (void) strcpy(buf, Lf->fsdir); + if (buf[len - 1] != '/') + buf[len++] = '/'; + (void) strcpy(&buf[len], cp); + if (statsafely(buf, &sb) != 0 + || (unsigned long)sb.st_ino != Lf->inode) + return(0); + } +/* + * Add the vnode address to the root vnode cache. + */ + if (vcn >= vca) { + vca += 10; + len = (MALLOC_S)(vca * sizeof(KA_T)); + if (!vc) + vc = (KA_T *)malloc(len); + else + vc = (KA_T *)realloc(vc, len); + if (!vc) { + (void) fprintf(stderr, "%s: no space for root vnode table\n", + Pn); + Exit(1); + } + } + vc[vcn++] = va; + return(1); +} + + +/* + * ncache_load() - load the kernel's name cache + */ + +void +ncache_load() +{ + char *cp, *np; + struct l_nch **hp, *lc; + int i, len, n; + static int iNc = 0; + struct ncache *kc; + static KA_T kp = (KA_T)NULL; + KA_T v; + +# if defined(HASDNLCPTR) + static int na = 0; + static char *nb = (char *)NULL; +# endif /* defined(HASDNLCPTR) */ + +# if defined(NCACHE_NXT) + static KA_T kf; + struct ncache nc; +# else /* !defined(NCACHE_NXT) */ + static struct ncache *kca = (struct ncache *)NULL; +# endif /* defined(NCACHE_NXT) */ + + if (!Fncache) + return; + if (Ncfirst) { + + /* + * Do startup (first-time) functions. + */ + Ncfirst = 0; + /* + * Establish kernel cache size. + */ + +# if defined(X_NCSIZE) + v = (KA_T)0; + if (get_Nl_value(X_NCSIZE, (struct drive_Nl *)NULL, &v) < 0 + || !v + || kread((KA_T)v, (char *)&Nc, sizeof(Nc))) + { + if (!Fwarn) + (void) fprintf(stderr, + "%s: WARNING: can't read name cache size: %s\n", + Pn, print_kptr(v, (char *)NULL, 0)); + iNc = Nc = 0; + return; + } + iNc = Nc; +# else /* !defined(X_NCSIZE) */ + iNc = Nc = FIXED_NCSIZE; +# endif /* defined(X_NCSIZE) */ + + if (Nc < 1) { + if (!Fwarn) { + (void) fprintf(stderr, + "%s: WARNING: kernel name cache size: %d\n", Pn, Nc); + (void) fprintf(stderr, + " Cache size assumed to be: %d\n", DEFNCACHESZ); + } + iNc = Nc = DEFNCACHESZ; + } + +# if defined(NCACHE_NEGVN) + /* + * Get negative vnode address. + */ + if (!NegVNSt) { + if (get_Nl_value(NCACHE_NEGVN, (struct drive_Nl *)NULL, &NegVN) + < 0) + NegVN = (KA_T)NULL; + NegVNSt = 1; + } +# endif /* defined(NCACHE_NEGVN) */ + + /* + * Establish kernel cache address. + */ + +# if defined(ADDR_NCACHE) + kp = (KA_T)0; + if (get_Nl_value(X_NCACHE,(struct drive_Nl *)NULL,(KA_T *)&kp) < 0 + || !kp) { + if (!Fwarn) + (void) fprintf(stderr, + "%s: WARNING: no name cache address\n", Pn); + iNc = Nc = 0; + return; + } +# else /* !defined(ADDR_NCACHE) */ + v = (KA_T)0; + if (get_Nl_value(X_NCACHE, (struct drive_Nl *)NULL, &v) < 0 + || !v + || kread((KA_T)v, (char *)&kp, sizeof(kp))) { + if (!Fwarn) + (void) fprintf(stderr, + "%s: WARNING: can't read name cache ptr: %s\n", + Pn, print_kptr(v, (char *)NULL, 0)); + iNc = Nc = 0; + return; + } +# endif /* defined(ADDR_NCACHE) */ + + /* + * Allocate space for a local copy of the kernel's cache. + */ + +# if !defined(NCACHE_NXT) + len = Nc * sizeof(struct ncache); + if (!(kca = (struct ncache *)malloc((MALLOC_S)len))) { + if (!Fwarn) + (void) fprintf(stderr, + "%s: can't allocate name cache space: %d\n", Pn, len); + Exit(1); + } +# endif /* !defined(NCACHE_NXT) */ + + /* + * Allocate space for the local cache. + */ + len = Nc * sizeof(struct l_nch); + if (!(Ncache = (struct l_nch *)calloc(Nc, sizeof(struct l_nch)))) { + +no_local_space: + + if (!Fwarn) + (void) fprintf(stderr, + "%s: no space for %d byte local name cache\n", Pn, len); + Exit(1); + } + } else { + + /* + * Do setup for repeat calls. + */ + if (!iNc) + return; + if (Nchash) { + (void) free((FREE_P *)Nchash); + Nchash = (struct l_nch **)NULL; + } + if (Ncache) { + + /* + * Free space malloc'd to names in local name cache. + */ + for (i = 0, lc = Ncache; i < Nc; i++, lc++) { + if (lc->nm) { + (void) free((FREE_P *)lc->nm); + lc->nm = (char *)NULL; + } + } + } + Nc = iNc; + +# if defined(NCACHE_NXT) + kp = kf; +# endif /* defined(NCACHE_NXT) */ + + } + +# if !defined(NCACHE_NXT) + +/* + * Read the kernel's name cache. + */ + if (kread(kp, (char *)kca, (Nc * sizeof(struct ncache)))) { + if (!Fwarn) + (void) fprintf(stderr, + "%s: WARNING: can't read kernel's name cache: %s\n", + Pn, print_kptr(kp, (char *)NULL, 0)); + Nc = 0; + return; + } +# endif /* !defined(NCACHE_NXT) */ + +/* + * Build a local copy of the kernel name cache. + */ + +# if defined(NCACHE_NXT) + for (i = iNc * 16, kc = &nc, kf = kp, lc = Ncache, n = 0; kp; ) +# else /* !defined(NCACHE_NXT) */ + for (i = n = 0, kc = kca, lc = Ncache; i < Nc; i++, kc++) +# endif /* defined(NCACHE_NXT) */ + + { + +# if defined(NCACHE_NXT) + if (kread(kp, (char *)kc, sizeof(nc))) + break; + if ((kp = (KA_T)kc->NCACHE_NXT) == kf) + kp = (KA_T)NULL; +# endif /* defined(NCACHE_NXT) */ + + if (!kc->NCACHE_VP || (len = kc->NCACHE_NAMLEN) < 1) + continue; + +# if defined(NCACHE_NEGVN) + if (NegVN && ((KA_T)kc->NCACHE_VP == NegVN)) + continue; +# endif /* defined(NCACHE_NEGVN) */ + +# if defined(HASDNLCPTR) + /* + * Read name from kernel to a temporary buffer. + */ + if (len > na) { + na = len; + if (!nb) + nb = (char *)malloc(na); + else + nb = (char *)realloc((MALLOC_P *)nb, na); + if (!nb) { + (void) fprintf(stderr, + "%s: can't allocate %d byte temporary name buffer\n", + Pn, na); + Exit(1); + } + } + if (!kc->NCACHE_NAME || kread((KA_T)kc->NCACHE_NAME, nb, len)) + continue; + np = nb; +# else /* !defined(HASDNLCPTR) */ + /* + * Use name that is in the kernel cache entry. + */ + if (len > NC_NAMLEN) + continue; + np = kc->NCACHE_NAME; +# endif /* defined(HASDNLCPTR) */ + + if (len < 3 && *np == '.') { + if (len == 1 || (len == 2 && np[1] == '.')) + continue; + } + /* + * Allocate space for name in local cache entry. + */ + if (!(cp = (char *)malloc(len + 1))) { + (void) fprintf(stderr, + "%s: can't allocate %d bytes for name cache name: %s\n", + Pn, len + 1, np); + Exit(1); + } + (void) strncpy(cp, np, len); + cp[len] = '\0'; + +# if defined(NCACHE_NXT) + if (n >= Nc) { + + /* + * Allocate more local space to receive the kernel's linked + * entries. + */ + Nc += LNCHINCRSZ; + if (!(Ncache = (struct l_nch *)realloc(Ncache, + (MALLOC_S)(Nc * sizeof(struct l_nch))))) + { + (void) fprintf(stderr, + "%s: no more space for %d entry local name cache\n", + Pn, Nc); + Exit(1); + } + lc = &Ncache[n]; + iNc = Nc; + } +# endif /* defined(NCACHE_NXT) */ + + /* + * Complete the local cache entry. + */ + lc->vp = (KA_T)kc->NCACHE_VP; + lc->dp = (KA_T)kc->NCACHE_DP; + lc->pa = (struct l_nch *)NULL; + lc->nm = cp; + lc->nl = len; + +# if defined(NCACHE_NODEID) + lc->id = (unsigned long)kc->NCACHE_NODEID; + lc->did = (unsigned long)kc->NCACHE_PARID; +# endif /* defined(NCACHE_NODEID) */ + + n++; + lc++; + +# if defined(NCACHE_NXT) + if (n >= i) { + if (!Fwarn) + (void) fprintf(stderr, + "%s: WARNING: name cache truncated at %d entries\n", + Pn, n); + break; + } +# endif /* defined(NCACHE_NXT) */ + + } +/* + * Reduce memory usage, as required. + */ + +# if !defined(NCACHE_NXT) + if (!RptTm) + (void) free((FREE_P *)kca); +# endif /* !defined(NCACHE_NXT) */ + + if (n < 1) { + if (!RptTm && Ncache) { + + /* + * If not in repeat mode, free the space that has been malloc'd + * to the local name cache. + */ + for (i = 0, lc = Ncache; i < Nc; i++, lc++) { + if (lc->nm) { + (void) free((FREE_P *)lc->nm); + lc->nm = (char *)NULL; + } + } + (void) free((FREE_P *)Ncache); + Ncache = (struct l_nch *)NULL; + Nc = 0; + } + if (!Fwarn) + (void) fprintf(stderr, + "%s: WARNING: unusable name cache size: %d\n", Pn, n); + return; + } + if (n < Nc) { + Nc = n; + if (!RptTm) { + len = Nc * sizeof(struct l_nch); + if (!(Ncache = (struct l_nch *)realloc(Ncache, len))) + goto no_local_space; + } + } +/* + * Build a hash table to locate Ncache entries. + */ + for (Nch = 1; Nch < Nc; Nch <<= 1) + ; + Nch <<= 1; + Mch = Nch - 1; + if (!(Nchash = (struct l_nch **)calloc(Nch+Nc, sizeof(struct l_nch *)))) + { + if (!Fwarn) + (void) fprintf(stderr, + "%s: no space for %d name cache hash pointers\n", + Pn, Nch + Nc); + Exit(1); + } + for (i = 0, lc = Ncache; i < Nc; i++, lc++) { + +# if defined(NCACHE_NODEID) + for (hp = ncachehash(lc->id, lc->vp), n = 1; *hp; hp++) +# else /* !defined(NCACHE_NODEID) */ + for (hp = ncachehash(lc->vp), n = 1; *hp; hp++) +# endif /* defined(NCACHE_NODEID) */ + + { + if ((*hp)->vp == lc->vp && strcmp((*hp)->nm, lc->nm) == 0 + && (*hp)->dp == lc->dp + +# if defined(NCACHE_NODEID) + && (*hp)->id == lc->id && (*hp)->did == lc->did +# endif /* defined(NCACHE_NODEID) */ + + ) { + n = 0; + break; + } + } + if (n) + *hp = lc; + } +/* + * Make a final pass through the local cache and convert parent vnode + * addresses to local name cache pointers. + */ + for (i = 0, lc = Ncache; i < Nc; i++, lc++) { + if (!lc->dp) + continue; + +# if defined(NCACHE_NEGVN) + if (NegVN && (lc->dp == NegVN)) { + lc->pa = (struct l_nch *)NULL; + continue; + } +# endif /* defined(NCACHE_NEGVN) */ + +# if defined(NCACHE_NODEID) + lc->pa = ncache_addr(lc->did, lc->dp); +# else /* !defined(NCACHE_NODEID) */ + lc->pa = ncache_addr(lc->dp); +# endif /* defined(NCACHE_NODEID) */ + + } +} + + +/* + * ncache_lookup() - look up a node's name in the kernel's name cache + */ + +char * +ncache_lookup(buf, blen, fp) + char *buf; /* receiving name buffer */ + int blen; /* receiving buffer length */ + int *fp; /* full path reply */ +{ + char *cp = buf; + struct l_nch *lc; + struct mounts *mtp; + int nl, rlen; + + *cp = '\0'; + *fp = 0; + +# if defined(HASFSINO) +/* + * If the entry has an inode number that matches the inode number of the + * file system mount point, return an empty path reply. That tells the + * caller to print the file system mount point name only. + */ + if ((Lf->inp_ty == 1) && Lf->fs_ino && (Lf->inode == Lf->fs_ino)) + return(cp); +# endif /* defined(HASFSINO) */ + +/* + * Look up the name cache entry for the node address. + */ + if (!Nc + +# if defined(NCACHE_NODEID) + || !(lc = ncache_addr(Lf->id, Lf->na)) +# else /* !defined(NCACHE_NODEID) */ + || !(lc = ncache_addr(Lf->na)) +# endif /* defined(NCACHE_NODEID) */ + + ) { + + /* + * If the node has no cache entry, see if it's the mount + * point of a known file system. + */ + if (!Lf->fsdir || !Lf->dev_def || Lf->inp_ty != 1) + return((char *)NULL); + for (mtp = readmnt(); mtp; mtp = mtp->next) { + if (!mtp->dir || !mtp->inode) + continue; + if (Lf->dev == mtp->dev + && mtp->inode == Lf->inode + && strcmp(mtp->dir, Lf->fsdir) == 0) + return(cp); + } + return((char *)NULL); + } +/* + * Begin the path assembly. + */ + if ((nl = lc->nl) > (blen - 1)) + return((char *)NULL); + cp = buf + blen - nl - 1; + rlen = blen - nl - 1; + (void) strcpy(cp, lc->nm); +/* + * Look up the name cache entries that are parents of the node address. + * Quit when: + * + * there's no parent; + * the name is too large to fit in the receiving buffer. + */ + for (;;) { + if (!lc->pa) { + if (ncache_isroot(lc->dp, cp)) + *fp = 1; + break; + } + lc = lc->pa; + if (((nl = lc->nl) + 1) > rlen) + break; + *(cp - 1) = '/'; + cp--; + rlen--; + (void) strncpy((cp - nl), lc->nm, nl); + cp -= nl; + rlen -= nl; + } + return(cp); +} +#else /* !defined(HASNCACHE) || !defined(USE_LIB_RNCH) */ +char rnch_d1[] = "d"; char *rnch_d2 = rnch_d1; +#endif /* defined(HASNCACHE) && defined(USE_LIB_RNCH) */ |