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 /* 235800Sdougm * Copyright 2008 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 *); 765331Samw 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, 995331Samw NULL, /* update_legacy */ 1005331Samw NULL, /* delete_legacy */ 1015331Samw NULL, /* change_notify */ 1025331Samw NULL, /* enable_resource */ 1035331Samw NULL, /* disable_resource */ 1045331Samw nfs_features, 1055331Samw NULL, /* transient shares */ 1065331Samw NULL, /* notify resource */ 1076007Sthurlow NULL, /* rename_resource */ 1086007Sthurlow NULL, /* run_command */ 1096007Sthurlow NULL, /* command_help */ 1106007Sthurlow NULL /* delete_proto_section */ 1113910Sdougm }; 1123910Sdougm 1133910Sdougm /* 1143910Sdougm * list of support services needed 1153910Sdougm * defines should come from head/rpcsvc/daemon_utils.h 1163910Sdougm */ 1173910Sdougm 1183910Sdougm static char *service_list_default[] = 1193910Sdougm { STATD, LOCKD, MOUNTD, NFSD, NFSMAPID, RQUOTAD, NULL }; 1203910Sdougm static char *service_list_logging[] = 1213910Sdougm { STATD, LOCKD, MOUNTD, NFSD, NFSMAPID, RQUOTAD, NFSLOGD, NULL }; 1223910Sdougm 1233910Sdougm /* 1243910Sdougm * option definitions. Make sure to keep the #define for the option 1253910Sdougm * index just before the entry it is the index for. Changing the order 1263910Sdougm * can cause breakage. E.g OPT_RW is index 1 and must precede the 1273910Sdougm * line that includes the SHOPT_RW and OPT_RW entries. 1283910Sdougm */ 1293910Sdougm 1303910Sdougm struct option_defs optdefs[] = { 1313910Sdougm #define OPT_RO 0 1323910Sdougm {SHOPT_RO, OPT_RO, OPT_TYPE_ACCLIST}, 1333910Sdougm #define OPT_RW 1 1343910Sdougm {SHOPT_RW, OPT_RW, OPT_TYPE_ACCLIST}, 1353910Sdougm #define OPT_ROOT 2 1363910Sdougm {SHOPT_ROOT, OPT_ROOT, OPT_TYPE_ACCLIST}, 1373910Sdougm #define OPT_SECURE 3 1383910Sdougm {SHOPT_SECURE, OPT_SECURE, OPT_TYPE_DEPRECATED}, 1393910Sdougm #define OPT_ANON 4 1403910Sdougm {SHOPT_ANON, OPT_ANON, OPT_TYPE_USER}, 1413910Sdougm #define OPT_WINDOW 5 1423910Sdougm {SHOPT_WINDOW, OPT_WINDOW, OPT_TYPE_NUMBER}, 1433910Sdougm #define OPT_NOSUID 6 1443910Sdougm {SHOPT_NOSUID, OPT_NOSUID, OPT_TYPE_BOOLEAN}, 1453910Sdougm #define OPT_ACLOK 7 1463910Sdougm {SHOPT_ACLOK, OPT_ACLOK, OPT_TYPE_BOOLEAN}, 1473910Sdougm #define OPT_NOSUB 8 1483910Sdougm {SHOPT_NOSUB, OPT_NOSUB, OPT_TYPE_BOOLEAN}, 1493910Sdougm #define OPT_SEC 9 1503910Sdougm {SHOPT_SEC, OPT_SEC, OPT_TYPE_SECURITY}, 1513910Sdougm #define OPT_PUBLIC 10 1523910Sdougm {SHOPT_PUBLIC, OPT_PUBLIC, OPT_TYPE_BOOLEAN, OPT_SHARE_ONLY}, 1533910Sdougm #define OPT_INDEX 11 1543910Sdougm {SHOPT_INDEX, OPT_INDEX, OPT_TYPE_FILE}, 1553910Sdougm #define OPT_LOG 12 1563910Sdougm {SHOPT_LOG, OPT_LOG, OPT_TYPE_LOGTAG}, 1573910Sdougm #define OPT_CKSUM 13 1583910Sdougm {SHOPT_CKSUM, OPT_CKSUM, OPT_TYPE_STRINGSET}, 1593910Sdougm #ifdef VOLATILE_FH_TEST /* XXX added for testing volatile fh's only */ 1603910Sdougm #define OPT_VOLFH 14 1613910Sdougm {SHOPT_VOLFH, OPT_VOLFH}, 1623910Sdougm #endif /* VOLATILE_FH_TEST */ 1633910Sdougm NULL 1643910Sdougm }; 1653910Sdougm 1663910Sdougm /* 1673910Sdougm * list of properties that are related to security flavors. 1683910Sdougm */ 1693910Sdougm static char *seclist[] = { 1703910Sdougm SHOPT_RO, 1713910Sdougm SHOPT_RW, 1723910Sdougm SHOPT_ROOT, 1733910Sdougm SHOPT_WINDOW, 1743910Sdougm NULL 1753910Sdougm }; 1763910Sdougm 1773910Sdougm /* structure for list of securities */ 1783910Sdougm struct securities { 1793910Sdougm sa_security_t security; 1803910Sdougm struct securities *next; 1813910Sdougm }; 1823910Sdougm 1833910Sdougm /* 1843910Sdougm * findopt(name) 1853910Sdougm * 1863910Sdougm * Lookup option "name" in the option table and return the table 1873910Sdougm * index. 1883910Sdougm */ 1893910Sdougm 1903910Sdougm static int 1913910Sdougm findopt(char *name) 1923910Sdougm { 1933910Sdougm int i; 1943910Sdougm if (name != NULL) { 1954345Sdougm for (i = 0; optdefs[i].tag != NULL; i++) { 1964345Sdougm if (strcmp(optdefs[i].tag, name) == 0) 1974345Sdougm return (i); 1984345Sdougm } 1993910Sdougm } 2003910Sdougm return (-1); 2013910Sdougm } 2023910Sdougm 2033910Sdougm /* 2043910Sdougm * gettype(name) 2053910Sdougm * 2063910Sdougm * Return the type of option "name". 2073910Sdougm */ 2083910Sdougm 2093910Sdougm static int 2103910Sdougm gettype(char *name) 2113910Sdougm { 2123910Sdougm int optdef; 2133910Sdougm 2143910Sdougm optdef = findopt(name); 2153910Sdougm if (optdef != -1) 2164345Sdougm return (optdefs[optdef].type); 2173910Sdougm return (OPT_TYPE_ANY); 2183910Sdougm } 2193910Sdougm 2203910Sdougm /* 2213910Sdougm * nfs_validate_security_mode(mode) 2223910Sdougm * 2233910Sdougm * is the specified mode string a valid one for use with NFS? 2243910Sdougm */ 2253910Sdougm 2263910Sdougm static int 2273910Sdougm nfs_validate_security_mode(char *mode) 2283910Sdougm { 2293910Sdougm seconfig_t secinfo; 2303910Sdougm int err; 2313910Sdougm 2323910Sdougm (void) memset(&secinfo, '\0', sizeof (secinfo)); 2333910Sdougm err = nfs_getseconfig_byname(mode, &secinfo); 2343910Sdougm if (err == SC_NOERROR) 2354345Sdougm return (1); 2363910Sdougm return (0); 2373910Sdougm } 2383910Sdougm 2393910Sdougm /* 2403910Sdougm * nfs_is_security_opt(tok) 2413910Sdougm * 2423910Sdougm * check to see if tok represents an option that is only valid in some 2433910Sdougm * security flavor. 2443910Sdougm */ 2453910Sdougm 2463910Sdougm static int 2473910Sdougm nfs_is_security_opt(char *tok) 2483910Sdougm { 2493910Sdougm int i; 2503910Sdougm 2513910Sdougm for (i = 0; seclist[i] != NULL; i++) { 2524345Sdougm if (strcmp(tok, seclist[i]) == 0) 2534345Sdougm return (1); 2543910Sdougm } 2553910Sdougm return (0); 2563910Sdougm } 2573910Sdougm 2583910Sdougm /* 2593910Sdougm * find_security(seclist, sec) 2603910Sdougm * 2613910Sdougm * Walk the current list of security flavors and return true if it is 2623910Sdougm * present, else return false. 2633910Sdougm */ 2643910Sdougm 2653910Sdougm static int 2663910Sdougm find_security(struct securities *seclist, sa_security_t sec) 2673910Sdougm { 2683910Sdougm while (seclist != NULL) { 2694345Sdougm if (seclist->security == sec) 2704345Sdougm return (1); 2714345Sdougm seclist = seclist->next; 2723910Sdougm } 2733910Sdougm return (0); 2743910Sdougm } 2753910Sdougm 2763910Sdougm /* 2773910Sdougm * make_security_list(group, securitymodes, proto) 2783910Sdougm * go through the list of securitymodes and add them to the 2793910Sdougm * group's list of security optionsets. We also keep a list of 2803910Sdougm * those optionsets so we don't have to find them later. All of 2813910Sdougm * these will get copies of the same properties. 2823910Sdougm */ 2833910Sdougm 2843910Sdougm static struct securities * 2853910Sdougm make_security_list(sa_group_t group, char *securitymodes, char *proto) 2863910Sdougm { 2873910Sdougm char *tok, *next = NULL; 2883910Sdougm struct securities *curp, *headp = NULL, *prev; 2893910Sdougm sa_security_t check; 2903910Sdougm int freetok = 0; 2913910Sdougm 2923910Sdougm for (tok = securitymodes; tok != NULL; tok = next) { 2934345Sdougm next = strchr(tok, ':'); 2944345Sdougm if (next != NULL) 2954345Sdougm *next++ = '\0'; 2964345Sdougm if (strcmp(tok, "default") == 0) { 2974345Sdougm /* resolve default into the real type */ 2984345Sdougm tok = nfs_space_alias(tok); 2994345Sdougm freetok = 1; 3004345Sdougm } 3014345Sdougm check = sa_get_security(group, tok, proto); 3023910Sdougm 3034345Sdougm /* add to the security list if it isn't there already */ 3044345Sdougm if (check == NULL || !find_security(headp, check)) { 3054345Sdougm curp = (struct securities *)calloc(1, 3064345Sdougm sizeof (struct securities)); 3074345Sdougm if (curp != NULL) { 3084345Sdougm if (check == NULL) { 3094345Sdougm curp->security = sa_create_security( 3104345Sdougm group, tok, proto); 3114345Sdougm } else { 3124345Sdougm curp->security = check; 3134345Sdougm } 3144345Sdougm /* 3154345Sdougm * note that the first time through the loop, 3164345Sdougm * headp will be NULL and prev will be 3174345Sdougm * undefined. Since headp is NULL, we set 3184345Sdougm * both it and prev to the curp (first 3194345Sdougm * structure to be allocated). 3204345Sdougm * 3214345Sdougm * later passes through the loop will have 3224345Sdougm * headp not being NULL and prev will be used 3234345Sdougm * to allocate at the end of the list. 3244345Sdougm */ 3254345Sdougm if (headp == NULL) { 3264345Sdougm headp = curp; 3274345Sdougm prev = curp; 3284345Sdougm } else { 3294345Sdougm prev->next = curp; 3304345Sdougm prev = curp; 3314345Sdougm } 3324345Sdougm } 3333910Sdougm } 3343910Sdougm 3354345Sdougm if (freetok) { 3364345Sdougm freetok = 0; 3374345Sdougm sa_free_attr_string(tok); 3384345Sdougm } 3393910Sdougm } 3403910Sdougm return (headp); 3413910Sdougm } 3423910Sdougm 3433910Sdougm static void 3443910Sdougm free_security_list(struct securities *sec) 3453910Sdougm { 3463910Sdougm struct securities *next; 3473910Sdougm if (sec != NULL) { 3484345Sdougm for (next = sec->next; sec != NULL; sec = next) { 3494345Sdougm next = sec->next; 3504345Sdougm free(sec); 3514345Sdougm } 3523910Sdougm } 3533910Sdougm } 3543910Sdougm 3553910Sdougm /* 3563910Sdougm * nfs_alistcat(str1, str2, sep) 3573910Sdougm * 3583910Sdougm * concatenate str1 and str2 into a new string using sep as a separate 3593910Sdougm * character. If memory allocation fails, return NULL; 3603910Sdougm */ 3613910Sdougm 3623910Sdougm static char * 3633910Sdougm nfs_alistcat(char *str1, char *str2, char sep) 3643910Sdougm { 3653910Sdougm char *newstr; 3663910Sdougm size_t len; 3673910Sdougm 3683910Sdougm len = strlen(str1) + strlen(str2) + 2; 3693910Sdougm newstr = (char *)malloc(len); 3703910Sdougm if (newstr != NULL) 3714345Sdougm (void) snprintf(newstr, len, "%s%c%s", str1, sep, str2); 3723910Sdougm return (newstr); 3733910Sdougm } 3743910Sdougm 3753910Sdougm /* 3763910Sdougm * add_security_prop(sec, name, value, persist) 3773910Sdougm * 3783910Sdougm * Add the property to the securities structure. This accumulates 3793910Sdougm * properties for as part of parsing legacy options. 3803910Sdougm */ 3813910Sdougm 3823910Sdougm static int 3833910Sdougm add_security_prop(struct securities *sec, char *name, char *value, 3843910Sdougm int persist, int iszfs) 3853910Sdougm { 3863910Sdougm sa_property_t prop; 3873910Sdougm int ret = SA_OK; 3883910Sdougm 3893910Sdougm for (; sec != NULL; sec = sec->next) { 3904345Sdougm if (value == NULL) { 3914345Sdougm if (strcmp(name, SHOPT_RW) == 0 || 3924345Sdougm strcmp(name, SHOPT_RO) == 0) 3934345Sdougm value = "*"; 3944345Sdougm else 3954345Sdougm value = "true"; 3964345Sdougm } 3973910Sdougm 3983910Sdougm /* 3993910Sdougm * Get the existing property, if it exists, so we can 4003910Sdougm * determine what to do with it. The ro/rw/root 4013910Sdougm * properties can be merged if multiple instances of 4023910Sdougm * these properies are given. For example, if "rw" 4033910Sdougm * exists with a value "host1" and a later token of 4043910Sdougm * rw="host2" is seen, the values are merged into a 4053910Sdougm * single rw="host1:host2". 4063910Sdougm */ 4074345Sdougm prop = sa_get_property(sec->security, name); 4083910Sdougm 4094345Sdougm if (prop != NULL) { 4104345Sdougm char *oldvalue; 4114345Sdougm char *newvalue; 4123910Sdougm 4133910Sdougm /* 4144345Sdougm * The security options of ro/rw/root might appear 4154345Sdougm * multiple times. If they do, the values need to be 4164345Sdougm * merged into an access list. If it was previously 4174345Sdougm * empty, the new value alone is added. 4183910Sdougm */ 4194345Sdougm oldvalue = sa_get_property_attr(prop, "value"); 4204345Sdougm if (oldvalue != NULL) { 4214345Sdougm /* 4224345Sdougm * The general case is to concatenate the new 4234345Sdougm * value onto the old value for multiple 4244345Sdougm * rw(ro/root) properties. A special case 4254345Sdougm * exists when either the old or new is the 4264345Sdougm * "all" case. In the special case, if both 4274345Sdougm * are "all", then it is "all", else if one is 4284345Sdougm * an access-list, that replaces the "all". 4294345Sdougm */ 4304345Sdougm if (strcmp(oldvalue, "*") == 0) { 4314345Sdougm /* Replace old value with new value. */ 4324345Sdougm newvalue = strdup(value); 4335454Sdougm } else if (strcmp(value, "*") == 0 || 4345454Sdougm strcmp(oldvalue, value) == 0) { 4354345Sdougm /* 4364345Sdougm * Keep old value and ignore 4374345Sdougm * the new value. 4384345Sdougm */ 4394345Sdougm newvalue = NULL; 4404345Sdougm } else { 4414345Sdougm /* 4424345Sdougm * Make a new list of old plus new 4434345Sdougm * access-list. 4444345Sdougm */ 4454345Sdougm newvalue = nfs_alistcat(oldvalue, 4464345Sdougm value, ':'); 4474345Sdougm } 4483910Sdougm 4494345Sdougm if (newvalue != NULL) { 4504345Sdougm (void) sa_remove_property(prop); 4514345Sdougm prop = sa_create_property(name, 4524345Sdougm newvalue); 4534345Sdougm ret = sa_add_property(sec->security, 4544345Sdougm prop); 4554345Sdougm free(newvalue); 4564345Sdougm } 4574345Sdougm if (oldvalue != NULL) 4584345Sdougm sa_free_attr_string(oldvalue); 4594345Sdougm } 4604345Sdougm } else { 4614345Sdougm prop = sa_create_property(name, value); 4623910Sdougm ret = sa_add_property(sec->security, prop); 4633910Sdougm } 4644345Sdougm if (ret == SA_OK && !iszfs) { 4654345Sdougm ret = sa_commit_properties(sec->security, !persist); 4664345Sdougm } 4673910Sdougm } 4683910Sdougm return (ret); 4693910Sdougm } 4703910Sdougm 4713910Sdougm /* 4723910Sdougm * check to see if group/share is persistent. 4733910Sdougm */ 4743910Sdougm static int 4753910Sdougm is_persistent(sa_group_t group) 4763910Sdougm { 4773910Sdougm char *type; 4783910Sdougm int persist = 1; 4793910Sdougm 4803910Sdougm type = sa_get_group_attr(group, "type"); 4813910Sdougm if (type != NULL && strcmp(type, "persist") != 0) 4824345Sdougm persist = 0; 4833910Sdougm if (type != NULL) 4844345Sdougm sa_free_attr_string(type); 4853910Sdougm return (persist); 4863910Sdougm } 4873910Sdougm 4883910Sdougm /* 4893910Sdougm * invalid_security(options) 4903910Sdougm * 4913910Sdougm * search option string for any invalid sec= type. 4923910Sdougm * return true (1) if any are not valid else false (0) 4933910Sdougm */ 4943910Sdougm static int 4953910Sdougm invalid_security(char *options) 4963910Sdougm { 4973910Sdougm char *copy, *base, *token, *value; 4983910Sdougm int ret = 0; 4993910Sdougm 5003910Sdougm copy = strdup(options); 5013910Sdougm token = base = copy; 5023910Sdougm while (token != NULL && ret == 0) { 5034345Sdougm token = strtok(base, ","); 5044345Sdougm base = NULL; 5054345Sdougm if (token != NULL) { 5064345Sdougm value = strchr(token, '='); 5074345Sdougm if (value != NULL) 5084345Sdougm *value++ = '\0'; 5094345Sdougm if (strcmp(token, "sec") == 0) { 5104345Sdougm /* HAVE security flavors so check them */ 5114345Sdougm char *tok, *next; 5124345Sdougm for (next = NULL, tok = value; tok != NULL; 5134345Sdougm tok = next) { 5144345Sdougm next = strchr(tok, ':'); 5154345Sdougm if (next != NULL) 5164345Sdougm *next++ = '\0'; 5174345Sdougm ret = !nfs_validate_security_mode(tok); 5184345Sdougm if (ret) 5194345Sdougm break; 5204345Sdougm } 5214345Sdougm } 5223910Sdougm } 5233910Sdougm } 5243910Sdougm if (copy != NULL) 5254345Sdougm free(copy); 5263910Sdougm return (ret); 5273910Sdougm } 5283910Sdougm 5293910Sdougm /* 5303910Sdougm * nfs_parse_legacy_options(group, options) 5313910Sdougm * 5323910Sdougm * Parse the old style options into internal format and store on the 5333910Sdougm * specified group. Group could be a share for full legacy support. 5343910Sdougm */ 5353910Sdougm 5363910Sdougm static int 5373910Sdougm nfs_parse_legacy_options(sa_group_t group, char *options) 5383910Sdougm { 5394704Sdougm char *dup; 5403910Sdougm char *base; 5413910Sdougm char *token; 5423910Sdougm sa_optionset_t optionset; 5433910Sdougm struct securities *security_list = NULL; 5443910Sdougm sa_property_t prop; 5453910Sdougm int ret = SA_OK; 5463910Sdougm int iszfs = 0; 5473910Sdougm sa_group_t parent; 5483910Sdougm int persist = 0; 5493910Sdougm char *lasts; 5503910Sdougm 5513910Sdougm /* do we have an existing optionset? */ 5523910Sdougm optionset = sa_get_optionset(group, "nfs"); 5533910Sdougm if (optionset == NULL) { 5544345Sdougm /* didn't find existing optionset so create one */ 5554345Sdougm optionset = sa_create_optionset(group, "nfs"); 5563910Sdougm } else { 5573910Sdougm /* 5585331Samw * Have an existing optionset . Ideally, we would need 5595331Samw * to compare options in order to detect errors. For 5605331Samw * now, we assume that the first optionset is the 5615331Samw * correct one and the others will be the same. An 5625331Samw * empty optionset is the same as no optionset so we 5635331Samw * don't want to exit in that case. Getting an empty 5645331Samw * optionset can occur with ZFS property checking. 5653910Sdougm */ 5665331Samw if (sa_get_property(optionset, NULL) != NULL) 5675331Samw return (ret); 5683910Sdougm } 5693910Sdougm 5703910Sdougm if (strcmp(options, SHOPT_RW) == 0) { 5713910Sdougm /* 5723910Sdougm * there is a special case of only the option "rw" 5733910Sdougm * being the default option. We don't have to do 5743910Sdougm * anything. 5753910Sdougm */ 5764345Sdougm return (ret); 5773910Sdougm } 5783910Sdougm 5793910Sdougm /* 5803910Sdougm * check if security types are present and validate them. If 5813910Sdougm * any are not legal, fail. 5823910Sdougm */ 5833910Sdougm 5843910Sdougm if (invalid_security(options)) { 5854345Sdougm return (SA_INVALID_SECURITY); 5863910Sdougm } 5873910Sdougm 5883910Sdougm /* 5893910Sdougm * in order to not attempt to change ZFS properties unless 5903910Sdougm * absolutely necessary, we never do it in the legacy parsing. 5913910Sdougm */ 5923910Sdougm if (sa_is_share(group)) { 5934345Sdougm char *zfs; 5944345Sdougm parent = sa_get_parent_group(group); 5954345Sdougm if (parent != NULL) { 5964345Sdougm zfs = sa_get_group_attr(parent, "zfs"); 5974345Sdougm if (zfs != NULL) { 5984345Sdougm sa_free_attr_string(zfs); 5994345Sdougm iszfs++; 6004345Sdougm } 6013910Sdougm } 6023910Sdougm } else { 6034345Sdougm iszfs = sa_group_is_zfs(group); 6043910Sdougm } 6053910Sdougm 6064704Sdougm /* We need a copy of options for the next part. */ 6074704Sdougm dup = strdup(options); 6084704Sdougm if (dup == NULL) 6094704Sdougm return (SA_NO_MEMORY); 6104704Sdougm 6113910Sdougm /* 6123910Sdougm * we need to step through each option in the string and then 6133910Sdougm * add either the option or the security option as needed. If 6143910Sdougm * this is not a persistent share, don't commit to the 6153910Sdougm * repository. If there is an error, we also want to abort the 6163910Sdougm * processing and report it. 6173910Sdougm */ 6183910Sdougm persist = is_persistent(group); 6193910Sdougm base = dup; 6203910Sdougm token = dup; 6213910Sdougm lasts = NULL; 6223910Sdougm while (token != NULL && ret == SA_OK) { 6234345Sdougm ret = SA_OK; 6244345Sdougm token = strtok_r(base, ",", &lasts); 6254345Sdougm base = NULL; 6264345Sdougm if (token != NULL) { 6274345Sdougm char *value; 6283910Sdougm /* 6294345Sdougm * if the option has a value, it will have an '=' to 6304345Sdougm * separate the name from the value. The following 6314345Sdougm * code will result in value != NULL and token 6324345Sdougm * pointing to just the name if there is a value. 6333910Sdougm */ 6344345Sdougm value = strchr(token, '='); 6354345Sdougm if (value != NULL) { 6364345Sdougm *value++ = '\0'; 6374345Sdougm } 6384345Sdougm if (strcmp(token, "sec") == 0 || 6394345Sdougm strcmp(token, "secure") == 0) { 6403910Sdougm /* 6414345Sdougm * Once in security parsing, we only 6424345Sdougm * do security. We do need to move 6434345Sdougm * between the security node and the 6444345Sdougm * toplevel. The security tag goes on 6454345Sdougm * the root while the following ones 6464345Sdougm * go on the security. 6473910Sdougm */ 6484345Sdougm if (security_list != NULL) { 6494345Sdougm /* 6504345Sdougm * have an old list so close it and 6514345Sdougm * start the new 6524345Sdougm */ 6534345Sdougm free_security_list(security_list); 6544345Sdougm } 6554345Sdougm if (strcmp(token, "secure") == 0) { 6564345Sdougm value = "dh"; 6574345Sdougm } else { 6584345Sdougm if (value == NULL) { 6594345Sdougm ret = SA_SYNTAX_ERR; 6604345Sdougm break; 6614345Sdougm } 6624345Sdougm } 6634345Sdougm security_list = make_security_list(group, 6644345Sdougm value, "nfs"); 6653910Sdougm } else { 6664345Sdougm /* 6674345Sdougm * Note that the "old" syntax allowed a 6684345Sdougm * default security model This must be 6694345Sdougm * accounted for and internally converted to 6704345Sdougm * "standard" security structure. 6714345Sdougm */ 6724345Sdougm if (nfs_is_security_opt(token)) { 6734345Sdougm if (security_list == NULL) { 6744345Sdougm /* 6754345Sdougm * need to have a 6764345Sdougm * security 6774345Sdougm * option. This will 6784345Sdougm * be "closed" when a 6794345Sdougm * defined "sec=" 6804345Sdougm * option is 6814345Sdougm * seen. This is 6824345Sdougm * technically an 6834345Sdougm * error but will be 6844345Sdougm * allowed with 6854345Sdougm * warning. 6864345Sdougm */ 6874345Sdougm security_list = 6884345Sdougm make_security_list(group, 6894345Sdougm "default", 6904345Sdougm "nfs"); 6914345Sdougm } 6924345Sdougm if (security_list != NULL) { 6934345Sdougm ret = add_security_prop( 6944345Sdougm security_list, token, 6954345Sdougm value, persist, iszfs); 6964345Sdougm } else { 6974345Sdougm ret = SA_NO_MEMORY; 6984345Sdougm } 6994345Sdougm } else { 7004345Sdougm /* regular options */ 7014345Sdougm if (value == NULL) { 7024345Sdougm if (strcmp(token, SHOPT_RW) == 7034345Sdougm 0 || strcmp(token, 7044345Sdougm SHOPT_RO) == 0) { 7054345Sdougm value = "*"; 7064345Sdougm } else { 7074345Sdougm value = "global"; 7084345Sdougm if (strcmp(token, 7094345Sdougm SHOPT_LOG) != 0) { 7104345Sdougm value = "true"; 7114345Sdougm } 7124345Sdougm } 7134345Sdougm } 7144372Sdougm /* 7154372Sdougm * In all cases, create the 7164372Sdougm * property specified. If the 7174372Sdougm * value was NULL, the default 7184372Sdougm * value will have been 7194372Sdougm * substituted. 7204372Sdougm */ 7214372Sdougm prop = sa_create_property(token, value); 7224372Sdougm ret = sa_add_property(optionset, prop); 7234372Sdougm if (ret != SA_OK) 7244372Sdougm break; 7254372Sdougm 7264345Sdougm if (!iszfs) { 7274345Sdougm ret = sa_commit_properties( 7284345Sdougm optionset, !persist); 7294345Sdougm } 7304345Sdougm } 7313910Sdougm } 7323910Sdougm } 7333910Sdougm } 7343910Sdougm if (security_list != NULL) 7354345Sdougm free_security_list(security_list); 7364704Sdougm 7374704Sdougm free(dup); 7383910Sdougm return (ret); 7393910Sdougm } 7403910Sdougm 7413910Sdougm /* 7423910Sdougm * is_a_number(number) 7433910Sdougm * 7443910Sdougm * is the string a number in one of the forms we want to use? 7453910Sdougm */ 7463910Sdougm 7473910Sdougm static int 7483910Sdougm is_a_number(char *number) 7493910Sdougm { 7503910Sdougm int ret = 1; 7513910Sdougm int hex = 0; 7523910Sdougm 7533910Sdougm if (strncmp(number, "0x", 2) == 0) { 7544345Sdougm number += 2; 7554345Sdougm hex = 1; 7564345Sdougm } else if (*number == '-') { 7574345Sdougm number++; /* skip the minus */ 7584345Sdougm } 7593910Sdougm while (ret == 1 && *number != '\0') { 7604345Sdougm if (hex) { 7614345Sdougm ret = isxdigit(*number++); 7624345Sdougm } else { 7634345Sdougm ret = isdigit(*number++); 7644345Sdougm } 7653910Sdougm } 7663910Sdougm return (ret); 7673910Sdougm } 7683910Sdougm 7693910Sdougm /* 7703910Sdougm * Look for the specified tag in the configuration file. If it is found, 7713910Sdougm * enable logging and set the logging configuration information for exp. 7723910Sdougm */ 7733910Sdougm static void 7743910Sdougm configlog(struct exportdata *exp, char *tag) 7753910Sdougm { 7763910Sdougm nfsl_config_t *configlist = NULL, *configp; 7773910Sdougm int error = 0; 7783910Sdougm char globaltag[] = DEFAULTTAG; 7793910Sdougm 7803910Sdougm /* 7813910Sdougm * Sends config errors to stderr 7823910Sdougm */ 7833910Sdougm nfsl_errs_to_syslog = B_FALSE; 7843910Sdougm 7853910Sdougm /* 7863910Sdougm * get the list of configuration settings 7873910Sdougm */ 7883910Sdougm error = nfsl_getconfig_list(&configlist); 7893910Sdougm if (error) { 7903910Sdougm (void) fprintf(stderr, 7914345Sdougm dgettext(TEXT_DOMAIN, "Cannot get log configuration: %s\n"), 7924345Sdougm strerror(error)); 7933910Sdougm } 7943910Sdougm 7953910Sdougm if (tag == NULL) 7963910Sdougm tag = globaltag; 7973910Sdougm if ((configp = nfsl_findconfig(configlist, tag, &error)) == NULL) { 7983910Sdougm nfsl_freeconfig_list(&configlist); 7993910Sdougm (void) fprintf(stderr, 8004345Sdougm dgettext(TEXT_DOMAIN, "No tags matching \"%s\"\n"), tag); 8013910Sdougm /* bad configuration */ 8023910Sdougm error = ENOENT; 8033910Sdougm goto err; 8043910Sdougm } 8053910Sdougm 8063910Sdougm if ((exp->ex_tag = strdup(tag)) == NULL) { 8073910Sdougm error = ENOMEM; 8083910Sdougm goto out; 8093910Sdougm } 8103910Sdougm if ((exp->ex_log_buffer = strdup(configp->nc_bufferpath)) == NULL) { 8113910Sdougm error = ENOMEM; 8123910Sdougm goto out; 8133910Sdougm } 8143910Sdougm exp->ex_flags |= EX_LOG; 8153910Sdougm if (configp->nc_rpclogpath != NULL) 8163910Sdougm exp->ex_flags |= EX_LOG_ALLOPS; 8173910Sdougm out: 8183910Sdougm if (configlist != NULL) 8194345Sdougm nfsl_freeconfig_list(&configlist); 8203910Sdougm 8213910Sdougm err: 8223910Sdougm if (error != 0) { 8233910Sdougm if (exp->ex_flags != NULL) 8243910Sdougm free(exp->ex_tag); 8253910Sdougm if (exp->ex_log_buffer != NULL) 8263910Sdougm free(exp->ex_log_buffer); 8273910Sdougm (void) fprintf(stderr, 8284345Sdougm dgettext(TEXT_DOMAIN, "Cannot set log configuration: %s\n"), 8294345Sdougm strerror(error)); 8303910Sdougm } 8313910Sdougm } 8323910Sdougm 8333910Sdougm /* 8343910Sdougm * fill_export_from_optionset(export, optionset) 8353910Sdougm * 8363910Sdougm * In order to share, we need to set all the possible general options 8373910Sdougm * into the export structure. Share info will be filled in by the 8383910Sdougm * caller. Various property values get turned into structure specific 8393910Sdougm * values. 8403910Sdougm */ 8413910Sdougm 8423910Sdougm static int 8433910Sdougm fill_export_from_optionset(struct exportdata *export, sa_optionset_t optionset) 8443910Sdougm { 8453910Sdougm sa_property_t option; 8463910Sdougm int ret = SA_OK; 8473910Sdougm 8483910Sdougm for (option = sa_get_property(optionset, NULL); 8494345Sdougm option != NULL; option = sa_get_next_property(option)) { 8504345Sdougm char *name; 8514345Sdougm char *value; 8524345Sdougm uint32_t val; 8533910Sdougm 8544345Sdougm /* 8554345Sdougm * since options may be set/reset multiple times, always do an 8564345Sdougm * explicit set or clear of the option. This allows defaults 8575331Samw * to be set and then the protocol specific to override. 8584345Sdougm */ 8593910Sdougm 8604345Sdougm name = sa_get_property_attr(option, "type"); 8614345Sdougm value = sa_get_property_attr(option, "value"); 8624345Sdougm switch (findopt(name)) { 8634345Sdougm case OPT_ANON: 8644345Sdougm if (value != NULL && is_a_number(value)) { 8654345Sdougm val = strtoul(value, NULL, 0); 8664345Sdougm } else { 8674345Sdougm struct passwd *pw; 8684345Sdougm pw = getpwnam(value != NULL ? value : "nobody"); 8694345Sdougm if (pw != NULL) { 8704345Sdougm val = pw->pw_uid; 8714345Sdougm } else { 8724345Sdougm val = UID_NOBODY; 8734345Sdougm } 8744345Sdougm endpwent(); 8754345Sdougm } 8764345Sdougm export->ex_anon = val; 8774345Sdougm break; 8784345Sdougm case OPT_NOSUID: 8794345Sdougm if (value != NULL && (strcasecmp(value, "true") == 0 || 8804345Sdougm strcmp(value, "1") == 0)) 8814345Sdougm export->ex_flags |= EX_NOSUID; 8824345Sdougm else 8834345Sdougm export->ex_flags &= ~EX_NOSUID; 8844345Sdougm break; 8854345Sdougm case OPT_ACLOK: 8864345Sdougm if (value != NULL && (strcasecmp(value, "true") == 0 || 8874345Sdougm strcmp(value, "1") == 0)) 8884345Sdougm export->ex_flags |= EX_ACLOK; 8894345Sdougm else 8904345Sdougm export->ex_flags &= ~EX_ACLOK; 8914345Sdougm break; 8924345Sdougm case OPT_NOSUB: 8934345Sdougm if (value != NULL && (strcasecmp(value, "true") == 0 || 8944345Sdougm strcmp(value, "1") == 0)) 8954345Sdougm export->ex_flags |= EX_NOSUB; 8964345Sdougm else 8974345Sdougm export->ex_flags &= ~EX_NOSUB; 8984345Sdougm break; 8994345Sdougm case OPT_PUBLIC: 9004345Sdougm if (value != NULL && (strcasecmp(value, "true") == 0 || 9014345Sdougm strcmp(value, "1") == 0)) 9024345Sdougm export->ex_flags |= EX_PUBLIC; 9034345Sdougm else 9044345Sdougm export->ex_flags &= ~EX_PUBLIC; 9054345Sdougm break; 9064345Sdougm case OPT_INDEX: 9074345Sdougm if (value != NULL && (strcmp(value, "..") == 0 || 9084345Sdougm strchr(value, '/') != NULL)) { 9094345Sdougm /* this is an error */ 9104345Sdougm (void) printf(dgettext(TEXT_DOMAIN, 9114345Sdougm "NFS: index=\"%s\" not valid;" 9124345Sdougm "must be a filename.\n"), 9134345Sdougm value); 9144345Sdougm break; 9154345Sdougm } 9164345Sdougm if (value != NULL && *value != '\0' && 9174345Sdougm strcmp(value, ".") != 0) { 9184345Sdougm /* valid index file string */ 9194345Sdougm if (export->ex_index != NULL) { 9204345Sdougm /* left over from "default" */ 9214345Sdougm free(export->ex_index); 9224345Sdougm } 9234345Sdougm /* remember to free */ 9244345Sdougm export->ex_index = strdup(value); 9254345Sdougm if (export->ex_index == NULL) { 9264345Sdougm (void) printf(dgettext(TEXT_DOMAIN, 9274345Sdougm "NFS: out of memory setting " 9284345Sdougm "index property\n")); 9294345Sdougm break; 9304345Sdougm } 9314345Sdougm export->ex_flags |= EX_INDEX; 9324345Sdougm } 9334345Sdougm break; 9344345Sdougm case OPT_LOG: 9354345Sdougm if (value == NULL) 9364345Sdougm value = strdup("global"); 9374345Sdougm if (value != NULL) 9384345Sdougm configlog(export, 9394345Sdougm strlen(value) ? value : "global"); 9404345Sdougm break; 9414345Sdougm default: 9424345Sdougm /* have a syntactic error */ 9434345Sdougm (void) printf(dgettext(TEXT_DOMAIN, 9444345Sdougm "NFS: unrecognized option %s=%s\n"), 9454345Sdougm name, value != NULL ? value : ""); 9464345Sdougm break; 9473910Sdougm } 9484345Sdougm if (name != NULL) 9494345Sdougm sa_free_attr_string(name); 9503910Sdougm if (value != NULL) 9514345Sdougm sa_free_attr_string(value); 9523910Sdougm } 9533910Sdougm return (ret); 9543910Sdougm } 9553910Sdougm 9563910Sdougm /* 9573910Sdougm * cleanup_export(export) 9583910Sdougm * 9593910Sdougm * Cleanup the allocated areas so we don't leak memory 9603910Sdougm */ 9613910Sdougm 9623910Sdougm static void 9633910Sdougm cleanup_export(struct exportdata *export) 9643910Sdougm { 9653910Sdougm int i; 9663910Sdougm 9673910Sdougm if (export->ex_index != NULL) 9684345Sdougm free(export->ex_index); 9693910Sdougm if (export->ex_secinfo != NULL) { 9704345Sdougm for (i = 0; i < export->ex_seccnt; i++) 9714345Sdougm if (export->ex_secinfo[i].s_rootnames != NULL) 9724345Sdougm free(export->ex_secinfo[i].s_rootnames); 9734345Sdougm free(export->ex_secinfo); 9743910Sdougm } 9753910Sdougm } 9763910Sdougm 9773910Sdougm /* 9783910Sdougm * Given a seconfig entry and a colon-separated 9793910Sdougm * list of names, allocate an array big enough 9803910Sdougm * to hold the root list, then convert each name to 9813910Sdougm * a principal name according to the security 9823910Sdougm * info and assign it to an array element. 9833910Sdougm * Return the array and its size. 9843910Sdougm */ 9853910Sdougm static caddr_t * 9863910Sdougm get_rootnames(seconfig_t *sec, char *list, int *count) 9873910Sdougm { 9883910Sdougm caddr_t *a; 9893910Sdougm int c, i; 9903910Sdougm char *host, *p; 9913910Sdougm 9923910Sdougm /* 9933910Sdougm * Count the number of strings in the list. 9943910Sdougm * This is the number of colon separators + 1. 9953910Sdougm */ 9963910Sdougm c = 1; 9973910Sdougm for (p = list; *p; p++) 9983910Sdougm if (*p == ':') 9993910Sdougm c++; 10003910Sdougm *count = c; 10013910Sdougm 10023910Sdougm a = (caddr_t *)malloc(c * sizeof (char *)); 10033910Sdougm if (a == NULL) { 10043910Sdougm (void) printf(dgettext(TEXT_DOMAIN, 10054345Sdougm "get_rootnames: no memory\n")); 10063910Sdougm } else { 10074345Sdougm for (i = 0; i < c; i++) { 10084345Sdougm host = strtok(list, ":"); 10094345Sdougm if (!nfs_get_root_principal(sec, host, &a[i])) { 10104345Sdougm free(a); 10114345Sdougm a = NULL; 10124345Sdougm break; 10134345Sdougm } 10144345Sdougm list = NULL; 10153910Sdougm } 10163910Sdougm } 10173910Sdougm 10183910Sdougm return (a); 10193910Sdougm } 10203910Sdougm 10213910Sdougm /* 10223910Sdougm * fill_security_from_secopts(sp, secopts) 10233910Sdougm * 10243910Sdougm * Fill the secinfo structure from the secopts optionset. 10253910Sdougm */ 10263910Sdougm 10273910Sdougm static int 10283910Sdougm fill_security_from_secopts(struct secinfo *sp, sa_security_t secopts) 10293910Sdougm { 10303910Sdougm sa_property_t prop; 10313910Sdougm char *type; 10323910Sdougm int longform; 10333910Sdougm int err = SC_NOERROR; 10343910Sdougm 10353910Sdougm type = sa_get_security_attr(secopts, "sectype"); 10363910Sdougm if (type != NULL) { 10374345Sdougm /* named security type needs secinfo to be filled in */ 10384345Sdougm err = nfs_getseconfig_byname(type, &sp->s_secinfo); 10394345Sdougm sa_free_attr_string(type); 10404345Sdougm if (err != SC_NOERROR) 10414345Sdougm return (err); 10423910Sdougm } else { 10434345Sdougm /* default case */ 10444345Sdougm err = nfs_getseconfig_default(&sp->s_secinfo); 10454345Sdougm if (err != SC_NOERROR) 10464345Sdougm return (err); 10473910Sdougm } 10483910Sdougm 10493910Sdougm err = SA_OK; 10503910Sdougm for (prop = sa_get_property(secopts, NULL); 10514345Sdougm prop != NULL && err == SA_OK; 10524345Sdougm prop = sa_get_next_property(prop)) { 10534345Sdougm char *name; 10544345Sdougm char *value; 10553910Sdougm 10564345Sdougm name = sa_get_property_attr(prop, "type"); 10574345Sdougm value = sa_get_property_attr(prop, "value"); 10583910Sdougm 10594345Sdougm longform = value != NULL && strcmp(value, "*") != 0; 10603910Sdougm 10614345Sdougm switch (findopt(name)) { 10624345Sdougm case OPT_RO: 10634345Sdougm sp->s_flags |= longform ? M_ROL : M_RO; 10644345Sdougm break; 10654345Sdougm case OPT_RW: 10664345Sdougm sp->s_flags |= longform ? M_RWL : M_RW; 10674345Sdougm break; 10684345Sdougm case OPT_ROOT: 10694345Sdougm sp->s_flags |= M_ROOT; 10704345Sdougm /* 10714345Sdougm * if we are using AUTH_UNIX, handle like other things 10724345Sdougm * such as RO/RW 10734345Sdougm */ 10744345Sdougm if (sp->s_secinfo.sc_rpcnum == AUTH_UNIX) 10754345Sdougm continue; 10764345Sdougm /* not AUTH_UNIX */ 10774345Sdougm if (value != NULL) { 10784345Sdougm sp->s_rootnames = get_rootnames(&sp->s_secinfo, 10794345Sdougm value, &sp->s_rootcnt); 10804345Sdougm if (sp->s_rootnames == NULL) { 10814345Sdougm err = SA_BAD_VALUE; 10824345Sdougm (void) fprintf(stderr, 10834345Sdougm dgettext(TEXT_DOMAIN, 10844345Sdougm "Bad root list\n")); 10854345Sdougm } 10864345Sdougm } 10874345Sdougm break; 10884345Sdougm case OPT_WINDOW: 10894345Sdougm if (value != NULL) { 10904345Sdougm sp->s_window = atoi(value); 10914345Sdougm /* just in case */ 10924345Sdougm if (sp->s_window < 0) 10934345Sdougm sp->s_window = DEF_WIN; 10944345Sdougm } 10954345Sdougm break; 10964345Sdougm default: 10974345Sdougm break; 10983910Sdougm } 10994345Sdougm if (name != NULL) 11004345Sdougm sa_free_attr_string(name); 11014345Sdougm if (value != NULL) 11024345Sdougm sa_free_attr_string(value); 11033910Sdougm } 11043910Sdougm /* if rw/ro options not set, use default of RW */ 11053910Sdougm if ((sp->s_flags & NFS_RWMODES) == 0) 11064345Sdougm sp->s_flags |= M_RW; 11073910Sdougm return (err); 11083910Sdougm } 11093910Sdougm 11103910Sdougm /* 11113910Sdougm * This is for testing only 11123910Sdougm * It displays the export structure that 11133910Sdougm * goes into the kernel. 11143910Sdougm */ 11153910Sdougm static void 11163910Sdougm printarg(char *path, struct exportdata *ep) 11173910Sdougm { 11183910Sdougm int i, j; 11193910Sdougm struct secinfo *sp; 11203910Sdougm 11213910Sdougm if (debug == 0) 11224345Sdougm return; 11233910Sdougm 11243910Sdougm (void) printf("%s:\n", path); 11253910Sdougm (void) printf("\tex_version = %d\n", ep->ex_version); 11263910Sdougm (void) printf("\tex_path = %s\n", ep->ex_path); 11273910Sdougm (void) printf("\tex_pathlen = %ld\n", (ulong_t)ep->ex_pathlen); 11283910Sdougm (void) printf("\tex_flags: (0x%02x) ", ep->ex_flags); 11293910Sdougm if (ep->ex_flags & EX_NOSUID) 11303910Sdougm (void) printf("NOSUID "); 11313910Sdougm if (ep->ex_flags & EX_ACLOK) 11323910Sdougm (void) printf("ACLOK "); 11333910Sdougm if (ep->ex_flags & EX_PUBLIC) 11343910Sdougm (void) printf("PUBLIC "); 11353910Sdougm if (ep->ex_flags & EX_NOSUB) 11363910Sdougm (void) printf("NOSUB "); 11373910Sdougm if (ep->ex_flags & EX_LOG) 11383910Sdougm (void) printf("LOG "); 11393910Sdougm if (ep->ex_flags & EX_LOG_ALLOPS) 11403910Sdougm (void) printf("LOG_ALLOPS "); 11413910Sdougm if (ep->ex_flags == 0) 11423910Sdougm (void) printf("(none)"); 11433910Sdougm (void) printf("\n"); 11443910Sdougm if (ep->ex_flags & EX_LOG) { 11453910Sdougm (void) printf("\tex_log_buffer = %s\n", 11464345Sdougm (ep->ex_log_buffer ? ep->ex_log_buffer : "(NULL)")); 11473910Sdougm (void) printf("\tex_tag = %s\n", 11484345Sdougm (ep->ex_tag ? ep->ex_tag : "(NULL)")); 11493910Sdougm } 11503910Sdougm (void) printf("\tex_anon = %d\n", ep->ex_anon); 11513910Sdougm (void) printf("\tex_seccnt = %d\n", ep->ex_seccnt); 11523910Sdougm (void) printf("\n"); 11533910Sdougm for (i = 0; i < ep->ex_seccnt; i++) { 11543910Sdougm sp = &ep->ex_secinfo[i]; 11553910Sdougm (void) printf("\t\ts_secinfo = %s\n", sp->s_secinfo.sc_name); 11563910Sdougm (void) printf("\t\ts_flags: (0x%02x) ", sp->s_flags); 11573910Sdougm if (sp->s_flags & M_ROOT) (void) printf("M_ROOT "); 11583910Sdougm if (sp->s_flags & M_RO) (void) printf("M_RO "); 11593910Sdougm if (sp->s_flags & M_ROL) (void) printf("M_ROL "); 11603910Sdougm if (sp->s_flags & M_RW) (void) printf("M_RW "); 11613910Sdougm if (sp->s_flags & M_RWL) (void) printf("M_RWL "); 11623910Sdougm if (sp->s_flags == 0) (void) printf("(none)"); 11633910Sdougm (void) printf("\n"); 11643910Sdougm (void) printf("\t\ts_window = %d\n", sp->s_window); 11653910Sdougm (void) printf("\t\ts_rootcnt = %d ", sp->s_rootcnt); 11663910Sdougm (void) fflush(stdout); 11673910Sdougm for (j = 0; j < sp->s_rootcnt; j++) 11683910Sdougm (void) printf("%s ", sp->s_rootnames[j] ? 11694345Sdougm sp->s_rootnames[j] : "<null>"); 11703910Sdougm (void) printf("\n\n"); 11713910Sdougm } 11723910Sdougm } 11733910Sdougm 11743910Sdougm /* 11753910Sdougm * count_security(opts) 11763910Sdougm * 11773910Sdougm * Count the number of security types (flavors). The optionset has 11783910Sdougm * been populated with the security flavors as a holding mechanism. 11793910Sdougm * We later use this number to allocate data structures. 11803910Sdougm */ 11813910Sdougm 11823910Sdougm static int 11833910Sdougm count_security(sa_optionset_t opts) 11843910Sdougm { 11853910Sdougm int count = 0; 11863910Sdougm sa_property_t prop; 11873910Sdougm if (opts != NULL) { 11884345Sdougm for (prop = sa_get_property(opts, NULL); prop != NULL; 11894345Sdougm prop = sa_get_next_property(prop)) { 11904345Sdougm count++; 11914345Sdougm } 11923910Sdougm } 11933910Sdougm return (count); 11943910Sdougm } 11953910Sdougm 11963910Sdougm /* 11973910Sdougm * nfs_sprint_option(rbuff, rbuffsize, incr, prop, sep) 11983910Sdougm * 11993910Sdougm * provides a mechanism to format NFS properties into legacy output 12003910Sdougm * format. If the buffer would overflow, it is reallocated and grown 12013910Sdougm * as appropriate. Special cases of converting internal form of values 12023910Sdougm * to those used by "share" are done. this function does one property 12033910Sdougm * at a time. 12043910Sdougm */ 12053910Sdougm 12065179Sdougm static int 12073910Sdougm nfs_sprint_option(char **rbuff, size_t *rbuffsize, size_t incr, 12083910Sdougm sa_property_t prop, int sep) 12093910Sdougm { 12103910Sdougm char *name; 12113910Sdougm char *value; 12123910Sdougm int curlen; 12133910Sdougm char *buff = *rbuff; 12143910Sdougm size_t buffsize = *rbuffsize; 12155179Sdougm int printed = B_FALSE; 12163910Sdougm 12173910Sdougm name = sa_get_property_attr(prop, "type"); 12183910Sdougm value = sa_get_property_attr(prop, "value"); 12193910Sdougm if (buff != NULL) 12204345Sdougm curlen = strlen(buff); 12213910Sdougm else 12224345Sdougm curlen = 0; 12233910Sdougm if (name != NULL) { 12244345Sdougm int len; 12254345Sdougm len = strlen(name) + sep; 12263910Sdougm 12273910Sdougm /* 12283910Sdougm * A future RFE would be to replace this with more 12293910Sdougm * generic code and to possibly handle more types. 12303910Sdougm */ 12314345Sdougm switch (gettype(name)) { 12324345Sdougm case OPT_TYPE_BOOLEAN: 12335179Sdougm /* 12345179Sdougm * For NFS, boolean value of FALSE means it 12355179Sdougm * doesn't show up in the option list at all. 12365179Sdougm */ 12374345Sdougm if (value != NULL && strcasecmp(value, "false") == 0) 12385179Sdougm goto skip; 12395179Sdougm if (value != NULL) { 12404345Sdougm sa_free_attr_string(value); 12415179Sdougm value = NULL; 12425179Sdougm } 12434345Sdougm break; 12444345Sdougm case OPT_TYPE_ACCLIST: 12454345Sdougm if (value != NULL && strcmp(value, "*") == 0) { 12464345Sdougm sa_free_attr_string(value); 12474345Sdougm value = NULL; 12484345Sdougm } else { 12494345Sdougm if (value != NULL) 12504345Sdougm len += 1 + strlen(value); 12514345Sdougm } 12524345Sdougm break; 12534345Sdougm case OPT_TYPE_LOGTAG: 12544345Sdougm if (value != NULL && strlen(value) == 0) { 12554345Sdougm sa_free_attr_string(value); 12564345Sdougm value = NULL; 12574345Sdougm } else { 12584345Sdougm if (value != NULL) 12594345Sdougm len += 1 + strlen(value); 12604345Sdougm } 12614345Sdougm break; 12624345Sdougm default: 12634345Sdougm if (value != NULL) 12644345Sdougm len += 1 + strlen(value); 12654345Sdougm break; 12663910Sdougm } 12674345Sdougm while (buffsize <= (curlen + len)) { 12684345Sdougm /* need more room */ 12694345Sdougm buffsize += incr; 12704345Sdougm buff = realloc(buff, buffsize); 12714345Sdougm if (buff == NULL) { 12724345Sdougm /* realloc failed so free everything */ 12734345Sdougm if (*rbuff != NULL) 12744345Sdougm free(*rbuff); 12754345Sdougm } 12764345Sdougm *rbuff = buff; 12774345Sdougm *rbuffsize = buffsize; 12785179Sdougm if (buff == NULL) 12795179Sdougm goto skip; 12805179Sdougm 12813910Sdougm } 12825179Sdougm 12834345Sdougm if (buff == NULL) 12845179Sdougm goto skip; 12855179Sdougm 12864345Sdougm if (value == NULL) { 12874345Sdougm (void) snprintf(buff + curlen, buffsize - curlen, 12884345Sdougm "%s%s", sep ? "," : "", 12894345Sdougm name, value != NULL ? value : ""); 12904345Sdougm } else { 12914345Sdougm (void) snprintf(buff + curlen, buffsize - curlen, 12924345Sdougm "%s%s=%s", sep ? "," : "", 12934345Sdougm name, value != NULL ? value : ""); 12943910Sdougm } 12955179Sdougm printed = B_TRUE; 12963910Sdougm } 12975179Sdougm skip: 12983910Sdougm if (name != NULL) 12994345Sdougm sa_free_attr_string(name); 13003910Sdougm if (value != NULL) 13014345Sdougm sa_free_attr_string(value); 13025179Sdougm return (printed); 13033910Sdougm } 13043910Sdougm 13053910Sdougm /* 13063910Sdougm * nfs_format_options(group, hier) 13073910Sdougm * 13083910Sdougm * format all the options on the group into an old-style option 13093910Sdougm * string. If hier is non-zero, walk up the tree to get inherited 13103910Sdougm * options. 13113910Sdougm */ 13123910Sdougm 13133910Sdougm static char * 13143910Sdougm nfs_format_options(sa_group_t group, int hier) 13153910Sdougm { 13163910Sdougm sa_optionset_t options = NULL; 13174345Sdougm sa_optionset_t secoptions = NULL; 13183910Sdougm sa_property_t prop, secprop; 13194345Sdougm sa_security_t security = NULL; 13203910Sdougm char *buff; 13213910Sdougm size_t buffsize; 13224345Sdougm char *sectype = NULL; 13234345Sdougm int sep = 0; 13244345Sdougm 13254345Sdougm 13264345Sdougm buff = malloc(OPT_CHUNK); 13274345Sdougm if (buff == NULL) { 13284345Sdougm return (NULL); 13294345Sdougm } 13304345Sdougm 13314345Sdougm buff[0] = '\0'; 13324345Sdougm buffsize = OPT_CHUNK; 13334345Sdougm 13344345Sdougm /* 13354345Sdougm * We may have a an optionset relative to this item. format 13364345Sdougm * these if we find them and then add any security definitions. 13374345Sdougm */ 13383910Sdougm 13393910Sdougm options = sa_get_derived_optionset(group, "nfs", hier); 13403910Sdougm 13413910Sdougm /* 13424345Sdougm * do the default set first but skip any option that is also 13434345Sdougm * in the protocol specific optionset. 13443910Sdougm */ 13454345Sdougm if (options != NULL) { 13464345Sdougm for (prop = sa_get_property(options, NULL); 13474345Sdougm prop != NULL; prop = sa_get_next_property(prop)) { 13483910Sdougm /* 13494345Sdougm * use this one since we skipped any 13504345Sdougm * of these that were also in 13514345Sdougm * optdefault 13523910Sdougm */ 13535179Sdougm if (nfs_sprint_option(&buff, &buffsize, OPT_CHUNK, 13545179Sdougm prop, sep)) 13555179Sdougm sep = 1; 13564345Sdougm if (buff == NULL) { 13574345Sdougm /* 13584345Sdougm * buff could become NULL if there 13594345Sdougm * isn't enough memory for 13604345Sdougm * nfs_sprint_option to realloc() 13614345Sdougm * as necessary. We can't really 13624345Sdougm * do anything about it at this 13634345Sdougm * point so we return NULL. The 13644345Sdougm * caller should handle the 13654345Sdougm * failure. 13664345Sdougm */ 13674345Sdougm if (options != NULL) 13684345Sdougm sa_free_derived_optionset( 13694345Sdougm options); 13704345Sdougm return (buff); 13714345Sdougm } 13723910Sdougm } 13734345Sdougm } 13744345Sdougm secoptions = (sa_optionset_t)sa_get_all_security_types(group, 13754345Sdougm "nfs", hier); 13764345Sdougm if (secoptions != NULL) { 13773910Sdougm for (secprop = sa_get_property(secoptions, NULL); 13784345Sdougm secprop != NULL; 13794345Sdougm secprop = sa_get_next_property(secprop)) { 13804345Sdougm sectype = sa_get_property_attr(secprop, "type"); 13814345Sdougm security = 13824345Sdougm (sa_security_t)sa_get_derived_security( 13834345Sdougm group, sectype, "nfs", hier); 13844345Sdougm if (security != NULL) { 13854345Sdougm if (sectype != NULL) { 13864345Sdougm prop = sa_create_property( 13874345Sdougm "sec", sectype); 13885179Sdougm if (prop == NULL) 13895179Sdougm goto err; 13905179Sdougm if (nfs_sprint_option(&buff, 13915179Sdougm &buffsize, OPT_CHUNK, prop, sep)) 13925179Sdougm sep = 1; 13934345Sdougm (void) sa_remove_property(prop); 13945179Sdougm if (buff == NULL) 13955179Sdougm goto err; 13964345Sdougm } 13974345Sdougm for (prop = sa_get_property(security, 13984345Sdougm NULL); prop != NULL; 13994345Sdougm prop = sa_get_next_property(prop)) { 14005179Sdougm if (nfs_sprint_option(&buff, 14015179Sdougm &buffsize, OPT_CHUNK, prop, sep)) 14025179Sdougm sep = 1; 14034345Sdougm if (buff == NULL) 14044345Sdougm goto err; 14054345Sdougm } 14064345Sdougm sa_free_derived_optionset(security); 14073910Sdougm } 14084345Sdougm if (sectype != NULL) 14094345Sdougm sa_free_attr_string(sectype); 14103910Sdougm } 14113910Sdougm sa_free_derived_optionset(secoptions); 14123910Sdougm } 14134345Sdougm 14143910Sdougm if (options != NULL) 14154345Sdougm sa_free_derived_optionset(options); 14164345Sdougm return (buff); 14174345Sdougm 14184345Sdougm err: 14194345Sdougm /* 14204345Sdougm * If we couldn't allocate memory for option printing, we need 14214345Sdougm * to break out of the nested loops, cleanup and return NULL. 14224345Sdougm */ 14234345Sdougm if (secoptions != NULL) 14244345Sdougm sa_free_derived_optionset(secoptions); 14254345Sdougm if (security != NULL) 14264345Sdougm sa_free_derived_optionset(security); 14274345Sdougm if (sectype != NULL) 14284345Sdougm sa_free_attr_string(sectype); 14294345Sdougm if (options != NULL) 14304345Sdougm sa_free_derived_optionset(options); 14313910Sdougm return (buff); 14323910Sdougm } 14334345Sdougm 14343910Sdougm /* 14353910Sdougm * Append an entry to the nfslogtab file 14363910Sdougm */ 14373910Sdougm static int 14383910Sdougm nfslogtab_add(dir, buffer, tag) 14393910Sdougm char *dir, *buffer, *tag; 14403910Sdougm { 14413910Sdougm FILE *f; 14423910Sdougm struct logtab_ent lep; 14433910Sdougm int error = 0; 14443910Sdougm 14453910Sdougm /* 14463910Sdougm * Open the file for update and create it if necessary. 14473910Sdougm * This may leave the I/O offset at the end of the file, 14483910Sdougm * so rewind back to the beginning of the file. 14493910Sdougm */ 14503910Sdougm f = fopen(NFSLOGTAB, "a+"); 14513910Sdougm if (f == NULL) { 14523910Sdougm error = errno; 14533910Sdougm goto out; 14543910Sdougm } 14553910Sdougm rewind(f); 14563910Sdougm 14573910Sdougm if (lockf(fileno(f), F_LOCK, 0L) < 0) { 14583910Sdougm (void) fprintf(stderr, dgettext(TEXT_DOMAIN, 14594345Sdougm "share complete, however failed to lock %s " 14604345Sdougm "for update: %s\n"), NFSLOGTAB, strerror(errno)); 14613910Sdougm error = -1; 14623910Sdougm goto out; 14633910Sdougm } 14643910Sdougm 14653910Sdougm if (logtab_deactivate_after_boot(f) == -1) { 14663910Sdougm (void) fprintf(stderr, dgettext(TEXT_DOMAIN, 14674345Sdougm "share complete, however could not deactivate " 14684345Sdougm "entries in %s\n"), NFSLOGTAB); 14693910Sdougm error = -1; 14703910Sdougm goto out; 14713910Sdougm } 14723910Sdougm 14733910Sdougm /* 14743910Sdougm * Remove entries matching buffer and sharepoint since we're 14753910Sdougm * going to replace it with perhaps an entry with a new tag. 14763910Sdougm */ 14773910Sdougm if (logtab_rement(f, buffer, dir, NULL, -1)) { 14783910Sdougm (void) fprintf(stderr, dgettext(TEXT_DOMAIN, 14794345Sdougm "share complete, however could not remove matching " 14804345Sdougm "entries in %s\n"), NFSLOGTAB); 14813910Sdougm error = -1; 14823910Sdougm goto out; 14833910Sdougm } 14843910Sdougm 14853910Sdougm /* 14863910Sdougm * Deactivate all active entries matching this sharepoint 14873910Sdougm */ 14883910Sdougm if (logtab_deactivate(f, NULL, dir, NULL)) { 14893910Sdougm (void) fprintf(stderr, dgettext(TEXT_DOMAIN, 14904345Sdougm "share complete, however could not deactivate matching " 14914345Sdougm "entries in %s\n"), NFSLOGTAB); 14923910Sdougm error = -1; 14933910Sdougm goto out; 14943910Sdougm } 14953910Sdougm 14963910Sdougm lep.le_buffer = buffer; 14973910Sdougm lep.le_path = dir; 14983910Sdougm lep.le_tag = tag; 14993910Sdougm lep.le_state = LES_ACTIVE; 15003910Sdougm 15013910Sdougm /* 15023910Sdougm * Add new sharepoint / buffer location to nfslogtab 15033910Sdougm */ 15043910Sdougm if (logtab_putent(f, &lep) < 0) { 15053910Sdougm (void) fprintf(stderr, dgettext(TEXT_DOMAIN, 15064345Sdougm "share complete, however could not add %s to %s\n"), 15074345Sdougm dir, NFSLOGTAB); 15083910Sdougm error = -1; 15093910Sdougm } 15103910Sdougm 15113910Sdougm out: 15123910Sdougm if (f != NULL) 15133910Sdougm (void) fclose(f); 15143910Sdougm return (error); 15153910Sdougm } 15163910Sdougm 15173910Sdougm /* 15183910Sdougm * Deactivate an entry from the nfslogtab file 15193910Sdougm */ 15203910Sdougm static int 15213910Sdougm nfslogtab_deactivate(path) 15223910Sdougm char *path; 15233910Sdougm { 15243910Sdougm FILE *f; 15253910Sdougm int error = 0; 15263910Sdougm 15273910Sdougm f = fopen(NFSLOGTAB, "r+"); 15283910Sdougm if (f == NULL) { 15293910Sdougm error = errno; 15303910Sdougm goto out; 15313910Sdougm } 15323910Sdougm if (lockf(fileno(f), F_LOCK, 0L) < 0) { 15333910Sdougm error = errno; 15343910Sdougm (void) fprintf(stderr, dgettext(TEXT_DOMAIN, 15354345Sdougm "share complete, however could not lock %s for " 15364345Sdougm "update: %s\n"), NFSLOGTAB, strerror(error)); 15373910Sdougm goto out; 15383910Sdougm } 15393910Sdougm if (logtab_deactivate(f, NULL, path, NULL) == -1) { 15403910Sdougm error = -1; 15413910Sdougm (void) fprintf(stderr, 15424345Sdougm dgettext(TEXT_DOMAIN, 15434345Sdougm "share complete, however could not " 15444345Sdougm "deactivate %s in %s\n"), path, NFSLOGTAB); 15453910Sdougm goto out; 15463910Sdougm } 15473910Sdougm 15483910Sdougm out: if (f != NULL) 15493910Sdougm (void) fclose(f); 15503910Sdougm 15513910Sdougm return (error); 15523910Sdougm } 15533910Sdougm 15543910Sdougm /* 15554524Sdougm * check_public(group, skipshare) 15564524Sdougm * 15574524Sdougm * Check the group for any shares that have the public property 15584524Sdougm * enabled. We skip "skipshare" since that is the one we are 15594524Sdougm * working with. This is a separate function to make handling 15604524Sdougm * subgroups simpler. Returns true if there is a share with public. 15614524Sdougm */ 15624524Sdougm static int 15634524Sdougm check_public(sa_group_t group, sa_share_t skipshare) 15644524Sdougm { 15654524Sdougm int exists = B_FALSE; 15664524Sdougm sa_share_t share; 15674524Sdougm sa_optionset_t opt; 15684524Sdougm sa_property_t prop; 15694524Sdougm char *shared; 15704524Sdougm 15714524Sdougm for (share = sa_get_share(group, NULL); share != NULL; 15724524Sdougm share = sa_get_next_share(share)) { 15734524Sdougm if (share == skipshare) 15744524Sdougm continue; 15754524Sdougm 15764524Sdougm opt = sa_get_optionset(share, "nfs"); 15774524Sdougm if (opt == NULL) 15784524Sdougm continue; 15794524Sdougm prop = sa_get_property(opt, "public"); 15804524Sdougm if (prop == NULL) 15814524Sdougm continue; 15824524Sdougm shared = sa_get_share_attr(share, "shared"); 15834524Sdougm if (shared != NULL) { 15844524Sdougm exists = strcmp(shared, "true") == 0; 15854524Sdougm sa_free_attr_string(shared); 15864524Sdougm if (exists == B_TRUE) 15874524Sdougm break; 15884524Sdougm } 15894524Sdougm } 15904524Sdougm 15914524Sdougm return (exists); 15924524Sdougm } 15934524Sdougm 15944524Sdougm /* 15953910Sdougm * public_exists(share) 15963910Sdougm * 15973910Sdougm * check to see if public option is set on any other share than the 15984524Sdougm * one specified. Need to check zfs sub-groups as well as the top 15994524Sdougm * level groups. 16003910Sdougm */ 16013910Sdougm static int 16023910Sdougm public_exists(sa_share_t skipshare) 16033910Sdougm { 16043910Sdougm sa_group_t group; 16053910Sdougm sa_handle_t handle; 16063910Sdougm 16073910Sdougm group = sa_get_parent_group(skipshare); 16083910Sdougm if (group == NULL) 16094345Sdougm return (SA_NO_SUCH_GROUP); 16103910Sdougm 16113910Sdougm handle = sa_find_group_handle(group); 16123910Sdougm if (handle == NULL) 16134345Sdougm return (SA_SYSTEM_ERR); 16143910Sdougm 16153910Sdougm for (group = sa_get_group(handle, NULL); group != NULL; 16163910Sdougm group = sa_get_next_group(group)) { 16174524Sdougm /* Walk any ZFS subgroups as well as all standard groups */ 16184524Sdougm if (sa_group_is_zfs(group)) { 16194524Sdougm sa_group_t subgroup; 16204524Sdougm for (subgroup = sa_get_sub_group(group); 16214524Sdougm subgroup != NULL; 16224524Sdougm subgroup = sa_get_next_group(subgroup)) { 16234524Sdougm if (check_public(subgroup, skipshare)) 16244524Sdougm return (B_TRUE); 16253910Sdougm } 16264524Sdougm } else { 16274524Sdougm if (check_public(group, skipshare)) 16284524Sdougm return (B_TRUE); 16293910Sdougm } 16303910Sdougm } 16314524Sdougm return (B_FALSE); 16323910Sdougm } 16333910Sdougm 16343910Sdougm /* 16353910Sdougm * sa_enable_share at the protocol level, enable_share must tell the 16363910Sdougm * implementation that it is to enable the share. This entails 16373910Sdougm * converting the path and options into the appropriate ioctl 16383910Sdougm * calls. It is assumed that all error checking of paths, etc. were 16393910Sdougm * done earlier. 16403910Sdougm */ 16413910Sdougm static int 16423910Sdougm nfs_enable_share(sa_share_t share) 16433910Sdougm { 16443910Sdougm struct exportdata export; 16453910Sdougm sa_optionset_t secoptlist; 16463910Sdougm struct secinfo *sp; 16473910Sdougm int num_secinfo; 16483910Sdougm sa_optionset_t opt; 16493910Sdougm sa_security_t sec; 16503910Sdougm sa_property_t prop; 16513910Sdougm char *path; 16523910Sdougm int err = SA_OK; 16534524Sdougm int i; 16544543Smarks int iszfs; 16553910Sdougm 16563910Sdougm /* Don't drop core if the NFS module isn't loaded. */ 16573910Sdougm (void) signal(SIGSYS, SIG_IGN); 16583910Sdougm 16593910Sdougm /* get the path since it is important in several places */ 16603910Sdougm path = sa_get_share_attr(share, "path"); 16613910Sdougm if (path == NULL) 16624345Sdougm return (SA_NO_SUCH_PATH); 16633910Sdougm 16644543Smarks iszfs = sa_path_is_zfs(path); 16653910Sdougm /* 16663910Sdougm * find the optionsets and security sets. There may not be 16673910Sdougm * any or there could be one or two for each of optionset and 16683910Sdougm * security may have multiple, one per security type per 16693910Sdougm * protocol type. 16703910Sdougm */ 16713910Sdougm opt = sa_get_derived_optionset(share, "nfs", 1); 16723910Sdougm secoptlist = (sa_optionset_t)sa_get_all_security_types(share, "nfs", 1); 16733910Sdougm if (secoptlist != NULL) 16744345Sdougm num_secinfo = MAX(1, count_security(secoptlist)); 16753910Sdougm else 16764345Sdougm num_secinfo = 1; 16773910Sdougm 16783910Sdougm /* 16793910Sdougm * walk through the options and fill in the structure 16803910Sdougm * appropriately. 16813910Sdougm */ 16823910Sdougm 16833910Sdougm (void) memset(&export, '\0', sizeof (export)); 16843910Sdougm 16853910Sdougm /* 16863910Sdougm * do non-security options first since there is only one after 16873910Sdougm * the derived group is constructed. 16883910Sdougm */ 16893910Sdougm export.ex_version = EX_CURRENT_VERSION; 16903910Sdougm export.ex_anon = UID_NOBODY; /* this is our default value */ 16913910Sdougm export.ex_index = NULL; 16923910Sdougm export.ex_path = path; 16933910Sdougm export.ex_pathlen = strlen(path) + 1; 16943910Sdougm 16953910Sdougm if (opt != NULL) 16964345Sdougm err = fill_export_from_optionset(&export, opt); 16973910Sdougm 16983910Sdougm /* 16993910Sdougm * check to see if "public" is set. If it is, then make sure 17003910Sdougm * no other share has it set. If it is already used, fail. 17013910Sdougm */ 17023910Sdougm 17033910Sdougm if (export.ex_flags & EX_PUBLIC && public_exists(share)) { 17044345Sdougm (void) printf(dgettext(TEXT_DOMAIN, 17054345Sdougm "NFS: Cannot share more than one file " 17064345Sdougm "system with 'public' property\n")); 17074345Sdougm err = SA_NOT_ALLOWED; 17084345Sdougm goto out; 17093910Sdougm } 17103910Sdougm 17114524Sdougm sp = calloc(num_secinfo, sizeof (struct secinfo)); 17123910Sdougm if (sp == NULL) { 17134345Sdougm err = SA_NO_MEMORY; 17144524Sdougm (void) printf(dgettext(TEXT_DOMAIN, 17154524Sdougm "NFS: NFS: no memory for security\n")); 17164524Sdougm goto out; 17174524Sdougm } 17184524Sdougm export.ex_secinfo = sp; 17194524Sdougm /* get default secinfo */ 17204524Sdougm export.ex_seccnt = num_secinfo; 17214524Sdougm /* 17224524Sdougm * since we must have one security option defined, we 17234524Sdougm * init to the default and then override as we find 17244524Sdougm * defined security options. This handles the case 17254524Sdougm * where we have no defined options but we need to set 17264524Sdougm * up one. 17274524Sdougm */ 17284524Sdougm sp[0].s_window = DEF_WIN; 17294524Sdougm sp[0].s_rootnames = NULL; 17304524Sdougm /* setup a default in case no properties defined */ 17314524Sdougm if (nfs_getseconfig_default(&sp[0].s_secinfo)) { 17324524Sdougm (void) printf(dgettext(TEXT_DOMAIN, 17334524Sdougm "NFS: nfs_getseconfig_default: failed to " 17344524Sdougm "get default security mode\n")); 17354524Sdougm err = SA_CONFIG_ERR; 17364524Sdougm } 17374524Sdougm if (secoptlist != NULL) { 17384524Sdougm for (i = 0, prop = sa_get_property(secoptlist, NULL); 17394524Sdougm prop != NULL && i < num_secinfo; 17404524Sdougm prop = sa_get_next_property(prop), i++) { 17414524Sdougm char *sectype; 17424345Sdougm sectype = sa_get_property_attr(prop, "type"); 17434524Sdougm /* 17444524Sdougm * if sectype is NULL, we probably 17454524Sdougm * have a memory problem and can't get 17464524Sdougm * the correct values. Rather than 17474524Sdougm * exporting with incorrect security, 17484524Sdougm * don't share it. 17494524Sdougm */ 17504524Sdougm if (sectype == NULL) { 17514524Sdougm err = SA_NO_MEMORY; 17524524Sdougm (void) printf(dgettext(TEXT_DOMAIN, 17534524Sdougm "NFS: Cannot share %s: " 17544524Sdougm "no memory\n"), path); 17554524Sdougm goto out; 17564524Sdougm } 17574524Sdougm sec = (sa_security_t)sa_get_derived_security( 17584524Sdougm share, sectype, "nfs", 1); 17594524Sdougm sp[i].s_window = DEF_WIN; 17604524Sdougm sp[i].s_rootcnt = 0; 17614524Sdougm sp[i].s_rootnames = NULL; 17624345Sdougm (void) fill_security_from_secopts(&sp[i], sec); 17634524Sdougm if (sec != NULL) 17644524Sdougm sa_free_derived_security(sec); 17654524Sdougm if (sectype != NULL) 17664524Sdougm sa_free_attr_string(sectype); 17673910Sdougm } 17684524Sdougm } 17694524Sdougm /* 17704524Sdougm * when we get here, we can do the exportfs system call and 17714524Sdougm * initiate thinsg. We probably want to enable the nfs.server 17724524Sdougm * service first if it isn't running within SMF. 17734524Sdougm */ 17744524Sdougm /* check nfs.server status and start if needed */ 17754524Sdougm /* now add the share to the internal tables */ 17764524Sdougm printarg(path, &export); 17774524Sdougm /* 17784524Sdougm * call the exportfs system call which is implemented 17794524Sdougm * via the nfssys() call as the EXPORTFS subfunction. 17804524Sdougm */ 17814543Smarks if (iszfs) { 17824543Smarks struct exportfs_args ea; 17834543Smarks share_t sh; 17844543Smarks char *str; 17854543Smarks priv_set_t *priv_effective; 17864543Smarks int privileged; 17874543Smarks 17884543Smarks /* 17894543Smarks * If we aren't a privileged user 17904543Smarks * and NFS server service isn't running 17914543Smarks * then print out an error message 17924543Smarks * and return EPERM 17934543Smarks */ 17944543Smarks 17954543Smarks priv_effective = priv_allocset(); 17964543Smarks (void) getppriv(PRIV_EFFECTIVE, priv_effective); 17974543Smarks 17984543Smarks privileged = (priv_isfullset(priv_effective) == B_TRUE); 17994543Smarks priv_freeset(priv_effective); 18004543Smarks 18014543Smarks if (!privileged && 18024543Smarks (str = smf_get_state(NFS_SERVER_SVC)) != NULL) { 18034543Smarks err = 0; 18044543Smarks if (strcmp(str, SCF_STATE_STRING_ONLINE) != 0) { 18054543Smarks (void) printf(dgettext(TEXT_DOMAIN, 18064543Smarks "NFS: Cannot share remote " 18074543Smarks "filesystem: %s\n"), path); 18084543Smarks (void) printf(dgettext(TEXT_DOMAIN, 18094543Smarks "NFS: Service needs to be enabled " 18104543Smarks "by a privileged user\n")); 18114543Smarks err = SA_SYSTEM_ERR; 18124543Smarks errno = EPERM; 18134543Smarks } 18144543Smarks free(str); 18154543Smarks } 18164543Smarks 18174543Smarks if (err == 0) { 18184543Smarks ea.dname = path; 18194543Smarks ea.uex = &export; 18204543Smarks 18214543Smarks sa_sharetab_fill_zfs(share, &sh, "nfs"); 18225331Samw err = sa_share_zfs(share, path, &sh, 18235331Samw &ea, ZFS_SHARE_NFS); 18244543Smarks sa_emptyshare(&sh); 18254543Smarks } 18264543Smarks } else { 18274543Smarks err = exportfs(path, &export); 18284543Smarks } 18294543Smarks 18304543Smarks if (err < 0) { 18314524Sdougm err = SA_SYSTEM_ERR; 18324524Sdougm switch (errno) { 18334524Sdougm case EREMOTE: 18344524Sdougm (void) printf(dgettext(TEXT_DOMAIN, 18354543Smarks "NFS: Cannot share filesystems " 18364543Smarks "in non-global zones: %s\n"), path); 18374543Smarks err = SA_NOT_SUPPORTED; 18384524Sdougm break; 18394524Sdougm case EPERM: 18404524Sdougm if (getzoneid() != GLOBAL_ZONEID) { 18414345Sdougm (void) printf(dgettext(TEXT_DOMAIN, 18424543Smarks "NFS: Cannot share file systems " 18434524Sdougm "in non-global zones: %s\n"), path); 18444524Sdougm err = SA_NOT_SUPPORTED; 18454345Sdougm break; 18464345Sdougm } 18474524Sdougm err = SA_NO_PERMISSION; 18484524Sdougm /* FALLTHROUGH */ 18494524Sdougm default: 18504524Sdougm break; 18513910Sdougm } 18524524Sdougm } else { 18534524Sdougm /* update sharetab with an add/modify */ 18544543Smarks if (!iszfs) { 18554543Smarks (void) sa_update_sharetab(share, "nfs"); 18564543Smarks } 18573910Sdougm } 18583910Sdougm 18593910Sdougm if (err == SA_OK) { 18603910Sdougm /* 18613910Sdougm * enable services as needed. This should probably be 18623910Sdougm * done elsewhere in order to minimize the calls to 18633910Sdougm * check services. 18643910Sdougm */ 18653910Sdougm /* 18663910Sdougm * check to see if logging and other services need to 18673910Sdougm * be triggered, but only if there wasn't an 18683910Sdougm * error. This is probably where sharetab should be 18693910Sdougm * updated with the NFS specific entry. 18703910Sdougm */ 18714345Sdougm if (export.ex_flags & EX_LOG) { 18724345Sdougm /* enable logging */ 18734345Sdougm if (nfslogtab_add(path, export.ex_log_buffer, 18744345Sdougm export.ex_tag) != 0) { 18754345Sdougm (void) fprintf(stderr, dgettext(TEXT_DOMAIN, 18764345Sdougm "Could not enable logging for %s\n"), 18774345Sdougm path); 18784345Sdougm } 18794345Sdougm _check_services(service_list_logging); 18804345Sdougm } else { 18814345Sdougm /* 18824345Sdougm * don't have logging so remove it from file. It might 18834345Sdougm * not be thre, but that doesn't matter. 18844345Sdougm */ 18854345Sdougm (void) nfslogtab_deactivate(path); 18864345Sdougm _check_services(service_list_default); 18873910Sdougm } 18883910Sdougm } 18893910Sdougm 18903910Sdougm out: 18913910Sdougm if (path != NULL) 18924345Sdougm free(path); 18933910Sdougm 18943910Sdougm cleanup_export(&export); 18953910Sdougm if (opt != NULL) 18964345Sdougm sa_free_derived_optionset(opt); 18973910Sdougm if (secoptlist != NULL) 18984345Sdougm (void) sa_destroy_optionset(secoptlist); 18993910Sdougm return (err); 19003910Sdougm } 19013910Sdougm 19023910Sdougm /* 19035800Sdougm * nfs_disable_share(share, path) 19043910Sdougm * 19055800Sdougm * Unshare the specified share. Note that "path" is the same path as 19065800Sdougm * what is in the "share" object. It is passed in to avoid an 19075800Sdougm * additional lookup. A missing "path" value makes this a no-op 19085800Sdougm * function. 19093910Sdougm */ 19103910Sdougm static int 19114543Smarks nfs_disable_share(sa_share_t share, char *path) 19123910Sdougm { 19133910Sdougm int err; 19143910Sdougm int ret = SA_OK; 19154543Smarks int iszfs; 19165800Sdougm sa_group_t parent; 19175951Sdougm sa_handle_t handle; 19184543Smarks 19195800Sdougm if (path == NULL) 19205800Sdougm return (ret); 19214543Smarks 19225800Sdougm /* 19235800Sdougm * If the share is in a ZFS group we need to handle it 19245800Sdougm * differently. Just being on a ZFS file system isn't 19255800Sdougm * enough since we may be in a legacy share case. 19265800Sdougm */ 19275800Sdougm parent = sa_get_parent_group(share); 19285800Sdougm iszfs = sa_group_is_zfs(parent); 19295800Sdougm if (iszfs) { 19305800Sdougm struct exportfs_args ea; 19315800Sdougm share_t sh = { 0 }; 19325800Sdougm ea.dname = path; 19335800Sdougm ea.uex = NULL; 19345800Sdougm sh.sh_path = path; 19355800Sdougm sh.sh_fstype = "nfs"; 19364543Smarks 19375800Sdougm err = sa_share_zfs(share, path, &sh, 19385800Sdougm &ea, ZFS_UNSHARE_NFS); 19395800Sdougm } else { 19405800Sdougm err = exportfs(path, NULL); 19415800Sdougm } 19425800Sdougm if (err < 0) { 19435800Sdougm /* 19445800Sdougm * TBD: only an error in some 19455800Sdougm * cases - need better analysis 19465800Sdougm */ 19475800Sdougm switch (errno) { 19485800Sdougm case EPERM: 19495800Sdougm case EACCES: 19505800Sdougm ret = SA_NO_PERMISSION; 19515800Sdougm if (getzoneid() != GLOBAL_ZONEID) { 19525800Sdougm ret = SA_NOT_SUPPORTED; 19535800Sdougm } 19545800Sdougm break; 19555800Sdougm case EINVAL: 19565800Sdougm case ENOENT: 19575800Sdougm ret = SA_NO_SUCH_PATH; 19584543Smarks break; 19594345Sdougm default: 19604345Sdougm ret = SA_SYSTEM_ERR; 19614543Smarks break; 19623910Sdougm } 19635800Sdougm } 19645800Sdougm if (ret == SA_OK || ret == SA_NO_SUCH_PATH) { 19655951Sdougm handle = sa_find_group_handle((sa_group_t)share); 19665800Sdougm if (!iszfs) 19675951Sdougm (void) sa_delete_sharetab(handle, path, "nfs"); 19685800Sdougm /* just in case it was logged */ 19695800Sdougm (void) nfslogtab_deactivate(path); 19703910Sdougm } 19713910Sdougm return (ret); 19723910Sdougm } 19733910Sdougm 19743910Sdougm /* 19753910Sdougm * check ro vs rw values. Over time this may get beefed up. 19763910Sdougm * for now it just does simple checks. 19773910Sdougm */ 19783910Sdougm 19793910Sdougm static int 19803910Sdougm check_rorw(char *v1, char *v2) 19813910Sdougm { 19823910Sdougm int ret = SA_OK; 19833910Sdougm if (strcmp(v1, v2) == 0) 19844345Sdougm ret = SA_VALUE_CONFLICT; 19853910Sdougm return (ret); 19863910Sdougm } 19873910Sdougm 19883910Sdougm /* 19893910Sdougm * nfs_validate_property(property, parent) 19903910Sdougm * 19913910Sdougm * Check that the property has a legitimate value for its type. 19923910Sdougm */ 19933910Sdougm 19943910Sdougm static int 19953910Sdougm nfs_validate_property(sa_property_t property, sa_optionset_t parent) 19963910Sdougm { 19973910Sdougm int ret = SA_OK; 19983910Sdougm char *propname; 19993910Sdougm char *other; 20003910Sdougm int optindex; 20013910Sdougm nfsl_config_t *configlist; 20023910Sdougm sa_group_t parent_group; 20033910Sdougm char *value; 20043910Sdougm 20053910Sdougm propname = sa_get_property_attr(property, "type"); 20063910Sdougm 20073910Sdougm if ((optindex = findopt(propname)) < 0) 20084345Sdougm ret = SA_NO_SUCH_PROP; 20093910Sdougm 20103910Sdougm /* need to validate value range here as well */ 20113910Sdougm 20123910Sdougm if (ret == SA_OK) { 20134345Sdougm parent_group = sa_get_parent_group((sa_share_t)parent); 20144345Sdougm if (optdefs[optindex].share && !sa_is_share(parent_group)) 20154345Sdougm ret = SA_PROP_SHARE_ONLY; 20163910Sdougm } 20173910Sdougm if (ret == SA_OK) { 20184345Sdougm value = sa_get_property_attr(property, "value"); 20194345Sdougm if (value != NULL) { 20204345Sdougm /* first basic type checking */ 20214345Sdougm switch (optdefs[optindex].type) { 20224345Sdougm case OPT_TYPE_NUMBER: 20234345Sdougm /* check that the value is all digits */ 20244345Sdougm if (!is_a_number(value)) 20254345Sdougm ret = SA_BAD_VALUE; 20264345Sdougm break; 20274345Sdougm case OPT_TYPE_BOOLEAN: 20284345Sdougm if (strlen(value) == 0 || 20294345Sdougm strcasecmp(value, "true") == 0 || 20304345Sdougm strcmp(value, "1") == 0 || 20314345Sdougm strcasecmp(value, "false") == 0 || 20324345Sdougm strcmp(value, "0") == 0) { 20334345Sdougm ret = SA_OK; 20344345Sdougm } else { 20354345Sdougm ret = SA_BAD_VALUE; 20364345Sdougm } 20374345Sdougm break; 20384345Sdougm case OPT_TYPE_USER: 20394345Sdougm if (!is_a_number(value)) { 20404345Sdougm struct passwd *pw; 20414345Sdougm /* 20424345Sdougm * in this case it would have to be a 20434345Sdougm * user name 20444345Sdougm */ 20454345Sdougm pw = getpwnam(value); 20464345Sdougm if (pw == NULL) 20474345Sdougm ret = SA_BAD_VALUE; 20484345Sdougm endpwent(); 20494345Sdougm } else { 20504345Sdougm uint64_t intval; 20514345Sdougm intval = strtoull(value, NULL, 0); 20524345Sdougm if (intval > UID_MAX && intval != ~0) 20534345Sdougm ret = SA_BAD_VALUE; 20544345Sdougm } 20554345Sdougm break; 20564345Sdougm case OPT_TYPE_FILE: 20574345Sdougm if (strcmp(value, "..") == 0 || 20584345Sdougm strchr(value, '/') != NULL) { 20594345Sdougm ret = SA_BAD_VALUE; 20604345Sdougm } 20614345Sdougm break; 20624345Sdougm case OPT_TYPE_ACCLIST: 20634345Sdougm /* 20644345Sdougm * access list handling. Should eventually 20654345Sdougm * validate that all the values make sense. 20664345Sdougm * Also, ro and rw may have cross value 20674345Sdougm * conflicts. 20684345Sdougm */ 20694345Sdougm if (strcmp(propname, SHOPT_RO) == 0) 20704345Sdougm other = SHOPT_RW; 20714345Sdougm else if (strcmp(propname, SHOPT_RW) == 0) 20724345Sdougm other = SHOPT_RO; 20734345Sdougm else 20744345Sdougm other = NULL; 20754345Sdougm 20764345Sdougm if (other != NULL && parent != NULL) { 20774345Sdougm /* compare rw(ro) with ro(rw) */ 20784345Sdougm sa_property_t oprop; 20794345Sdougm oprop = sa_get_property(parent, other); 20804345Sdougm if (oprop != NULL) { 20814345Sdougm /* 20824345Sdougm * only potential 20834345Sdougm * confusion if other 20844345Sdougm * exists 20854345Sdougm */ 20864345Sdougm char *ovalue; 20874345Sdougm ovalue = sa_get_property_attr( 20884345Sdougm oprop, "value"); 20894345Sdougm if (ovalue != NULL) { 20904345Sdougm ret = check_rorw(value, 20914345Sdougm ovalue); 20924345Sdougm sa_free_attr_string( 20934345Sdougm ovalue); 20944345Sdougm } 20954345Sdougm } 20964345Sdougm } 20974345Sdougm break; 20984345Sdougm case OPT_TYPE_LOGTAG: 20994345Sdougm if (nfsl_getconfig_list(&configlist) == 0) { 21004345Sdougm int error; 21014345Sdougm if (value == NULL || 21024345Sdougm strlen(value) == 0) { 21034345Sdougm if (value != NULL) 21044345Sdougm sa_free_attr_string( 21054345Sdougm value); 21064345Sdougm value = strdup("global"); 21074345Sdougm } 21084345Sdougm if (value != NULL && 21094345Sdougm nfsl_findconfig(configlist, value, 21104345Sdougm &error) == NULL) { 21114345Sdougm ret = SA_BAD_VALUE; 21124345Sdougm } 21135179Sdougm /* Must always free when done */ 21145179Sdougm nfsl_freeconfig_list(&configlist); 21154345Sdougm } else { 21164345Sdougm ret = SA_CONFIG_ERR; 21174345Sdougm } 21184345Sdougm break; 21194345Sdougm case OPT_TYPE_STRING: 21204345Sdougm /* whatever is here should be ok */ 21214345Sdougm break; 21224345Sdougm case OPT_TYPE_SECURITY: 21234345Sdougm /* 21244345Sdougm * The "sec" property isn't used in the 21254345Sdougm * non-legacy parts of sharemgr. We need to 21264345Sdougm * reject it here. For legacy, it is pulled 21274345Sdougm * out well before we get here. 21284345Sdougm */ 21294345Sdougm ret = SA_NO_SUCH_PROP; 21304345Sdougm break; 21314345Sdougm default: 21324345Sdougm break; 21333910Sdougm } 21345179Sdougm 21355179Sdougm if (value != NULL) 21365179Sdougm sa_free_attr_string(value); 21375179Sdougm 21384345Sdougm if (ret == SA_OK && optdefs[optindex].check != NULL) { 21394345Sdougm /* do the property specific check */ 21404345Sdougm ret = optdefs[optindex].check(property); 21413910Sdougm } 21423910Sdougm } 21433910Sdougm } 21443910Sdougm 21453910Sdougm if (propname != NULL) 21464345Sdougm sa_free_attr_string(propname); 21473910Sdougm return (ret); 21483910Sdougm } 21493910Sdougm 21503910Sdougm /* 21513910Sdougm * Protocol management functions 21523910Sdougm * 21533910Sdougm * Properties defined in the default files are defined in 21543910Sdougm * proto_option_defs for parsing and validation. If "other" and 21553910Sdougm * "compare" are set, then the value for this property should be 21563910Sdougm * compared against the property specified in "other" using the 21573910Sdougm * "compare" check (either <= or >=) in order to ensure that the 21583910Sdougm * values are in the correct range. E.g. setting server_versmin 21593910Sdougm * higher than server_versmax should not be allowed. 21603910Sdougm */ 21613910Sdougm 21623910Sdougm struct proto_option_defs { 21633910Sdougm char *tag; 21643910Sdougm char *name; /* display name -- remove protocol identifier */ 21653910Sdougm int index; 21663910Sdougm int type; 21673910Sdougm union { 21683910Sdougm int intval; 21693910Sdougm char *string; 21703910Sdougm } defvalue; 21713910Sdougm uint32_t svcs; 21723910Sdougm int32_t minval; 21733910Sdougm int32_t maxval; 21743910Sdougm char *file; 21753910Sdougm char *other; 21763910Sdougm int compare; 21773910Sdougm #define OPT_CMP_GE 0 21783910Sdougm #define OPT_CMP_LE 1 21793910Sdougm int (*check)(char *); 21803910Sdougm } proto_options[] = { 21813910Sdougm #define PROTO_OPT_NFSD_SERVERS 0 21823910Sdougm {"nfsd_servers", 21833910Sdougm "servers", PROTO_OPT_NFSD_SERVERS, OPT_TYPE_NUMBER, 16, SVC_NFSD, 21843910Sdougm 1, INT32_MAX, NFSADMIN}, 21853910Sdougm #define PROTO_OPT_LOCKD_LISTEN_BACKLOG 1 21863910Sdougm {"lockd_listen_backlog", 21873910Sdougm "lockd_listen_backlog", PROTO_OPT_LOCKD_LISTEN_BACKLOG, 21883910Sdougm OPT_TYPE_NUMBER, 32, SVC_LOCKD, 32, INT32_MAX, NFSADMIN}, 21893910Sdougm #define PROTO_OPT_LOCKD_SERVERS 2 21903910Sdougm {"lockd_servers", 21913910Sdougm "lockd_servers", PROTO_OPT_LOCKD_SERVERS, OPT_TYPE_NUMBER, 20, 21923910Sdougm SVC_LOCKD, 1, INT32_MAX, NFSADMIN}, 21933910Sdougm #define PROTO_OPT_LOCKD_RETRANSMIT_TIMEOUT 3 21943910Sdougm {"lockd_retransmit_timeout", 21953910Sdougm "lockd_retransmit_timeout", PROTO_OPT_LOCKD_RETRANSMIT_TIMEOUT, 21963910Sdougm OPT_TYPE_NUMBER, 5, SVC_LOCKD, 0, INT32_MAX, NFSADMIN}, 21973910Sdougm #define PROTO_OPT_GRACE_PERIOD 4 21983910Sdougm {"grace_period", 21993910Sdougm "grace_period", PROTO_OPT_GRACE_PERIOD, OPT_TYPE_NUMBER, 90, 22003910Sdougm SVC_LOCKD, 0, INT32_MAX, NFSADMIN}, 22013910Sdougm #define PROTO_OPT_NFS_SERVER_VERSMIN 5 22023910Sdougm {"nfs_server_versmin", 22033910Sdougm "server_versmin", PROTO_OPT_NFS_SERVER_VERSMIN, OPT_TYPE_NUMBER, 22043910Sdougm (int)NFS_VERSMIN_DEFAULT, SVC_NFSD|SVC_MOUNTD, NFS_VERSMIN, 22053910Sdougm NFS_VERSMAX, NFSADMIN, "server_versmax", OPT_CMP_LE}, 22063910Sdougm #define PROTO_OPT_NFS_SERVER_VERSMAX 6 22073910Sdougm {"nfs_server_versmax", 22083910Sdougm "server_versmax", PROTO_OPT_NFS_SERVER_VERSMAX, OPT_TYPE_NUMBER, 22093910Sdougm (int)NFS_VERSMAX_DEFAULT, SVC_NFSD|SVC_MOUNTD, NFS_VERSMIN, 22103910Sdougm NFS_VERSMAX, NFSADMIN, "server_versmin", OPT_CMP_GE}, 22113910Sdougm #define PROTO_OPT_NFS_CLIENT_VERSMIN 7 22123910Sdougm {"nfs_client_versmin", 22133910Sdougm "client_versmin", PROTO_OPT_NFS_CLIENT_VERSMIN, OPT_TYPE_NUMBER, 22143910Sdougm (int)NFS_VERSMIN_DEFAULT, NULL, NFS_VERSMIN, NFS_VERSMAX, 22153910Sdougm NFSADMIN, "client_versmax", OPT_CMP_LE}, 22163910Sdougm #define PROTO_OPT_NFS_CLIENT_VERSMAX 8 22173910Sdougm {"nfs_client_versmax", 22183910Sdougm "client_versmax", PROTO_OPT_NFS_CLIENT_VERSMAX, OPT_TYPE_NUMBER, 22193910Sdougm (int)NFS_VERSMAX_DEFAULT, NULL, NFS_VERSMIN, NFS_VERSMAX, 22203910Sdougm NFSADMIN, "client_versmin", OPT_CMP_GE}, 22213910Sdougm #define PROTO_OPT_NFS_SERVER_DELEGATION 9 22223910Sdougm {"nfs_server_delegation", 22233910Sdougm "server_delegation", PROTO_OPT_NFS_SERVER_DELEGATION, 22243910Sdougm OPT_TYPE_ONOFF, NFS_SERVER_DELEGATION_DEFAULT, SVC_NFSD, 0, 0, 22253910Sdougm NFSADMIN}, 22263910Sdougm #define PROTO_OPT_NFSMAPID_DOMAIN 10 22273910Sdougm {"nfsmapid_domain", 22283910Sdougm "nfsmapid_domain", PROTO_OPT_NFSMAPID_DOMAIN, OPT_TYPE_DOMAIN, 22293910Sdougm NULL, SVC_NFSMAPID, 0, 0, NFSADMIN}, 22303910Sdougm #define PROTO_OPT_NFSD_MAX_CONNECTIONS 11 22313910Sdougm {"nfsd_max_connections", 22323910Sdougm "max_connections", PROTO_OPT_NFSD_MAX_CONNECTIONS, 22333910Sdougm OPT_TYPE_NUMBER, -1, SVC_NFSD, -1, INT32_MAX, NFSADMIN}, 22343910Sdougm #define PROTO_OPT_NFSD_PROTOCOL 12 22353910Sdougm {"nfsd_protocol", 22363910Sdougm "protocol", PROTO_OPT_NFSD_PROTOCOL, OPT_TYPE_PROTOCOL, 0, 22373910Sdougm SVC_NFSD, 0, 0, NFSADMIN}, 22383910Sdougm #define PROTO_OPT_NFSD_LISTEN_BACKLOG 13 22393910Sdougm {"nfsd_listen_backlog", 22403910Sdougm "listen_backlog", PROTO_OPT_NFSD_LISTEN_BACKLOG, 22413910Sdougm OPT_TYPE_NUMBER, 0, 22423910Sdougm SVC_LOCKD, 0, INT32_MAX, NFSADMIN}, 22433910Sdougm {NULL} 22443910Sdougm }; 22453910Sdougm 22463910Sdougm /* 22473910Sdougm * the protoset holds the defined options so we don't have to read 22483910Sdougm * them multiple times 22493910Sdougm */ 22505179Sdougm static sa_protocol_properties_t protoset; 22513910Sdougm 22523910Sdougm static int 22533910Sdougm findprotoopt(char *name, int whichname) 22543910Sdougm { 22553910Sdougm int i; 22563910Sdougm for (i = 0; proto_options[i].tag != NULL; i++) { 22574345Sdougm if (whichname == 1) { 22584345Sdougm if (strcasecmp(proto_options[i].name, name) == 0) 22593910Sdougm return (i); 22604345Sdougm } else { 22614345Sdougm if (strcasecmp(proto_options[i].tag, name) == 0) 22624345Sdougm return (i); 22634345Sdougm } 22643910Sdougm } 22653910Sdougm return (-1); 22663910Sdougm } 22673910Sdougm 22683910Sdougm /* 22693910Sdougm * fixcaselower(str) 22703910Sdougm * 22713910Sdougm * convert a string to lower case (inplace). 22723910Sdougm */ 22733910Sdougm 22743910Sdougm static void 22753910Sdougm fixcaselower(char *str) 22763910Sdougm { 22773910Sdougm while (*str) { 22784345Sdougm *str = tolower(*str); 22794345Sdougm str++; 22803910Sdougm } 22813910Sdougm } 22823910Sdougm 22833910Sdougm /* 22843910Sdougm * fixcaseupper(str) 22853910Sdougm * 22863910Sdougm * convert a string to upper case (inplace). 22873910Sdougm */ 22883910Sdougm 22893910Sdougm static void 22903910Sdougm fixcaseupper(char *str) 22913910Sdougm { 22923910Sdougm while (*str) { 22934345Sdougm *str = toupper(*str); 22944345Sdougm str++; 22953910Sdougm } 22963910Sdougm } 22973910Sdougm 22983910Sdougm /* 22994241Sdougm * skipwhitespace(str) 23004241Sdougm * 23014241Sdougm * Skip leading white space. It is assumed that it is called with a 23024241Sdougm * valid pointer. 23034241Sdougm */ 23044241Sdougm 23054241Sdougm static char * 23064241Sdougm skipwhitespace(char *str) 23074241Sdougm { 23084241Sdougm while (*str && isspace(*str)) 23094241Sdougm str++; 23104241Sdougm 23114241Sdougm return (str); 23124241Sdougm } 23134241Sdougm 23144241Sdougm /* 23154345Sdougm * extractprop() 23164345Sdougm * 23174345Sdougm * Extract the property and value out of the line and create the 23184345Sdougm * property in the optionset. 23194345Sdougm */ 23206019Sdougm static int 23214345Sdougm extractprop(char *name, char *value) 23224345Sdougm { 23234345Sdougm sa_property_t prop; 23244345Sdougm int index; 23256019Sdougm int ret = SA_OK; 23264345Sdougm /* 23274345Sdougm * Remove any leading 23284345Sdougm * white space. 23294345Sdougm */ 23304345Sdougm name = skipwhitespace(name); 23314345Sdougm 23324345Sdougm index = findprotoopt(name, 0); 23334345Sdougm if (index >= 0) { 23344345Sdougm fixcaselower(name); 23354345Sdougm prop = sa_create_property(proto_options[index].name, value); 23364345Sdougm if (prop != NULL) 23376019Sdougm ret = sa_add_protocol_property(protoset, prop); 23386019Sdougm else 23396019Sdougm ret = SA_NO_MEMORY; 23404345Sdougm } 23416019Sdougm return (ret); 23424345Sdougm } 23434345Sdougm 23444345Sdougm /* 23453910Sdougm * initprotofromdefault() 23463910Sdougm * 2347*6162Sdougm * Read the default file(s) and add the defined values to the 23483910Sdougm * protoset. Note that default values are known from the built in 2349*6162Sdougm * table in case the file doesn't have a definition. Not having the 2350*6162Sdougm * /etc/default/nfs file is OK since we have builtin default 2351*6162Sdougm * values. The default file will get constructed as needed if values 2352*6162Sdougm * are changed from the defaults. 23533910Sdougm */ 23543910Sdougm 23553910Sdougm static int 23563910Sdougm initprotofromdefault() 23573910Sdougm { 23583910Sdougm FILE *nfs; 23593910Sdougm char buff[BUFSIZ]; 23603910Sdougm char *name; 23613910Sdougm char *value; 23626019Sdougm int ret = SA_OK; 23633910Sdougm 23643910Sdougm protoset = sa_create_protocol_properties("nfs"); 23653910Sdougm 23663910Sdougm if (protoset != NULL) { 23674345Sdougm nfs = fopen(NFSADMIN, "r"); 23684345Sdougm if (nfs != NULL) { 23696019Sdougm while (ret == SA_OK && 23706019Sdougm fgets(buff, sizeof (buff), nfs) != NULL) { 23714345Sdougm switch (buff[0]) { 23724345Sdougm case '\n': 23734345Sdougm case '#': 23744345Sdougm /* skip */ 23754345Sdougm break; 23764345Sdougm default: 23774345Sdougm name = buff; 23784345Sdougm buff[strlen(buff) - 1] = '\0'; 23794345Sdougm value = strchr(name, '='); 23804345Sdougm if (value != NULL) { 23814345Sdougm *value++ = '\0'; 23826019Sdougm ret = extractprop(name, value); 23834345Sdougm } 23844345Sdougm } 23853910Sdougm } 23866019Sdougm (void) fclose(nfs); 23876019Sdougm } else { 2388*6162Sdougm switch (errno) { 2389*6162Sdougm case EPERM: 2390*6162Sdougm case EACCES: 2391*6162Sdougm ret = SA_NO_PERMISSION; 2392*6162Sdougm break; 2393*6162Sdougm case ENOENT: 2394*6162Sdougm break; 2395*6162Sdougm default: 2396*6162Sdougm ret = SA_SYSTEM_ERR; 2397*6162Sdougm break; 2398*6162Sdougm } 23993910Sdougm } 24006019Sdougm } else { 24016019Sdougm ret = SA_NO_MEMORY; 24023910Sdougm } 24036019Sdougm return (ret); 24043910Sdougm } 24053910Sdougm 24063910Sdougm /* 24074345Sdougm * add_defaults() 24083910Sdougm * 24093910Sdougm * Add the default values for any property not defined in the parsing 24103910Sdougm * of the default files. Values are set according to their defined 24113910Sdougm * types. 24123910Sdougm */ 24133910Sdougm 24143910Sdougm static void 24153910Sdougm add_defaults() 24163910Sdougm { 24173910Sdougm int i; 24183910Sdougm char number[MAXDIGITS]; 24193910Sdougm 24203910Sdougm for (i = 0; proto_options[i].tag != NULL; i++) { 24214345Sdougm sa_property_t prop; 24224345Sdougm prop = sa_get_protocol_property(protoset, 24234345Sdougm proto_options[i].name); 24244345Sdougm if (prop == NULL) { 24254345Sdougm /* add the default value */ 24264345Sdougm switch (proto_options[i].type) { 24274345Sdougm case OPT_TYPE_NUMBER: 24284345Sdougm (void) snprintf(number, sizeof (number), "%d", 24294345Sdougm proto_options[i].defvalue.intval); 24304345Sdougm prop = sa_create_property(proto_options[i].name, 24314345Sdougm number); 24324345Sdougm break; 24333910Sdougm 24344345Sdougm case OPT_TYPE_BOOLEAN: 24354345Sdougm prop = sa_create_property(proto_options[i].name, 24364345Sdougm proto_options[i].defvalue.intval ? 24374345Sdougm "true" : "false"); 24384345Sdougm break; 24393910Sdougm 24404345Sdougm case OPT_TYPE_ONOFF: 24414345Sdougm prop = sa_create_property(proto_options[i].name, 24424345Sdougm proto_options[i].defvalue.intval ? 24434345Sdougm "on" : "off"); 24444345Sdougm break; 24453910Sdougm 24464345Sdougm default: 24474345Sdougm /* treat as strings of zero length */ 24484345Sdougm prop = sa_create_property(proto_options[i].name, 24494345Sdougm ""); 24504345Sdougm break; 24514345Sdougm } 24524345Sdougm if (prop != NULL) 24534345Sdougm (void) sa_add_protocol_property(protoset, prop); 24543910Sdougm } 24553910Sdougm } 24563910Sdougm } 24573910Sdougm 24583910Sdougm static void 24593910Sdougm free_protoprops() 24603910Sdougm { 24615179Sdougm if (protoset != NULL) { 24625179Sdougm xmlFreeNode(protoset); 24635179Sdougm protoset = NULL; 24645179Sdougm } 24653910Sdougm } 24663910Sdougm 24673910Sdougm /* 24683910Sdougm * nfs_init() 24693910Sdougm * 24703910Sdougm * Initialize the NFS plugin. 24713910Sdougm */ 24723910Sdougm 24733910Sdougm static int 24743910Sdougm nfs_init() 24753910Sdougm { 24763910Sdougm int ret = SA_OK; 24773910Sdougm 2478*6162Sdougm if (sa_plugin_ops.sa_init != nfs_init) { 24794345Sdougm (void) printf(dgettext(TEXT_DOMAIN, 24804345Sdougm "NFS plugin not properly initialized\n")); 2481*6162Sdougm return (SA_CONFIG_ERR); 2482*6162Sdougm } 24833910Sdougm 24843910Sdougm ret = initprotofromdefault(); 2485*6162Sdougm if (ret != SA_OK) { 2486*6162Sdougm (void) printf(dgettext(TEXT_DOMAIN, 2487*6162Sdougm "NFS plugin problem with default file: %s\n"), 2488*6162Sdougm sa_errorstr(ret)); 2489*6162Sdougm ret = SA_OK; 2490*6162Sdougm } 2491*6162Sdougm add_defaults(); 24923910Sdougm 24933910Sdougm return (ret); 24943910Sdougm } 24953910Sdougm 24963910Sdougm /* 24973910Sdougm * nfs_fini() 24983910Sdougm * 24993910Sdougm * uninitialize the NFS plugin. Want to avoid memory leaks. 25003910Sdougm */ 25013910Sdougm 25023910Sdougm static void 25033910Sdougm nfs_fini() 25043910Sdougm { 25053910Sdougm free_protoprops(); 25063910Sdougm } 25073910Sdougm 25083910Sdougm /* 25093910Sdougm * nfs_get_proto_set() 25103910Sdougm * 25113910Sdougm * Return an optionset with all the protocol specific properties in 25123910Sdougm * it. 25133910Sdougm */ 25143910Sdougm 25153910Sdougm static sa_protocol_properties_t 25163910Sdougm nfs_get_proto_set() 25173910Sdougm { 25183910Sdougm return (protoset); 25193910Sdougm } 25203910Sdougm 25213910Sdougm struct deffile { 25223910Sdougm struct deffile *next; 25233910Sdougm char *line; 25243910Sdougm }; 25253910Sdougm 25263910Sdougm /* 25273910Sdougm * read_default_file(fname) 25283910Sdougm * 25293910Sdougm * Read the specified default file. We return a list of entries. This 25303910Sdougm * get used for adding or removing values. 25313910Sdougm */ 25323910Sdougm 25333910Sdougm static struct deffile * 25343910Sdougm read_default_file(char *fname) 25353910Sdougm { 25363910Sdougm FILE *file; 25373910Sdougm struct deffile *defs = NULL; 25383910Sdougm struct deffile *newdef; 25393910Sdougm struct deffile *prevdef = NULL; 25403910Sdougm char buff[BUFSIZ * 2]; 25413910Sdougm 25423910Sdougm file = fopen(fname, "r"); 25433910Sdougm if (file != NULL) { 25444345Sdougm while (fgets(buff, sizeof (buff), file) != NULL) { 25454345Sdougm newdef = (struct deffile *)calloc(1, 25464345Sdougm sizeof (struct deffile)); 25474345Sdougm if (newdef != NULL) { 25484345Sdougm /* Make sure we skip any leading whitespace. */ 25494345Sdougm newdef->line = strdup(skipwhitespace(buff)); 25504345Sdougm if (defs == NULL) { 25514345Sdougm prevdef = defs = newdef; 25524345Sdougm } else { 25534345Sdougm prevdef->next = newdef; 25544345Sdougm prevdef = newdef; 25554345Sdougm } 25564345Sdougm } 25573910Sdougm } 2558*6162Sdougm (void) fclose(file); 2559*6162Sdougm } else { 2560*6162Sdougm int ret = SA_OK; 2561*6162Sdougm switch (errno) { 2562*6162Sdougm case EPERM: 2563*6162Sdougm case EACCES: 2564*6162Sdougm ret = SA_NO_PERMISSION; 2565*6162Sdougm break; 2566*6162Sdougm case ENOENT: 2567*6162Sdougm break; 2568*6162Sdougm default: 2569*6162Sdougm ret = SA_SYSTEM_ERR; 2570*6162Sdougm break; 2571*6162Sdougm } 2572*6162Sdougm if (ret == SA_OK) { 2573*6162Sdougm /* Want at least one comment line */ 2574*6162Sdougm defs = (struct deffile *) 2575*6162Sdougm calloc(1, sizeof (struct deffile)); 2576*6162Sdougm defs->line = strdup("# NFS default file\n"); 2577*6162Sdougm } 25783910Sdougm } 25793910Sdougm return (defs); 25803910Sdougm } 25813910Sdougm 25823910Sdougm static void 25833910Sdougm free_default_file(struct deffile *defs) 25843910Sdougm { 25853910Sdougm struct deffile *curdefs = NULL; 25863910Sdougm 25873910Sdougm while (defs != NULL) { 25884345Sdougm curdefs = defs; 25894345Sdougm defs = defs->next; 25904345Sdougm if (curdefs->line != NULL) 25914345Sdougm free(curdefs->line); 25924345Sdougm free(curdefs); 25933910Sdougm } 25943910Sdougm } 25953910Sdougm 25963910Sdougm /* 25973910Sdougm * write_default_file(fname, defs) 25983910Sdougm * 25993910Sdougm * Write the default file back. 26003910Sdougm */ 26013910Sdougm 26023910Sdougm static int 26033910Sdougm write_default_file(char *fname, struct deffile *defs) 26043910Sdougm { 26053910Sdougm FILE *file; 26063910Sdougm int ret = SA_OK; 26073910Sdougm sigset_t old, new; 26083910Sdougm 26093910Sdougm file = fopen(fname, "w+"); 26103910Sdougm if (file != NULL) { 26114345Sdougm (void) sigprocmask(SIG_BLOCK, NULL, &new); 26124345Sdougm (void) sigaddset(&new, SIGHUP); 26134345Sdougm (void) sigaddset(&new, SIGINT); 26144345Sdougm (void) sigaddset(&new, SIGQUIT); 26154345Sdougm (void) sigaddset(&new, SIGTSTP); 26164345Sdougm (void) sigprocmask(SIG_SETMASK, &new, &old); 26174345Sdougm while (defs != NULL) { 26184345Sdougm (void) fputs(defs->line, file); 26194345Sdougm defs = defs->next; 26204345Sdougm } 26214345Sdougm (void) fsync(fileno(file)); 26224345Sdougm (void) sigprocmask(SIG_SETMASK, &old, NULL); 26234345Sdougm (void) fclose(file); 26243910Sdougm } else { 26254345Sdougm switch (errno) { 26264345Sdougm case EPERM: 26274345Sdougm case EACCES: 26284345Sdougm ret = SA_NO_PERMISSION; 26294345Sdougm break; 26304345Sdougm default: 26314345Sdougm ret = SA_SYSTEM_ERR; 26324345Sdougm } 26333910Sdougm } 26343910Sdougm return (ret); 26353910Sdougm } 26363910Sdougm 26373910Sdougm 26383910Sdougm /* 26393910Sdougm * set_default_file_value(tag, value) 26403910Sdougm * 26413910Sdougm * Set the default file value for tag to value. Then rewrite the file. 26423910Sdougm * tag and value are always set. The caller must ensure this. 26433910Sdougm */ 26443910Sdougm 26453910Sdougm #define MAX_STRING_LENGTH 256 26463910Sdougm static int 26473910Sdougm set_default_file_value(char *tag, char *value) 26483910Sdougm { 26493910Sdougm int ret = SA_OK; 26503910Sdougm struct deffile *root; 26513910Sdougm struct deffile *defs; 26523910Sdougm struct deffile *prev; 26533910Sdougm char string[MAX_STRING_LENGTH]; 26543910Sdougm int len; 2655*6162Sdougm boolean_t update = B_FALSE; 26563910Sdougm 26573910Sdougm (void) snprintf(string, MAX_STRING_LENGTH, "%s=", tag); 26583910Sdougm len = strlen(string); 26593910Sdougm 26603910Sdougm root = defs = read_default_file(NFSADMIN); 26613910Sdougm if (root == NULL) { 2662*6162Sdougm switch (errno) { 2663*6162Sdougm case EPERM: 2664*6162Sdougm case EACCES: 26654345Sdougm ret = SA_NO_PERMISSION; 2666*6162Sdougm break; 2667*6162Sdougm default: 2668*6162Sdougm ret = SA_NO_MEMORY; 2669*6162Sdougm break; 2670*6162Sdougm } 2671*6162Sdougm return (ret); 2672*6162Sdougm } 2673*6162Sdougm 2674*6162Sdougm while (defs != NULL) { 2675*6162Sdougm if (defs->line != NULL && 2676*6162Sdougm strncasecmp(defs->line, string, len) == 0) { 2677*6162Sdougm /* replace with the new value */ 2678*6162Sdougm free(defs->line); 2679*6162Sdougm fixcaseupper(tag); 2680*6162Sdougm (void) snprintf(string, sizeof (string), 2681*6162Sdougm "%s=%s\n", tag, value); 2682*6162Sdougm string[MAX_STRING_LENGTH - 1] = '\0'; 2683*6162Sdougm defs->line = strdup(string); 2684*6162Sdougm update = B_TRUE; 2685*6162Sdougm break; 2686*6162Sdougm } 2687*6162Sdougm defs = defs->next; 2688*6162Sdougm } 2689*6162Sdougm if (!update) { 2690*6162Sdougm defs = root; 2691*6162Sdougm /* didn't find, so see if it is a comment */ 2692*6162Sdougm (void) snprintf(string, MAX_STRING_LENGTH, "#%s=", tag); 2693*6162Sdougm len = strlen(string); 26943910Sdougm while (defs != NULL) { 2695*6162Sdougm if (strncasecmp(defs->line, string, len) == 0) { 26964345Sdougm /* replace with the new value */ 26974345Sdougm free(defs->line); 26984345Sdougm fixcaseupper(tag); 26994345Sdougm (void) snprintf(string, sizeof (string), 27003910Sdougm "%s=%s\n", tag, value); 27014345Sdougm string[MAX_STRING_LENGTH - 1] = '\0'; 27024345Sdougm defs->line = strdup(string); 2703*6162Sdougm update = B_TRUE; 27044345Sdougm break; 27054345Sdougm } 27064345Sdougm defs = defs->next; 27073910Sdougm } 2708*6162Sdougm } 2709*6162Sdougm if (!update) { 2710*6162Sdougm fixcaseupper(tag); 2711*6162Sdougm (void) snprintf(string, sizeof (string), "%s=%s\n", 2712*6162Sdougm tag, value); 2713*6162Sdougm prev = root; 2714*6162Sdougm while (prev->next != NULL) 2715*6162Sdougm prev = prev->next; 2716*6162Sdougm defs = malloc(sizeof (struct deffile)); 2717*6162Sdougm prev->next = defs; 2718*6162Sdougm if (defs != NULL) { 2719*6162Sdougm defs->next = NULL; 2720*6162Sdougm defs->line = strdup(string); 2721*6162Sdougm update = B_TRUE; 27223910Sdougm } 27233910Sdougm } 2724*6162Sdougm if (update) { 2725*6162Sdougm ret = write_default_file(NFSADMIN, root); 2726*6162Sdougm } 2727*6162Sdougm free_default_file(root); 27283910Sdougm return (ret); 27293910Sdougm } 27303910Sdougm 27313910Sdougm /* 27323910Sdougm * service_in_state(service, chkstate) 27333910Sdougm * 27343910Sdougm * Want to know if the specified service is in the desired state 27353910Sdougm * (chkstate) or not. Return true (1) if it is and false (0) if it 27363910Sdougm * isn't. 27373910Sdougm */ 27383910Sdougm static int 27393910Sdougm service_in_state(char *service, const char *chkstate) 27403910Sdougm { 27413910Sdougm char *state; 27423910Sdougm int ret = B_FALSE; 27433910Sdougm 27443910Sdougm state = smf_get_state(service); 27453910Sdougm if (state != NULL) { 27464345Sdougm /* got the state so get the equality for the return value */ 27474345Sdougm ret = strcmp(state, chkstate) == 0 ? B_TRUE : B_FALSE; 27484345Sdougm free(state); 27493910Sdougm } 27503910Sdougm return (ret); 27513910Sdougm } 27523910Sdougm 27533910Sdougm /* 27543910Sdougm * restart_service(svcs) 27553910Sdougm * 27563910Sdougm * Walk through the bit mask of services that need to be restarted in 27573910Sdougm * order to use the new property values. Some properties affect 27583910Sdougm * multiple daemons. Should only restart a service if it is currently 27593910Sdougm * enabled (online). 27603910Sdougm */ 27613910Sdougm 27623910Sdougm static void 27633910Sdougm restart_service(uint32_t svcs) 27643910Sdougm { 27653910Sdougm uint32_t mask; 27663910Sdougm int ret; 27673910Sdougm char *service; 27683910Sdougm 27693910Sdougm for (mask = 1; svcs != 0; mask <<= 1) { 27704345Sdougm switch (svcs & mask) { 27714345Sdougm case SVC_LOCKD: 27724345Sdougm service = LOCKD; 27734345Sdougm break; 27744345Sdougm case SVC_STATD: 27754345Sdougm service = STATD; 27764345Sdougm break; 27774345Sdougm case SVC_NFSD: 27784345Sdougm service = NFSD; 27794345Sdougm break; 27804345Sdougm case SVC_MOUNTD: 27814345Sdougm service = MOUNTD; 27824345Sdougm break; 27834345Sdougm case SVC_NFS4CBD: 27844345Sdougm service = NFS4CBD; 27854345Sdougm break; 27864345Sdougm case SVC_NFSMAPID: 27874345Sdougm service = NFSMAPID; 27884345Sdougm break; 27894345Sdougm case SVC_RQUOTAD: 27904345Sdougm service = RQUOTAD; 27914345Sdougm break; 27924345Sdougm case SVC_NFSLOGD: 27934345Sdougm service = NFSLOGD; 27944345Sdougm break; 27954345Sdougm default: 27964345Sdougm continue; 27974345Sdougm } 27983910Sdougm 27993910Sdougm /* 28003910Sdougm * Only attempt to restart the service if it is 28013910Sdougm * currently running. In the future, it may be 28023910Sdougm * desirable to use smf_refresh_instance if the NFS 28033910Sdougm * services ever implement the refresh method. 28043910Sdougm */ 28054345Sdougm if (service_in_state(service, SCF_STATE_STRING_ONLINE)) { 28064345Sdougm ret = smf_restart_instance(service); 28073910Sdougm /* 28084345Sdougm * There are only a few SMF errors at this point, but 28094345Sdougm * it is also possible that a bad value may have put 28104345Sdougm * the service into maintenance if there wasn't an 28114345Sdougm * SMF level error. 28123910Sdougm */ 28134345Sdougm if (ret != 0) { 28144345Sdougm (void) fprintf(stderr, 28154345Sdougm dgettext(TEXT_DOMAIN, 28164345Sdougm "%s failed to restart: %s\n"), 28174345Sdougm scf_strerror(scf_error())); 28184345Sdougm } else { 28194345Sdougm /* 28204345Sdougm * Check whether it has gone to "maintenance" 28214345Sdougm * mode or not. Maintenance implies something 28224345Sdougm * went wrong. 28234345Sdougm */ 28244345Sdougm if (service_in_state(service, 28254345Sdougm SCF_STATE_STRING_MAINT)) { 28264345Sdougm (void) fprintf(stderr, 28274345Sdougm dgettext(TEXT_DOMAIN, 28284345Sdougm "%s failed to restart\n"), 28294345Sdougm service); 28304345Sdougm } 28314345Sdougm } 28323910Sdougm } 28334345Sdougm svcs &= ~mask; 28343910Sdougm } 28353910Sdougm } 28363910Sdougm 28373910Sdougm /* 28383910Sdougm * nfs_minmax_check(name, value) 28393910Sdougm * 28403910Sdougm * Verify that the value for the property specified by index is valid 28413910Sdougm * relative to the opposite value in the case of a min/max variable. 28423910Sdougm * Currently, server_minvers/server_maxvers and 28433910Sdougm * client_minvers/client_maxvers are the only ones to check. 28443910Sdougm */ 28453910Sdougm 28463910Sdougm static int 28473910Sdougm nfs_minmax_check(int index, int value) 28483910Sdougm { 28493910Sdougm int val; 28503910Sdougm char *pval; 28513910Sdougm sa_property_t prop; 28523910Sdougm sa_optionset_t opts; 28533910Sdougm int ret = B_TRUE; 28543910Sdougm 28553910Sdougm if (proto_options[index].other != NULL) { 28564345Sdougm /* have a property to compare against */ 28574345Sdougm opts = nfs_get_proto_set(); 28584345Sdougm prop = sa_get_property(opts, proto_options[index].other); 28593910Sdougm /* 28603910Sdougm * If we don't find the property, assume default 28613910Sdougm * values which will work since the max will be at the 28623910Sdougm * max and the min at the min. 28633910Sdougm */ 28644345Sdougm if (prop != NULL) { 28654345Sdougm pval = sa_get_property_attr(prop, "value"); 28664345Sdougm if (pval != NULL) { 28674345Sdougm val = strtoul(pval, NULL, 0); 28684345Sdougm if (proto_options[index].compare == 28694345Sdougm OPT_CMP_LE) { 28704345Sdougm ret = value <= val ? B_TRUE : B_FALSE; 28714345Sdougm } else if (proto_options[index].compare == 28724345Sdougm OPT_CMP_GE) { 28734345Sdougm ret = value >= val ? B_TRUE : B_FALSE; 28744345Sdougm } 28754345Sdougm } 28763910Sdougm } 28773910Sdougm } 28783910Sdougm return (ret); 28793910Sdougm } 28803910Sdougm 28813910Sdougm /* 28823910Sdougm * nfs_validate_proto_prop(index, name, value) 28833910Sdougm * 28845331Samw * Verify that the property specified by name can take the new 28853910Sdougm * value. This is a sanity check to prevent bad values getting into 28863910Sdougm * the default files. All values need to be checked against what is 28873910Sdougm * allowed by their defined type. If a type isn't explicitly defined 28883910Sdougm * here, it is treated as a string. 28893910Sdougm * 28903910Sdougm * Note that OPT_TYPE_NUMBER will additionally check that the value is 28913910Sdougm * within the range specified and potentially against another property 28923910Sdougm * value as well as specified in the proto_options members other and 28933910Sdougm * compare. 28943910Sdougm */ 28953910Sdougm 28963910Sdougm static int 28973910Sdougm nfs_validate_proto_prop(int index, char *name, char *value) 28983910Sdougm { 28993910Sdougm int ret = SA_OK; 29003910Sdougm char *cp; 29013910Sdougm #ifdef lint 29023910Sdougm name = name; 29033910Sdougm #endif 29043910Sdougm 29053910Sdougm switch (proto_options[index].type) { 29063910Sdougm case OPT_TYPE_NUMBER: 29074345Sdougm if (!is_a_number(value)) 29084345Sdougm ret = SA_BAD_VALUE; 29094345Sdougm else { 29104345Sdougm int val; 29114345Sdougm val = strtoul(value, NULL, 0); 29124345Sdougm if (val < proto_options[index].minval || 29134345Sdougm val > proto_options[index].maxval) 29144345Sdougm ret = SA_BAD_VALUE; 29154345Sdougm /* 29164345Sdougm * For server_versmin/server_versmax and 29174345Sdougm * client_versmin/client_versmax, the value of the 29184345Sdougm * min(max) should be checked to be correct relative 29194345Sdougm * to the current max(min). 29204345Sdougm */ 29214345Sdougm if (!nfs_minmax_check(index, val)) { 29224345Sdougm ret = SA_BAD_VALUE; 29234345Sdougm } 29243910Sdougm } 29254345Sdougm break; 29263910Sdougm 29273910Sdougm case OPT_TYPE_DOMAIN: 29283910Sdougm /* 29293910Sdougm * needs to be a qualified domain so will have at 29303910Sdougm * least one period and other characters on either 29313910Sdougm * side of it. A zero length string is also allowed 29323910Sdougm * and is the way to turn off the override. 29333910Sdougm */ 29344345Sdougm if (strlen(value) == 0) 29354345Sdougm break; 29364345Sdougm cp = strchr(value, '.'); 29374345Sdougm if (cp == NULL || cp == value || strchr(value, '@') != NULL) 29384345Sdougm ret = SA_BAD_VALUE; 29393910Sdougm break; 29403910Sdougm 29413910Sdougm case OPT_TYPE_BOOLEAN: 29424345Sdougm if (strlen(value) == 0 || 29434345Sdougm strcasecmp(value, "true") == 0 || 29444345Sdougm strcmp(value, "1") == 0 || 29454345Sdougm strcasecmp(value, "false") == 0 || 29464345Sdougm strcmp(value, "0") == 0) { 29474345Sdougm ret = SA_OK; 29484345Sdougm } else { 29494345Sdougm ret = SA_BAD_VALUE; 29504345Sdougm } 29514345Sdougm break; 29523910Sdougm 29533910Sdougm case OPT_TYPE_ONOFF: 29544345Sdougm if (strcasecmp(value, "on") != 0 && 29554345Sdougm strcasecmp(value, "off") != 0) { 29564345Sdougm ret = SA_BAD_VALUE; 29574345Sdougm } 29584345Sdougm break; 29593910Sdougm 29603910Sdougm case OPT_TYPE_PROTOCOL: 2961*6162Sdougm if (strlen(value) != 0 && 2962*6162Sdougm strcasecmp(value, "all") != 0 && 29634345Sdougm strcasecmp(value, "tcp") != 0 && 29644345Sdougm strcasecmp(value, "udp") != 0) 29654345Sdougm ret = SA_BAD_VALUE; 29664345Sdougm break; 29673910Sdougm 29683910Sdougm default: 29694345Sdougm /* treat as a string */ 29704345Sdougm break; 29713910Sdougm } 29723910Sdougm return (ret); 29733910Sdougm } 29743910Sdougm 29753910Sdougm /* 29763910Sdougm * nfs_set_proto_prop(prop) 29773910Sdougm * 29783910Sdougm * check that prop is valid. 29793910Sdougm */ 29803910Sdougm 29813910Sdougm static int 29823910Sdougm nfs_set_proto_prop(sa_property_t prop) 29833910Sdougm { 29843910Sdougm int ret = SA_OK; 29853910Sdougm char *name; 29863910Sdougm char *value; 29873910Sdougm 29883910Sdougm name = sa_get_property_attr(prop, "type"); 29893910Sdougm value = sa_get_property_attr(prop, "value"); 29903910Sdougm if (name != NULL && value != NULL) { 29914345Sdougm int index = findprotoopt(name, 1); 29924345Sdougm if (index >= 0) { 29934345Sdougm /* should test for valid value */ 29944345Sdougm ret = nfs_validate_proto_prop(index, name, value); 29954345Sdougm if (ret == SA_OK) 29964345Sdougm ret = set_default_file_value( 29974345Sdougm proto_options[index].tag, value); 29984345Sdougm if (ret == SA_OK) 29994345Sdougm restart_service(proto_options[index].svcs); 30004345Sdougm } 30013910Sdougm } 30023910Sdougm if (name != NULL) 30034345Sdougm sa_free_attr_string(name); 30043910Sdougm if (value != NULL) 30054345Sdougm sa_free_attr_string(value); 30063910Sdougm return (ret); 30073910Sdougm } 30083910Sdougm 30093910Sdougm /* 30103910Sdougm * nfs_get_status() 30113910Sdougm * 30123910Sdougm * What is the current status of the nfsd? We use the SMF state here. 30133910Sdougm * Caller must free the returned value. 30143910Sdougm */ 30153910Sdougm 30163910Sdougm static char * 30173910Sdougm nfs_get_status() 30183910Sdougm { 30193910Sdougm char *state; 30203910Sdougm state = smf_get_state(NFSD); 30213910Sdougm return (state != NULL ? state : strdup("-")); 30223910Sdougm } 30233910Sdougm 30243910Sdougm /* 30253910Sdougm * nfs_space_alias(alias) 30263910Sdougm * 30273910Sdougm * Lookup the space (security) name. If it is default, convert to the 30283910Sdougm * real name. 30293910Sdougm */ 30303910Sdougm 30313910Sdougm static char * 30323910Sdougm nfs_space_alias(char *space) 30333910Sdougm { 30343910Sdougm char *name = space; 30353910Sdougm seconfig_t secconf; 30363910Sdougm 30373910Sdougm /* 30383910Sdougm * Only the space named "default" is special. If it is used, 30393910Sdougm * the default needs to be looked up and the real name used. 30403910Sdougm * This is normally "sys" but could be changed. We always 30413910Sdougm * change defautl to the real name. 30423910Sdougm */ 30433910Sdougm if (strcmp(space, "default") == 0 && 30443910Sdougm nfs_getseconfig_default(&secconf) == 0) { 30454345Sdougm if (nfs_getseconfig_bynumber(secconf.sc_nfsnum, &secconf) == 0) 30464345Sdougm name = secconf.sc_name; 30473910Sdougm } 30483910Sdougm return (strdup(name)); 30493910Sdougm } 30505331Samw 30515331Samw /* 30525331Samw * nfs_features() 30535331Samw * 30545331Samw * Return a mask of the features required. 30555331Samw */ 30565331Samw 30575331Samw static uint64_t 30585331Samw nfs_features() 30595331Samw { 30606088Sdougm return ((uint64_t)SA_FEATURE_DFSTAB | SA_FEATURE_SERVER); 30615331Samw } 3062