/* * rnmh.c -- functions to read BSD format name cache information from a * kernel hash table */ /* * 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_RNMH) # if !defined(lint) static char copyright[] = "@(#) Copyright 1997 Purdue Research Foundation.\nAll rights reserved.\n"; static char *rcsid = "$Id: rnmh.c,v 1.13 2008/10/21 16:13:23 abe Exp $"; # endif /* !defined(lint) */ #include "../lsof.h" /* * rnmh.c - read BSD format hashed kernel name cache */ /* * The caller must: * * #include the relevant header file -- e.g., . * * Define X_NCACHE as the nickname for the kernel cache hash tables * address. * * Define X_NCSIZE as the nickname for the size of the kernel cache has * table length. * * Define NCACHE_NO_ROOT if the calling dialect doesn't support * the locating of the root node of a file system. * * Define the name of the name cache structure -- e.g., * * #define NCACHE * * * Define the following casts, if they differ from the defaults: * * NCACHE_SZ_CAST case for X_NCSIZE (default unsigned long) * * Define the names of these elements of struct NCACHE: * * #define NCACHE_NM * #define NCACHE_NXT * #define NCACHE_NODEADDR * #define NCACHE_PARADDR * * Optionally define: * * #define NCACHE_NMLEN * * Optionally define *both*: * * #define NCACHE_NODEID * #define NCACHE_PARID * * The caller may need to: * * Define this prototype for ncache_load(): * * _PROTOTYPE(static void ncache_load,(void)); * * Define NCACHE_VROOT to be the value of the flag that signifies that * the vnode is the root of its file system. * * E.g., for BSDI >= 5: * * #define NCACHE_VROOT VV_ROOT * * If not defined, NCACHE_VROOT is defined as "VROOT". * * Define VNODE_VFLAG if the vnode's flag member's name isn't v_flag. * * Note: if NCHNAMLEN is defined, the name is assumed to be in * NCACHE_NM[NCHNAMLEN]; if it isn't defined, the name is assumed to be in an * extension that begins at NCACHE_NM[0]. * * Note: if NCACHE_NMLEN is not defined, then NCACHE_NM must be a pointer to * a kernel allocated, NUL-terminated, string buffer. */ /* * Casts */ # if !defined(NCACHE_NC_CAST) #define NCACHE_SZ_CAST unsigned long # endif /* !defined(NCACHE_NC_CAST) */ /* * Flags */ # if !defined(NCACHE_NMLEN) #undef NCHNAMLEN # endif /* !defined(NCACHE_NMLEN) */ # if !defined(NCACHE_VROOT) #define NCACHE_VROOT VROOT /* vnode is root of its file system */ # endif /* !defined(NCACHE_VROOT) */ # if !defined(VNODE_VFLAG) #define VNODE_VFLAG v_flag # endif /* !defined(VNODE_VFLAG) */ /* * Local static values */ static int Mch; /* name cache hash mask */ struct l_nch { KA_T na; /* node address */ KA_T pa; /* parent node address */ struct l_nch *pla; /* parent local node address */ int nl; /* name length */ struct l_nch *next; /* next entry */ # if defined(NCACHE_NODEID) unsigned long id; /* capability ID */ unsigned long did; /* parent capability ID */ # endif /* defined(NCACHE_NODEID) */ # if defined(NCHNAMLEN) char nm[NCHNAMLEN + 1]; /* name */ # else /* !defined(NCHNAMLEN) */ char nm[1]; /* variable length name */ # endif /* defined(NCHNAMLEN) */ }; static struct l_nch *Ncache = (struct l_nch *)NULL; /* the head of the local name cache */ static struct l_nch **Nchash = (struct l_nch **)NULL; /* Ncache hash pointers */ # if defined(NCACHE_NODEID) #define ncachehash(i,n) Nchash+(((((int)(n)>>2)+((int)(i)))*31415)&Mch) _PROTOTYPE(static struct l_nch *ncache_addr,(unsigned long i, KA_T na)); # else /* !defined(NCACHE_NODEID) */ #define ncachehash(n) Nchash+((((int)(n)>>2)*31415)&Mch) _PROTOTYPE(static struct l_nch *ncache_addr,(KA_T na)); # endif /* defined(NCACHE_NODEID) */ # if !defined(NCACHE_NO_ROOT) _PROTOTYPE(static int ncache_isroot,(KA_T na, char *cp)); # endif /* !defined(NCACHE_NO_ROOT) */ /* * ncache_addr() - look up a node's local ncache address */ static struct l_nch * # if defined(NCACHE_NODEID) ncache_addr(i, na) unsigned long i; /* node's capability ID */ # else /* !defined(NCACHE_NODEID) */ ncache_addr(na) # endif /* defined(NCACHE_NODEID) */ KA_T na; /* node's address */ { struct l_nch **hp; # if defined(NCACHE_NODEID) for (hp = ncachehash(i, na); *hp; hp++) # else /* !defined(NCACHE_NODEID) */ for (hp = ncachehash(na); *hp; hp++) # endif /* defined(NCACHE_NODEID) */ { # if defined(NCACHE_NODEID) if ((*hp)->id == i && (*hp)->na == na) # else /* !defined(NCACHE_NODEID) */ if ((*hp)->na == na) # endif /* defined(NCACHE_NODEID) */ return(*hp); } return((struct l_nch *)NULL); } # if !defined(NCACHE_NO_ROOT) /* * ncache_isroot() - is head of name cache path a file system root? */ static int ncache_isroot(na, cp) KA_T na; /* kernel node address */ char *cp; /* partial path */ { char buf[MAXPATHLEN]; int i; MALLOC_S len; struct mounts *mtp; static int nca = 0; static int ncn = 0; static KA_T *nc = (KA_T *)NULL; struct stat sb; struct vnode v; if (!na) return(0); /* * Search the root vnode cache. */ for (i = 0; i < ncn; i++) { if (na == nc[i]) return(1); } /* * Read the vnode and see if it's a VDIR node with the NCACHE_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)na, (char *)&v, sizeof(v)) || v.v_type != VDIR || !(v.VNODE_VFLAG & NCACHE_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 node address to the root node cache. */ if (ncn >= nca) { if (!nca) { len = (MALLOC_S)(10 * sizeof(KA_T)); nc = (KA_T *)malloc(len); } else { len = (MALLOC_S)((nca + 10) * sizeof(KA_T)); nc = (KA_T *)realloc(nc, len); } if (!nc) { (void) fprintf(stderr, "%s: no space for root node table\n", Pn); Exit(1); } nca += 10; } nc[ncn++] = na; return(1); } # endif /* !defined(NCACHE_NO_ROOT) */ /* * ncache_load() - load the kernel's name cache */ void ncache_load() { struct NCACHE c; struct l_nch **hp, *ln; KA_T ka, knx; static struct NCACHE **khp = (struct namecache **)NULL; static int khpl = 0; NCACHE_SZ_CAST khsz; unsigned long kx; static struct l_nch *lc = (struct l_nch *)NULL; static int lcl = 0; int len, lim, n, nch, nchl, nlcl; char tbuf[32]; KA_T v; # if !defined(NCHNAMLEN) int cin = sizeof(c.NCACHE_NM); KA_T nmo = (KA_T)offsetof(struct NCACHE, NCACHE_NM); # endif /* !defined(NCHNAMLEN) */ # if !defined(NCACHE_NMLEN) char nbf[MAXPATHLEN + 1]; int nbfl = (int)(sizeof(nbf) - 1); KA_T nk; char *np; int rl; nbf[nbfl] = '\0'; # endif /* !defined(NCACHE_NMLEN) */ if (!Fncache) return; /* * Free previously allocated space. */ for (lc = Ncache; lc; lc = ln) { ln = lc->next; (void) free((FREE_P *)lc); } Ncache = (struct l_nch *)NULL; if (Nchash) (void) free((FREE_P *)Nchash); Nchash = (struct l_nch **)NULL; /* * Get kernel cache hash table size */ v = (KA_T)0; if (get_Nl_value(X_NCSIZE, (struct drive_Nl *)NULL, &v) < 0 || !v || kread((KA_T)v, (char *)&khsz, sizeof(khsz))) { if (!Fwarn) (void) fprintf(stderr, "%s: WARNING: can't read name cache hash size: %s\n", Pn, print_kptr(v, (char *)NULL, 0)); return; } if (khsz < 1) { if (!Fwarn) (void) fprintf(stderr, "%s: WARNING: name cache hash size length error: %#lx\n", Pn, khsz); return; } /* * Get kernel cache hash table address. */ ka = (KA_T)0; v = (KA_T)0; if (get_Nl_value(X_NCACHE, (struct drive_Nl *)NULL, &v) < 0 || !v || kread((KA_T)v, (char *)&ka, sizeof(ka)) || !ka) { if (!Fwarn) (void) fprintf(stderr, "%s: WARNING: unusable name cache hash pointer: (%s)=%s\n", Pn, print_kptr(v, tbuf, sizeof(tbuf)), print_kptr(ka, (char *)NULL, 0)); return; } /* * Allocate space for the hash table pointers and read them. */ len = (MALLOC_S)(khsz * sizeof(struct NCACHE *)); if (len > khpl) { if (khp) khp = (struct NCACHE **)realloc((MALLOC_P *)khp, len); else khp = (struct NCACHE **)malloc(len); if (!khp) { (void) fprintf(stderr, "%s: can't allocate %d bytes for name cache hash table\n", Pn, len); Exit(1); } khpl = len; } if (kread((KA_T)ka, (char *)khp, len)) { (void) fprintf(stderr, "%s: can't read name cache hash pointers from: %s\n", Pn, print_kptr(ka, (char *)NULL, 0)); return; } /* * Process the kernel's name cache hash table buckets. */ lim = khsz * 10; for (kx = nch = 0; kx < khsz; kx++) { /* * Loop through the entries for a hash bucket. */ for (ka = (KA_T)khp[kx], n = 0; ka; ka = knx, n++) { if (n > lim) { if (!Fwarn) (void) fprintf(stderr, "%s: WARNING: name cache hash chain too long\n", Pn); break; } if (kread(ka, (char *)&c, sizeof(c))) break; knx = (KA_T)c.NCACHE_NXT; if (!c.NCACHE_NODEADDR) continue; # if defined(NCACHE_NMLEN) if ((len = c.NCACHE_NMLEN) < 1) continue; # else /* !defined(NCACHE_NMLEN) */ /* * If it's possible to read the first four characters of the name, * do so and check for "." and "..". */ if (!c.NCACHE_NM || kread((KA_T)c.NCACHE_NM, nbf, 4)) continue; if (nbf[0] == '.') { if (!nbf[1] || ((nbf[1] == '.') && !nbf[2])) continue; } /* * Read the rest of the name, 32 characters at a time, until a NUL * character has been read or nbfl characters have been read. */ nbf[4] = '\0'; if ((len = (int)strlen(nbf)) < 4) { if (!len) continue; } else { for (np = &nbf[4]; len < nbfl; np += rl) { if ((rl = nbfl - len) > 32) { rl = 32; nbf[len + rl] = '\0'; } nk = (KA_T)((char *)c.NCACHE_NM + len); if (kread(nk, np, rl)) { rl = -1; break; } rl = (int)strlen(np); len += rl; if (rl < 32) break; } if (rl < 0) continue; } # endif /* defined(NCACHE_NMLEN) */ /* * Allocate a cache entry long enough to contain the name and * move the name to it. */ # if defined(NCHNAMLEN) if (len > NCHNAMLEN) continue; if (len < 3 && c.NCACHE_NM[0] == '.') { if (len == 1 || (len == 2 && c.NCACHE_NM[1] == '.')) continue; } if ((nlcl = sizeof(struct l_nch)) > lcl) # else /* !defined(NCHNAMLEN) */ if ((nlcl = sizeof(struct l_nch) + len) > lcl) # endif /* defined(NCHNAMLEN) */ { if (lc) lc = (struct l_nch *)realloc(lc, nlcl); else lc = (struct l_nch *)malloc(nlcl); if (!lc) { (void) fprintf(stderr, "%s: can't allocate %d local name cache bytes\n", Pn, nlcl); Exit(1); } lcl = nlcl; } # if defined(NCHNAMLEN) (void) strncpy(lc->nm, c.NCACHE_NM, len); # else /* !defined(NCHNAMLEN) */ # if defined(NCACHE_NMLEN) if ((len < 3) && (cin > 1)) { /* * If this is a one or two character name, and if NCACHE_NM[] * in c has room for at least two characters, check for "." * and ".." first, ignoring this entry if the name is either. */ if (len < 3 && c.NCACHE_NM[0] == '.') { if (len == 1 || (len == 2 && c.NCACHE_NM[1] == '.')) continue; } } if (len > cin) { /* * If not all (possibly not any, depending on the value in * cin) of the name has yet been read to lc->nm[], read it * or the rest of it. If it wasn't possible before to check * for "." or "..", do that. too. */ if (cin > 0) (void) strncpy(lc->nm, c.NCACHE_NM, cin); if (kread(ka + (KA_T)(nmo + cin), &lc->nm[cin], len - cin)) continue; if ((cin < 2) && (len < 3) && (lc->nm[0] == '.')) { if (len == 1 || (len == 2 && lc->nm[1] == '.')) continue; } } else (void) strncpy(lc->nm, c.NCACHE_NM, len); # else /* !defined(NCACHE_NMLEN) */ (void) strncpy(lc->nm, nbf, len); # endif /* defined(NCACHE_NMLEN) */ # endif /* defined(NCHNAMLEN) */ lc->nm[len] = '\0'; /* * Complete the new local cache entry and link it to the previous * local cache chain. */ lc->next = Ncache; Ncache = lc; lc->na = (KA_T)c.NCACHE_NODEADDR; lc->nl = len; lc->pa = (KA_T)c.NCACHE_PARADDR; lc->pla = (struct l_nch *)NULL; # if defined(NCACHE_NODEID) lc->id = c.NCACHE_NODEID; lc->did = c.NCACHE_PARID; # endif /* defined(NCACHE_NODEID) */ lcl = 0; lc = (struct l_nch *)NULL; nch++; } } /* * Reduce memory usage, as required. */ if (!RptTm) { (void) free((FREE_P *)khp); khp = (struct NCACHE **)NULL; khpl = 0; } if (nch < 1) { if (!Fwarn) (void) fprintf(stderr, "%s: WARNING: unusable name cache size: %d\n", Pn, nch); return; } /* * Build a hash table to locate Ncache entries. */ for (nchl = 1; nchl < nch; nchl <<= 1) ; nchl <<= 1; Mch = nchl - 1; len = nchl + nch; if (!(Nchash = (struct l_nch **)calloc(len, sizeof(struct l_nch *)))) { if (!Fwarn) (void) fprintf(stderr, "%s: no space for %d local name cache hash pointers\n", Pn, len); Exit(1); } for (lc = Ncache; lc; lc = lc->next) { # if defined(NCACHE_NODEID) for (hp = ncachehash(lc->id, lc->na), # else /* !defined(NCACHE_NODEID) */ for (hp = ncachehash(lc->na), # endif /* defined(NCACHE_NODEID) */ n = 1; *hp; hp++) { if ((*hp)->na == lc->na && strcmp((*hp)->nm, lc->nm) == 0) { n = 0; break; } } if (n) *hp = lc; else lc->pa = (KA_T)0; } /* * Make a final pass through the local cache and convert parent node * addresses to local name cache pointers. */ for (lc = Ncache; lc; lc = lc->next) { if (!lc->pa) continue; # if defined(NCACHE_NODEID) lc->pla = ncache_addr(lc->did, lc->pa); # else /* !defined(NCACHE_NODEID) */ lc->pla = ncache_addr(lc->pa); # 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 defined(NCACHE_NODEID) if (!Nchash || !(lc = ncache_addr(Lf->id, Lf->na))) # else /* !defined(NCACHE_NODEID) */ if (!Nchash || !(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); } /* * Start 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 length is too large to fit in the receiving buffer. */ for (;;) { if (!lc->pla) { # if !defined(NCACHE_NO_ROOT) if (ncache_isroot(lc->pa, cp)) *fp = 1; # endif /* !defined(NCACHE_NO_ROOT) */ break; } lc = lc->pla; 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_RNMH) */ char rnmh_d1[] = "d"; char *rnmh_d2 = rnmh_d1; #endif /* defined(HASNCACHE) && defined(USE_LIB_RNMH) */