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 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * This file defines the domain environment values and the domain 28 * database interface. The database is a single linked list of 29 * structures containing domain type, name and SID information. 30 */ 31 32 #include <sys/types.h> 33 #include <sys/stat.h> 34 #include <sys/list.h> 35 #include <stdio.h> 36 #include <strings.h> 37 #include <string.h> 38 #include <unistd.h> 39 #include <stdlib.h> 40 #include <synch.h> 41 #include <pwd.h> 42 #include <grp.h> 43 #include <assert.h> 44 45 #include <smbsrv/smbinfo.h> 46 #include <smbsrv/string.h> 47 #include <smbsrv/smb_sid.h> 48 #include <smbsrv/libsmb.h> 49 50 #define SMB_DOMAINS_FILE "domains" 51 52 #define SMB_DCACHE_UPDATE_WAIT 45 /* seconds */ 53 54 /* 55 * Domain cache states 56 */ 57 #define SMB_DCACHE_STATE_NONE 0 58 #define SMB_DCACHE_STATE_READY 1 59 #define SMB_DCACHE_STATE_UPDATING 2 60 #define SMB_DCACHE_STATE_DESTROYING 3 61 62 /* 63 * Cache lock modes 64 */ 65 #define SMB_DCACHE_RDLOCK 0 66 #define SMB_DCACHE_WRLOCK 1 67 68 typedef struct smb_domain_cache { 69 list_t dc_cache; 70 rwlock_t dc_cache_lck; 71 mutex_t dc_mtx; 72 cond_t dc_cv; 73 uint32_t dc_state; 74 uint32_t dc_nops; 75 char dc_server[MAXHOSTNAMELEN]; 76 } smb_domain_cache_t; 77 78 static smb_domain_cache_t smb_dcache; 79 80 static uint32_t smb_domain_add(smb_domain_type_t, smb_domain_t *); 81 static uint32_t smb_domain_add_local(void); 82 static uint32_t smb_domain_add_primary(uint32_t); 83 static void smb_domain_unlink(void); 84 85 static void smb_dcache_create(void); 86 static void smb_dcache_destroy(void); 87 static uint32_t smb_dcache_lock(int); 88 static void smb_dcache_unlock(void); 89 static void smb_dcache_remove(smb_domain_t *); 90 static uint32_t smb_dcache_add(smb_domain_t *); 91 static void smb_dcache_getdc(char *, size_t); 92 static void smb_dcache_setdc(char *); 93 static boolean_t smb_dcache_wait(void); 94 static uint32_t smb_dcache_updating(void); 95 static void smb_dcache_ready(void); 96 97 /* 98 * domain cache one time initialization. This function should 99 * only be called during service startup. 100 * 101 * Returns 0 on success and an error code on failure. 102 */ 103 int 104 smb_domain_init(uint32_t secmode) 105 { 106 smb_domain_t di; 107 int rc; 108 109 smb_dcache_create(); 110 111 if ((rc = smb_domain_add_local()) != 0) 112 return (rc); 113 114 smb_domain_set_basic_info(NT_BUILTIN_DOMAIN_SIDSTR, "BUILTIN", "", &di); 115 (void) smb_domain_add(SMB_DOMAIN_BUILTIN, &di); 116 117 return (smb_domain_add_primary(secmode)); 118 } 119 120 /* 121 * Destroys the cache upon service termination 122 */ 123 void 124 smb_domain_fini(void) 125 { 126 smb_dcache_destroy(); 127 smb_domain_unlink(); 128 } 129 130 /* 131 * Add a domain structure to domain cache. There is no checking 132 * for duplicates. 133 */ 134 static uint32_t 135 smb_domain_add(smb_domain_type_t type, smb_domain_t *di) 136 { 137 uint32_t res; 138 139 if ((di == NULL) || (di->di_sid == NULL)) 140 return (SMB_DOMAIN_INVALID_ARG); 141 142 if ((res = smb_dcache_lock(SMB_DCACHE_WRLOCK)) == SMB_DOMAIN_SUCCESS) { 143 di->di_type = type; 144 res = smb_dcache_add(di); 145 smb_dcache_unlock(); 146 } 147 148 return (res); 149 } 150 151 /* 152 * Lookup a domain by its name. The passed name is the NETBIOS or fully 153 * qualified DNS name or non-qualified DNS name. 154 * 155 * If the requested domain is found and given 'di' pointer is not NULL 156 * it'll be filled with the domain information and B_TRUE is returned. 157 * If the caller only needs to check a domain existence it can pass 158 * NULL for 'di' and just check the return value. 159 * 160 * If the domain is not in the cache B_FALSE is returned. 161 */ 162 boolean_t 163 smb_domain_lookup_name(char *name, smb_domain_t *di) 164 { 165 boolean_t found = B_FALSE; 166 smb_domain_t *dcnode; 167 char *p; 168 169 bzero(di, sizeof (smb_domain_t)); 170 171 if (name == NULL || *name == '\0') 172 return (B_FALSE); 173 174 if (smb_dcache_lock(SMB_DCACHE_RDLOCK) != SMB_DOMAIN_SUCCESS) 175 return (B_FALSE); 176 177 dcnode = list_head(&smb_dcache.dc_cache); 178 while (dcnode) { 179 found = (utf8_strcasecmp(dcnode->di_nbname, name) == 0) || 180 (utf8_strcasecmp(dcnode->di_fqname, name) == 0); 181 182 if (found) { 183 if (di) 184 *di = *dcnode; 185 break; 186 } 187 188 if ((p = strchr(dcnode->di_fqname, '.')) != NULL) { 189 *p = '\0'; 190 found = (utf8_strcasecmp(dcnode->di_fqname, name) == 0); 191 *p = '.'; 192 if (found) { 193 if (di) 194 *di = *dcnode; 195 break; 196 } 197 } 198 199 dcnode = list_next(&smb_dcache.dc_cache, dcnode); 200 } 201 202 smb_dcache_unlock(); 203 return (found); 204 } 205 206 /* 207 * Lookup a domain by its SID. 208 * 209 * If the requested domain is found and given 'di' pointer is not NULL 210 * it'll be filled with the domain information and B_TRUE is returned. 211 * If the caller only needs to check a domain existence it can pass 212 * NULL for 'di' and just check the return value. 213 * 214 * If the domain is not in the cache B_FALSE is returned. 215 */ 216 boolean_t 217 smb_domain_lookup_sid(smb_sid_t *sid, smb_domain_t *di) 218 { 219 boolean_t found = B_FALSE; 220 smb_domain_t *dcnode; 221 char sidstr[SMB_SID_STRSZ]; 222 223 bzero(di, sizeof (smb_domain_t)); 224 225 if (sid == NULL) 226 return (B_FALSE); 227 228 smb_sid_tostr(sid, sidstr); 229 230 if (smb_dcache_lock(SMB_DCACHE_RDLOCK) != SMB_DOMAIN_SUCCESS) 231 return (B_FALSE); 232 233 dcnode = list_head(&smb_dcache.dc_cache); 234 while (dcnode) { 235 found = (strcmp(dcnode->di_sid, sidstr) == 0); 236 if (found) { 237 if (di) 238 *di = *dcnode; 239 break; 240 } 241 242 dcnode = list_next(&smb_dcache.dc_cache, dcnode); 243 } 244 245 smb_dcache_unlock(); 246 return (found); 247 } 248 249 /* 250 * Lookup a domain by its type. 251 * 252 * If the requested domain is found and given 'di' pointer is not NULL 253 * it'll be filled with the domain information and B_TRUE is returned. 254 * If the caller only needs to check a domain existence it can pass 255 * NULL for 'di' and just check the return value. 256 * 257 * If the domain is not in the cache B_FALSE is returned. 258 */ 259 boolean_t 260 smb_domain_lookup_type(smb_domain_type_t type, smb_domain_t *di) 261 { 262 boolean_t found = B_FALSE; 263 smb_domain_t *dcnode; 264 265 bzero(di, sizeof (smb_domain_t)); 266 267 if (smb_dcache_lock(SMB_DCACHE_RDLOCK) != SMB_DOMAIN_SUCCESS) 268 return (B_FALSE); 269 270 dcnode = list_head(&smb_dcache.dc_cache); 271 while (dcnode) { 272 if (dcnode->di_type == type) { 273 found = B_TRUE; 274 if (di) 275 *di = *dcnode; 276 break; 277 } 278 279 dcnode = list_next(&smb_dcache.dc_cache, dcnode); 280 } 281 282 smb_dcache_unlock(); 283 return (found); 284 } 285 286 /* 287 * Returns primary domain information plus the name of 288 * the selected domain controller. 289 */ 290 boolean_t 291 smb_domain_getinfo(smb_domainex_t *dxi) 292 { 293 boolean_t success; 294 295 success = smb_domain_lookup_type(SMB_DOMAIN_PRIMARY, &dxi->d_primary); 296 if (success) 297 smb_dcache_getdc(dxi->d_dc, sizeof (dxi->d_dc)); 298 299 return (success); 300 } 301 302 /* 303 * Transfer the cache to updating state. 304 * In this state any request for reading the cache would 305 * be blocked until the update is finished. 306 */ 307 uint32_t 308 smb_domain_start_update(void) 309 { 310 return (smb_dcache_updating()); 311 } 312 313 /* 314 * Transfer the cache from updating to ready state. 315 */ 316 void 317 smb_domain_end_update(void) 318 { 319 smb_dcache_ready(); 320 } 321 322 /* 323 * Updates the cache with given information for the primary 324 * domain, possible trusted domains and the selected domain 325 * controller. 326 * 327 * Before adding the new entries existing entries of type 328 * primary and trusted will be removed from cache. 329 */ 330 void 331 smb_domain_update(smb_domainex_t *dxi) 332 { 333 smb_domain_t *dcnode; 334 int i; 335 336 if (smb_dcache_lock(SMB_DCACHE_WRLOCK) != SMB_DOMAIN_SUCCESS) 337 return; 338 339 dcnode = list_head(&smb_dcache.dc_cache); 340 while (dcnode) { 341 if ((dcnode->di_type == SMB_DOMAIN_PRIMARY) || 342 (dcnode->di_type == SMB_DOMAIN_TRUSTED)) { 343 smb_dcache_remove(dcnode); 344 dcnode = list_head(&smb_dcache.dc_cache); 345 } else { 346 dcnode = list_next(&smb_dcache.dc_cache, dcnode); 347 } 348 } 349 350 if (smb_dcache_add(&dxi->d_primary) == SMB_DOMAIN_SUCCESS) { 351 for (i = 0; i < dxi->d_trusted.td_num; i++) 352 (void) smb_dcache_add(&dxi->d_trusted.td_domains[i]); 353 354 smb_dcache_setdc(dxi->d_dc); 355 } 356 357 smb_dcache_unlock(); 358 } 359 360 /* 361 * Write the list of domains to /var/run/smb/domains. 362 */ 363 void 364 smb_domain_save(void) 365 { 366 char fname[MAXPATHLEN]; 367 char tag; 368 smb_domain_t *domain; 369 FILE *fp; 370 struct passwd *pwd; 371 struct group *grp; 372 uid_t uid; 373 gid_t gid; 374 375 (void) snprintf(fname, MAXPATHLEN, "%s/%s", 376 SMB_VARRUN_DIR, SMB_DOMAINS_FILE); 377 378 if ((fp = fopen(fname, "w")) == NULL) 379 return; 380 381 pwd = getpwnam("root"); 382 grp = getgrnam("sys"); 383 uid = (pwd == NULL) ? 0 : pwd->pw_uid; 384 gid = (grp == NULL) ? 3 : grp->gr_gid; 385 386 (void) lockf(fileno(fp), F_LOCK, 0); 387 (void) fchmod(fileno(fp), 0600); 388 (void) fchown(fileno(fp), uid, gid); 389 390 if (smb_dcache_lock(SMB_DCACHE_RDLOCK) != SMB_DOMAIN_SUCCESS) 391 return; 392 393 domain = list_head(&smb_dcache.dc_cache); 394 while (domain) { 395 switch (domain->di_type) { 396 case SMB_DOMAIN_PRIMARY: 397 tag = '*'; 398 break; 399 400 case SMB_DOMAIN_TRUSTED: 401 case SMB_DOMAIN_UNTRUSTED: 402 tag = '-'; 403 break; 404 405 case SMB_DOMAIN_LOCAL: 406 tag = '.'; 407 break; 408 default: 409 domain = list_next(&smb_dcache.dc_cache, domain); 410 continue; 411 } 412 413 (void) fprintf(fp, "[%c] [%s] [%s]\n", 414 tag, domain->di_nbname, domain->di_sid); 415 416 domain = list_next(&smb_dcache.dc_cache, domain); 417 } 418 419 smb_dcache_unlock(); 420 (void) lockf(fileno(fp), F_ULOCK, 0); 421 (void) fclose(fp); 422 } 423 424 /* 425 * List the domains in /var/run/smb/domains. 426 */ 427 void 428 smb_domain_show(void) 429 { 430 char buf[MAXPATHLEN]; 431 char *p; 432 FILE *fp; 433 434 (void) snprintf(buf, MAXPATHLEN, "%s/%s", 435 SMB_VARRUN_DIR, SMB_DOMAINS_FILE); 436 437 if ((fp = fopen(buf, "r")) != NULL) { 438 (void) lockf(fileno(fp), F_LOCK, 0); 439 440 while (fgets(buf, MAXPATHLEN, fp) != NULL) { 441 if ((p = strchr(buf, '\n')) != NULL) 442 *p = '\0'; 443 (void) printf("%s\n", buf); 444 } 445 446 (void) lockf(fileno(fp), F_ULOCK, 0); 447 (void) fclose(fp); 448 } 449 } 450 451 void 452 smb_domain_set_basic_info(char *sid, char *nb_domain, char *fq_domain, 453 smb_domain_t *di) 454 { 455 if (sid == NULL || nb_domain == NULL || fq_domain == NULL || 456 di == NULL) 457 return; 458 459 (void) strlcpy(di->di_sid, sid, SMB_SID_STRSZ); 460 (void) strlcpy(di->di_nbname, nb_domain, NETBIOS_NAME_SZ); 461 (void) utf8_strupr(di->di_nbname); 462 (void) strlcpy(di->di_fqname, fq_domain, MAXHOSTNAMELEN); 463 di->di_binsid = NULL; 464 } 465 466 void 467 smb_domain_set_dns_info(char *sid, char *nb_domain, char *fq_domain, 468 char *forest, char *guid, smb_domain_t *di) 469 { 470 if (di == NULL || forest == NULL || guid == NULL) 471 return; 472 473 smb_domain_set_basic_info(sid, nb_domain, fq_domain, di); 474 (void) strlcpy(di->di_u.di_dns.ddi_forest, forest, MAXHOSTNAMELEN); 475 (void) strlcpy(di->di_u.di_dns.ddi_guid, guid, 476 UUID_PRINTABLE_STRING_LENGTH); 477 } 478 479 void 480 smb_domain_set_trust_info(char *sid, char *nb_domain, char *fq_domain, 481 uint32_t trust_dir, uint32_t trust_type, uint32_t trust_attrs, 482 smb_domain_t *di) 483 { 484 smb_domain_trust_t *ti; 485 486 if (di == NULL) 487 return; 488 489 di->di_type = SMB_DOMAIN_TRUSTED; 490 ti = &di->di_u.di_trust; 491 smb_domain_set_basic_info(sid, nb_domain, fq_domain, di); 492 ti->dti_trust_direction = trust_dir; 493 ti->dti_trust_type = trust_type; 494 ti->dti_trust_attrs = trust_attrs; 495 } 496 497 /* 498 * Remove the /var/run/smb/domains file. 499 */ 500 static void 501 smb_domain_unlink(void) 502 { 503 char fname[MAXPATHLEN]; 504 505 (void) snprintf(fname, MAXPATHLEN, "%s/%s", 506 SMB_VARRUN_DIR, SMB_DOMAINS_FILE); 507 (void) unlink(fname); 508 } 509 510 /* 511 * Add an entry for the local domain to the domain cache 512 */ 513 static uint32_t 514 smb_domain_add_local(void) 515 { 516 char *lsidstr; 517 char hostname[NETBIOS_NAME_SZ]; 518 char fq_name[MAXHOSTNAMELEN]; 519 smb_domain_t di; 520 521 if ((lsidstr = smb_config_get_localsid()) == NULL) 522 return (SMB_DOMAIN_NOMACHINE_SID); 523 524 if (smb_getnetbiosname(hostname, NETBIOS_NAME_SZ) != 0) { 525 free(lsidstr); 526 return (SMB_DOMAIN_NOMACHINE_SID); 527 } 528 529 *fq_name = '\0'; 530 (void) smb_getfqhostname(fq_name, MAXHOSTNAMELEN); 531 smb_domain_set_basic_info(lsidstr, hostname, fq_name, &di); 532 (void) smb_domain_add(SMB_DOMAIN_LOCAL, &di); 533 534 free(lsidstr); 535 return (SMB_DOMAIN_SUCCESS); 536 } 537 538 /* 539 * Add an entry for the primary domain to the domain cache 540 */ 541 static uint32_t 542 smb_domain_add_primary(uint32_t secmode) 543 { 544 char sidstr[SMB_SID_STRSZ]; 545 char fq_name[MAXHOSTNAMELEN]; 546 char nb_name[NETBIOS_NAME_SZ]; 547 smb_domain_t di; 548 int rc; 549 550 if (secmode != SMB_SECMODE_DOMAIN) 551 return (SMB_DOMAIN_SUCCESS); 552 553 rc = smb_config_getstr(SMB_CI_DOMAIN_SID, sidstr, sizeof (sidstr)); 554 if (rc != SMBD_SMF_OK) 555 return (SMB_DOMAIN_NODOMAIN_SID); 556 557 rc = smb_config_getstr(SMB_CI_DOMAIN_NAME, nb_name, NETBIOS_NAME_SZ); 558 if ((rc != SMBD_SMF_OK) || (*nb_name == '\0')) 559 return (SMB_DOMAIN_NODOMAIN_NAME); 560 561 (void) smb_getfqdomainname(fq_name, MAXHOSTNAMELEN); 562 smb_domain_set_basic_info(sidstr, nb_name, fq_name, &di); 563 (void) smb_domain_add(SMB_DOMAIN_PRIMARY, &di); 564 return (SMB_DOMAIN_SUCCESS); 565 } 566 567 /* 568 * Initialize the domain cache. 569 * This function does not populate the cache. 570 */ 571 static void 572 smb_dcache_create(void) 573 { 574 (void) mutex_lock(&smb_dcache.dc_mtx); 575 if (smb_dcache.dc_state != SMB_DCACHE_STATE_NONE) { 576 (void) mutex_unlock(&smb_dcache.dc_mtx); 577 return; 578 } 579 580 list_create(&smb_dcache.dc_cache, sizeof (smb_domain_t), 581 offsetof(smb_domain_t, di_lnd)); 582 583 smb_dcache.dc_nops = 0; 584 *smb_dcache.dc_server = '\0'; 585 smb_dcache.dc_state = SMB_DCACHE_STATE_READY; 586 (void) mutex_unlock(&smb_dcache.dc_mtx); 587 } 588 589 /* 590 * Removes and frees all the cache entries 591 */ 592 static void 593 smb_dcache_flush(void) 594 { 595 smb_domain_t *di; 596 597 (void) rw_wrlock(&smb_dcache.dc_cache_lck); 598 while ((di = list_head(&smb_dcache.dc_cache)) != NULL) 599 smb_dcache_remove(di); 600 (void) rw_unlock(&smb_dcache.dc_cache_lck); 601 } 602 603 /* 604 * Destroys the cache. 605 */ 606 static void 607 smb_dcache_destroy(void) 608 { 609 (void) mutex_lock(&smb_dcache.dc_mtx); 610 if ((smb_dcache.dc_state == SMB_DCACHE_STATE_READY) || 611 (smb_dcache.dc_state == SMB_DCACHE_STATE_UPDATING)) { 612 smb_dcache.dc_state = SMB_DCACHE_STATE_DESTROYING; 613 while (smb_dcache.dc_nops > 0) 614 (void) cond_wait(&smb_dcache.dc_cv, 615 &smb_dcache.dc_mtx); 616 617 smb_dcache_flush(); 618 list_destroy(&smb_dcache.dc_cache); 619 620 smb_dcache.dc_state = SMB_DCACHE_STATE_NONE; 621 } 622 (void) mutex_unlock(&smb_dcache.dc_mtx); 623 } 624 625 /* 626 * Lock the cache with the specified mode. 627 * If the cache is in updating state and a read lock is 628 * requested, the lock won't be granted until either the 629 * update is finished or SMB_DCACHE_UPDATE_WAIT has passed. 630 * 631 * Whenever a lock is granted, the number of inflight cache 632 * operations is incremented. 633 */ 634 static uint32_t 635 smb_dcache_lock(int mode) 636 { 637 (void) mutex_lock(&smb_dcache.dc_mtx); 638 switch (smb_dcache.dc_state) { 639 case SMB_DCACHE_STATE_NONE: 640 case SMB_DCACHE_STATE_DESTROYING: 641 (void) mutex_unlock(&smb_dcache.dc_mtx); 642 return (SMB_DOMAIN_INTERNAL_ERR); 643 644 case SMB_DCACHE_STATE_UPDATING: 645 if (mode == SMB_DCACHE_RDLOCK) { 646 /* 647 * Read operations should wait until the update 648 * is completed. 649 */ 650 if (!smb_dcache_wait()) { 651 (void) mutex_unlock(&smb_dcache.dc_mtx); 652 return (SMB_DOMAIN_INTERNAL_ERR); 653 } 654 } 655 656 default: 657 smb_dcache.dc_nops++; 658 break; 659 } 660 (void) mutex_unlock(&smb_dcache.dc_mtx); 661 662 /* 663 * Lock has to be taken outside the mutex otherwise 664 * there could be a deadlock 665 */ 666 if (mode == SMB_DCACHE_RDLOCK) 667 (void) rw_rdlock(&smb_dcache.dc_cache_lck); 668 else 669 (void) rw_wrlock(&smb_dcache.dc_cache_lck); 670 671 return (SMB_DOMAIN_SUCCESS); 672 } 673 674 /* 675 * Decrement the number of inflight operations and then unlock. 676 */ 677 static void 678 smb_dcache_unlock(void) 679 { 680 (void) mutex_lock(&smb_dcache.dc_mtx); 681 assert(smb_dcache.dc_nops > 0); 682 smb_dcache.dc_nops--; 683 (void) cond_broadcast(&smb_dcache.dc_cv); 684 (void) mutex_unlock(&smb_dcache.dc_mtx); 685 686 (void) rw_unlock(&smb_dcache.dc_cache_lck); 687 } 688 689 static uint32_t 690 smb_dcache_add(smb_domain_t *di) 691 { 692 smb_domain_t *dcnode; 693 694 if ((dcnode = malloc(sizeof (smb_domain_t))) == NULL) 695 return (SMB_DOMAIN_NO_MEMORY); 696 697 *dcnode = *di; 698 dcnode->di_binsid = smb_sid_fromstr(dcnode->di_sid); 699 if (dcnode->di_binsid == NULL) { 700 free(dcnode); 701 return (SMB_DOMAIN_NO_MEMORY); 702 } 703 704 list_insert_tail(&smb_dcache.dc_cache, dcnode); 705 return (SMB_DOMAIN_SUCCESS); 706 } 707 708 static void 709 smb_dcache_remove(smb_domain_t *di) 710 { 711 list_remove(&smb_dcache.dc_cache, di); 712 smb_sid_free(di->di_binsid); 713 free(di); 714 } 715 716 static void 717 smb_dcache_setdc(char *dc) 718 { 719 (void) mutex_lock(&smb_dcache.dc_mtx); 720 (void) strlcpy(smb_dcache.dc_server, dc, sizeof (smb_dcache.dc_server)); 721 (void) mutex_unlock(&smb_dcache.dc_mtx); 722 } 723 724 static void 725 smb_dcache_getdc(char *buf, size_t buflen) 726 { 727 (void) mutex_lock(&smb_dcache.dc_mtx); 728 (void) strlcpy(buf, smb_dcache.dc_server, buflen); 729 (void) mutex_unlock(&smb_dcache.dc_mtx); 730 } 731 732 /* 733 * Waits for SMB_DCACHE_UPDATE_WAIT seconds if cache is in 734 * UPDATING state. Upon wake up returns true if cache is 735 * ready to be used, otherwise it returns false. 736 */ 737 static boolean_t 738 smb_dcache_wait(void) 739 { 740 timestruc_t to; 741 int err; 742 743 to.tv_sec = SMB_DCACHE_UPDATE_WAIT; 744 to.tv_nsec = 0; 745 while (smb_dcache.dc_state == SMB_DCACHE_STATE_UPDATING) { 746 err = cond_reltimedwait(&smb_dcache.dc_cv, 747 &smb_dcache.dc_mtx, &to); 748 if (err == ETIME) 749 break; 750 } 751 752 return (smb_dcache.dc_state == SMB_DCACHE_STATE_READY); 753 } 754 755 /* 756 * Transfers the cache into UPDATING state, this will ensure 757 * any read access to the cache will be stalled until the 758 * update is finished. This is to avoid providing incomplete, 759 * inconsistent or stale information. 760 * 761 * If another thread is already updating the cache, other 762 * callers will wait until cache is no longer in UPDATING 763 * state. The return code is decided based on the new 764 * state of the cache. 765 */ 766 static uint32_t 767 smb_dcache_updating(void) 768 { 769 uint32_t rc; 770 771 (void) mutex_lock(&smb_dcache.dc_mtx); 772 switch (smb_dcache.dc_state) { 773 case SMB_DCACHE_STATE_READY: 774 smb_dcache.dc_state = SMB_DCACHE_STATE_UPDATING; 775 rc = SMB_DOMAIN_SUCCESS; 776 break; 777 778 case SMB_DCACHE_STATE_UPDATING: 779 while (smb_dcache.dc_state == SMB_DCACHE_STATE_UPDATING) 780 (void) cond_wait(&smb_dcache.dc_cv, 781 &smb_dcache.dc_mtx); 782 783 if (smb_dcache.dc_state == SMB_DCACHE_STATE_READY) { 784 smb_dcache.dc_state = SMB_DCACHE_STATE_UPDATING; 785 rc = SMB_DOMAIN_SUCCESS; 786 } else { 787 rc = SMB_DOMAIN_NO_CACHE; 788 } 789 break; 790 791 case SMB_DCACHE_STATE_NONE: 792 case SMB_DCACHE_STATE_DESTROYING: 793 rc = SMB_DOMAIN_NO_CACHE; 794 break; 795 796 default: 797 break; 798 } 799 800 (void) mutex_unlock(&smb_dcache.dc_mtx); 801 return (rc); 802 } 803 804 /* 805 * Transfers the cache from UPDATING to READY state. 806 * 807 * Nothing will happen if the cache is no longer available 808 * or it is being destroyed. 809 */ 810 static void 811 smb_dcache_ready(void) 812 { 813 (void) mutex_lock(&smb_dcache.dc_mtx); 814 switch (smb_dcache.dc_state) { 815 case SMB_DCACHE_STATE_UPDATING: 816 smb_dcache.dc_state = SMB_DCACHE_STATE_READY; 817 (void) cond_broadcast(&smb_dcache.dc_cv); 818 break; 819 820 case SMB_DCACHE_STATE_NONE: 821 case SMB_DCACHE_STATE_DESTROYING: 822 break; 823 824 default: 825 assert(0); 826 } 827 (void) mutex_unlock(&smb_dcache.dc_mtx); 828 } 829