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)
39static char copyright[] =
40"@(#) Copyright 1997 Purdue Research Foundation.\nAll rights reserved.\n";
41static 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
146static int Mch;				/* name cache hash mask */
147
148struct 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
168static struct l_nch *Ncache = (struct l_nch *)NULL;
169					/* the head of the local name cache */
170static 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
190static struct l_nch *
191
192# if	defined(NCACHE_NODEID)
193ncache_addr(i, na)
194	unsigned long i;		/* node's capability ID */
195# else	/* !defined(NCACHE_NODEID) */
196ncache_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
228static int
229ncache_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
316void
317ncache_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
652char *
653ncache_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) */
742char rnmh_d1[] = "d"; char *rnmh_d2 = rnmh_d1;
743#endif	/* defined(HASNCACHE) && defined(USE_LIB_RNMH) */
744