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