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