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 if (export->ex_index != NULL) 1045 free(export->ex_index); 1046 if (export->ex_secinfo != NULL) { 1047 for (i = 0; i < export->ex_seccnt; i++) 1048 if (export->ex_secinfo[i].s_rootnames != NULL) 1049 free(export->ex_secinfo[i].s_rootnames); 1050 free(export->ex_secinfo); 1051 } 1052 } 1053 1054 /* 1055 * Given a seconfig entry and a colon-separated 1056 * list of names, allocate an array big enough 1057 * to hold the root list, then convert each name to 1058 * a principal name according to the security 1059 * info and assign it to an array element. 1060 * Return the array and its size. 1061 */ 1062 static caddr_t * 1063 get_rootnames(seconfig_t *sec, char *list, int *count) 1064 { 1065 caddr_t *a; 1066 int c, i; 1067 char *host, *p; 1068 1069 /* 1070 * Count the number of strings in the list. 1071 * This is the number of colon separators + 1. 1072 */ 1073 c = 1; 1074 for (p = list; *p; p++) 1075 if (*p == ':') 1076 c++; 1077 *count = c; 1078 1079 a = (caddr_t *)malloc(c * sizeof (char *)); 1080 if (a == NULL) { 1081 (void) printf(dgettext(TEXT_DOMAIN, 1082 "get_rootnames: no memory\n")); 1083 } else { 1084 for (i = 0; i < c; i++) { 1085 host = strtok(list, ":"); 1086 if (!nfs_get_root_principal(sec, host, &a[i])) { 1087 free(a); 1088 a = NULL; 1089 break; 1090 } 1091 list = NULL; 1092 } 1093 } 1094 1095 return (a); 1096 } 1097 1098 /* 1099 * fill_security_from_secopts(sp, secopts) 1100 * 1101 * Fill the secinfo structure from the secopts optionset. 1102 */ 1103 1104 static int 1105 fill_security_from_secopts(struct secinfo *sp, sa_security_t secopts) 1106 { 1107 sa_property_t prop; 1108 char *type; 1109 int longform; 1110 int err = SC_NOERROR; 1111 uint32_t val; 1112 1113 type = sa_get_security_attr(secopts, "sectype"); 1114 if (type != NULL) { 1115 /* named security type needs secinfo to be filled in */ 1116 err = nfs_getseconfig_byname(type, &sp->s_secinfo); 1117 sa_free_attr_string(type); 1118 if (err != SC_NOERROR) 1119 return (err); 1120 } else { 1121 /* default case */ 1122 err = nfs_getseconfig_default(&sp->s_secinfo); 1123 if (err != SC_NOERROR) 1124 return (err); 1125 } 1126 1127 err = SA_OK; 1128 for (prop = sa_get_property(secopts, NULL); 1129 prop != NULL && err == SA_OK; 1130 prop = sa_get_next_property(prop)) { 1131 char *name; 1132 char *value; 1133 1134 name = sa_get_property_attr(prop, "type"); 1135 value = sa_get_property_attr(prop, "value"); 1136 1137 longform = value != NULL && strcmp(value, "*") != 0; 1138 1139 switch (findopt(name)) { 1140 case OPT_RO: 1141 sp->s_flags |= longform ? M_ROL : M_RO; 1142 break; 1143 case OPT_RW: 1144 sp->s_flags |= longform ? M_RWL : M_RW; 1145 break; 1146 case OPT_ROOT: 1147 sp->s_flags |= M_ROOT; 1148 /* 1149 * if we are using AUTH_UNIX, handle like other things 1150 * such as RO/RW 1151 */ 1152 if (sp->s_secinfo.sc_rpcnum == AUTH_UNIX) 1153 break; 1154 /* not AUTH_UNIX */ 1155 if (value != NULL) { 1156 sp->s_rootnames = get_rootnames(&sp->s_secinfo, 1157 value, &sp->s_rootcnt); 1158 if (sp->s_rootnames == NULL) { 1159 err = SA_BAD_VALUE; 1160 (void) fprintf(stderr, 1161 dgettext(TEXT_DOMAIN, 1162 "Bad root list\n")); 1163 } 1164 } 1165 break; 1166 case OPT_NONE: 1167 sp->s_flags |= M_NONE; 1168 break; 1169 case OPT_WINDOW: 1170 if (value != NULL) { 1171 sp->s_window = atoi(value); 1172 /* just in case */ 1173 if (sp->s_window < 0) 1174 sp->s_window = DEF_WIN; 1175 } 1176 break; 1177 case OPT_ROOT_MAPPING: 1178 if (value != NULL && is_a_number(value)) { 1179 val = strtoul(value, NULL, 0); 1180 } else { 1181 struct passwd *pw; 1182 pw = getpwnam(value != NULL ? value : "nobody"); 1183 if (pw != NULL) { 1184 val = pw->pw_uid; 1185 } else { 1186 val = UID_NOBODY; 1187 } 1188 endpwent(); 1189 } 1190 sp->s_rootid = val; 1191 break; 1192 default: 1193 break; 1194 } 1195 if (name != NULL) 1196 sa_free_attr_string(name); 1197 if (value != NULL) 1198 sa_free_attr_string(value); 1199 } 1200 /* if rw/ro options not set, use default of RW */ 1201 if ((sp->s_flags & NFS_RWMODES) == 0) 1202 sp->s_flags |= M_RW; 1203 return (err); 1204 } 1205 1206 /* 1207 * This is for testing only 1208 * It displays the export structure that 1209 * goes into the kernel. 1210 */ 1211 static void 1212 printarg(char *path, struct exportdata *ep) 1213 { 1214 int i, j; 1215 struct secinfo *sp; 1216 1217 if (debug == 0) 1218 return; 1219 1220 (void) printf("%s:\n", path); 1221 (void) printf("\tex_version = %d\n", ep->ex_version); 1222 (void) printf("\tex_path = %s\n", ep->ex_path); 1223 (void) printf("\tex_pathlen = %ld\n", (ulong_t)ep->ex_pathlen); 1224 (void) printf("\tex_flags: (0x%02x) ", ep->ex_flags); 1225 if (ep->ex_flags & EX_NOSUID) 1226 (void) printf("NOSUID "); 1227 if (ep->ex_flags & EX_ACLOK) 1228 (void) printf("ACLOK "); 1229 if (ep->ex_flags & EX_PUBLIC) 1230 (void) printf("PUBLIC "); 1231 if (ep->ex_flags & EX_NOSUB) 1232 (void) printf("NOSUB "); 1233 if (ep->ex_flags & EX_LOG) 1234 (void) printf("LOG "); 1235 if (ep->ex_flags & EX_CHARMAP) 1236 (void) printf("CHARMAP "); 1237 if (ep->ex_flags & EX_LOG_ALLOPS) 1238 (void) printf("LOG_ALLOPS "); 1239 if (ep->ex_flags == 0) 1240 (void) printf("(none)"); 1241 (void) printf("\n"); 1242 if (ep->ex_flags & EX_LOG) { 1243 (void) printf("\tex_log_buffer = %s\n", 1244 (ep->ex_log_buffer ? ep->ex_log_buffer : "(NULL)")); 1245 (void) printf("\tex_tag = %s\n", 1246 (ep->ex_tag ? ep->ex_tag : "(NULL)")); 1247 } 1248 (void) printf("\tex_anon = %d\n", ep->ex_anon); 1249 (void) printf("\tex_seccnt = %d\n", ep->ex_seccnt); 1250 (void) printf("\n"); 1251 for (i = 0; i < ep->ex_seccnt; i++) { 1252 sp = &ep->ex_secinfo[i]; 1253 (void) printf("\t\ts_secinfo = %s\n", sp->s_secinfo.sc_name); 1254 (void) printf("\t\ts_flags: (0x%02x) ", sp->s_flags); 1255 if (sp->s_flags & M_ROOT) (void) printf("M_ROOT "); 1256 if (sp->s_flags & M_RO) (void) printf("M_RO "); 1257 if (sp->s_flags & M_ROL) (void) printf("M_ROL "); 1258 if (sp->s_flags & M_RW) (void) printf("M_RW "); 1259 if (sp->s_flags & M_RWL) (void) printf("M_RWL "); 1260 if (sp->s_flags & M_NONE) (void) printf("M_NONE "); 1261 if (sp->s_flags == 0) (void) printf("(none)"); 1262 (void) printf("\n"); 1263 (void) printf("\t\ts_window = %d\n", sp->s_window); 1264 (void) printf("\t\ts_rootid = %d\n", sp->s_rootid); 1265 (void) printf("\t\ts_rootcnt = %d ", sp->s_rootcnt); 1266 (void) fflush(stdout); 1267 for (j = 0; j < sp->s_rootcnt; j++) 1268 (void) printf("%s ", sp->s_rootnames[j] ? 1269 sp->s_rootnames[j] : "<null>"); 1270 (void) printf("\n\n"); 1271 } 1272 } 1273 1274 /* 1275 * count_security(opts) 1276 * 1277 * Count the number of security types (flavors). The optionset has 1278 * been populated with the security flavors as a holding mechanism. 1279 * We later use this number to allocate data structures. 1280 */ 1281 1282 static int 1283 count_security(sa_optionset_t opts) 1284 { 1285 int count = 0; 1286 sa_property_t prop; 1287 if (opts != NULL) { 1288 for (prop = sa_get_property(opts, NULL); prop != NULL; 1289 prop = sa_get_next_property(prop)) { 1290 count++; 1291 } 1292 } 1293 return (count); 1294 } 1295 1296 /* 1297 * nfs_sprint_option(rbuff, rbuffsize, incr, prop, sep) 1298 * 1299 * provides a mechanism to format NFS properties into legacy output 1300 * format. If the buffer would overflow, it is reallocated and grown 1301 * as appropriate. Special cases of converting internal form of values 1302 * to those used by "share" are done. this function does one property 1303 * at a time. 1304 */ 1305 1306 static int 1307 nfs_sprint_option(char **rbuff, size_t *rbuffsize, size_t incr, 1308 sa_property_t prop, int sep) 1309 { 1310 char *name; 1311 char *value; 1312 int curlen; 1313 char *buff = *rbuff; 1314 size_t buffsize = *rbuffsize; 1315 int printed = B_FALSE; 1316 1317 name = sa_get_property_attr(prop, "type"); 1318 value = sa_get_property_attr(prop, "value"); 1319 if (buff != NULL) 1320 curlen = strlen(buff); 1321 else 1322 curlen = 0; 1323 if (name != NULL) { 1324 int len; 1325 len = strlen(name) + sep; 1326 1327 /* 1328 * A future RFE would be to replace this with more 1329 * generic code and to possibly handle more types. 1330 */ 1331 switch (gettype(name)) { 1332 case OPT_TYPE_BOOLEAN: 1333 /* 1334 * For NFS, boolean value of FALSE means it 1335 * doesn't show up in the option list at all. 1336 */ 1337 if (value != NULL && strcasecmp(value, "false") == 0) 1338 goto skip; 1339 if (value != NULL) { 1340 sa_free_attr_string(value); 1341 value = NULL; 1342 } 1343 break; 1344 case OPT_TYPE_ACCLIST: 1345 if (value != NULL && strcmp(value, "*") == 0) { 1346 sa_free_attr_string(value); 1347 value = NULL; 1348 } else { 1349 if (value != NULL) 1350 len += 1 + strlen(value); 1351 } 1352 break; 1353 case OPT_TYPE_LOGTAG: 1354 if (value != NULL && strlen(value) == 0) { 1355 sa_free_attr_string(value); 1356 value = NULL; 1357 } else { 1358 if (value != NULL) 1359 len += 1 + strlen(value); 1360 } 1361 break; 1362 default: 1363 if (value != NULL) 1364 len += 1 + strlen(value); 1365 break; 1366 } 1367 while (buffsize <= (curlen + len)) { 1368 /* need more room */ 1369 buffsize += incr; 1370 buff = realloc(buff, buffsize); 1371 if (buff == NULL) { 1372 /* realloc failed so free everything */ 1373 if (*rbuff != NULL) 1374 free(*rbuff); 1375 } 1376 *rbuff = buff; 1377 *rbuffsize = buffsize; 1378 if (buff == NULL) 1379 goto skip; 1380 1381 } 1382 1383 if (buff == NULL) 1384 goto skip; 1385 1386 if (value == NULL) { 1387 (void) snprintf(buff + curlen, buffsize - curlen, 1388 "%s%s", sep ? "," : "", 1389 name, value != NULL ? value : ""); 1390 } else { 1391 (void) snprintf(buff + curlen, buffsize - curlen, 1392 "%s%s=%s", sep ? "," : "", 1393 name, value != NULL ? value : ""); 1394 } 1395 printed = B_TRUE; 1396 } 1397 skip: 1398 if (name != NULL) 1399 sa_free_attr_string(name); 1400 if (value != NULL) 1401 sa_free_attr_string(value); 1402 return (printed); 1403 } 1404 1405 /* 1406 * nfs_format_options(group, hier) 1407 * 1408 * format all the options on the group into an old-style option 1409 * string. If hier is non-zero, walk up the tree to get inherited 1410 * options. 1411 */ 1412 1413 static char * 1414 nfs_format_options(sa_group_t group, int hier) 1415 { 1416 sa_optionset_t options = NULL; 1417 sa_optionset_t secoptions = NULL; 1418 sa_property_t prop, secprop; 1419 sa_security_t security = NULL; 1420 char *buff; 1421 size_t buffsize; 1422 char *sectype = NULL; 1423 int sep = 0; 1424 1425 1426 buff = malloc(OPT_CHUNK); 1427 if (buff == NULL) { 1428 return (NULL); 1429 } 1430 1431 buff[0] = '\0'; 1432 buffsize = OPT_CHUNK; 1433 1434 /* 1435 * We may have a an optionset relative to this item. format 1436 * these if we find them and then add any security definitions. 1437 */ 1438 1439 options = sa_get_derived_optionset(group, "nfs", hier); 1440 1441 /* 1442 * do the default set first but skip any option that is also 1443 * in the protocol specific optionset. 1444 */ 1445 if (options != NULL) { 1446 for (prop = sa_get_property(options, NULL); 1447 prop != NULL; prop = sa_get_next_property(prop)) { 1448 /* 1449 * use this one since we skipped any 1450 * of these that were also in 1451 * optdefault 1452 */ 1453 if (nfs_sprint_option(&buff, &buffsize, OPT_CHUNK, 1454 prop, sep)) 1455 sep = 1; 1456 if (buff == NULL) { 1457 /* 1458 * buff could become NULL if there 1459 * isn't enough memory for 1460 * nfs_sprint_option to realloc() 1461 * as necessary. We can't really 1462 * do anything about it at this 1463 * point so we return NULL. The 1464 * caller should handle the 1465 * failure. 1466 */ 1467 if (options != NULL) 1468 sa_free_derived_optionset( 1469 options); 1470 return (buff); 1471 } 1472 } 1473 } 1474 secoptions = (sa_optionset_t)sa_get_all_security_types(group, 1475 "nfs", hier); 1476 if (secoptions != NULL) { 1477 for (secprop = sa_get_property(secoptions, NULL); 1478 secprop != NULL; 1479 secprop = sa_get_next_property(secprop)) { 1480 sectype = sa_get_property_attr(secprop, "type"); 1481 security = 1482 (sa_security_t)sa_get_derived_security( 1483 group, sectype, "nfs", hier); 1484 if (security != NULL) { 1485 if (sectype != NULL) { 1486 prop = sa_create_property( 1487 "sec", sectype); 1488 if (prop == NULL) 1489 goto err; 1490 if (nfs_sprint_option(&buff, 1491 &buffsize, OPT_CHUNK, prop, sep)) 1492 sep = 1; 1493 (void) sa_remove_property(prop); 1494 if (buff == NULL) 1495 goto err; 1496 } 1497 for (prop = sa_get_property(security, 1498 NULL); prop != NULL; 1499 prop = sa_get_next_property(prop)) { 1500 if (nfs_sprint_option(&buff, 1501 &buffsize, OPT_CHUNK, prop, sep)) 1502 sep = 1; 1503 if (buff == NULL) 1504 goto err; 1505 } 1506 sa_free_derived_optionset(security); 1507 } 1508 if (sectype != NULL) 1509 sa_free_attr_string(sectype); 1510 } 1511 sa_free_derived_optionset(secoptions); 1512 } 1513 1514 if (options != NULL) 1515 sa_free_derived_optionset(options); 1516 return (buff); 1517 1518 err: 1519 /* 1520 * If we couldn't allocate memory for option printing, we need 1521 * to break out of the nested loops, cleanup and return NULL. 1522 */ 1523 if (secoptions != NULL) 1524 sa_free_derived_optionset(secoptions); 1525 if (security != NULL) 1526 sa_free_derived_optionset(security); 1527 if (sectype != NULL) 1528 sa_free_attr_string(sectype); 1529 if (options != NULL) 1530 sa_free_derived_optionset(options); 1531 return (buff); 1532 } 1533 1534 /* 1535 * Append an entry to the nfslogtab file 1536 */ 1537 static int 1538 nfslogtab_add(dir, buffer, tag) 1539 char *dir, *buffer, *tag; 1540 { 1541 FILE *f; 1542 struct logtab_ent lep; 1543 int error = 0; 1544 1545 /* 1546 * Open the file for update and create it if necessary. 1547 * This may leave the I/O offset at the end of the file, 1548 * so rewind back to the beginning of the file. 1549 */ 1550 f = fopen(NFSLOGTAB, "a+"); 1551 if (f == NULL) { 1552 error = errno; 1553 goto out; 1554 } 1555 rewind(f); 1556 1557 if (lockf(fileno(f), F_LOCK, 0L) < 0) { 1558 (void) fprintf(stderr, dgettext(TEXT_DOMAIN, 1559 "share complete, however failed to lock %s " 1560 "for update: %s\n"), NFSLOGTAB, strerror(errno)); 1561 error = -1; 1562 goto out; 1563 } 1564 1565 if (logtab_deactivate_after_boot(f) == -1) { 1566 (void) fprintf(stderr, dgettext(TEXT_DOMAIN, 1567 "share complete, however could not deactivate " 1568 "entries in %s\n"), NFSLOGTAB); 1569 error = -1; 1570 goto out; 1571 } 1572 1573 /* 1574 * Remove entries matching buffer and sharepoint since we're 1575 * going to replace it with perhaps an entry with a new tag. 1576 */ 1577 if (logtab_rement(f, buffer, dir, NULL, -1)) { 1578 (void) fprintf(stderr, dgettext(TEXT_DOMAIN, 1579 "share complete, however could not remove matching " 1580 "entries in %s\n"), NFSLOGTAB); 1581 error = -1; 1582 goto out; 1583 } 1584 1585 /* 1586 * Deactivate all active entries matching this sharepoint 1587 */ 1588 if (logtab_deactivate(f, NULL, dir, NULL)) { 1589 (void) fprintf(stderr, dgettext(TEXT_DOMAIN, 1590 "share complete, however could not deactivate matching " 1591 "entries in %s\n"), NFSLOGTAB); 1592 error = -1; 1593 goto out; 1594 } 1595 1596 lep.le_buffer = buffer; 1597 lep.le_path = dir; 1598 lep.le_tag = tag; 1599 lep.le_state = LES_ACTIVE; 1600 1601 /* 1602 * Add new sharepoint / buffer location to nfslogtab 1603 */ 1604 if (logtab_putent(f, &lep) < 0) { 1605 (void) fprintf(stderr, dgettext(TEXT_DOMAIN, 1606 "share complete, however could not add %s to %s\n"), 1607 dir, NFSLOGTAB); 1608 error = -1; 1609 } 1610 1611 out: 1612 if (f != NULL) 1613 (void) fclose(f); 1614 return (error); 1615 } 1616 1617 /* 1618 * Deactivate an entry from the nfslogtab file 1619 */ 1620 static int 1621 nfslogtab_deactivate(path) 1622 char *path; 1623 { 1624 FILE *f; 1625 int error = 0; 1626 1627 f = fopen(NFSLOGTAB, "r+"); 1628 if (f == NULL) { 1629 error = errno; 1630 goto out; 1631 } 1632 if (lockf(fileno(f), F_LOCK, 0L) < 0) { 1633 error = errno; 1634 (void) fprintf(stderr, dgettext(TEXT_DOMAIN, 1635 "share complete, however could not lock %s for " 1636 "update: %s\n"), NFSLOGTAB, strerror(error)); 1637 goto out; 1638 } 1639 if (logtab_deactivate(f, NULL, path, NULL) == -1) { 1640 error = -1; 1641 (void) fprintf(stderr, 1642 dgettext(TEXT_DOMAIN, 1643 "share complete, however could not " 1644 "deactivate %s in %s\n"), path, NFSLOGTAB); 1645 goto out; 1646 } 1647 1648 out: if (f != NULL) 1649 (void) fclose(f); 1650 1651 return (error); 1652 } 1653 1654 /* 1655 * check_public(group, skipshare) 1656 * 1657 * Check the group for any shares that have the public property 1658 * enabled. We skip "skipshare" since that is the one we are 1659 * working with. This is a separate function to make handling 1660 * subgroups simpler. Returns true if there is a share with public. 1661 */ 1662 static int 1663 check_public(sa_group_t group, sa_share_t skipshare) 1664 { 1665 int exists = B_FALSE; 1666 sa_share_t share; 1667 sa_optionset_t opt; 1668 sa_property_t prop; 1669 char *shared; 1670 1671 for (share = sa_get_share(group, NULL); share != NULL; 1672 share = sa_get_next_share(share)) { 1673 if (share == skipshare) 1674 continue; 1675 1676 opt = sa_get_optionset(share, "nfs"); 1677 if (opt == NULL) 1678 continue; 1679 prop = sa_get_property(opt, "public"); 1680 if (prop == NULL) 1681 continue; 1682 shared = sa_get_share_attr(share, "shared"); 1683 if (shared != NULL) { 1684 exists = strcmp(shared, "true") == 0; 1685 sa_free_attr_string(shared); 1686 if (exists == B_TRUE) 1687 break; 1688 } 1689 } 1690 1691 return (exists); 1692 } 1693 1694 /* 1695 * public_exists(handle, share) 1696 * 1697 * check to see if public option is set on any other share than the 1698 * one specified. Need to check zfs sub-groups as well as the top 1699 * level groups. 1700 */ 1701 static int 1702 public_exists(sa_handle_t handle, sa_share_t skipshare) 1703 { 1704 sa_group_t group = NULL; 1705 1706 /* 1707 * If we don't have a handle, we can only do syntax check. We 1708 * can't check against other shares so we assume OK and will 1709 * catch the problem only when we actually try to apply it. 1710 */ 1711 if (handle == NULL) 1712 return (SA_OK); 1713 1714 if (skipshare != NULL) { 1715 group = sa_get_parent_group(skipshare); 1716 if (group == NULL) 1717 return (SA_NO_SUCH_GROUP); 1718 } 1719 1720 for (group = sa_get_group(handle, NULL); group != NULL; 1721 group = sa_get_next_group(group)) { 1722 /* Walk any ZFS subgroups as well as all standard groups */ 1723 if (sa_group_is_zfs(group)) { 1724 sa_group_t subgroup; 1725 for (subgroup = sa_get_sub_group(group); 1726 subgroup != NULL; 1727 subgroup = sa_get_next_group(subgroup)) { 1728 if (check_public(subgroup, skipshare)) 1729 return (B_TRUE); 1730 } 1731 } else { 1732 if (check_public(group, skipshare)) 1733 return (B_TRUE); 1734 } 1735 } 1736 return (B_FALSE); 1737 } 1738 1739 /* 1740 * sa_enable_share at the protocol level, enable_share must tell the 1741 * implementation that it is to enable the share. This entails 1742 * converting the path and options into the appropriate ioctl 1743 * calls. It is assumed that all error checking of paths, etc. were 1744 * done earlier. 1745 */ 1746 static int 1747 nfs_enable_share(sa_share_t share) 1748 { 1749 struct exportdata export; 1750 sa_optionset_t secoptlist; 1751 struct secinfo *sp; 1752 int num_secinfo; 1753 sa_optionset_t opt; 1754 sa_security_t sec; 1755 sa_property_t prop; 1756 char *path; 1757 int err = SA_OK; 1758 int i; 1759 int iszfs; 1760 sa_handle_t handle; 1761 1762 /* Don't drop core if the NFS module isn't loaded. */ 1763 (void) signal(SIGSYS, SIG_IGN); 1764 1765 /* get the path since it is important in several places */ 1766 path = sa_get_share_attr(share, "path"); 1767 if (path == NULL) 1768 return (SA_NO_SUCH_PATH); 1769 1770 iszfs = sa_path_is_zfs(path); 1771 /* 1772 * find the optionsets and security sets. There may not be 1773 * any or there could be one or two for each of optionset and 1774 * security may have multiple, one per security type per 1775 * protocol type. 1776 */ 1777 opt = sa_get_derived_optionset(share, "nfs", 1); 1778 secoptlist = (sa_optionset_t)sa_get_all_security_types(share, "nfs", 1); 1779 if (secoptlist != NULL) 1780 num_secinfo = MAX(1, count_security(secoptlist)); 1781 else 1782 num_secinfo = 1; 1783 1784 /* 1785 * walk through the options and fill in the structure 1786 * appropriately. 1787 */ 1788 1789 (void) memset(&export, '\0', sizeof (export)); 1790 1791 /* 1792 * do non-security options first since there is only one after 1793 * the derived group is constructed. 1794 */ 1795 export.ex_version = EX_CURRENT_VERSION; 1796 export.ex_anon = UID_NOBODY; /* this is our default value */ 1797 export.ex_index = NULL; 1798 export.ex_path = path; 1799 export.ex_pathlen = strlen(path) + 1; 1800 1801 if (opt != NULL) 1802 err = fill_export_from_optionset(&export, opt); 1803 1804 /* 1805 * check to see if "public" is set. If it is, then make sure 1806 * no other share has it set. If it is already used, fail. 1807 */ 1808 1809 handle = sa_find_group_handle((sa_group_t)share); 1810 if (export.ex_flags & EX_PUBLIC && public_exists(handle, share)) { 1811 (void) printf(dgettext(TEXT_DOMAIN, 1812 "NFS: Cannot share more than one file " 1813 "system with 'public' property\n")); 1814 err = SA_NOT_ALLOWED; 1815 goto out; 1816 } 1817 1818 sp = calloc(num_secinfo, sizeof (struct secinfo)); 1819 if (sp == NULL) { 1820 err = SA_NO_MEMORY; 1821 (void) printf(dgettext(TEXT_DOMAIN, 1822 "NFS: NFS: no memory for security\n")); 1823 goto out; 1824 } 1825 export.ex_secinfo = sp; 1826 /* get default secinfo */ 1827 export.ex_seccnt = num_secinfo; 1828 /* 1829 * since we must have one security option defined, we 1830 * init to the default and then override as we find 1831 * defined security options. This handles the case 1832 * where we have no defined options but we need to set 1833 * up one. 1834 */ 1835 sp[0].s_window = DEF_WIN; 1836 sp[0].s_rootnames = NULL; 1837 /* setup a default in case no properties defined */ 1838 if (nfs_getseconfig_default(&sp[0].s_secinfo)) { 1839 (void) printf(dgettext(TEXT_DOMAIN, 1840 "NFS: nfs_getseconfig_default: failed to " 1841 "get default security mode\n")); 1842 err = SA_CONFIG_ERR; 1843 } 1844 if (secoptlist != NULL) { 1845 for (i = 0, prop = sa_get_property(secoptlist, NULL); 1846 prop != NULL && i < num_secinfo; 1847 prop = sa_get_next_property(prop), i++) { 1848 char *sectype; 1849 sectype = sa_get_property_attr(prop, "type"); 1850 /* 1851 * if sectype is NULL, we probably 1852 * have a memory problem and can't get 1853 * the correct values. Rather than 1854 * exporting with incorrect security, 1855 * don't share it. 1856 */ 1857 if (sectype == NULL) { 1858 err = SA_NO_MEMORY; 1859 (void) printf(dgettext(TEXT_DOMAIN, 1860 "NFS: Cannot share %s: " 1861 "no memory\n"), path); 1862 goto out; 1863 } 1864 sec = (sa_security_t)sa_get_derived_security( 1865 share, sectype, "nfs", 1); 1866 sp[i].s_window = DEF_WIN; 1867 sp[i].s_rootcnt = 0; 1868 sp[i].s_rootnames = NULL; 1869 (void) fill_security_from_secopts(&sp[i], sec); 1870 if (sec != NULL) 1871 sa_free_derived_security(sec); 1872 if (sectype != NULL) 1873 sa_free_attr_string(sectype); 1874 } 1875 } 1876 /* 1877 * when we get here, we can do the exportfs system call and 1878 * initiate thinsg. We probably want to enable the nfs.server 1879 * service first if it isn't running within SMF. 1880 */ 1881 /* check nfs.server status and start if needed */ 1882 /* now add the share to the internal tables */ 1883 printarg(path, &export); 1884 /* 1885 * call the exportfs system call which is implemented 1886 * via the nfssys() call as the EXPORTFS subfunction. 1887 */ 1888 if (iszfs) { 1889 struct exportfs_args ea; 1890 share_t sh; 1891 char *str; 1892 priv_set_t *priv_effective; 1893 int privileged; 1894 1895 /* 1896 * If we aren't a privileged user 1897 * and NFS server service isn't running 1898 * then print out an error message 1899 * and return EPERM 1900 */ 1901 1902 priv_effective = priv_allocset(); 1903 (void) getppriv(PRIV_EFFECTIVE, priv_effective); 1904 1905 privileged = (priv_isfullset(priv_effective) == B_TRUE); 1906 priv_freeset(priv_effective); 1907 1908 if (!privileged && 1909 (str = smf_get_state(NFS_SERVER_SVC)) != NULL) { 1910 err = 0; 1911 if (strcmp(str, SCF_STATE_STRING_ONLINE) != 0) { 1912 (void) printf(dgettext(TEXT_DOMAIN, 1913 "NFS: Cannot share remote " 1914 "filesystem: %s\n"), path); 1915 (void) printf(dgettext(TEXT_DOMAIN, 1916 "NFS: Service needs to be enabled " 1917 "by a privileged user\n")); 1918 err = SA_SYSTEM_ERR; 1919 errno = EPERM; 1920 } 1921 free(str); 1922 } 1923 1924 if (err == 0) { 1925 ea.dname = path; 1926 ea.uex = &export; 1927 1928 (void) sa_sharetab_fill_zfs(share, &sh, "nfs"); 1929 err = sa_share_zfs(share, NULL, path, &sh, 1930 &ea, ZFS_SHARE_NFS); 1931 if (err != SA_OK) { 1932 errno = err; 1933 err = -1; 1934 } 1935 sa_emptyshare(&sh); 1936 } 1937 } else { 1938 err = exportfs(path, &export); 1939 } 1940 1941 if (err < 0) { 1942 err = SA_SYSTEM_ERR; 1943 switch (errno) { 1944 case EREMOTE: 1945 (void) printf(dgettext(TEXT_DOMAIN, 1946 "NFS: Cannot share filesystems " 1947 "in non-global zones: %s\n"), path); 1948 err = SA_NOT_SUPPORTED; 1949 break; 1950 case EPERM: 1951 if (getzoneid() != GLOBAL_ZONEID) { 1952 (void) printf(dgettext(TEXT_DOMAIN, 1953 "NFS: Cannot share file systems " 1954 "in non-global zones: %s\n"), path); 1955 err = SA_NOT_SUPPORTED; 1956 break; 1957 } 1958 err = SA_NO_PERMISSION; 1959 break; 1960 case EEXIST: 1961 err = SA_SHARE_EXISTS; 1962 break; 1963 default: 1964 break; 1965 } 1966 } else { 1967 /* update sharetab with an add/modify */ 1968 if (!iszfs) { 1969 (void) sa_update_sharetab(share, "nfs"); 1970 } 1971 } 1972 1973 if (err == SA_OK) { 1974 /* 1975 * enable services as needed. This should probably be 1976 * done elsewhere in order to minimize the calls to 1977 * check services. 1978 */ 1979 /* 1980 * check to see if logging and other services need to 1981 * be triggered, but only if there wasn't an 1982 * error. This is probably where sharetab should be 1983 * updated with the NFS specific entry. 1984 */ 1985 if (export.ex_flags & EX_LOG) { 1986 /* enable logging */ 1987 if (nfslogtab_add(path, export.ex_log_buffer, 1988 export.ex_tag) != 0) { 1989 (void) fprintf(stderr, dgettext(TEXT_DOMAIN, 1990 "Could not enable logging for %s\n"), 1991 path); 1992 } 1993 _check_services(service_list_logging); 1994 } else { 1995 /* 1996 * don't have logging so remove it from file. It might 1997 * not be thre, but that doesn't matter. 1998 */ 1999 (void) nfslogtab_deactivate(path); 2000 _check_services(service_list_default); 2001 } 2002 } 2003 2004 out: 2005 if (path != NULL) 2006 free(path); 2007 2008 cleanup_export(&export); 2009 if (opt != NULL) 2010 sa_free_derived_optionset(opt); 2011 if (secoptlist != NULL) 2012 (void) sa_destroy_optionset(secoptlist); 2013 return (err); 2014 } 2015 2016 /* 2017 * nfs_disable_share(share, path) 2018 * 2019 * Unshare the specified share. Note that "path" is the same path as 2020 * what is in the "share" object. It is passed in to avoid an 2021 * additional lookup. A missing "path" value makes this a no-op 2022 * function. 2023 */ 2024 static int 2025 nfs_disable_share(sa_share_t share, char *path) 2026 { 2027 int err; 2028 int ret = SA_OK; 2029 int iszfs; 2030 sa_group_t parent; 2031 sa_handle_t handle; 2032 2033 if (path == NULL) 2034 return (ret); 2035 2036 /* 2037 * If the share is in a ZFS group we need to handle it 2038 * differently. Just being on a ZFS file system isn't 2039 * enough since we may be in a legacy share case. 2040 */ 2041 parent = sa_get_parent_group(share); 2042 iszfs = sa_group_is_zfs(parent); 2043 if (iszfs) { 2044 struct exportfs_args ea; 2045 share_t sh = { 0 }; 2046 ea.dname = path; 2047 ea.uex = NULL; 2048 sh.sh_path = path; 2049 sh.sh_fstype = "nfs"; 2050 2051 err = sa_share_zfs(share, NULL, path, &sh, 2052 &ea, ZFS_UNSHARE_NFS); 2053 if (err != SA_OK) { 2054 errno = err; 2055 err = -1; 2056 } 2057 } else { 2058 err = exportfs(path, NULL); 2059 } 2060 if (err < 0) { 2061 /* 2062 * TBD: only an error in some 2063 * cases - need better analysis 2064 */ 2065 switch (errno) { 2066 case EPERM: 2067 case EACCES: 2068 ret = SA_NO_PERMISSION; 2069 if (getzoneid() != GLOBAL_ZONEID) { 2070 ret = SA_NOT_SUPPORTED; 2071 } 2072 break; 2073 case EINVAL: 2074 case ENOENT: 2075 ret = SA_NO_SUCH_PATH; 2076 break; 2077 default: 2078 ret = SA_SYSTEM_ERR; 2079 break; 2080 } 2081 } 2082 if (ret == SA_OK || ret == SA_NO_SUCH_PATH) { 2083 handle = sa_find_group_handle((sa_group_t)share); 2084 if (!iszfs) 2085 (void) sa_delete_sharetab(handle, path, "nfs"); 2086 /* just in case it was logged */ 2087 (void) nfslogtab_deactivate(path); 2088 } 2089 return (ret); 2090 } 2091 2092 /* 2093 * check_rorwnone(v1, v2, v3) 2094 * 2095 * check ro vs rw vs none values. Over time this may get beefed up. 2096 * for now it just does simple checks. v1 is never NULL but v2 or v3 2097 * could be. 2098 */ 2099 2100 static int 2101 check_rorwnone(char *v1, char *v2, char *v3) 2102 { 2103 int ret = SA_OK; 2104 if (v2 != NULL && strcmp(v1, v2) == 0) 2105 ret = SA_VALUE_CONFLICT; 2106 else if (v3 != NULL && strcmp(v1, v3) == 0) 2107 ret = SA_VALUE_CONFLICT; 2108 2109 return (ret); 2110 } 2111 2112 /* 2113 * nfs_validate_property(handle, property, parent) 2114 * 2115 * Check that the property has a legitimate value for its type. 2116 */ 2117 2118 static int 2119 nfs_validate_property(sa_handle_t handle, sa_property_t property, 2120 sa_optionset_t parent) 2121 { 2122 int ret = SA_OK; 2123 char *propname; 2124 char *other1; 2125 char *other2; 2126 int optindex; 2127 nfsl_config_t *configlist; 2128 sa_group_t parent_group; 2129 char *value; 2130 2131 propname = sa_get_property_attr(property, "type"); 2132 2133 if ((optindex = findopt(propname)) < 0) 2134 ret = SA_NO_SUCH_PROP; 2135 2136 /* need to validate value range here as well */ 2137 2138 if (ret == SA_OK) { 2139 parent_group = sa_get_parent_group((sa_share_t)parent); 2140 if (optdefs[optindex].share && parent_group != NULL && 2141 !sa_is_share(parent_group)) 2142 ret = SA_PROP_SHARE_ONLY; 2143 } 2144 if (ret == SA_OK) { 2145 if (optdefs[optindex].index == OPT_PUBLIC) { 2146 /* 2147 * Public is special in that only one instance can 2148 * be in the repository at the same time. 2149 */ 2150 if (public_exists(handle, parent_group)) { 2151 sa_free_attr_string(propname); 2152 return (SA_VALUE_CONFLICT); 2153 } 2154 } 2155 value = sa_get_property_attr(property, "value"); 2156 if (value != NULL) { 2157 /* first basic type checking */ 2158 switch (optdefs[optindex].type) { 2159 case OPT_TYPE_NUMBER: 2160 /* check that the value is all digits */ 2161 if (!is_a_number(value)) 2162 ret = SA_BAD_VALUE; 2163 break; 2164 case OPT_TYPE_BOOLEAN: 2165 if (strlen(value) == 0 || 2166 strcasecmp(value, "true") == 0 || 2167 strcmp(value, "1") == 0 || 2168 strcasecmp(value, "false") == 0 || 2169 strcmp(value, "0") == 0) { 2170 ret = SA_OK; 2171 } else { 2172 ret = SA_BAD_VALUE; 2173 } 2174 break; 2175 case OPT_TYPE_USER: 2176 if (!is_a_number(value)) { 2177 struct passwd *pw; 2178 /* 2179 * in this case it would have to be a 2180 * user name 2181 */ 2182 pw = getpwnam(value); 2183 if (pw == NULL) 2184 ret = SA_BAD_VALUE; 2185 endpwent(); 2186 } else { 2187 uint64_t intval; 2188 intval = strtoull(value, NULL, 0); 2189 if (intval > UID_MAX && intval != ~0) 2190 ret = SA_BAD_VALUE; 2191 } 2192 break; 2193 case OPT_TYPE_FILE: 2194 if (strcmp(value, "..") == 0 || 2195 strchr(value, '/') != NULL) { 2196 ret = SA_BAD_VALUE; 2197 } 2198 break; 2199 case OPT_TYPE_ACCLIST: { 2200 sa_property_t oprop1; 2201 sa_property_t oprop2; 2202 char *ovalue1 = NULL; 2203 char *ovalue2 = NULL; 2204 2205 if (parent == NULL) 2206 break; 2207 /* 2208 * access list handling. Should eventually 2209 * validate that all the values make sense. 2210 * Also, ro and rw may have cross value 2211 * conflicts. 2212 */ 2213 if (strcmp(propname, SHOPT_RO) == 0) { 2214 other1 = SHOPT_RW; 2215 other2 = SHOPT_NONE; 2216 } else if (strcmp(propname, SHOPT_RW) == 0) { 2217 other1 = SHOPT_RO; 2218 other2 = SHOPT_NONE; 2219 } else if (strcmp(propname, SHOPT_NONE) == 0) { 2220 other1 = SHOPT_RO; 2221 other2 = SHOPT_RW; 2222 } else { 2223 other1 = NULL; 2224 other2 = NULL; 2225 } 2226 if (other1 == NULL && other2 == NULL) 2227 break; 2228 2229 /* compare rw(ro) with ro(rw) */ 2230 2231 oprop1 = sa_get_property(parent, other1); 2232 oprop2 = sa_get_property(parent, other2); 2233 if (oprop1 == NULL && oprop2 == NULL) 2234 break; 2235 /* 2236 * Only potential confusion if other1 2237 * or other2 exists. Check the values 2238 * and run the check if there is a 2239 * value other than the one we are 2240 * explicitly looking at. 2241 */ 2242 ovalue1 = sa_get_property_attr(oprop1, "value"); 2243 ovalue2 = sa_get_property_attr(oprop2, "value"); 2244 if (ovalue1 != NULL || ovalue2 != NULL) 2245 ret = check_rorwnone(value, ovalue1, 2246 ovalue2); 2247 2248 if (ovalue1 != NULL) 2249 sa_free_attr_string(ovalue1); 2250 if (ovalue2 != NULL) 2251 sa_free_attr_string(ovalue2); 2252 break; 2253 } 2254 case OPT_TYPE_LOGTAG: 2255 if (nfsl_getconfig_list(&configlist) == 0) { 2256 int error; 2257 if (value == NULL || 2258 strlen(value) == 0) { 2259 if (value != NULL) 2260 sa_free_attr_string( 2261 value); 2262 value = strdup("global"); 2263 } 2264 if (value != NULL && 2265 nfsl_findconfig(configlist, value, 2266 &error) == NULL) { 2267 ret = SA_BAD_VALUE; 2268 } 2269 /* Must always free when done */ 2270 nfsl_freeconfig_list(&configlist); 2271 } else { 2272 ret = SA_CONFIG_ERR; 2273 } 2274 break; 2275 case OPT_TYPE_STRING: 2276 /* whatever is here should be ok */ 2277 break; 2278 case OPT_TYPE_SECURITY: 2279 /* 2280 * The "sec" property isn't used in the 2281 * non-legacy parts of sharemgr. We need to 2282 * reject it here. For legacy, it is pulled 2283 * out well before we get here. 2284 */ 2285 ret = SA_NO_SUCH_PROP; 2286 break; 2287 default: 2288 break; 2289 } 2290 2291 if (value != NULL) 2292 sa_free_attr_string(value); 2293 2294 if (ret == SA_OK && optdefs[optindex].check != NULL) { 2295 /* do the property specific check */ 2296 ret = optdefs[optindex].check(handle, property); 2297 } 2298 } 2299 } 2300 2301 if (propname != NULL) 2302 sa_free_attr_string(propname); 2303 return (ret); 2304 } 2305 2306 /* 2307 * Protocol management functions 2308 * 2309 * Properties defined in the default files are defined in 2310 * proto_option_defs for parsing and validation. If "other" and 2311 * "compare" are set, then the value for this property should be 2312 * compared against the property specified in "other" using the 2313 * "compare" check (either <= or >=) in order to ensure that the 2314 * values are in the correct range. E.g. setting server_versmin 2315 * higher than server_versmax should not be allowed. 2316 */ 2317 2318 struct proto_option_defs { 2319 char *tag; 2320 char *name; /* display name -- remove protocol identifier */ 2321 int index; 2322 int type; 2323 union { 2324 int intval; 2325 char *string; 2326 } defvalue; 2327 uint32_t svcs; 2328 int32_t minval; 2329 int32_t maxval; 2330 char *other; 2331 int compare; 2332 #define OPT_CMP_GE 0 2333 #define OPT_CMP_LE 1 2334 int (*check)(char *); 2335 } proto_options[] = { 2336 #define PROTO_OPT_NFSD_SERVERS 0 2337 {"nfsd_servers", 2338 "servers", PROTO_OPT_NFSD_SERVERS, OPT_TYPE_NUMBER, 16, SVC_NFSD, 2339 1, INT32_MAX}, 2340 #define PROTO_OPT_LOCKD_LISTEN_BACKLOG 1 2341 {"lockd_listen_backlog", 2342 "lockd_listen_backlog", PROTO_OPT_LOCKD_LISTEN_BACKLOG, 2343 OPT_TYPE_NUMBER, 32, SVC_LOCKD, 32, INT32_MAX}, 2344 #define PROTO_OPT_LOCKD_SERVERS 2 2345 {"lockd_servers", 2346 "lockd_servers", PROTO_OPT_LOCKD_SERVERS, OPT_TYPE_NUMBER, 20, 2347 SVC_LOCKD, 1, INT32_MAX}, 2348 #define PROTO_OPT_LOCKD_RETRANSMIT_TIMEOUT 3 2349 {"lockd_retransmit_timeout", 2350 "lockd_retransmit_timeout", PROTO_OPT_LOCKD_RETRANSMIT_TIMEOUT, 2351 OPT_TYPE_NUMBER, 5, SVC_LOCKD, 0, INT32_MAX}, 2352 #define PROTO_OPT_GRACE_PERIOD 4 2353 {"grace_period", 2354 "grace_period", PROTO_OPT_GRACE_PERIOD, OPT_TYPE_NUMBER, 90, 2355 SVC_LOCKD, 0, INT32_MAX}, 2356 #define PROTO_OPT_NFS_SERVER_VERSMIN 5 2357 {"nfs_server_versmin", 2358 "server_versmin", PROTO_OPT_NFS_SERVER_VERSMIN, OPT_TYPE_NUMBER, 2359 (int)NFS_VERSMIN_DEFAULT, SVC_NFSD|SVC_MOUNTD, NFS_VERSMIN, 2360 NFS_VERSMAX, "server_versmax", OPT_CMP_LE}, 2361 #define PROTO_OPT_NFS_SERVER_VERSMAX 6 2362 {"nfs_server_versmax", 2363 "server_versmax", PROTO_OPT_NFS_SERVER_VERSMAX, OPT_TYPE_NUMBER, 2364 (int)NFS_VERSMAX_DEFAULT, SVC_NFSD|SVC_MOUNTD, NFS_VERSMIN, 2365 NFS_VERSMAX, "server_versmin", OPT_CMP_GE}, 2366 #define PROTO_OPT_NFS_CLIENT_VERSMIN 7 2367 {"nfs_client_versmin", 2368 "client_versmin", PROTO_OPT_NFS_CLIENT_VERSMIN, OPT_TYPE_NUMBER, 2369 (int)NFS_VERSMIN_DEFAULT, SVC_CLIENT, NFS_VERSMIN, NFS_VERSMAX, 2370 "client_versmax", OPT_CMP_LE}, 2371 #define PROTO_OPT_NFS_CLIENT_VERSMAX 8 2372 {"nfs_client_versmax", 2373 "client_versmax", PROTO_OPT_NFS_CLIENT_VERSMAX, OPT_TYPE_NUMBER, 2374 (int)NFS_VERSMAX_DEFAULT, SVC_CLIENT, NFS_VERSMIN, NFS_VERSMAX, 2375 "client_versmin", OPT_CMP_GE}, 2376 #define PROTO_OPT_NFS_SERVER_DELEGATION 9 2377 {"nfs_server_delegation", 2378 "server_delegation", PROTO_OPT_NFS_SERVER_DELEGATION, 2379 OPT_TYPE_ONOFF, NFS_SERVER_DELEGATION_DEFAULT, SVC_NFSD, 0, 0}, 2380 #define PROTO_OPT_NFSMAPID_DOMAIN 10 2381 {"nfsmapid_domain", 2382 "nfsmapid_domain", PROTO_OPT_NFSMAPID_DOMAIN, OPT_TYPE_DOMAIN, 2383 NULL, SVC_NFSMAPID, 0, 0}, 2384 #define PROTO_OPT_NFSD_MAX_CONNECTIONS 11 2385 {"nfsd_max_connections", 2386 "max_connections", PROTO_OPT_NFSD_MAX_CONNECTIONS, 2387 OPT_TYPE_NUMBER, -1, SVC_NFSD, -1, INT32_MAX}, 2388 #define PROTO_OPT_NFSD_PROTOCOL 12 2389 {"nfsd_protocol", 2390 "protocol", PROTO_OPT_NFSD_PROTOCOL, OPT_TYPE_PROTOCOL, 0, 2391 SVC_NFSD, 0, 0}, 2392 #define PROTO_OPT_NFSD_LISTEN_BACKLOG 13 2393 {"nfsd_listen_backlog", 2394 "listen_backlog", PROTO_OPT_NFSD_LISTEN_BACKLOG, 2395 OPT_TYPE_NUMBER, 0, SVC_NFSD, 0, INT32_MAX}, 2396 #define PROTO_OPT_NFSD_DEVICE 14 2397 {"nfsd_device", 2398 "device", PROTO_OPT_NFSD_DEVICE, 2399 OPT_TYPE_STRING, NULL, SVC_NFSD, 0, 0}, 2400 #define PROTO_OPT_MOUNTD_LISTEN_BACKLOG 15 2401 {"mountd_listen_backlog", 2402 "mountd_listen_backlog", PROTO_OPT_MOUNTD_LISTEN_BACKLOG, 2403 OPT_TYPE_NUMBER, 64, SVC_NFSD|SVC_MOUNTD, 1, INT32_MAX}, 2404 #define PROTO_OPT_MOUNTD_MAX_THREADS 16 2405 {"mountd_max_threads", 2406 "mountd_max_threads", PROTO_OPT_MOUNTD_MAX_THREADS, 2407 OPT_TYPE_NUMBER, 16, SVC_NFSD|SVC_MOUNTD, 1, INT32_MAX}, 2408 {NULL} 2409 }; 2410 2411 /* 2412 * the protoset holds the defined options so we don't have to read 2413 * them multiple times 2414 */ 2415 static sa_protocol_properties_t protoset; 2416 2417 static int 2418 findprotoopt(char *name, int whichname) 2419 { 2420 int i; 2421 for (i = 0; proto_options[i].tag != NULL; i++) { 2422 if (whichname == 1) { 2423 if (strcasecmp(proto_options[i].name, name) == 0) 2424 return (i); 2425 } else { 2426 if (strcasecmp(proto_options[i].tag, name) == 0) 2427 return (i); 2428 } 2429 } 2430 return (-1); 2431 } 2432 2433 /* 2434 * fixcaselower(str) 2435 * 2436 * convert a string to lower case (inplace). 2437 */ 2438 2439 static void 2440 fixcaselower(char *str) 2441 { 2442 while (*str) { 2443 *str = tolower(*str); 2444 str++; 2445 } 2446 } 2447 2448 /* 2449 * skipwhitespace(str) 2450 * 2451 * Skip leading white space. It is assumed that it is called with a 2452 * valid pointer. 2453 */ 2454 2455 static char * 2456 skipwhitespace(char *str) 2457 { 2458 while (*str && isspace(*str)) 2459 str++; 2460 2461 return (str); 2462 } 2463 2464 /* 2465 * extractprop() 2466 * 2467 * Extract the property and value out of the line and create the 2468 * property in the optionset. 2469 */ 2470 static int 2471 extractprop(char *name, char *value) 2472 { 2473 sa_property_t prop; 2474 int index; 2475 int ret = SA_OK; 2476 /* 2477 * Remove any leading 2478 * white space. 2479 */ 2480 name = skipwhitespace(name); 2481 2482 index = findprotoopt(name, 1); 2483 if (index >= 0) { 2484 fixcaselower(name); 2485 prop = sa_create_property(proto_options[index].name, value); 2486 if (prop != NULL) 2487 ret = sa_add_protocol_property(protoset, prop); 2488 else 2489 ret = SA_NO_MEMORY; 2490 } 2491 return (ret); 2492 } 2493 2494 scf_type_t 2495 getscftype(int type) 2496 { 2497 scf_type_t ret; 2498 2499 switch (type) { 2500 case OPT_TYPE_NUMBER: 2501 ret = SCF_TYPE_INTEGER; 2502 break; 2503 case OPT_TYPE_BOOLEAN: 2504 ret = SCF_TYPE_BOOLEAN; 2505 break; 2506 default: 2507 ret = SCF_TYPE_ASTRING; 2508 } 2509 return (ret); 2510 } 2511 2512 char * 2513 getsvcname(uint32_t svcs) 2514 { 2515 char *service; 2516 switch (svcs) { 2517 case SVC_LOCKD: 2518 service = LOCKD; 2519 break; 2520 case SVC_STATD: 2521 service = STATD; 2522 break; 2523 case SVC_NFSD: 2524 service = NFSD; 2525 break; 2526 case SVC_CLIENT: 2527 service = NFS_CLIENT_SVC; 2528 break; 2529 case SVC_NFS4CBD: 2530 service = NFS4CBD; 2531 break; 2532 case SVC_NFSMAPID: 2533 service = NFSMAPID; 2534 break; 2535 case SVC_RQUOTAD: 2536 service = RQUOTAD; 2537 break; 2538 case SVC_NFSLOGD: 2539 service = NFSLOGD; 2540 break; 2541 case SVC_REPARSED: 2542 service = REPARSED; 2543 break; 2544 default: 2545 service = NFSD; 2546 } 2547 return (service); 2548 } 2549 2550 /* 2551 * initprotofromsmf() 2552 * 2553 * Read NFS SMF properties and add the defined values to the 2554 * protoset. Note that default values are known from the built in 2555 * table in case SMF doesn't have a definition. Not having 2556 * SMF properties is OK since we have builtin default 2557 * values. 2558 */ 2559 static int 2560 initprotofromsmf() 2561 { 2562 char name[PATH_MAX]; 2563 char value[PATH_MAX]; 2564 int ret = SA_OK, bufsz = 0, i; 2565 2566 protoset = sa_create_protocol_properties("nfs"); 2567 if (protoset != NULL) { 2568 for (i = 0; proto_options[i].tag != NULL; i++) { 2569 scf_type_t ptype; 2570 char *svc_name; 2571 2572 bzero(value, PATH_MAX); 2573 (void) strncpy(name, proto_options[i].name, PATH_MAX); 2574 /* Replace NULL with the correct instance */ 2575 ptype = getscftype(proto_options[i].type); 2576 svc_name = getsvcname(proto_options[i].svcs); 2577 bufsz = PATH_MAX; 2578 ret = nfs_smf_get_prop(name, value, 2579 (char *)DEFAULT_INSTANCE, ptype, 2580 svc_name, &bufsz); 2581 if (ret == SA_OK) { 2582 ret = extractprop(name, value); 2583 } 2584 } 2585 } else { 2586 ret = SA_NO_MEMORY; 2587 } 2588 2589 return (ret); 2590 } 2591 2592 /* 2593 * add_defaults() 2594 * 2595 * Add the default values for any property not defined 2596 * in NFS SMF repository. 2597 * Values are set according to their defined types. 2598 */ 2599 2600 static void 2601 add_defaults() 2602 { 2603 int i; 2604 char number[MAXDIGITS]; 2605 2606 for (i = 0; proto_options[i].tag != NULL; i++) { 2607 sa_property_t prop; 2608 prop = sa_get_protocol_property(protoset, 2609 proto_options[i].name); 2610 if (prop == NULL) { 2611 /* add the default value */ 2612 switch (proto_options[i].type) { 2613 case OPT_TYPE_NUMBER: 2614 (void) snprintf(number, sizeof (number), "%d", 2615 proto_options[i].defvalue.intval); 2616 prop = sa_create_property(proto_options[i].name, 2617 number); 2618 break; 2619 2620 case OPT_TYPE_BOOLEAN: 2621 prop = sa_create_property(proto_options[i].name, 2622 proto_options[i].defvalue.intval ? 2623 "true" : "false"); 2624 break; 2625 2626 case OPT_TYPE_ONOFF: 2627 prop = sa_create_property(proto_options[i].name, 2628 proto_options[i].defvalue.intval ? 2629 "on" : "off"); 2630 break; 2631 2632 default: 2633 /* treat as strings of zero length */ 2634 prop = sa_create_property(proto_options[i].name, 2635 ""); 2636 break; 2637 } 2638 if (prop != NULL) 2639 (void) sa_add_protocol_property(protoset, prop); 2640 } 2641 } 2642 } 2643 2644 static void 2645 free_protoprops() 2646 { 2647 if (protoset != NULL) { 2648 xmlFreeNode(protoset); 2649 protoset = NULL; 2650 } 2651 } 2652 2653 /* 2654 * nfs_init() 2655 * 2656 * Initialize the NFS plugin. 2657 */ 2658 2659 static int 2660 nfs_init() 2661 { 2662 int ret = SA_OK; 2663 2664 if (sa_plugin_ops.sa_init != nfs_init) { 2665 (void) printf(dgettext(TEXT_DOMAIN, 2666 "NFS plugin not properly initialized\n")); 2667 return (SA_CONFIG_ERR); 2668 } 2669 2670 ret = initprotofromsmf(); 2671 if (ret != SA_OK) { 2672 (void) printf(dgettext(TEXT_DOMAIN, 2673 "NFS plugin problem with SMF repository: %s\n"), 2674 sa_errorstr(ret)); 2675 ret = SA_OK; 2676 } 2677 add_defaults(); 2678 2679 return (ret); 2680 } 2681 2682 /* 2683 * nfs_fini() 2684 * 2685 * uninitialize the NFS plugin. Want to avoid memory leaks. 2686 */ 2687 2688 static void 2689 nfs_fini() 2690 { 2691 free_protoprops(); 2692 } 2693 2694 /* 2695 * nfs_get_proto_set() 2696 * 2697 * Return an optionset with all the protocol specific properties in 2698 * it. 2699 */ 2700 2701 static sa_protocol_properties_t 2702 nfs_get_proto_set() 2703 { 2704 return (protoset); 2705 } 2706 2707 /* 2708 * service_in_state(service, chkstate) 2709 * 2710 * Want to know if the specified service is in the desired state 2711 * (chkstate) or not. Return true (1) if it is and false (0) if it 2712 * isn't. 2713 */ 2714 static int 2715 service_in_state(char *service, const char *chkstate) 2716 { 2717 char *state; 2718 int ret = B_FALSE; 2719 2720 state = smf_get_state(service); 2721 if (state != NULL) { 2722 /* got the state so get the equality for the return value */ 2723 ret = strcmp(state, chkstate) == 0 ? B_TRUE : B_FALSE; 2724 free(state); 2725 } 2726 return (ret); 2727 } 2728 2729 /* 2730 * restart_service(svcs) 2731 * 2732 * Walk through the bit mask of services that need to be restarted in 2733 * order to use the new property values. Some properties affect 2734 * multiple daemons. Should only restart a service if it is currently 2735 * enabled (online). 2736 */ 2737 2738 static void 2739 restart_service(uint32_t svcs) 2740 { 2741 uint32_t mask; 2742 int ret; 2743 char *service; 2744 2745 for (mask = 1; svcs != 0; mask <<= 1) { 2746 switch (svcs & mask) { 2747 case SVC_LOCKD: 2748 service = LOCKD; 2749 break; 2750 case SVC_STATD: 2751 service = STATD; 2752 break; 2753 case SVC_NFSD: 2754 service = NFSD; 2755 break; 2756 case SVC_MOUNTD: 2757 service = MOUNTD; 2758 break; 2759 case SVC_NFS4CBD: 2760 service = NFS4CBD; 2761 break; 2762 case SVC_NFSMAPID: 2763 service = NFSMAPID; 2764 break; 2765 case SVC_RQUOTAD: 2766 service = RQUOTAD; 2767 break; 2768 case SVC_NFSLOGD: 2769 service = NFSLOGD; 2770 break; 2771 case SVC_REPARSED: 2772 service = REPARSED; 2773 break; 2774 case SVC_CLIENT: 2775 service = NFS_CLIENT_SVC; 2776 break; 2777 default: 2778 continue; 2779 } 2780 2781 /* 2782 * Only attempt to restart the service if it is 2783 * currently running. In the future, it may be 2784 * desirable to use smf_refresh_instance if the NFS 2785 * services ever implement the refresh method. 2786 */ 2787 if (service_in_state(service, SCF_STATE_STRING_ONLINE)) { 2788 ret = smf_restart_instance(service); 2789 /* 2790 * There are only a few SMF errors at this point, but 2791 * it is also possible that a bad value may have put 2792 * the service into maintenance if there wasn't an 2793 * SMF level error. 2794 */ 2795 if (ret != 0) { 2796 (void) fprintf(stderr, 2797 dgettext(TEXT_DOMAIN, 2798 "%s failed to restart: %s\n"), 2799 service, scf_strerror(scf_error())); 2800 } else { 2801 /* 2802 * Check whether it has gone to "maintenance" 2803 * mode or not. Maintenance implies something 2804 * went wrong. 2805 */ 2806 if (service_in_state(service, 2807 SCF_STATE_STRING_MAINT)) { 2808 (void) fprintf(stderr, 2809 dgettext(TEXT_DOMAIN, 2810 "%s failed to restart\n"), 2811 service); 2812 } 2813 } 2814 } 2815 svcs &= ~mask; 2816 } 2817 } 2818 2819 /* 2820 * nfs_minmax_check(name, value) 2821 * 2822 * Verify that the value for the property specified by index is valid 2823 * relative to the opposite value in the case of a min/max variable. 2824 * Currently, server_minvers/server_maxvers and 2825 * client_minvers/client_maxvers are the only ones to check. 2826 */ 2827 2828 static int 2829 nfs_minmax_check(int index, int value) 2830 { 2831 int val; 2832 char *pval; 2833 sa_property_t prop; 2834 sa_optionset_t opts; 2835 int ret = B_TRUE; 2836 2837 if (proto_options[index].other != NULL) { 2838 /* have a property to compare against */ 2839 opts = nfs_get_proto_set(); 2840 prop = sa_get_property(opts, proto_options[index].other); 2841 /* 2842 * If we don't find the property, assume default 2843 * values which will work since the max will be at the 2844 * max and the min at the min. 2845 */ 2846 if (prop != NULL) { 2847 pval = sa_get_property_attr(prop, "value"); 2848 if (pval != NULL) { 2849 val = strtoul(pval, NULL, 0); 2850 if (proto_options[index].compare == 2851 OPT_CMP_LE) { 2852 ret = value <= val ? B_TRUE : B_FALSE; 2853 } else if (proto_options[index].compare == 2854 OPT_CMP_GE) { 2855 ret = value >= val ? B_TRUE : B_FALSE; 2856 } 2857 sa_free_attr_string(pval); 2858 } 2859 } 2860 } 2861 return (ret); 2862 } 2863 2864 /* 2865 * nfs_validate_proto_prop(index, name, value) 2866 * 2867 * Verify that the property specified by name can take the new 2868 * value. This is a sanity check to prevent bad values getting into 2869 * the default files. All values need to be checked against what is 2870 * allowed by their defined type. If a type isn't explicitly defined 2871 * here, it is treated as a string. 2872 * 2873 * Note that OPT_TYPE_NUMBER will additionally check that the value is 2874 * within the range specified and potentially against another property 2875 * value as well as specified in the proto_options members other and 2876 * compare. 2877 */ 2878 2879 static int 2880 nfs_validate_proto_prop(int index, char *name, char *value) 2881 { 2882 int ret = SA_OK; 2883 char *cp; 2884 #ifdef lint 2885 name = name; 2886 #endif 2887 switch (proto_options[index].type) { 2888 case OPT_TYPE_NUMBER: 2889 if (!is_a_number(value)) 2890 ret = SA_BAD_VALUE; 2891 else { 2892 int val; 2893 val = strtoul(value, NULL, 0); 2894 if (val < proto_options[index].minval || 2895 val > proto_options[index].maxval) 2896 ret = SA_BAD_VALUE; 2897 /* 2898 * For server_versmin/server_versmax and 2899 * client_versmin/client_versmax, the value of the 2900 * min(max) should be checked to be correct relative 2901 * to the current max(min). 2902 */ 2903 if (!nfs_minmax_check(index, val)) { 2904 ret = SA_BAD_VALUE; 2905 } 2906 } 2907 break; 2908 2909 case OPT_TYPE_DOMAIN: 2910 /* 2911 * needs to be a qualified domain so will have at 2912 * least one period and other characters on either 2913 * side of it. A zero length string is also allowed 2914 * and is the way to turn off the override. 2915 */ 2916 if (strlen(value) == 0) 2917 break; 2918 cp = strchr(value, '.'); 2919 if (cp == NULL || cp == value || strchr(value, '@') != NULL) 2920 ret = SA_BAD_VALUE; 2921 break; 2922 2923 case OPT_TYPE_BOOLEAN: 2924 if (strlen(value) == 0 || 2925 strcasecmp(value, "true") == 0 || 2926 strcmp(value, "1") == 0 || 2927 strcasecmp(value, "false") == 0 || 2928 strcmp(value, "0") == 0) { 2929 ret = SA_OK; 2930 } else { 2931 ret = SA_BAD_VALUE; 2932 } 2933 break; 2934 2935 case OPT_TYPE_ONOFF: 2936 if (strcasecmp(value, "on") != 0 && 2937 strcasecmp(value, "off") != 0) { 2938 ret = SA_BAD_VALUE; 2939 } 2940 break; 2941 2942 case OPT_TYPE_PROTOCOL: 2943 if (strlen(value) != 0 && 2944 strcasecmp(value, "all") != 0 && 2945 strcasecmp(value, "tcp") != 0 && 2946 strcasecmp(value, "udp") != 0) 2947 ret = SA_BAD_VALUE; 2948 break; 2949 2950 default: 2951 /* treat as a string */ 2952 break; 2953 } 2954 return (ret); 2955 } 2956 2957 /* 2958 * nfs_set_proto_prop(prop) 2959 * 2960 * check that prop is valid. 2961 */ 2962 2963 static int 2964 nfs_set_proto_prop(sa_property_t prop) 2965 { 2966 int ret = SA_OK; 2967 char *name; 2968 char *value; 2969 2970 name = sa_get_property_attr(prop, "type"); 2971 value = sa_get_property_attr(prop, "value"); 2972 if (name != NULL && value != NULL) { 2973 scf_type_t sctype; 2974 char *svc_name; 2975 char *instance = NULL; 2976 int index = findprotoopt(name, 1); 2977 2978 ret = nfs_validate_proto_prop(index, name, value); 2979 if (ret == SA_OK) { 2980 sctype = getscftype(proto_options[index].type); 2981 svc_name = getsvcname(proto_options[index].svcs); 2982 if (sctype == SCF_TYPE_BOOLEAN) { 2983 if (value != NULL) 2984 sa_free_attr_string(value); 2985 if (string_to_boolean(value) == 0) 2986 value = strdup("0"); 2987 else 2988 value = strdup("1"); 2989 } 2990 ret = nfs_smf_set_prop(name, value, instance, sctype, 2991 svc_name); 2992 if (ret == SA_OK) { 2993 restart_service(proto_options[index].svcs); 2994 } else { 2995 (void) printf(dgettext(TEXT_DOMAIN, 2996 "Cannot restart NFS services : %s\n"), 2997 sa_errorstr(ret)); 2998 } 2999 } 3000 } 3001 if (name != NULL) 3002 sa_free_attr_string(name); 3003 if (value != NULL) 3004 sa_free_attr_string(value); 3005 return (ret); 3006 } 3007 3008 /* 3009 * nfs_get_status() 3010 * 3011 * What is the current status of the nfsd? We use the SMF state here. 3012 * Caller must free the returned value. 3013 */ 3014 3015 static char * 3016 nfs_get_status() 3017 { 3018 return (smf_get_state(NFSD)); 3019 } 3020 3021 /* 3022 * nfs_space_alias(alias) 3023 * 3024 * Lookup the space (security) name. If it is default, convert to the 3025 * real name. 3026 */ 3027 3028 static char * 3029 nfs_space_alias(char *space) 3030 { 3031 char *name = space; 3032 seconfig_t secconf; 3033 3034 /* 3035 * Only the space named "default" is special. If it is used, 3036 * the default needs to be looked up and the real name used. 3037 * This is normally "sys" but could be changed. We always 3038 * change defautl to the real name. 3039 */ 3040 if (strcmp(space, "default") == 0 && 3041 nfs_getseconfig_default(&secconf) == 0) { 3042 if (nfs_getseconfig_bynumber(secconf.sc_nfsnum, &secconf) == 0) 3043 name = secconf.sc_name; 3044 } 3045 return (strdup(name)); 3046 } 3047 3048 /* 3049 * nfs_features() 3050 * 3051 * Return a mask of the features required. 3052 */ 3053 3054 static uint64_t 3055 nfs_features() 3056 { 3057 return ((uint64_t)SA_FEATURE_DFSTAB | SA_FEATURE_SERVER); 3058 } 3059