1 /* $NetBSD: nsdispatch.c,v 1.34 2009/02/05 13:21:11 lukem Exp $ */ 2 3 /*- 4 * Copyright (c) 1997, 1998, 1999, 2004 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Luke Mewburn; and by Jason R. Thorpe. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /*- 33 * Copyright (c) 2003 Networks Associates Technology, Inc. 34 * All rights reserved. 35 * 36 * Portions of this software were developed for the FreeBSD Project by 37 * Jacques A. Vidrine, Safeport Network Services, and Network 38 * Associates Laboratories, the Security Research Division of Network 39 * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 40 * ("CBOSS"), as part of the DARPA CHATS research program. 41 * 42 * Redistribution and use in source and binary forms, with or without 43 * modification, are permitted provided that the following conditions 44 * are met: 45 * 1. Redistributions of source code must retain the above copyright 46 * notice, this list of conditions and the following disclaimer. 47 * 2. Redistributions in binary form must reproduce the above copyright 48 * notice, this list of conditions and the following disclaimer in the 49 * documentation and/or other materials provided with the distribution. 50 * 51 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 52 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 53 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 54 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 55 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 56 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 57 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 58 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 59 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 60 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 61 * SUCH DAMAGE. 62 */ 63 64 #include <sys/cdefs.h> 65 #if defined(LIBC_SCCS) && !defined(lint) 66 __RCSID("$NetBSD: nsdispatch.c,v 1.34 2009/02/05 13:21:11 lukem Exp $"); 67 #endif /* LIBC_SCCS and not lint */ 68 69 #include "namespace.h" 70 71 #include <sys/types.h> 72 #include <sys/param.h> 73 #include <sys/stat.h> 74 #include <sys/queue.h> 75 76 #include <assert.h> 77 #ifdef __ELF__ 78 #include <dlfcn.h> 79 #endif /* __ELF__ */ 80 #include <err.h> 81 #include <fcntl.h> 82 #define _NS_PRIVATE 83 #include <nsswitch.h> 84 #include <stdarg.h> 85 #include <stdio.h> 86 #include <stdlib.h> 87 #include <string.h> 88 #include <unistd.h> 89 90 #include "reentrant.h" 91 92 extern FILE *_nsyyin; 93 extern int _nsyyparse(void); 94 95 96 #ifdef __weak_alias 97 __weak_alias(nsdispatch,_nsdispatch) 98 #endif 99 100 101 /* 102 * default sourcelist: `files' 103 */ 104 const ns_src __nsdefaultsrc[] = { 105 { NSSRC_FILES, NS_SUCCESS }, 106 { 0, 0 }, 107 }; 108 109 const ns_src __nsdefaultcompat[] = { 110 { NSSRC_COMPAT, NS_SUCCESS }, 111 { 0, 0 } 112 }; 113 114 const ns_src __nsdefaultcompat_forceall[] = { 115 { NSSRC_COMPAT, NS_SUCCESS | NS_FORCEALL }, 116 { 0, 0 } 117 }; 118 119 const ns_src __nsdefaultfiles[] = { 120 { NSSRC_FILES, NS_SUCCESS }, 121 { 0, 0 }, 122 }; 123 124 const ns_src __nsdefaultfiles_forceall[] = { 125 { NSSRC_FILES, NS_SUCCESS | NS_FORCEALL }, 126 { 0, 0 }, 127 }; 128 129 const ns_src __nsdefaultnis[] = { 130 { NSSRC_NIS, NS_SUCCESS }, 131 { 0, 0 } 132 }; 133 134 const ns_src __nsdefaultnis_forceall[] = { 135 { NSSRC_NIS, NS_SUCCESS | NS_FORCEALL }, 136 { 0, 0 } 137 }; 138 139 140 /* Database, source mappings. */ 141 static u_int _nsmapsize; 142 static ns_dbt *_nsmap; 143 144 /* Nsswitch modules. */ 145 static u_int _nsmodsize; 146 static ns_mod *_nsmod; 147 148 /* Placeholder for built-in modules' dlopen() handles. */ 149 static void *_nsbuiltin = &_nsbuiltin; 150 151 #ifdef _REENTRANT 152 /* 153 * Global nsswitch data structures are mostly read-only, but we update them 154 * when we read or re-read nsswitch.conf. 155 */ 156 static rwlock_t _nslock = RWLOCK_INITIALIZER; 157 158 /* 159 * List of threads currently in nsdispatch(). We use this to detect 160 * recursive calls and avoid reloading configuration in such cases, 161 * which could cause deadlock. 162 */ 163 struct _ns_drec { 164 LIST_ENTRY(_ns_drec) list; 165 thr_t thr; 166 }; 167 static LIST_HEAD(, _ns_drec) _ns_drec = LIST_HEAD_INITIALIZER(&_ns_drec); 168 static mutex_t _ns_drec_lock = MUTEX_INITIALIZER; 169 #endif /* _REENTRANT */ 170 171 #ifndef __ELF__ 172 #define is_dynamic() (0) /* don't bother - switch to ELF! */ 173 #elif __GNUC_PREREQ__(4,2) 174 static int rtld_DYNAMIC __attribute__((__weakref__, __alias__("_DYNAMIC"))); 175 #define is_dynamic() (&rtld_DYNAMIC != NULL) 176 #else 177 extern int _DYNAMIC __weak_reference(_DYNAMIC); 178 #define is_dynamic() (&_DYNAMIC != NULL) 179 #endif 180 181 /* 182 * size of dynamic array chunk for _nsmap and _nsmap[x].srclist (and other 183 * growing arrays). 184 */ 185 #define NSELEMSPERCHUNK 8 186 187 /* 188 * Dynamically growable arrays are used for lists of databases, sources, 189 * and modules. The following "vector" API is used to isolate the 190 * common operations. 191 */ 192 typedef void (*_nsvect_free_elem)(void *); 193 194 static void * 195 _nsvect_append(const void *elem, void *vec, u_int *count, size_t esize) 196 { 197 void *p; 198 199 if ((*count % NSELEMSPERCHUNK) == 0) { 200 p = realloc(vec, (*count + NSELEMSPERCHUNK) * esize); 201 if (p == NULL) 202 return (NULL); 203 vec = p; 204 } 205 memmove((void *)(((uintptr_t)vec) + (*count * esize)), elem, esize); 206 (*count)++; 207 return (vec); 208 } 209 210 static void * 211 _nsvect_elem(u_int i, void *vec, u_int count, size_t esize) 212 { 213 214 if (i < count) 215 return ((void *)((uintptr_t)vec + (i * esize))); 216 else 217 return (NULL); 218 } 219 220 static void 221 _nsvect_free(void *vec, u_int *count, size_t esize, _nsvect_free_elem free_elem) 222 { 223 void *elem; 224 u_int i; 225 226 for (i = 0; i < *count; i++) { 227 elem = _nsvect_elem(i, vec, *count, esize); 228 if (elem != NULL) 229 (*free_elem)(elem); 230 } 231 if (vec != NULL) 232 free(vec); 233 *count = 0; 234 } 235 #define _NSVECT_FREE(v, c, s, f) \ 236 do { \ 237 _nsvect_free((v), (c), (s), (f)); \ 238 (v) = NULL; \ 239 } while (/*CONSTCOND*/0) 240 241 static int 242 _nsdbtcmp(const void *a, const void *b) 243 { 244 245 return (strcasecmp(((const ns_dbt *)a)->name, 246 ((const ns_dbt *)b)->name)); 247 } 248 249 static int 250 _nsmodcmp(const void *a, const void *b) 251 { 252 253 return (strcasecmp(((const ns_mod *)a)->name, 254 ((const ns_mod *)b)->name)); 255 } 256 257 static int 258 _nsmtabcmp(const void *a, const void *b) 259 { 260 int cmp; 261 262 cmp = strcmp(((const ns_mtab *)a)->name, 263 ((const ns_mtab *)b)->name); 264 if (cmp) 265 return (cmp); 266 267 return (strcasecmp(((const ns_mtab *)a)->database, 268 ((const ns_mtab *)b)->database)); 269 } 270 271 static void 272 _nsmodfree(ns_mod *mod) 273 { 274 275 free(__UNCONST(mod->name)); 276 if (mod->handle == NULL) 277 return; 278 if (mod->unregister != NULL) 279 (*mod->unregister)(mod->mtab, mod->mtabsize); 280 #ifdef __ELF__ 281 if (mod->handle != _nsbuiltin) 282 (void) dlclose(mod->handle); 283 #endif /* __ELF__ */ 284 } 285 286 /* 287 * Load a built-in or dyanamically linked module. If the `reg_fn' 288 * argument is non-NULL, assume a built-in module and use `reg_fn' 289 * to register it. Otherwise, search for a dynamic nsswitch module. 290 */ 291 static int 292 _nsloadmod(const char *source, nss_module_register_fn reg_fn) 293 { 294 #ifdef __ELF__ 295 char buf[PATH_MAX]; 296 #endif 297 ns_mod mod, *new; 298 299 memset(&mod, 0, sizeof(mod)); 300 mod.name = strdup(source); 301 if (mod.name == NULL) 302 return (-1); 303 304 if (reg_fn != NULL) { 305 /* 306 * The placeholder is required, as a NULL handle 307 * represents an invalid module. 308 */ 309 mod.handle = _nsbuiltin; 310 } else if (!is_dynamic()) { 311 goto out; 312 } else { 313 #ifdef __ELF__ 314 if (snprintf(buf, sizeof(buf), "nss_%s.so.%d", mod.name, 315 NSS_MODULE_INTERFACE_VERSION) >= (int)sizeof(buf)) 316 goto out; 317 mod.handle = dlopen(buf, RTLD_LOCAL | RTLD_LAZY); 318 if (mod.handle == NULL) { 319 #ifdef _NSS_DEBUG 320 /* 321 * This gets pretty annoying, since the built-in 322 * sources are not yet modules. 323 */ 324 /* XXX log some error? */ 325 #endif 326 goto out; 327 } 328 reg_fn = (nss_module_register_fn) dlsym(mod.handle, 329 "nss_module_register"); 330 if (reg_fn == NULL) { 331 (void) dlclose(mod.handle); 332 mod.handle = NULL; 333 /* XXX log some error? */ 334 goto out; 335 } 336 #else /* ! __ELF__ */ 337 mod.handle = NULL; 338 #endif /* __ELF__ */ 339 } 340 mod.mtab = (*reg_fn)(mod.name, &mod.mtabsize, &mod.unregister); 341 if (mod.mtab == NULL || mod.mtabsize == 0) { 342 #ifdef __ELF__ 343 if (mod.handle != _nsbuiltin) 344 (void) dlclose(mod.handle); 345 #endif /* __ELF__ */ 346 mod.handle = NULL; 347 /* XXX log some error? */ 348 goto out; 349 } 350 if (mod.mtabsize > 1) 351 qsort(mod.mtab, mod.mtabsize, sizeof(mod.mtab[0]), 352 _nsmtabcmp); 353 out: 354 new = _nsvect_append(&mod, _nsmod, &_nsmodsize, sizeof(*_nsmod)); 355 if (new == NULL) { 356 _nsmodfree(&mod); 357 return (-1); 358 } 359 _nsmod = new; 360 /* _nsmodsize already incremented */ 361 362 qsort(_nsmod, _nsmodsize, sizeof(*_nsmod), _nsmodcmp); 363 return (0); 364 } 365 366 static void 367 _nsloadbuiltin(void) 368 { 369 370 /* Do nothing, for now. */ 371 } 372 373 int 374 _nsdbtaddsrc(ns_dbt *dbt, const ns_src *src) 375 { 376 void *new; 377 const ns_mod *mod; 378 ns_mod modkey; 379 380 _DIAGASSERT(dbt != NULL); 381 _DIAGASSERT(src != NULL); 382 383 new = _nsvect_append(src, dbt->srclist, &dbt->srclistsize, 384 sizeof(*src)); 385 if (new == NULL) 386 return (-1); 387 dbt->srclist = new; 388 /* dbt->srclistsize already incremented */ 389 390 modkey.name = src->name; 391 mod = bsearch(&modkey, _nsmod, _nsmodsize, sizeof(*_nsmod), 392 _nsmodcmp); 393 if (mod == NULL) 394 return (_nsloadmod(src->name, NULL)); 395 396 return (0); 397 } 398 399 void 400 _nsdbtdump(const ns_dbt *dbt) 401 { 402 unsigned int i; 403 404 _DIAGASSERT(dbt != NULL); 405 406 printf("%s (%d source%s):", dbt->name, dbt->srclistsize, 407 dbt->srclistsize == 1 ? "" : "s"); 408 for (i = 0; i < dbt->srclistsize; i++) { 409 printf(" %s", dbt->srclist[i].name); 410 if (!(dbt->srclist[i].flags & 411 (NS_UNAVAIL|NS_NOTFOUND|NS_TRYAGAIN)) && 412 (dbt->srclist[i].flags & NS_SUCCESS)) 413 continue; 414 printf(" ["); 415 if (!(dbt->srclist[i].flags & NS_SUCCESS)) 416 printf(" SUCCESS=continue"); 417 if (dbt->srclist[i].flags & NS_UNAVAIL) 418 printf(" UNAVAIL=return"); 419 if (dbt->srclist[i].flags & NS_NOTFOUND) 420 printf(" NOTFOUND=return"); 421 if (dbt->srclist[i].flags & NS_TRYAGAIN) 422 printf(" TRYAGAIN=return"); 423 printf(" ]"); 424 } 425 printf("\n"); 426 } 427 428 static void 429 _nssrclist_free(ns_src **src, u_int srclistsize) 430 { 431 u_int i; 432 433 for (i = 0; i < srclistsize; i++) { 434 if ((*src)[i].name != NULL) 435 free(__UNCONST((*src)[i].name)); 436 } 437 free(*src); 438 *src = NULL; 439 } 440 441 static void 442 _nsdbtfree(ns_dbt *dbt) 443 { 444 445 _nssrclist_free(&dbt->srclist, dbt->srclistsize); 446 if (dbt->name != NULL) 447 free(__UNCONST(dbt->name)); 448 } 449 450 int 451 _nsdbtput(const ns_dbt *dbt) 452 { 453 ns_dbt *p; 454 void *new; 455 u_int i; 456 457 _DIAGASSERT(dbt != NULL); 458 459 for (i = 0; i < _nsmapsize; i++) { 460 p = _nsvect_elem(i, _nsmap, _nsmapsize, sizeof(*_nsmap)); 461 if (strcasecmp(dbt->name, p->name) == 0) { 462 /* overwrite existing entry */ 463 if (p->srclist != NULL) 464 _nssrclist_free(&p->srclist, p->srclistsize); 465 memmove(p, dbt, sizeof(*dbt)); 466 return (0); 467 } 468 } 469 new = _nsvect_append(dbt, _nsmap, &_nsmapsize, sizeof(*_nsmap)); 470 if (new == NULL) 471 return (-1); 472 _nsmap = new; 473 /* _nsmapsize already incremented */ 474 475 return (0); 476 } 477 478 /* 479 * This function is called each time nsdispatch() is called. If this 480 * is the first call, or if the configuration has changed, (re-)prepare 481 * the global data used by NSS. 482 */ 483 static int 484 _nsconfigure(void) 485 { 486 #ifdef _REENTRANT 487 static mutex_t _nsconflock = MUTEX_INITIALIZER; 488 #endif 489 static time_t _nsconfmod; 490 struct stat statbuf; 491 492 mutex_lock(&_nsconflock); 493 494 if (stat(_PATH_NS_CONF, &statbuf) == -1) { 495 /* 496 * No nsswitch.conf; just use whatever configuration we 497 * currently have, or fall back on the defaults specified 498 * by the caller. 499 */ 500 mutex_unlock(&_nsconflock); 501 return (0); 502 } 503 504 if (statbuf.st_mtime <= _nsconfmod) { 505 /* Internal state is up-to-date with nsswitch.conf. */ 506 mutex_unlock(&_nsconflock); 507 return (0); 508 } 509 510 /* 511 * Ok, we've decided we need to update the nsswitch configuration 512 * structures. Acquire a write-lock on _nslock while continuing 513 * to hold _nsconflock. Acquiring a write-lock blocks while 514 * waiting for other threads already holding a read-lock to clear. 515 * We hold _nsconflock for the duration, and update the time stamp 516 * at the end of the update operation, at which time we release 517 * both locks. 518 */ 519 rwlock_wrlock(&_nslock); 520 521 _nsyyin = fopen(_PATH_NS_CONF, "r"); 522 if (_nsyyin == NULL) { 523 /* 524 * Unable to open nsswitch.conf; behave as though the 525 * stat() above failed. Even though we have already 526 * updated _nsconfmod, if the file reappears, the 527 * mtime will change. 528 */ 529 goto out; 530 } 531 532 _NSVECT_FREE(_nsmap, &_nsmapsize, sizeof(*_nsmap), 533 (_nsvect_free_elem) _nsdbtfree); 534 _NSVECT_FREE(_nsmod, &_nsmodsize, sizeof(*_nsmod), 535 (_nsvect_free_elem) _nsmodfree); 536 537 _nsloadbuiltin(); 538 539 _nsyyparse(); 540 (void) fclose(_nsyyin); 541 if (_nsmapsize != 0) 542 qsort(_nsmap, _nsmapsize, sizeof(*_nsmap), _nsdbtcmp); 543 544 _nsconfmod = statbuf.st_mtime; 545 546 out: 547 rwlock_unlock(&_nslock); 548 mutex_unlock(&_nsconflock); 549 return (0); 550 } 551 552 static nss_method 553 _nsmethod(const char *source, const char *database, const char *method, 554 const ns_dtab disp_tab[], void **cb_data) 555 { 556 int curdisp; 557 ns_mod *mod, modkey; 558 ns_mtab *mtab, mtabkey; 559 560 if (disp_tab != NULL) { 561 for (curdisp = 0; disp_tab[curdisp].src != NULL; curdisp++) { 562 if (strcasecmp(source, disp_tab[curdisp].src) == 0) { 563 *cb_data = disp_tab[curdisp].cb_data; 564 return (disp_tab[curdisp].callback); 565 } 566 } 567 } 568 569 modkey.name = source; 570 mod = bsearch(&modkey, _nsmod, _nsmodsize, sizeof(*_nsmod), 571 _nsmodcmp); 572 if (mod != NULL && mod->handle != NULL) { 573 mtabkey.database = database; 574 mtabkey.name = method; 575 mtab = bsearch(&mtabkey, mod->mtab, mod->mtabsize, 576 sizeof(mod->mtab[0]), _nsmtabcmp); 577 if (mtab != NULL) { 578 *cb_data = mtab->mdata; 579 return (mtab->method); 580 } 581 } 582 583 *cb_data = NULL; 584 return (NULL); 585 } 586 587 int 588 /*ARGSUSED*/ 589 nsdispatch(void *retval, const ns_dtab disp_tab[], const char *database, 590 const char *method, const ns_src defaults[], ...) 591 { 592 static int _nsdispatching; 593 #ifdef _REENTRANT 594 struct _ns_drec drec, *ldrec; 595 #endif 596 va_list ap; 597 int i, result; 598 ns_dbt key; 599 const ns_dbt *dbt; 600 const ns_src *srclist; 601 int srclistsize; 602 nss_method cb; 603 void *cb_data; 604 605 /* retval may be NULL */ 606 /* disp_tab may be NULL */ 607 _DIAGASSERT(database != NULL); 608 _DIAGASSERT(method != NULL); 609 _DIAGASSERT(defaults != NULL); 610 if (database == NULL || method == NULL || defaults == NULL) 611 return (NS_UNAVAIL); 612 613 /* 614 * In both the threaded and non-threaded cases, avoid reloading 615 * the configuration if the current thread is already running 616 * nsdispatch() (i.e. recursive call). 617 * 618 * In the non-threaded case, this avoids changing the data structures 619 * while we're using them. 620 * 621 * In the threaded case, this avoids trying to take a write lock 622 * while the current thread holds a read lock (which would result 623 * in deadlock). 624 */ 625 #ifdef _REENTRANT 626 if (__isthreaded) { 627 drec.thr = thr_self(); 628 mutex_lock(&_ns_drec_lock); 629 LIST_FOREACH(ldrec, &_ns_drec, list) { 630 if (ldrec->thr == drec.thr) 631 break; 632 } 633 LIST_INSERT_HEAD(&_ns_drec, &drec, list); 634 mutex_unlock(&_ns_drec_lock); 635 if (ldrec == NULL && _nsconfigure()) { 636 mutex_lock(&_ns_drec_lock); 637 LIST_REMOVE(&drec, list); 638 mutex_unlock(&_ns_drec_lock); 639 return (NS_UNAVAIL); 640 } 641 } else 642 #endif /* _REENTRANT */ 643 if (_nsdispatching++ == 0 && _nsconfigure()) { 644 _nsdispatching--; 645 return (NS_UNAVAIL); 646 } 647 648 rwlock_rdlock(&_nslock); 649 650 key.name = database; 651 dbt = bsearch(&key, _nsmap, _nsmapsize, sizeof(*_nsmap), _nsdbtcmp); 652 if (dbt != NULL) { 653 srclist = dbt->srclist; 654 srclistsize = dbt->srclistsize; 655 } else { 656 srclist = defaults; 657 srclistsize = 0; 658 while (srclist[srclistsize].name != NULL) 659 srclistsize++; 660 } 661 result = 0; 662 663 for (i = 0; i < srclistsize; i++) { 664 cb = _nsmethod(srclist[i].name, database, method, 665 disp_tab, &cb_data); 666 result = 0; 667 if (cb != NULL) { 668 va_start(ap, defaults); 669 result = (*cb)(retval, cb_data, ap); 670 va_end(ap); 671 if (defaults[0].flags & NS_FORCEALL) 672 continue; 673 if (result & srclist[i].flags) 674 break; 675 } 676 } 677 result &= NS_STATUSMASK; /* clear private flags in result */ 678 679 rwlock_unlock(&_nslock); 680 681 #ifdef _REENTRANT 682 if (__isthreaded) { 683 mutex_lock(&_ns_drec_lock); 684 LIST_REMOVE(&drec, list); 685 mutex_unlock(&_ns_drec_lock); 686 } else 687 #endif /* _REENTRANT */ 688 _nsdispatching--; 689 690 return (result ? result : NS_NOTFOUND); 691 } 692