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 2014 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 smb_dcinfo_t dc_dci; 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 boolean_t smb_dcache_getdc(smb_dcinfo_t *); 94 static void smb_dcache_setdc(const smb_dcinfo_t *); 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 rv; 297 298 /* Note: this waits for the dcache lock. */ 299 rv = smb_domain_lookup_type(SMB_DOMAIN_PRIMARY, &dxi->d_primary); 300 if (rv) 301 rv = smb_dcache_getdc(&dxi->d_dci); 302 303 return (rv); 304 } 305 306 /* 307 * Get the name of the current DC (if any) 308 * Does NOT block. 309 */ 310 void 311 smb_domain_current_dc(smb_dcinfo_t *dci) 312 { 313 (void) smb_dcache_getdc(dci); 314 } 315 316 /* 317 * Transfer the cache to updating state. 318 * In this state any request for reading the cache would 319 * be blocked until the update is finished. 320 */ 321 uint32_t 322 smb_domain_start_update(void) 323 { 324 return (smb_dcache_updating()); 325 } 326 327 /* 328 * Transfer the cache from updating to ready state. 329 */ 330 void 331 smb_domain_end_update(void) 332 { 333 smb_dcache_ready(); 334 } 335 336 /* 337 * Updates the cache with given information for the primary 338 * domain, possible trusted domains and the selected domain 339 * controller. 340 * 341 * Before adding the new entries existing entries of type 342 * primary and trusted will be removed from cache. 343 */ 344 void 345 smb_domain_update(smb_domainex_t *dxi) 346 { 347 smb_domain_t *dcnode; 348 int i; 349 350 if (smb_dcache_lock(SMB_DCACHE_WRLOCK) != SMB_DOMAIN_SUCCESS) 351 return; 352 353 dcnode = list_head(&smb_dcache.dc_cache); 354 while (dcnode) { 355 if ((dcnode->di_type == SMB_DOMAIN_PRIMARY) || 356 (dcnode->di_type == SMB_DOMAIN_TRUSTED)) { 357 smb_dcache_remove(dcnode); 358 dcnode = list_head(&smb_dcache.dc_cache); 359 } else { 360 dcnode = list_next(&smb_dcache.dc_cache, dcnode); 361 } 362 } 363 364 if (smb_dcache_add(&dxi->d_primary) == SMB_DOMAIN_SUCCESS) { 365 for (i = 0; i < dxi->d_trusted.td_num; i++) 366 (void) smb_dcache_add(&dxi->d_trusted.td_domains[i]); 367 368 smb_dcache_setdc(&dxi->d_dci); 369 } 370 371 smb_dcache_unlock(); 372 } 373 374 /* 375 * Write the list of domains to /var/run/smb/domains. 376 */ 377 void 378 smb_domain_save(void) 379 { 380 char fname[MAXPATHLEN]; 381 char tag; 382 smb_domain_t *domain; 383 FILE *fp; 384 struct passwd *pwd; 385 struct group *grp; 386 uid_t uid; 387 gid_t gid; 388 389 (void) snprintf(fname, MAXPATHLEN, "%s/%s", 390 SMB_VARRUN_DIR, SMB_DOMAINS_FILE); 391 392 if ((fp = fopen(fname, "w")) == NULL) 393 return; 394 395 pwd = getpwnam("root"); 396 grp = getgrnam("sys"); 397 uid = (pwd == NULL) ? 0 : pwd->pw_uid; 398 gid = (grp == NULL) ? 3 : grp->gr_gid; 399 400 (void) lockf(fileno(fp), F_LOCK, 0); 401 (void) fchmod(fileno(fp), 0600); 402 (void) fchown(fileno(fp), uid, gid); 403 404 if (smb_dcache_lock(SMB_DCACHE_RDLOCK) != SMB_DOMAIN_SUCCESS) 405 return; 406 407 domain = list_head(&smb_dcache.dc_cache); 408 while (domain) { 409 switch (domain->di_type) { 410 case SMB_DOMAIN_PRIMARY: 411 tag = '*'; 412 break; 413 414 case SMB_DOMAIN_TRUSTED: 415 case SMB_DOMAIN_UNTRUSTED: 416 tag = '-'; 417 break; 418 419 case SMB_DOMAIN_LOCAL: 420 tag = '.'; 421 break; 422 default: 423 domain = list_next(&smb_dcache.dc_cache, domain); 424 continue; 425 } 426 427 (void) fprintf(fp, "[%c] [%s] [%s]\n", 428 tag, domain->di_nbname, domain->di_sid); 429 430 domain = list_next(&smb_dcache.dc_cache, domain); 431 } 432 433 smb_dcache_unlock(); 434 (void) lockf(fileno(fp), F_ULOCK, 0); 435 (void) fclose(fp); 436 } 437 438 /* 439 * List the domains in /var/run/smb/domains. 440 */ 441 void 442 smb_domain_show(void) 443 { 444 char buf[MAXPATHLEN]; 445 char *p; 446 FILE *fp; 447 448 (void) snprintf(buf, MAXPATHLEN, "%s/%s", 449 SMB_VARRUN_DIR, SMB_DOMAINS_FILE); 450 451 if ((fp = fopen(buf, "r")) != NULL) { 452 (void) lockf(fileno(fp), F_LOCK, 0); 453 454 while (fgets(buf, MAXPATHLEN, fp) != NULL) { 455 if ((p = strchr(buf, '\n')) != NULL) 456 *p = '\0'; 457 (void) printf("%s\n", buf); 458 } 459 460 (void) lockf(fileno(fp), F_ULOCK, 0); 461 (void) fclose(fp); 462 } 463 } 464 465 void 466 smb_domain_set_basic_info(char *sid, char *nb_domain, char *fq_domain, 467 smb_domain_t *di) 468 { 469 if (sid == NULL || nb_domain == NULL || fq_domain == NULL || 470 di == NULL) 471 return; 472 473 (void) strlcpy(di->di_sid, sid, SMB_SID_STRSZ); 474 (void) strlcpy(di->di_nbname, nb_domain, NETBIOS_NAME_SZ); 475 (void) smb_strupr(di->di_nbname); 476 (void) strlcpy(di->di_fqname, fq_domain, MAXHOSTNAMELEN); 477 di->di_binsid = NULL; 478 } 479 480 void 481 smb_domain_set_dns_info(char *sid, char *nb_domain, char *fq_domain, 482 char *forest, char *guid, smb_domain_t *di) 483 { 484 if (di == NULL || forest == NULL || guid == NULL) 485 return; 486 487 smb_domain_set_basic_info(sid, nb_domain, fq_domain, di); 488 (void) strlcpy(di->di_u.di_dns.ddi_forest, forest, MAXHOSTNAMELEN); 489 (void) strlcpy(di->di_u.di_dns.ddi_guid, guid, 490 UUID_PRINTABLE_STRING_LENGTH); 491 } 492 493 void 494 smb_domain_set_trust_info(char *sid, char *nb_domain, char *fq_domain, 495 uint32_t trust_dir, uint32_t trust_type, uint32_t trust_attrs, 496 smb_domain_t *di) 497 { 498 smb_domain_trust_t *ti; 499 500 if (di == NULL) 501 return; 502 503 di->di_type = SMB_DOMAIN_TRUSTED; 504 ti = &di->di_u.di_trust; 505 smb_domain_set_basic_info(sid, nb_domain, fq_domain, di); 506 ti->dti_trust_direction = trust_dir; 507 ti->dti_trust_type = trust_type; 508 ti->dti_trust_attrs = trust_attrs; 509 } 510 511 /* 512 * Remove the /var/run/smb/domains file. 513 */ 514 static void 515 smb_domain_unlink(void) 516 { 517 char fname[MAXPATHLEN]; 518 519 (void) snprintf(fname, MAXPATHLEN, "%s/%s", 520 SMB_VARRUN_DIR, SMB_DOMAINS_FILE); 521 (void) unlink(fname); 522 } 523 524 /* 525 * Add an entry for the local domain to the domain cache 526 */ 527 static uint32_t 528 smb_domain_add_local(void) 529 { 530 char *lsidstr; 531 char hostname[NETBIOS_NAME_SZ]; 532 char fq_name[MAXHOSTNAMELEN]; 533 smb_domain_t di; 534 535 if ((lsidstr = smb_config_get_localsid()) == NULL) 536 return (SMB_DOMAIN_NOMACHINE_SID); 537 538 if (smb_getnetbiosname(hostname, NETBIOS_NAME_SZ) != 0) { 539 free(lsidstr); 540 return (SMB_DOMAIN_NOMACHINE_SID); 541 } 542 543 *fq_name = '\0'; 544 (void) smb_getfqhostname(fq_name, MAXHOSTNAMELEN); 545 smb_domain_set_basic_info(lsidstr, hostname, fq_name, &di); 546 (void) smb_domain_add(SMB_DOMAIN_LOCAL, &di); 547 548 free(lsidstr); 549 return (SMB_DOMAIN_SUCCESS); 550 } 551 552 /* 553 * Add an entry for the primary domain to the domain cache 554 */ 555 static uint32_t 556 smb_domain_add_primary(uint32_t secmode) 557 { 558 char sidstr[SMB_SID_STRSZ]; 559 char fq_name[MAXHOSTNAMELEN]; 560 char nb_name[NETBIOS_NAME_SZ]; 561 smb_domain_t di; 562 int rc; 563 564 if (secmode != SMB_SECMODE_DOMAIN) 565 return (SMB_DOMAIN_SUCCESS); 566 567 rc = smb_config_getstr(SMB_CI_DOMAIN_SID, sidstr, sizeof (sidstr)); 568 if (rc != SMBD_SMF_OK) 569 return (SMB_DOMAIN_NODOMAIN_SID); 570 571 rc = smb_config_getstr(SMB_CI_DOMAIN_NAME, nb_name, NETBIOS_NAME_SZ); 572 if ((rc != SMBD_SMF_OK) || (*nb_name == '\0')) 573 return (SMB_DOMAIN_NODOMAIN_NAME); 574 575 (void) smb_getfqdomainname(fq_name, MAXHOSTNAMELEN); 576 smb_domain_set_basic_info(sidstr, nb_name, fq_name, &di); 577 (void) smb_domain_add(SMB_DOMAIN_PRIMARY, &di); 578 return (SMB_DOMAIN_SUCCESS); 579 } 580 581 /* 582 * Initialize the domain cache. 583 * This function does not populate the cache. 584 */ 585 static void 586 smb_dcache_create(void) 587 { 588 (void) mutex_lock(&smb_dcache.dc_mtx); 589 if (smb_dcache.dc_state != SMB_DCACHE_STATE_NONE) { 590 (void) mutex_unlock(&smb_dcache.dc_mtx); 591 return; 592 } 593 594 list_create(&smb_dcache.dc_cache, sizeof (smb_domain_t), 595 offsetof(smb_domain_t, di_lnd)); 596 597 smb_dcache.dc_nops = 0; 598 bzero(&smb_dcache.dc_dci, sizeof (smb_dcache.dc_dci)); 599 smb_dcache.dc_state = SMB_DCACHE_STATE_READY; 600 (void) mutex_unlock(&smb_dcache.dc_mtx); 601 } 602 603 /* 604 * Removes and frees all the cache entries 605 */ 606 static void 607 smb_dcache_flush(void) 608 { 609 smb_domain_t *di; 610 611 (void) rw_wrlock(&smb_dcache.dc_cache_lck); 612 while ((di = list_head(&smb_dcache.dc_cache)) != NULL) 613 smb_dcache_remove(di); 614 (void) rw_unlock(&smb_dcache.dc_cache_lck); 615 } 616 617 /* 618 * Destroys the cache. 619 */ 620 static void 621 smb_dcache_destroy(void) 622 { 623 (void) mutex_lock(&smb_dcache.dc_mtx); 624 if ((smb_dcache.dc_state == SMB_DCACHE_STATE_READY) || 625 (smb_dcache.dc_state == SMB_DCACHE_STATE_UPDATING)) { 626 smb_dcache.dc_state = SMB_DCACHE_STATE_DESTROYING; 627 while (smb_dcache.dc_nops > 0) 628 (void) cond_wait(&smb_dcache.dc_cv, 629 &smb_dcache.dc_mtx); 630 631 smb_dcache_flush(); 632 list_destroy(&smb_dcache.dc_cache); 633 634 smb_dcache.dc_state = SMB_DCACHE_STATE_NONE; 635 } 636 (void) mutex_unlock(&smb_dcache.dc_mtx); 637 } 638 639 /* 640 * Lock the cache with the specified mode. 641 * If the cache is in updating state and a read lock is 642 * requested, the lock won't be granted until either the 643 * update is finished or SMB_DCACHE_UPDATE_WAIT has passed. 644 * 645 * Whenever a lock is granted, the number of inflight cache 646 * operations is incremented. 647 */ 648 static uint32_t 649 smb_dcache_lock(int mode) 650 { 651 (void) mutex_lock(&smb_dcache.dc_mtx); 652 switch (smb_dcache.dc_state) { 653 case SMB_DCACHE_STATE_NONE: 654 case SMB_DCACHE_STATE_DESTROYING: 655 (void) mutex_unlock(&smb_dcache.dc_mtx); 656 return (SMB_DOMAIN_INTERNAL_ERR); 657 658 case SMB_DCACHE_STATE_UPDATING: 659 if (mode == SMB_DCACHE_RDLOCK) { 660 /* 661 * Read operations should wait until the update 662 * is completed. 663 */ 664 if (!smb_dcache_wait()) { 665 (void) mutex_unlock(&smb_dcache.dc_mtx); 666 return (SMB_DOMAIN_INTERNAL_ERR); 667 } 668 } 669 /* FALLTHROUGH */ 670 671 default: 672 smb_dcache.dc_nops++; 673 break; 674 } 675 (void) mutex_unlock(&smb_dcache.dc_mtx); 676 677 /* 678 * Lock has to be taken outside the mutex otherwise 679 * there could be a deadlock 680 */ 681 if (mode == SMB_DCACHE_RDLOCK) 682 (void) rw_rdlock(&smb_dcache.dc_cache_lck); 683 else 684 (void) rw_wrlock(&smb_dcache.dc_cache_lck); 685 686 return (SMB_DOMAIN_SUCCESS); 687 } 688 689 /* 690 * Decrement the number of inflight operations and then unlock. 691 */ 692 static void 693 smb_dcache_unlock(void) 694 { 695 (void) mutex_lock(&smb_dcache.dc_mtx); 696 assert(smb_dcache.dc_nops > 0); 697 smb_dcache.dc_nops--; 698 (void) cond_broadcast(&smb_dcache.dc_cv); 699 (void) mutex_unlock(&smb_dcache.dc_mtx); 700 701 (void) rw_unlock(&smb_dcache.dc_cache_lck); 702 } 703 704 static uint32_t 705 smb_dcache_add(smb_domain_t *di) 706 { 707 smb_domain_t *dcnode; 708 709 if ((dcnode = malloc(sizeof (smb_domain_t))) == NULL) 710 return (SMB_DOMAIN_NO_MEMORY); 711 712 *dcnode = *di; 713 dcnode->di_binsid = smb_sid_fromstr(dcnode->di_sid); 714 if (dcnode->di_binsid == NULL) { 715 free(dcnode); 716 return (SMB_DOMAIN_NO_MEMORY); 717 } 718 719 list_insert_tail(&smb_dcache.dc_cache, dcnode); 720 return (SMB_DOMAIN_SUCCESS); 721 } 722 723 static void 724 smb_dcache_remove(smb_domain_t *di) 725 { 726 list_remove(&smb_dcache.dc_cache, di); 727 smb_sid_free(di->di_binsid); 728 free(di); 729 } 730 731 static void 732 smb_dcache_setdc(const smb_dcinfo_t *dci) 733 { 734 (void) mutex_lock(&smb_dcache.dc_mtx); 735 smb_dcache.dc_dci = *dci; /* struct assignment! */ 736 (void) mutex_unlock(&smb_dcache.dc_mtx); 737 } 738 739 /* 740 * Return B_TRUE if we have DC information. 741 */ 742 static boolean_t 743 smb_dcache_getdc(smb_dcinfo_t *dci) 744 { 745 (void) mutex_lock(&smb_dcache.dc_mtx); 746 *dci = smb_dcache.dc_dci; /* struct assignment! */ 747 (void) mutex_unlock(&smb_dcache.dc_mtx); 748 return (dci->dc_name[0] != '\0'); 749 } 750 751 /* 752 * Waits for SMB_DCACHE_UPDATE_WAIT seconds if cache is in 753 * UPDATING state. Upon wake up returns true if cache is 754 * ready to be used, otherwise it returns false. 755 */ 756 static boolean_t 757 smb_dcache_wait(void) 758 { 759 timestruc_t to; 760 int err; 761 762 to.tv_sec = SMB_DCACHE_UPDATE_WAIT; 763 to.tv_nsec = 0; 764 while (smb_dcache.dc_state == SMB_DCACHE_STATE_UPDATING) { 765 err = cond_reltimedwait(&smb_dcache.dc_cv, 766 &smb_dcache.dc_mtx, &to); 767 if (err == ETIME) 768 break; 769 } 770 771 return (smb_dcache.dc_state == SMB_DCACHE_STATE_READY); 772 } 773 774 /* 775 * Transfers the cache into UPDATING state, this will ensure 776 * any read access to the cache will be stalled until the 777 * update is finished. This is to avoid providing incomplete, 778 * inconsistent or stale information. 779 * 780 * If another thread is already updating the cache, other 781 * callers will wait until cache is no longer in UPDATING 782 * state. The return code is decided based on the new 783 * state of the cache. 784 */ 785 static uint32_t 786 smb_dcache_updating(void) 787 { 788 uint32_t rc; 789 790 (void) mutex_lock(&smb_dcache.dc_mtx); 791 switch (smb_dcache.dc_state) { 792 case SMB_DCACHE_STATE_READY: 793 smb_dcache.dc_state = SMB_DCACHE_STATE_UPDATING; 794 rc = SMB_DOMAIN_SUCCESS; 795 break; 796 797 case SMB_DCACHE_STATE_UPDATING: 798 while (smb_dcache.dc_state == SMB_DCACHE_STATE_UPDATING) 799 (void) cond_wait(&smb_dcache.dc_cv, 800 &smb_dcache.dc_mtx); 801 802 if (smb_dcache.dc_state == SMB_DCACHE_STATE_READY) { 803 smb_dcache.dc_state = SMB_DCACHE_STATE_UPDATING; 804 rc = SMB_DOMAIN_SUCCESS; 805 } else { 806 rc = SMB_DOMAIN_NO_CACHE; 807 } 808 break; 809 810 case SMB_DCACHE_STATE_NONE: 811 case SMB_DCACHE_STATE_DESTROYING: 812 rc = SMB_DOMAIN_NO_CACHE; 813 break; 814 815 default: 816 break; 817 } 818 819 (void) mutex_unlock(&smb_dcache.dc_mtx); 820 return (rc); 821 } 822 823 /* 824 * Transfers the cache from UPDATING to READY state. 825 * 826 * Nothing will happen if the cache is no longer available 827 * or it is being destroyed. 828 */ 829 static void 830 smb_dcache_ready(void) 831 { 832 (void) mutex_lock(&smb_dcache.dc_mtx); 833 switch (smb_dcache.dc_state) { 834 case SMB_DCACHE_STATE_UPDATING: 835 smb_dcache.dc_state = SMB_DCACHE_STATE_READY; 836 (void) cond_broadcast(&smb_dcache.dc_cv); 837 break; 838 839 case SMB_DCACHE_STATE_NONE: 840 case SMB_DCACHE_STATE_DESTROYING: 841 break; 842 843 default: 844 assert(0); 845 } 846 (void) mutex_unlock(&smb_dcache.dc_mtx); 847 } 848