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