tizen 2.3.1 release
[external/lsof.git] / lib / rnam.c
1 /*
2  * rnam.c -- BSD format name cache functions for lsof library
3  */
4
5
6 /*
7  * Copyright 1997 Purdue Research Foundation, West Lafayette, Indiana
8  * 47907.  All rights reserved.
9  *
10  * Written by Victor A. Abell
11  *
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.
14  *
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:
18  *
19  * 1. Neither the authors nor Purdue University are responsible for any
20  *    consequences of the use of this software.
21  *
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.
25  *
26  * 3. Altered versions must be plainly marked as such, and must not be
27  *    misrepresented as being the original software.
28  *
29  * 4. This notice may not be removed or altered.
30  */
31
32
33 #include "../machine.h"
34
35 #if     defined(HASNCACHE) && defined(USE_LIB_RNAM)
36
37 # if    !defined(lint)
38 static char copyright[] =
39 "@(#) Copyright 1997 Purdue Research Foundation.\nAll rights reserved.\n";
40 static char *rcsid = "$Id: rnam.c,v 1.11 2008/10/21 16:13:23 abe Exp $";
41 # endif /* !defined(lint) */
42
43 #include "../lsof.h"
44
45
46 /*
47  * rnam.c - read BSD format (struct namecache or nch) name cache
48  *
49  * This code is effective only when HASNCACHE is defined.
50  */
51
52 /*
53  * The caller must:
54  *
55  *      #include the relevant header file -- e.g., <sys/namei.h>.
56  *
57  *      Define X_NCACHE as the nickname for the kernel cache address.
58  *
59  *      Define X_NCSIZE as the nickname for the size of the kernel cache.
60  *
61  *      Define NCACHE_NXT if the kernel's name cache is a linked list, starting
62  *      at the X_NCACHE address, rather than a table, starting at that address.
63  *
64  *      Define NCACHE_NO_ROOT if the calling dialect doesn't support
65  *      the locating of the root node of a file system.
66  *
67  *      Define the name of the name cache structure -- e.g.,
68  *
69  *              #define NCACHE  <structure name>
70  *
71  *      Define the following casts, if they differ from the defaults:
72  *
73  *              NCACHE_SZ_CAST  cast for X_NCSIZE (default int)
74  *
75  *      e.g.,
76  *              #define NCACHE_SZ_CAST unsigned long
77  *
78  *      Define the names of these elements of struct NCACHE:
79  *
80  *      must            #define NCACHE_NM       <name>
81  *      must            #define NCACHE_NMLEN    <name length
82  *      optional        #define NCACHE_NXT      <link to next entry>
83  *      must            #define NCACHE_NODEADDR <node address>
84  *      must            #define NCACHE_NODEID   <node capability ID)
85  *      optional        #define NCACHE_PARADDR  <parent node address>
86  *      optional        #define NCACHE_PARID    <parent node capability ID)
87  *
88  * The caller may need to:
89  *
90  *      Define NCHNAMLEN as the length of the name element of NCACHE, if it's
91  *      not defined in the header file that defines the NCACHE structure.
92  *
93  *      Define this prototype for ncache_load():
94  *
95  *              _PROTOTYPE(static void ncache_load,(void));
96  */
97
98
99 /*
100  * Local static values
101  */
102
103 static int Mch;                         /* name cache hash mask */
104
105 # if    !defined(NCACHE_NC_CAST)
106 #define NCACHE_SZ_CAST  int
107 # endif /* !defined(NCACHE_NC_CAST) */
108
109 static NCACHE_SZ_CAST Nc = 0;           /* size of name cache */
110 static int Nch = 0;                     /* size of name cache hash pointer
111                                          * table */
112 struct l_nch {
113         KA_T na;                        /* node address */
114
115 # if    defined(NCACHE_NODEID)
116         unsigned long id;               /* capability ID */
117 # endif /* defined(NCACHE_NODEID) */
118
119 # if    defined(NCACHE_PARADDR) && defined(NCACHE_PARID)
120         KA_T pa;                        /* parent node address */
121         struct l_nch *pla;              /* parent local node address */
122         unsigned long did;              /* parent capability ID */
123 # endif /* defined(NCACHE_PARADDR) && defined(NCACHE_PARID) */
124
125         char nm[NCHNAMLEN+1];           /* name */
126         int nl;                         /* name length */
127 };
128
129 static struct l_nch *Ncache = (struct l_nch*)NULL;
130                                         /* the local name cache */
131 static struct l_nch **Nchash = (struct l_nch **)NULL;
132                                         /* Ncache hash pointers */
133 static int Ncfirst = 1;                 /* first-call status */
134
135 # if    defined(NCACHE_NODEID)
136 #define ncachehash(i,n)         Nchash+(((((int)(n)>>2)+((int)(i)))*31415)&Mch)
137 _PROTOTYPE(static struct l_nch *ncache_addr,(unsigned long i, KA_T na));
138 # else  /* !defined(NCACHE_NODEID) */
139 #define ncachehash(n)           Nchash+((((int)(n)>>2)*31415)&Mch)
140 _PROTOTYPE(static struct l_nch *ncache_addr,(KA_T na));
141 # endif /* defined(NCACHE_NODEID) */
142
143 #define DEFNCACHESZ     1024    /* local size if X_NCSIZE kernel value < 1 */
144 #define LNCHINCRSZ      64      /* local size increment */
145
146 # if    !defined(NCACHE_NO_ROOT)
147 _PROTOTYPE(static int ncache_isroot,(KA_T na, char *cp));
148 # endif /* !defined(NCACHE_NO_ROOT) */
149
150
151 /*
152  * ncache_addr() - look up a node's local ncache address
153  */
154
155 static struct l_nch *
156
157 # if    defined(NCACHE_NODEID)
158 ncache_addr(i, na)
159         unsigned long i;                /* node's capability ID */
160 # else  /* !defined(NCACHE_NODEID) */
161 ncache_addr(na)
162 # endif /* defined(NCACHE_NODEID) */
163
164         KA_T na;                        /* node's address */
165 {
166         struct l_nch **hp;
167
168 # if    defined(NCACHE_NODEID)
169         for (hp = ncachehash(i, na); *hp; hp++)
170 # else  /* !defined(NCACHE_NODEID) */
171         for (hp = ncachehash(na); *hp; hp++)
172 # endif /* defined(NCACHE_NODEID) */
173
174         {
175
176 # if    defined(NCACHE_NODEID)
177             if ((*hp)->id == i && (*hp)->na == na)
178 # else  /* !defined(NCACHE_NODEID) */
179             if ((*hp)->na == na)
180 # endif /* defined(NCACHE_NODEID) */
181
182                 return(*hp);
183         }
184         return((struct l_nch *)NULL);
185 }
186
187
188 # if    !defined(NCACHE_NO_ROOT)
189 /*
190  * ncache_isroot() - is head of name cache path a file system root?
191  */
192
193 static int
194 ncache_isroot(na, cp)
195         KA_T na;                                /* kernel node address */
196         char *cp;                               /* partial path */
197 {
198         char buf[MAXPATHLEN];
199         int i;
200         MALLOC_S len;
201         struct mounts *mtp;
202         static int nca = 0;
203         static int ncn = 0;
204         static KA_T *nc = (KA_T *)NULL;
205         struct stat sb;
206         struct vnode v;
207
208         if (!na)
209             return(0);
210 /*
211  * Search the root vnode cache.
212  */
213         for (i = 0; i < ncn; i++) {
214             if (na == nc[i])
215                 return(1);
216         }
217 /*
218  * Read the vnode and see if it's a VDIR node with the VROOT flag set.  If
219  * it is, then the path is complete.
220  *
221  * If it isn't, and if the file has an inode number, search the mount table
222  * and see if the file system's inode number is known.  If it is, form the
223  * possible full path, safely stat() it, and see if it's inode number matches
224  * the one we have for this file.  If it does, then the path is complete.
225  */
226         if (kread((KA_T)na, (char *)&v, sizeof(v))
227         ||  v.v_type != VDIR || !(v.v_flag & VROOT)) {
228
229         /*
230          * The vnode tests failed.  Try the inode tests.
231          */
232             if (Lf->inp_ty != 1 || !Lf->inode
233             ||  !Lf->fsdir || (len = strlen(Lf->fsdir)) < 1)
234                 return(0);
235             if ((len + 1 + strlen(cp) + 1) > sizeof(buf))
236                 return(0);
237             for (mtp = readmnt(); mtp; mtp = mtp->next) {
238                 if (!mtp->dir || !mtp->inode)
239                     continue;
240                 if (strcmp(Lf->fsdir, mtp->dir) == 0)
241                     break;
242             }
243             if (!mtp)
244                 return(0);
245             (void) strcpy(buf, Lf->fsdir);
246             if (buf[len - 1] != '/')
247                 buf[len++] = '/';
248             (void) strcpy(&buf[len], cp);
249             if (statsafely(buf, &sb) != 0
250             ||  (unsigned long)sb.st_ino != Lf->inode)
251                 return(0);
252         }
253 /*
254  * Add the node address to the root node cache.
255  */
256         if (ncn >= nca) {
257             if (!nca) {
258                 len = (MALLOC_S)(10 * sizeof(KA_T));
259                 nc = (KA_T *)malloc(len);
260             } else {
261                 len = (MALLOC_S)((nca + 10) * sizeof(KA_T));
262                 nc = (KA_T *)realloc(nc, len);
263             }
264             if (!nc) {
265                 (void) fprintf(stderr, "%s: no space for root node table\n",
266                     Pn);
267                 Exit(1);
268             }
269             nca += 10;
270         }
271         nc[ncn++] = na;
272         return(1);
273 }
274 # endif /* !defined(NCACHE_NO_ROOT) */
275
276
277 /*
278  * ncache_load() - load the kernel's name cache
279  */
280
281 void
282 ncache_load()
283 {
284         struct l_nch **hp, *lc;
285         int i, len, n;
286         static int iNc = 0;
287         struct NCACHE *kc;
288         static KA_T kp = (KA_T)NULL;
289         KA_T v;
290
291 # if    defined(NCACHE_NXT)
292         static KA_T kf;
293         struct NCACHE nc;
294 # else  /* !defined NCACHE_NXT) */
295         static struct NCACHE *kca = (struct NCACHE *)NULL;
296 # endif /* defined(NCACHE_NXT) */
297
298         if (!Fncache)
299             return;
300         if (Ncfirst) {
301
302         /*
303          * Do startup (first-time) functions.
304          */
305             Ncfirst = 0;
306         /*
307          * Establish kernel cache size.
308          */
309             v = (KA_T)0;
310             if (get_Nl_value(X_NCSIZE, (struct drive_Nl *)NULL, &v) < 0
311             ||  !v
312             ||  kread((KA_T)v, (char *)&Nc, sizeof(Nc)))
313             {
314                 if (!Fwarn)
315                     (void) fprintf(stderr,
316                         "%s: WARNING: can't read name cache size: %s\n",
317                         Pn, print_kptr(v, (char *)NULL, 0));
318                 iNc = Nc = 0;
319                 return;
320             }
321             iNc = Nc;
322             if (Nc < 1) {
323                 if (!Fwarn) {
324                     (void) fprintf(stderr,
325                         "%s: WARNING: kernel name cache size: %d\n", Pn, Nc);
326                     (void) fprintf(stderr,
327                         "      Cache size assumed to be: %d\n", DEFNCACHESZ);
328                 }
329                 iNc = Nc = DEFNCACHESZ;
330             }
331         /*
332          * Establish kernel cache address.
333          */
334             v = (KA_T)0;
335             if (get_Nl_value(X_NCACHE, (struct drive_Nl *)NULL, &v) < 0
336             ||  !v
337             ||  kread((KA_T)v, (char *)&kp, sizeof(kp))) {
338                 if (!Fwarn)
339                     (void) fprintf(stderr,
340                         "%s: WARNING: can't read name cache address: %s\n",
341                         Pn, print_kptr(v, (char *)NULL, 0));
342                 iNc = Nc = 0;
343                 return;
344             }
345
346 # if    defined(NCACHE_NXT)
347             kf = kp;
348
349 # else  /* !defined(NCACHE_NXT) */
350         /*
351          * Allocate space for a local copy of the kernel's cache.
352          */
353             len = Nc * sizeof(struct NCACHE);
354             if (!(kca = (struct NCACHE *)malloc((MALLOC_S)len))) {
355                 if (!Fwarn)
356                     (void) fprintf(stderr,
357                         "%s: can't allocate name cache space: %d\n", Pn, len);
358                 Exit(1);
359             }
360 # endif /* defined(NCACHE_NXT) */
361
362         /*
363          * Allocate space for the local cache.
364          */
365             len = Nc * sizeof(struct l_nch);
366             if (!(Ncache = (struct l_nch *)malloc((MALLOC_S)len))) {
367
368 no_local_space:
369
370                 if (!Fwarn)
371                     (void) fprintf(stderr,
372                         "%s: no space for %d byte local name cache\n", Pn, len);
373                 Exit(1);
374             }
375         } else {
376
377         /*
378          * Do setup for repeat calls.
379          */
380             if ((Nc = iNc) == 0)
381                 return;
382             if (Nchash) {
383                 (void) free((FREE_P *)Nchash);
384                 Nchash = (struct l_nch **)NULL;
385             }
386
387 # if    defined(NCACHE_NXT)
388             kp = kf;
389 # endif /* defined(NCACHE_NXT) */
390
391         }
392
393 # if    !defined(NCACHE_NXT)
394
395 /*
396  * Read the kernel's name cache.
397  */
398         if (kread(kp, (char *)kca, (Nc * sizeof(struct NCACHE)))) {
399             if (!Fwarn)
400                 (void) fprintf(stderr,
401                     "%s: WARNING: can't read kernel's name cache: %s\n",
402                     Pn, print_kptr(kp, (char *)NULL, 0));
403             Nc = 0;
404             return;
405         }
406 # endif /* !defined(NCACHE_NXT) */
407
408 /*
409  * Build a local copy of the kernel name cache.
410  */
411
412 # if    defined(NCACHE_NXT)
413         for (i = iNc * 16, kc = &nc, lc = Ncache, n = 0; kp; )
414 # else  /* !defined(NCACHE_NXT) */
415         for (i = n = 0, kc = kca, lc = Ncache; i < Nc; i++, kc++)
416 # endif /* defined(NCACHE_NXT) */
417
418         {
419
420 # if    defined(NCACHE_NXT)
421             if (kread(kp, (char *)kc, sizeof(nc)))
422                 break;
423             if ((kp = (KA_T)kc->NCACHE_NXT) == kf)
424                 kp = (KA_T)NULL;
425 # endif /* defined(NCACHE_NXT) */
426
427             if (!kc->NCACHE_NODEADDR)
428                 continue;
429             if ((len = kc->NCACHE_NMLEN) < 1 || len > NCHNAMLEN)
430                 continue;
431             if (len < 3 && kc->NCACHE_NM[0] == '.') {
432                 if (len == 1 || (len == 2 && kc->NCACHE_NM[1] == '.'))
433                     continue;
434             }
435
436 # if    defined(NCACHE_NXT)
437             if (n >= Nc) {
438                 Nc += LNCHINCRSZ;
439                 if (!(Ncache = (struct l_nch *)realloc(Ncache,
440                      (MALLOC_S)(Nc * sizeof(struct l_nch)))))
441                 {
442                     (void) fprintf(stderr,
443                         "%s: no more space for %d entry local name cache\n",
444                         Pn, Nc);
445                     Exit(1);
446                 }
447                 lc = &Ncache[n];
448             }
449 # endif /* defined(NCACHE_NXT) */
450
451 #  if   defined(NCACHE_NODEID)
452             lc->na = (KA_T)kc->NCACHE_NODEADDR;
453             lc->id = kc->NCACHE_NODEID;
454 #  endif        /* defined(NCACHE_NODEID) */
455
456 #  if   defined(NCACHE_PARADDR)
457             lc->pa = (KA_T)kc->NCACHE_PARADDR;
458             lc->pla = (struct l_nch *)NULL;
459 #  endif        /* defined(NCACHE_PARADDR) */
460
461 #  if   defined(NCACHE_PARID)
462             lc->did = kc->NCACHE_PARID;
463 #  endif        /* defined(NCACHE_PARID) */
464
465             (void) strncpy(lc->nm, kc->NCACHE_NM, len);
466             lc->nm[len] = '\0';
467             lc->nl = strlen(lc->nm);
468             n++;
469             lc++;
470
471 # if    defined(NCACHE_NXT)
472             if (n >= i) {
473                 if (!Fwarn)
474                     (void) fprintf(stderr,
475                         "%s: WARNING: name cache truncated at %d entries\n",
476                         Pn, n);
477                 break;
478             }
479 # endif /* defined(NCACHE_NXT) */
480
481         }
482 /*
483  * Reduce memory usage, as required.
484  */
485
486 # if    !defined(NCACHE_NXT)
487         if (!RptTm)
488             (void) free((FREE_P *)kca);
489 # endif /* !defined(NCACHE_NXT) */
490
491         if (n < 1) {
492             Nc = 0;
493             if (!RptTm) {
494                 (void) free((FREE_P *)Ncache);
495                 Ncache = (struct l_nch *)NULL;
496             }
497             if (!Fwarn)
498                 (void) fprintf(stderr,
499                     "%s: WARNING: unusable name cache size: %d\n", Pn, n);
500             return;
501         }
502         if (n < Nc) {
503             Nc = n;
504             if (!RptTm) {
505                 len = Nc * sizeof(struct l_nch);
506                 if (!(Ncache = (struct l_nch *)realloc(Ncache, len)))
507                     goto no_local_space;
508             }
509         }
510 /*
511  * Build a hash table to locate Ncache entries.
512  */
513         for (Nch = 1; Nch < Nc; Nch <<= 1)
514             ;
515         Nch <<= 1;
516         Mch = Nch - 1;
517         if (!(Nchash = (struct l_nch **)calloc(Nch+Nc, sizeof(struct l_nch *))))
518         {
519             if (!Fwarn)
520                 (void) fprintf(stderr,
521                     "%s: no space for %d name cache hash pointers\n",
522                     Pn, Nch + Nc);
523             Exit(1);
524         }
525         for (i = 0, lc = Ncache; i < Nc; i++, lc++) {
526
527 # if    defined(NCACHE_NODEID)
528             for (hp = ncachehash(lc->id, lc->na), n = 1; *hp; hp++)
529 # else  /* defined(NCACHE_NODEID) */
530             for (hp = ncachehash(lc->na), n = 1; *hp; hp++)
531 # endif /* defined(NCACHE_NODEID) */
532
533             {
534
535 # if    defined(NCACHE_NODEID)
536                 if ((*hp)->na == lc->na && (*hp)->id == lc->id
537 # else  /* defined(NCACHE_NODEID) */
538                 if ((*hp)->na == lc->na
539 # endif /* defined(NCACHE_NODEID) */
540
541                 &&  strcmp((*hp)->nm, lc->nm) == 0
542
543 # if    defined(NCACHE_PARADDR) && defined(NCACHE_PARID)
544                 &&  (*hp)->pa == lc->pa && (*hp)->did == lc->did
545 # endif /* defined(NCACHE_PARADDR) && defined(NCACHE_PARID) */
546
547                 ) {
548                     n = 0;
549                     break;
550                 }
551             }
552             if (n)
553                 *hp = lc;
554         }
555
556 # if    defined(NCACHE_PARADDR) && defined(NCACHE_PARID)
557 /*
558  * Make a final pass through the local cache and convert parent node
559  * addresses to local name cache pointers.
560  */
561         for (i = 0, lc = Ncache; i < Nc; i++, lc++) {
562             if (!lc->pa)
563                 continue;
564             lc->pla = ncache_addr(lc->did, lc->pa);
565         }
566 # endif /* defined(NCACHE_PARADDR) && defined(NCACHE_PARID) */
567 }
568
569
570 /*
571  * ncache_lookup() - look up a node's name in the kernel's name cache
572  */
573
574 char *
575 ncache_lookup(buf, blen, fp)
576         char *buf;                      /* receiving name buffer */
577         int blen;                       /* receiving buffer length */
578         int *fp;                        /* full path reply */
579 {
580         char *cp = buf;
581         struct l_nch *lc;
582         struct mounts *mtp;
583         int nl, rlen;
584
585         *cp = '\0';
586         *fp = 0;
587
588 # if    defined(HASFSINO)
589 /*
590  * If the entry has an inode number that matches the inode number of the
591  * file system mount point, return an empty path reply.  That tells the
592  * caller to print the file system mount point name only.
593  */
594         if ((Lf->inp_ty == 1) && Lf->fs_ino && (Lf->inode == Lf->fs_ino))
595             return(cp);
596 # endif /* defined(HASFSINO) */
597
598 /*
599  * Look up the name cache entry for the node address.
600  */
601
602 # if    defined(NCACHE_NODEID)
603         if (Nc == 0 || !(lc = ncache_addr(Lf->id, Lf->na)))
604 # else  /* defined(NCACHE_NODEID) */
605         if (Nc == 0 || !(lc = ncache_addr(Lf->na)))
606 # endif /* defined(NCACHE_NODEID) */
607
608
609         {
610
611         /*
612          * If the node has no cache entry, see if it's the mount
613          * point of a known file system.
614          */
615             if (!Lf->fsdir || !Lf->dev_def || Lf->inp_ty != 1)
616                 return((char *)NULL);
617             for (mtp = readmnt(); mtp; mtp = mtp->next) {
618                 if (!mtp->dir || !mtp->inode)
619                     continue;
620                 if (Lf->dev == mtp->dev
621                 &&  mtp->inode == Lf->inode
622                 &&  strcmp(mtp->dir, Lf->fsdir) == 0)
623                     return(cp);
624             }
625             return((char *)NULL);
626         }
627 /*
628  * Start the path assembly.
629  */
630         if ((nl = lc->nl) > (blen - 1))
631             return((char *)NULL);
632         cp = buf + blen - nl - 1;
633         rlen = blen - nl - 1;
634         (void) strcpy(cp, lc->nm);
635
636 # if    defined(NCACHE_PARADDR) && defined(NCACHE_PARID)
637 /*
638  * Look up the name cache entries that are parents of the node address.
639  * Quit when:
640  *
641  *      there's no parent;
642  *      the name length is too large to fit in the receiving buffer.
643  */
644         for (;;) {
645             if (!lc->pla) {
646
647 #  if   !defined(NCACHE_NO_ROOT)
648                 if (ncache_isroot(lc->pa, cp))
649                     *fp = 1;
650 #  endif        /* !defined(NCACHE_NO_ROOT) */
651
652                 break;
653             }
654             lc = lc->pla;
655             if (((nl = lc->nl) + 1) > rlen)
656                 break;
657             *(cp - 1) = '/';
658             cp--;
659             rlen--;
660             (void) strncpy((cp - nl), lc->nm, nl);
661             cp -= nl;
662             rlen -= nl;
663         }
664 # endif /* defined(NCACHE_PARADDR) && defined(NCACHE_PARID) */
665         return(cp);
666 }
667 #else   /* !defined(HASNCACHE) || !defined(USE_LIB_RNAM) */
668 char rnam_d1[] = "d"; char *rnam_d2 = rnam_d1;
669 #endif  /* defined(HASNCACHE) && defined(USE_LIB_RNAM) */