13910Sdougm /* 23910Sdougm * CDDL HEADER START 33910Sdougm * 43910Sdougm * The contents of this file are subject to the terms of the 53910Sdougm * Common Development and Distribution License (the "License"). 63910Sdougm * You may not use this file except in compliance with the License. 73910Sdougm * 83910Sdougm * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 93910Sdougm * or http://www.opensolaris.org/os/licensing. 103910Sdougm * See the License for the specific language governing permissions 113910Sdougm * and limitations under the License. 123910Sdougm * 133910Sdougm * When distributing Covered Code, include this CDDL HEADER in each 143910Sdougm * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 153910Sdougm * If applicable, add the following below this CDDL HEADER, with the 163910Sdougm * fields enclosed by brackets "[]" replaced with your own identifying 173910Sdougm * information: Portions Copyright [yyyy] [name of copyright owner] 183910Sdougm * 193910Sdougm * CDDL HEADER END 203910Sdougm */ 213910Sdougm 223910Sdougm /* 233910Sdougm * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 243910Sdougm * Use is subject to license terms. 253910Sdougm */ 263910Sdougm 273910Sdougm #pragma ident "%Z%%M% %I% %E% SMI" 283910Sdougm 293910Sdougm /* 303910Sdougm * NFS specific functions 313910Sdougm */ 323910Sdougm #include <stdio.h> 333910Sdougm #include <string.h> 343910Sdougm #include <ctype.h> 353910Sdougm #include <stdlib.h> 363910Sdougm #include <unistd.h> 373910Sdougm #include <zone.h> 383910Sdougm #include <errno.h> 393910Sdougm #include <locale.h> 403910Sdougm #include <signal.h> 413910Sdougm #include "libshare.h" 423910Sdougm #include "libshare_impl.h" 433910Sdougm #include <nfs/export.h> 443910Sdougm #include <pwd.h> 453910Sdougm #include <limits.h> 463910Sdougm #include <libscf.h> 473910Sdougm #include "nfslog_config.h" 483910Sdougm #include "nfslogtab.h" 493910Sdougm #include "libshare_nfs.h" 503910Sdougm #include <rpcsvc/daemon_utils.h> 513910Sdougm #include <nfs/nfs.h> 524543Smarks #include <nfs/nfssys.h> 533910Sdougm 543910Sdougm /* should really be in some global place */ 553910Sdougm #define DEF_WIN 30000 563910Sdougm #define OPT_CHUNK 1024 573910Sdougm 583910Sdougm int debug = 0; 593910Sdougm 604543Smarks #define NFS_SERVER_SVC "svc:/network/nfs/server:default" 613910Sdougm 623910Sdougm /* internal functions */ 633910Sdougm static int nfs_init(); 643910Sdougm static void nfs_fini(); 653910Sdougm static int nfs_enable_share(sa_share_t); 664543Smarks static int nfs_disable_share(sa_share_t, char *); 673910Sdougm static int nfs_validate_property(sa_property_t, sa_optionset_t); 683910Sdougm static int nfs_validate_security_mode(char *); 693910Sdougm static int nfs_is_security_opt(char *); 703910Sdougm static int nfs_parse_legacy_options(sa_group_t, char *); 713910Sdougm static char *nfs_format_options(sa_group_t, int); 723910Sdougm static int nfs_set_proto_prop(sa_property_t); 733910Sdougm static sa_protocol_properties_t nfs_get_proto_set(); 743910Sdougm static char *nfs_get_status(); 753910Sdougm static char *nfs_space_alias(char *); 76*5331Samw static uint64_t nfs_features(); 773910Sdougm 783910Sdougm /* 793910Sdougm * ops vector that provides the protocol specific info and operations 803910Sdougm * for share management. 813910Sdougm */ 823910Sdougm 833910Sdougm struct sa_plugin_ops sa_plugin_ops = { 843910Sdougm SA_PLUGIN_VERSION, 853910Sdougm "nfs", 863910Sdougm nfs_init, 873910Sdougm nfs_fini, 883910Sdougm nfs_enable_share, 893910Sdougm nfs_disable_share, 903910Sdougm nfs_validate_property, 913910Sdougm nfs_validate_security_mode, 923910Sdougm nfs_is_security_opt, 933910Sdougm nfs_parse_legacy_options, 943910Sdougm nfs_format_options, 953910Sdougm nfs_set_proto_prop, 963910Sdougm nfs_get_proto_set, 973910Sdougm nfs_get_status, 983910Sdougm nfs_space_alias, 99*5331Samw NULL, /* update_legacy */ 100*5331Samw NULL, /* delete_legacy */ 101*5331Samw NULL, /* change_notify */ 102*5331Samw NULL, /* enable_resource */ 103*5331Samw NULL, /* disable_resource */ 104*5331Samw nfs_features, 105*5331Samw NULL, /* transient shares */ 106*5331Samw NULL, /* notify resource */ 1073910Sdougm NULL 1083910Sdougm }; 1093910Sdougm 1103910Sdougm /* 1113910Sdougm * list of support services needed 1123910Sdougm * defines should come from head/rpcsvc/daemon_utils.h 1133910Sdougm */ 1143910Sdougm 1153910Sdougm static char *service_list_default[] = 1163910Sdougm { STATD, LOCKD, MOUNTD, NFSD, NFSMAPID, RQUOTAD, NULL }; 1173910Sdougm static char *service_list_logging[] = 1183910Sdougm { STATD, LOCKD, MOUNTD, NFSD, NFSMAPID, RQUOTAD, NFSLOGD, NULL }; 1193910Sdougm 1203910Sdougm /* 1213910Sdougm * option definitions. Make sure to keep the #define for the option 1223910Sdougm * index just before the entry it is the index for. Changing the order 1233910Sdougm * can cause breakage. E.g OPT_RW is index 1 and must precede the 1243910Sdougm * line that includes the SHOPT_RW and OPT_RW entries. 1253910Sdougm */ 1263910Sdougm 1273910Sdougm struct option_defs optdefs[] = { 1283910Sdougm #define OPT_RO 0 1293910Sdougm {SHOPT_RO, OPT_RO, OPT_TYPE_ACCLIST}, 1303910Sdougm #define OPT_RW 1 1313910Sdougm {SHOPT_RW, OPT_RW, OPT_TYPE_ACCLIST}, 1323910Sdougm #define OPT_ROOT 2 1333910Sdougm {SHOPT_ROOT, OPT_ROOT, OPT_TYPE_ACCLIST}, 1343910Sdougm #define OPT_SECURE 3 1353910Sdougm {SHOPT_SECURE, OPT_SECURE, OPT_TYPE_DEPRECATED}, 1363910Sdougm #define OPT_ANON 4 1373910Sdougm {SHOPT_ANON, OPT_ANON, OPT_TYPE_USER}, 1383910Sdougm #define OPT_WINDOW 5 1393910Sdougm {SHOPT_WINDOW, OPT_WINDOW, OPT_TYPE_NUMBER}, 1403910Sdougm #define OPT_NOSUID 6 1413910Sdougm {SHOPT_NOSUID, OPT_NOSUID, OPT_TYPE_BOOLEAN}, 1423910Sdougm #define OPT_ACLOK 7 1433910Sdougm {SHOPT_ACLOK, OPT_ACLOK, OPT_TYPE_BOOLEAN}, 1443910Sdougm #define OPT_NOSUB 8 1453910Sdougm {SHOPT_NOSUB, OPT_NOSUB, OPT_TYPE_BOOLEAN}, 1463910Sdougm #define OPT_SEC 9 1473910Sdougm {SHOPT_SEC, OPT_SEC, OPT_TYPE_SECURITY}, 1483910Sdougm #define OPT_PUBLIC 10 1493910Sdougm {SHOPT_PUBLIC, OPT_PUBLIC, OPT_TYPE_BOOLEAN, OPT_SHARE_ONLY}, 1503910Sdougm #define OPT_INDEX 11 1513910Sdougm {SHOPT_INDEX, OPT_INDEX, OPT_TYPE_FILE}, 1523910Sdougm #define OPT_LOG 12 1533910Sdougm {SHOPT_LOG, OPT_LOG, OPT_TYPE_LOGTAG}, 1543910Sdougm #define OPT_CKSUM 13 1553910Sdougm {SHOPT_CKSUM, OPT_CKSUM, OPT_TYPE_STRINGSET}, 1563910Sdougm #ifdef VOLATILE_FH_TEST /* XXX added for testing volatile fh's only */ 1573910Sdougm #define OPT_VOLFH 14 1583910Sdougm {SHOPT_VOLFH, OPT_VOLFH}, 1593910Sdougm #endif /* VOLATILE_FH_TEST */ 1603910Sdougm NULL 1613910Sdougm }; 1623910Sdougm 1633910Sdougm /* 1643910Sdougm * list of properties that are related to security flavors. 1653910Sdougm */ 1663910Sdougm static char *seclist[] = { 1673910Sdougm SHOPT_RO, 1683910Sdougm SHOPT_RW, 1693910Sdougm SHOPT_ROOT, 1703910Sdougm SHOPT_WINDOW, 1713910Sdougm NULL 1723910Sdougm }; 1733910Sdougm 1743910Sdougm /* structure for list of securities */ 1753910Sdougm struct securities { 1763910Sdougm sa_security_t security; 1773910Sdougm struct securities *next; 1783910Sdougm }; 1793910Sdougm 1803910Sdougm /* 1813910Sdougm * findopt(name) 1823910Sdougm * 1833910Sdougm * Lookup option "name" in the option table and return the table 1843910Sdougm * index. 1853910Sdougm */ 1863910Sdougm 1873910Sdougm static int 1883910Sdougm findopt(char *name) 1893910Sdougm { 1903910Sdougm int i; 1913910Sdougm if (name != NULL) { 1924345Sdougm for (i = 0; optdefs[i].tag != NULL; i++) { 1934345Sdougm if (strcmp(optdefs[i].tag, name) == 0) 1944345Sdougm return (i); 1954345Sdougm } 1963910Sdougm } 1973910Sdougm return (-1); 1983910Sdougm } 1993910Sdougm 2003910Sdougm /* 2013910Sdougm * gettype(name) 2023910Sdougm * 2033910Sdougm * Return the type of option "name". 2043910Sdougm */ 2053910Sdougm 2063910Sdougm static int 2073910Sdougm gettype(char *name) 2083910Sdougm { 2093910Sdougm int optdef; 2103910Sdougm 2113910Sdougm optdef = findopt(name); 2123910Sdougm if (optdef != -1) 2134345Sdougm return (optdefs[optdef].type); 2143910Sdougm return (OPT_TYPE_ANY); 2153910Sdougm } 2163910Sdougm 2173910Sdougm /* 2183910Sdougm * nfs_validate_security_mode(mode) 2193910Sdougm * 2203910Sdougm * is the specified mode string a valid one for use with NFS? 2213910Sdougm */ 2223910Sdougm 2233910Sdougm static int 2243910Sdougm nfs_validate_security_mode(char *mode) 2253910Sdougm { 2263910Sdougm seconfig_t secinfo; 2273910Sdougm int err; 2283910Sdougm 2293910Sdougm (void) memset(&secinfo, '\0', sizeof (secinfo)); 2303910Sdougm err = nfs_getseconfig_byname(mode, &secinfo); 2313910Sdougm if (err == SC_NOERROR) 2324345Sdougm return (1); 2333910Sdougm return (0); 2343910Sdougm } 2353910Sdougm 2363910Sdougm /* 2373910Sdougm * nfs_is_security_opt(tok) 2383910Sdougm * 2393910Sdougm * check to see if tok represents an option that is only valid in some 2403910Sdougm * security flavor. 2413910Sdougm */ 2423910Sdougm 2433910Sdougm static int 2443910Sdougm nfs_is_security_opt(char *tok) 2453910Sdougm { 2463910Sdougm int i; 2473910Sdougm 2483910Sdougm for (i = 0; seclist[i] != NULL; i++) { 2494345Sdougm if (strcmp(tok, seclist[i]) == 0) 2504345Sdougm return (1); 2513910Sdougm } 2523910Sdougm return (0); 2533910Sdougm } 2543910Sdougm 2553910Sdougm /* 2563910Sdougm * find_security(seclist, sec) 2573910Sdougm * 2583910Sdougm * Walk the current list of security flavors and return true if it is 2593910Sdougm * present, else return false. 2603910Sdougm */ 2613910Sdougm 2623910Sdougm static int 2633910Sdougm find_security(struct securities *seclist, sa_security_t sec) 2643910Sdougm { 2653910Sdougm while (seclist != NULL) { 2664345Sdougm if (seclist->security == sec) 2674345Sdougm return (1); 2684345Sdougm seclist = seclist->next; 2693910Sdougm } 2703910Sdougm return (0); 2713910Sdougm } 2723910Sdougm 2733910Sdougm /* 2743910Sdougm * make_security_list(group, securitymodes, proto) 2753910Sdougm * go through the list of securitymodes and add them to the 2763910Sdougm * group's list of security optionsets. We also keep a list of 2773910Sdougm * those optionsets so we don't have to find them later. All of 2783910Sdougm * these will get copies of the same properties. 2793910Sdougm */ 2803910Sdougm 2813910Sdougm static struct securities * 2823910Sdougm make_security_list(sa_group_t group, char *securitymodes, char *proto) 2833910Sdougm { 2843910Sdougm char *tok, *next = NULL; 2853910Sdougm struct securities *curp, *headp = NULL, *prev; 2863910Sdougm sa_security_t check; 2873910Sdougm int freetok = 0; 2883910Sdougm 2893910Sdougm for (tok = securitymodes; tok != NULL; tok = next) { 2904345Sdougm next = strchr(tok, ':'); 2914345Sdougm if (next != NULL) 2924345Sdougm *next++ = '\0'; 2934345Sdougm if (strcmp(tok, "default") == 0) { 2944345Sdougm /* resolve default into the real type */ 2954345Sdougm tok = nfs_space_alias(tok); 2964345Sdougm freetok = 1; 2974345Sdougm } 2984345Sdougm check = sa_get_security(group, tok, proto); 2993910Sdougm 3004345Sdougm /* add to the security list if it isn't there already */ 3014345Sdougm if (check == NULL || !find_security(headp, check)) { 3024345Sdougm curp = (struct securities *)calloc(1, 3034345Sdougm sizeof (struct securities)); 3044345Sdougm if (curp != NULL) { 3054345Sdougm if (check == NULL) { 3064345Sdougm curp->security = sa_create_security( 3074345Sdougm group, tok, proto); 3084345Sdougm } else { 3094345Sdougm curp->security = check; 3104345Sdougm } 3114345Sdougm /* 3124345Sdougm * note that the first time through the loop, 3134345Sdougm * headp will be NULL and prev will be 3144345Sdougm * undefined. Since headp is NULL, we set 3154345Sdougm * both it and prev to the curp (first 3164345Sdougm * structure to be allocated). 3174345Sdougm * 3184345Sdougm * later passes through the loop will have 3194345Sdougm * headp not being NULL and prev will be used 3204345Sdougm * to allocate at the end of the list. 3214345Sdougm */ 3224345Sdougm if (headp == NULL) { 3234345Sdougm headp = curp; 3244345Sdougm prev = curp; 3254345Sdougm } else { 3264345Sdougm prev->next = curp; 3274345Sdougm prev = curp; 3284345Sdougm } 3294345Sdougm } 3303910Sdougm } 3313910Sdougm 3324345Sdougm if (freetok) { 3334345Sdougm freetok = 0; 3344345Sdougm sa_free_attr_string(tok); 3354345Sdougm } 3363910Sdougm } 3373910Sdougm return (headp); 3383910Sdougm } 3393910Sdougm 3403910Sdougm static void 3413910Sdougm free_security_list(struct securities *sec) 3423910Sdougm { 3433910Sdougm struct securities *next; 3443910Sdougm if (sec != NULL) { 3454345Sdougm for (next = sec->next; sec != NULL; sec = next) { 3464345Sdougm next = sec->next; 3474345Sdougm free(sec); 3484345Sdougm } 3493910Sdougm } 3503910Sdougm } 3513910Sdougm 3523910Sdougm /* 3533910Sdougm * nfs_alistcat(str1, str2, sep) 3543910Sdougm * 3553910Sdougm * concatenate str1 and str2 into a new string using sep as a separate 3563910Sdougm * character. If memory allocation fails, return NULL; 3573910Sdougm */ 3583910Sdougm 3593910Sdougm static char * 3603910Sdougm nfs_alistcat(char *str1, char *str2, char sep) 3613910Sdougm { 3623910Sdougm char *newstr; 3633910Sdougm size_t len; 3643910Sdougm 3653910Sdougm len = strlen(str1) + strlen(str2) + 2; 3663910Sdougm newstr = (char *)malloc(len); 3673910Sdougm if (newstr != NULL) 3684345Sdougm (void) snprintf(newstr, len, "%s%c%s", str1, sep, str2); 3693910Sdougm return (newstr); 3703910Sdougm } 3713910Sdougm 3723910Sdougm /* 3733910Sdougm * add_security_prop(sec, name, value, persist) 3743910Sdougm * 3753910Sdougm * Add the property to the securities structure. This accumulates 3763910Sdougm * properties for as part of parsing legacy options. 3773910Sdougm */ 3783910Sdougm 3793910Sdougm static int 3803910Sdougm add_security_prop(struct securities *sec, char *name, char *value, 3813910Sdougm int persist, int iszfs) 3823910Sdougm { 3833910Sdougm sa_property_t prop; 3843910Sdougm int ret = SA_OK; 3853910Sdougm 3863910Sdougm for (; sec != NULL; sec = sec->next) { 3874345Sdougm if (value == NULL) { 3884345Sdougm if (strcmp(name, SHOPT_RW) == 0 || 3894345Sdougm strcmp(name, SHOPT_RO) == 0) 3904345Sdougm value = "*"; 3914345Sdougm else 3924345Sdougm value = "true"; 3934345Sdougm } 3943910Sdougm 3953910Sdougm /* 3963910Sdougm * Get the existing property, if it exists, so we can 3973910Sdougm * determine what to do with it. The ro/rw/root 3983910Sdougm * properties can be merged if multiple instances of 3993910Sdougm * these properies are given. For example, if "rw" 4003910Sdougm * exists with a value "host1" and a later token of 4013910Sdougm * rw="host2" is seen, the values are merged into a 4023910Sdougm * single rw="host1:host2". 4033910Sdougm */ 4044345Sdougm prop = sa_get_property(sec->security, name); 4053910Sdougm 4064345Sdougm if (prop != NULL) { 4074345Sdougm char *oldvalue; 4084345Sdougm char *newvalue; 4093910Sdougm 4103910Sdougm /* 4114345Sdougm * The security options of ro/rw/root might appear 4124345Sdougm * multiple times. If they do, the values need to be 4134345Sdougm * merged into an access list. If it was previously 4144345Sdougm * empty, the new value alone is added. 4153910Sdougm */ 4164345Sdougm oldvalue = sa_get_property_attr(prop, "value"); 4174345Sdougm if (oldvalue != NULL) { 4184345Sdougm /* 4194345Sdougm * The general case is to concatenate the new 4204345Sdougm * value onto the old value for multiple 4214345Sdougm * rw(ro/root) properties. A special case 4224345Sdougm * exists when either the old or new is the 4234345Sdougm * "all" case. In the special case, if both 4244345Sdougm * are "all", then it is "all", else if one is 4254345Sdougm * an access-list, that replaces the "all". 4264345Sdougm */ 4274345Sdougm if (strcmp(oldvalue, "*") == 0) { 4284345Sdougm /* Replace old value with new value. */ 4294345Sdougm newvalue = strdup(value); 4304345Sdougm } else if (strcmp(value, "*") == 0) { 4314345Sdougm /* 4324345Sdougm * Keep old value and ignore 4334345Sdougm * the new value. 4344345Sdougm */ 4354345Sdougm newvalue = NULL; 4364345Sdougm } else { 4374345Sdougm /* 4384345Sdougm * Make a new list of old plus new 4394345Sdougm * access-list. 4404345Sdougm */ 4414345Sdougm newvalue = nfs_alistcat(oldvalue, 4424345Sdougm value, ':'); 4434345Sdougm } 4443910Sdougm 4454345Sdougm if (newvalue != NULL) { 4464345Sdougm (void) sa_remove_property(prop); 4474345Sdougm prop = sa_create_property(name, 4484345Sdougm newvalue); 4494345Sdougm ret = sa_add_property(sec->security, 4504345Sdougm prop); 4514345Sdougm free(newvalue); 4524345Sdougm } 4534345Sdougm if (oldvalue != NULL) 4544345Sdougm sa_free_attr_string(oldvalue); 4554345Sdougm } 4564345Sdougm } else { 4574345Sdougm prop = sa_create_property(name, value); 4583910Sdougm ret = sa_add_property(sec->security, prop); 4593910Sdougm } 4604345Sdougm if (ret == SA_OK && !iszfs) { 4614345Sdougm ret = sa_commit_properties(sec->security, !persist); 4624345Sdougm } 4633910Sdougm } 4643910Sdougm return (ret); 4653910Sdougm } 4663910Sdougm 4673910Sdougm /* 4683910Sdougm * check to see if group/share is persistent. 4693910Sdougm */ 4703910Sdougm static int 4713910Sdougm is_persistent(sa_group_t group) 4723910Sdougm { 4733910Sdougm char *type; 4743910Sdougm int persist = 1; 4753910Sdougm 4763910Sdougm type = sa_get_group_attr(group, "type"); 4773910Sdougm if (type != NULL && strcmp(type, "persist") != 0) 4784345Sdougm persist = 0; 4793910Sdougm if (type != NULL) 4804345Sdougm sa_free_attr_string(type); 4813910Sdougm return (persist); 4823910Sdougm } 4833910Sdougm 4843910Sdougm /* 4853910Sdougm * invalid_security(options) 4863910Sdougm * 4873910Sdougm * search option string for any invalid sec= type. 4883910Sdougm * return true (1) if any are not valid else false (0) 4893910Sdougm */ 4903910Sdougm static int 4913910Sdougm invalid_security(char *options) 4923910Sdougm { 4933910Sdougm char *copy, *base, *token, *value; 4943910Sdougm int ret = 0; 4953910Sdougm 4963910Sdougm copy = strdup(options); 4973910Sdougm token = base = copy; 4983910Sdougm while (token != NULL && ret == 0) { 4994345Sdougm token = strtok(base, ","); 5004345Sdougm base = NULL; 5014345Sdougm if (token != NULL) { 5024345Sdougm value = strchr(token, '='); 5034345Sdougm if (value != NULL) 5044345Sdougm *value++ = '\0'; 5054345Sdougm if (strcmp(token, "sec") == 0) { 5064345Sdougm /* HAVE security flavors so check them */ 5074345Sdougm char *tok, *next; 5084345Sdougm for (next = NULL, tok = value; tok != NULL; 5094345Sdougm tok = next) { 5104345Sdougm next = strchr(tok, ':'); 5114345Sdougm if (next != NULL) 5124345Sdougm *next++ = '\0'; 5134345Sdougm ret = !nfs_validate_security_mode(tok); 5144345Sdougm if (ret) 5154345Sdougm break; 5164345Sdougm } 5174345Sdougm } 5183910Sdougm } 5193910Sdougm } 5203910Sdougm if (copy != NULL) 5214345Sdougm free(copy); 5223910Sdougm return (ret); 5233910Sdougm } 5243910Sdougm 5253910Sdougm /* 5263910Sdougm * nfs_parse_legacy_options(group, options) 5273910Sdougm * 5283910Sdougm * Parse the old style options into internal format and store on the 5293910Sdougm * specified group. Group could be a share for full legacy support. 5303910Sdougm */ 5313910Sdougm 5323910Sdougm static int 5333910Sdougm nfs_parse_legacy_options(sa_group_t group, char *options) 5343910Sdougm { 5354704Sdougm char *dup; 5363910Sdougm char *base; 5373910Sdougm char *token; 5383910Sdougm sa_optionset_t optionset; 5393910Sdougm struct securities *security_list = NULL; 5403910Sdougm sa_property_t prop; 5413910Sdougm int ret = SA_OK; 5423910Sdougm int iszfs = 0; 5433910Sdougm sa_group_t parent; 5443910Sdougm int persist = 0; 5453910Sdougm char *lasts; 5463910Sdougm 5473910Sdougm /* do we have an existing optionset? */ 5483910Sdougm optionset = sa_get_optionset(group, "nfs"); 5493910Sdougm if (optionset == NULL) { 5504345Sdougm /* didn't find existing optionset so create one */ 5514345Sdougm optionset = sa_create_optionset(group, "nfs"); 5523910Sdougm } else { 5533910Sdougm /* 554*5331Samw * Have an existing optionset . Ideally, we would need 555*5331Samw * to compare options in order to detect errors. For 556*5331Samw * now, we assume that the first optionset is the 557*5331Samw * correct one and the others will be the same. An 558*5331Samw * empty optionset is the same as no optionset so we 559*5331Samw * don't want to exit in that case. Getting an empty 560*5331Samw * optionset can occur with ZFS property checking. 5613910Sdougm */ 562*5331Samw if (sa_get_property(optionset, NULL) != NULL) 563*5331Samw return (ret); 5643910Sdougm } 5653910Sdougm 5663910Sdougm if (strcmp(options, SHOPT_RW) == 0) { 5673910Sdougm /* 5683910Sdougm * there is a special case of only the option "rw" 5693910Sdougm * being the default option. We don't have to do 5703910Sdougm * anything. 5713910Sdougm */ 5724345Sdougm return (ret); 5733910Sdougm } 5743910Sdougm 5753910Sdougm /* 5763910Sdougm * check if security types are present and validate them. If 5773910Sdougm * any are not legal, fail. 5783910Sdougm */ 5793910Sdougm 5803910Sdougm if (invalid_security(options)) { 5814345Sdougm return (SA_INVALID_SECURITY); 5823910Sdougm } 5833910Sdougm 5843910Sdougm /* 5853910Sdougm * in order to not attempt to change ZFS properties unless 5863910Sdougm * absolutely necessary, we never do it in the legacy parsing. 5873910Sdougm */ 5883910Sdougm if (sa_is_share(group)) { 5894345Sdougm char *zfs; 5904345Sdougm parent = sa_get_parent_group(group); 5914345Sdougm if (parent != NULL) { 5924345Sdougm zfs = sa_get_group_attr(parent, "zfs"); 5934345Sdougm if (zfs != NULL) { 5944345Sdougm sa_free_attr_string(zfs); 5954345Sdougm iszfs++; 5964345Sdougm } 5973910Sdougm } 5983910Sdougm } else { 5994345Sdougm iszfs = sa_group_is_zfs(group); 6003910Sdougm } 6013910Sdougm 6024704Sdougm /* We need a copy of options for the next part. */ 6034704Sdougm dup = strdup(options); 6044704Sdougm if (dup == NULL) 6054704Sdougm return (SA_NO_MEMORY); 6064704Sdougm 6073910Sdougm /* 6083910Sdougm * we need to step through each option in the string and then 6093910Sdougm * add either the option or the security option as needed. If 6103910Sdougm * this is not a persistent share, don't commit to the 6113910Sdougm * repository. If there is an error, we also want to abort the 6123910Sdougm * processing and report it. 6133910Sdougm */ 6143910Sdougm persist = is_persistent(group); 6153910Sdougm base = dup; 6163910Sdougm token = dup; 6173910Sdougm lasts = NULL; 6183910Sdougm while (token != NULL && ret == SA_OK) { 6194345Sdougm ret = SA_OK; 6204345Sdougm token = strtok_r(base, ",", &lasts); 6214345Sdougm base = NULL; 6224345Sdougm if (token != NULL) { 6234345Sdougm char *value; 6243910Sdougm /* 6254345Sdougm * if the option has a value, it will have an '=' to 6264345Sdougm * separate the name from the value. The following 6274345Sdougm * code will result in value != NULL and token 6284345Sdougm * pointing to just the name if there is a value. 6293910Sdougm */ 6304345Sdougm value = strchr(token, '='); 6314345Sdougm if (value != NULL) { 6324345Sdougm *value++ = '\0'; 6334345Sdougm } 6344345Sdougm if (strcmp(token, "sec") == 0 || 6354345Sdougm strcmp(token, "secure") == 0) { 6363910Sdougm /* 6374345Sdougm * Once in security parsing, we only 6384345Sdougm * do security. We do need to move 6394345Sdougm * between the security node and the 6404345Sdougm * toplevel. The security tag goes on 6414345Sdougm * the root while the following ones 6424345Sdougm * go on the security. 6433910Sdougm */ 6444345Sdougm if (security_list != NULL) { 6454345Sdougm /* 6464345Sdougm * have an old list so close it and 6474345Sdougm * start the new 6484345Sdougm */ 6494345Sdougm free_security_list(security_list); 6504345Sdougm } 6514345Sdougm if (strcmp(token, "secure") == 0) { 6524345Sdougm value = "dh"; 6534345Sdougm } else { 6544345Sdougm if (value == NULL) { 6554345Sdougm ret = SA_SYNTAX_ERR; 6564345Sdougm break; 6574345Sdougm } 6584345Sdougm } 6594345Sdougm security_list = make_security_list(group, 6604345Sdougm value, "nfs"); 6613910Sdougm } else { 6624345Sdougm /* 6634345Sdougm * Note that the "old" syntax allowed a 6644345Sdougm * default security model This must be 6654345Sdougm * accounted for and internally converted to 6664345Sdougm * "standard" security structure. 6674345Sdougm */ 6684345Sdougm if (nfs_is_security_opt(token)) { 6694345Sdougm if (security_list == NULL) { 6704345Sdougm /* 6714345Sdougm * need to have a 6724345Sdougm * security 6734345Sdougm * option. This will 6744345Sdougm * be "closed" when a 6754345Sdougm * defined "sec=" 6764345Sdougm * option is 6774345Sdougm * seen. This is 6784345Sdougm * technically an 6794345Sdougm * error but will be 6804345Sdougm * allowed with 6814345Sdougm * warning. 6824345Sdougm */ 6834345Sdougm security_list = 6844345Sdougm make_security_list(group, 6854345Sdougm "default", 6864345Sdougm "nfs"); 6874345Sdougm } 6884345Sdougm if (security_list != NULL) { 6894345Sdougm ret = add_security_prop( 6904345Sdougm security_list, token, 6914345Sdougm value, persist, iszfs); 6924345Sdougm } else { 6934345Sdougm ret = SA_NO_MEMORY; 6944345Sdougm } 6954345Sdougm } else { 6964345Sdougm /* regular options */ 6974345Sdougm if (value == NULL) { 6984345Sdougm if (strcmp(token, SHOPT_RW) == 6994345Sdougm 0 || strcmp(token, 7004345Sdougm SHOPT_RO) == 0) { 7014345Sdougm value = "*"; 7024345Sdougm } else { 7034345Sdougm value = "global"; 7044345Sdougm if (strcmp(token, 7054345Sdougm SHOPT_LOG) != 0) { 7064345Sdougm value = "true"; 7074345Sdougm } 7084345Sdougm } 7094345Sdougm } 7104372Sdougm /* 7114372Sdougm * In all cases, create the 7124372Sdougm * property specified. If the 7134372Sdougm * value was NULL, the default 7144372Sdougm * value will have been 7154372Sdougm * substituted. 7164372Sdougm */ 7174372Sdougm prop = sa_create_property(token, value); 7184372Sdougm ret = sa_add_property(optionset, prop); 7194372Sdougm if (ret != SA_OK) 7204372Sdougm break; 7214372Sdougm 7224345Sdougm if (!iszfs) { 7234345Sdougm ret = sa_commit_properties( 7244345Sdougm optionset, !persist); 7254345Sdougm } 7264345Sdougm } 7273910Sdougm } 7283910Sdougm } 7293910Sdougm } 7303910Sdougm if (security_list != NULL) 7314345Sdougm free_security_list(security_list); 7324704Sdougm 7334704Sdougm free(dup); 7343910Sdougm return (ret); 7353910Sdougm } 7363910Sdougm 7373910Sdougm /* 7383910Sdougm * is_a_number(number) 7393910Sdougm * 7403910Sdougm * is the string a number in one of the forms we want to use? 7413910Sdougm */ 7423910Sdougm 7433910Sdougm static int 7443910Sdougm is_a_number(char *number) 7453910Sdougm { 7463910Sdougm int ret = 1; 7473910Sdougm int hex = 0; 7483910Sdougm 7493910Sdougm if (strncmp(number, "0x", 2) == 0) { 7504345Sdougm number += 2; 7514345Sdougm hex = 1; 7524345Sdougm } else if (*number == '-') { 7534345Sdougm number++; /* skip the minus */ 7544345Sdougm } 7553910Sdougm while (ret == 1 && *number != '\0') { 7564345Sdougm if (hex) { 7574345Sdougm ret = isxdigit(*number++); 7584345Sdougm } else { 7594345Sdougm ret = isdigit(*number++); 7604345Sdougm } 7613910Sdougm } 7623910Sdougm return (ret); 7633910Sdougm } 7643910Sdougm 7653910Sdougm /* 7663910Sdougm * Look for the specified tag in the configuration file. If it is found, 7673910Sdougm * enable logging and set the logging configuration information for exp. 7683910Sdougm */ 7693910Sdougm static void 7703910Sdougm configlog(struct exportdata *exp, char *tag) 7713910Sdougm { 7723910Sdougm nfsl_config_t *configlist = NULL, *configp; 7733910Sdougm int error = 0; 7743910Sdougm char globaltag[] = DEFAULTTAG; 7753910Sdougm 7763910Sdougm /* 7773910Sdougm * Sends config errors to stderr 7783910Sdougm */ 7793910Sdougm nfsl_errs_to_syslog = B_FALSE; 7803910Sdougm 7813910Sdougm /* 7823910Sdougm * get the list of configuration settings 7833910Sdougm */ 7843910Sdougm error = nfsl_getconfig_list(&configlist); 7853910Sdougm if (error) { 7863910Sdougm (void) fprintf(stderr, 7874345Sdougm dgettext(TEXT_DOMAIN, "Cannot get log configuration: %s\n"), 7884345Sdougm strerror(error)); 7893910Sdougm } 7903910Sdougm 7913910Sdougm if (tag == NULL) 7923910Sdougm tag = globaltag; 7933910Sdougm if ((configp = nfsl_findconfig(configlist, tag, &error)) == NULL) { 7943910Sdougm nfsl_freeconfig_list(&configlist); 7953910Sdougm (void) fprintf(stderr, 7964345Sdougm dgettext(TEXT_DOMAIN, "No tags matching \"%s\"\n"), tag); 7973910Sdougm /* bad configuration */ 7983910Sdougm error = ENOENT; 7993910Sdougm goto err; 8003910Sdougm } 8013910Sdougm 8023910Sdougm if ((exp->ex_tag = strdup(tag)) == NULL) { 8033910Sdougm error = ENOMEM; 8043910Sdougm goto out; 8053910Sdougm } 8063910Sdougm if ((exp->ex_log_buffer = strdup(configp->nc_bufferpath)) == NULL) { 8073910Sdougm error = ENOMEM; 8083910Sdougm goto out; 8093910Sdougm } 8103910Sdougm exp->ex_flags |= EX_LOG; 8113910Sdougm if (configp->nc_rpclogpath != NULL) 8123910Sdougm exp->ex_flags |= EX_LOG_ALLOPS; 8133910Sdougm out: 8143910Sdougm if (configlist != NULL) 8154345Sdougm nfsl_freeconfig_list(&configlist); 8163910Sdougm 8173910Sdougm err: 8183910Sdougm if (error != 0) { 8193910Sdougm if (exp->ex_flags != NULL) 8203910Sdougm free(exp->ex_tag); 8213910Sdougm if (exp->ex_log_buffer != NULL) 8223910Sdougm free(exp->ex_log_buffer); 8233910Sdougm (void) fprintf(stderr, 8244345Sdougm dgettext(TEXT_DOMAIN, "Cannot set log configuration: %s\n"), 8254345Sdougm strerror(error)); 8263910Sdougm } 8273910Sdougm } 8283910Sdougm 8293910Sdougm /* 8303910Sdougm * fill_export_from_optionset(export, optionset) 8313910Sdougm * 8323910Sdougm * In order to share, we need to set all the possible general options 8333910Sdougm * into the export structure. Share info will be filled in by the 8343910Sdougm * caller. Various property values get turned into structure specific 8353910Sdougm * values. 8363910Sdougm */ 8373910Sdougm 8383910Sdougm static int 8393910Sdougm fill_export_from_optionset(struct exportdata *export, sa_optionset_t optionset) 8403910Sdougm { 8413910Sdougm sa_property_t option; 8423910Sdougm int ret = SA_OK; 8433910Sdougm 8443910Sdougm for (option = sa_get_property(optionset, NULL); 8454345Sdougm option != NULL; option = sa_get_next_property(option)) { 8464345Sdougm char *name; 8474345Sdougm char *value; 8484345Sdougm uint32_t val; 8493910Sdougm 8504345Sdougm /* 8514345Sdougm * since options may be set/reset multiple times, always do an 8524345Sdougm * explicit set or clear of the option. This allows defaults 853*5331Samw * to be set and then the protocol specific to override. 8544345Sdougm */ 8553910Sdougm 8564345Sdougm name = sa_get_property_attr(option, "type"); 8574345Sdougm value = sa_get_property_attr(option, "value"); 8584345Sdougm switch (findopt(name)) { 8594345Sdougm case OPT_ANON: 8604345Sdougm if (value != NULL && is_a_number(value)) { 8614345Sdougm val = strtoul(value, NULL, 0); 8624345Sdougm } else { 8634345Sdougm struct passwd *pw; 8644345Sdougm pw = getpwnam(value != NULL ? value : "nobody"); 8654345Sdougm if (pw != NULL) { 8664345Sdougm val = pw->pw_uid; 8674345Sdougm } else { 8684345Sdougm val = UID_NOBODY; 8694345Sdougm } 8704345Sdougm endpwent(); 8714345Sdougm } 8724345Sdougm export->ex_anon = val; 8734345Sdougm break; 8744345Sdougm case OPT_NOSUID: 8754345Sdougm if (value != NULL && (strcasecmp(value, "true") == 0 || 8764345Sdougm strcmp(value, "1") == 0)) 8774345Sdougm export->ex_flags |= EX_NOSUID; 8784345Sdougm else 8794345Sdougm export->ex_flags &= ~EX_NOSUID; 8804345Sdougm break; 8814345Sdougm case OPT_ACLOK: 8824345Sdougm if (value != NULL && (strcasecmp(value, "true") == 0 || 8834345Sdougm strcmp(value, "1") == 0)) 8844345Sdougm export->ex_flags |= EX_ACLOK; 8854345Sdougm else 8864345Sdougm export->ex_flags &= ~EX_ACLOK; 8874345Sdougm break; 8884345Sdougm case OPT_NOSUB: 8894345Sdougm if (value != NULL && (strcasecmp(value, "true") == 0 || 8904345Sdougm strcmp(value, "1") == 0)) 8914345Sdougm export->ex_flags |= EX_NOSUB; 8924345Sdougm else 8934345Sdougm export->ex_flags &= ~EX_NOSUB; 8944345Sdougm break; 8954345Sdougm case OPT_PUBLIC: 8964345Sdougm if (value != NULL && (strcasecmp(value, "true") == 0 || 8974345Sdougm strcmp(value, "1") == 0)) 8984345Sdougm export->ex_flags |= EX_PUBLIC; 8994345Sdougm else 9004345Sdougm export->ex_flags &= ~EX_PUBLIC; 9014345Sdougm break; 9024345Sdougm case OPT_INDEX: 9034345Sdougm if (value != NULL && (strcmp(value, "..") == 0 || 9044345Sdougm strchr(value, '/') != NULL)) { 9054345Sdougm /* this is an error */ 9064345Sdougm (void) printf(dgettext(TEXT_DOMAIN, 9074345Sdougm "NFS: index=\"%s\" not valid;" 9084345Sdougm "must be a filename.\n"), 9094345Sdougm value); 9104345Sdougm break; 9114345Sdougm } 9124345Sdougm if (value != NULL && *value != '\0' && 9134345Sdougm strcmp(value, ".") != 0) { 9144345Sdougm /* valid index file string */ 9154345Sdougm if (export->ex_index != NULL) { 9164345Sdougm /* left over from "default" */ 9174345Sdougm free(export->ex_index); 9184345Sdougm } 9194345Sdougm /* remember to free */ 9204345Sdougm export->ex_index = strdup(value); 9214345Sdougm if (export->ex_index == NULL) { 9224345Sdougm (void) printf(dgettext(TEXT_DOMAIN, 9234345Sdougm "NFS: out of memory setting " 9244345Sdougm "index property\n")); 9254345Sdougm break; 9264345Sdougm } 9274345Sdougm export->ex_flags |= EX_INDEX; 9284345Sdougm } 9294345Sdougm break; 9304345Sdougm case OPT_LOG: 9314345Sdougm if (value == NULL) 9324345Sdougm value = strdup("global"); 9334345Sdougm if (value != NULL) 9344345Sdougm configlog(export, 9354345Sdougm strlen(value) ? value : "global"); 9364345Sdougm break; 9374345Sdougm default: 9384345Sdougm /* have a syntactic error */ 9394345Sdougm (void) printf(dgettext(TEXT_DOMAIN, 9404345Sdougm "NFS: unrecognized option %s=%s\n"), 9414345Sdougm name, value != NULL ? value : ""); 9424345Sdougm break; 9433910Sdougm } 9444345Sdougm if (name != NULL) 9454345Sdougm sa_free_attr_string(name); 9463910Sdougm if (value != NULL) 9474345Sdougm sa_free_attr_string(value); 9483910Sdougm } 9493910Sdougm return (ret); 9503910Sdougm } 9513910Sdougm 9523910Sdougm /* 9533910Sdougm * cleanup_export(export) 9543910Sdougm * 9553910Sdougm * Cleanup the allocated areas so we don't leak memory 9563910Sdougm */ 9573910Sdougm 9583910Sdougm static void 9593910Sdougm cleanup_export(struct exportdata *export) 9603910Sdougm { 9613910Sdougm int i; 9623910Sdougm 9633910Sdougm if (export->ex_index != NULL) 9644345Sdougm free(export->ex_index); 9653910Sdougm if (export->ex_secinfo != NULL) { 9664345Sdougm for (i = 0; i < export->ex_seccnt; i++) 9674345Sdougm if (export->ex_secinfo[i].s_rootnames != NULL) 9684345Sdougm free(export->ex_secinfo[i].s_rootnames); 9694345Sdougm free(export->ex_secinfo); 9703910Sdougm } 9713910Sdougm } 9723910Sdougm 9733910Sdougm /* 9743910Sdougm * Given a seconfig entry and a colon-separated 9753910Sdougm * list of names, allocate an array big enough 9763910Sdougm * to hold the root list, then convert each name to 9773910Sdougm * a principal name according to the security 9783910Sdougm * info and assign it to an array element. 9793910Sdougm * Return the array and its size. 9803910Sdougm */ 9813910Sdougm static caddr_t * 9823910Sdougm get_rootnames(seconfig_t *sec, char *list, int *count) 9833910Sdougm { 9843910Sdougm caddr_t *a; 9853910Sdougm int c, i; 9863910Sdougm char *host, *p; 9873910Sdougm 9883910Sdougm /* 9893910Sdougm * Count the number of strings in the list. 9903910Sdougm * This is the number of colon separators + 1. 9913910Sdougm */ 9923910Sdougm c = 1; 9933910Sdougm for (p = list; *p; p++) 9943910Sdougm if (*p == ':') 9953910Sdougm c++; 9963910Sdougm *count = c; 9973910Sdougm 9983910Sdougm a = (caddr_t *)malloc(c * sizeof (char *)); 9993910Sdougm if (a == NULL) { 10003910Sdougm (void) printf(dgettext(TEXT_DOMAIN, 10014345Sdougm "get_rootnames: no memory\n")); 10023910Sdougm } else { 10034345Sdougm for (i = 0; i < c; i++) { 10044345Sdougm host = strtok(list, ":"); 10054345Sdougm if (!nfs_get_root_principal(sec, host, &a[i])) { 10064345Sdougm free(a); 10074345Sdougm a = NULL; 10084345Sdougm break; 10094345Sdougm } 10104345Sdougm list = NULL; 10113910Sdougm } 10123910Sdougm } 10133910Sdougm 10143910Sdougm return (a); 10153910Sdougm } 10163910Sdougm 10173910Sdougm /* 10183910Sdougm * fill_security_from_secopts(sp, secopts) 10193910Sdougm * 10203910Sdougm * Fill the secinfo structure from the secopts optionset. 10213910Sdougm */ 10223910Sdougm 10233910Sdougm static int 10243910Sdougm fill_security_from_secopts(struct secinfo *sp, sa_security_t secopts) 10253910Sdougm { 10263910Sdougm sa_property_t prop; 10273910Sdougm char *type; 10283910Sdougm int longform; 10293910Sdougm int err = SC_NOERROR; 10303910Sdougm 10313910Sdougm type = sa_get_security_attr(secopts, "sectype"); 10323910Sdougm if (type != NULL) { 10334345Sdougm /* named security type needs secinfo to be filled in */ 10344345Sdougm err = nfs_getseconfig_byname(type, &sp->s_secinfo); 10354345Sdougm sa_free_attr_string(type); 10364345Sdougm if (err != SC_NOERROR) 10374345Sdougm return (err); 10383910Sdougm } else { 10394345Sdougm /* default case */ 10404345Sdougm err = nfs_getseconfig_default(&sp->s_secinfo); 10414345Sdougm if (err != SC_NOERROR) 10424345Sdougm return (err); 10433910Sdougm } 10443910Sdougm 10453910Sdougm err = SA_OK; 10463910Sdougm for (prop = sa_get_property(secopts, NULL); 10474345Sdougm prop != NULL && err == SA_OK; 10484345Sdougm prop = sa_get_next_property(prop)) { 10494345Sdougm char *name; 10504345Sdougm char *value; 10513910Sdougm 10524345Sdougm name = sa_get_property_attr(prop, "type"); 10534345Sdougm value = sa_get_property_attr(prop, "value"); 10543910Sdougm 10554345Sdougm longform = value != NULL && strcmp(value, "*") != 0; 10563910Sdougm 10574345Sdougm switch (findopt(name)) { 10584345Sdougm case OPT_RO: 10594345Sdougm sp->s_flags |= longform ? M_ROL : M_RO; 10604345Sdougm break; 10614345Sdougm case OPT_RW: 10624345Sdougm sp->s_flags |= longform ? M_RWL : M_RW; 10634345Sdougm break; 10644345Sdougm case OPT_ROOT: 10654345Sdougm sp->s_flags |= M_ROOT; 10664345Sdougm /* 10674345Sdougm * if we are using AUTH_UNIX, handle like other things 10684345Sdougm * such as RO/RW 10694345Sdougm */ 10704345Sdougm if (sp->s_secinfo.sc_rpcnum == AUTH_UNIX) 10714345Sdougm continue; 10724345Sdougm /* not AUTH_UNIX */ 10734345Sdougm if (value != NULL) { 10744345Sdougm sp->s_rootnames = get_rootnames(&sp->s_secinfo, 10754345Sdougm value, &sp->s_rootcnt); 10764345Sdougm if (sp->s_rootnames == NULL) { 10774345Sdougm err = SA_BAD_VALUE; 10784345Sdougm (void) fprintf(stderr, 10794345Sdougm dgettext(TEXT_DOMAIN, 10804345Sdougm "Bad root list\n")); 10814345Sdougm } 10824345Sdougm } 10834345Sdougm break; 10844345Sdougm case OPT_WINDOW: 10854345Sdougm if (value != NULL) { 10864345Sdougm sp->s_window = atoi(value); 10874345Sdougm /* just in case */ 10884345Sdougm if (sp->s_window < 0) 10894345Sdougm sp->s_window = DEF_WIN; 10904345Sdougm } 10914345Sdougm break; 10924345Sdougm default: 10934345Sdougm break; 10943910Sdougm } 10954345Sdougm if (name != NULL) 10964345Sdougm sa_free_attr_string(name); 10974345Sdougm if (value != NULL) 10984345Sdougm sa_free_attr_string(value); 10993910Sdougm } 11003910Sdougm /* if rw/ro options not set, use default of RW */ 11013910Sdougm if ((sp->s_flags & NFS_RWMODES) == 0) 11024345Sdougm sp->s_flags |= M_RW; 11033910Sdougm return (err); 11043910Sdougm } 11053910Sdougm 11063910Sdougm /* 11073910Sdougm * This is for testing only 11083910Sdougm * It displays the export structure that 11093910Sdougm * goes into the kernel. 11103910Sdougm */ 11113910Sdougm static void 11123910Sdougm printarg(char *path, struct exportdata *ep) 11133910Sdougm { 11143910Sdougm int i, j; 11153910Sdougm struct secinfo *sp; 11163910Sdougm 11173910Sdougm if (debug == 0) 11184345Sdougm return; 11193910Sdougm 11203910Sdougm (void) printf("%s:\n", path); 11213910Sdougm (void) printf("\tex_version = %d\n", ep->ex_version); 11223910Sdougm (void) printf("\tex_path = %s\n", ep->ex_path); 11233910Sdougm (void) printf("\tex_pathlen = %ld\n", (ulong_t)ep->ex_pathlen); 11243910Sdougm (void) printf("\tex_flags: (0x%02x) ", ep->ex_flags); 11253910Sdougm if (ep->ex_flags & EX_NOSUID) 11263910Sdougm (void) printf("NOSUID "); 11273910Sdougm if (ep->ex_flags & EX_ACLOK) 11283910Sdougm (void) printf("ACLOK "); 11293910Sdougm if (ep->ex_flags & EX_PUBLIC) 11303910Sdougm (void) printf("PUBLIC "); 11313910Sdougm if (ep->ex_flags & EX_NOSUB) 11323910Sdougm (void) printf("NOSUB "); 11333910Sdougm if (ep->ex_flags & EX_LOG) 11343910Sdougm (void) printf("LOG "); 11353910Sdougm if (ep->ex_flags & EX_LOG_ALLOPS) 11363910Sdougm (void) printf("LOG_ALLOPS "); 11373910Sdougm if (ep->ex_flags == 0) 11383910Sdougm (void) printf("(none)"); 11393910Sdougm (void) printf("\n"); 11403910Sdougm if (ep->ex_flags & EX_LOG) { 11413910Sdougm (void) printf("\tex_log_buffer = %s\n", 11424345Sdougm (ep->ex_log_buffer ? ep->ex_log_buffer : "(NULL)")); 11433910Sdougm (void) printf("\tex_tag = %s\n", 11444345Sdougm (ep->ex_tag ? ep->ex_tag : "(NULL)")); 11453910Sdougm } 11463910Sdougm (void) printf("\tex_anon = %d\n", ep->ex_anon); 11473910Sdougm (void) printf("\tex_seccnt = %d\n", ep->ex_seccnt); 11483910Sdougm (void) printf("\n"); 11493910Sdougm for (i = 0; i < ep->ex_seccnt; i++) { 11503910Sdougm sp = &ep->ex_secinfo[i]; 11513910Sdougm (void) printf("\t\ts_secinfo = %s\n", sp->s_secinfo.sc_name); 11523910Sdougm (void) printf("\t\ts_flags: (0x%02x) ", sp->s_flags); 11533910Sdougm if (sp->s_flags & M_ROOT) (void) printf("M_ROOT "); 11543910Sdougm if (sp->s_flags & M_RO) (void) printf("M_RO "); 11553910Sdougm if (sp->s_flags & M_ROL) (void) printf("M_ROL "); 11563910Sdougm if (sp->s_flags & M_RW) (void) printf("M_RW "); 11573910Sdougm if (sp->s_flags & M_RWL) (void) printf("M_RWL "); 11583910Sdougm if (sp->s_flags == 0) (void) printf("(none)"); 11593910Sdougm (void) printf("\n"); 11603910Sdougm (void) printf("\t\ts_window = %d\n", sp->s_window); 11613910Sdougm (void) printf("\t\ts_rootcnt = %d ", sp->s_rootcnt); 11623910Sdougm (void) fflush(stdout); 11633910Sdougm for (j = 0; j < sp->s_rootcnt; j++) 11643910Sdougm (void) printf("%s ", sp->s_rootnames[j] ? 11654345Sdougm sp->s_rootnames[j] : "<null>"); 11663910Sdougm (void) printf("\n\n"); 11673910Sdougm } 11683910Sdougm } 11693910Sdougm 11703910Sdougm /* 11713910Sdougm * count_security(opts) 11723910Sdougm * 11733910Sdougm * Count the number of security types (flavors). The optionset has 11743910Sdougm * been populated with the security flavors as a holding mechanism. 11753910Sdougm * We later use this number to allocate data structures. 11763910Sdougm */ 11773910Sdougm 11783910Sdougm static int 11793910Sdougm count_security(sa_optionset_t opts) 11803910Sdougm { 11813910Sdougm int count = 0; 11823910Sdougm sa_property_t prop; 11833910Sdougm if (opts != NULL) { 11844345Sdougm for (prop = sa_get_property(opts, NULL); prop != NULL; 11854345Sdougm prop = sa_get_next_property(prop)) { 11864345Sdougm count++; 11874345Sdougm } 11883910Sdougm } 11893910Sdougm return (count); 11903910Sdougm } 11913910Sdougm 11923910Sdougm /* 11933910Sdougm * nfs_sprint_option(rbuff, rbuffsize, incr, prop, sep) 11943910Sdougm * 11953910Sdougm * provides a mechanism to format NFS properties into legacy output 11963910Sdougm * format. If the buffer would overflow, it is reallocated and grown 11973910Sdougm * as appropriate. Special cases of converting internal form of values 11983910Sdougm * to those used by "share" are done. this function does one property 11993910Sdougm * at a time. 12003910Sdougm */ 12013910Sdougm 12025179Sdougm static int 12033910Sdougm nfs_sprint_option(char **rbuff, size_t *rbuffsize, size_t incr, 12043910Sdougm sa_property_t prop, int sep) 12053910Sdougm { 12063910Sdougm char *name; 12073910Sdougm char *value; 12083910Sdougm int curlen; 12093910Sdougm char *buff = *rbuff; 12103910Sdougm size_t buffsize = *rbuffsize; 12115179Sdougm int printed = B_FALSE; 12123910Sdougm 12133910Sdougm name = sa_get_property_attr(prop, "type"); 12143910Sdougm value = sa_get_property_attr(prop, "value"); 12153910Sdougm if (buff != NULL) 12164345Sdougm curlen = strlen(buff); 12173910Sdougm else 12184345Sdougm curlen = 0; 12193910Sdougm if (name != NULL) { 12204345Sdougm int len; 12214345Sdougm len = strlen(name) + sep; 12223910Sdougm 12233910Sdougm /* 12243910Sdougm * A future RFE would be to replace this with more 12253910Sdougm * generic code and to possibly handle more types. 12263910Sdougm */ 12274345Sdougm switch (gettype(name)) { 12284345Sdougm case OPT_TYPE_BOOLEAN: 12295179Sdougm /* 12305179Sdougm * For NFS, boolean value of FALSE means it 12315179Sdougm * doesn't show up in the option list at all. 12325179Sdougm */ 12334345Sdougm if (value != NULL && strcasecmp(value, "false") == 0) 12345179Sdougm goto skip; 12355179Sdougm if (value != NULL) { 12364345Sdougm sa_free_attr_string(value); 12375179Sdougm value = NULL; 12385179Sdougm } 12394345Sdougm break; 12404345Sdougm case OPT_TYPE_ACCLIST: 12414345Sdougm if (value != NULL && strcmp(value, "*") == 0) { 12424345Sdougm sa_free_attr_string(value); 12434345Sdougm value = NULL; 12444345Sdougm } else { 12454345Sdougm if (value != NULL) 12464345Sdougm len += 1 + strlen(value); 12474345Sdougm } 12484345Sdougm break; 12494345Sdougm case OPT_TYPE_LOGTAG: 12504345Sdougm if (value != NULL && strlen(value) == 0) { 12514345Sdougm sa_free_attr_string(value); 12524345Sdougm value = NULL; 12534345Sdougm } else { 12544345Sdougm if (value != NULL) 12554345Sdougm len += 1 + strlen(value); 12564345Sdougm } 12574345Sdougm break; 12584345Sdougm default: 12594345Sdougm if (value != NULL) 12604345Sdougm len += 1 + strlen(value); 12614345Sdougm break; 12623910Sdougm } 12634345Sdougm while (buffsize <= (curlen + len)) { 12644345Sdougm /* need more room */ 12654345Sdougm buffsize += incr; 12664345Sdougm buff = realloc(buff, buffsize); 12674345Sdougm if (buff == NULL) { 12684345Sdougm /* realloc failed so free everything */ 12694345Sdougm if (*rbuff != NULL) 12704345Sdougm free(*rbuff); 12714345Sdougm } 12724345Sdougm *rbuff = buff; 12734345Sdougm *rbuffsize = buffsize; 12745179Sdougm if (buff == NULL) 12755179Sdougm goto skip; 12765179Sdougm 12773910Sdougm } 12785179Sdougm 12794345Sdougm if (buff == NULL) 12805179Sdougm goto skip; 12815179Sdougm 12824345Sdougm if (value == NULL) { 12834345Sdougm (void) snprintf(buff + curlen, buffsize - curlen, 12844345Sdougm "%s%s", sep ? "," : "", 12854345Sdougm name, value != NULL ? value : ""); 12864345Sdougm } else { 12874345Sdougm (void) snprintf(buff + curlen, buffsize - curlen, 12884345Sdougm "%s%s=%s", sep ? "," : "", 12894345Sdougm name, value != NULL ? value : ""); 12903910Sdougm } 12915179Sdougm printed = B_TRUE; 12923910Sdougm } 12935179Sdougm skip: 12943910Sdougm if (name != NULL) 12954345Sdougm sa_free_attr_string(name); 12963910Sdougm if (value != NULL) 12974345Sdougm sa_free_attr_string(value); 12985179Sdougm return (printed); 12993910Sdougm } 13003910Sdougm 13013910Sdougm /* 13023910Sdougm * nfs_format_options(group, hier) 13033910Sdougm * 13043910Sdougm * format all the options on the group into an old-style option 13053910Sdougm * string. If hier is non-zero, walk up the tree to get inherited 13063910Sdougm * options. 13073910Sdougm */ 13083910Sdougm 13093910Sdougm static char * 13103910Sdougm nfs_format_options(sa_group_t group, int hier) 13113910Sdougm { 13123910Sdougm sa_optionset_t options = NULL; 13134345Sdougm sa_optionset_t secoptions = NULL; 13143910Sdougm sa_property_t prop, secprop; 13154345Sdougm sa_security_t security = NULL; 13163910Sdougm char *buff; 13173910Sdougm size_t buffsize; 13184345Sdougm char *sectype = NULL; 13194345Sdougm int sep = 0; 13204345Sdougm 13214345Sdougm 13224345Sdougm buff = malloc(OPT_CHUNK); 13234345Sdougm if (buff == NULL) { 13244345Sdougm return (NULL); 13254345Sdougm } 13264345Sdougm 13274345Sdougm buff[0] = '\0'; 13284345Sdougm buffsize = OPT_CHUNK; 13294345Sdougm 13304345Sdougm /* 13314345Sdougm * We may have a an optionset relative to this item. format 13324345Sdougm * these if we find them and then add any security definitions. 13334345Sdougm */ 13343910Sdougm 13353910Sdougm options = sa_get_derived_optionset(group, "nfs", hier); 13363910Sdougm 13373910Sdougm /* 13384345Sdougm * do the default set first but skip any option that is also 13394345Sdougm * in the protocol specific optionset. 13403910Sdougm */ 13414345Sdougm if (options != NULL) { 13424345Sdougm for (prop = sa_get_property(options, NULL); 13434345Sdougm prop != NULL; prop = sa_get_next_property(prop)) { 13443910Sdougm /* 13454345Sdougm * use this one since we skipped any 13464345Sdougm * of these that were also in 13474345Sdougm * optdefault 13483910Sdougm */ 13495179Sdougm if (nfs_sprint_option(&buff, &buffsize, OPT_CHUNK, 13505179Sdougm prop, sep)) 13515179Sdougm sep = 1; 13524345Sdougm if (buff == NULL) { 13534345Sdougm /* 13544345Sdougm * buff could become NULL if there 13554345Sdougm * isn't enough memory for 13564345Sdougm * nfs_sprint_option to realloc() 13574345Sdougm * as necessary. We can't really 13584345Sdougm * do anything about it at this 13594345Sdougm * point so we return NULL. The 13604345Sdougm * caller should handle the 13614345Sdougm * failure. 13624345Sdougm */ 13634345Sdougm if (options != NULL) 13644345Sdougm sa_free_derived_optionset( 13654345Sdougm options); 13664345Sdougm return (buff); 13674345Sdougm } 13683910Sdougm } 13694345Sdougm } 13704345Sdougm secoptions = (sa_optionset_t)sa_get_all_security_types(group, 13714345Sdougm "nfs", hier); 13724345Sdougm if (secoptions != NULL) { 13733910Sdougm for (secprop = sa_get_property(secoptions, NULL); 13744345Sdougm secprop != NULL; 13754345Sdougm secprop = sa_get_next_property(secprop)) { 13764345Sdougm sectype = sa_get_property_attr(secprop, "type"); 13774345Sdougm security = 13784345Sdougm (sa_security_t)sa_get_derived_security( 13794345Sdougm group, sectype, "nfs", hier); 13804345Sdougm if (security != NULL) { 13814345Sdougm if (sectype != NULL) { 13824345Sdougm prop = sa_create_property( 13834345Sdougm "sec", sectype); 13845179Sdougm if (prop == NULL) 13855179Sdougm goto err; 13865179Sdougm if (nfs_sprint_option(&buff, 13875179Sdougm &buffsize, OPT_CHUNK, prop, sep)) 13885179Sdougm sep = 1; 13894345Sdougm (void) sa_remove_property(prop); 13905179Sdougm if (buff == NULL) 13915179Sdougm goto err; 13924345Sdougm } 13934345Sdougm for (prop = sa_get_property(security, 13944345Sdougm NULL); prop != NULL; 13954345Sdougm prop = sa_get_next_property(prop)) { 13965179Sdougm if (nfs_sprint_option(&buff, 13975179Sdougm &buffsize, OPT_CHUNK, prop, sep)) 13985179Sdougm sep = 1; 13994345Sdougm if (buff == NULL) 14004345Sdougm goto err; 14014345Sdougm } 14024345Sdougm sa_free_derived_optionset(security); 14033910Sdougm } 14044345Sdougm if (sectype != NULL) 14054345Sdougm sa_free_attr_string(sectype); 14063910Sdougm } 14073910Sdougm sa_free_derived_optionset(secoptions); 14083910Sdougm } 14094345Sdougm 14103910Sdougm if (options != NULL) 14114345Sdougm sa_free_derived_optionset(options); 14124345Sdougm return (buff); 14134345Sdougm 14144345Sdougm err: 14154345Sdougm /* 14164345Sdougm * If we couldn't allocate memory for option printing, we need 14174345Sdougm * to break out of the nested loops, cleanup and return NULL. 14184345Sdougm */ 14194345Sdougm if (secoptions != NULL) 14204345Sdougm sa_free_derived_optionset(secoptions); 14214345Sdougm if (security != NULL) 14224345Sdougm sa_free_derived_optionset(security); 14234345Sdougm if (sectype != NULL) 14244345Sdougm sa_free_attr_string(sectype); 14254345Sdougm if (options != NULL) 14264345Sdougm sa_free_derived_optionset(options); 14273910Sdougm return (buff); 14283910Sdougm } 14294345Sdougm 14303910Sdougm /* 14313910Sdougm * Append an entry to the nfslogtab file 14323910Sdougm */ 14333910Sdougm static int 14343910Sdougm nfslogtab_add(dir, buffer, tag) 14353910Sdougm char *dir, *buffer, *tag; 14363910Sdougm { 14373910Sdougm FILE *f; 14383910Sdougm struct logtab_ent lep; 14393910Sdougm int error = 0; 14403910Sdougm 14413910Sdougm /* 14423910Sdougm * Open the file for update and create it if necessary. 14433910Sdougm * This may leave the I/O offset at the end of the file, 14443910Sdougm * so rewind back to the beginning of the file. 14453910Sdougm */ 14463910Sdougm f = fopen(NFSLOGTAB, "a+"); 14473910Sdougm if (f == NULL) { 14483910Sdougm error = errno; 14493910Sdougm goto out; 14503910Sdougm } 14513910Sdougm rewind(f); 14523910Sdougm 14533910Sdougm if (lockf(fileno(f), F_LOCK, 0L) < 0) { 14543910Sdougm (void) fprintf(stderr, dgettext(TEXT_DOMAIN, 14554345Sdougm "share complete, however failed to lock %s " 14564345Sdougm "for update: %s\n"), NFSLOGTAB, strerror(errno)); 14573910Sdougm error = -1; 14583910Sdougm goto out; 14593910Sdougm } 14603910Sdougm 14613910Sdougm if (logtab_deactivate_after_boot(f) == -1) { 14623910Sdougm (void) fprintf(stderr, dgettext(TEXT_DOMAIN, 14634345Sdougm "share complete, however could not deactivate " 14644345Sdougm "entries in %s\n"), NFSLOGTAB); 14653910Sdougm error = -1; 14663910Sdougm goto out; 14673910Sdougm } 14683910Sdougm 14693910Sdougm /* 14703910Sdougm * Remove entries matching buffer and sharepoint since we're 14713910Sdougm * going to replace it with perhaps an entry with a new tag. 14723910Sdougm */ 14733910Sdougm if (logtab_rement(f, buffer, dir, NULL, -1)) { 14743910Sdougm (void) fprintf(stderr, dgettext(TEXT_DOMAIN, 14754345Sdougm "share complete, however could not remove matching " 14764345Sdougm "entries in %s\n"), NFSLOGTAB); 14773910Sdougm error = -1; 14783910Sdougm goto out; 14793910Sdougm } 14803910Sdougm 14813910Sdougm /* 14823910Sdougm * Deactivate all active entries matching this sharepoint 14833910Sdougm */ 14843910Sdougm if (logtab_deactivate(f, NULL, dir, NULL)) { 14853910Sdougm (void) fprintf(stderr, dgettext(TEXT_DOMAIN, 14864345Sdougm "share complete, however could not deactivate matching " 14874345Sdougm "entries in %s\n"), NFSLOGTAB); 14883910Sdougm error = -1; 14893910Sdougm goto out; 14903910Sdougm } 14913910Sdougm 14923910Sdougm lep.le_buffer = buffer; 14933910Sdougm lep.le_path = dir; 14943910Sdougm lep.le_tag = tag; 14953910Sdougm lep.le_state = LES_ACTIVE; 14963910Sdougm 14973910Sdougm /* 14983910Sdougm * Add new sharepoint / buffer location to nfslogtab 14993910Sdougm */ 15003910Sdougm if (logtab_putent(f, &lep) < 0) { 15013910Sdougm (void) fprintf(stderr, dgettext(TEXT_DOMAIN, 15024345Sdougm "share complete, however could not add %s to %s\n"), 15034345Sdougm dir, NFSLOGTAB); 15043910Sdougm error = -1; 15053910Sdougm } 15063910Sdougm 15073910Sdougm out: 15083910Sdougm if (f != NULL) 15093910Sdougm (void) fclose(f); 15103910Sdougm return (error); 15113910Sdougm } 15123910Sdougm 15133910Sdougm /* 15143910Sdougm * Deactivate an entry from the nfslogtab file 15153910Sdougm */ 15163910Sdougm static int 15173910Sdougm nfslogtab_deactivate(path) 15183910Sdougm char *path; 15193910Sdougm { 15203910Sdougm FILE *f; 15213910Sdougm int error = 0; 15223910Sdougm 15233910Sdougm f = fopen(NFSLOGTAB, "r+"); 15243910Sdougm if (f == NULL) { 15253910Sdougm error = errno; 15263910Sdougm goto out; 15273910Sdougm } 15283910Sdougm if (lockf(fileno(f), F_LOCK, 0L) < 0) { 15293910Sdougm error = errno; 15303910Sdougm (void) fprintf(stderr, dgettext(TEXT_DOMAIN, 15314345Sdougm "share complete, however could not lock %s for " 15324345Sdougm "update: %s\n"), NFSLOGTAB, strerror(error)); 15333910Sdougm goto out; 15343910Sdougm } 15353910Sdougm if (logtab_deactivate(f, NULL, path, NULL) == -1) { 15363910Sdougm error = -1; 15373910Sdougm (void) fprintf(stderr, 15384345Sdougm dgettext(TEXT_DOMAIN, 15394345Sdougm "share complete, however could not " 15404345Sdougm "deactivate %s in %s\n"), path, NFSLOGTAB); 15413910Sdougm goto out; 15423910Sdougm } 15433910Sdougm 15443910Sdougm out: if (f != NULL) 15453910Sdougm (void) fclose(f); 15463910Sdougm 15473910Sdougm return (error); 15483910Sdougm } 15493910Sdougm 15503910Sdougm /* 15514524Sdougm * check_public(group, skipshare) 15524524Sdougm * 15534524Sdougm * Check the group for any shares that have the public property 15544524Sdougm * enabled. We skip "skipshare" since that is the one we are 15554524Sdougm * working with. This is a separate function to make handling 15564524Sdougm * subgroups simpler. Returns true if there is a share with public. 15574524Sdougm */ 15584524Sdougm static int 15594524Sdougm check_public(sa_group_t group, sa_share_t skipshare) 15604524Sdougm { 15614524Sdougm int exists = B_FALSE; 15624524Sdougm sa_share_t share; 15634524Sdougm sa_optionset_t opt; 15644524Sdougm sa_property_t prop; 15654524Sdougm char *shared; 15664524Sdougm 15674524Sdougm for (share = sa_get_share(group, NULL); share != NULL; 15684524Sdougm share = sa_get_next_share(share)) { 15694524Sdougm if (share == skipshare) 15704524Sdougm continue; 15714524Sdougm 15724524Sdougm opt = sa_get_optionset(share, "nfs"); 15734524Sdougm if (opt == NULL) 15744524Sdougm continue; 15754524Sdougm prop = sa_get_property(opt, "public"); 15764524Sdougm if (prop == NULL) 15774524Sdougm continue; 15784524Sdougm shared = sa_get_share_attr(share, "shared"); 15794524Sdougm if (shared != NULL) { 15804524Sdougm exists = strcmp(shared, "true") == 0; 15814524Sdougm sa_free_attr_string(shared); 15824524Sdougm if (exists == B_TRUE) 15834524Sdougm break; 15844524Sdougm } 15854524Sdougm } 15864524Sdougm 15874524Sdougm return (exists); 15884524Sdougm } 15894524Sdougm 15904524Sdougm /* 15913910Sdougm * public_exists(share) 15923910Sdougm * 15933910Sdougm * check to see if public option is set on any other share than the 15944524Sdougm * one specified. Need to check zfs sub-groups as well as the top 15954524Sdougm * level groups. 15963910Sdougm */ 15973910Sdougm static int 15983910Sdougm public_exists(sa_share_t skipshare) 15993910Sdougm { 16003910Sdougm sa_group_t group; 16013910Sdougm sa_handle_t handle; 16023910Sdougm 16033910Sdougm group = sa_get_parent_group(skipshare); 16043910Sdougm if (group == NULL) 16054345Sdougm return (SA_NO_SUCH_GROUP); 16063910Sdougm 16073910Sdougm handle = sa_find_group_handle(group); 16083910Sdougm if (handle == NULL) 16094345Sdougm return (SA_SYSTEM_ERR); 16103910Sdougm 16113910Sdougm for (group = sa_get_group(handle, NULL); group != NULL; 16123910Sdougm group = sa_get_next_group(group)) { 16134524Sdougm /* Walk any ZFS subgroups as well as all standard groups */ 16144524Sdougm if (sa_group_is_zfs(group)) { 16154524Sdougm sa_group_t subgroup; 16164524Sdougm for (subgroup = sa_get_sub_group(group); 16174524Sdougm subgroup != NULL; 16184524Sdougm subgroup = sa_get_next_group(subgroup)) { 16194524Sdougm if (check_public(subgroup, skipshare)) 16204524Sdougm return (B_TRUE); 16213910Sdougm } 16224524Sdougm } else { 16234524Sdougm if (check_public(group, skipshare)) 16244524Sdougm return (B_TRUE); 16253910Sdougm } 16263910Sdougm } 16274524Sdougm return (B_FALSE); 16283910Sdougm } 16293910Sdougm 16303910Sdougm /* 16313910Sdougm * sa_enable_share at the protocol level, enable_share must tell the 16323910Sdougm * implementation that it is to enable the share. This entails 16333910Sdougm * converting the path and options into the appropriate ioctl 16343910Sdougm * calls. It is assumed that all error checking of paths, etc. were 16353910Sdougm * done earlier. 16363910Sdougm */ 16373910Sdougm static int 16383910Sdougm nfs_enable_share(sa_share_t share) 16393910Sdougm { 16403910Sdougm struct exportdata export; 16413910Sdougm sa_optionset_t secoptlist; 16423910Sdougm struct secinfo *sp; 16433910Sdougm int num_secinfo; 16443910Sdougm sa_optionset_t opt; 16453910Sdougm sa_security_t sec; 16463910Sdougm sa_property_t prop; 16473910Sdougm char *path; 16483910Sdougm int err = SA_OK; 16494524Sdougm int i; 16504543Smarks int iszfs; 16513910Sdougm 16523910Sdougm /* Don't drop core if the NFS module isn't loaded. */ 16533910Sdougm (void) signal(SIGSYS, SIG_IGN); 16543910Sdougm 16553910Sdougm /* get the path since it is important in several places */ 16563910Sdougm path = sa_get_share_attr(share, "path"); 16573910Sdougm if (path == NULL) 16584345Sdougm return (SA_NO_SUCH_PATH); 16593910Sdougm 16604543Smarks iszfs = sa_path_is_zfs(path); 16613910Sdougm /* 16623910Sdougm * find the optionsets and security sets. There may not be 16633910Sdougm * any or there could be one or two for each of optionset and 16643910Sdougm * security may have multiple, one per security type per 16653910Sdougm * protocol type. 16663910Sdougm */ 16673910Sdougm opt = sa_get_derived_optionset(share, "nfs", 1); 16683910Sdougm secoptlist = (sa_optionset_t)sa_get_all_security_types(share, "nfs", 1); 16693910Sdougm if (secoptlist != NULL) 16704345Sdougm num_secinfo = MAX(1, count_security(secoptlist)); 16713910Sdougm else 16724345Sdougm num_secinfo = 1; 16733910Sdougm 16743910Sdougm /* 16753910Sdougm * walk through the options and fill in the structure 16763910Sdougm * appropriately. 16773910Sdougm */ 16783910Sdougm 16793910Sdougm (void) memset(&export, '\0', sizeof (export)); 16803910Sdougm 16813910Sdougm /* 16823910Sdougm * do non-security options first since there is only one after 16833910Sdougm * the derived group is constructed. 16843910Sdougm */ 16853910Sdougm export.ex_version = EX_CURRENT_VERSION; 16863910Sdougm export.ex_anon = UID_NOBODY; /* this is our default value */ 16873910Sdougm export.ex_index = NULL; 16883910Sdougm export.ex_path = path; 16893910Sdougm export.ex_pathlen = strlen(path) + 1; 16903910Sdougm 16913910Sdougm if (opt != NULL) 16924345Sdougm err = fill_export_from_optionset(&export, opt); 16933910Sdougm 16943910Sdougm /* 16953910Sdougm * check to see if "public" is set. If it is, then make sure 16963910Sdougm * no other share has it set. If it is already used, fail. 16973910Sdougm */ 16983910Sdougm 16993910Sdougm if (export.ex_flags & EX_PUBLIC && public_exists(share)) { 17004345Sdougm (void) printf(dgettext(TEXT_DOMAIN, 17014345Sdougm "NFS: Cannot share more than one file " 17024345Sdougm "system with 'public' property\n")); 17034345Sdougm err = SA_NOT_ALLOWED; 17044345Sdougm goto out; 17053910Sdougm } 17063910Sdougm 17074524Sdougm sp = calloc(num_secinfo, sizeof (struct secinfo)); 17083910Sdougm if (sp == NULL) { 17094345Sdougm err = SA_NO_MEMORY; 17104524Sdougm (void) printf(dgettext(TEXT_DOMAIN, 17114524Sdougm "NFS: NFS: no memory for security\n")); 17124524Sdougm goto out; 17134524Sdougm } 17144524Sdougm export.ex_secinfo = sp; 17154524Sdougm /* get default secinfo */ 17164524Sdougm export.ex_seccnt = num_secinfo; 17174524Sdougm /* 17184524Sdougm * since we must have one security option defined, we 17194524Sdougm * init to the default and then override as we find 17204524Sdougm * defined security options. This handles the case 17214524Sdougm * where we have no defined options but we need to set 17224524Sdougm * up one. 17234524Sdougm */ 17244524Sdougm sp[0].s_window = DEF_WIN; 17254524Sdougm sp[0].s_rootnames = NULL; 17264524Sdougm /* setup a default in case no properties defined */ 17274524Sdougm if (nfs_getseconfig_default(&sp[0].s_secinfo)) { 17284524Sdougm (void) printf(dgettext(TEXT_DOMAIN, 17294524Sdougm "NFS: nfs_getseconfig_default: failed to " 17304524Sdougm "get default security mode\n")); 17314524Sdougm err = SA_CONFIG_ERR; 17324524Sdougm } 17334524Sdougm if (secoptlist != NULL) { 17344524Sdougm for (i = 0, prop = sa_get_property(secoptlist, NULL); 17354524Sdougm prop != NULL && i < num_secinfo; 17364524Sdougm prop = sa_get_next_property(prop), i++) { 17374524Sdougm char *sectype; 17384345Sdougm sectype = sa_get_property_attr(prop, "type"); 17394524Sdougm /* 17404524Sdougm * if sectype is NULL, we probably 17414524Sdougm * have a memory problem and can't get 17424524Sdougm * the correct values. Rather than 17434524Sdougm * exporting with incorrect security, 17444524Sdougm * don't share it. 17454524Sdougm */ 17464524Sdougm if (sectype == NULL) { 17474524Sdougm err = SA_NO_MEMORY; 17484524Sdougm (void) printf(dgettext(TEXT_DOMAIN, 17494524Sdougm "NFS: Cannot share %s: " 17504524Sdougm "no memory\n"), path); 17514524Sdougm goto out; 17524524Sdougm } 17534524Sdougm sec = (sa_security_t)sa_get_derived_security( 17544524Sdougm share, sectype, "nfs", 1); 17554524Sdougm sp[i].s_window = DEF_WIN; 17564524Sdougm sp[i].s_rootcnt = 0; 17574524Sdougm sp[i].s_rootnames = NULL; 17584345Sdougm (void) fill_security_from_secopts(&sp[i], sec); 17594524Sdougm if (sec != NULL) 17604524Sdougm sa_free_derived_security(sec); 17614524Sdougm if (sectype != NULL) 17624524Sdougm sa_free_attr_string(sectype); 17633910Sdougm } 17644524Sdougm } 17654524Sdougm /* 17664524Sdougm * when we get here, we can do the exportfs system call and 17674524Sdougm * initiate thinsg. We probably want to enable the nfs.server 17684524Sdougm * service first if it isn't running within SMF. 17694524Sdougm */ 17704524Sdougm /* check nfs.server status and start if needed */ 17714524Sdougm /* now add the share to the internal tables */ 17724524Sdougm printarg(path, &export); 17734524Sdougm /* 17744524Sdougm * call the exportfs system call which is implemented 17754524Sdougm * via the nfssys() call as the EXPORTFS subfunction. 17764524Sdougm */ 17774543Smarks if (iszfs) { 17784543Smarks struct exportfs_args ea; 17794543Smarks share_t sh; 17804543Smarks char *str; 17814543Smarks priv_set_t *priv_effective; 17824543Smarks int privileged; 17834543Smarks 17844543Smarks /* 17854543Smarks * If we aren't a privileged user 17864543Smarks * and NFS server service isn't running 17874543Smarks * then print out an error message 17884543Smarks * and return EPERM 17894543Smarks */ 17904543Smarks 17914543Smarks priv_effective = priv_allocset(); 17924543Smarks (void) getppriv(PRIV_EFFECTIVE, priv_effective); 17934543Smarks 17944543Smarks privileged = (priv_isfullset(priv_effective) == B_TRUE); 17954543Smarks priv_freeset(priv_effective); 17964543Smarks 17974543Smarks if (!privileged && 17984543Smarks (str = smf_get_state(NFS_SERVER_SVC)) != NULL) { 17994543Smarks err = 0; 18004543Smarks if (strcmp(str, SCF_STATE_STRING_ONLINE) != 0) { 18014543Smarks (void) printf(dgettext(TEXT_DOMAIN, 18024543Smarks "NFS: Cannot share remote " 18034543Smarks "filesystem: %s\n"), path); 18044543Smarks (void) printf(dgettext(TEXT_DOMAIN, 18054543Smarks "NFS: Service needs to be enabled " 18064543Smarks "by a privileged user\n")); 18074543Smarks err = SA_SYSTEM_ERR; 18084543Smarks errno = EPERM; 18094543Smarks } 18104543Smarks free(str); 18114543Smarks } 18124543Smarks 18134543Smarks if (err == 0) { 18144543Smarks ea.dname = path; 18154543Smarks ea.uex = &export; 18164543Smarks 18174543Smarks sa_sharetab_fill_zfs(share, &sh, "nfs"); 1818*5331Samw err = sa_share_zfs(share, path, &sh, 1819*5331Samw &ea, ZFS_SHARE_NFS); 18204543Smarks sa_emptyshare(&sh); 18214543Smarks } 18224543Smarks } else { 18234543Smarks err = exportfs(path, &export); 18244543Smarks } 18254543Smarks 18264543Smarks if (err < 0) { 18274524Sdougm err = SA_SYSTEM_ERR; 18284524Sdougm switch (errno) { 18294524Sdougm case EREMOTE: 18304524Sdougm (void) printf(dgettext(TEXT_DOMAIN, 18314543Smarks "NFS: Cannot share filesystems " 18324543Smarks "in non-global zones: %s\n"), path); 18334543Smarks err = SA_NOT_SUPPORTED; 18344524Sdougm break; 18354524Sdougm case EPERM: 18364524Sdougm if (getzoneid() != GLOBAL_ZONEID) { 18374345Sdougm (void) printf(dgettext(TEXT_DOMAIN, 18384543Smarks "NFS: Cannot share file systems " 18394524Sdougm "in non-global zones: %s\n"), path); 18404524Sdougm err = SA_NOT_SUPPORTED; 18414345Sdougm break; 18424345Sdougm } 18434524Sdougm err = SA_NO_PERMISSION; 18444524Sdougm /* FALLTHROUGH */ 18454524Sdougm default: 18464524Sdougm break; 18473910Sdougm } 18484524Sdougm } else { 18494524Sdougm /* update sharetab with an add/modify */ 18504543Smarks if (!iszfs) { 18514543Smarks (void) sa_update_sharetab(share, "nfs"); 18524543Smarks } 18533910Sdougm } 18543910Sdougm 18553910Sdougm if (err == SA_OK) { 18563910Sdougm /* 18573910Sdougm * enable services as needed. This should probably be 18583910Sdougm * done elsewhere in order to minimize the calls to 18593910Sdougm * check services. 18603910Sdougm */ 18613910Sdougm /* 18623910Sdougm * check to see if logging and other services need to 18633910Sdougm * be triggered, but only if there wasn't an 18643910Sdougm * error. This is probably where sharetab should be 18653910Sdougm * updated with the NFS specific entry. 18663910Sdougm */ 18674345Sdougm if (export.ex_flags & EX_LOG) { 18684345Sdougm /* enable logging */ 18694345Sdougm if (nfslogtab_add(path, export.ex_log_buffer, 18704345Sdougm export.ex_tag) != 0) { 18714345Sdougm (void) fprintf(stderr, dgettext(TEXT_DOMAIN, 18724345Sdougm "Could not enable logging for %s\n"), 18734345Sdougm path); 18744345Sdougm } 18754345Sdougm _check_services(service_list_logging); 18764345Sdougm } else { 18774345Sdougm /* 18784345Sdougm * don't have logging so remove it from file. It might 18794345Sdougm * not be thre, but that doesn't matter. 18804345Sdougm */ 18814345Sdougm (void) nfslogtab_deactivate(path); 18824345Sdougm _check_services(service_list_default); 18833910Sdougm } 18843910Sdougm } 18853910Sdougm 18863910Sdougm out: 18873910Sdougm if (path != NULL) 18884345Sdougm free(path); 18893910Sdougm 18903910Sdougm cleanup_export(&export); 18913910Sdougm if (opt != NULL) 18924345Sdougm sa_free_derived_optionset(opt); 18933910Sdougm if (secoptlist != NULL) 18944345Sdougm (void) sa_destroy_optionset(secoptlist); 18953910Sdougm return (err); 18963910Sdougm } 18973910Sdougm 18983910Sdougm /* 18993910Sdougm * nfs_disable_share(share) 19003910Sdougm * 19013910Sdougm * Unshare the specified share. How much error checking should be 19023910Sdougm * done? We only do basic errors for now. 19033910Sdougm */ 19043910Sdougm static int 19054543Smarks nfs_disable_share(sa_share_t share, char *path) 19063910Sdougm { 19073910Sdougm int err; 19083910Sdougm int ret = SA_OK; 19094543Smarks int iszfs; 19103910Sdougm 19114543Smarks 19124543Smarks if (path != NULL) { 19134543Smarks iszfs = sa_path_is_zfs(path); 19144543Smarks 19154543Smarks if (iszfs) { 19164543Smarks struct exportfs_args ea; 19174543Smarks share_t sh = { 0 }; 19184543Smarks 19194543Smarks ea.dname = path; 19204543Smarks ea.uex = NULL; 19214543Smarks sh.sh_path = path; 19224543Smarks sh.sh_fstype = "nfs"; 19234543Smarks 1924*5331Samw err = sa_share_zfs(share, path, &sh, 1925*5331Samw &ea, ZFS_UNSHARE_NFS); 19264543Smarks } else 19274543Smarks err = exportfs(path, NULL); 19284345Sdougm if (err < 0) { 19294345Sdougm /* 19304543Smarks * TBD: only an error in some 19314543Smarks * cases - need better analysis 19324345Sdougm */ 19334543Smarks 19344345Sdougm switch (errno) { 19354345Sdougm case EPERM: 19364345Sdougm case EACCES: 19374345Sdougm ret = SA_NO_PERMISSION; 19384543Smarks if (getzoneid() != GLOBAL_ZONEID) { 19394345Sdougm ret = SA_NOT_SUPPORTED; 19404543Smarks } 19414345Sdougm break; 19424345Sdougm case EINVAL: 19434345Sdougm case ENOENT: 19444345Sdougm ret = SA_NO_SUCH_PATH; 19454543Smarks break; 19464345Sdougm default: 19474345Sdougm ret = SA_SYSTEM_ERR; 19484543Smarks break; 19494345Sdougm } 19503910Sdougm } 19514345Sdougm if (ret == SA_OK || ret == SA_NO_SUCH_PATH) { 19524543Smarks if (!iszfs) 19534543Smarks (void) sa_delete_sharetab(path, "nfs"); 19544345Sdougm /* just in case it was logged */ 19554543Smarks (void) nfslogtab_deactivate(path); 19564345Sdougm } 19573910Sdougm } 19583910Sdougm return (ret); 19593910Sdougm } 19603910Sdougm 19613910Sdougm /* 19623910Sdougm * check ro vs rw values. Over time this may get beefed up. 19633910Sdougm * for now it just does simple checks. 19643910Sdougm */ 19653910Sdougm 19663910Sdougm static int 19673910Sdougm check_rorw(char *v1, char *v2) 19683910Sdougm { 19693910Sdougm int ret = SA_OK; 19703910Sdougm if (strcmp(v1, v2) == 0) 19714345Sdougm ret = SA_VALUE_CONFLICT; 19723910Sdougm return (ret); 19733910Sdougm } 19743910Sdougm 19753910Sdougm /* 19763910Sdougm * nfs_validate_property(property, parent) 19773910Sdougm * 19783910Sdougm * Check that the property has a legitimate value for its type. 19793910Sdougm */ 19803910Sdougm 19813910Sdougm static int 19823910Sdougm nfs_validate_property(sa_property_t property, sa_optionset_t parent) 19833910Sdougm { 19843910Sdougm int ret = SA_OK; 19853910Sdougm char *propname; 19863910Sdougm char *other; 19873910Sdougm int optindex; 19883910Sdougm nfsl_config_t *configlist; 19893910Sdougm sa_group_t parent_group; 19903910Sdougm char *value; 19913910Sdougm 19923910Sdougm propname = sa_get_property_attr(property, "type"); 19933910Sdougm 19943910Sdougm if ((optindex = findopt(propname)) < 0) 19954345Sdougm ret = SA_NO_SUCH_PROP; 19963910Sdougm 19973910Sdougm /* need to validate value range here as well */ 19983910Sdougm 19993910Sdougm if (ret == SA_OK) { 20004345Sdougm parent_group = sa_get_parent_group((sa_share_t)parent); 20014345Sdougm if (optdefs[optindex].share && !sa_is_share(parent_group)) 20024345Sdougm ret = SA_PROP_SHARE_ONLY; 20033910Sdougm } 20043910Sdougm if (ret == SA_OK) { 20054345Sdougm value = sa_get_property_attr(property, "value"); 20064345Sdougm if (value != NULL) { 20074345Sdougm /* first basic type checking */ 20084345Sdougm switch (optdefs[optindex].type) { 20094345Sdougm case OPT_TYPE_NUMBER: 20104345Sdougm /* check that the value is all digits */ 20114345Sdougm if (!is_a_number(value)) 20124345Sdougm ret = SA_BAD_VALUE; 20134345Sdougm break; 20144345Sdougm case OPT_TYPE_BOOLEAN: 20154345Sdougm if (strlen(value) == 0 || 20164345Sdougm strcasecmp(value, "true") == 0 || 20174345Sdougm strcmp(value, "1") == 0 || 20184345Sdougm strcasecmp(value, "false") == 0 || 20194345Sdougm strcmp(value, "0") == 0) { 20204345Sdougm ret = SA_OK; 20214345Sdougm } else { 20224345Sdougm ret = SA_BAD_VALUE; 20234345Sdougm } 20244345Sdougm break; 20254345Sdougm case OPT_TYPE_USER: 20264345Sdougm if (!is_a_number(value)) { 20274345Sdougm struct passwd *pw; 20284345Sdougm /* 20294345Sdougm * in this case it would have to be a 20304345Sdougm * user name 20314345Sdougm */ 20324345Sdougm pw = getpwnam(value); 20334345Sdougm if (pw == NULL) 20344345Sdougm ret = SA_BAD_VALUE; 20354345Sdougm endpwent(); 20364345Sdougm } else { 20374345Sdougm uint64_t intval; 20384345Sdougm intval = strtoull(value, NULL, 0); 20394345Sdougm if (intval > UID_MAX && intval != ~0) 20404345Sdougm ret = SA_BAD_VALUE; 20414345Sdougm } 20424345Sdougm break; 20434345Sdougm case OPT_TYPE_FILE: 20444345Sdougm if (strcmp(value, "..") == 0 || 20454345Sdougm strchr(value, '/') != NULL) { 20464345Sdougm ret = SA_BAD_VALUE; 20474345Sdougm } 20484345Sdougm break; 20494345Sdougm case OPT_TYPE_ACCLIST: 20504345Sdougm /* 20514345Sdougm * access list handling. Should eventually 20524345Sdougm * validate that all the values make sense. 20534345Sdougm * Also, ro and rw may have cross value 20544345Sdougm * conflicts. 20554345Sdougm */ 20564345Sdougm if (strcmp(propname, SHOPT_RO) == 0) 20574345Sdougm other = SHOPT_RW; 20584345Sdougm else if (strcmp(propname, SHOPT_RW) == 0) 20594345Sdougm other = SHOPT_RO; 20604345Sdougm else 20614345Sdougm other = NULL; 20624345Sdougm 20634345Sdougm if (other != NULL && parent != NULL) { 20644345Sdougm /* compare rw(ro) with ro(rw) */ 20654345Sdougm sa_property_t oprop; 20664345Sdougm oprop = sa_get_property(parent, other); 20674345Sdougm if (oprop != NULL) { 20684345Sdougm /* 20694345Sdougm * only potential 20704345Sdougm * confusion if other 20714345Sdougm * exists 20724345Sdougm */ 20734345Sdougm char *ovalue; 20744345Sdougm ovalue = sa_get_property_attr( 20754345Sdougm oprop, "value"); 20764345Sdougm if (ovalue != NULL) { 20774345Sdougm ret = check_rorw(value, 20784345Sdougm ovalue); 20794345Sdougm sa_free_attr_string( 20804345Sdougm ovalue); 20814345Sdougm } 20824345Sdougm } 20834345Sdougm } 20844345Sdougm break; 20854345Sdougm case OPT_TYPE_LOGTAG: 20864345Sdougm if (nfsl_getconfig_list(&configlist) == 0) { 20874345Sdougm int error; 20884345Sdougm if (value == NULL || 20894345Sdougm strlen(value) == 0) { 20904345Sdougm if (value != NULL) 20914345Sdougm sa_free_attr_string( 20924345Sdougm value); 20934345Sdougm value = strdup("global"); 20944345Sdougm } 20954345Sdougm if (value != NULL && 20964345Sdougm nfsl_findconfig(configlist, value, 20974345Sdougm &error) == NULL) { 20984345Sdougm ret = SA_BAD_VALUE; 20994345Sdougm } 21005179Sdougm /* Must always free when done */ 21015179Sdougm nfsl_freeconfig_list(&configlist); 21024345Sdougm } else { 21034345Sdougm ret = SA_CONFIG_ERR; 21044345Sdougm } 21054345Sdougm break; 21064345Sdougm case OPT_TYPE_STRING: 21074345Sdougm /* whatever is here should be ok */ 21084345Sdougm break; 21094345Sdougm case OPT_TYPE_SECURITY: 21104345Sdougm /* 21114345Sdougm * The "sec" property isn't used in the 21124345Sdougm * non-legacy parts of sharemgr. We need to 21134345Sdougm * reject it here. For legacy, it is pulled 21144345Sdougm * out well before we get here. 21154345Sdougm */ 21164345Sdougm ret = SA_NO_SUCH_PROP; 21174345Sdougm break; 21184345Sdougm default: 21194345Sdougm break; 21203910Sdougm } 21215179Sdougm 21225179Sdougm if (value != NULL) 21235179Sdougm sa_free_attr_string(value); 21245179Sdougm 21254345Sdougm if (ret == SA_OK && optdefs[optindex].check != NULL) { 21264345Sdougm /* do the property specific check */ 21274345Sdougm ret = optdefs[optindex].check(property); 21283910Sdougm } 21293910Sdougm } 21303910Sdougm } 21313910Sdougm 21323910Sdougm if (propname != NULL) 21334345Sdougm sa_free_attr_string(propname); 21343910Sdougm return (ret); 21353910Sdougm } 21363910Sdougm 21373910Sdougm /* 21383910Sdougm * Protocol management functions 21393910Sdougm * 21403910Sdougm * Properties defined in the default files are defined in 21413910Sdougm * proto_option_defs for parsing and validation. If "other" and 21423910Sdougm * "compare" are set, then the value for this property should be 21433910Sdougm * compared against the property specified in "other" using the 21443910Sdougm * "compare" check (either <= or >=) in order to ensure that the 21453910Sdougm * values are in the correct range. E.g. setting server_versmin 21463910Sdougm * higher than server_versmax should not be allowed. 21473910Sdougm */ 21483910Sdougm 21493910Sdougm struct proto_option_defs { 21503910Sdougm char *tag; 21513910Sdougm char *name; /* display name -- remove protocol identifier */ 21523910Sdougm int index; 21533910Sdougm int type; 21543910Sdougm union { 21553910Sdougm int intval; 21563910Sdougm char *string; 21573910Sdougm } defvalue; 21583910Sdougm uint32_t svcs; 21593910Sdougm int32_t minval; 21603910Sdougm int32_t maxval; 21613910Sdougm char *file; 21623910Sdougm char *other; 21633910Sdougm int compare; 21643910Sdougm #define OPT_CMP_GE 0 21653910Sdougm #define OPT_CMP_LE 1 21663910Sdougm int (*check)(char *); 21673910Sdougm } proto_options[] = { 21683910Sdougm #define PROTO_OPT_NFSD_SERVERS 0 21693910Sdougm {"nfsd_servers", 21703910Sdougm "servers", PROTO_OPT_NFSD_SERVERS, OPT_TYPE_NUMBER, 16, SVC_NFSD, 21713910Sdougm 1, INT32_MAX, NFSADMIN}, 21723910Sdougm #define PROTO_OPT_LOCKD_LISTEN_BACKLOG 1 21733910Sdougm {"lockd_listen_backlog", 21743910Sdougm "lockd_listen_backlog", PROTO_OPT_LOCKD_LISTEN_BACKLOG, 21753910Sdougm OPT_TYPE_NUMBER, 32, SVC_LOCKD, 32, INT32_MAX, NFSADMIN}, 21763910Sdougm #define PROTO_OPT_LOCKD_SERVERS 2 21773910Sdougm {"lockd_servers", 21783910Sdougm "lockd_servers", PROTO_OPT_LOCKD_SERVERS, OPT_TYPE_NUMBER, 20, 21793910Sdougm SVC_LOCKD, 1, INT32_MAX, NFSADMIN}, 21803910Sdougm #define PROTO_OPT_LOCKD_RETRANSMIT_TIMEOUT 3 21813910Sdougm {"lockd_retransmit_timeout", 21823910Sdougm "lockd_retransmit_timeout", PROTO_OPT_LOCKD_RETRANSMIT_TIMEOUT, 21833910Sdougm OPT_TYPE_NUMBER, 5, SVC_LOCKD, 0, INT32_MAX, NFSADMIN}, 21843910Sdougm #define PROTO_OPT_GRACE_PERIOD 4 21853910Sdougm {"grace_period", 21863910Sdougm "grace_period", PROTO_OPT_GRACE_PERIOD, OPT_TYPE_NUMBER, 90, 21873910Sdougm SVC_LOCKD, 0, INT32_MAX, NFSADMIN}, 21883910Sdougm #define PROTO_OPT_NFS_SERVER_VERSMIN 5 21893910Sdougm {"nfs_server_versmin", 21903910Sdougm "server_versmin", PROTO_OPT_NFS_SERVER_VERSMIN, OPT_TYPE_NUMBER, 21913910Sdougm (int)NFS_VERSMIN_DEFAULT, SVC_NFSD|SVC_MOUNTD, NFS_VERSMIN, 21923910Sdougm NFS_VERSMAX, NFSADMIN, "server_versmax", OPT_CMP_LE}, 21933910Sdougm #define PROTO_OPT_NFS_SERVER_VERSMAX 6 21943910Sdougm {"nfs_server_versmax", 21953910Sdougm "server_versmax", PROTO_OPT_NFS_SERVER_VERSMAX, OPT_TYPE_NUMBER, 21963910Sdougm (int)NFS_VERSMAX_DEFAULT, SVC_NFSD|SVC_MOUNTD, NFS_VERSMIN, 21973910Sdougm NFS_VERSMAX, NFSADMIN, "server_versmin", OPT_CMP_GE}, 21983910Sdougm #define PROTO_OPT_NFS_CLIENT_VERSMIN 7 21993910Sdougm {"nfs_client_versmin", 22003910Sdougm "client_versmin", PROTO_OPT_NFS_CLIENT_VERSMIN, OPT_TYPE_NUMBER, 22013910Sdougm (int)NFS_VERSMIN_DEFAULT, NULL, NFS_VERSMIN, NFS_VERSMAX, 22023910Sdougm NFSADMIN, "client_versmax", OPT_CMP_LE}, 22033910Sdougm #define PROTO_OPT_NFS_CLIENT_VERSMAX 8 22043910Sdougm {"nfs_client_versmax", 22053910Sdougm "client_versmax", PROTO_OPT_NFS_CLIENT_VERSMAX, OPT_TYPE_NUMBER, 22063910Sdougm (int)NFS_VERSMAX_DEFAULT, NULL, NFS_VERSMIN, NFS_VERSMAX, 22073910Sdougm NFSADMIN, "client_versmin", OPT_CMP_GE}, 22083910Sdougm #define PROTO_OPT_NFS_SERVER_DELEGATION 9 22093910Sdougm {"nfs_server_delegation", 22103910Sdougm "server_delegation", PROTO_OPT_NFS_SERVER_DELEGATION, 22113910Sdougm OPT_TYPE_ONOFF, NFS_SERVER_DELEGATION_DEFAULT, SVC_NFSD, 0, 0, 22123910Sdougm NFSADMIN}, 22133910Sdougm #define PROTO_OPT_NFSMAPID_DOMAIN 10 22143910Sdougm {"nfsmapid_domain", 22153910Sdougm "nfsmapid_domain", PROTO_OPT_NFSMAPID_DOMAIN, OPT_TYPE_DOMAIN, 22163910Sdougm NULL, SVC_NFSMAPID, 0, 0, NFSADMIN}, 22173910Sdougm #define PROTO_OPT_NFSD_MAX_CONNECTIONS 11 22183910Sdougm {"nfsd_max_connections", 22193910Sdougm "max_connections", PROTO_OPT_NFSD_MAX_CONNECTIONS, 22203910Sdougm OPT_TYPE_NUMBER, -1, SVC_NFSD, -1, INT32_MAX, NFSADMIN}, 22213910Sdougm #define PROTO_OPT_NFSD_PROTOCOL 12 22223910Sdougm {"nfsd_protocol", 22233910Sdougm "protocol", PROTO_OPT_NFSD_PROTOCOL, OPT_TYPE_PROTOCOL, 0, 22243910Sdougm SVC_NFSD, 0, 0, NFSADMIN}, 22253910Sdougm #define PROTO_OPT_NFSD_LISTEN_BACKLOG 13 22263910Sdougm {"nfsd_listen_backlog", 22273910Sdougm "listen_backlog", PROTO_OPT_NFSD_LISTEN_BACKLOG, 22283910Sdougm OPT_TYPE_NUMBER, 0, 22293910Sdougm SVC_LOCKD, 0, INT32_MAX, NFSADMIN}, 22303910Sdougm {NULL} 22313910Sdougm }; 22323910Sdougm 22333910Sdougm /* 22343910Sdougm * the protoset holds the defined options so we don't have to read 22353910Sdougm * them multiple times 22363910Sdougm */ 22375179Sdougm static sa_protocol_properties_t protoset; 22383910Sdougm 22393910Sdougm static int 22403910Sdougm findprotoopt(char *name, int whichname) 22413910Sdougm { 22423910Sdougm int i; 22433910Sdougm for (i = 0; proto_options[i].tag != NULL; i++) { 22444345Sdougm if (whichname == 1) { 22454345Sdougm if (strcasecmp(proto_options[i].name, name) == 0) 22463910Sdougm return (i); 22474345Sdougm } else { 22484345Sdougm if (strcasecmp(proto_options[i].tag, name) == 0) 22494345Sdougm return (i); 22504345Sdougm } 22513910Sdougm } 22523910Sdougm return (-1); 22533910Sdougm } 22543910Sdougm 22553910Sdougm /* 22563910Sdougm * fixcaselower(str) 22573910Sdougm * 22583910Sdougm * convert a string to lower case (inplace). 22593910Sdougm */ 22603910Sdougm 22613910Sdougm static void 22623910Sdougm fixcaselower(char *str) 22633910Sdougm { 22643910Sdougm while (*str) { 22654345Sdougm *str = tolower(*str); 22664345Sdougm str++; 22673910Sdougm } 22683910Sdougm } 22693910Sdougm 22703910Sdougm /* 22713910Sdougm * fixcaseupper(str) 22723910Sdougm * 22733910Sdougm * convert a string to upper case (inplace). 22743910Sdougm */ 22753910Sdougm 22763910Sdougm static void 22773910Sdougm fixcaseupper(char *str) 22783910Sdougm { 22793910Sdougm while (*str) { 22804345Sdougm *str = toupper(*str); 22814345Sdougm str++; 22823910Sdougm } 22833910Sdougm } 22843910Sdougm 22853910Sdougm /* 22864241Sdougm * skipwhitespace(str) 22874241Sdougm * 22884241Sdougm * Skip leading white space. It is assumed that it is called with a 22894241Sdougm * valid pointer. 22904241Sdougm */ 22914241Sdougm 22924241Sdougm static char * 22934241Sdougm skipwhitespace(char *str) 22944241Sdougm { 22954241Sdougm while (*str && isspace(*str)) 22964241Sdougm str++; 22974241Sdougm 22984241Sdougm return (str); 22994241Sdougm } 23004241Sdougm 23014241Sdougm /* 23024345Sdougm * extractprop() 23034345Sdougm * 23044345Sdougm * Extract the property and value out of the line and create the 23054345Sdougm * property in the optionset. 23064345Sdougm */ 23074345Sdougm static void 23084345Sdougm extractprop(char *name, char *value) 23094345Sdougm { 23104345Sdougm sa_property_t prop; 23114345Sdougm int index; 23124345Sdougm /* 23134345Sdougm * Remove any leading 23144345Sdougm * white space. 23154345Sdougm */ 23164345Sdougm name = skipwhitespace(name); 23174345Sdougm 23184345Sdougm index = findprotoopt(name, 0); 23194345Sdougm if (index >= 0) { 23204345Sdougm fixcaselower(name); 23214345Sdougm prop = sa_create_property(proto_options[index].name, value); 23224345Sdougm if (prop != NULL) 23234345Sdougm (void) sa_add_protocol_property(protoset, prop); 23244345Sdougm } 23254345Sdougm } 23264345Sdougm 23274345Sdougm /* 23283910Sdougm * initprotofromdefault() 23293910Sdougm * 23303910Sdougm * read the default file(s) and add the defined values to the 23313910Sdougm * protoset. Note that default values are known from the built in 23323910Sdougm * table in case the file doesn't have a definition. 23333910Sdougm */ 23343910Sdougm 23353910Sdougm static int 23363910Sdougm initprotofromdefault() 23373910Sdougm { 23383910Sdougm FILE *nfs; 23393910Sdougm char buff[BUFSIZ]; 23403910Sdougm char *name; 23413910Sdougm char *value; 23423910Sdougm 23433910Sdougm protoset = sa_create_protocol_properties("nfs"); 23443910Sdougm 23453910Sdougm if (protoset != NULL) { 23464345Sdougm nfs = fopen(NFSADMIN, "r"); 23474345Sdougm if (nfs != NULL) { 23484345Sdougm while (fgets(buff, sizeof (buff), nfs) != NULL) { 23494345Sdougm switch (buff[0]) { 23504345Sdougm case '\n': 23514345Sdougm case '#': 23524345Sdougm /* skip */ 23534345Sdougm break; 23544345Sdougm default: 23554345Sdougm name = buff; 23564345Sdougm buff[strlen(buff) - 1] = '\0'; 23574345Sdougm value = strchr(name, '='); 23584345Sdougm if (value != NULL) { 23594345Sdougm *value++ = '\0'; 23604345Sdougm extractprop(name, value); 23614345Sdougm } 23624345Sdougm } 23633910Sdougm } 23644345Sdougm if (nfs != NULL) 23654345Sdougm (void) fclose(nfs); 23663910Sdougm } 23673910Sdougm } 23683910Sdougm if (protoset == NULL) 23694345Sdougm return (SA_NO_MEMORY); 23703910Sdougm return (SA_OK); 23713910Sdougm } 23723910Sdougm 23733910Sdougm /* 23744345Sdougm * add_defaults() 23753910Sdougm * 23763910Sdougm * Add the default values for any property not defined in the parsing 23773910Sdougm * of the default files. Values are set according to their defined 23783910Sdougm * types. 23793910Sdougm */ 23803910Sdougm 23813910Sdougm static void 23823910Sdougm add_defaults() 23833910Sdougm { 23843910Sdougm int i; 23853910Sdougm char number[MAXDIGITS]; 23863910Sdougm 23873910Sdougm for (i = 0; proto_options[i].tag != NULL; i++) { 23884345Sdougm sa_property_t prop; 23894345Sdougm prop = sa_get_protocol_property(protoset, 23904345Sdougm proto_options[i].name); 23914345Sdougm if (prop == NULL) { 23924345Sdougm /* add the default value */ 23934345Sdougm switch (proto_options[i].type) { 23944345Sdougm case OPT_TYPE_NUMBER: 23954345Sdougm (void) snprintf(number, sizeof (number), "%d", 23964345Sdougm proto_options[i].defvalue.intval); 23974345Sdougm prop = sa_create_property(proto_options[i].name, 23984345Sdougm number); 23994345Sdougm break; 24003910Sdougm 24014345Sdougm case OPT_TYPE_BOOLEAN: 24024345Sdougm prop = sa_create_property(proto_options[i].name, 24034345Sdougm proto_options[i].defvalue.intval ? 24044345Sdougm "true" : "false"); 24054345Sdougm break; 24063910Sdougm 24074345Sdougm case OPT_TYPE_ONOFF: 24084345Sdougm prop = sa_create_property(proto_options[i].name, 24094345Sdougm proto_options[i].defvalue.intval ? 24104345Sdougm "on" : "off"); 24114345Sdougm break; 24123910Sdougm 24134345Sdougm default: 24144345Sdougm /* treat as strings of zero length */ 24154345Sdougm prop = sa_create_property(proto_options[i].name, 24164345Sdougm ""); 24174345Sdougm break; 24184345Sdougm } 24194345Sdougm if (prop != NULL) 24204345Sdougm (void) sa_add_protocol_property(protoset, prop); 24213910Sdougm } 24223910Sdougm } 24233910Sdougm } 24243910Sdougm 24253910Sdougm static void 24263910Sdougm free_protoprops() 24273910Sdougm { 24285179Sdougm if (protoset != NULL) { 24295179Sdougm xmlFreeNode(protoset); 24305179Sdougm protoset = NULL; 24315179Sdougm } 24323910Sdougm } 24333910Sdougm 24343910Sdougm /* 24353910Sdougm * nfs_init() 24363910Sdougm * 24373910Sdougm * Initialize the NFS plugin. 24383910Sdougm */ 24393910Sdougm 24403910Sdougm static int 24413910Sdougm nfs_init() 24423910Sdougm { 24433910Sdougm int ret = SA_OK; 24443910Sdougm 24453910Sdougm if (sa_plugin_ops.sa_init != nfs_init) 24464345Sdougm (void) printf(dgettext(TEXT_DOMAIN, 24474345Sdougm "NFS plugin not properly initialized\n")); 24483910Sdougm 24493910Sdougm ret = initprotofromdefault(); 24504345Sdougm if (ret == SA_OK) 24514345Sdougm add_defaults(); 24523910Sdougm 24533910Sdougm return (ret); 24543910Sdougm } 24553910Sdougm 24563910Sdougm /* 24573910Sdougm * nfs_fini() 24583910Sdougm * 24593910Sdougm * uninitialize the NFS plugin. Want to avoid memory leaks. 24603910Sdougm */ 24613910Sdougm 24623910Sdougm static void 24633910Sdougm nfs_fini() 24643910Sdougm { 24653910Sdougm free_protoprops(); 24663910Sdougm } 24673910Sdougm 24683910Sdougm /* 24693910Sdougm * nfs_get_proto_set() 24703910Sdougm * 24713910Sdougm * Return an optionset with all the protocol specific properties in 24723910Sdougm * it. 24733910Sdougm */ 24743910Sdougm 24753910Sdougm static sa_protocol_properties_t 24763910Sdougm nfs_get_proto_set() 24773910Sdougm { 24783910Sdougm return (protoset); 24793910Sdougm } 24803910Sdougm 24813910Sdougm struct deffile { 24823910Sdougm struct deffile *next; 24833910Sdougm char *line; 24843910Sdougm }; 24853910Sdougm 24863910Sdougm /* 24873910Sdougm * read_default_file(fname) 24883910Sdougm * 24893910Sdougm * Read the specified default file. We return a list of entries. This 24903910Sdougm * get used for adding or removing values. 24913910Sdougm */ 24923910Sdougm 24933910Sdougm static struct deffile * 24943910Sdougm read_default_file(char *fname) 24953910Sdougm { 24963910Sdougm FILE *file; 24973910Sdougm struct deffile *defs = NULL; 24983910Sdougm struct deffile *newdef; 24993910Sdougm struct deffile *prevdef = NULL; 25003910Sdougm char buff[BUFSIZ * 2]; 25013910Sdougm 25023910Sdougm file = fopen(fname, "r"); 25033910Sdougm if (file != NULL) { 25044345Sdougm while (fgets(buff, sizeof (buff), file) != NULL) { 25054345Sdougm newdef = (struct deffile *)calloc(1, 25064345Sdougm sizeof (struct deffile)); 25074345Sdougm if (newdef != NULL) { 25084345Sdougm /* Make sure we skip any leading whitespace. */ 25094345Sdougm newdef->line = strdup(skipwhitespace(buff)); 25104345Sdougm if (defs == NULL) { 25114345Sdougm prevdef = defs = newdef; 25124345Sdougm } else { 25134345Sdougm prevdef->next = newdef; 25144345Sdougm prevdef = newdef; 25154345Sdougm } 25164345Sdougm } 25173910Sdougm } 25183910Sdougm } 25193910Sdougm (void) fclose(file); 25203910Sdougm return (defs); 25213910Sdougm } 25223910Sdougm 25233910Sdougm static void 25243910Sdougm free_default_file(struct deffile *defs) 25253910Sdougm { 25263910Sdougm struct deffile *curdefs = NULL; 25273910Sdougm 25283910Sdougm while (defs != NULL) { 25294345Sdougm curdefs = defs; 25304345Sdougm defs = defs->next; 25314345Sdougm if (curdefs->line != NULL) 25324345Sdougm free(curdefs->line); 25334345Sdougm free(curdefs); 25343910Sdougm } 25353910Sdougm } 25363910Sdougm 25373910Sdougm /* 25383910Sdougm * write_default_file(fname, defs) 25393910Sdougm * 25403910Sdougm * Write the default file back. 25413910Sdougm */ 25423910Sdougm 25433910Sdougm static int 25443910Sdougm write_default_file(char *fname, struct deffile *defs) 25453910Sdougm { 25463910Sdougm FILE *file; 25473910Sdougm int ret = SA_OK; 25483910Sdougm sigset_t old, new; 25493910Sdougm 25503910Sdougm file = fopen(fname, "w+"); 25513910Sdougm if (file != NULL) { 25524345Sdougm (void) sigprocmask(SIG_BLOCK, NULL, &new); 25534345Sdougm (void) sigaddset(&new, SIGHUP); 25544345Sdougm (void) sigaddset(&new, SIGINT); 25554345Sdougm (void) sigaddset(&new, SIGQUIT); 25564345Sdougm (void) sigaddset(&new, SIGTSTP); 25574345Sdougm (void) sigprocmask(SIG_SETMASK, &new, &old); 25584345Sdougm while (defs != NULL) { 25594345Sdougm (void) fputs(defs->line, file); 25604345Sdougm defs = defs->next; 25614345Sdougm } 25624345Sdougm (void) fsync(fileno(file)); 25634345Sdougm (void) sigprocmask(SIG_SETMASK, &old, NULL); 25644345Sdougm (void) fclose(file); 25653910Sdougm } else { 25664345Sdougm switch (errno) { 25674345Sdougm case EPERM: 25684345Sdougm case EACCES: 25694345Sdougm ret = SA_NO_PERMISSION; 25704345Sdougm break; 25714345Sdougm default: 25724345Sdougm ret = SA_SYSTEM_ERR; 25734345Sdougm } 25743910Sdougm } 25753910Sdougm return (ret); 25763910Sdougm } 25773910Sdougm 25783910Sdougm 25793910Sdougm /* 25803910Sdougm * set_default_file_value(tag, value) 25813910Sdougm * 25823910Sdougm * Set the default file value for tag to value. Then rewrite the file. 25833910Sdougm * tag and value are always set. The caller must ensure this. 25843910Sdougm */ 25853910Sdougm 25863910Sdougm #define MAX_STRING_LENGTH 256 25873910Sdougm static int 25883910Sdougm set_default_file_value(char *tag, char *value) 25893910Sdougm { 25903910Sdougm int ret = SA_OK; 25913910Sdougm struct deffile *root; 25923910Sdougm struct deffile *defs; 25933910Sdougm struct deffile *prev; 25943910Sdougm char string[MAX_STRING_LENGTH]; 25953910Sdougm int len; 25963910Sdougm int update = 0; 25973910Sdougm 25983910Sdougm (void) snprintf(string, MAX_STRING_LENGTH, "%s=", tag); 25993910Sdougm len = strlen(string); 26003910Sdougm 26013910Sdougm root = defs = read_default_file(NFSADMIN); 26023910Sdougm if (root == NULL) { 26034345Sdougm if (errno == EPERM || errno == EACCES) 26044345Sdougm ret = SA_NO_PERMISSION; 26054345Sdougm else 26064345Sdougm ret = SA_SYSTEM_ERR; 26073910Sdougm } else { 26083910Sdougm while (defs != NULL) { 26094345Sdougm if (defs->line != NULL && 26104345Sdougm strncasecmp(defs->line, string, len) == 0) { 26114345Sdougm /* replace with the new value */ 26124345Sdougm free(defs->line); 26134345Sdougm fixcaseupper(tag); 26144345Sdougm (void) snprintf(string, sizeof (string), 26153910Sdougm "%s=%s\n", tag, value); 26164345Sdougm string[MAX_STRING_LENGTH - 1] = '\0'; 26174345Sdougm defs->line = strdup(string); 26184345Sdougm update = 1; 26194345Sdougm break; 26204345Sdougm } 26214345Sdougm defs = defs->next; 26223910Sdougm } 26234345Sdougm if (!update) { 26244345Sdougm defs = root; 26254345Sdougm /* didn't find, so see if it is a comment */ 26264345Sdougm (void) snprintf(string, MAX_STRING_LENGTH, "#%s=", tag); 26274345Sdougm len = strlen(string); 26284345Sdougm while (defs != NULL) { 26294345Sdougm if (strncasecmp(defs->line, string, len) == 0) { 26304345Sdougm /* replace with the new value */ 26314345Sdougm free(defs->line); 26324345Sdougm fixcaseupper(tag); 26334345Sdougm (void) snprintf(string, sizeof (string), 26344345Sdougm "%s=%s\n", tag, value); 26354345Sdougm string[MAX_STRING_LENGTH - 1] = '\0'; 26364345Sdougm defs->line = strdup(string); 26374345Sdougm update = 1; 26384345Sdougm break; 26394345Sdougm } 26404345Sdougm defs = defs->next; 26414345Sdougm } 26423910Sdougm } 26434345Sdougm if (!update) { 26444345Sdougm fixcaseupper(tag); 26454345Sdougm (void) snprintf(string, sizeof (string), "%s=%s\n", 26464345Sdougm tag, value); 26474345Sdougm prev = root; 26484345Sdougm while (prev->next != NULL) 26494345Sdougm prev = prev->next; 26504345Sdougm defs = malloc(sizeof (struct deffile)); 26514345Sdougm prev->next = defs; 26524345Sdougm if (defs != NULL) { 26534345Sdougm defs->next = NULL; 26544345Sdougm defs->line = strdup(string); 26554345Sdougm } 26564345Sdougm } 26574345Sdougm if (update) { 26584345Sdougm ret = write_default_file(NFSADMIN, root); 26594345Sdougm } 26604345Sdougm free_default_file(root); 26613910Sdougm } 26623910Sdougm return (ret); 26633910Sdougm } 26643910Sdougm 26653910Sdougm /* 26663910Sdougm * service_in_state(service, chkstate) 26673910Sdougm * 26683910Sdougm * Want to know if the specified service is in the desired state 26693910Sdougm * (chkstate) or not. Return true (1) if it is and false (0) if it 26703910Sdougm * isn't. 26713910Sdougm */ 26723910Sdougm static int 26733910Sdougm service_in_state(char *service, const char *chkstate) 26743910Sdougm { 26753910Sdougm char *state; 26763910Sdougm int ret = B_FALSE; 26773910Sdougm 26783910Sdougm state = smf_get_state(service); 26793910Sdougm if (state != NULL) { 26804345Sdougm /* got the state so get the equality for the return value */ 26814345Sdougm ret = strcmp(state, chkstate) == 0 ? B_TRUE : B_FALSE; 26824345Sdougm free(state); 26833910Sdougm } 26843910Sdougm return (ret); 26853910Sdougm } 26863910Sdougm 26873910Sdougm /* 26883910Sdougm * restart_service(svcs) 26893910Sdougm * 26903910Sdougm * Walk through the bit mask of services that need to be restarted in 26913910Sdougm * order to use the new property values. Some properties affect 26923910Sdougm * multiple daemons. Should only restart a service if it is currently 26933910Sdougm * enabled (online). 26943910Sdougm */ 26953910Sdougm 26963910Sdougm static void 26973910Sdougm restart_service(uint32_t svcs) 26983910Sdougm { 26993910Sdougm uint32_t mask; 27003910Sdougm int ret; 27013910Sdougm char *service; 27023910Sdougm 27033910Sdougm for (mask = 1; svcs != 0; mask <<= 1) { 27044345Sdougm switch (svcs & mask) { 27054345Sdougm case SVC_LOCKD: 27064345Sdougm service = LOCKD; 27074345Sdougm break; 27084345Sdougm case SVC_STATD: 27094345Sdougm service = STATD; 27104345Sdougm break; 27114345Sdougm case SVC_NFSD: 27124345Sdougm service = NFSD; 27134345Sdougm break; 27144345Sdougm case SVC_MOUNTD: 27154345Sdougm service = MOUNTD; 27164345Sdougm break; 27174345Sdougm case SVC_NFS4CBD: 27184345Sdougm service = NFS4CBD; 27194345Sdougm break; 27204345Sdougm case SVC_NFSMAPID: 27214345Sdougm service = NFSMAPID; 27224345Sdougm break; 27234345Sdougm case SVC_RQUOTAD: 27244345Sdougm service = RQUOTAD; 27254345Sdougm break; 27264345Sdougm case SVC_NFSLOGD: 27274345Sdougm service = NFSLOGD; 27284345Sdougm break; 27294345Sdougm default: 27304345Sdougm continue; 27314345Sdougm } 27323910Sdougm 27333910Sdougm /* 27343910Sdougm * Only attempt to restart the service if it is 27353910Sdougm * currently running. In the future, it may be 27363910Sdougm * desirable to use smf_refresh_instance if the NFS 27373910Sdougm * services ever implement the refresh method. 27383910Sdougm */ 27394345Sdougm if (service_in_state(service, SCF_STATE_STRING_ONLINE)) { 27404345Sdougm ret = smf_restart_instance(service); 27413910Sdougm /* 27424345Sdougm * There are only a few SMF errors at this point, but 27434345Sdougm * it is also possible that a bad value may have put 27444345Sdougm * the service into maintenance if there wasn't an 27454345Sdougm * SMF level error. 27463910Sdougm */ 27474345Sdougm if (ret != 0) { 27484345Sdougm (void) fprintf(stderr, 27494345Sdougm dgettext(TEXT_DOMAIN, 27504345Sdougm "%s failed to restart: %s\n"), 27514345Sdougm scf_strerror(scf_error())); 27524345Sdougm } else { 27534345Sdougm /* 27544345Sdougm * Check whether it has gone to "maintenance" 27554345Sdougm * mode or not. Maintenance implies something 27564345Sdougm * went wrong. 27574345Sdougm */ 27584345Sdougm if (service_in_state(service, 27594345Sdougm SCF_STATE_STRING_MAINT)) { 27604345Sdougm (void) fprintf(stderr, 27614345Sdougm dgettext(TEXT_DOMAIN, 27624345Sdougm "%s failed to restart\n"), 27634345Sdougm service); 27644345Sdougm } 27654345Sdougm } 27663910Sdougm } 27674345Sdougm svcs &= ~mask; 27683910Sdougm } 27693910Sdougm } 27703910Sdougm 27713910Sdougm /* 27723910Sdougm * nfs_minmax_check(name, value) 27733910Sdougm * 27743910Sdougm * Verify that the value for the property specified by index is valid 27753910Sdougm * relative to the opposite value in the case of a min/max variable. 27763910Sdougm * Currently, server_minvers/server_maxvers and 27773910Sdougm * client_minvers/client_maxvers are the only ones to check. 27783910Sdougm */ 27793910Sdougm 27803910Sdougm static int 27813910Sdougm nfs_minmax_check(int index, int value) 27823910Sdougm { 27833910Sdougm int val; 27843910Sdougm char *pval; 27853910Sdougm sa_property_t prop; 27863910Sdougm sa_optionset_t opts; 27873910Sdougm int ret = B_TRUE; 27883910Sdougm 27893910Sdougm if (proto_options[index].other != NULL) { 27904345Sdougm /* have a property to compare against */ 27914345Sdougm opts = nfs_get_proto_set(); 27924345Sdougm prop = sa_get_property(opts, proto_options[index].other); 27933910Sdougm /* 27943910Sdougm * If we don't find the property, assume default 27953910Sdougm * values which will work since the max will be at the 27963910Sdougm * max and the min at the min. 27973910Sdougm */ 27984345Sdougm if (prop != NULL) { 27994345Sdougm pval = sa_get_property_attr(prop, "value"); 28004345Sdougm if (pval != NULL) { 28014345Sdougm val = strtoul(pval, NULL, 0); 28024345Sdougm if (proto_options[index].compare == 28034345Sdougm OPT_CMP_LE) { 28044345Sdougm ret = value <= val ? B_TRUE : B_FALSE; 28054345Sdougm } else if (proto_options[index].compare == 28064345Sdougm OPT_CMP_GE) { 28074345Sdougm ret = value >= val ? B_TRUE : B_FALSE; 28084345Sdougm } 28094345Sdougm } 28103910Sdougm } 28113910Sdougm } 28123910Sdougm return (ret); 28133910Sdougm } 28143910Sdougm 28153910Sdougm /* 28163910Sdougm * nfs_validate_proto_prop(index, name, value) 28173910Sdougm * 2818*5331Samw * Verify that the property specified by name can take the new 28193910Sdougm * value. This is a sanity check to prevent bad values getting into 28203910Sdougm * the default files. All values need to be checked against what is 28213910Sdougm * allowed by their defined type. If a type isn't explicitly defined 28223910Sdougm * here, it is treated as a string. 28233910Sdougm * 28243910Sdougm * Note that OPT_TYPE_NUMBER will additionally check that the value is 28253910Sdougm * within the range specified and potentially against another property 28263910Sdougm * value as well as specified in the proto_options members other and 28273910Sdougm * compare. 28283910Sdougm */ 28293910Sdougm 28303910Sdougm static int 28313910Sdougm nfs_validate_proto_prop(int index, char *name, char *value) 28323910Sdougm { 28333910Sdougm int ret = SA_OK; 28343910Sdougm char *cp; 28353910Sdougm #ifdef lint 28363910Sdougm name = name; 28373910Sdougm #endif 28383910Sdougm 28393910Sdougm switch (proto_options[index].type) { 28403910Sdougm case OPT_TYPE_NUMBER: 28414345Sdougm if (!is_a_number(value)) 28424345Sdougm ret = SA_BAD_VALUE; 28434345Sdougm else { 28444345Sdougm int val; 28454345Sdougm val = strtoul(value, NULL, 0); 28464345Sdougm if (val < proto_options[index].minval || 28474345Sdougm val > proto_options[index].maxval) 28484345Sdougm ret = SA_BAD_VALUE; 28494345Sdougm /* 28504345Sdougm * For server_versmin/server_versmax and 28514345Sdougm * client_versmin/client_versmax, the value of the 28524345Sdougm * min(max) should be checked to be correct relative 28534345Sdougm * to the current max(min). 28544345Sdougm */ 28554345Sdougm if (!nfs_minmax_check(index, val)) { 28564345Sdougm ret = SA_BAD_VALUE; 28574345Sdougm } 28583910Sdougm } 28594345Sdougm break; 28603910Sdougm 28613910Sdougm case OPT_TYPE_DOMAIN: 28623910Sdougm /* 28633910Sdougm * needs to be a qualified domain so will have at 28643910Sdougm * least one period and other characters on either 28653910Sdougm * side of it. A zero length string is also allowed 28663910Sdougm * and is the way to turn off the override. 28673910Sdougm */ 28684345Sdougm if (strlen(value) == 0) 28694345Sdougm break; 28704345Sdougm cp = strchr(value, '.'); 28714345Sdougm if (cp == NULL || cp == value || strchr(value, '@') != NULL) 28724345Sdougm ret = SA_BAD_VALUE; 28733910Sdougm break; 28743910Sdougm 28753910Sdougm case OPT_TYPE_BOOLEAN: 28764345Sdougm if (strlen(value) == 0 || 28774345Sdougm strcasecmp(value, "true") == 0 || 28784345Sdougm strcmp(value, "1") == 0 || 28794345Sdougm strcasecmp(value, "false") == 0 || 28804345Sdougm strcmp(value, "0") == 0) { 28814345Sdougm ret = SA_OK; 28824345Sdougm } else { 28834345Sdougm ret = SA_BAD_VALUE; 28844345Sdougm } 28854345Sdougm break; 28863910Sdougm 28873910Sdougm case OPT_TYPE_ONOFF: 28884345Sdougm if (strcasecmp(value, "on") != 0 && 28894345Sdougm strcasecmp(value, "off") != 0) { 28904345Sdougm ret = SA_BAD_VALUE; 28914345Sdougm } 28924345Sdougm break; 28933910Sdougm 28943910Sdougm case OPT_TYPE_PROTOCOL: 28954345Sdougm if (strcasecmp(value, "all") != 0 && 28964345Sdougm strcasecmp(value, "tcp") != 0 && 28974345Sdougm strcasecmp(value, "udp") != 0) 28984345Sdougm ret = SA_BAD_VALUE; 28994345Sdougm break; 29003910Sdougm 29013910Sdougm default: 29024345Sdougm /* treat as a string */ 29034345Sdougm break; 29043910Sdougm } 29053910Sdougm return (ret); 29063910Sdougm } 29073910Sdougm 29083910Sdougm /* 29093910Sdougm * nfs_set_proto_prop(prop) 29103910Sdougm * 29113910Sdougm * check that prop is valid. 29123910Sdougm */ 29133910Sdougm 29143910Sdougm static int 29153910Sdougm nfs_set_proto_prop(sa_property_t prop) 29163910Sdougm { 29173910Sdougm int ret = SA_OK; 29183910Sdougm char *name; 29193910Sdougm char *value; 29203910Sdougm 29213910Sdougm name = sa_get_property_attr(prop, "type"); 29223910Sdougm value = sa_get_property_attr(prop, "value"); 29233910Sdougm if (name != NULL && value != NULL) { 29244345Sdougm int index = findprotoopt(name, 1); 29254345Sdougm if (index >= 0) { 29264345Sdougm /* should test for valid value */ 29274345Sdougm ret = nfs_validate_proto_prop(index, name, value); 29284345Sdougm if (ret == SA_OK) 29294345Sdougm ret = set_default_file_value( 29304345Sdougm proto_options[index].tag, value); 29314345Sdougm if (ret == SA_OK) 29324345Sdougm restart_service(proto_options[index].svcs); 29334345Sdougm } 29343910Sdougm } 29353910Sdougm if (name != NULL) 29364345Sdougm sa_free_attr_string(name); 29373910Sdougm if (value != NULL) 29384345Sdougm sa_free_attr_string(value); 29393910Sdougm return (ret); 29403910Sdougm } 29413910Sdougm 29423910Sdougm /* 29433910Sdougm * nfs_get_status() 29443910Sdougm * 29453910Sdougm * What is the current status of the nfsd? We use the SMF state here. 29463910Sdougm * Caller must free the returned value. 29473910Sdougm */ 29483910Sdougm 29493910Sdougm static char * 29503910Sdougm nfs_get_status() 29513910Sdougm { 29523910Sdougm char *state; 29533910Sdougm state = smf_get_state(NFSD); 29543910Sdougm return (state != NULL ? state : strdup("-")); 29553910Sdougm } 29563910Sdougm 29573910Sdougm /* 29583910Sdougm * nfs_space_alias(alias) 29593910Sdougm * 29603910Sdougm * Lookup the space (security) name. If it is default, convert to the 29613910Sdougm * real name. 29623910Sdougm */ 29633910Sdougm 29643910Sdougm static char * 29653910Sdougm nfs_space_alias(char *space) 29663910Sdougm { 29673910Sdougm char *name = space; 29683910Sdougm seconfig_t secconf; 29693910Sdougm 29703910Sdougm /* 29713910Sdougm * Only the space named "default" is special. If it is used, 29723910Sdougm * the default needs to be looked up and the real name used. 29733910Sdougm * This is normally "sys" but could be changed. We always 29743910Sdougm * change defautl to the real name. 29753910Sdougm */ 29763910Sdougm if (strcmp(space, "default") == 0 && 29773910Sdougm nfs_getseconfig_default(&secconf) == 0) { 29784345Sdougm if (nfs_getseconfig_bynumber(secconf.sc_nfsnum, &secconf) == 0) 29794345Sdougm name = secconf.sc_name; 29803910Sdougm } 29813910Sdougm return (strdup(name)); 29823910Sdougm } 2983*5331Samw 2984*5331Samw /* 2985*5331Samw * nfs_features() 2986*5331Samw * 2987*5331Samw * Return a mask of the features required. 2988*5331Samw */ 2989*5331Samw 2990*5331Samw static uint64_t 2991*5331Samw nfs_features() 2992*5331Samw { 2993*5331Samw return ((uint64_t)SA_FEATURE_DFSTAB); 2994*5331Samw } 2995