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