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