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 *); 676214Sdougm static int nfs_validate_property(sa_handle_t, 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 /* 15956214Sdougm * public_exists(handle, 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 16026214Sdougm public_exists(sa_handle_t handle, sa_share_t skipshare) 16033910Sdougm { 16046214Sdougm sa_group_t group = NULL; 16053910Sdougm 1606*6271Sdougm /* 1607*6271Sdougm * If we don't have a handle, we can only do syntax check. We 1608*6271Sdougm * can't check against other shares so we assume OK and will 1609*6271Sdougm * catch the problem only when we actually try to apply it. 1610*6271Sdougm */ 16113910Sdougm if (handle == NULL) 1612*6271Sdougm return (SA_OK); 16133910Sdougm 16146214Sdougm if (skipshare != NULL) { 16156214Sdougm group = sa_get_parent_group(skipshare); 16166214Sdougm if (group == NULL) 16176214Sdougm return (SA_NO_SUCH_GROUP); 16186214Sdougm } 16196214Sdougm 16203910Sdougm for (group = sa_get_group(handle, NULL); group != NULL; 16213910Sdougm group = sa_get_next_group(group)) { 16224524Sdougm /* Walk any ZFS subgroups as well as all standard groups */ 16234524Sdougm if (sa_group_is_zfs(group)) { 16244524Sdougm sa_group_t subgroup; 16254524Sdougm for (subgroup = sa_get_sub_group(group); 16264524Sdougm subgroup != NULL; 16274524Sdougm subgroup = sa_get_next_group(subgroup)) { 16284524Sdougm if (check_public(subgroup, skipshare)) 16294524Sdougm return (B_TRUE); 16303910Sdougm } 16314524Sdougm } else { 16324524Sdougm if (check_public(group, skipshare)) 16334524Sdougm return (B_TRUE); 16343910Sdougm } 16353910Sdougm } 16364524Sdougm return (B_FALSE); 16373910Sdougm } 16383910Sdougm 16393910Sdougm /* 16403910Sdougm * sa_enable_share at the protocol level, enable_share must tell the 16413910Sdougm * implementation that it is to enable the share. This entails 16423910Sdougm * converting the path and options into the appropriate ioctl 16433910Sdougm * calls. It is assumed that all error checking of paths, etc. were 16443910Sdougm * done earlier. 16453910Sdougm */ 16463910Sdougm static int 16473910Sdougm nfs_enable_share(sa_share_t share) 16483910Sdougm { 16493910Sdougm struct exportdata export; 16503910Sdougm sa_optionset_t secoptlist; 16513910Sdougm struct secinfo *sp; 16523910Sdougm int num_secinfo; 16533910Sdougm sa_optionset_t opt; 16543910Sdougm sa_security_t sec; 16553910Sdougm sa_property_t prop; 16563910Sdougm char *path; 16573910Sdougm int err = SA_OK; 16584524Sdougm int i; 16594543Smarks int iszfs; 16606214Sdougm sa_handle_t handle; 16613910Sdougm 16623910Sdougm /* Don't drop core if the NFS module isn't loaded. */ 16633910Sdougm (void) signal(SIGSYS, SIG_IGN); 16643910Sdougm 16653910Sdougm /* get the path since it is important in several places */ 16663910Sdougm path = sa_get_share_attr(share, "path"); 16673910Sdougm if (path == NULL) 16684345Sdougm return (SA_NO_SUCH_PATH); 16693910Sdougm 16704543Smarks iszfs = sa_path_is_zfs(path); 16713910Sdougm /* 16723910Sdougm * find the optionsets and security sets. There may not be 16733910Sdougm * any or there could be one or two for each of optionset and 16743910Sdougm * security may have multiple, one per security type per 16753910Sdougm * protocol type. 16763910Sdougm */ 16773910Sdougm opt = sa_get_derived_optionset(share, "nfs", 1); 16783910Sdougm secoptlist = (sa_optionset_t)sa_get_all_security_types(share, "nfs", 1); 16793910Sdougm if (secoptlist != NULL) 16804345Sdougm num_secinfo = MAX(1, count_security(secoptlist)); 16813910Sdougm else 16824345Sdougm num_secinfo = 1; 16833910Sdougm 16843910Sdougm /* 16853910Sdougm * walk through the options and fill in the structure 16863910Sdougm * appropriately. 16873910Sdougm */ 16883910Sdougm 16893910Sdougm (void) memset(&export, '\0', sizeof (export)); 16903910Sdougm 16913910Sdougm /* 16923910Sdougm * do non-security options first since there is only one after 16933910Sdougm * the derived group is constructed. 16943910Sdougm */ 16953910Sdougm export.ex_version = EX_CURRENT_VERSION; 16963910Sdougm export.ex_anon = UID_NOBODY; /* this is our default value */ 16973910Sdougm export.ex_index = NULL; 16983910Sdougm export.ex_path = path; 16993910Sdougm export.ex_pathlen = strlen(path) + 1; 17003910Sdougm 17013910Sdougm if (opt != NULL) 17024345Sdougm err = fill_export_from_optionset(&export, opt); 17033910Sdougm 17043910Sdougm /* 17053910Sdougm * check to see if "public" is set. If it is, then make sure 17063910Sdougm * no other share has it set. If it is already used, fail. 17073910Sdougm */ 17083910Sdougm 17096214Sdougm handle = sa_find_group_handle((sa_group_t)share); 17106214Sdougm if (export.ex_flags & EX_PUBLIC && public_exists(handle, share)) { 17114345Sdougm (void) printf(dgettext(TEXT_DOMAIN, 17124345Sdougm "NFS: Cannot share more than one file " 17134345Sdougm "system with 'public' property\n")); 17144345Sdougm err = SA_NOT_ALLOWED; 17154345Sdougm goto out; 17163910Sdougm } 17173910Sdougm 17184524Sdougm sp = calloc(num_secinfo, sizeof (struct secinfo)); 17193910Sdougm if (sp == NULL) { 17204345Sdougm err = SA_NO_MEMORY; 17214524Sdougm (void) printf(dgettext(TEXT_DOMAIN, 17224524Sdougm "NFS: NFS: no memory for security\n")); 17234524Sdougm goto out; 17244524Sdougm } 17254524Sdougm export.ex_secinfo = sp; 17264524Sdougm /* get default secinfo */ 17274524Sdougm export.ex_seccnt = num_secinfo; 17284524Sdougm /* 17294524Sdougm * since we must have one security option defined, we 17304524Sdougm * init to the default and then override as we find 17314524Sdougm * defined security options. This handles the case 17324524Sdougm * where we have no defined options but we need to set 17334524Sdougm * up one. 17344524Sdougm */ 17354524Sdougm sp[0].s_window = DEF_WIN; 17364524Sdougm sp[0].s_rootnames = NULL; 17374524Sdougm /* setup a default in case no properties defined */ 17384524Sdougm if (nfs_getseconfig_default(&sp[0].s_secinfo)) { 17394524Sdougm (void) printf(dgettext(TEXT_DOMAIN, 17404524Sdougm "NFS: nfs_getseconfig_default: failed to " 17414524Sdougm "get default security mode\n")); 17424524Sdougm err = SA_CONFIG_ERR; 17434524Sdougm } 17444524Sdougm if (secoptlist != NULL) { 17454524Sdougm for (i = 0, prop = sa_get_property(secoptlist, NULL); 17464524Sdougm prop != NULL && i < num_secinfo; 17474524Sdougm prop = sa_get_next_property(prop), i++) { 17484524Sdougm char *sectype; 17494345Sdougm sectype = sa_get_property_attr(prop, "type"); 17504524Sdougm /* 17514524Sdougm * if sectype is NULL, we probably 17524524Sdougm * have a memory problem and can't get 17534524Sdougm * the correct values. Rather than 17544524Sdougm * exporting with incorrect security, 17554524Sdougm * don't share it. 17564524Sdougm */ 17574524Sdougm if (sectype == NULL) { 17584524Sdougm err = SA_NO_MEMORY; 17594524Sdougm (void) printf(dgettext(TEXT_DOMAIN, 17604524Sdougm "NFS: Cannot share %s: " 17614524Sdougm "no memory\n"), path); 17624524Sdougm goto out; 17634524Sdougm } 17644524Sdougm sec = (sa_security_t)sa_get_derived_security( 17654524Sdougm share, sectype, "nfs", 1); 17664524Sdougm sp[i].s_window = DEF_WIN; 17674524Sdougm sp[i].s_rootcnt = 0; 17684524Sdougm sp[i].s_rootnames = NULL; 17694345Sdougm (void) fill_security_from_secopts(&sp[i], sec); 17704524Sdougm if (sec != NULL) 17714524Sdougm sa_free_derived_security(sec); 17724524Sdougm if (sectype != NULL) 17734524Sdougm sa_free_attr_string(sectype); 17743910Sdougm } 17754524Sdougm } 17764524Sdougm /* 17774524Sdougm * when we get here, we can do the exportfs system call and 17784524Sdougm * initiate thinsg. We probably want to enable the nfs.server 17794524Sdougm * service first if it isn't running within SMF. 17804524Sdougm */ 17814524Sdougm /* check nfs.server status and start if needed */ 17824524Sdougm /* now add the share to the internal tables */ 17834524Sdougm printarg(path, &export); 17844524Sdougm /* 17854524Sdougm * call the exportfs system call which is implemented 17864524Sdougm * via the nfssys() call as the EXPORTFS subfunction. 17874524Sdougm */ 17884543Smarks if (iszfs) { 17894543Smarks struct exportfs_args ea; 17904543Smarks share_t sh; 17914543Smarks char *str; 17924543Smarks priv_set_t *priv_effective; 17934543Smarks int privileged; 17944543Smarks 17954543Smarks /* 17964543Smarks * If we aren't a privileged user 17974543Smarks * and NFS server service isn't running 17984543Smarks * then print out an error message 17994543Smarks * and return EPERM 18004543Smarks */ 18014543Smarks 18024543Smarks priv_effective = priv_allocset(); 18034543Smarks (void) getppriv(PRIV_EFFECTIVE, priv_effective); 18044543Smarks 18054543Smarks privileged = (priv_isfullset(priv_effective) == B_TRUE); 18064543Smarks priv_freeset(priv_effective); 18074543Smarks 18084543Smarks if (!privileged && 18094543Smarks (str = smf_get_state(NFS_SERVER_SVC)) != NULL) { 18104543Smarks err = 0; 18114543Smarks if (strcmp(str, SCF_STATE_STRING_ONLINE) != 0) { 18124543Smarks (void) printf(dgettext(TEXT_DOMAIN, 18134543Smarks "NFS: Cannot share remote " 18144543Smarks "filesystem: %s\n"), path); 18154543Smarks (void) printf(dgettext(TEXT_DOMAIN, 18164543Smarks "NFS: Service needs to be enabled " 18174543Smarks "by a privileged user\n")); 18184543Smarks err = SA_SYSTEM_ERR; 18194543Smarks errno = EPERM; 18204543Smarks } 18214543Smarks free(str); 18224543Smarks } 18234543Smarks 18244543Smarks if (err == 0) { 18254543Smarks ea.dname = path; 18264543Smarks ea.uex = &export; 18274543Smarks 18284543Smarks sa_sharetab_fill_zfs(share, &sh, "nfs"); 18295331Samw err = sa_share_zfs(share, path, &sh, 18305331Samw &ea, ZFS_SHARE_NFS); 18314543Smarks sa_emptyshare(&sh); 18324543Smarks } 18334543Smarks } else { 18344543Smarks err = exportfs(path, &export); 18354543Smarks } 18364543Smarks 18374543Smarks if (err < 0) { 18384524Sdougm err = SA_SYSTEM_ERR; 18394524Sdougm switch (errno) { 18404524Sdougm case EREMOTE: 18414524Sdougm (void) printf(dgettext(TEXT_DOMAIN, 18424543Smarks "NFS: Cannot share filesystems " 18434543Smarks "in non-global zones: %s\n"), path); 18444543Smarks err = SA_NOT_SUPPORTED; 18454524Sdougm break; 18464524Sdougm case EPERM: 18474524Sdougm if (getzoneid() != GLOBAL_ZONEID) { 18484345Sdougm (void) printf(dgettext(TEXT_DOMAIN, 18494543Smarks "NFS: Cannot share file systems " 18504524Sdougm "in non-global zones: %s\n"), path); 18514524Sdougm err = SA_NOT_SUPPORTED; 18524345Sdougm break; 18534345Sdougm } 18544524Sdougm err = SA_NO_PERMISSION; 18554524Sdougm /* FALLTHROUGH */ 18564524Sdougm default: 18574524Sdougm break; 18583910Sdougm } 18594524Sdougm } else { 18604524Sdougm /* update sharetab with an add/modify */ 18614543Smarks if (!iszfs) { 18624543Smarks (void) sa_update_sharetab(share, "nfs"); 18634543Smarks } 18643910Sdougm } 18653910Sdougm 18663910Sdougm if (err == SA_OK) { 18673910Sdougm /* 18683910Sdougm * enable services as needed. This should probably be 18693910Sdougm * done elsewhere in order to minimize the calls to 18703910Sdougm * check services. 18713910Sdougm */ 18723910Sdougm /* 18733910Sdougm * check to see if logging and other services need to 18743910Sdougm * be triggered, but only if there wasn't an 18753910Sdougm * error. This is probably where sharetab should be 18763910Sdougm * updated with the NFS specific entry. 18773910Sdougm */ 18784345Sdougm if (export.ex_flags & EX_LOG) { 18794345Sdougm /* enable logging */ 18804345Sdougm if (nfslogtab_add(path, export.ex_log_buffer, 18814345Sdougm export.ex_tag) != 0) { 18824345Sdougm (void) fprintf(stderr, dgettext(TEXT_DOMAIN, 18834345Sdougm "Could not enable logging for %s\n"), 18844345Sdougm path); 18854345Sdougm } 18864345Sdougm _check_services(service_list_logging); 18874345Sdougm } else { 18884345Sdougm /* 18894345Sdougm * don't have logging so remove it from file. It might 18904345Sdougm * not be thre, but that doesn't matter. 18914345Sdougm */ 18924345Sdougm (void) nfslogtab_deactivate(path); 18934345Sdougm _check_services(service_list_default); 18943910Sdougm } 18953910Sdougm } 18963910Sdougm 18973910Sdougm out: 18983910Sdougm if (path != NULL) 18994345Sdougm free(path); 19003910Sdougm 19013910Sdougm cleanup_export(&export); 19023910Sdougm if (opt != NULL) 19034345Sdougm sa_free_derived_optionset(opt); 19043910Sdougm if (secoptlist != NULL) 19054345Sdougm (void) sa_destroy_optionset(secoptlist); 19063910Sdougm return (err); 19073910Sdougm } 19083910Sdougm 19093910Sdougm /* 19105800Sdougm * nfs_disable_share(share, path) 19113910Sdougm * 19125800Sdougm * Unshare the specified share. Note that "path" is the same path as 19135800Sdougm * what is in the "share" object. It is passed in to avoid an 19145800Sdougm * additional lookup. A missing "path" value makes this a no-op 19155800Sdougm * function. 19163910Sdougm */ 19173910Sdougm static int 19184543Smarks nfs_disable_share(sa_share_t share, char *path) 19193910Sdougm { 19203910Sdougm int err; 19213910Sdougm int ret = SA_OK; 19224543Smarks int iszfs; 19235800Sdougm sa_group_t parent; 19245951Sdougm sa_handle_t handle; 19254543Smarks 19265800Sdougm if (path == NULL) 19275800Sdougm return (ret); 19284543Smarks 19295800Sdougm /* 19305800Sdougm * If the share is in a ZFS group we need to handle it 19315800Sdougm * differently. Just being on a ZFS file system isn't 19325800Sdougm * enough since we may be in a legacy share case. 19335800Sdougm */ 19345800Sdougm parent = sa_get_parent_group(share); 19355800Sdougm iszfs = sa_group_is_zfs(parent); 19365800Sdougm if (iszfs) { 19375800Sdougm struct exportfs_args ea; 19385800Sdougm share_t sh = { 0 }; 19395800Sdougm ea.dname = path; 19405800Sdougm ea.uex = NULL; 19415800Sdougm sh.sh_path = path; 19425800Sdougm sh.sh_fstype = "nfs"; 19434543Smarks 19445800Sdougm err = sa_share_zfs(share, path, &sh, 19455800Sdougm &ea, ZFS_UNSHARE_NFS); 19465800Sdougm } else { 19475800Sdougm err = exportfs(path, NULL); 19485800Sdougm } 19495800Sdougm if (err < 0) { 19505800Sdougm /* 19515800Sdougm * TBD: only an error in some 19525800Sdougm * cases - need better analysis 19535800Sdougm */ 19545800Sdougm switch (errno) { 19555800Sdougm case EPERM: 19565800Sdougm case EACCES: 19575800Sdougm ret = SA_NO_PERMISSION; 19585800Sdougm if (getzoneid() != GLOBAL_ZONEID) { 19595800Sdougm ret = SA_NOT_SUPPORTED; 19605800Sdougm } 19615800Sdougm break; 19625800Sdougm case EINVAL: 19635800Sdougm case ENOENT: 19645800Sdougm ret = SA_NO_SUCH_PATH; 19654543Smarks break; 19664345Sdougm default: 19674345Sdougm ret = SA_SYSTEM_ERR; 19684543Smarks break; 19693910Sdougm } 19705800Sdougm } 19715800Sdougm if (ret == SA_OK || ret == SA_NO_SUCH_PATH) { 19725951Sdougm handle = sa_find_group_handle((sa_group_t)share); 19735800Sdougm if (!iszfs) 19745951Sdougm (void) sa_delete_sharetab(handle, path, "nfs"); 19755800Sdougm /* just in case it was logged */ 19765800Sdougm (void) nfslogtab_deactivate(path); 19773910Sdougm } 19783910Sdougm return (ret); 19793910Sdougm } 19803910Sdougm 19813910Sdougm /* 19823910Sdougm * check ro vs rw values. Over time this may get beefed up. 19833910Sdougm * for now it just does simple checks. 19843910Sdougm */ 19853910Sdougm 19863910Sdougm static int 19873910Sdougm check_rorw(char *v1, char *v2) 19883910Sdougm { 19893910Sdougm int ret = SA_OK; 19903910Sdougm if (strcmp(v1, v2) == 0) 19914345Sdougm ret = SA_VALUE_CONFLICT; 19923910Sdougm return (ret); 19933910Sdougm } 19943910Sdougm 19953910Sdougm /* 19966214Sdougm * nfs_validate_property(handle, property, parent) 19973910Sdougm * 19983910Sdougm * Check that the property has a legitimate value for its type. 19993910Sdougm */ 20003910Sdougm 20013910Sdougm static int 20026214Sdougm nfs_validate_property(sa_handle_t handle, sa_property_t property, 20036214Sdougm sa_optionset_t parent) 20043910Sdougm { 20053910Sdougm int ret = SA_OK; 20063910Sdougm char *propname; 20073910Sdougm char *other; 20083910Sdougm int optindex; 20093910Sdougm nfsl_config_t *configlist; 20103910Sdougm sa_group_t parent_group; 20113910Sdougm char *value; 20123910Sdougm 20133910Sdougm propname = sa_get_property_attr(property, "type"); 20143910Sdougm 20153910Sdougm if ((optindex = findopt(propname)) < 0) 20164345Sdougm ret = SA_NO_SUCH_PROP; 20173910Sdougm 20183910Sdougm /* need to validate value range here as well */ 20193910Sdougm 20203910Sdougm if (ret == SA_OK) { 20214345Sdougm parent_group = sa_get_parent_group((sa_share_t)parent); 20226214Sdougm if (optdefs[optindex].share && parent_group != NULL && 20236214Sdougm !sa_is_share(parent_group)) 20244345Sdougm ret = SA_PROP_SHARE_ONLY; 20253910Sdougm } 20263910Sdougm if (ret == SA_OK) { 20276214Sdougm if (optdefs[optindex].index == OPT_PUBLIC) { 20286214Sdougm /* 20296214Sdougm * Public is special in that only one instance can 20306214Sdougm * be in the repository at the same time. 20316214Sdougm */ 20326214Sdougm if (public_exists(handle, parent_group)) { 20336214Sdougm if (propname != NULL) 20346214Sdougm sa_free_attr_string(propname); 20356214Sdougm return (SA_VALUE_CONFLICT); 20366214Sdougm } 20376214Sdougm } 20384345Sdougm value = sa_get_property_attr(property, "value"); 20394345Sdougm if (value != NULL) { 20404345Sdougm /* first basic type checking */ 20414345Sdougm switch (optdefs[optindex].type) { 20424345Sdougm case OPT_TYPE_NUMBER: 20434345Sdougm /* check that the value is all digits */ 20444345Sdougm if (!is_a_number(value)) 20454345Sdougm ret = SA_BAD_VALUE; 20464345Sdougm break; 20474345Sdougm case OPT_TYPE_BOOLEAN: 20484345Sdougm if (strlen(value) == 0 || 20494345Sdougm strcasecmp(value, "true") == 0 || 20504345Sdougm strcmp(value, "1") == 0 || 20514345Sdougm strcasecmp(value, "false") == 0 || 20524345Sdougm strcmp(value, "0") == 0) { 20534345Sdougm ret = SA_OK; 20544345Sdougm } else { 20554345Sdougm ret = SA_BAD_VALUE; 20564345Sdougm } 20574345Sdougm break; 20584345Sdougm case OPT_TYPE_USER: 20594345Sdougm if (!is_a_number(value)) { 20604345Sdougm struct passwd *pw; 20614345Sdougm /* 20624345Sdougm * in this case it would have to be a 20634345Sdougm * user name 20644345Sdougm */ 20654345Sdougm pw = getpwnam(value); 20664345Sdougm if (pw == NULL) 20674345Sdougm ret = SA_BAD_VALUE; 20684345Sdougm endpwent(); 20694345Sdougm } else { 20704345Sdougm uint64_t intval; 20714345Sdougm intval = strtoull(value, NULL, 0); 20724345Sdougm if (intval > UID_MAX && intval != ~0) 20734345Sdougm ret = SA_BAD_VALUE; 20744345Sdougm } 20754345Sdougm break; 20764345Sdougm case OPT_TYPE_FILE: 20774345Sdougm if (strcmp(value, "..") == 0 || 20784345Sdougm strchr(value, '/') != NULL) { 20794345Sdougm ret = SA_BAD_VALUE; 20804345Sdougm } 20814345Sdougm break; 20824345Sdougm case OPT_TYPE_ACCLIST: 20834345Sdougm /* 20844345Sdougm * access list handling. Should eventually 20854345Sdougm * validate that all the values make sense. 20864345Sdougm * Also, ro and rw may have cross value 20874345Sdougm * conflicts. 20884345Sdougm */ 20894345Sdougm if (strcmp(propname, SHOPT_RO) == 0) 20904345Sdougm other = SHOPT_RW; 20914345Sdougm else if (strcmp(propname, SHOPT_RW) == 0) 20924345Sdougm other = SHOPT_RO; 20934345Sdougm else 20944345Sdougm other = NULL; 20954345Sdougm 20964345Sdougm if (other != NULL && parent != NULL) { 20974345Sdougm /* compare rw(ro) with ro(rw) */ 20984345Sdougm sa_property_t oprop; 20994345Sdougm oprop = sa_get_property(parent, other); 21004345Sdougm if (oprop != NULL) { 21014345Sdougm /* 21024345Sdougm * only potential 21034345Sdougm * confusion if other 21044345Sdougm * exists 21054345Sdougm */ 21064345Sdougm char *ovalue; 21074345Sdougm ovalue = sa_get_property_attr( 21084345Sdougm oprop, "value"); 21094345Sdougm if (ovalue != NULL) { 21104345Sdougm ret = check_rorw(value, 21114345Sdougm ovalue); 21124345Sdougm sa_free_attr_string( 21134345Sdougm ovalue); 21144345Sdougm } 21154345Sdougm } 21164345Sdougm } 21174345Sdougm break; 21184345Sdougm case OPT_TYPE_LOGTAG: 21194345Sdougm if (nfsl_getconfig_list(&configlist) == 0) { 21204345Sdougm int error; 21214345Sdougm if (value == NULL || 21224345Sdougm strlen(value) == 0) { 21234345Sdougm if (value != NULL) 21244345Sdougm sa_free_attr_string( 21254345Sdougm value); 21264345Sdougm value = strdup("global"); 21274345Sdougm } 21284345Sdougm if (value != NULL && 21294345Sdougm nfsl_findconfig(configlist, value, 21304345Sdougm &error) == NULL) { 21314345Sdougm ret = SA_BAD_VALUE; 21324345Sdougm } 21335179Sdougm /* Must always free when done */ 21345179Sdougm nfsl_freeconfig_list(&configlist); 21354345Sdougm } else { 21364345Sdougm ret = SA_CONFIG_ERR; 21374345Sdougm } 21384345Sdougm break; 21394345Sdougm case OPT_TYPE_STRING: 21404345Sdougm /* whatever is here should be ok */ 21414345Sdougm break; 21424345Sdougm case OPT_TYPE_SECURITY: 21434345Sdougm /* 21444345Sdougm * The "sec" property isn't used in the 21454345Sdougm * non-legacy parts of sharemgr. We need to 21464345Sdougm * reject it here. For legacy, it is pulled 21474345Sdougm * out well before we get here. 21484345Sdougm */ 21494345Sdougm ret = SA_NO_SUCH_PROP; 21504345Sdougm break; 21514345Sdougm default: 21524345Sdougm break; 21533910Sdougm } 21545179Sdougm 21555179Sdougm if (value != NULL) 21565179Sdougm sa_free_attr_string(value); 21575179Sdougm 21584345Sdougm if (ret == SA_OK && optdefs[optindex].check != NULL) { 21594345Sdougm /* do the property specific check */ 21606214Sdougm ret = optdefs[optindex].check(handle, property); 21613910Sdougm } 21623910Sdougm } 21633910Sdougm } 21643910Sdougm 21653910Sdougm if (propname != NULL) 21664345Sdougm sa_free_attr_string(propname); 21673910Sdougm return (ret); 21683910Sdougm } 21693910Sdougm 21703910Sdougm /* 21713910Sdougm * Protocol management functions 21723910Sdougm * 21733910Sdougm * Properties defined in the default files are defined in 21743910Sdougm * proto_option_defs for parsing and validation. If "other" and 21753910Sdougm * "compare" are set, then the value for this property should be 21763910Sdougm * compared against the property specified in "other" using the 21773910Sdougm * "compare" check (either <= or >=) in order to ensure that the 21783910Sdougm * values are in the correct range. E.g. setting server_versmin 21793910Sdougm * higher than server_versmax should not be allowed. 21803910Sdougm */ 21813910Sdougm 21823910Sdougm struct proto_option_defs { 21833910Sdougm char *tag; 21843910Sdougm char *name; /* display name -- remove protocol identifier */ 21853910Sdougm int index; 21863910Sdougm int type; 21873910Sdougm union { 21883910Sdougm int intval; 21893910Sdougm char *string; 21903910Sdougm } defvalue; 21913910Sdougm uint32_t svcs; 21923910Sdougm int32_t minval; 21933910Sdougm int32_t maxval; 21943910Sdougm char *file; 21953910Sdougm char *other; 21963910Sdougm int compare; 21973910Sdougm #define OPT_CMP_GE 0 21983910Sdougm #define OPT_CMP_LE 1 21993910Sdougm int (*check)(char *); 22003910Sdougm } proto_options[] = { 22013910Sdougm #define PROTO_OPT_NFSD_SERVERS 0 22023910Sdougm {"nfsd_servers", 22033910Sdougm "servers", PROTO_OPT_NFSD_SERVERS, OPT_TYPE_NUMBER, 16, SVC_NFSD, 22043910Sdougm 1, INT32_MAX, NFSADMIN}, 22053910Sdougm #define PROTO_OPT_LOCKD_LISTEN_BACKLOG 1 22063910Sdougm {"lockd_listen_backlog", 22073910Sdougm "lockd_listen_backlog", PROTO_OPT_LOCKD_LISTEN_BACKLOG, 22083910Sdougm OPT_TYPE_NUMBER, 32, SVC_LOCKD, 32, INT32_MAX, NFSADMIN}, 22093910Sdougm #define PROTO_OPT_LOCKD_SERVERS 2 22103910Sdougm {"lockd_servers", 22113910Sdougm "lockd_servers", PROTO_OPT_LOCKD_SERVERS, OPT_TYPE_NUMBER, 20, 22123910Sdougm SVC_LOCKD, 1, INT32_MAX, NFSADMIN}, 22133910Sdougm #define PROTO_OPT_LOCKD_RETRANSMIT_TIMEOUT 3 22143910Sdougm {"lockd_retransmit_timeout", 22153910Sdougm "lockd_retransmit_timeout", PROTO_OPT_LOCKD_RETRANSMIT_TIMEOUT, 22163910Sdougm OPT_TYPE_NUMBER, 5, SVC_LOCKD, 0, INT32_MAX, NFSADMIN}, 22173910Sdougm #define PROTO_OPT_GRACE_PERIOD 4 22183910Sdougm {"grace_period", 22193910Sdougm "grace_period", PROTO_OPT_GRACE_PERIOD, OPT_TYPE_NUMBER, 90, 22203910Sdougm SVC_LOCKD, 0, INT32_MAX, NFSADMIN}, 22213910Sdougm #define PROTO_OPT_NFS_SERVER_VERSMIN 5 22223910Sdougm {"nfs_server_versmin", 22233910Sdougm "server_versmin", PROTO_OPT_NFS_SERVER_VERSMIN, OPT_TYPE_NUMBER, 22243910Sdougm (int)NFS_VERSMIN_DEFAULT, SVC_NFSD|SVC_MOUNTD, NFS_VERSMIN, 22253910Sdougm NFS_VERSMAX, NFSADMIN, "server_versmax", OPT_CMP_LE}, 22263910Sdougm #define PROTO_OPT_NFS_SERVER_VERSMAX 6 22273910Sdougm {"nfs_server_versmax", 22283910Sdougm "server_versmax", PROTO_OPT_NFS_SERVER_VERSMAX, OPT_TYPE_NUMBER, 22293910Sdougm (int)NFS_VERSMAX_DEFAULT, SVC_NFSD|SVC_MOUNTD, NFS_VERSMIN, 22303910Sdougm NFS_VERSMAX, NFSADMIN, "server_versmin", OPT_CMP_GE}, 22313910Sdougm #define PROTO_OPT_NFS_CLIENT_VERSMIN 7 22323910Sdougm {"nfs_client_versmin", 22333910Sdougm "client_versmin", PROTO_OPT_NFS_CLIENT_VERSMIN, OPT_TYPE_NUMBER, 22343910Sdougm (int)NFS_VERSMIN_DEFAULT, NULL, NFS_VERSMIN, NFS_VERSMAX, 22353910Sdougm NFSADMIN, "client_versmax", OPT_CMP_LE}, 22363910Sdougm #define PROTO_OPT_NFS_CLIENT_VERSMAX 8 22373910Sdougm {"nfs_client_versmax", 22383910Sdougm "client_versmax", PROTO_OPT_NFS_CLIENT_VERSMAX, OPT_TYPE_NUMBER, 22393910Sdougm (int)NFS_VERSMAX_DEFAULT, NULL, NFS_VERSMIN, NFS_VERSMAX, 22403910Sdougm NFSADMIN, "client_versmin", OPT_CMP_GE}, 22413910Sdougm #define PROTO_OPT_NFS_SERVER_DELEGATION 9 22423910Sdougm {"nfs_server_delegation", 22433910Sdougm "server_delegation", PROTO_OPT_NFS_SERVER_DELEGATION, 22443910Sdougm OPT_TYPE_ONOFF, NFS_SERVER_DELEGATION_DEFAULT, SVC_NFSD, 0, 0, 22453910Sdougm NFSADMIN}, 22463910Sdougm #define PROTO_OPT_NFSMAPID_DOMAIN 10 22473910Sdougm {"nfsmapid_domain", 22483910Sdougm "nfsmapid_domain", PROTO_OPT_NFSMAPID_DOMAIN, OPT_TYPE_DOMAIN, 22493910Sdougm NULL, SVC_NFSMAPID, 0, 0, NFSADMIN}, 22503910Sdougm #define PROTO_OPT_NFSD_MAX_CONNECTIONS 11 22513910Sdougm {"nfsd_max_connections", 22523910Sdougm "max_connections", PROTO_OPT_NFSD_MAX_CONNECTIONS, 22533910Sdougm OPT_TYPE_NUMBER, -1, SVC_NFSD, -1, INT32_MAX, NFSADMIN}, 22543910Sdougm #define PROTO_OPT_NFSD_PROTOCOL 12 22553910Sdougm {"nfsd_protocol", 22563910Sdougm "protocol", PROTO_OPT_NFSD_PROTOCOL, OPT_TYPE_PROTOCOL, 0, 22573910Sdougm SVC_NFSD, 0, 0, NFSADMIN}, 22583910Sdougm #define PROTO_OPT_NFSD_LISTEN_BACKLOG 13 22593910Sdougm {"nfsd_listen_backlog", 22603910Sdougm "listen_backlog", PROTO_OPT_NFSD_LISTEN_BACKLOG, 22613910Sdougm OPT_TYPE_NUMBER, 0, 22623910Sdougm SVC_LOCKD, 0, INT32_MAX, NFSADMIN}, 22633910Sdougm {NULL} 22643910Sdougm }; 22653910Sdougm 22663910Sdougm /* 22673910Sdougm * the protoset holds the defined options so we don't have to read 22683910Sdougm * them multiple times 22693910Sdougm */ 22705179Sdougm static sa_protocol_properties_t protoset; 22713910Sdougm 22723910Sdougm static int 22733910Sdougm findprotoopt(char *name, int whichname) 22743910Sdougm { 22753910Sdougm int i; 22763910Sdougm for (i = 0; proto_options[i].tag != NULL; i++) { 22774345Sdougm if (whichname == 1) { 22784345Sdougm if (strcasecmp(proto_options[i].name, name) == 0) 22793910Sdougm return (i); 22804345Sdougm } else { 22814345Sdougm if (strcasecmp(proto_options[i].tag, name) == 0) 22824345Sdougm return (i); 22834345Sdougm } 22843910Sdougm } 22853910Sdougm return (-1); 22863910Sdougm } 22873910Sdougm 22883910Sdougm /* 22893910Sdougm * fixcaselower(str) 22903910Sdougm * 22913910Sdougm * convert a string to lower case (inplace). 22923910Sdougm */ 22933910Sdougm 22943910Sdougm static void 22953910Sdougm fixcaselower(char *str) 22963910Sdougm { 22973910Sdougm while (*str) { 22984345Sdougm *str = tolower(*str); 22994345Sdougm str++; 23003910Sdougm } 23013910Sdougm } 23023910Sdougm 23033910Sdougm /* 23043910Sdougm * fixcaseupper(str) 23053910Sdougm * 23063910Sdougm * convert a string to upper case (inplace). 23073910Sdougm */ 23083910Sdougm 23093910Sdougm static void 23103910Sdougm fixcaseupper(char *str) 23113910Sdougm { 23123910Sdougm while (*str) { 23134345Sdougm *str = toupper(*str); 23144345Sdougm str++; 23153910Sdougm } 23163910Sdougm } 23173910Sdougm 23183910Sdougm /* 23194241Sdougm * skipwhitespace(str) 23204241Sdougm * 23214241Sdougm * Skip leading white space. It is assumed that it is called with a 23224241Sdougm * valid pointer. 23234241Sdougm */ 23244241Sdougm 23254241Sdougm static char * 23264241Sdougm skipwhitespace(char *str) 23274241Sdougm { 23284241Sdougm while (*str && isspace(*str)) 23294241Sdougm str++; 23304241Sdougm 23314241Sdougm return (str); 23324241Sdougm } 23334241Sdougm 23344241Sdougm /* 23354345Sdougm * extractprop() 23364345Sdougm * 23374345Sdougm * Extract the property and value out of the line and create the 23384345Sdougm * property in the optionset. 23394345Sdougm */ 23406019Sdougm static int 23414345Sdougm extractprop(char *name, char *value) 23424345Sdougm { 23434345Sdougm sa_property_t prop; 23444345Sdougm int index; 23456019Sdougm int ret = SA_OK; 23464345Sdougm /* 23474345Sdougm * Remove any leading 23484345Sdougm * white space. 23494345Sdougm */ 23504345Sdougm name = skipwhitespace(name); 23514345Sdougm 23524345Sdougm index = findprotoopt(name, 0); 23534345Sdougm if (index >= 0) { 23544345Sdougm fixcaselower(name); 23554345Sdougm prop = sa_create_property(proto_options[index].name, value); 23564345Sdougm if (prop != NULL) 23576019Sdougm ret = sa_add_protocol_property(protoset, prop); 23586019Sdougm else 23596019Sdougm ret = SA_NO_MEMORY; 23604345Sdougm } 23616019Sdougm return (ret); 23624345Sdougm } 23634345Sdougm 23644345Sdougm /* 23653910Sdougm * initprotofromdefault() 23663910Sdougm * 23676162Sdougm * Read the default file(s) and add the defined values to the 23683910Sdougm * protoset. Note that default values are known from the built in 23696162Sdougm * table in case the file doesn't have a definition. Not having the 23706162Sdougm * /etc/default/nfs file is OK since we have builtin default 23716162Sdougm * values. The default file will get constructed as needed if values 23726162Sdougm * are changed from the defaults. 23733910Sdougm */ 23743910Sdougm 23753910Sdougm static int 23763910Sdougm initprotofromdefault() 23773910Sdougm { 23783910Sdougm FILE *nfs; 23793910Sdougm char buff[BUFSIZ]; 23803910Sdougm char *name; 23813910Sdougm char *value; 23826019Sdougm int ret = SA_OK; 23833910Sdougm 23843910Sdougm protoset = sa_create_protocol_properties("nfs"); 23853910Sdougm 23863910Sdougm if (protoset != NULL) { 23874345Sdougm nfs = fopen(NFSADMIN, "r"); 23884345Sdougm if (nfs != NULL) { 23896019Sdougm while (ret == SA_OK && 23906019Sdougm fgets(buff, sizeof (buff), nfs) != NULL) { 23914345Sdougm switch (buff[0]) { 23924345Sdougm case '\n': 23934345Sdougm case '#': 23944345Sdougm /* skip */ 23954345Sdougm break; 23964345Sdougm default: 23974345Sdougm name = buff; 23984345Sdougm buff[strlen(buff) - 1] = '\0'; 23994345Sdougm value = strchr(name, '='); 24004345Sdougm if (value != NULL) { 24014345Sdougm *value++ = '\0'; 24026019Sdougm ret = extractprop(name, value); 24034345Sdougm } 24044345Sdougm } 24053910Sdougm } 24066019Sdougm (void) fclose(nfs); 24076019Sdougm } else { 24086162Sdougm switch (errno) { 24096162Sdougm case EPERM: 24106162Sdougm case EACCES: 24116162Sdougm ret = SA_NO_PERMISSION; 24126162Sdougm break; 24136162Sdougm case ENOENT: 24146162Sdougm break; 24156162Sdougm default: 24166162Sdougm ret = SA_SYSTEM_ERR; 24176162Sdougm break; 24186162Sdougm } 24193910Sdougm } 24206019Sdougm } else { 24216019Sdougm ret = SA_NO_MEMORY; 24223910Sdougm } 24236019Sdougm return (ret); 24243910Sdougm } 24253910Sdougm 24263910Sdougm /* 24274345Sdougm * add_defaults() 24283910Sdougm * 24293910Sdougm * Add the default values for any property not defined in the parsing 24303910Sdougm * of the default files. Values are set according to their defined 24313910Sdougm * types. 24323910Sdougm */ 24333910Sdougm 24343910Sdougm static void 24353910Sdougm add_defaults() 24363910Sdougm { 24373910Sdougm int i; 24383910Sdougm char number[MAXDIGITS]; 24393910Sdougm 24403910Sdougm for (i = 0; proto_options[i].tag != NULL; i++) { 24414345Sdougm sa_property_t prop; 24424345Sdougm prop = sa_get_protocol_property(protoset, 24434345Sdougm proto_options[i].name); 24444345Sdougm if (prop == NULL) { 24454345Sdougm /* add the default value */ 24464345Sdougm switch (proto_options[i].type) { 24474345Sdougm case OPT_TYPE_NUMBER: 24484345Sdougm (void) snprintf(number, sizeof (number), "%d", 24494345Sdougm proto_options[i].defvalue.intval); 24504345Sdougm prop = sa_create_property(proto_options[i].name, 24514345Sdougm number); 24524345Sdougm break; 24533910Sdougm 24544345Sdougm case OPT_TYPE_BOOLEAN: 24554345Sdougm prop = sa_create_property(proto_options[i].name, 24564345Sdougm proto_options[i].defvalue.intval ? 24574345Sdougm "true" : "false"); 24584345Sdougm break; 24593910Sdougm 24604345Sdougm case OPT_TYPE_ONOFF: 24614345Sdougm prop = sa_create_property(proto_options[i].name, 24624345Sdougm proto_options[i].defvalue.intval ? 24634345Sdougm "on" : "off"); 24644345Sdougm break; 24653910Sdougm 24664345Sdougm default: 24674345Sdougm /* treat as strings of zero length */ 24684345Sdougm prop = sa_create_property(proto_options[i].name, 24694345Sdougm ""); 24704345Sdougm break; 24714345Sdougm } 24724345Sdougm if (prop != NULL) 24734345Sdougm (void) sa_add_protocol_property(protoset, prop); 24743910Sdougm } 24753910Sdougm } 24763910Sdougm } 24773910Sdougm 24783910Sdougm static void 24793910Sdougm free_protoprops() 24803910Sdougm { 24815179Sdougm if (protoset != NULL) { 24825179Sdougm xmlFreeNode(protoset); 24835179Sdougm protoset = NULL; 24845179Sdougm } 24853910Sdougm } 24863910Sdougm 24873910Sdougm /* 24883910Sdougm * nfs_init() 24893910Sdougm * 24903910Sdougm * Initialize the NFS plugin. 24913910Sdougm */ 24923910Sdougm 24933910Sdougm static int 24943910Sdougm nfs_init() 24953910Sdougm { 24963910Sdougm int ret = SA_OK; 24973910Sdougm 24986162Sdougm if (sa_plugin_ops.sa_init != nfs_init) { 24994345Sdougm (void) printf(dgettext(TEXT_DOMAIN, 25004345Sdougm "NFS plugin not properly initialized\n")); 25016162Sdougm return (SA_CONFIG_ERR); 25026162Sdougm } 25033910Sdougm 25043910Sdougm ret = initprotofromdefault(); 25056162Sdougm if (ret != SA_OK) { 25066162Sdougm (void) printf(dgettext(TEXT_DOMAIN, 25076162Sdougm "NFS plugin problem with default file: %s\n"), 25086162Sdougm sa_errorstr(ret)); 25096162Sdougm ret = SA_OK; 25106162Sdougm } 25116162Sdougm add_defaults(); 25123910Sdougm 25133910Sdougm return (ret); 25143910Sdougm } 25153910Sdougm 25163910Sdougm /* 25173910Sdougm * nfs_fini() 25183910Sdougm * 25193910Sdougm * uninitialize the NFS plugin. Want to avoid memory leaks. 25203910Sdougm */ 25213910Sdougm 25223910Sdougm static void 25233910Sdougm nfs_fini() 25243910Sdougm { 25253910Sdougm free_protoprops(); 25263910Sdougm } 25273910Sdougm 25283910Sdougm /* 25293910Sdougm * nfs_get_proto_set() 25303910Sdougm * 25313910Sdougm * Return an optionset with all the protocol specific properties in 25323910Sdougm * it. 25333910Sdougm */ 25343910Sdougm 25353910Sdougm static sa_protocol_properties_t 25363910Sdougm nfs_get_proto_set() 25373910Sdougm { 25383910Sdougm return (protoset); 25393910Sdougm } 25403910Sdougm 25413910Sdougm struct deffile { 25423910Sdougm struct deffile *next; 25433910Sdougm char *line; 25443910Sdougm }; 25453910Sdougm 25463910Sdougm /* 25473910Sdougm * read_default_file(fname) 25483910Sdougm * 25493910Sdougm * Read the specified default file. We return a list of entries. This 25503910Sdougm * get used for adding or removing values. 25513910Sdougm */ 25523910Sdougm 25533910Sdougm static struct deffile * 25543910Sdougm read_default_file(char *fname) 25553910Sdougm { 25563910Sdougm FILE *file; 25573910Sdougm struct deffile *defs = NULL; 25583910Sdougm struct deffile *newdef; 25593910Sdougm struct deffile *prevdef = NULL; 25603910Sdougm char buff[BUFSIZ * 2]; 25613910Sdougm 25623910Sdougm file = fopen(fname, "r"); 25633910Sdougm if (file != NULL) { 25644345Sdougm while (fgets(buff, sizeof (buff), file) != NULL) { 25654345Sdougm newdef = (struct deffile *)calloc(1, 25664345Sdougm sizeof (struct deffile)); 25674345Sdougm if (newdef != NULL) { 25684345Sdougm /* Make sure we skip any leading whitespace. */ 25694345Sdougm newdef->line = strdup(skipwhitespace(buff)); 25704345Sdougm if (defs == NULL) { 25714345Sdougm prevdef = defs = newdef; 25724345Sdougm } else { 25734345Sdougm prevdef->next = newdef; 25744345Sdougm prevdef = newdef; 25754345Sdougm } 25764345Sdougm } 25773910Sdougm } 25786162Sdougm (void) fclose(file); 25796162Sdougm } else { 25806162Sdougm int ret = SA_OK; 25816162Sdougm switch (errno) { 25826162Sdougm case EPERM: 25836162Sdougm case EACCES: 25846162Sdougm ret = SA_NO_PERMISSION; 25856162Sdougm break; 25866162Sdougm case ENOENT: 25876162Sdougm break; 25886162Sdougm default: 25896162Sdougm ret = SA_SYSTEM_ERR; 25906162Sdougm break; 25916162Sdougm } 25926162Sdougm if (ret == SA_OK) { 25936162Sdougm /* Want at least one comment line */ 25946162Sdougm defs = (struct deffile *) 25956162Sdougm calloc(1, sizeof (struct deffile)); 25966162Sdougm defs->line = strdup("# NFS default file\n"); 25976162Sdougm } 25983910Sdougm } 25993910Sdougm return (defs); 26003910Sdougm } 26013910Sdougm 26023910Sdougm static void 26033910Sdougm free_default_file(struct deffile *defs) 26043910Sdougm { 26053910Sdougm struct deffile *curdefs = NULL; 26063910Sdougm 26073910Sdougm while (defs != NULL) { 26084345Sdougm curdefs = defs; 26094345Sdougm defs = defs->next; 26104345Sdougm if (curdefs->line != NULL) 26114345Sdougm free(curdefs->line); 26124345Sdougm free(curdefs); 26133910Sdougm } 26143910Sdougm } 26153910Sdougm 26163910Sdougm /* 26173910Sdougm * write_default_file(fname, defs) 26183910Sdougm * 26193910Sdougm * Write the default file back. 26203910Sdougm */ 26213910Sdougm 26223910Sdougm static int 26233910Sdougm write_default_file(char *fname, struct deffile *defs) 26243910Sdougm { 26253910Sdougm FILE *file; 26263910Sdougm int ret = SA_OK; 26273910Sdougm sigset_t old, new; 26283910Sdougm 26293910Sdougm file = fopen(fname, "w+"); 26303910Sdougm if (file != NULL) { 26314345Sdougm (void) sigprocmask(SIG_BLOCK, NULL, &new); 26324345Sdougm (void) sigaddset(&new, SIGHUP); 26334345Sdougm (void) sigaddset(&new, SIGINT); 26344345Sdougm (void) sigaddset(&new, SIGQUIT); 26354345Sdougm (void) sigaddset(&new, SIGTSTP); 26364345Sdougm (void) sigprocmask(SIG_SETMASK, &new, &old); 26374345Sdougm while (defs != NULL) { 26384345Sdougm (void) fputs(defs->line, file); 26394345Sdougm defs = defs->next; 26404345Sdougm } 26414345Sdougm (void) fsync(fileno(file)); 26424345Sdougm (void) sigprocmask(SIG_SETMASK, &old, NULL); 26434345Sdougm (void) fclose(file); 26443910Sdougm } else { 26454345Sdougm switch (errno) { 26464345Sdougm case EPERM: 26474345Sdougm case EACCES: 26484345Sdougm ret = SA_NO_PERMISSION; 26494345Sdougm break; 26504345Sdougm default: 26514345Sdougm ret = SA_SYSTEM_ERR; 26524345Sdougm } 26533910Sdougm } 26543910Sdougm return (ret); 26553910Sdougm } 26563910Sdougm 26573910Sdougm 26583910Sdougm /* 26593910Sdougm * set_default_file_value(tag, value) 26603910Sdougm * 26613910Sdougm * Set the default file value for tag to value. Then rewrite the file. 26623910Sdougm * tag and value are always set. The caller must ensure this. 26633910Sdougm */ 26643910Sdougm 26653910Sdougm #define MAX_STRING_LENGTH 256 26663910Sdougm static int 26673910Sdougm set_default_file_value(char *tag, char *value) 26683910Sdougm { 26693910Sdougm int ret = SA_OK; 26703910Sdougm struct deffile *root; 26713910Sdougm struct deffile *defs; 26723910Sdougm struct deffile *prev; 26733910Sdougm char string[MAX_STRING_LENGTH]; 26743910Sdougm int len; 26756162Sdougm boolean_t update = B_FALSE; 26763910Sdougm 26773910Sdougm (void) snprintf(string, MAX_STRING_LENGTH, "%s=", tag); 26783910Sdougm len = strlen(string); 26793910Sdougm 26803910Sdougm root = defs = read_default_file(NFSADMIN); 26813910Sdougm if (root == NULL) { 26826162Sdougm switch (errno) { 26836162Sdougm case EPERM: 26846162Sdougm case EACCES: 26854345Sdougm ret = SA_NO_PERMISSION; 26866162Sdougm break; 26876162Sdougm default: 26886162Sdougm ret = SA_NO_MEMORY; 26896162Sdougm break; 26906162Sdougm } 26916162Sdougm return (ret); 26926162Sdougm } 26936162Sdougm 26946162Sdougm while (defs != NULL) { 26956162Sdougm if (defs->line != NULL && 26966162Sdougm strncasecmp(defs->line, string, len) == 0) { 26976162Sdougm /* replace with the new value */ 26986162Sdougm free(defs->line); 26996162Sdougm fixcaseupper(tag); 27006162Sdougm (void) snprintf(string, sizeof (string), 27016162Sdougm "%s=%s\n", tag, value); 27026162Sdougm string[MAX_STRING_LENGTH - 1] = '\0'; 27036162Sdougm defs->line = strdup(string); 27046162Sdougm update = B_TRUE; 27056162Sdougm break; 27066162Sdougm } 27076162Sdougm defs = defs->next; 27086162Sdougm } 27096162Sdougm if (!update) { 27106162Sdougm defs = root; 27116162Sdougm /* didn't find, so see if it is a comment */ 27126162Sdougm (void) snprintf(string, MAX_STRING_LENGTH, "#%s=", tag); 27136162Sdougm len = strlen(string); 27143910Sdougm while (defs != NULL) { 27156162Sdougm if (strncasecmp(defs->line, string, len) == 0) { 27164345Sdougm /* replace with the new value */ 27174345Sdougm free(defs->line); 27184345Sdougm fixcaseupper(tag); 27194345Sdougm (void) snprintf(string, sizeof (string), 27203910Sdougm "%s=%s\n", tag, value); 27214345Sdougm string[MAX_STRING_LENGTH - 1] = '\0'; 27224345Sdougm defs->line = strdup(string); 27236162Sdougm update = B_TRUE; 27244345Sdougm break; 27254345Sdougm } 27264345Sdougm defs = defs->next; 27273910Sdougm } 27286162Sdougm } 27296162Sdougm if (!update) { 27306162Sdougm fixcaseupper(tag); 27316162Sdougm (void) snprintf(string, sizeof (string), "%s=%s\n", 27326162Sdougm tag, value); 27336162Sdougm prev = root; 27346162Sdougm while (prev->next != NULL) 27356162Sdougm prev = prev->next; 27366162Sdougm defs = malloc(sizeof (struct deffile)); 27376162Sdougm prev->next = defs; 27386162Sdougm if (defs != NULL) { 27396162Sdougm defs->next = NULL; 27406162Sdougm defs->line = strdup(string); 27416162Sdougm update = B_TRUE; 27423910Sdougm } 27433910Sdougm } 27446162Sdougm if (update) { 27456162Sdougm ret = write_default_file(NFSADMIN, root); 27466162Sdougm } 27476162Sdougm free_default_file(root); 27483910Sdougm return (ret); 27493910Sdougm } 27503910Sdougm 27513910Sdougm /* 27523910Sdougm * service_in_state(service, chkstate) 27533910Sdougm * 27543910Sdougm * Want to know if the specified service is in the desired state 27553910Sdougm * (chkstate) or not. Return true (1) if it is and false (0) if it 27563910Sdougm * isn't. 27573910Sdougm */ 27583910Sdougm static int 27593910Sdougm service_in_state(char *service, const char *chkstate) 27603910Sdougm { 27613910Sdougm char *state; 27623910Sdougm int ret = B_FALSE; 27633910Sdougm 27643910Sdougm state = smf_get_state(service); 27653910Sdougm if (state != NULL) { 27664345Sdougm /* got the state so get the equality for the return value */ 27674345Sdougm ret = strcmp(state, chkstate) == 0 ? B_TRUE : B_FALSE; 27684345Sdougm free(state); 27693910Sdougm } 27703910Sdougm return (ret); 27713910Sdougm } 27723910Sdougm 27733910Sdougm /* 27743910Sdougm * restart_service(svcs) 27753910Sdougm * 27763910Sdougm * Walk through the bit mask of services that need to be restarted in 27773910Sdougm * order to use the new property values. Some properties affect 27783910Sdougm * multiple daemons. Should only restart a service if it is currently 27793910Sdougm * enabled (online). 27803910Sdougm */ 27813910Sdougm 27823910Sdougm static void 27833910Sdougm restart_service(uint32_t svcs) 27843910Sdougm { 27853910Sdougm uint32_t mask; 27863910Sdougm int ret; 27873910Sdougm char *service; 27883910Sdougm 27893910Sdougm for (mask = 1; svcs != 0; mask <<= 1) { 27904345Sdougm switch (svcs & mask) { 27914345Sdougm case SVC_LOCKD: 27924345Sdougm service = LOCKD; 27934345Sdougm break; 27944345Sdougm case SVC_STATD: 27954345Sdougm service = STATD; 27964345Sdougm break; 27974345Sdougm case SVC_NFSD: 27984345Sdougm service = NFSD; 27994345Sdougm break; 28004345Sdougm case SVC_MOUNTD: 28014345Sdougm service = MOUNTD; 28024345Sdougm break; 28034345Sdougm case SVC_NFS4CBD: 28044345Sdougm service = NFS4CBD; 28054345Sdougm break; 28064345Sdougm case SVC_NFSMAPID: 28074345Sdougm service = NFSMAPID; 28084345Sdougm break; 28094345Sdougm case SVC_RQUOTAD: 28104345Sdougm service = RQUOTAD; 28114345Sdougm break; 28124345Sdougm case SVC_NFSLOGD: 28134345Sdougm service = NFSLOGD; 28144345Sdougm break; 28154345Sdougm default: 28164345Sdougm continue; 28174345Sdougm } 28183910Sdougm 28193910Sdougm /* 28203910Sdougm * Only attempt to restart the service if it is 28213910Sdougm * currently running. In the future, it may be 28223910Sdougm * desirable to use smf_refresh_instance if the NFS 28233910Sdougm * services ever implement the refresh method. 28243910Sdougm */ 28254345Sdougm if (service_in_state(service, SCF_STATE_STRING_ONLINE)) { 28264345Sdougm ret = smf_restart_instance(service); 28273910Sdougm /* 28284345Sdougm * There are only a few SMF errors at this point, but 28294345Sdougm * it is also possible that a bad value may have put 28304345Sdougm * the service into maintenance if there wasn't an 28314345Sdougm * SMF level error. 28323910Sdougm */ 28334345Sdougm if (ret != 0) { 28344345Sdougm (void) fprintf(stderr, 28354345Sdougm dgettext(TEXT_DOMAIN, 28364345Sdougm "%s failed to restart: %s\n"), 28374345Sdougm scf_strerror(scf_error())); 28384345Sdougm } else { 28394345Sdougm /* 28404345Sdougm * Check whether it has gone to "maintenance" 28414345Sdougm * mode or not. Maintenance implies something 28424345Sdougm * went wrong. 28434345Sdougm */ 28444345Sdougm if (service_in_state(service, 28454345Sdougm SCF_STATE_STRING_MAINT)) { 28464345Sdougm (void) fprintf(stderr, 28474345Sdougm dgettext(TEXT_DOMAIN, 28484345Sdougm "%s failed to restart\n"), 28494345Sdougm service); 28504345Sdougm } 28514345Sdougm } 28523910Sdougm } 28534345Sdougm svcs &= ~mask; 28543910Sdougm } 28553910Sdougm } 28563910Sdougm 28573910Sdougm /* 28583910Sdougm * nfs_minmax_check(name, value) 28593910Sdougm * 28603910Sdougm * Verify that the value for the property specified by index is valid 28613910Sdougm * relative to the opposite value in the case of a min/max variable. 28623910Sdougm * Currently, server_minvers/server_maxvers and 28633910Sdougm * client_minvers/client_maxvers are the only ones to check. 28643910Sdougm */ 28653910Sdougm 28663910Sdougm static int 28673910Sdougm nfs_minmax_check(int index, int value) 28683910Sdougm { 28693910Sdougm int val; 28703910Sdougm char *pval; 28713910Sdougm sa_property_t prop; 28723910Sdougm sa_optionset_t opts; 28733910Sdougm int ret = B_TRUE; 28743910Sdougm 28753910Sdougm if (proto_options[index].other != NULL) { 28764345Sdougm /* have a property to compare against */ 28774345Sdougm opts = nfs_get_proto_set(); 28784345Sdougm prop = sa_get_property(opts, proto_options[index].other); 28793910Sdougm /* 28803910Sdougm * If we don't find the property, assume default 28813910Sdougm * values which will work since the max will be at the 28823910Sdougm * max and the min at the min. 28833910Sdougm */ 28844345Sdougm if (prop != NULL) { 28854345Sdougm pval = sa_get_property_attr(prop, "value"); 28864345Sdougm if (pval != NULL) { 28874345Sdougm val = strtoul(pval, NULL, 0); 28884345Sdougm if (proto_options[index].compare == 28894345Sdougm OPT_CMP_LE) { 28904345Sdougm ret = value <= val ? B_TRUE : B_FALSE; 28914345Sdougm } else if (proto_options[index].compare == 28924345Sdougm OPT_CMP_GE) { 28934345Sdougm ret = value >= val ? B_TRUE : B_FALSE; 28944345Sdougm } 28954345Sdougm } 28963910Sdougm } 28973910Sdougm } 28983910Sdougm return (ret); 28993910Sdougm } 29003910Sdougm 29013910Sdougm /* 29023910Sdougm * nfs_validate_proto_prop(index, name, value) 29033910Sdougm * 29045331Samw * Verify that the property specified by name can take the new 29053910Sdougm * value. This is a sanity check to prevent bad values getting into 29063910Sdougm * the default files. All values need to be checked against what is 29073910Sdougm * allowed by their defined type. If a type isn't explicitly defined 29083910Sdougm * here, it is treated as a string. 29093910Sdougm * 29103910Sdougm * Note that OPT_TYPE_NUMBER will additionally check that the value is 29113910Sdougm * within the range specified and potentially against another property 29123910Sdougm * value as well as specified in the proto_options members other and 29133910Sdougm * compare. 29143910Sdougm */ 29153910Sdougm 29163910Sdougm static int 29173910Sdougm nfs_validate_proto_prop(int index, char *name, char *value) 29183910Sdougm { 29193910Sdougm int ret = SA_OK; 29203910Sdougm char *cp; 29213910Sdougm #ifdef lint 29223910Sdougm name = name; 29233910Sdougm #endif 29243910Sdougm 29253910Sdougm switch (proto_options[index].type) { 29263910Sdougm case OPT_TYPE_NUMBER: 29274345Sdougm if (!is_a_number(value)) 29284345Sdougm ret = SA_BAD_VALUE; 29294345Sdougm else { 29304345Sdougm int val; 29314345Sdougm val = strtoul(value, NULL, 0); 29324345Sdougm if (val < proto_options[index].minval || 29334345Sdougm val > proto_options[index].maxval) 29344345Sdougm ret = SA_BAD_VALUE; 29354345Sdougm /* 29364345Sdougm * For server_versmin/server_versmax and 29374345Sdougm * client_versmin/client_versmax, the value of the 29384345Sdougm * min(max) should be checked to be correct relative 29394345Sdougm * to the current max(min). 29404345Sdougm */ 29414345Sdougm if (!nfs_minmax_check(index, val)) { 29424345Sdougm ret = SA_BAD_VALUE; 29434345Sdougm } 29443910Sdougm } 29454345Sdougm break; 29463910Sdougm 29473910Sdougm case OPT_TYPE_DOMAIN: 29483910Sdougm /* 29493910Sdougm * needs to be a qualified domain so will have at 29503910Sdougm * least one period and other characters on either 29513910Sdougm * side of it. A zero length string is also allowed 29523910Sdougm * and is the way to turn off the override. 29533910Sdougm */ 29544345Sdougm if (strlen(value) == 0) 29554345Sdougm break; 29564345Sdougm cp = strchr(value, '.'); 29574345Sdougm if (cp == NULL || cp == value || strchr(value, '@') != NULL) 29584345Sdougm ret = SA_BAD_VALUE; 29593910Sdougm break; 29603910Sdougm 29613910Sdougm case OPT_TYPE_BOOLEAN: 29624345Sdougm if (strlen(value) == 0 || 29634345Sdougm strcasecmp(value, "true") == 0 || 29644345Sdougm strcmp(value, "1") == 0 || 29654345Sdougm strcasecmp(value, "false") == 0 || 29664345Sdougm strcmp(value, "0") == 0) { 29674345Sdougm ret = SA_OK; 29684345Sdougm } else { 29694345Sdougm ret = SA_BAD_VALUE; 29704345Sdougm } 29714345Sdougm break; 29723910Sdougm 29733910Sdougm case OPT_TYPE_ONOFF: 29744345Sdougm if (strcasecmp(value, "on") != 0 && 29754345Sdougm strcasecmp(value, "off") != 0) { 29764345Sdougm ret = SA_BAD_VALUE; 29774345Sdougm } 29784345Sdougm break; 29793910Sdougm 29803910Sdougm case OPT_TYPE_PROTOCOL: 29816162Sdougm if (strlen(value) != 0 && 29826162Sdougm strcasecmp(value, "all") != 0 && 29834345Sdougm strcasecmp(value, "tcp") != 0 && 29844345Sdougm strcasecmp(value, "udp") != 0) 29854345Sdougm ret = SA_BAD_VALUE; 29864345Sdougm break; 29873910Sdougm 29883910Sdougm default: 29894345Sdougm /* treat as a string */ 29904345Sdougm break; 29913910Sdougm } 29923910Sdougm return (ret); 29933910Sdougm } 29943910Sdougm 29953910Sdougm /* 29963910Sdougm * nfs_set_proto_prop(prop) 29973910Sdougm * 29983910Sdougm * check that prop is valid. 29993910Sdougm */ 30003910Sdougm 30013910Sdougm static int 30023910Sdougm nfs_set_proto_prop(sa_property_t prop) 30033910Sdougm { 30043910Sdougm int ret = SA_OK; 30053910Sdougm char *name; 30063910Sdougm char *value; 30073910Sdougm 30083910Sdougm name = sa_get_property_attr(prop, "type"); 30093910Sdougm value = sa_get_property_attr(prop, "value"); 30103910Sdougm if (name != NULL && value != NULL) { 30114345Sdougm int index = findprotoopt(name, 1); 30124345Sdougm if (index >= 0) { 30134345Sdougm /* should test for valid value */ 30144345Sdougm ret = nfs_validate_proto_prop(index, name, value); 30154345Sdougm if (ret == SA_OK) 30164345Sdougm ret = set_default_file_value( 30174345Sdougm proto_options[index].tag, value); 30184345Sdougm if (ret == SA_OK) 30194345Sdougm restart_service(proto_options[index].svcs); 30204345Sdougm } 30213910Sdougm } 30223910Sdougm if (name != NULL) 30234345Sdougm sa_free_attr_string(name); 30243910Sdougm if (value != NULL) 30254345Sdougm sa_free_attr_string(value); 30263910Sdougm return (ret); 30273910Sdougm } 30283910Sdougm 30293910Sdougm /* 30303910Sdougm * nfs_get_status() 30313910Sdougm * 30323910Sdougm * What is the current status of the nfsd? We use the SMF state here. 30333910Sdougm * Caller must free the returned value. 30343910Sdougm */ 30353910Sdougm 30363910Sdougm static char * 30373910Sdougm nfs_get_status() 30383910Sdougm { 30393910Sdougm char *state; 30403910Sdougm state = smf_get_state(NFSD); 30413910Sdougm return (state != NULL ? state : strdup("-")); 30423910Sdougm } 30433910Sdougm 30443910Sdougm /* 30453910Sdougm * nfs_space_alias(alias) 30463910Sdougm * 30473910Sdougm * Lookup the space (security) name. If it is default, convert to the 30483910Sdougm * real name. 30493910Sdougm */ 30503910Sdougm 30513910Sdougm static char * 30523910Sdougm nfs_space_alias(char *space) 30533910Sdougm { 30543910Sdougm char *name = space; 30553910Sdougm seconfig_t secconf; 30563910Sdougm 30573910Sdougm /* 30583910Sdougm * Only the space named "default" is special. If it is used, 30593910Sdougm * the default needs to be looked up and the real name used. 30603910Sdougm * This is normally "sys" but could be changed. We always 30613910Sdougm * change defautl to the real name. 30623910Sdougm */ 30633910Sdougm if (strcmp(space, "default") == 0 && 30643910Sdougm nfs_getseconfig_default(&secconf) == 0) { 30654345Sdougm if (nfs_getseconfig_bynumber(secconf.sc_nfsnum, &secconf) == 0) 30664345Sdougm name = secconf.sc_name; 30673910Sdougm } 30683910Sdougm return (strdup(name)); 30693910Sdougm } 30705331Samw 30715331Samw /* 30725331Samw * nfs_features() 30735331Samw * 30745331Samw * Return a mask of the features required. 30755331Samw */ 30765331Samw 30775331Samw static uint64_t 30785331Samw nfs_features() 30795331Samw { 30806088Sdougm return ((uint64_t)SA_FEATURE_DFSTAB | SA_FEATURE_SERVER); 30815331Samw } 3082