1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include "lint.h" 30 #include <mtlib.h> 31 #include <sys/types.h> 32 #include <errno.h> 33 #include <pwd.h> 34 #include <nss_dbdefs.h> 35 #include <stdio.h> 36 #include <string.h> 37 #include <synch.h> 38 #include <sys/param.h> 39 #include <fcntl.h> 40 #include <unistd.h> 41 #include <stdlib.h> 42 #include <getxby_door.h> 43 #include <sys/door.h> 44 #include <procfs.h> 45 #include <door.h> 46 #include <sys/mman.h> 47 #include "libc.h" 48 #include "tsd.h" 49 #include "base_conversion.h" 50 51 /* nss<->door hints */ 52 static mutex_t hints_lock = DEFAULTMUTEX; 53 static size_t door_bsize = 0; 54 static size_t door_nbsize = 0; 55 static int proc_is_cache = -1; 56 57 /* library<->nscd door interaction apis */ 58 59 /* 60 * 61 * Routine that actually performs the door call. 62 * Note that we cache a file descriptor. We do 63 * the following to prevent disasters: 64 * 65 * 1) Never use 0,1 or 2; if we get this from the open 66 * we dup it upwards. 67 * 68 * 2) Set the close on exec flags so descriptor remains available 69 * to child processes. 70 * 71 * 3) Verify that the door is still the same one we had before 72 * by using door_info on the client side. 73 * 74 * Note that we never close the file descriptor if it isn't one 75 * we allocated; we check this with door info. The rather tricky 76 * logic is designed to be fast in the normal case (fd is already 77 * allocated and is ok) while handling the case where the application 78 * closed it underneath us or where the nscd dies or re-execs itself 79 * and we're a multi-threaded application. Note that we cannot protect 80 * the application if it closes the fd and it is multi-threaded. 81 * 82 * int _nsc_trydoorcall(void *dptr, size_t *bufsize, size_t *actualsize); 83 * 84 * *dptr IN: points to arg buffer OUT: points to results buffer 85 * *bufsize IN: overall size of buffer OUT: overall size of buffer 86 * *actualsize IN: size of call data OUT: size of return data 87 * 88 * Note that *dptr may change if provided space as defined by *bufsize is 89 * inadequate. In this case the door call mmaps more space and places 90 * the answer there and sets dptr to contain a pointer to the space, which 91 * should be freed with munmap. 92 * 93 * Returns 0 if the door call reached the server, -1 if contact was not made. 94 * 95 */ 96 97 /* 98 * Max size for list of db names supported by the private nscd 99 * No implied max here, any size will do, fixed size chosen to 100 * reduce yet another malloc 101 */ 102 103 #define BD_BUFSIZE 1024 104 #define BD_SEP ',' 105 106 typedef struct _nsc_door_t { 107 int doorfd; 108 mutex_t door_lock; 109 door_info_t doori; 110 } nsc_door_t; 111 112 static nsc_door_t nsc_door[2] = { 113 { -1, DEFAULTMUTEX, { 0 } }, /* front (fattached) door */ 114 { -1, DEFAULTMUTEX, { 0 } }, /* back (private) door */ 115 }; 116 117 /* assumed to be locked by using nsc_door[1] mutex */ 118 static char *nsc_db_buf = NULL; 119 static char **nsc_db_list = NULL; 120 121 /* 122 * Check for a valid and matching db in the list. 123 * assume list is in the locked state. 124 */ 125 126 static int 127 _nsc_use_backdoor(char *db) 128 { 129 char **ndb; 130 131 if (db && nsc_db_buf != NULL && nsc_db_list != NULL) { 132 for (ndb = nsc_db_list; *ndb; ndb++) { 133 if (strcmp(db, *ndb) == 0) 134 return (1); 135 } 136 } 137 return (0); 138 } 139 140 /* 141 * flush private db lists 142 */ 143 static void 144 _nsc_flush_private_db() 145 { 146 if (nsc_db_buf != NULL) { 147 libc_free((void *)nsc_db_buf); 148 nsc_db_buf = NULL; 149 } 150 if (nsc_db_list != NULL) { 151 libc_free((void *)nsc_db_list); 152 nsc_db_list = NULL; 153 } 154 } 155 156 /* 157 * init/update nsc_db_buf given buff containing list of 158 * db's to be processed by a private nscd. 159 * This function assumes it has a well formed string from nscd. 160 */ 161 162 static int 163 _nsc_init_private_db(char *dblist) 164 { 165 char *cp, **lp; 166 int buflen = 0; 167 int arrlen = 0; 168 169 if (dblist == NULL) 170 return (0); 171 172 /* reset db list */ 173 _nsc_flush_private_db(); 174 175 /* rebuild fresh list */ 176 buflen = strlen(dblist) + 1; 177 for (cp = dblist; *cp; cp++) 178 if (*cp == BD_SEP) 179 arrlen++; 180 if (cp == dblist) 181 return (0); 182 arrlen += 2; 183 nsc_db_buf = (char *)libc_malloc(buflen); 184 if (nsc_db_buf == (char *)NULL) 185 return (0); 186 nsc_db_list = (char **)libc_malloc(arrlen * sizeof (char *)); 187 if (nsc_db_list == (char **)NULL) { 188 libc_free((void *)nsc_db_buf); 189 nsc_db_buf = NULL; 190 return (0); 191 } 192 (void) memcpy(nsc_db_buf, dblist, buflen); 193 lp = nsc_db_list; 194 *lp++ = nsc_db_buf; 195 for (cp = nsc_db_buf; *cp; ) { 196 if (*cp == BD_SEP) { 197 *cp++ = '\0'; 198 *lp++ = cp; 199 } else 200 cp++; 201 } 202 *lp = NULL; 203 return (1); 204 } 205 206 /* 207 * _nsc_initdoor_fp attempts to validate the given door and 208 * confirm that it is still available for use. The options are: 209 * Front door: 210 * If it's not open, attempt to open or error 211 * If it's open attempt to validate. 212 * If it's not validatable, reset fd and try again. 213 * Other wise it open and validated, return success 214 * Per user (back) door: 215 * This door is passed to the client through th front door 216 * attempt to validate it. If it can't be validated, it 217 * must be reset. Then send a NSS_ALTRESET error, so nscd can 218 * forward another fd if desired. 219 */ 220 221 static nss_status_t 222 _nsc_initdoor_fp(nsc_door_t *dp) 223 { 224 225 door_info_t my_door; 226 227 if (dp == NULL) { 228 errno = ENOTCONN; 229 return (NSS_ERROR); 230 } 231 232 /* 233 * the first time in we try and open and validate the front door. 234 * A front door request may return an alternate private back door 235 * that the client should use instead. 236 * 237 * To validate a door the door must have been created with 238 * the name service door cookie. The front door is file 239 * attached, owned by root and readonly by user, group and 240 * other. If any of these validations fail we refuse to use 241 * the door. A back door is delivered from the front door 242 * via a door_desc_t, and have the same cooke notification. 243 */ 244 245 lmutex_lock(&dp->door_lock); 246 247 try_again: 248 249 if (dp->doorfd == -1 && dp == &nsc_door[0]) { /* open front door */ 250 int tbc[3]; 251 int i; 252 253 dp->doorfd = open64(NAME_SERVICE_DOOR, O_RDONLY, 0); 254 if (dp->doorfd == -1) { 255 lmutex_unlock(&dp->door_lock); 256 return (NSS_ERROR); 257 } 258 259 /* 260 * dup up the file descriptor if we have 0 - 2 261 * to avoid problems with shells stdin/out/err 262 */ 263 i = 0; 264 265 while (dp->doorfd < 3) { /* we have a reserved fd */ 266 tbc[i++] = dp->doorfd; 267 if ((dp->doorfd = dup(dp->doorfd)) < 0) { 268 while (i--) 269 (void) close(tbc[i]); 270 dp->doorfd = -1; 271 lmutex_unlock(&dp->door_lock); 272 return (NSS_ERROR); 273 } 274 } 275 276 while (i--) 277 (void) close(tbc[i]); 278 279 /* 280 * mark this door descriptor as close on exec 281 */ 282 (void) fcntl(dp->doorfd, F_SETFD, FD_CLOEXEC); 283 if (__door_info(dp->doorfd, &dp->doori) < 0 || 284 (dp->doori.di_attributes & DOOR_REVOKED) || 285 dp->doori.di_data != (uintptr_t)NAME_SERVICE_DOOR_COOKIE) { 286 /* 287 * we should close doorfd because we just opened it 288 */ 289 (void) close(dp->doorfd); 290 dp->doorfd = -1; 291 (void) memset((void *)&dp->doori, 292 '\0', sizeof (door_info_t)); 293 lmutex_unlock(&dp->door_lock); 294 errno = ECONNREFUSED; 295 return (NSS_ERROR); 296 } 297 } else { 298 if (__door_info(dp->doorfd, &my_door) < 0 || 299 my_door.di_data != (uintptr_t)NAME_SERVICE_DOOR_COOKIE || 300 my_door.di_uniquifier != dp->doori.di_uniquifier) { 301 /* 302 * don't close it - 303 * someone else has clobbered fd 304 */ 305 dp->doorfd = -1; 306 (void) memset((void *)&dp->doori, 307 '\0', sizeof (door_info_t)); 308 if (dp == &nsc_door[1]) { /* reset back door */ 309 /* flush invalid db list */ 310 _nsc_flush_private_db(); 311 lmutex_unlock(&dp->door_lock); 312 return (NSS_ALTRESET); 313 } 314 goto try_again; 315 } 316 317 if (my_door.di_attributes & DOOR_REVOKED) { 318 (void) close(dp->doorfd); /* nscd exited .... */ 319 dp->doorfd = -1; /* try and restart connection */ 320 (void) memset((void *)&dp->doori, 321 '\0', sizeof (door_info_t)); 322 if (dp == &nsc_door[1]) { /* back door reset */ 323 /* flush invalid db list */ 324 _nsc_flush_private_db(); 325 lmutex_unlock(&dp->door_lock); 326 return (NSS_ALTRESET); 327 } 328 goto try_again; 329 } 330 } 331 332 lmutex_unlock(&dp->door_lock); 333 return (NSS_SUCCESS); 334 } 335 336 /* 337 * Try the door request once only, to the specified connection. 338 * return the results or error. 339 */ 340 341 static nss_status_t 342 _nsc_try1door(nsc_door_t *dp, void **dptr, size_t *ndata, 343 size_t *adata, int *pdesc) 344 { 345 door_arg_t param; 346 int ret; 347 nss_pheader_t *rp; 348 349 ret = _nsc_initdoor_fp(dp); 350 if (ret != NSS_SUCCESS) 351 return (ret); 352 353 param.rbuf = (char *)*dptr; 354 param.rsize = *ndata; 355 param.data_ptr = (char *)*dptr; 356 param.data_size = *adata; 357 param.desc_ptr = NULL; 358 param.desc_num = 0; 359 ret = __door_call(dp->doorfd, ¶m); 360 if (ret < 0) { 361 return (NSS_ERROR); 362 } 363 *adata = param.data_size; 364 *ndata = param.rsize; 365 *dptr = (void *)param.data_ptr; 366 rp = (nss_pheader_t *)((void *)param.rbuf); 367 if (pdesc != NULL && rp && rp->p_status == NSS_ALTRETRY && 368 param.desc_ptr != NULL && param.desc_num > 0) { 369 if ((param.desc_ptr->d_attributes & DOOR_DESCRIPTOR) && 370 param.desc_ptr->d_data.d_desc.d_descriptor >= 0 && 371 param.desc_ptr->d_data.d_desc.d_id != 0) { 372 /* have an alt descriptor */ 373 *pdesc = param.desc_ptr->d_data.d_desc.d_descriptor; 374 /* got a NSS_ALTRETRY command */ 375 return (NSS_ALTRETRY); 376 } 377 errno = EINVAL; 378 return (NSS_ERROR); /* other error? */ 379 } 380 if (*adata == 0 || *dptr == NULL) { 381 errno = ENOTCONN; 382 return (NSS_ERROR); 383 } 384 385 if (rp->p_status == NSS_ALTRESET || 386 rp->p_status == NSS_ALTRETRY || 387 rp->p_status == NSS_TRYLOCAL) 388 return (rp->p_status); 389 390 return (NSS_SUCCESS); 391 } 392 393 /* 394 * Backwards compatible API 395 */ 396 397 nss_status_t 398 _nsc_trydoorcall(void **dptr, size_t *ndata, size_t *adata) 399 { 400 return (_nsc_try1door(&nsc_door[0], dptr, ndata, adata, NULL)); 401 } 402 403 /* 404 * Send the request to the designated door, based on the supplied db 405 * Retry on the alternate door fd if possible. 406 */ 407 408 nss_status_t 409 _nsc_trydoorcall_ext(void **dptr, size_t *ndata, size_t *adata) 410 { 411 int ret = NSS_ALTRETRY; 412 nsc_door_t *frontd = &nsc_door[0]; 413 nsc_door_t *backd = &nsc_door[1]; 414 int fd; 415 416 nss_pheader_t *ph, ph_save; 417 char *dbl; 418 char *db = NULL; 419 nss_dbd_t *dbd; 420 int fb2frontd = 0; 421 int reset_frontd = 0; 422 size_t ndata_save = *ndata, adata_save = *adata; 423 void *dptr_save = *dptr; 424 425 ph = (nss_pheader_t *)*dptr; 426 dbd = (nss_dbd_t *)((void *)((char *)ph + ph->dbd_off)); 427 if (dbd->o_name != 0) 428 db = (char *)dbd + dbd->o_name; 429 430 /* 431 * save away a copy of the header, in case the request needs 432 * to be sent to nscd more than once. In that case, this 433 * original header can be copied back to the door buffer 434 * to replace the possibly changed header 435 */ 436 ph_save = *ph; 437 438 while (ret == NSS_ALTRETRY || ret == NSS_ALTRESET) { 439 /* try private (back) door first if it exists and applies */ 440 if (db != NULL && backd->doorfd > 0 && fb2frontd == 0 && 441 _nsc_use_backdoor(db)) { 442 ret = _nsc_try1door(backd, dptr, ndata, adata, NULL); 443 if (ret == NSS_ALTRESET) { 444 /* 445 * received NSS_ALTRESET command, 446 * retry on front door 447 */ 448 lmutex_lock(&backd->door_lock); 449 backd->doorfd = -1; 450 (void) memset((void *)&backd->doori, 451 '\0', sizeof (door_info_t)); 452 /* flush now invalid db list */ 453 _nsc_flush_private_db(); 454 lmutex_unlock(&backd->door_lock); 455 continue; 456 } else if (ret == NSS_ALTRETRY) { 457 /* 458 * received NSS_ALTRETRY command, 459 * fall back and retry on front door 460 */ 461 fb2frontd = 1; 462 if (*dptr != dptr_save) 463 (void) munmap((void *)*dptr, *ndata); 464 465 /* 466 * restore the buffer size and header 467 * data so that the front door will 468 * see the original request 469 */ 470 *ndata = ndata_save; 471 *adata = adata_save; 472 *dptr = dptr_save; 473 ph = (nss_pheader_t *)*dptr; 474 *ph = ph_save; 475 /* 476 * tell the front door server, this is 477 * a fallback call 478 */ 479 ph->p_status = NSS_ALTRETRY; 480 continue; 481 } 482 483 /* return the result or error */ 484 break; 485 } 486 487 /* try the front door */ 488 fd = -1; 489 ret = _nsc_try1door(frontd, dptr, ndata, adata, &fd); 490 491 if (ret != NSS_ALTRETRY) { 492 /* 493 * got a success or failure result. 494 * but front door should never send NSS_ALTRESET 495 */ 496 if (ret == NSS_ALTRESET) 497 /* reset the front door */ 498 reset_frontd = 1; 499 else 500 /* 501 * not NSS_ALTRETRY and not NSS_ALTRESET 502 * return the result or error 503 */ 504 break; 505 } else if (fb2frontd == 1) { 506 /* 507 * front door should never send NSS_ALTRETRY 508 * in a fallback call. Reset the front door. 509 */ 510 reset_frontd = 1; 511 } 512 513 if (reset_frontd == 1) { 514 lmutex_lock(&frontd->door_lock); 515 frontd->doorfd = -1; 516 (void) memset((void *)&frontd->doori, 517 '\0', sizeof (door_info_t)); 518 lmutex_unlock(&frontd->door_lock); 519 /* error out */ 520 ret = NSS_ERROR; 521 break; 522 } 523 524 /* process NSS_ALTRETRY request from front door */ 525 if (fd < 0) 526 continue; /* no new door given, try again */ 527 528 /* update and try alternate door */ 529 lmutex_lock(&backd->door_lock); 530 if (backd->doorfd >= 0) { 531 /* unexpected open alt door - clean up, continue */ 532 _nsc_flush_private_db(); 533 (void) close(backd->doorfd); 534 } 535 536 /* set up back door fd */ 537 backd->doorfd = fd; 538 539 /* set up back door db list */ 540 ph = (nss_pheader_t *)*dptr; 541 dbl = ((char *)ph) + ph->data_off; 542 543 if (_nsc_init_private_db(dbl) == 0) { 544 /* could not init db list, try again */ 545 (void) close(backd->doorfd); 546 backd->doorfd = -1; 547 lmutex_unlock(&backd->door_lock); 548 continue; 549 } 550 if (door_info(backd->doorfd, &backd->doori) < 0 || 551 (backd->doori.di_attributes & DOOR_REVOKED) || 552 backd->doori.di_data != 553 (uintptr_t)NAME_SERVICE_DOOR_COOKIE) { 554 /* doorfd bad, or must not really be open */ 555 (void) close(backd->doorfd); 556 backd->doorfd = -1; 557 (void) memset((void *)&backd->doori, 558 '\0', sizeof (door_info_t)); 559 } 560 (void) fcntl(backd->doorfd, F_SETFD, FD_CLOEXEC); 561 lmutex_unlock(&backd->door_lock); 562 /* NSS_ALTRETRY new back door */ 563 if (*dptr != dptr_save) 564 (void) munmap((void *)*dptr, *ndata); 565 566 /* 567 * restore the buffer size and header 568 * data so that the back door will 569 * see the original request 570 */ 571 *ndata = ndata_save; 572 *adata = adata_save; 573 *dptr = dptr_save; 574 ph = (nss_pheader_t *)*dptr; 575 *ph = ph_save; 576 } 577 return (ret); 578 } 579 580 /* 581 * Get the current (but growable) buffer size for a NSS2 packet. 582 * Heuristic algorithm used: 583 * 1) Make sure it's at least NSS_BUFLEN_DOOR in length (16k default) 584 * 2) if an incoming user buffer is > larger than the current size 585 * Make the buffer at least NSS_BUFLEN_DOOR/2+user buffer size 586 * This should account for any reasonable nss_pheader, keys 587 * extended area etc. 588 * 3) keep the prototype/debugging (private)NSS_BUFLEN option 589 * to change any preconfigured value if needed(?) 590 */ 591 592 static size_t 593 _nsc_getdoorbsize(size_t min_size) 594 { 595 if (!door_bsize) { 596 lmutex_lock(&hints_lock); 597 if (!door_bsize) { 598 /* future work - get nscd hint & use hint size */ 599 door_bsize = ROUND_UP(door_bsize, NSS_BUFSIZ); 600 if (door_bsize < NSS_BUFLEN_DOOR) { 601 door_bsize = NSS_BUFLEN_DOOR; 602 } 603 } 604 lmutex_unlock(&hints_lock); 605 } 606 if (min_size && door_bsize < (min_size + NSS_BUFLEN_DOOR/2)) { 607 lmutex_lock(&hints_lock); 608 if (door_bsize < (min_size + NSS_BUFLEN_DOOR/2)) { 609 min_size += NSS_BUFLEN_DOOR; 610 door_bsize = ROUND_UP(min_size, NSS_BUFSIZ); 611 } 612 lmutex_unlock(&hints_lock); 613 } 614 return (door_bsize); 615 } 616 617 static void 618 _nsc_freedbuf(void *arg) 619 { 620 nss_XbyY_buf_t *tsdbuf = arg; 621 622 if (tsdbuf != NULL && tsdbuf->buffer != NULL) { 623 lfree(tsdbuf->buffer, (size_t)tsdbuf->buflen); 624 tsdbuf->result = NULL; 625 tsdbuf->buffer = NULL; 626 tsdbuf->buflen = 0; 627 } 628 } 629 630 /* 631 * _nsc_getdoorbuf - return the client side per thread door buffer 632 * Elsewhere, it is assumed that the header is 0'd upon return from here. 633 */ 634 635 int 636 _nsc_getdoorbuf(void **doorptr, size_t *bufsize) 637 { 638 nss_XbyY_buf_t *tsdbuf; 639 char *bp; 640 size_t dsize; 641 642 if (doorptr == NULL || bufsize == NULL) 643 return (-1); 644 645 /* Get thread specific pointer to door buffer */ 646 tsdbuf = tsdalloc(_T_DOORBUF, sizeof (nss_XbyY_buf_t), _nsc_freedbuf); 647 if (tsdbuf == NULL) 648 return (-1); 649 650 /* if door buffer does not exist create it */ 651 if (tsdbuf->buffer == NULL) { 652 dsize = _nsc_getdoorbsize(*bufsize); 653 654 /* setup a door buffer with a total length of dsize */ 655 bp = lmalloc(dsize); 656 if (bp == NULL) 657 return (-1); 658 tsdbuf->buffer = bp; 659 tsdbuf->buflen = dsize; 660 } else { 661 /* check old buffer size and resize if needed */ 662 if (*bufsize) { 663 dsize = _nsc_getdoorbsize(*bufsize); 664 if (tsdbuf->buflen < dsize) { 665 lfree(tsdbuf->buffer, (size_t)tsdbuf->buflen); 666 bp = lmalloc(dsize); 667 if (bp == NULL) 668 return (-1); 669 tsdbuf->buffer = bp; 670 tsdbuf->buflen = dsize; 671 } 672 } 673 /* freshly malloc'd door bufs are 0'd */ 674 /* 0 header for now. Zero entire buf(?) TDB */ 675 (void) memset((void *)tsdbuf->buffer, 0, 676 (size_t)sizeof (nss_pheader_t)); 677 678 } 679 *doorptr = (void *)tsdbuf->buffer; 680 *bufsize = tsdbuf->buflen; 681 return (0); 682 } 683 684 void 685 _nsc_resizedoorbuf(size_t bsize) 686 { 687 /* signal to update if new door size is desired */ 688 lmutex_lock(&hints_lock); 689 if (bsize > door_bsize && door_nbsize < bsize) 690 door_nbsize = bsize; 691 lmutex_unlock(&hints_lock); 692 } 693 694 /* 695 * Check uid and /proc/PID/psinfo to see if this process is nscd 696 * If it is set the appropriate flags and allow policy reconfiguration. 697 */ 698 int 699 _nsc_proc_is_cache() 700 { 701 psinfo_t pinfo; 702 char fname[128]; 703 int ret; 704 int fd; 705 706 if (proc_is_cache >= 0) 707 return (proc_is_cache); 708 lmutex_lock(&hints_lock); 709 if (proc_is_cache >= 0) { 710 lmutex_unlock(&hints_lock); 711 return (proc_is_cache); 712 } 713 proc_is_cache = 0; 714 /* It can't be nscd if it's not running as root... */ 715 if (getuid() != 0) { 716 lmutex_unlock(&hints_lock); 717 return (0); 718 } 719 ret = snprintf(fname, 128, "/proc/%d/psinfo", getpid()); 720 if (ret > 0 && ret < 128) { 721 if ((fd = open(fname, O_RDONLY)) >= 0) { 722 ret = read(fd, &pinfo, sizeof (psinfo_t)); 723 (void) close(fd); 724 if (ret == sizeof (psinfo_t) && 725 (strcmp(pinfo.pr_fname, "nscd") == 0)) { 726 /* process runs as root and is named nscd */ 727 /* that's good enough for now */ 728 proc_is_cache = 1; 729 } 730 } 731 } 732 lmutex_unlock(&hints_lock); 733 return (proc_is_cache); 734 } 735