2 * rnch.c -- Sun format name cache functions for lsof library
7 * Copyright 1997 Purdue Research Foundation, West Lafayette, Indiana
8 * 47907. All rights reserved.
10 * Written by Victor A. Abell
12 * This software is not subject to any license of the American Telephone
13 * and Telegraph Company or the Regents of the University of California.
15 * Permission is granted to anyone to use this software for any purpose on
16 * any computer system, and to alter it and redistribute it freely, subject
17 * to the following restrictions:
19 * 1. Neither the authors nor Purdue University are responsible for any
20 * consequences of the use of this software.
22 * 2. The origin of this software must not be misrepresented, either by
23 * explicit claim or by omission. Credit to the authors and Purdue
24 * University must appear in documentation and sources.
26 * 3. Altered versions must be plainly marked as such, and must not be
27 * misrepresented as being the original software.
29 * 4. This notice may not be removed or altered.
33 #include "../machine.h"
35 #if defined(HASNCACHE) && defined(USE_LIB_RNCH)
38 static char copyright[] =
39 "@(#) Copyright 1997 Purdue Research Foundation.\nAll rights reserved.\n";
40 static char *rcsid = "$Id: rnch.c,v 1.11 2008/10/21 16:13:23 abe Exp $";
41 # endif /* !defined(lint) */
47 * rnch.c - read Sun format (struct ncache) name cache
49 * This code is effective only when HASNCACHE is defined.
55 * #include the relevant header file -- e.g., <sys/dnlc.h>.
57 * Define X_NCSIZE as the nickname for the kernel cache size variable,
58 * or, if X_NCSIZE is undefined, define FIXED_NCSIZE as the size of the
61 * Define X_NCACHE as the nickname for the kernel cache address and
62 * define ADDR_NCACHE if the address is the address of the cache,
63 * rather than the address of a pointer to it.
65 * Define NCACHE_NXT if the kernel's name cache is a linked list, starting
66 * at the X_NCACHE address, rather than a table, starting at that address.
68 * Define any of the following casts that differ from their defaults:
70 * NCACHE_SZ_CAST cast for X_NCACHE (default int)
74 * Define NCACHE_DP as the name of the element in the
75 * ncache structure that contains the
76 * parent vnode pointer.
80 * Define NCACHE_NAME as the name of the element in the
81 * ncache structure that contains the
86 * Define NCACHE_NAMLEN as the name of the element in the
87 * ncache structure that contains the
92 * Define NCACHE_NEGVN as the name of the name list element
93 * whose value is a vnode address to
94 * ignore when loading the kernel name
97 * Define NCACHE_NODEID as the name of the element in the
98 * ncache structure that contains the
99 * vnode's capability ID.
101 * Define NCACHE_PARID as the name of the element in the
102 * ncache structure that contains the
103 * parent vnode's capability ID.
105 * Define NCACHE_VP as the name of the element in the
106 * ncache structure that contains the
111 * Note: if NCACHE_NODEID is defined, then NCACHE_PARID must be defined.
116 * Define this prototype for ncache_load():
118 * _PROTOTYPE(void ncache_load,(void));
123 * Local static values
126 static int Mch; /* name cache hash mask */
128 # if !defined(NCACHE_NC_CAST)
129 #define NCACHE_SZ_CAST int
130 # endif /* !defined(NCACHE_NC_CAST) */
132 static NCACHE_SZ_CAST Nc = 0; /* size of name cache */
133 static int Nch = 0; /* size of name cache hash pointer
136 KA_T vp; /* vnode address */
137 KA_T dp; /* parent vnode address */
138 struct l_nch *pa; /* parent Ncache address */
140 # if defined(NCACHE_NODEID)
141 unsigned long id; /* node's capability ID */
142 unsigned long did; /* parent node's capability ID */
143 # endif /* defined(NCACHE_NODEID) */
146 int nl; /* name length */
149 static struct l_nch *Ncache = (struct l_nch *)NULL;
150 /* the local name cache */
151 static struct l_nch **Nchash = (struct l_nch **)NULL;
152 /* Ncache hash pointers */
153 static int Ncfirst = 1; /* first-call status */
155 # if defined(NCACHE_NEGVN)
156 static KA_T NegVN = (KA_T)NULL; /* negative vnode address */
157 static int NegVNSt = 0; /* NegVN status: 0 = not loaded */
158 # endif /* defined(NCACHE_NEGVN) */
160 # if defined(NCACHE_NODEID)
161 _PROTOTYPE(static struct l_nch *ncache_addr,(unsigned long i, KA_T v));
162 #define ncachehash(i,v) Nchash+(((((int)(v)>>2)+((int)(i)))*31415)&Mch)
163 # else /* !defined(NCACHE_NODEID) */
164 _PROTOTYPE(static struct l_nch *ncache_addr,(KA_T v));
165 #define ncachehash(v) Nchash+((((int)(v)>>2)*31415)&Mch)
166 # endif /* defined(NCACHE_NODEID) */
168 _PROTOTYPE(static int ncache_isroot,(KA_T va, char *cp));
170 #define DEFNCACHESZ 1024 /* local size if X_NCSIZE kernel value < 1 */
171 #define LNCHINCRSZ 64 /* local size increment */
173 # if !defined(NCACHE_DP)
175 # endif /* !defined(NCACHE_DP) */
177 # if !defined(NCACHE_NAME)
178 #define NCACHE_NAME name
179 # endif /* !defined(NCACHE_NAME) */
181 # if !defined(NCACHE_NAMLEN)
182 #define NCACHE_NAMLEN namlen
183 # endif /* !defined(NCACHE_NAMLEN) */
185 # if !defined(NCACHE_VP)
187 # endif /* !defined(NCACHE_VP) */
191 * ncache_addr() - look up a node's local ncache address
194 static struct l_nch *
196 # if defined(NCACHE_NODEID)
198 # else /* !defined(NCACHE_NODEID) */
200 # endif /* defined(NCACHE_NODEID) */
202 # if defined(NCACHE_NODEID)
203 unsigned long i; /* capability ID */
204 # endif /* defined(NCACHE_NODEID) */
206 KA_T v; /* vnode's address */
210 # if defined(NCACHE_NODEID)
211 for (hp = ncachehash(i, v); *hp; hp++)
212 # else /* !defined(NCACHE_NODEID) */
213 for (hp = ncachehash(v); *hp; hp++)
214 # endif /* defined(NCACHE_NODEID) */
218 # if defined(NCACHE_NODEID)
219 if ((*hp)->vp == v && (*hp)->id == i)
220 # else /* !defined(NCACHE_NODEID) */
222 # endif /* defined(NCACHE_NODEID) */
226 return((struct l_nch *)NULL);
231 * ncache_isroot() - is head of name cache path a file system root?
235 ncache_isroot(va, cp)
236 KA_T va; /* kernel vnode address */
237 char *cp; /* partial path */
239 char buf[MAXPATHLEN];
247 static KA_T *vc = (KA_T *)NULL;
252 * Search the root vnode cache.
254 for (i = 0; i < vcn; i++) {
259 * Read the vnode and see if it's a VDIR node with the VROOT flag set. If
260 * it is, then the path is complete.
262 * If it isn't, and if the file has an inode number, search the mount table
263 * and see if the file system's inode number is known. If it is, form the
264 * possible full path, safely stat() it, and see if it's inode number matches
265 * the one we have for this file. If it does, then the path is complete.
267 if (kread((KA_T)va, (char *)&v, sizeof(v))
268 || v.v_type != VDIR || !(v.v_flag & VROOT)) {
271 * The vnode tests failed. Try the inode tests.
273 if (Lf->inp_ty != 1 || !Lf->inode
274 || !Lf->fsdir || (len = strlen(Lf->fsdir)) < 1)
276 if ((len + 1 + strlen(cp) + 1) > sizeof(buf))
278 for (mtp = readmnt(); mtp; mtp = mtp->next) {
279 if (!mtp->dir || !mtp->inode)
281 if (strcmp(Lf->fsdir, mtp->dir) == 0)
286 (void) strcpy(buf, Lf->fsdir);
287 if (buf[len - 1] != '/')
289 (void) strcpy(&buf[len], cp);
290 if (statsafely(buf, &sb) != 0
291 || (unsigned long)sb.st_ino != Lf->inode)
295 * Add the vnode address to the root vnode cache.
299 len = (MALLOC_S)(vca * sizeof(KA_T));
301 vc = (KA_T *)malloc(len);
303 vc = (KA_T *)realloc(vc, len);
305 (void) fprintf(stderr, "%s: no space for root vnode table\n",
316 * ncache_load() - load the kernel's name cache
323 struct l_nch **hp, *lc;
327 static KA_T kp = (KA_T)NULL;
330 # if defined(HASDNLCPTR)
332 static char *nb = (char *)NULL;
333 # endif /* defined(HASDNLCPTR) */
335 # if defined(NCACHE_NXT)
338 # else /* !defined(NCACHE_NXT) */
339 static struct ncache *kca = (struct ncache *)NULL;
340 # endif /* defined(NCACHE_NXT) */
347 * Do startup (first-time) functions.
351 * Establish kernel cache size.
354 # if defined(X_NCSIZE)
356 if (get_Nl_value(X_NCSIZE, (struct drive_Nl *)NULL, &v) < 0
358 || kread((KA_T)v, (char *)&Nc, sizeof(Nc)))
361 (void) fprintf(stderr,
362 "%s: WARNING: can't read name cache size: %s\n",
363 Pn, print_kptr(v, (char *)NULL, 0));
368 # else /* !defined(X_NCSIZE) */
369 iNc = Nc = FIXED_NCSIZE;
370 # endif /* defined(X_NCSIZE) */
374 (void) fprintf(stderr,
375 "%s: WARNING: kernel name cache size: %d\n", Pn, Nc);
376 (void) fprintf(stderr,
377 " Cache size assumed to be: %d\n", DEFNCACHESZ);
379 iNc = Nc = DEFNCACHESZ;
382 # if defined(NCACHE_NEGVN)
384 * Get negative vnode address.
387 if (get_Nl_value(NCACHE_NEGVN, (struct drive_Nl *)NULL, &NegVN)
392 # endif /* defined(NCACHE_NEGVN) */
395 * Establish kernel cache address.
398 # if defined(ADDR_NCACHE)
400 if (get_Nl_value(X_NCACHE,(struct drive_Nl *)NULL,(KA_T *)&kp) < 0
403 (void) fprintf(stderr,
404 "%s: WARNING: no name cache address\n", Pn);
408 # else /* !defined(ADDR_NCACHE) */
410 if (get_Nl_value(X_NCACHE, (struct drive_Nl *)NULL, &v) < 0
412 || kread((KA_T)v, (char *)&kp, sizeof(kp))) {
414 (void) fprintf(stderr,
415 "%s: WARNING: can't read name cache ptr: %s\n",
416 Pn, print_kptr(v, (char *)NULL, 0));
420 # endif /* defined(ADDR_NCACHE) */
423 * Allocate space for a local copy of the kernel's cache.
426 # if !defined(NCACHE_NXT)
427 len = Nc * sizeof(struct ncache);
428 if (!(kca = (struct ncache *)malloc((MALLOC_S)len))) {
430 (void) fprintf(stderr,
431 "%s: can't allocate name cache space: %d\n", Pn, len);
434 # endif /* !defined(NCACHE_NXT) */
437 * Allocate space for the local cache.
439 len = Nc * sizeof(struct l_nch);
440 if (!(Ncache = (struct l_nch *)calloc(Nc, sizeof(struct l_nch)))) {
445 (void) fprintf(stderr,
446 "%s: no space for %d byte local name cache\n", Pn, len);
452 * Do setup for repeat calls.
457 (void) free((FREE_P *)Nchash);
458 Nchash = (struct l_nch **)NULL;
463 * Free space malloc'd to names in local name cache.
465 for (i = 0, lc = Ncache; i < Nc; i++, lc++) {
467 (void) free((FREE_P *)lc->nm);
468 lc->nm = (char *)NULL;
474 # if defined(NCACHE_NXT)
476 # endif /* defined(NCACHE_NXT) */
480 # if !defined(NCACHE_NXT)
483 * Read the kernel's name cache.
485 if (kread(kp, (char *)kca, (Nc * sizeof(struct ncache)))) {
487 (void) fprintf(stderr,
488 "%s: WARNING: can't read kernel's name cache: %s\n",
489 Pn, print_kptr(kp, (char *)NULL, 0));
493 # endif /* !defined(NCACHE_NXT) */
496 * Build a local copy of the kernel name cache.
499 # if defined(NCACHE_NXT)
500 for (i = iNc * 16, kc = &nc, kf = kp, lc = Ncache, n = 0; kp; )
501 # else /* !defined(NCACHE_NXT) */
502 for (i = n = 0, kc = kca, lc = Ncache; i < Nc; i++, kc++)
503 # endif /* defined(NCACHE_NXT) */
507 # if defined(NCACHE_NXT)
508 if (kread(kp, (char *)kc, sizeof(nc)))
510 if ((kp = (KA_T)kc->NCACHE_NXT) == kf)
512 # endif /* defined(NCACHE_NXT) */
514 if (!kc->NCACHE_VP || (len = kc->NCACHE_NAMLEN) < 1)
517 # if defined(NCACHE_NEGVN)
518 if (NegVN && ((KA_T)kc->NCACHE_VP == NegVN))
520 # endif /* defined(NCACHE_NEGVN) */
522 # if defined(HASDNLCPTR)
524 * Read name from kernel to a temporary buffer.
529 nb = (char *)malloc(na);
531 nb = (char *)realloc((MALLOC_P *)nb, na);
533 (void) fprintf(stderr,
534 "%s: can't allocate %d byte temporary name buffer\n",
539 if (!kc->NCACHE_NAME || kread((KA_T)kc->NCACHE_NAME, nb, len))
542 # else /* !defined(HASDNLCPTR) */
544 * Use name that is in the kernel cache entry.
548 np = kc->NCACHE_NAME;
549 # endif /* defined(HASDNLCPTR) */
551 if (len < 3 && *np == '.') {
552 if (len == 1 || (len == 2 && np[1] == '.'))
556 * Allocate space for name in local cache entry.
558 if (!(cp = (char *)malloc(len + 1))) {
559 (void) fprintf(stderr,
560 "%s: can't allocate %d bytes for name cache name: %s\n",
564 (void) strncpy(cp, np, len);
567 # if defined(NCACHE_NXT)
571 * Allocate more local space to receive the kernel's linked
575 if (!(Ncache = (struct l_nch *)realloc(Ncache,
576 (MALLOC_S)(Nc * sizeof(struct l_nch)))))
578 (void) fprintf(stderr,
579 "%s: no more space for %d entry local name cache\n",
586 # endif /* defined(NCACHE_NXT) */
589 * Complete the local cache entry.
591 lc->vp = (KA_T)kc->NCACHE_VP;
592 lc->dp = (KA_T)kc->NCACHE_DP;
593 lc->pa = (struct l_nch *)NULL;
597 # if defined(NCACHE_NODEID)
598 lc->id = (unsigned long)kc->NCACHE_NODEID;
599 lc->did = (unsigned long)kc->NCACHE_PARID;
600 # endif /* defined(NCACHE_NODEID) */
605 # if defined(NCACHE_NXT)
608 (void) fprintf(stderr,
609 "%s: WARNING: name cache truncated at %d entries\n",
613 # endif /* defined(NCACHE_NXT) */
617 * Reduce memory usage, as required.
620 # if !defined(NCACHE_NXT)
622 (void) free((FREE_P *)kca);
623 # endif /* !defined(NCACHE_NXT) */
626 if (!RptTm && Ncache) {
629 * If not in repeat mode, free the space that has been malloc'd
630 * to the local name cache.
632 for (i = 0, lc = Ncache; i < Nc; i++, lc++) {
634 (void) free((FREE_P *)lc->nm);
635 lc->nm = (char *)NULL;
638 (void) free((FREE_P *)Ncache);
639 Ncache = (struct l_nch *)NULL;
643 (void) fprintf(stderr,
644 "%s: WARNING: unusable name cache size: %d\n", Pn, n);
650 len = Nc * sizeof(struct l_nch);
651 if (!(Ncache = (struct l_nch *)realloc(Ncache, len)))
656 * Build a hash table to locate Ncache entries.
658 for (Nch = 1; Nch < Nc; Nch <<= 1)
662 if (!(Nchash = (struct l_nch **)calloc(Nch+Nc, sizeof(struct l_nch *))))
665 (void) fprintf(stderr,
666 "%s: no space for %d name cache hash pointers\n",
670 for (i = 0, lc = Ncache; i < Nc; i++, lc++) {
672 # if defined(NCACHE_NODEID)
673 for (hp = ncachehash(lc->id, lc->vp), n = 1; *hp; hp++)
674 # else /* !defined(NCACHE_NODEID) */
675 for (hp = ncachehash(lc->vp), n = 1; *hp; hp++)
676 # endif /* defined(NCACHE_NODEID) */
679 if ((*hp)->vp == lc->vp && strcmp((*hp)->nm, lc->nm) == 0
680 && (*hp)->dp == lc->dp
682 # if defined(NCACHE_NODEID)
683 && (*hp)->id == lc->id && (*hp)->did == lc->did
684 # endif /* defined(NCACHE_NODEID) */
695 * Make a final pass through the local cache and convert parent vnode
696 * addresses to local name cache pointers.
698 for (i = 0, lc = Ncache; i < Nc; i++, lc++) {
702 # if defined(NCACHE_NEGVN)
703 if (NegVN && (lc->dp == NegVN)) {
704 lc->pa = (struct l_nch *)NULL;
707 # endif /* defined(NCACHE_NEGVN) */
709 # if defined(NCACHE_NODEID)
710 lc->pa = ncache_addr(lc->did, lc->dp);
711 # else /* !defined(NCACHE_NODEID) */
712 lc->pa = ncache_addr(lc->dp);
713 # endif /* defined(NCACHE_NODEID) */
720 * ncache_lookup() - look up a node's name in the kernel's name cache
724 ncache_lookup(buf, blen, fp)
725 char *buf; /* receiving name buffer */
726 int blen; /* receiving buffer length */
727 int *fp; /* full path reply */
737 # if defined(HASFSINO)
739 * If the entry has an inode number that matches the inode number of the
740 * file system mount point, return an empty path reply. That tells the
741 * caller to print the file system mount point name only.
743 if ((Lf->inp_ty == 1) && Lf->fs_ino && (Lf->inode == Lf->fs_ino))
745 # endif /* defined(HASFSINO) */
748 * Look up the name cache entry for the node address.
752 # if defined(NCACHE_NODEID)
753 || !(lc = ncache_addr(Lf->id, Lf->na))
754 # else /* !defined(NCACHE_NODEID) */
755 || !(lc = ncache_addr(Lf->na))
756 # endif /* defined(NCACHE_NODEID) */
761 * If the node has no cache entry, see if it's the mount
762 * point of a known file system.
764 if (!Lf->fsdir || !Lf->dev_def || Lf->inp_ty != 1)
765 return((char *)NULL);
766 for (mtp = readmnt(); mtp; mtp = mtp->next) {
767 if (!mtp->dir || !mtp->inode)
769 if (Lf->dev == mtp->dev
770 && mtp->inode == Lf->inode
771 && strcmp(mtp->dir, Lf->fsdir) == 0)
774 return((char *)NULL);
777 * Begin the path assembly.
779 if ((nl = lc->nl) > (blen - 1))
780 return((char *)NULL);
781 cp = buf + blen - nl - 1;
782 rlen = blen - nl - 1;
783 (void) strcpy(cp, lc->nm);
785 * Look up the name cache entries that are parents of the node address.
789 * the name is too large to fit in the receiving buffer.
793 if (ncache_isroot(lc->dp, cp))
798 if (((nl = lc->nl) + 1) > rlen)
803 (void) strncpy((cp - nl), lc->nm, nl);
809 #else /* !defined(HASNCACHE) || !defined(USE_LIB_RNCH) */
810 char rnch_d1[] = "d"; char *rnch_d2 = rnch_d1;
811 #endif /* defined(HASNCACHE) && defined(USE_LIB_RNCH) */