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