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