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 (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. 24 * Copyright 2014 Nexenta Systems, Inc. All rights reserved. 25 */ 26 27 /* 28 * NFS specific functions 29 */ 30 #include <stdio.h> 31 #include <string.h> 32 #include <ctype.h> 33 #include <stdlib.h> 34 #include <unistd.h> 35 #include <zone.h> 36 #include <errno.h> 37 #include <locale.h> 38 #include <signal.h> 39 #include <strings.h> 40 #include "libshare.h" 41 #include "libshare_impl.h" 42 #include <nfs/export.h> 43 #include <pwd.h> 44 #include <limits.h> 45 #include <libscf.h> 46 #include <syslog.h> 47 #include <rpcsvc/daemon_utils.h> 48 #include "nfslog_config.h" 49 #include "nfslogtab.h" 50 #include "libshare_nfs.h" 51 #include <nfs/nfs.h> 52 #include <nfs/nfssys.h> 53 #include "smfcfg.h" 54 55 /* should really be in some global place */ 56 #define DEF_WIN 30000 57 #define OPT_CHUNK 1024 58 59 int debug = 0; 60 61 #define NFS_SERVER_SVC "svc:/network/nfs/server:default" 62 #define NFS_CLIENT_SVC (char *)"svc:/network/nfs/client:default" 63 64 /* internal functions */ 65 static int nfs_init(); 66 static void nfs_fini(); 67 static int nfs_enable_share(sa_share_t); 68 static int nfs_disable_share(sa_share_t, char *); 69 static int nfs_validate_property(sa_handle_t, sa_property_t, sa_optionset_t); 70 static int nfs_validate_security_mode(char *); 71 static int nfs_is_security_opt(char *); 72 static int nfs_parse_legacy_options(sa_group_t, char *); 73 static char *nfs_format_options(sa_group_t, int); 74 static int nfs_set_proto_prop(sa_property_t); 75 static sa_protocol_properties_t nfs_get_proto_set(); 76 static char *nfs_get_status(); 77 static char *nfs_space_alias(char *); 78 static uint64_t nfs_features(); 79 80 /* 81 * ops vector that provides the protocol specific info and operations 82 * for share management. 83 */ 84 85 struct sa_plugin_ops sa_plugin_ops = { 86 SA_PLUGIN_VERSION, 87 "nfs", 88 nfs_init, 89 nfs_fini, 90 nfs_enable_share, 91 nfs_disable_share, 92 nfs_validate_property, 93 nfs_validate_security_mode, 94 nfs_is_security_opt, 95 nfs_parse_legacy_options, 96 nfs_format_options, 97 nfs_set_proto_prop, 98 nfs_get_proto_set, 99 nfs_get_status, 100 nfs_space_alias, 101 NULL, /* update_legacy */ 102 NULL, /* delete_legacy */ 103 NULL, /* change_notify */ 104 NULL, /* enable_resource */ 105 NULL, /* disable_resource */ 106 nfs_features, 107 NULL, /* transient shares */ 108 NULL, /* notify resource */ 109 NULL, /* rename_resource */ 110 NULL, /* run_command */ 111 NULL, /* command_help */ 112 NULL /* delete_proto_section */ 113 }; 114 115 /* 116 * list of support services needed 117 * defines should come from head/rpcsvc/daemon_utils.h 118 */ 119 120 static char *service_list_default[] = 121 { STATD, LOCKD, MOUNTD, NFSD, NFSMAPID, RQUOTAD, REPARSED, NULL }; 122 static char *service_list_logging[] = 123 { STATD, LOCKD, MOUNTD, NFSD, NFSMAPID, RQUOTAD, NFSLOGD, REPARSED, 124 NULL }; 125 126 /* 127 * option definitions. Make sure to keep the #define for the option 128 * index just before the entry it is the index for. Changing the order 129 * can cause breakage. E.g OPT_RW is index 1 and must precede the 130 * line that includes the SHOPT_RW and OPT_RW entries. 131 */ 132 133 struct option_defs optdefs[] = { 134 #define OPT_RO 0 135 {SHOPT_RO, OPT_RO, OPT_TYPE_ACCLIST}, 136 #define OPT_RW 1 137 {SHOPT_RW, OPT_RW, OPT_TYPE_ACCLIST}, 138 #define OPT_ROOT 2 139 {SHOPT_ROOT, OPT_ROOT, OPT_TYPE_ACCLIST}, 140 #define OPT_SECURE 3 141 {SHOPT_SECURE, OPT_SECURE, OPT_TYPE_DEPRECATED}, 142 #define OPT_ANON 4 143 {SHOPT_ANON, OPT_ANON, OPT_TYPE_USER}, 144 #define OPT_WINDOW 5 145 {SHOPT_WINDOW, OPT_WINDOW, OPT_TYPE_NUMBER}, 146 #define OPT_NOSUID 6 147 {SHOPT_NOSUID, OPT_NOSUID, OPT_TYPE_BOOLEAN}, 148 #define OPT_ACLOK 7 149 {SHOPT_ACLOK, OPT_ACLOK, OPT_TYPE_BOOLEAN}, 150 #define OPT_NOSUB 8 151 {SHOPT_NOSUB, OPT_NOSUB, OPT_TYPE_BOOLEAN}, 152 #define OPT_SEC 9 153 {SHOPT_SEC, OPT_SEC, OPT_TYPE_SECURITY}, 154 #define OPT_PUBLIC 10 155 {SHOPT_PUBLIC, OPT_PUBLIC, OPT_TYPE_BOOLEAN, OPT_SHARE_ONLY}, 156 #define OPT_INDEX 11 157 {SHOPT_INDEX, OPT_INDEX, OPT_TYPE_FILE}, 158 #define OPT_LOG 12 159 {SHOPT_LOG, OPT_LOG, OPT_TYPE_LOGTAG}, 160 #define OPT_CKSUM 13 161 {SHOPT_CKSUM, OPT_CKSUM, OPT_TYPE_STRINGSET}, 162 #define OPT_NONE 14 163 {SHOPT_NONE, OPT_NONE, OPT_TYPE_ACCLIST}, 164 #define OPT_ROOT_MAPPING 15 165 {SHOPT_ROOT_MAPPING, OPT_ROOT_MAPPING, OPT_TYPE_USER}, 166 #define OPT_CHARSET_MAP 16 167 {"", OPT_CHARSET_MAP, OPT_TYPE_ACCLIST}, 168 #define OPT_NOACLFAB 17 169 {SHOPT_NOACLFAB, OPT_NOACLFAB, OPT_TYPE_BOOLEAN}, 170 #ifdef VOLATILE_FH_TEST /* XXX added for testing volatile fh's only */ 171 #define OPT_VOLFH 18 172 {SHOPT_VOLFH, OPT_VOLFH}, 173 #endif /* VOLATILE_FH_TEST */ 174 NULL 175 }; 176 177 /* 178 * Codesets that may need to be converted to UTF-8 for file paths. 179 * Add new names here to add new property support. If we ever get a 180 * way to query the kernel for character sets, this should become 181 * dynamically loaded. Make sure changes here are reflected in 182 * cmd/fs.d/nfs/mountd/nfscmd.c 183 */ 184 185 static char *legal_conv[] = { 186 "euc-cn", 187 "euc-jp", 188 "euc-jpms", 189 "euc-kr", 190 "euc-tw", 191 "iso8859-1", 192 "iso8859-2", 193 "iso8859-5", 194 "iso8859-6", 195 "iso8859-7", 196 "iso8859-8", 197 "iso8859-9", 198 "iso8859-13", 199 "iso8859-15", 200 "koi8-r", 201 NULL 202 }; 203 204 /* 205 * list of properties that are related to security flavors. 206 */ 207 static char *seclist[] = { 208 SHOPT_RO, 209 SHOPT_RW, 210 SHOPT_ROOT, 211 SHOPT_WINDOW, 212 SHOPT_NONE, 213 SHOPT_ROOT_MAPPING, 214 NULL 215 }; 216 217 /* structure for list of securities */ 218 struct securities { 219 sa_security_t security; 220 struct securities *next; 221 }; 222 223 /* 224 * findcharset(charset) 225 * 226 * Returns B_TRUE if the charset is a legal conversion otherwise 227 * B_FALSE. This will need to be rewritten to be more efficient when 228 * we have a dynamic list of legal conversions. 229 */ 230 231 static boolean_t 232 findcharset(char *charset) 233 { 234 int i; 235 236 for (i = 0; legal_conv[i] != NULL; i++) 237 if (strcmp(charset, legal_conv[i]) == 0) 238 return (B_TRUE); 239 return (B_FALSE); 240 } 241 242 /* 243 * findopt(name) 244 * 245 * Lookup option "name" in the option table and return the table 246 * index. 247 */ 248 249 static int 250 findopt(char *name) 251 { 252 int i; 253 if (name != NULL) { 254 for (i = 0; optdefs[i].tag != NULL; i++) { 255 if (strcmp(optdefs[i].tag, name) == 0) 256 return (optdefs[i].index); 257 } 258 if (findcharset(name)) 259 return (OPT_CHARSET_MAP); 260 } 261 return (-1); 262 } 263 264 /* 265 * gettype(name) 266 * 267 * Return the type of option "name". 268 */ 269 270 static int 271 gettype(char *name) 272 { 273 int optdef; 274 275 optdef = findopt(name); 276 if (optdef != -1) 277 return (optdefs[optdef].type); 278 return (OPT_TYPE_ANY); 279 } 280 281 /* 282 * nfs_validate_security_mode(mode) 283 * 284 * is the specified mode string a valid one for use with NFS? 285 */ 286 287 static int 288 nfs_validate_security_mode(char *mode) 289 { 290 seconfig_t secinfo; 291 int err; 292 293 (void) memset(&secinfo, '\0', sizeof (secinfo)); 294 err = nfs_getseconfig_byname(mode, &secinfo); 295 if (err == SC_NOERROR) 296 return (1); 297 return (0); 298 } 299 300 /* 301 * nfs_is_security_opt(tok) 302 * 303 * check to see if tok represents an option that is only valid in some 304 * security flavor. 305 */ 306 307 static int 308 nfs_is_security_opt(char *tok) 309 { 310 int i; 311 312 for (i = 0; seclist[i] != NULL; i++) { 313 if (strcmp(tok, seclist[i]) == 0) 314 return (1); 315 } 316 return (0); 317 } 318 319 /* 320 * find_security(seclist, sec) 321 * 322 * Walk the current list of security flavors and return true if it is 323 * present, else return false. 324 */ 325 326 static int 327 find_security(struct securities *seclist, sa_security_t sec) 328 { 329 while (seclist != NULL) { 330 if (seclist->security == sec) 331 return (1); 332 seclist = seclist->next; 333 } 334 return (0); 335 } 336 337 /* 338 * make_security_list(group, securitymodes, proto) 339 * go through the list of securitymodes and add them to the 340 * group's list of security optionsets. We also keep a list of 341 * those optionsets so we don't have to find them later. All of 342 * these will get copies of the same properties. 343 */ 344 345 static struct securities * 346 make_security_list(sa_group_t group, char *securitymodes, char *proto) 347 { 348 char *tok, *next = NULL; 349 struct securities *curp, *headp = NULL, *prev; 350 sa_security_t check; 351 int freetok = 0; 352 353 for (tok = securitymodes; tok != NULL; tok = next) { 354 next = strchr(tok, ':'); 355 if (next != NULL) 356 *next++ = '\0'; 357 if (strcmp(tok, "default") == 0) { 358 /* resolve default into the real type */ 359 tok = nfs_space_alias(tok); 360 freetok = 1; 361 } 362 check = sa_get_security(group, tok, proto); 363 364 /* add to the security list if it isn't there already */ 365 if (check == NULL || !find_security(headp, check)) { 366 curp = (struct securities *)calloc(1, 367 sizeof (struct securities)); 368 if (curp != NULL) { 369 if (check == NULL) { 370 curp->security = sa_create_security( 371 group, tok, proto); 372 } else { 373 curp->security = check; 374 } 375 /* 376 * note that the first time through the loop, 377 * headp will be NULL and prev will be 378 * undefined. Since headp is NULL, we set 379 * both it and prev to the curp (first 380 * structure to be allocated). 381 * 382 * later passes through the loop will have 383 * headp not being NULL and prev will be used 384 * to allocate at the end of the list. 385 */ 386 if (headp == NULL) { 387 headp = curp; 388 prev = curp; 389 } else { 390 prev->next = curp; 391 prev = curp; 392 } 393 } 394 } 395 396 if (freetok) { 397 freetok = 0; 398 sa_free_attr_string(tok); 399 } 400 } 401 return (headp); 402 } 403 404 static void 405 free_security_list(struct securities *sec) 406 { 407 struct securities *next; 408 if (sec != NULL) { 409 for (next = sec->next; sec != NULL; sec = next) { 410 next = sec->next; 411 free(sec); 412 } 413 } 414 } 415 416 /* 417 * nfs_alistcat(str1, str2, sep) 418 * 419 * concatenate str1 and str2 into a new string using sep as a separate 420 * character. If memory allocation fails, return NULL; 421 */ 422 423 static char * 424 nfs_alistcat(char *str1, char *str2, char sep) 425 { 426 char *newstr; 427 size_t len; 428 429 len = strlen(str1) + strlen(str2) + 2; 430 newstr = (char *)malloc(len); 431 if (newstr != NULL) 432 (void) snprintf(newstr, len, "%s%c%s", str1, sep, str2); 433 return (newstr); 434 } 435 436 /* 437 * add_security_prop(sec, name, value, persist) 438 * 439 * Add the property to the securities structure. This accumulates 440 * properties for as part of parsing legacy options. 441 */ 442 443 static int 444 add_security_prop(struct securities *sec, char *name, char *value, 445 int persist, int iszfs) 446 { 447 sa_property_t prop; 448 int ret = SA_OK; 449 450 for (; sec != NULL; sec = sec->next) { 451 if (value == NULL) { 452 if (strcmp(name, SHOPT_RW) == 0 || 453 strcmp(name, SHOPT_RO) == 0) 454 value = "*"; 455 else 456 value = "true"; 457 } 458 459 /* 460 * Get the existing property, if it exists, so we can 461 * determine what to do with it. The ro/rw/root 462 * properties can be merged if multiple instances of 463 * these properies are given. For example, if "rw" 464 * exists with a value "host1" and a later token of 465 * rw="host2" is seen, the values are merged into a 466 * single rw="host1:host2". 467 */ 468 prop = sa_get_property(sec->security, name); 469 470 if (prop != NULL) { 471 char *oldvalue; 472 char *newvalue; 473 474 /* 475 * The security options of ro/rw/root might appear 476 * multiple times. If they do, the values need to be 477 * merged into an access list. If it was previously 478 * empty, the new value alone is added. 479 */ 480 oldvalue = sa_get_property_attr(prop, "value"); 481 if (oldvalue != NULL) { 482 /* 483 * The general case is to concatenate the new 484 * value onto the old value for multiple 485 * rw(ro/root) properties. A special case 486 * exists when either the old or new is the 487 * "all" case. In the special case, if both 488 * are "all", then it is "all", else if one is 489 * an access-list, that replaces the "all". 490 */ 491 if (strcmp(oldvalue, "*") == 0) { 492 /* Replace old value with new value. */ 493 newvalue = strdup(value); 494 } else if (strcmp(value, "*") == 0 || 495 strcmp(oldvalue, value) == 0) { 496 /* 497 * Keep old value and ignore 498 * the new value. 499 */ 500 newvalue = NULL; 501 } else { 502 /* 503 * Make a new list of old plus new 504 * access-list. 505 */ 506 newvalue = nfs_alistcat(oldvalue, 507 value, ':'); 508 } 509 510 if (newvalue != NULL) { 511 (void) sa_remove_property(prop); 512 prop = sa_create_property(name, 513 newvalue); 514 ret = sa_add_property(sec->security, 515 prop); 516 free(newvalue); 517 } 518 if (oldvalue != NULL) 519 sa_free_attr_string(oldvalue); 520 } 521 } else { 522 prop = sa_create_property(name, value); 523 ret = sa_add_property(sec->security, prop); 524 } 525 if (ret == SA_OK && !iszfs) { 526 ret = sa_commit_properties(sec->security, !persist); 527 } 528 } 529 return (ret); 530 } 531 532 /* 533 * check to see if group/share is persistent. 534 */ 535 static int 536 is_persistent(sa_group_t group) 537 { 538 char *type; 539 int persist = 1; 540 541 type = sa_get_group_attr(group, "type"); 542 if (type != NULL && strcmp(type, "persist") != 0) 543 persist = 0; 544 if (type != NULL) 545 sa_free_attr_string(type); 546 return (persist); 547 } 548 549 /* 550 * invalid_security(options) 551 * 552 * search option string for any invalid sec= type. 553 * return true (1) if any are not valid else false (0) 554 */ 555 static int 556 invalid_security(char *options) 557 { 558 char *copy, *base, *token, *value; 559 int ret = 0; 560 561 copy = strdup(options); 562 token = base = copy; 563 while (token != NULL && ret == 0) { 564 token = strtok(base, ","); 565 base = NULL; 566 if (token != NULL) { 567 value = strchr(token, '='); 568 if (value != NULL) 569 *value++ = '\0'; 570 if (strcmp(token, "sec") == 0) { 571 /* HAVE security flavors so check them */ 572 char *tok, *next; 573 for (next = NULL, tok = value; tok != NULL; 574 tok = next) { 575 next = strchr(tok, ':'); 576 if (next != NULL) 577 *next++ = '\0'; 578 ret = !nfs_validate_security_mode(tok); 579 if (ret) 580 break; 581 } 582 } 583 } 584 } 585 if (copy != NULL) 586 free(copy); 587 return (ret); 588 } 589 590 /* 591 * nfs_parse_legacy_options(group, options) 592 * 593 * Parse the old style options into internal format and store on the 594 * specified group. Group could be a share for full legacy support. 595 */ 596 597 static int 598 nfs_parse_legacy_options(sa_group_t group, char *options) 599 { 600 char *dup; 601 char *base; 602 char *token; 603 sa_optionset_t optionset; 604 struct securities *security_list = NULL; 605 sa_property_t prop; 606 int ret = SA_OK; 607 int iszfs = 0; 608 sa_group_t parent; 609 int persist = 0; 610 char *lasts; 611 612 /* do we have an existing optionset? */ 613 optionset = sa_get_optionset(group, "nfs"); 614 if (optionset == NULL) { 615 /* didn't find existing optionset so create one */ 616 optionset = sa_create_optionset(group, "nfs"); 617 } else { 618 /* 619 * Have an existing optionset . Ideally, we would need 620 * to compare options in order to detect errors. For 621 * now, we assume that the first optionset is the 622 * correct one and the others will be the same. An 623 * empty optionset is the same as no optionset so we 624 * don't want to exit in that case. Getting an empty 625 * optionset can occur with ZFS property checking. 626 */ 627 if (sa_get_property(optionset, NULL) != NULL) 628 return (ret); 629 } 630 631 if (strcmp(options, SHOPT_RW) == 0) { 632 /* 633 * there is a special case of only the option "rw" 634 * being the default option. We don't have to do 635 * anything. 636 */ 637 return (ret); 638 } 639 640 /* 641 * check if security types are present and validate them. If 642 * any are not legal, fail. 643 */ 644 645 if (invalid_security(options)) { 646 return (SA_INVALID_SECURITY); 647 } 648 649 /* 650 * in order to not attempt to change ZFS properties unless 651 * absolutely necessary, we never do it in the legacy parsing. 652 */ 653 if (sa_is_share(group)) { 654 char *zfs; 655 parent = sa_get_parent_group(group); 656 if (parent != NULL) { 657 zfs = sa_get_group_attr(parent, "zfs"); 658 if (zfs != NULL) { 659 sa_free_attr_string(zfs); 660 iszfs++; 661 } 662 } 663 } else { 664 iszfs = sa_group_is_zfs(group); 665 } 666 667 /* We need a copy of options for the next part. */ 668 dup = strdup(options); 669 if (dup == NULL) 670 return (SA_NO_MEMORY); 671 672 /* 673 * we need to step through each option in the string and then 674 * add either the option or the security option as needed. If 675 * this is not a persistent share, don't commit to the 676 * repository. If there is an error, we also want to abort the 677 * processing and report it. 678 */ 679 persist = is_persistent(group); 680 base = dup; 681 token = dup; 682 lasts = NULL; 683 while (token != NULL && ret == SA_OK) { 684 ret = SA_OK; 685 token = strtok_r(base, ",", &lasts); 686 base = NULL; 687 if (token != NULL) { 688 char *value; 689 /* 690 * if the option has a value, it will have an '=' to 691 * separate the name from the value. The following 692 * code will result in value != NULL and token 693 * pointing to just the name if there is a value. 694 */ 695 value = strchr(token, '='); 696 if (value != NULL) { 697 *value++ = '\0'; 698 } 699 if (strcmp(token, "sec") == 0 || 700 strcmp(token, "secure") == 0) { 701 /* 702 * Once in security parsing, we only 703 * do security. We do need to move 704 * between the security node and the 705 * toplevel. The security tag goes on 706 * the root while the following ones 707 * go on the security. 708 */ 709 if (security_list != NULL) { 710 /* 711 * have an old list so close it and 712 * start the new 713 */ 714 free_security_list(security_list); 715 } 716 if (strcmp(token, "secure") == 0) { 717 value = "dh"; 718 } else { 719 if (value == NULL) { 720 ret = SA_SYNTAX_ERR; 721 break; 722 } 723 } 724 security_list = make_security_list(group, 725 value, "nfs"); 726 } else { 727 /* 728 * Note that the "old" syntax allowed a 729 * default security model This must be 730 * accounted for and internally converted to 731 * "standard" security structure. 732 */ 733 if (nfs_is_security_opt(token)) { 734 if (security_list == NULL) { 735 /* 736 * need to have a 737 * security 738 * option. This will 739 * be "closed" when a 740 * defined "sec=" 741 * option is 742 * seen. This is 743 * technically an 744 * error but will be 745 * allowed with 746 * warning. 747 */ 748 security_list = 749 make_security_list(group, 750 "default", 751 "nfs"); 752 } 753 if (security_list != NULL) { 754 ret = add_security_prop( 755 security_list, token, 756 value, persist, iszfs); 757 } else { 758 ret = SA_NO_MEMORY; 759 } 760 } else { 761 /* regular options */ 762 if (value == NULL) { 763 if (strcmp(token, SHOPT_RW) == 764 0 || strcmp(token, 765 SHOPT_RO) == 0) { 766 value = "*"; 767 } else { 768 value = "global"; 769 if (strcmp(token, 770 SHOPT_LOG) != 0) { 771 value = "true"; 772 } 773 } 774 } 775 /* 776 * In all cases, create the 777 * property specified. If the 778 * value was NULL, the default 779 * value will have been 780 * substituted. 781 */ 782 prop = sa_create_property(token, value); 783 ret = sa_add_property(optionset, prop); 784 if (ret != SA_OK) 785 break; 786 787 if (!iszfs) { 788 ret = sa_commit_properties( 789 optionset, !persist); 790 } 791 } 792 } 793 } 794 } 795 if (security_list != NULL) 796 free_security_list(security_list); 797 798 free(dup); 799 return (ret); 800 } 801 802 /* 803 * is_a_number(number) 804 * 805 * is the string a number in one of the forms we want to use? 806 */ 807 808 static int 809 is_a_number(char *number) 810 { 811 int ret = 1; 812 int hex = 0; 813 814 if (strncmp(number, "0x", 2) == 0) { 815 number += 2; 816 hex = 1; 817 } else if (*number == '-') { 818 number++; /* skip the minus */ 819 } 820 while (ret == 1 && *number != '\0') { 821 if (hex) { 822 ret = isxdigit(*number++); 823 } else { 824 ret = isdigit(*number++); 825 } 826 } 827 return (ret); 828 } 829 830 /* 831 * Look for the specified tag in the configuration file. If it is found, 832 * enable logging and set the logging configuration information for exp. 833 */ 834 static void 835 configlog(struct exportdata *exp, char *tag) 836 { 837 nfsl_config_t *configlist = NULL, *configp; 838 int error = 0; 839 char globaltag[] = DEFAULTTAG; 840 841 /* 842 * Sends config errors to stderr 843 */ 844 nfsl_errs_to_syslog = B_FALSE; 845 846 /* 847 * get the list of configuration settings 848 */ 849 error = nfsl_getconfig_list(&configlist); 850 if (error) { 851 (void) fprintf(stderr, 852 dgettext(TEXT_DOMAIN, "Cannot get log configuration: %s\n"), 853 strerror(error)); 854 } 855 856 if (tag == NULL) 857 tag = globaltag; 858 if ((configp = nfsl_findconfig(configlist, tag, &error)) == NULL) { 859 nfsl_freeconfig_list(&configlist); 860 (void) fprintf(stderr, 861 dgettext(TEXT_DOMAIN, "No tags matching \"%s\"\n"), tag); 862 /* bad configuration */ 863 error = ENOENT; 864 goto err; 865 } 866 867 if ((exp->ex_tag = strdup(tag)) == NULL) { 868 error = ENOMEM; 869 goto out; 870 } 871 if ((exp->ex_log_buffer = strdup(configp->nc_bufferpath)) == NULL) { 872 error = ENOMEM; 873 goto out; 874 } 875 exp->ex_flags |= EX_LOG; 876 if (configp->nc_rpclogpath != NULL) 877 exp->ex_flags |= EX_LOG_ALLOPS; 878 out: 879 if (configlist != NULL) 880 nfsl_freeconfig_list(&configlist); 881 882 err: 883 if (error != 0) { 884 if (exp->ex_flags != NULL) 885 free(exp->ex_tag); 886 if (exp->ex_log_buffer != NULL) 887 free(exp->ex_log_buffer); 888 (void) fprintf(stderr, 889 dgettext(TEXT_DOMAIN, "Cannot set log configuration: %s\n"), 890 strerror(error)); 891 } 892 } 893 894 /* 895 * fill_export_from_optionset(export, optionset) 896 * 897 * In order to share, we need to set all the possible general options 898 * into the export structure. Share info will be filled in by the 899 * caller. Various property values get turned into structure specific 900 * values. 901 */ 902 903 static int 904 fill_export_from_optionset(struct exportdata *export, sa_optionset_t optionset) 905 { 906 sa_property_t option; 907 int ret = SA_OK; 908 909 for (option = sa_get_property(optionset, NULL); 910 option != NULL; option = sa_get_next_property(option)) { 911 char *name; 912 char *value; 913 uint32_t val; 914 915 /* 916 * since options may be set/reset multiple times, always do an 917 * explicit set or clear of the option. This allows defaults 918 * to be set and then the protocol specific to override. 919 */ 920 921 name = sa_get_property_attr(option, "type"); 922 value = sa_get_property_attr(option, "value"); 923 switch (findopt(name)) { 924 case OPT_ANON: 925 if (value != NULL && is_a_number(value)) { 926 val = strtoul(value, NULL, 0); 927 } else { 928 struct passwd *pw; 929 pw = getpwnam(value != NULL ? value : "nobody"); 930 if (pw != NULL) { 931 val = pw->pw_uid; 932 } else { 933 val = UID_NOBODY; 934 } 935 endpwent(); 936 } 937 export->ex_anon = val; 938 break; 939 case OPT_NOSUID: 940 if (value != NULL && (strcasecmp(value, "true") == 0 || 941 strcmp(value, "1") == 0)) 942 export->ex_flags |= EX_NOSUID; 943 else 944 export->ex_flags &= ~EX_NOSUID; 945 break; 946 case OPT_ACLOK: 947 if (value != NULL && (strcasecmp(value, "true") == 0 || 948 strcmp(value, "1") == 0)) 949 export->ex_flags |= EX_ACLOK; 950 else 951 export->ex_flags &= ~EX_ACLOK; 952 break; 953 case OPT_NOSUB: 954 if (value != NULL && (strcasecmp(value, "true") == 0 || 955 strcmp(value, "1") == 0)) 956 export->ex_flags |= EX_NOSUB; 957 else 958 export->ex_flags &= ~EX_NOSUB; 959 break; 960 case OPT_PUBLIC: 961 if (value != NULL && (strcasecmp(value, "true") == 0 || 962 strcmp(value, "1") == 0)) 963 export->ex_flags |= EX_PUBLIC; 964 else 965 export->ex_flags &= ~EX_PUBLIC; 966 break; 967 case OPT_INDEX: 968 if (value != NULL && (strcmp(value, "..") == 0 || 969 strchr(value, '/') != NULL)) { 970 /* this is an error */ 971 (void) printf(dgettext(TEXT_DOMAIN, 972 "NFS: index=\"%s\" not valid;" 973 "must be a filename.\n"), 974 value); 975 break; 976 } 977 if (value != NULL && *value != '\0' && 978 strcmp(value, ".") != 0) { 979 /* valid index file string */ 980 if (export->ex_index != NULL) { 981 /* left over from "default" */ 982 free(export->ex_index); 983 } 984 /* remember to free */ 985 export->ex_index = strdup(value); 986 if (export->ex_index == NULL) { 987 (void) printf(dgettext(TEXT_DOMAIN, 988 "NFS: out of memory setting " 989 "index property\n")); 990 break; 991 } 992 export->ex_flags |= EX_INDEX; 993 } 994 break; 995 case OPT_LOG: 996 if (value == NULL) 997 value = strdup("global"); 998 if (value != NULL) 999 configlog(export, 1000 strlen(value) ? value : "global"); 1001 break; 1002 case OPT_CHARSET_MAP: 1003 /* 1004 * Set EX_CHARMAP when there is at least one 1005 * charmap conversion property. This will get 1006 * checked by the nfs server when it needs to. 1007 */ 1008 export->ex_flags |= EX_CHARMAP; 1009 break; 1010 case OPT_NOACLFAB: 1011 if (value != NULL && (strcasecmp(value, "true") == 0 || 1012 strcmp(value, "1") == 0)) 1013 export->ex_flags |= EX_NOACLFAB; 1014 else 1015 export->ex_flags &= ~EX_NOACLFAB; 1016 break; 1017 default: 1018 /* have a syntactic error */ 1019 (void) printf(dgettext(TEXT_DOMAIN, 1020 "NFS: unrecognized option %s=%s\n"), 1021 name != NULL ? name : "", 1022 value != NULL ? value : ""); 1023 break; 1024 } 1025 if (name != NULL) 1026 sa_free_attr_string(name); 1027 if (value != NULL) 1028 sa_free_attr_string(value); 1029 } 1030 return (ret); 1031 } 1032 1033 /* 1034 * cleanup_export(export) 1035 * 1036 * Cleanup the allocated areas so we don't leak memory 1037 */ 1038 1039 static void 1040 cleanup_export(struct exportdata *export) 1041 { 1042 int i; 1043 1044 free(export->ex_index); 1045 1046 for (i = 0; i < export->ex_seccnt; i++) { 1047 struct secinfo *s = &export->ex_secinfo[i]; 1048 1049 while (s->s_rootcnt > 0) 1050 free(s->s_rootnames[--s->s_rootcnt]); 1051 1052 free(s->s_rootnames); 1053 } 1054 free(export->ex_secinfo); 1055 } 1056 1057 /* 1058 * Given a seconfig entry and a colon-separated 1059 * list of names, allocate an array big enough 1060 * to hold the root list, then convert each name to 1061 * a principal name according to the security 1062 * info and assign it to an array element. 1063 * Return the array and its size. 1064 */ 1065 static caddr_t * 1066 get_rootnames(seconfig_t *sec, char *list, int *count) 1067 { 1068 caddr_t *a; 1069 int c, i; 1070 char *host, *p; 1071 1072 /* 1073 * Count the number of strings in the list. 1074 * This is the number of colon separators + 1. 1075 */ 1076 c = 1; 1077 for (p = list; *p; p++) 1078 if (*p == ':') 1079 c++; 1080 *count = c; 1081 1082 a = (caddr_t *)malloc(c * sizeof (char *)); 1083 if (a == NULL) { 1084 (void) printf(dgettext(TEXT_DOMAIN, 1085 "get_rootnames: no memory\n")); 1086 } else { 1087 for (i = 0; i < c; i++) { 1088 host = strtok(list, ":"); 1089 if (!nfs_get_root_principal(sec, host, &a[i])) { 1090 while (i > 0) 1091 free(a[--i]); 1092 free(a); 1093 a = NULL; 1094 break; 1095 } 1096 list = NULL; 1097 } 1098 } 1099 1100 return (a); 1101 } 1102 1103 /* 1104 * fill_security_from_secopts(sp, secopts) 1105 * 1106 * Fill the secinfo structure from the secopts optionset. 1107 */ 1108 1109 static int 1110 fill_security_from_secopts(struct secinfo *sp, sa_security_t secopts) 1111 { 1112 sa_property_t prop; 1113 char *type; 1114 int longform; 1115 int err = SC_NOERROR; 1116 uint32_t val; 1117 1118 type = sa_get_security_attr(secopts, "sectype"); 1119 if (type != NULL) { 1120 /* named security type needs secinfo to be filled in */ 1121 err = nfs_getseconfig_byname(type, &sp->s_secinfo); 1122 sa_free_attr_string(type); 1123 if (err != SC_NOERROR) 1124 return (err); 1125 } else { 1126 /* default case */ 1127 err = nfs_getseconfig_default(&sp->s_secinfo); 1128 if (err != SC_NOERROR) 1129 return (err); 1130 } 1131 1132 err = SA_OK; 1133 for (prop = sa_get_property(secopts, NULL); 1134 prop != NULL && err == SA_OK; 1135 prop = sa_get_next_property(prop)) { 1136 char *name; 1137 char *value; 1138 1139 name = sa_get_property_attr(prop, "type"); 1140 value = sa_get_property_attr(prop, "value"); 1141 1142 longform = value != NULL && strcmp(value, "*") != 0; 1143 1144 switch (findopt(name)) { 1145 case OPT_RO: 1146 sp->s_flags |= longform ? M_ROL : M_RO; 1147 break; 1148 case OPT_RW: 1149 sp->s_flags |= longform ? M_RWL : M_RW; 1150 break; 1151 case OPT_ROOT: 1152 sp->s_flags |= M_ROOT; 1153 /* 1154 * if we are using AUTH_UNIX, handle like other things 1155 * such as RO/RW 1156 */ 1157 if (sp->s_secinfo.sc_rpcnum == AUTH_UNIX) 1158 break; 1159 /* not AUTH_UNIX */ 1160 if (value != NULL) { 1161 sp->s_rootnames = get_rootnames(&sp->s_secinfo, 1162 value, &sp->s_rootcnt); 1163 if (sp->s_rootnames == NULL) { 1164 err = SA_BAD_VALUE; 1165 (void) fprintf(stderr, 1166 dgettext(TEXT_DOMAIN, 1167 "Bad root list\n")); 1168 } 1169 } 1170 break; 1171 case OPT_NONE: 1172 sp->s_flags |= M_NONE; 1173 break; 1174 case OPT_WINDOW: 1175 if (value != NULL) { 1176 sp->s_window = atoi(value); 1177 /* just in case */ 1178 if (sp->s_window < 0) 1179 sp->s_window = DEF_WIN; 1180 } 1181 break; 1182 case OPT_ROOT_MAPPING: 1183 if (value != NULL && is_a_number(value)) { 1184 val = strtoul(value, NULL, 0); 1185 } else { 1186 struct passwd *pw; 1187 pw = getpwnam(value != NULL ? value : "nobody"); 1188 if (pw != NULL) { 1189 val = pw->pw_uid; 1190 } else { 1191 val = UID_NOBODY; 1192 } 1193 endpwent(); 1194 } 1195 sp->s_rootid = val; 1196 break; 1197 default: 1198 break; 1199 } 1200 if (name != NULL) 1201 sa_free_attr_string(name); 1202 if (value != NULL) 1203 sa_free_attr_string(value); 1204 } 1205 /* if rw/ro options not set, use default of RW */ 1206 if ((sp->s_flags & NFS_RWMODES) == 0) 1207 sp->s_flags |= M_RW; 1208 return (err); 1209 } 1210 1211 /* 1212 * This is for testing only 1213 * It displays the export structure that 1214 * goes into the kernel. 1215 */ 1216 static void 1217 printarg(char *path, struct exportdata *ep) 1218 { 1219 int i, j; 1220 struct secinfo *sp; 1221 1222 if (debug == 0) 1223 return; 1224 1225 (void) printf("%s:\n", path); 1226 (void) printf("\tex_version = %d\n", ep->ex_version); 1227 (void) printf("\tex_path = %s\n", ep->ex_path); 1228 (void) printf("\tex_pathlen = %ld\n", (ulong_t)ep->ex_pathlen); 1229 (void) printf("\tex_flags: (0x%02x) ", ep->ex_flags); 1230 if (ep->ex_flags & EX_NOSUID) 1231 (void) printf("NOSUID "); 1232 if (ep->ex_flags & EX_ACLOK) 1233 (void) printf("ACLOK "); 1234 if (ep->ex_flags & EX_PUBLIC) 1235 (void) printf("PUBLIC "); 1236 if (ep->ex_flags & EX_NOSUB) 1237 (void) printf("NOSUB "); 1238 if (ep->ex_flags & EX_LOG) 1239 (void) printf("LOG "); 1240 if (ep->ex_flags & EX_CHARMAP) 1241 (void) printf("CHARMAP "); 1242 if (ep->ex_flags & EX_LOG_ALLOPS) 1243 (void) printf("LOG_ALLOPS "); 1244 if (ep->ex_flags == 0) 1245 (void) printf("(none)"); 1246 (void) printf("\n"); 1247 if (ep->ex_flags & EX_LOG) { 1248 (void) printf("\tex_log_buffer = %s\n", 1249 (ep->ex_log_buffer ? ep->ex_log_buffer : "(NULL)")); 1250 (void) printf("\tex_tag = %s\n", 1251 (ep->ex_tag ? ep->ex_tag : "(NULL)")); 1252 } 1253 (void) printf("\tex_anon = %d\n", ep->ex_anon); 1254 (void) printf("\tex_seccnt = %d\n", ep->ex_seccnt); 1255 (void) printf("\n"); 1256 for (i = 0; i < ep->ex_seccnt; i++) { 1257 sp = &ep->ex_secinfo[i]; 1258 (void) printf("\t\ts_secinfo = %s\n", sp->s_secinfo.sc_name); 1259 (void) printf("\t\ts_flags: (0x%02x) ", sp->s_flags); 1260 if (sp->s_flags & M_ROOT) (void) printf("M_ROOT "); 1261 if (sp->s_flags & M_RO) (void) printf("M_RO "); 1262 if (sp->s_flags & M_ROL) (void) printf("M_ROL "); 1263 if (sp->s_flags & M_RW) (void) printf("M_RW "); 1264 if (sp->s_flags & M_RWL) (void) printf("M_RWL "); 1265 if (sp->s_flags & M_NONE) (void) printf("M_NONE "); 1266 if (sp->s_flags == 0) (void) printf("(none)"); 1267 (void) printf("\n"); 1268 (void) printf("\t\ts_window = %d\n", sp->s_window); 1269 (void) printf("\t\ts_rootid = %d\n", sp->s_rootid); 1270 (void) printf("\t\ts_rootcnt = %d ", sp->s_rootcnt); 1271 (void) fflush(stdout); 1272 for (j = 0; j < sp->s_rootcnt; j++) 1273 (void) printf("%s ", sp->s_rootnames[j] ? 1274 sp->s_rootnames[j] : "<null>"); 1275 (void) printf("\n\n"); 1276 } 1277 } 1278 1279 /* 1280 * count_security(opts) 1281 * 1282 * Count the number of security types (flavors). The optionset has 1283 * been populated with the security flavors as a holding mechanism. 1284 * We later use this number to allocate data structures. 1285 */ 1286 1287 static int 1288 count_security(sa_optionset_t opts) 1289 { 1290 int count = 0; 1291 sa_property_t prop; 1292 if (opts != NULL) { 1293 for (prop = sa_get_property(opts, NULL); prop != NULL; 1294 prop = sa_get_next_property(prop)) { 1295 count++; 1296 } 1297 } 1298 return (count); 1299 } 1300 1301 /* 1302 * nfs_sprint_option(rbuff, rbuffsize, incr, prop, sep) 1303 * 1304 * provides a mechanism to format NFS properties into legacy output 1305 * format. If the buffer would overflow, it is reallocated and grown 1306 * as appropriate. Special cases of converting internal form of values 1307 * to those used by "share" are done. this function does one property 1308 * at a time. 1309 */ 1310 1311 static int 1312 nfs_sprint_option(char **rbuff, size_t *rbuffsize, size_t incr, 1313 sa_property_t prop, int sep) 1314 { 1315 char *name; 1316 char *value; 1317 int curlen; 1318 char *buff = *rbuff; 1319 size_t buffsize = *rbuffsize; 1320 int printed = B_FALSE; 1321 1322 name = sa_get_property_attr(prop, "type"); 1323 value = sa_get_property_attr(prop, "value"); 1324 if (buff != NULL) 1325 curlen = strlen(buff); 1326 else 1327 curlen = 0; 1328 if (name != NULL) { 1329 int len; 1330 len = strlen(name) + sep; 1331 1332 /* 1333 * A future RFE would be to replace this with more 1334 * generic code and to possibly handle more types. 1335 */ 1336 switch (gettype(name)) { 1337 case OPT_TYPE_BOOLEAN: 1338 /* 1339 * For NFS, boolean value of FALSE means it 1340 * doesn't show up in the option list at all. 1341 */ 1342 if (value != NULL && strcasecmp(value, "false") == 0) 1343 goto skip; 1344 if (value != NULL) { 1345 sa_free_attr_string(value); 1346 value = NULL; 1347 } 1348 break; 1349 case OPT_TYPE_ACCLIST: 1350 if (value != NULL && strcmp(value, "*") == 0) { 1351 sa_free_attr_string(value); 1352 value = NULL; 1353 } else { 1354 if (value != NULL) 1355 len += 1 + strlen(value); 1356 } 1357 break; 1358 case OPT_TYPE_LOGTAG: 1359 if (value != NULL && strlen(value) == 0) { 1360 sa_free_attr_string(value); 1361 value = NULL; 1362 } else { 1363 if (value != NULL) 1364 len += 1 + strlen(value); 1365 } 1366 break; 1367 default: 1368 if (value != NULL) 1369 len += 1 + strlen(value); 1370 break; 1371 } 1372 while (buffsize <= (curlen + len)) { 1373 /* need more room */ 1374 buffsize += incr; 1375 buff = realloc(buff, buffsize); 1376 if (buff == NULL) { 1377 /* realloc failed so free everything */ 1378 if (*rbuff != NULL) 1379 free(*rbuff); 1380 } 1381 *rbuff = buff; 1382 *rbuffsize = buffsize; 1383 if (buff == NULL) 1384 goto skip; 1385 1386 } 1387 1388 if (buff == NULL) 1389 goto skip; 1390 1391 if (value == NULL) { 1392 (void) snprintf(buff + curlen, buffsize - curlen, 1393 "%s%s", sep ? "," : "", 1394 name, value != NULL ? value : ""); 1395 } else { 1396 (void) snprintf(buff + curlen, buffsize - curlen, 1397 "%s%s=%s", sep ? "," : "", 1398 name, value != NULL ? value : ""); 1399 } 1400 printed = B_TRUE; 1401 } 1402 skip: 1403 if (name != NULL) 1404 sa_free_attr_string(name); 1405 if (value != NULL) 1406 sa_free_attr_string(value); 1407 return (printed); 1408 } 1409 1410 /* 1411 * nfs_format_options(group, hier) 1412 * 1413 * format all the options on the group into an old-style option 1414 * string. If hier is non-zero, walk up the tree to get inherited 1415 * options. 1416 */ 1417 1418 static char * 1419 nfs_format_options(sa_group_t group, int hier) 1420 { 1421 sa_optionset_t options = NULL; 1422 sa_optionset_t secoptions = NULL; 1423 sa_property_t prop, secprop; 1424 sa_security_t security = NULL; 1425 char *buff; 1426 size_t buffsize; 1427 char *sectype = NULL; 1428 int sep = 0; 1429 1430 1431 buff = malloc(OPT_CHUNK); 1432 if (buff == NULL) { 1433 return (NULL); 1434 } 1435 1436 buff[0] = '\0'; 1437 buffsize = OPT_CHUNK; 1438 1439 /* 1440 * We may have a an optionset relative to this item. format 1441 * these if we find them and then add any security definitions. 1442 */ 1443 1444 options = sa_get_derived_optionset(group, "nfs", hier); 1445 1446 /* 1447 * do the default set first but skip any option that is also 1448 * in the protocol specific optionset. 1449 */ 1450 if (options != NULL) { 1451 for (prop = sa_get_property(options, NULL); 1452 prop != NULL; prop = sa_get_next_property(prop)) { 1453 /* 1454 * use this one since we skipped any 1455 * of these that were also in 1456 * optdefault 1457 */ 1458 if (nfs_sprint_option(&buff, &buffsize, OPT_CHUNK, 1459 prop, sep)) 1460 sep = 1; 1461 if (buff == NULL) { 1462 /* 1463 * buff could become NULL if there 1464 * isn't enough memory for 1465 * nfs_sprint_option to realloc() 1466 * as necessary. We can't really 1467 * do anything about it at this 1468 * point so we return NULL. The 1469 * caller should handle the 1470 * failure. 1471 */ 1472 if (options != NULL) 1473 sa_free_derived_optionset( 1474 options); 1475 return (buff); 1476 } 1477 } 1478 } 1479 secoptions = (sa_optionset_t)sa_get_all_security_types(group, 1480 "nfs", hier); 1481 if (secoptions != NULL) { 1482 for (secprop = sa_get_property(secoptions, NULL); 1483 secprop != NULL; 1484 secprop = sa_get_next_property(secprop)) { 1485 sectype = sa_get_property_attr(secprop, "type"); 1486 security = 1487 (sa_security_t)sa_get_derived_security( 1488 group, sectype, "nfs", hier); 1489 if (security != NULL) { 1490 if (sectype != NULL) { 1491 prop = sa_create_property( 1492 "sec", sectype); 1493 if (prop == NULL) 1494 goto err; 1495 if (nfs_sprint_option(&buff, 1496 &buffsize, OPT_CHUNK, prop, sep)) 1497 sep = 1; 1498 (void) sa_remove_property(prop); 1499 if (buff == NULL) 1500 goto err; 1501 } 1502 for (prop = sa_get_property(security, 1503 NULL); prop != NULL; 1504 prop = sa_get_next_property(prop)) { 1505 if (nfs_sprint_option(&buff, 1506 &buffsize, OPT_CHUNK, prop, sep)) 1507 sep = 1; 1508 if (buff == NULL) 1509 goto err; 1510 } 1511 sa_free_derived_optionset(security); 1512 } 1513 if (sectype != NULL) 1514 sa_free_attr_string(sectype); 1515 } 1516 sa_free_derived_optionset(secoptions); 1517 } 1518 1519 if (options != NULL) 1520 sa_free_derived_optionset(options); 1521 return (buff); 1522 1523 err: 1524 /* 1525 * If we couldn't allocate memory for option printing, we need 1526 * to break out of the nested loops, cleanup and return NULL. 1527 */ 1528 if (secoptions != NULL) 1529 sa_free_derived_optionset(secoptions); 1530 if (security != NULL) 1531 sa_free_derived_optionset(security); 1532 if (sectype != NULL) 1533 sa_free_attr_string(sectype); 1534 if (options != NULL) 1535 sa_free_derived_optionset(options); 1536 return (buff); 1537 } 1538 1539 /* 1540 * Append an entry to the nfslogtab file 1541 */ 1542 static int 1543 nfslogtab_add(dir, buffer, tag) 1544 char *dir, *buffer, *tag; 1545 { 1546 FILE *f; 1547 struct logtab_ent lep; 1548 int error = 0; 1549 1550 /* 1551 * Open the file for update and create it if necessary. 1552 * This may leave the I/O offset at the end of the file, 1553 * so rewind back to the beginning of the file. 1554 */ 1555 f = fopen(NFSLOGTAB, "a+"); 1556 if (f == NULL) { 1557 error = errno; 1558 goto out; 1559 } 1560 rewind(f); 1561 1562 if (lockf(fileno(f), F_LOCK, 0L) < 0) { 1563 (void) fprintf(stderr, dgettext(TEXT_DOMAIN, 1564 "share complete, however failed to lock %s " 1565 "for update: %s\n"), NFSLOGTAB, strerror(errno)); 1566 error = -1; 1567 goto out; 1568 } 1569 1570 if (logtab_deactivate_after_boot(f) == -1) { 1571 (void) fprintf(stderr, dgettext(TEXT_DOMAIN, 1572 "share complete, however could not deactivate " 1573 "entries in %s\n"), NFSLOGTAB); 1574 error = -1; 1575 goto out; 1576 } 1577 1578 /* 1579 * Remove entries matching buffer and sharepoint since we're 1580 * going to replace it with perhaps an entry with a new tag. 1581 */ 1582 if (logtab_rement(f, buffer, dir, NULL, -1)) { 1583 (void) fprintf(stderr, dgettext(TEXT_DOMAIN, 1584 "share complete, however could not remove matching " 1585 "entries in %s\n"), NFSLOGTAB); 1586 error = -1; 1587 goto out; 1588 } 1589 1590 /* 1591 * Deactivate all active entries matching this sharepoint 1592 */ 1593 if (logtab_deactivate(f, NULL, dir, NULL)) { 1594 (void) fprintf(stderr, dgettext(TEXT_DOMAIN, 1595 "share complete, however could not deactivate matching " 1596 "entries in %s\n"), NFSLOGTAB); 1597 error = -1; 1598 goto out; 1599 } 1600 1601 lep.le_buffer = buffer; 1602 lep.le_path = dir; 1603 lep.le_tag = tag; 1604 lep.le_state = LES_ACTIVE; 1605 1606 /* 1607 * Add new sharepoint / buffer location to nfslogtab 1608 */ 1609 if (logtab_putent(f, &lep) < 0) { 1610 (void) fprintf(stderr, dgettext(TEXT_DOMAIN, 1611 "share complete, however could not add %s to %s\n"), 1612 dir, NFSLOGTAB); 1613 error = -1; 1614 } 1615 1616 out: 1617 if (f != NULL) 1618 (void) fclose(f); 1619 return (error); 1620 } 1621 1622 /* 1623 * Deactivate an entry from the nfslogtab file 1624 */ 1625 static int 1626 nfslogtab_deactivate(path) 1627 char *path; 1628 { 1629 FILE *f; 1630 int error = 0; 1631 1632 f = fopen(NFSLOGTAB, "r+"); 1633 if (f == NULL) { 1634 error = errno; 1635 goto out; 1636 } 1637 if (lockf(fileno(f), F_LOCK, 0L) < 0) { 1638 error = errno; 1639 (void) fprintf(stderr, dgettext(TEXT_DOMAIN, 1640 "share complete, however could not lock %s for " 1641 "update: %s\n"), NFSLOGTAB, strerror(error)); 1642 goto out; 1643 } 1644 if (logtab_deactivate(f, NULL, path, NULL) == -1) { 1645 error = -1; 1646 (void) fprintf(stderr, 1647 dgettext(TEXT_DOMAIN, 1648 "share complete, however could not " 1649 "deactivate %s in %s\n"), path, NFSLOGTAB); 1650 goto out; 1651 } 1652 1653 out: if (f != NULL) 1654 (void) fclose(f); 1655 1656 return (error); 1657 } 1658 1659 /* 1660 * check_public(group, skipshare) 1661 * 1662 * Check the group for any shares that have the public property 1663 * enabled. We skip "skipshare" since that is the one we are 1664 * working with. This is a separate function to make handling 1665 * subgroups simpler. Returns true if there is a share with public. 1666 */ 1667 static int 1668 check_public(sa_group_t group, sa_share_t skipshare) 1669 { 1670 int exists = B_FALSE; 1671 sa_share_t share; 1672 sa_optionset_t opt; 1673 sa_property_t prop; 1674 char *shared; 1675 1676 for (share = sa_get_share(group, NULL); share != NULL; 1677 share = sa_get_next_share(share)) { 1678 if (share == skipshare) 1679 continue; 1680 1681 opt = sa_get_optionset(share, "nfs"); 1682 if (opt == NULL) 1683 continue; 1684 prop = sa_get_property(opt, "public"); 1685 if (prop == NULL) 1686 continue; 1687 shared = sa_get_share_attr(share, "shared"); 1688 if (shared != NULL) { 1689 exists = strcmp(shared, "true") == 0; 1690 sa_free_attr_string(shared); 1691 if (exists == B_TRUE) 1692 break; 1693 } 1694 } 1695 1696 return (exists); 1697 } 1698 1699 /* 1700 * public_exists(handle, share) 1701 * 1702 * check to see if public option is set on any other share than the 1703 * one specified. Need to check zfs sub-groups as well as the top 1704 * level groups. 1705 */ 1706 static int 1707 public_exists(sa_handle_t handle, sa_share_t skipshare) 1708 { 1709 sa_group_t group = NULL; 1710 1711 /* 1712 * If we don't have a handle, we can only do syntax check. We 1713 * can't check against other shares so we assume OK and will 1714 * catch the problem only when we actually try to apply it. 1715 */ 1716 if (handle == NULL) 1717 return (SA_OK); 1718 1719 if (skipshare != NULL) { 1720 group = sa_get_parent_group(skipshare); 1721 if (group == NULL) 1722 return (SA_NO_SUCH_GROUP); 1723 } 1724 1725 for (group = sa_get_group(handle, NULL); group != NULL; 1726 group = sa_get_next_group(group)) { 1727 /* Walk any ZFS subgroups as well as all standard groups */ 1728 if (sa_group_is_zfs(group)) { 1729 sa_group_t subgroup; 1730 for (subgroup = sa_get_sub_group(group); 1731 subgroup != NULL; 1732 subgroup = sa_get_next_group(subgroup)) { 1733 if (check_public(subgroup, skipshare)) 1734 return (B_TRUE); 1735 } 1736 } else { 1737 if (check_public(group, skipshare)) 1738 return (B_TRUE); 1739 } 1740 } 1741 return (B_FALSE); 1742 } 1743 1744 /* 1745 * sa_enable_share at the protocol level, enable_share must tell the 1746 * implementation that it is to enable the share. This entails 1747 * converting the path and options into the appropriate ioctl 1748 * calls. It is assumed that all error checking of paths, etc. were 1749 * done earlier. 1750 */ 1751 static int 1752 nfs_enable_share(sa_share_t share) 1753 { 1754 struct exportdata export; 1755 sa_optionset_t secoptlist; 1756 struct secinfo *sp; 1757 int num_secinfo; 1758 sa_optionset_t opt; 1759 sa_security_t sec; 1760 sa_property_t prop; 1761 char *path; 1762 int err = SA_OK; 1763 int i; 1764 int iszfs; 1765 sa_handle_t handle; 1766 1767 /* Don't drop core if the NFS module isn't loaded. */ 1768 (void) signal(SIGSYS, SIG_IGN); 1769 1770 /* get the path since it is important in several places */ 1771 path = sa_get_share_attr(share, "path"); 1772 if (path == NULL) 1773 return (SA_NO_SUCH_PATH); 1774 1775 iszfs = sa_path_is_zfs(path); 1776 /* 1777 * find the optionsets and security sets. There may not be 1778 * any or there could be one or two for each of optionset and 1779 * security may have multiple, one per security type per 1780 * protocol type. 1781 */ 1782 opt = sa_get_derived_optionset(share, "nfs", 1); 1783 secoptlist = (sa_optionset_t)sa_get_all_security_types(share, "nfs", 1); 1784 if (secoptlist != NULL) 1785 num_secinfo = MAX(1, count_security(secoptlist)); 1786 else 1787 num_secinfo = 1; 1788 1789 /* 1790 * walk through the options and fill in the structure 1791 * appropriately. 1792 */ 1793 1794 (void) memset(&export, '\0', sizeof (export)); 1795 1796 /* 1797 * do non-security options first since there is only one after 1798 * the derived group is constructed. 1799 */ 1800 export.ex_version = EX_CURRENT_VERSION; 1801 export.ex_anon = UID_NOBODY; /* this is our default value */ 1802 export.ex_index = NULL; 1803 export.ex_path = path; 1804 export.ex_pathlen = strlen(path) + 1; 1805 1806 if (opt != NULL) 1807 err = fill_export_from_optionset(&export, opt); 1808 1809 /* 1810 * check to see if "public" is set. If it is, then make sure 1811 * no other share has it set. If it is already used, fail. 1812 */ 1813 1814 handle = sa_find_group_handle((sa_group_t)share); 1815 if (export.ex_flags & EX_PUBLIC && public_exists(handle, share)) { 1816 (void) printf(dgettext(TEXT_DOMAIN, 1817 "NFS: Cannot share more than one file " 1818 "system with 'public' property\n")); 1819 err = SA_NOT_ALLOWED; 1820 goto out; 1821 } 1822 1823 sp = calloc(num_secinfo, sizeof (struct secinfo)); 1824 if (sp == NULL) { 1825 err = SA_NO_MEMORY; 1826 (void) printf(dgettext(TEXT_DOMAIN, 1827 "NFS: NFS: no memory for security\n")); 1828 goto out; 1829 } 1830 export.ex_secinfo = sp; 1831 /* get default secinfo */ 1832 export.ex_seccnt = num_secinfo; 1833 /* 1834 * since we must have one security option defined, we 1835 * init to the default and then override as we find 1836 * defined security options. This handles the case 1837 * where we have no defined options but we need to set 1838 * up one. 1839 */ 1840 sp[0].s_window = DEF_WIN; 1841 sp[0].s_rootnames = NULL; 1842 /* setup a default in case no properties defined */ 1843 if (nfs_getseconfig_default(&sp[0].s_secinfo)) { 1844 (void) printf(dgettext(TEXT_DOMAIN, 1845 "NFS: nfs_getseconfig_default: failed to " 1846 "get default security mode\n")); 1847 err = SA_CONFIG_ERR; 1848 } 1849 if (secoptlist != NULL) { 1850 for (i = 0, prop = sa_get_property(secoptlist, NULL); 1851 prop != NULL && i < num_secinfo; 1852 prop = sa_get_next_property(prop), i++) { 1853 char *sectype; 1854 sectype = sa_get_property_attr(prop, "type"); 1855 /* 1856 * if sectype is NULL, we probably 1857 * have a memory problem and can't get 1858 * the correct values. Rather than 1859 * exporting with incorrect security, 1860 * don't share it. 1861 */ 1862 if (sectype == NULL) { 1863 err = SA_NO_MEMORY; 1864 (void) printf(dgettext(TEXT_DOMAIN, 1865 "NFS: Cannot share %s: " 1866 "no memory\n"), path); 1867 goto out; 1868 } 1869 sec = (sa_security_t)sa_get_derived_security( 1870 share, sectype, "nfs", 1); 1871 sp[i].s_window = DEF_WIN; 1872 sp[i].s_rootcnt = 0; 1873 sp[i].s_rootnames = NULL; 1874 (void) fill_security_from_secopts(&sp[i], sec); 1875 if (sec != NULL) 1876 sa_free_derived_security(sec); 1877 if (sectype != NULL) 1878 sa_free_attr_string(sectype); 1879 } 1880 } 1881 /* 1882 * when we get here, we can do the exportfs system call and 1883 * initiate thinsg. We probably want to enable the nfs.server 1884 * service first if it isn't running within SMF. 1885 */ 1886 /* check nfs.server status and start if needed */ 1887 /* now add the share to the internal tables */ 1888 printarg(path, &export); 1889 /* 1890 * call the exportfs system call which is implemented 1891 * via the nfssys() call as the EXPORTFS subfunction. 1892 */ 1893 if (iszfs) { 1894 struct exportfs_args ea; 1895 share_t sh; 1896 char *str; 1897 priv_set_t *priv_effective; 1898 int privileged; 1899 1900 /* 1901 * If we aren't a privileged user 1902 * and NFS server service isn't running 1903 * then print out an error message 1904 * and return EPERM 1905 */ 1906 1907 priv_effective = priv_allocset(); 1908 (void) getppriv(PRIV_EFFECTIVE, priv_effective); 1909 1910 privileged = (priv_isfullset(priv_effective) == B_TRUE); 1911 priv_freeset(priv_effective); 1912 1913 if (!privileged && 1914 (str = smf_get_state(NFS_SERVER_SVC)) != NULL) { 1915 err = 0; 1916 if (strcmp(str, SCF_STATE_STRING_ONLINE) != 0) { 1917 (void) printf(dgettext(TEXT_DOMAIN, 1918 "NFS: Cannot share remote " 1919 "filesystem: %s\n"), path); 1920 (void) printf(dgettext(TEXT_DOMAIN, 1921 "NFS: Service needs to be enabled " 1922 "by a privileged user\n")); 1923 err = SA_SYSTEM_ERR; 1924 errno = EPERM; 1925 } 1926 free(str); 1927 } 1928 1929 if (err == 0) { 1930 ea.dname = path; 1931 ea.uex = &export; 1932 1933 (void) sa_sharetab_fill_zfs(share, &sh, "nfs"); 1934 err = sa_share_zfs(share, NULL, path, &sh, 1935 &ea, ZFS_SHARE_NFS); 1936 if (err != SA_OK) { 1937 errno = err; 1938 err = -1; 1939 } 1940 sa_emptyshare(&sh); 1941 } 1942 } else { 1943 err = exportfs(path, &export); 1944 } 1945 1946 if (err < 0) { 1947 err = SA_SYSTEM_ERR; 1948 switch (errno) { 1949 case EREMOTE: 1950 (void) printf(dgettext(TEXT_DOMAIN, 1951 "NFS: Cannot share filesystems " 1952 "in non-global zones: %s\n"), path); 1953 err = SA_NOT_SUPPORTED; 1954 break; 1955 case EPERM: 1956 if (getzoneid() != GLOBAL_ZONEID) { 1957 (void) printf(dgettext(TEXT_DOMAIN, 1958 "NFS: Cannot share file systems " 1959 "in non-global zones: %s\n"), path); 1960 err = SA_NOT_SUPPORTED; 1961 break; 1962 } 1963 err = SA_NO_PERMISSION; 1964 break; 1965 case EEXIST: 1966 err = SA_SHARE_EXISTS; 1967 break; 1968 default: 1969 break; 1970 } 1971 } else { 1972 /* update sharetab with an add/modify */ 1973 if (!iszfs) { 1974 (void) sa_update_sharetab(share, "nfs"); 1975 } 1976 } 1977 1978 if (err == SA_OK) { 1979 /* 1980 * enable services as needed. This should probably be 1981 * done elsewhere in order to minimize the calls to 1982 * check services. 1983 */ 1984 /* 1985 * check to see if logging and other services need to 1986 * be triggered, but only if there wasn't an 1987 * error. This is probably where sharetab should be 1988 * updated with the NFS specific entry. 1989 */ 1990 if (export.ex_flags & EX_LOG) { 1991 /* enable logging */ 1992 if (nfslogtab_add(path, export.ex_log_buffer, 1993 export.ex_tag) != 0) { 1994 (void) fprintf(stderr, dgettext(TEXT_DOMAIN, 1995 "Could not enable logging for %s\n"), 1996 path); 1997 } 1998 _check_services(service_list_logging); 1999 } else { 2000 /* 2001 * don't have logging so remove it from file. It might 2002 * not be thre, but that doesn't matter. 2003 */ 2004 (void) nfslogtab_deactivate(path); 2005 _check_services(service_list_default); 2006 } 2007 } 2008 2009 out: 2010 if (path != NULL) 2011 free(path); 2012 2013 cleanup_export(&export); 2014 if (opt != NULL) 2015 sa_free_derived_optionset(opt); 2016 if (secoptlist != NULL) 2017 (void) sa_destroy_optionset(secoptlist); 2018 return (err); 2019 } 2020 2021 /* 2022 * nfs_disable_share(share, path) 2023 * 2024 * Unshare the specified share. Note that "path" is the same path as 2025 * what is in the "share" object. It is passed in to avoid an 2026 * additional lookup. A missing "path" value makes this a no-op 2027 * function. 2028 */ 2029 static int 2030 nfs_disable_share(sa_share_t share, char *path) 2031 { 2032 int err; 2033 int ret = SA_OK; 2034 int iszfs; 2035 sa_group_t parent; 2036 sa_handle_t handle; 2037 2038 if (path == NULL) 2039 return (ret); 2040 2041 /* 2042 * If the share is in a ZFS group we need to handle it 2043 * differently. Just being on a ZFS file system isn't 2044 * enough since we may be in a legacy share case. 2045 */ 2046 parent = sa_get_parent_group(share); 2047 iszfs = sa_group_is_zfs(parent); 2048 if (iszfs) { 2049 struct exportfs_args ea; 2050 share_t sh = { 0 }; 2051 ea.dname = path; 2052 ea.uex = NULL; 2053 sh.sh_path = path; 2054 sh.sh_fstype = "nfs"; 2055 2056 err = sa_share_zfs(share, NULL, path, &sh, 2057 &ea, ZFS_UNSHARE_NFS); 2058 if (err != SA_OK) { 2059 errno = err; 2060 err = -1; 2061 } 2062 } else { 2063 err = exportfs(path, NULL); 2064 } 2065 if (err < 0) { 2066 /* 2067 * TBD: only an error in some 2068 * cases - need better analysis 2069 */ 2070 switch (errno) { 2071 case EPERM: 2072 case EACCES: 2073 ret = SA_NO_PERMISSION; 2074 if (getzoneid() != GLOBAL_ZONEID) { 2075 ret = SA_NOT_SUPPORTED; 2076 } 2077 break; 2078 case EINVAL: 2079 case ENOENT: 2080 ret = SA_NO_SUCH_PATH; 2081 break; 2082 default: 2083 ret = SA_SYSTEM_ERR; 2084 break; 2085 } 2086 } 2087 if (ret == SA_OK || ret == SA_NO_SUCH_PATH) { 2088 handle = sa_find_group_handle((sa_group_t)share); 2089 if (!iszfs) 2090 (void) sa_delete_sharetab(handle, path, "nfs"); 2091 /* just in case it was logged */ 2092 (void) nfslogtab_deactivate(path); 2093 } 2094 return (ret); 2095 } 2096 2097 /* 2098 * check_rorwnone(v1, v2, v3) 2099 * 2100 * check ro vs rw vs none values. Over time this may get beefed up. 2101 * for now it just does simple checks. v1 is never NULL but v2 or v3 2102 * could be. 2103 */ 2104 2105 static int 2106 check_rorwnone(char *v1, char *v2, char *v3) 2107 { 2108 int ret = SA_OK; 2109 if (v2 != NULL && strcmp(v1, v2) == 0) 2110 ret = SA_VALUE_CONFLICT; 2111 else if (v3 != NULL && strcmp(v1, v3) == 0) 2112 ret = SA_VALUE_CONFLICT; 2113 2114 return (ret); 2115 } 2116 2117 /* 2118 * nfs_validate_property(handle, property, parent) 2119 * 2120 * Check that the property has a legitimate value for its type. 2121 */ 2122 2123 static int 2124 nfs_validate_property(sa_handle_t handle, sa_property_t property, 2125 sa_optionset_t parent) 2126 { 2127 int ret = SA_OK; 2128 char *propname; 2129 char *other1; 2130 char *other2; 2131 int optindex; 2132 nfsl_config_t *configlist; 2133 sa_group_t parent_group; 2134 char *value; 2135 2136 propname = sa_get_property_attr(property, "type"); 2137 2138 if ((optindex = findopt(propname)) < 0) 2139 ret = SA_NO_SUCH_PROP; 2140 2141 /* need to validate value range here as well */ 2142 2143 if (ret == SA_OK) { 2144 parent_group = sa_get_parent_group((sa_share_t)parent); 2145 if (optdefs[optindex].share && parent_group != NULL && 2146 !sa_is_share(parent_group)) 2147 ret = SA_PROP_SHARE_ONLY; 2148 } 2149 if (ret == SA_OK) { 2150 if (optdefs[optindex].index == OPT_PUBLIC) { 2151 /* 2152 * Public is special in that only one instance can 2153 * be in the repository at the same time. 2154 */ 2155 if (public_exists(handle, parent_group)) { 2156 sa_free_attr_string(propname); 2157 return (SA_VALUE_CONFLICT); 2158 } 2159 } 2160 value = sa_get_property_attr(property, "value"); 2161 if (value != NULL) { 2162 /* first basic type checking */ 2163 switch (optdefs[optindex].type) { 2164 case OPT_TYPE_NUMBER: 2165 /* check that the value is all digits */ 2166 if (!is_a_number(value)) 2167 ret = SA_BAD_VALUE; 2168 break; 2169 case OPT_TYPE_BOOLEAN: 2170 if (strlen(value) == 0 || 2171 strcasecmp(value, "true") == 0 || 2172 strcmp(value, "1") == 0 || 2173 strcasecmp(value, "false") == 0 || 2174 strcmp(value, "0") == 0) { 2175 ret = SA_OK; 2176 } else { 2177 ret = SA_BAD_VALUE; 2178 } 2179 break; 2180 case OPT_TYPE_USER: 2181 if (!is_a_number(value)) { 2182 struct passwd *pw; 2183 /* 2184 * in this case it would have to be a 2185 * user name 2186 */ 2187 pw = getpwnam(value); 2188 if (pw == NULL) 2189 ret = SA_BAD_VALUE; 2190 endpwent(); 2191 } else { 2192 uint64_t intval; 2193 intval = strtoull(value, NULL, 0); 2194 if (intval > UID_MAX && intval != ~0) 2195 ret = SA_BAD_VALUE; 2196 } 2197 break; 2198 case OPT_TYPE_FILE: 2199 if (strcmp(value, "..") == 0 || 2200 strchr(value, '/') != NULL) { 2201 ret = SA_BAD_VALUE; 2202 } 2203 break; 2204 case OPT_TYPE_ACCLIST: { 2205 sa_property_t oprop1; 2206 sa_property_t oprop2; 2207 char *ovalue1 = NULL; 2208 char *ovalue2 = NULL; 2209 2210 if (parent == NULL) 2211 break; 2212 /* 2213 * access list handling. Should eventually 2214 * validate that all the values make sense. 2215 * Also, ro and rw may have cross value 2216 * conflicts. 2217 */ 2218 if (strcmp(propname, SHOPT_RO) == 0) { 2219 other1 = SHOPT_RW; 2220 other2 = SHOPT_NONE; 2221 } else if (strcmp(propname, SHOPT_RW) == 0) { 2222 other1 = SHOPT_RO; 2223 other2 = SHOPT_NONE; 2224 } else if (strcmp(propname, SHOPT_NONE) == 0) { 2225 other1 = SHOPT_RO; 2226 other2 = SHOPT_RW; 2227 } else { 2228 other1 = NULL; 2229 other2 = NULL; 2230 } 2231 if (other1 == NULL && other2 == NULL) 2232 break; 2233 2234 /* compare rw(ro) with ro(rw) */ 2235 2236 oprop1 = sa_get_property(parent, other1); 2237 oprop2 = sa_get_property(parent, other2); 2238 if (oprop1 == NULL && oprop2 == NULL) 2239 break; 2240 /* 2241 * Only potential confusion if other1 2242 * or other2 exists. Check the values 2243 * and run the check if there is a 2244 * value other than the one we are 2245 * explicitly looking at. 2246 */ 2247 ovalue1 = sa_get_property_attr(oprop1, "value"); 2248 ovalue2 = sa_get_property_attr(oprop2, "value"); 2249 if (ovalue1 != NULL || ovalue2 != NULL) 2250 ret = check_rorwnone(value, ovalue1, 2251 ovalue2); 2252 2253 if (ovalue1 != NULL) 2254 sa_free_attr_string(ovalue1); 2255 if (ovalue2 != NULL) 2256 sa_free_attr_string(ovalue2); 2257 break; 2258 } 2259 case OPT_TYPE_LOGTAG: 2260 if (nfsl_getconfig_list(&configlist) == 0) { 2261 int error; 2262 if (value == NULL || 2263 strlen(value) == 0) { 2264 if (value != NULL) 2265 sa_free_attr_string( 2266 value); 2267 value = strdup("global"); 2268 } 2269 if (value != NULL && 2270 nfsl_findconfig(configlist, value, 2271 &error) == NULL) { 2272 ret = SA_BAD_VALUE; 2273 } 2274 /* Must always free when done */ 2275 nfsl_freeconfig_list(&configlist); 2276 } else { 2277 ret = SA_CONFIG_ERR; 2278 } 2279 break; 2280 case OPT_TYPE_STRING: 2281 /* whatever is here should be ok */ 2282 break; 2283 case OPT_TYPE_SECURITY: 2284 /* 2285 * The "sec" property isn't used in the 2286 * non-legacy parts of sharemgr. We need to 2287 * reject it here. For legacy, it is pulled 2288 * out well before we get here. 2289 */ 2290 ret = SA_NO_SUCH_PROP; 2291 break; 2292 default: 2293 break; 2294 } 2295 2296 if (value != NULL) 2297 sa_free_attr_string(value); 2298 2299 if (ret == SA_OK && optdefs[optindex].check != NULL) { 2300 /* do the property specific check */ 2301 ret = optdefs[optindex].check(handle, property); 2302 } 2303 } 2304 } 2305 2306 if (propname != NULL) 2307 sa_free_attr_string(propname); 2308 return (ret); 2309 } 2310 2311 /* 2312 * Protocol management functions 2313 * 2314 * Properties defined in the default files are defined in 2315 * proto_option_defs for parsing and validation. If "other" and 2316 * "compare" are set, then the value for this property should be 2317 * compared against the property specified in "other" using the 2318 * "compare" check (either <= or >=) in order to ensure that the 2319 * values are in the correct range. E.g. setting server_versmin 2320 * higher than server_versmax should not be allowed. 2321 */ 2322 2323 struct proto_option_defs { 2324 char *tag; 2325 char *name; /* display name -- remove protocol identifier */ 2326 int index; 2327 int type; 2328 union { 2329 int intval; 2330 char *string; 2331 } defvalue; 2332 uint32_t svcs; 2333 int32_t minval; 2334 int32_t maxval; 2335 char *other; 2336 int compare; 2337 #define OPT_CMP_GE 0 2338 #define OPT_CMP_LE 1 2339 int (*check)(char *); 2340 } proto_options[] = { 2341 #define PROTO_OPT_NFSD_SERVERS 0 2342 {"nfsd_servers", 2343 "servers", PROTO_OPT_NFSD_SERVERS, OPT_TYPE_NUMBER, 16, SVC_NFSD, 2344 1, INT32_MAX}, 2345 #define PROTO_OPT_LOCKD_LISTEN_BACKLOG 1 2346 {"lockd_listen_backlog", 2347 "lockd_listen_backlog", PROTO_OPT_LOCKD_LISTEN_BACKLOG, 2348 OPT_TYPE_NUMBER, 32, SVC_LOCKD, 32, INT32_MAX}, 2349 #define PROTO_OPT_LOCKD_SERVERS 2 2350 {"lockd_servers", 2351 "lockd_servers", PROTO_OPT_LOCKD_SERVERS, OPT_TYPE_NUMBER, 20, 2352 SVC_LOCKD, 1, INT32_MAX}, 2353 #define PROTO_OPT_LOCKD_RETRANSMIT_TIMEOUT 3 2354 {"lockd_retransmit_timeout", 2355 "lockd_retransmit_timeout", PROTO_OPT_LOCKD_RETRANSMIT_TIMEOUT, 2356 OPT_TYPE_NUMBER, 5, SVC_LOCKD, 0, INT32_MAX}, 2357 #define PROTO_OPT_GRACE_PERIOD 4 2358 {"grace_period", 2359 "grace_period", PROTO_OPT_GRACE_PERIOD, OPT_TYPE_NUMBER, 90, 2360 SVC_LOCKD, 0, INT32_MAX}, 2361 #define PROTO_OPT_NFS_SERVER_VERSMIN 5 2362 {"nfs_server_versmin", 2363 "server_versmin", PROTO_OPT_NFS_SERVER_VERSMIN, OPT_TYPE_NUMBER, 2364 (int)NFS_VERSMIN_DEFAULT, SVC_NFSD|SVC_MOUNTD, NFS_VERSMIN, 2365 NFS_VERSMAX, "server_versmax", OPT_CMP_LE}, 2366 #define PROTO_OPT_NFS_SERVER_VERSMAX 6 2367 {"nfs_server_versmax", 2368 "server_versmax", PROTO_OPT_NFS_SERVER_VERSMAX, OPT_TYPE_NUMBER, 2369 (int)NFS_VERSMAX_DEFAULT, SVC_NFSD|SVC_MOUNTD, NFS_VERSMIN, 2370 NFS_VERSMAX, "server_versmin", OPT_CMP_GE}, 2371 #define PROTO_OPT_NFS_CLIENT_VERSMIN 7 2372 {"nfs_client_versmin", 2373 "client_versmin", PROTO_OPT_NFS_CLIENT_VERSMIN, OPT_TYPE_NUMBER, 2374 (int)NFS_VERSMIN_DEFAULT, SVC_CLIENT, NFS_VERSMIN, NFS_VERSMAX, 2375 "client_versmax", OPT_CMP_LE}, 2376 #define PROTO_OPT_NFS_CLIENT_VERSMAX 8 2377 {"nfs_client_versmax", 2378 "client_versmax", PROTO_OPT_NFS_CLIENT_VERSMAX, OPT_TYPE_NUMBER, 2379 (int)NFS_VERSMAX_DEFAULT, SVC_CLIENT, NFS_VERSMIN, NFS_VERSMAX, 2380 "client_versmin", OPT_CMP_GE}, 2381 #define PROTO_OPT_NFS_SERVER_DELEGATION 9 2382 {"nfs_server_delegation", 2383 "server_delegation", PROTO_OPT_NFS_SERVER_DELEGATION, 2384 OPT_TYPE_ONOFF, NFS_SERVER_DELEGATION_DEFAULT, SVC_NFSD, 0, 0}, 2385 #define PROTO_OPT_NFSMAPID_DOMAIN 10 2386 {"nfsmapid_domain", 2387 "nfsmapid_domain", PROTO_OPT_NFSMAPID_DOMAIN, OPT_TYPE_DOMAIN, 2388 NULL, SVC_NFSMAPID, 0, 0}, 2389 #define PROTO_OPT_NFSD_MAX_CONNECTIONS 11 2390 {"nfsd_max_connections", 2391 "max_connections", PROTO_OPT_NFSD_MAX_CONNECTIONS, 2392 OPT_TYPE_NUMBER, -1, SVC_NFSD, -1, INT32_MAX}, 2393 #define PROTO_OPT_NFSD_PROTOCOL 12 2394 {"nfsd_protocol", 2395 "protocol", PROTO_OPT_NFSD_PROTOCOL, OPT_TYPE_PROTOCOL, 0, 2396 SVC_NFSD, 0, 0}, 2397 #define PROTO_OPT_NFSD_LISTEN_BACKLOG 13 2398 {"nfsd_listen_backlog", 2399 "listen_backlog", PROTO_OPT_NFSD_LISTEN_BACKLOG, 2400 OPT_TYPE_NUMBER, 0, SVC_NFSD, 0, INT32_MAX}, 2401 #define PROTO_OPT_NFSD_DEVICE 14 2402 {"nfsd_device", 2403 "device", PROTO_OPT_NFSD_DEVICE, 2404 OPT_TYPE_STRING, NULL, SVC_NFSD, 0, 0}, 2405 #define PROTO_OPT_MOUNTD_LISTEN_BACKLOG 15 2406 {"mountd_listen_backlog", 2407 "mountd_listen_backlog", PROTO_OPT_MOUNTD_LISTEN_BACKLOG, 2408 OPT_TYPE_NUMBER, 64, SVC_NFSD|SVC_MOUNTD, 1, INT32_MAX}, 2409 #define PROTO_OPT_MOUNTD_MAX_THREADS 16 2410 {"mountd_max_threads", 2411 "mountd_max_threads", PROTO_OPT_MOUNTD_MAX_THREADS, 2412 OPT_TYPE_NUMBER, 16, SVC_NFSD|SVC_MOUNTD, 1, INT32_MAX}, 2413 {NULL} 2414 }; 2415 2416 /* 2417 * the protoset holds the defined options so we don't have to read 2418 * them multiple times 2419 */ 2420 static sa_protocol_properties_t protoset; 2421 2422 static int 2423 findprotoopt(char *name, int whichname) 2424 { 2425 int i; 2426 for (i = 0; proto_options[i].tag != NULL; i++) { 2427 if (whichname == 1) { 2428 if (strcasecmp(proto_options[i].name, name) == 0) 2429 return (i); 2430 } else { 2431 if (strcasecmp(proto_options[i].tag, name) == 0) 2432 return (i); 2433 } 2434 } 2435 return (-1); 2436 } 2437 2438 /* 2439 * fixcaselower(str) 2440 * 2441 * convert a string to lower case (inplace). 2442 */ 2443 2444 static void 2445 fixcaselower(char *str) 2446 { 2447 while (*str) { 2448 *str = tolower(*str); 2449 str++; 2450 } 2451 } 2452 2453 /* 2454 * skipwhitespace(str) 2455 * 2456 * Skip leading white space. It is assumed that it is called with a 2457 * valid pointer. 2458 */ 2459 2460 static char * 2461 skipwhitespace(char *str) 2462 { 2463 while (*str && isspace(*str)) 2464 str++; 2465 2466 return (str); 2467 } 2468 2469 /* 2470 * extractprop() 2471 * 2472 * Extract the property and value out of the line and create the 2473 * property in the optionset. 2474 */ 2475 static int 2476 extractprop(char *name, char *value) 2477 { 2478 sa_property_t prop; 2479 int index; 2480 int ret = SA_OK; 2481 /* 2482 * Remove any leading 2483 * white space. 2484 */ 2485 name = skipwhitespace(name); 2486 2487 index = findprotoopt(name, 1); 2488 if (index >= 0) { 2489 fixcaselower(name); 2490 prop = sa_create_property(proto_options[index].name, value); 2491 if (prop != NULL) 2492 ret = sa_add_protocol_property(protoset, prop); 2493 else 2494 ret = SA_NO_MEMORY; 2495 } 2496 return (ret); 2497 } 2498 2499 scf_type_t 2500 getscftype(int type) 2501 { 2502 scf_type_t ret; 2503 2504 switch (type) { 2505 case OPT_TYPE_NUMBER: 2506 ret = SCF_TYPE_INTEGER; 2507 break; 2508 case OPT_TYPE_BOOLEAN: 2509 ret = SCF_TYPE_BOOLEAN; 2510 break; 2511 default: 2512 ret = SCF_TYPE_ASTRING; 2513 } 2514 return (ret); 2515 } 2516 2517 char * 2518 getsvcname(uint32_t svcs) 2519 { 2520 char *service; 2521 switch (svcs) { 2522 case SVC_LOCKD: 2523 service = LOCKD; 2524 break; 2525 case SVC_STATD: 2526 service = STATD; 2527 break; 2528 case SVC_NFSD: 2529 service = NFSD; 2530 break; 2531 case SVC_CLIENT: 2532 service = NFS_CLIENT_SVC; 2533 break; 2534 case SVC_NFS4CBD: 2535 service = NFS4CBD; 2536 break; 2537 case SVC_NFSMAPID: 2538 service = NFSMAPID; 2539 break; 2540 case SVC_RQUOTAD: 2541 service = RQUOTAD; 2542 break; 2543 case SVC_NFSLOGD: 2544 service = NFSLOGD; 2545 break; 2546 case SVC_REPARSED: 2547 service = REPARSED; 2548 break; 2549 default: 2550 service = NFSD; 2551 } 2552 return (service); 2553 } 2554 2555 /* 2556 * initprotofromsmf() 2557 * 2558 * Read NFS SMF properties and add the defined values to the 2559 * protoset. Note that default values are known from the built in 2560 * table in case SMF doesn't have a definition. Not having 2561 * SMF properties is OK since we have builtin default 2562 * values. 2563 */ 2564 static int 2565 initprotofromsmf() 2566 { 2567 char name[PATH_MAX]; 2568 char value[PATH_MAX]; 2569 int ret = SA_OK, bufsz = 0, i; 2570 2571 protoset = sa_create_protocol_properties("nfs"); 2572 if (protoset != NULL) { 2573 for (i = 0; proto_options[i].tag != NULL; i++) { 2574 scf_type_t ptype; 2575 char *svc_name; 2576 2577 bzero(value, PATH_MAX); 2578 (void) strncpy(name, proto_options[i].name, PATH_MAX); 2579 /* Replace NULL with the correct instance */ 2580 ptype = getscftype(proto_options[i].type); 2581 svc_name = getsvcname(proto_options[i].svcs); 2582 bufsz = PATH_MAX; 2583 ret = nfs_smf_get_prop(name, value, 2584 (char *)DEFAULT_INSTANCE, ptype, 2585 svc_name, &bufsz); 2586 if (ret == SA_OK) { 2587 ret = extractprop(name, value); 2588 } 2589 } 2590 } else { 2591 ret = SA_NO_MEMORY; 2592 } 2593 2594 return (ret); 2595 } 2596 2597 /* 2598 * add_defaults() 2599 * 2600 * Add the default values for any property not defined 2601 * in NFS SMF repository. 2602 * Values are set according to their defined types. 2603 */ 2604 2605 static void 2606 add_defaults() 2607 { 2608 int i; 2609 char number[MAXDIGITS]; 2610 2611 for (i = 0; proto_options[i].tag != NULL; i++) { 2612 sa_property_t prop; 2613 prop = sa_get_protocol_property(protoset, 2614 proto_options[i].name); 2615 if (prop == NULL) { 2616 /* add the default value */ 2617 switch (proto_options[i].type) { 2618 case OPT_TYPE_NUMBER: 2619 (void) snprintf(number, sizeof (number), "%d", 2620 proto_options[i].defvalue.intval); 2621 prop = sa_create_property(proto_options[i].name, 2622 number); 2623 break; 2624 2625 case OPT_TYPE_BOOLEAN: 2626 prop = sa_create_property(proto_options[i].name, 2627 proto_options[i].defvalue.intval ? 2628 "true" : "false"); 2629 break; 2630 2631 case OPT_TYPE_ONOFF: 2632 prop = sa_create_property(proto_options[i].name, 2633 proto_options[i].defvalue.intval ? 2634 "on" : "off"); 2635 break; 2636 2637 default: 2638 /* treat as strings of zero length */ 2639 prop = sa_create_property(proto_options[i].name, 2640 ""); 2641 break; 2642 } 2643 if (prop != NULL) 2644 (void) sa_add_protocol_property(protoset, prop); 2645 } 2646 } 2647 } 2648 2649 static void 2650 free_protoprops() 2651 { 2652 if (protoset != NULL) { 2653 xmlFreeNode(protoset); 2654 protoset = NULL; 2655 } 2656 } 2657 2658 /* 2659 * nfs_init() 2660 * 2661 * Initialize the NFS plugin. 2662 */ 2663 2664 static int 2665 nfs_init() 2666 { 2667 int ret = SA_OK; 2668 2669 if (sa_plugin_ops.sa_init != nfs_init) { 2670 (void) printf(dgettext(TEXT_DOMAIN, 2671 "NFS plugin not properly initialized\n")); 2672 return (SA_CONFIG_ERR); 2673 } 2674 2675 ret = initprotofromsmf(); 2676 if (ret != SA_OK) { 2677 (void) printf(dgettext(TEXT_DOMAIN, 2678 "NFS plugin problem with SMF repository: %s\n"), 2679 sa_errorstr(ret)); 2680 ret = SA_OK; 2681 } 2682 add_defaults(); 2683 2684 return (ret); 2685 } 2686 2687 /* 2688 * nfs_fini() 2689 * 2690 * uninitialize the NFS plugin. Want to avoid memory leaks. 2691 */ 2692 2693 static void 2694 nfs_fini() 2695 { 2696 free_protoprops(); 2697 } 2698 2699 /* 2700 * nfs_get_proto_set() 2701 * 2702 * Return an optionset with all the protocol specific properties in 2703 * it. 2704 */ 2705 2706 static sa_protocol_properties_t 2707 nfs_get_proto_set() 2708 { 2709 return (protoset); 2710 } 2711 2712 /* 2713 * service_in_state(service, chkstate) 2714 * 2715 * Want to know if the specified service is in the desired state 2716 * (chkstate) or not. Return true (1) if it is and false (0) if it 2717 * isn't. 2718 */ 2719 static int 2720 service_in_state(char *service, const char *chkstate) 2721 { 2722 char *state; 2723 int ret = B_FALSE; 2724 2725 state = smf_get_state(service); 2726 if (state != NULL) { 2727 /* got the state so get the equality for the return value */ 2728 ret = strcmp(state, chkstate) == 0 ? B_TRUE : B_FALSE; 2729 free(state); 2730 } 2731 return (ret); 2732 } 2733 2734 /* 2735 * restart_service(svcs) 2736 * 2737 * Walk through the bit mask of services that need to be restarted in 2738 * order to use the new property values. Some properties affect 2739 * multiple daemons. Should only restart a service if it is currently 2740 * enabled (online). 2741 */ 2742 2743 static void 2744 restart_service(uint32_t svcs) 2745 { 2746 uint32_t mask; 2747 int ret; 2748 char *service; 2749 2750 for (mask = 1; svcs != 0; mask <<= 1) { 2751 switch (svcs & mask) { 2752 case SVC_LOCKD: 2753 service = LOCKD; 2754 break; 2755 case SVC_STATD: 2756 service = STATD; 2757 break; 2758 case SVC_NFSD: 2759 service = NFSD; 2760 break; 2761 case SVC_MOUNTD: 2762 service = MOUNTD; 2763 break; 2764 case SVC_NFS4CBD: 2765 service = NFS4CBD; 2766 break; 2767 case SVC_NFSMAPID: 2768 service = NFSMAPID; 2769 break; 2770 case SVC_RQUOTAD: 2771 service = RQUOTAD; 2772 break; 2773 case SVC_NFSLOGD: 2774 service = NFSLOGD; 2775 break; 2776 case SVC_REPARSED: 2777 service = REPARSED; 2778 break; 2779 case SVC_CLIENT: 2780 service = NFS_CLIENT_SVC; 2781 break; 2782 default: 2783 continue; 2784 } 2785 2786 /* 2787 * Only attempt to restart the service if it is 2788 * currently running. In the future, it may be 2789 * desirable to use smf_refresh_instance if the NFS 2790 * services ever implement the refresh method. 2791 */ 2792 if (service_in_state(service, SCF_STATE_STRING_ONLINE)) { 2793 ret = smf_restart_instance(service); 2794 /* 2795 * There are only a few SMF errors at this point, but 2796 * it is also possible that a bad value may have put 2797 * the service into maintenance if there wasn't an 2798 * SMF level error. 2799 */ 2800 if (ret != 0) { 2801 (void) fprintf(stderr, 2802 dgettext(TEXT_DOMAIN, 2803 "%s failed to restart: %s\n"), 2804 service, scf_strerror(scf_error())); 2805 } else { 2806 /* 2807 * Check whether it has gone to "maintenance" 2808 * mode or not. Maintenance implies something 2809 * went wrong. 2810 */ 2811 if (service_in_state(service, 2812 SCF_STATE_STRING_MAINT)) { 2813 (void) fprintf(stderr, 2814 dgettext(TEXT_DOMAIN, 2815 "%s failed to restart\n"), 2816 service); 2817 } 2818 } 2819 } 2820 svcs &= ~mask; 2821 } 2822 } 2823 2824 /* 2825 * nfs_minmax_check(name, value) 2826 * 2827 * Verify that the value for the property specified by index is valid 2828 * relative to the opposite value in the case of a min/max variable. 2829 * Currently, server_minvers/server_maxvers and 2830 * client_minvers/client_maxvers are the only ones to check. 2831 */ 2832 2833 static int 2834 nfs_minmax_check(int index, int value) 2835 { 2836 int val; 2837 char *pval; 2838 sa_property_t prop; 2839 sa_optionset_t opts; 2840 int ret = B_TRUE; 2841 2842 if (proto_options[index].other != NULL) { 2843 /* have a property to compare against */ 2844 opts = nfs_get_proto_set(); 2845 prop = sa_get_property(opts, proto_options[index].other); 2846 /* 2847 * If we don't find the property, assume default 2848 * values which will work since the max will be at the 2849 * max and the min at the min. 2850 */ 2851 if (prop != NULL) { 2852 pval = sa_get_property_attr(prop, "value"); 2853 if (pval != NULL) { 2854 val = strtoul(pval, NULL, 0); 2855 if (proto_options[index].compare == 2856 OPT_CMP_LE) { 2857 ret = value <= val ? B_TRUE : B_FALSE; 2858 } else if (proto_options[index].compare == 2859 OPT_CMP_GE) { 2860 ret = value >= val ? B_TRUE : B_FALSE; 2861 } 2862 sa_free_attr_string(pval); 2863 } 2864 } 2865 } 2866 return (ret); 2867 } 2868 2869 /* 2870 * nfs_validate_proto_prop(index, name, value) 2871 * 2872 * Verify that the property specified by name can take the new 2873 * value. This is a sanity check to prevent bad values getting into 2874 * the default files. All values need to be checked against what is 2875 * allowed by their defined type. If a type isn't explicitly defined 2876 * here, it is treated as a string. 2877 * 2878 * Note that OPT_TYPE_NUMBER will additionally check that the value is 2879 * within the range specified and potentially against another property 2880 * value as well as specified in the proto_options members other and 2881 * compare. 2882 */ 2883 2884 static int 2885 nfs_validate_proto_prop(int index, char *name, char *value) 2886 { 2887 int ret = SA_OK; 2888 char *cp; 2889 #ifdef lint 2890 name = name; 2891 #endif 2892 switch (proto_options[index].type) { 2893 case OPT_TYPE_NUMBER: 2894 if (!is_a_number(value)) 2895 ret = SA_BAD_VALUE; 2896 else { 2897 int val; 2898 val = strtoul(value, NULL, 0); 2899 if (val < proto_options[index].minval || 2900 val > proto_options[index].maxval) 2901 ret = SA_BAD_VALUE; 2902 /* 2903 * For server_versmin/server_versmax and 2904 * client_versmin/client_versmax, the value of the 2905 * min(max) should be checked to be correct relative 2906 * to the current max(min). 2907 */ 2908 if (!nfs_minmax_check(index, val)) { 2909 ret = SA_BAD_VALUE; 2910 } 2911 } 2912 break; 2913 2914 case OPT_TYPE_DOMAIN: 2915 /* 2916 * needs to be a qualified domain so will have at 2917 * least one period and other characters on either 2918 * side of it. A zero length string is also allowed 2919 * and is the way to turn off the override. 2920 */ 2921 if (strlen(value) == 0) 2922 break; 2923 cp = strchr(value, '.'); 2924 if (cp == NULL || cp == value || strchr(value, '@') != NULL) 2925 ret = SA_BAD_VALUE; 2926 break; 2927 2928 case OPT_TYPE_BOOLEAN: 2929 if (strlen(value) == 0 || 2930 strcasecmp(value, "true") == 0 || 2931 strcmp(value, "1") == 0 || 2932 strcasecmp(value, "false") == 0 || 2933 strcmp(value, "0") == 0) { 2934 ret = SA_OK; 2935 } else { 2936 ret = SA_BAD_VALUE; 2937 } 2938 break; 2939 2940 case OPT_TYPE_ONOFF: 2941 if (strcasecmp(value, "on") != 0 && 2942 strcasecmp(value, "off") != 0) { 2943 ret = SA_BAD_VALUE; 2944 } 2945 break; 2946 2947 case OPT_TYPE_PROTOCOL: 2948 if (strlen(value) != 0 && 2949 strcasecmp(value, "all") != 0 && 2950 strcasecmp(value, "tcp") != 0 && 2951 strcasecmp(value, "udp") != 0) 2952 ret = SA_BAD_VALUE; 2953 break; 2954 2955 default: 2956 /* treat as a string */ 2957 break; 2958 } 2959 return (ret); 2960 } 2961 2962 /* 2963 * nfs_set_proto_prop(prop) 2964 * 2965 * check that prop is valid. 2966 */ 2967 2968 static int 2969 nfs_set_proto_prop(sa_property_t prop) 2970 { 2971 int ret = SA_OK; 2972 char *name; 2973 char *value; 2974 2975 name = sa_get_property_attr(prop, "type"); 2976 value = sa_get_property_attr(prop, "value"); 2977 if (name != NULL && value != NULL) { 2978 scf_type_t sctype; 2979 char *svc_name; 2980 char *instance = NULL; 2981 int index = findprotoopt(name, 1); 2982 2983 ret = nfs_validate_proto_prop(index, name, value); 2984 if (ret == SA_OK) { 2985 sctype = getscftype(proto_options[index].type); 2986 svc_name = getsvcname(proto_options[index].svcs); 2987 if (sctype == SCF_TYPE_BOOLEAN) { 2988 if (value != NULL) 2989 sa_free_attr_string(value); 2990 if (string_to_boolean(value) == 0) 2991 value = strdup("0"); 2992 else 2993 value = strdup("1"); 2994 } 2995 ret = nfs_smf_set_prop(name, value, instance, sctype, 2996 svc_name); 2997 if (ret == SA_OK) { 2998 restart_service(proto_options[index].svcs); 2999 } else { 3000 (void) printf(dgettext(TEXT_DOMAIN, 3001 "Cannot restart NFS services : %s\n"), 3002 sa_errorstr(ret)); 3003 } 3004 } 3005 } 3006 if (name != NULL) 3007 sa_free_attr_string(name); 3008 if (value != NULL) 3009 sa_free_attr_string(value); 3010 return (ret); 3011 } 3012 3013 /* 3014 * nfs_get_status() 3015 * 3016 * What is the current status of the nfsd? We use the SMF state here. 3017 * Caller must free the returned value. 3018 */ 3019 3020 static char * 3021 nfs_get_status() 3022 { 3023 return (smf_get_state(NFSD)); 3024 } 3025 3026 /* 3027 * nfs_space_alias(alias) 3028 * 3029 * Lookup the space (security) name. If it is default, convert to the 3030 * real name. 3031 */ 3032 3033 static char * 3034 nfs_space_alias(char *space) 3035 { 3036 char *name = space; 3037 seconfig_t secconf; 3038 3039 /* 3040 * Only the space named "default" is special. If it is used, 3041 * the default needs to be looked up and the real name used. 3042 * This is normally "sys" but could be changed. We always 3043 * change defautl to the real name. 3044 */ 3045 if (strcmp(space, "default") == 0 && 3046 nfs_getseconfig_default(&secconf) == 0) { 3047 if (nfs_getseconfig_bynumber(secconf.sc_nfsnum, &secconf) == 0) 3048 name = secconf.sc_name; 3049 } 3050 return (strdup(name)); 3051 } 3052 3053 /* 3054 * nfs_features() 3055 * 3056 * Return a mask of the features required. 3057 */ 3058 3059 static uint64_t 3060 nfs_features() 3061 { 3062 return ((uint64_t)SA_FEATURE_DFSTAB | SA_FEATURE_SERVER); 3063 } 3064