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 2018 Nexenta Systems, Inc.  All rights reserved.
  25  * Copyright (c) 2016 by Delphix. All rights reserved.
  26  */
  27 
  28 /*
  29  * Share control API
  30  */
  31 #include <stdio.h>
  32 #include <string.h>
  33 #include <ctype.h>
  34 #include <sys/types.h>
  35 #include <sys/stat.h>
  36 #include <fcntl.h>
  37 #include <unistd.h>
  38 #include <libxml/parser.h>
  39 #include <libxml/tree.h>
  40 #include "libshare.h"
  41 #include "libshare_impl.h"
  42 #include <libscf.h>
  43 #include "scfutil.h"
  44 #include <ctype.h>
  45 #include <libintl.h>
  46 #include <thread.h>
  47 #include <synch.h>
  48 #include <errno.h>
  49 
  50 #define DFS_LOCK_FILE   "/etc/dfs/fstypes"
  51 #define SA_STRSIZE      256     /* max string size for names */
  52 
  53 /*
  54  * internal object type values returned by sa_get_object_type()
  55  */
  56 #define SA_TYPE_UNKNOWN         0
  57 #define SA_TYPE_GROUP           1
  58 #define SA_TYPE_SHARE           2
  59 #define SA_TYPE_RESOURCE        3
  60 #define SA_TYPE_OPTIONSET       4
  61 #define SA_TYPE_ALTSPACE        5
  62 
  63 /*
  64  * internal data structures
  65  */
  66 
  67 extern struct sa_proto_plugin *sap_proto_list;
  68 
  69 /* current SMF/SVC repository handle */
  70 extern void getlegacyconfig(sa_handle_t, char *, xmlNodePtr *);
  71 extern int gettransients(sa_handle_impl_t, xmlNodePtr *);
  72 extern int get_one_transient(sa_handle_impl_t, xmlNodePtr *, char **, size_t);
  73 extern char *sa_fstype(char *);
  74 extern int sa_is_share(void *);
  75 extern int sa_is_resource(void *);
  76 extern ssize_t scf_max_name_len; /* defined in scfutil during initialization */
  77 extern int sa_group_is_zfs(sa_group_t);
  78 extern int sa_path_is_zfs(char *);
  79 extern int sa_zfs_set_sharenfs(sa_group_t, char *, int);
  80 extern int sa_zfs_set_sharesmb(sa_group_t, char *, int);
  81 extern void update_legacy_config(sa_handle_t);
  82 extern int issubdir(char *, char *);
  83 extern int sa_zfs_init(sa_handle_impl_t);
  84 extern void sa_zfs_fini(sa_handle_impl_t);
  85 extern void sablocksigs(sigset_t *);
  86 extern void saunblocksigs(sigset_t *);
  87 static sa_group_t sa_get_optionset_parent(sa_optionset_t);
  88 static char *get_node_attr(void *, char *);
  89 extern void sa_update_sharetab_ts(sa_handle_t);
  90 
  91 /*
  92  * Data structures for finding/managing the document root to access
  93  * handle mapping. The list isn't expected to grow very large so a
  94  * simple list is acceptable. The purpose is to provide a way to start
  95  * with a group or share and find the library handle needed for
  96  * various operations.
  97  */
  98 mutex_t sa_global_lock;
  99 struct doc2handle {
 100         struct doc2handle       *next;
 101         xmlNodePtr              root;
 102         sa_handle_impl_t        handle;
 103 };
 104 
 105 mutex_t sa_dfstab_lock;
 106 
 107 /* definitions used in a couple of property functions */
 108 #define SA_PROP_OP_REMOVE       1
 109 #define SA_PROP_OP_ADD          2
 110 #define SA_PROP_OP_UPDATE       3
 111 
 112 static struct doc2handle *sa_global_handles = NULL;
 113 
 114 /* helper functions */
 115 
 116 /*
 117  * sa_errorstr(err)
 118  *
 119  * convert an error value to an error string
 120  */
 121 
 122 char *
 123 sa_errorstr(int err)
 124 {
 125         static char errstr[32];
 126         char *ret = NULL;
 127 
 128         switch (err) {
 129         case SA_OK:
 130                 ret = dgettext(TEXT_DOMAIN, "ok");
 131                 break;
 132         case SA_NO_SUCH_PATH:
 133                 ret = dgettext(TEXT_DOMAIN, "path doesn't exist");
 134                 break;
 135         case SA_NO_MEMORY:
 136                 ret = dgettext(TEXT_DOMAIN, "no memory");
 137                 break;
 138         case SA_DUPLICATE_NAME:
 139                 ret = dgettext(TEXT_DOMAIN, "name in use");
 140                 break;
 141         case SA_BAD_PATH:
 142                 ret = dgettext(TEXT_DOMAIN, "bad path");
 143                 break;
 144         case SA_NO_SUCH_GROUP:
 145                 ret = dgettext(TEXT_DOMAIN, "no such group");
 146                 break;
 147         case SA_CONFIG_ERR:
 148                 ret = dgettext(TEXT_DOMAIN, "configuration error");
 149                 break;
 150         case SA_SYSTEM_ERR:
 151                 ret = dgettext(TEXT_DOMAIN, "system error");
 152                 break;
 153         case SA_SYNTAX_ERR:
 154                 ret = dgettext(TEXT_DOMAIN, "syntax error");
 155                 break;
 156         case SA_NO_PERMISSION:
 157                 ret = dgettext(TEXT_DOMAIN, "no permission");
 158                 break;
 159         case SA_BUSY:
 160                 ret = dgettext(TEXT_DOMAIN, "busy");
 161                 break;
 162         case SA_NO_SUCH_PROP:
 163                 ret = dgettext(TEXT_DOMAIN, "no such property");
 164                 break;
 165         case SA_INVALID_NAME:
 166                 ret = dgettext(TEXT_DOMAIN, "invalid name");
 167                 break;
 168         case SA_INVALID_PROTOCOL:
 169                 ret = dgettext(TEXT_DOMAIN, "invalid protocol");
 170                 break;
 171         case SA_NOT_ALLOWED:
 172                 ret = dgettext(TEXT_DOMAIN, "operation not allowed");
 173                 break;
 174         case SA_BAD_VALUE:
 175                 ret = dgettext(TEXT_DOMAIN, "bad property value");
 176                 break;
 177         case SA_INVALID_SECURITY:
 178                 ret = dgettext(TEXT_DOMAIN, "invalid security type");
 179                 break;
 180         case SA_NO_SUCH_SECURITY:
 181                 ret = dgettext(TEXT_DOMAIN, "security type not found");
 182                 break;
 183         case SA_VALUE_CONFLICT:
 184                 ret = dgettext(TEXT_DOMAIN, "property value conflict");
 185                 break;
 186         case SA_NOT_IMPLEMENTED:
 187                 ret = dgettext(TEXT_DOMAIN, "not implemented");
 188                 break;
 189         case SA_INVALID_PATH:
 190                 ret = dgettext(TEXT_DOMAIN, "invalid path");
 191                 break;
 192         case SA_NOT_SUPPORTED:
 193                 ret = dgettext(TEXT_DOMAIN, "operation not supported");
 194                 break;
 195         case SA_PROP_SHARE_ONLY:
 196                 ret = dgettext(TEXT_DOMAIN, "property not valid for group");
 197                 break;
 198         case SA_NOT_SHARED:
 199                 ret = dgettext(TEXT_DOMAIN, "not shared");
 200                 break;
 201         case SA_NO_SUCH_RESOURCE:
 202                 ret = dgettext(TEXT_DOMAIN, "no such resource");
 203                 break;
 204         case SA_RESOURCE_REQUIRED:
 205                 ret = dgettext(TEXT_DOMAIN, "resource name required");
 206                 break;
 207         case SA_MULTIPLE_ERROR:
 208                 ret = dgettext(TEXT_DOMAIN, "errors from multiple protocols");
 209                 break;
 210         case SA_PATH_IS_SUBDIR:
 211                 ret = dgettext(TEXT_DOMAIN, "path is a subpath of share");
 212                 break;
 213         case SA_PATH_IS_PARENTDIR:
 214                 ret = dgettext(TEXT_DOMAIN, "path is parent of a share");
 215                 break;
 216         case SA_NO_SECTION:
 217                 ret = dgettext(TEXT_DOMAIN, "protocol requires a section");
 218                 break;
 219         case SA_NO_PROPERTIES:
 220                 ret = dgettext(TEXT_DOMAIN, "properties not found");
 221                 break;
 222         case SA_NO_SUCH_SECTION:
 223                 ret = dgettext(TEXT_DOMAIN, "section not found");
 224                 break;
 225         case SA_PASSWORD_ENC:
 226                 ret = dgettext(TEXT_DOMAIN, "passwords must be encrypted");
 227                 break;
 228         case SA_SHARE_EXISTS:
 229                 ret = dgettext(TEXT_DOMAIN, "path or file is already shared");
 230                 break;
 231         default:
 232                 (void) snprintf(errstr, sizeof (errstr),
 233                     dgettext(TEXT_DOMAIN, "unknown %d"), err);
 234                 ret = errstr;
 235         }
 236         return (ret);
 237 }
 238 
 239 /*
 240  * Document root to active handle mapping functions.  These are only
 241  * used internally. A mutex is used to prevent access while the list
 242  * is changing. In general, the list will be relatively short - one
 243  * item per thread that has called sa_init().
 244  */
 245 
 246 sa_handle_impl_t
 247 get_handle_for_root(xmlNodePtr root)
 248 {
 249         struct doc2handle *item;
 250 
 251         (void) mutex_lock(&sa_global_lock);
 252         for (item = sa_global_handles; item != NULL; item = item->next) {
 253                 if (item->root == root)
 254                         break;
 255         }
 256         (void) mutex_unlock(&sa_global_lock);
 257         if (item != NULL)
 258                 return (item->handle);
 259         return (NULL);
 260 }
 261 
 262 static int
 263 add_handle_for_root(xmlNodePtr root, sa_handle_impl_t handle)
 264 {
 265         struct doc2handle *item;
 266         int ret = SA_NO_MEMORY;
 267 
 268         item = (struct doc2handle *)calloc(sizeof (struct doc2handle), 1);
 269         if (item != NULL) {
 270                 item->root = root;
 271                 item->handle = handle;
 272                 (void) mutex_lock(&sa_global_lock);
 273                 item->next = sa_global_handles;
 274                 sa_global_handles = item;
 275                 (void) mutex_unlock(&sa_global_lock);
 276                 ret = SA_OK;
 277         }
 278         return (ret);
 279 }
 280 
 281 /*
 282  * remove_handle_for_root(root)
 283  *
 284  * Walks the list of handles and removes the one for this "root" from
 285  * the list. It is up to the caller to free the data.
 286  */
 287 
 288 static void
 289 remove_handle_for_root(xmlNodePtr root)
 290 {
 291         struct doc2handle *item, *prev;
 292 
 293         (void) mutex_lock(&sa_global_lock);
 294         for (prev = NULL, item = sa_global_handles; item != NULL;
 295             item = item->next) {
 296                 if (item->root == root) {
 297                         /* first in the list */
 298                         if (prev == NULL)
 299                                 sa_global_handles = sa_global_handles->next;
 300                         else
 301                                 prev->next = item->next;
 302                         /* Item is out of the list so free the list structure */
 303                         free(item);
 304                         break;
 305                 }
 306                 prev = item;
 307         }
 308         (void) mutex_unlock(&sa_global_lock);
 309 }
 310 
 311 /*
 312  * sa_find_group_handle(sa_group_t group)
 313  *
 314  * Find the sa_handle_t for the configuration associated with this
 315  * group.
 316  */
 317 sa_handle_t
 318 sa_find_group_handle(sa_group_t group)
 319 {
 320         xmlNodePtr node = (xmlNodePtr)group;
 321         sa_handle_t handle;
 322 
 323         while (node != NULL) {
 324                 if (strcmp((char *)(node->name), "sharecfg") == 0) {
 325                         /* have the root so get the handle */
 326                         handle = (sa_handle_t)get_handle_for_root(node);
 327                         return (handle);
 328                 }
 329                 node = node->parent;
 330         }
 331         return (NULL);
 332 }
 333 
 334 /*
 335  * set_legacy_timestamp(root, path, timevalue)
 336  *
 337  * add the current timestamp value to the configuration for use in
 338  * determining when to update the legacy files.  For SMF, this
 339  * property is kept in default/operation/legacy_timestamp
 340  */
 341 
 342 static void
 343 set_legacy_timestamp(xmlNodePtr root, char *path, uint64_t tval)
 344 {
 345         xmlNodePtr node;
 346         xmlChar *lpath = NULL;
 347         sa_handle_impl_t handle;
 348 
 349         /* Have to have a handle or else we weren't initialized. */
 350         handle = get_handle_for_root(root);
 351         if (handle == NULL)
 352                 return;
 353 
 354         for (node = root->xmlChildrenNode; node != NULL;
 355             node = node->next) {
 356                 if (xmlStrcmp(node->name, (xmlChar *)"legacy") == 0) {
 357                         /* a possible legacy node for this path */
 358                         lpath = xmlGetProp(node, (xmlChar *)"path");
 359                         if (lpath != NULL &&
 360                             xmlStrcmp(lpath, (xmlChar *)path) == 0) {
 361                                 xmlFree(lpath);
 362                                 break;
 363                         }
 364                         if (lpath != NULL)
 365                                 xmlFree(lpath);
 366                 }
 367         }
 368         if (node == NULL) {
 369                 /* need to create the first legacy timestamp node */
 370                 node = xmlNewChild(root, NULL, (xmlChar *)"legacy", NULL);
 371         }
 372         if (node != NULL) {
 373                 char tstring[32];
 374                 int ret;
 375 
 376                 (void) snprintf(tstring, sizeof (tstring), "%lld", tval);
 377                 (void) xmlSetProp(node, (xmlChar *)"timestamp",
 378                     (xmlChar *)tstring);
 379                 (void) xmlSetProp(node, (xmlChar *)"path", (xmlChar *)path);
 380                 /* now commit to SMF */
 381                 ret = sa_get_instance(handle->scfhandle, "default");
 382                 if (ret == SA_OK) {
 383                         ret = sa_start_transaction(handle->scfhandle,
 384                             "operation");
 385                         if (ret == SA_OK) {
 386                                 ret = sa_set_property(handle->scfhandle,
 387                                     "legacy-timestamp", tstring);
 388                                 if (ret == SA_OK) {
 389                                         (void) sa_end_transaction(
 390                                             handle->scfhandle, handle);
 391                                 } else {
 392                                         sa_abort_transaction(handle->scfhandle);
 393                                 }
 394                         }
 395                 }
 396         }
 397 }
 398 
 399 /*
 400  * is_shared(share)
 401  *
 402  * determine if the specified share is currently shared or not.
 403  */
 404 static int
 405 is_shared(sa_share_t share)
 406 {
 407         char *shared;
 408         int result = 0; /* assume not */
 409 
 410         shared = sa_get_share_attr(share, "shared");
 411         if (shared != NULL) {
 412                 if (strcmp(shared, "true") == 0)
 413                         result = 1;
 414                 sa_free_attr_string(shared);
 415         }
 416         return (result);
 417 }
 418 
 419 /*
 420  * excluded_protocol(share, proto)
 421  *
 422  * Returns B_TRUE if the specified protocol appears in the "exclude"
 423  * property. This is used to prevent sharing special case shares
 424  * (e.g. subdirs when SMB wants a subdir and NFS doesn't. B_FALSE is
 425  * returned if the protocol isn't in the list.
 426  */
 427 static boolean_t
 428 excluded_protocol(sa_share_t share, char *proto)
 429 {
 430         char *protolist;
 431         char *str;
 432         char *token;
 433 
 434         protolist = sa_get_share_attr(share, "exclude");
 435         if (protolist != NULL) {
 436                 str = protolist;
 437                 while ((token = strtok(str, ",")) != NULL) {
 438                         if (strcmp(token, proto) == 0) {
 439                                 sa_free_attr_string(protolist);
 440                                 return (B_TRUE);
 441                         }
 442                         str = NULL;
 443                 }
 444                 sa_free_attr_string(protolist);
 445         }
 446         return (B_FALSE);
 447 }
 448 
 449 /*
 450  * checksubdirgroup(group, newpath, strictness)
 451  *
 452  * check all the specified newpath against all the paths in the
 453  * group. This is a helper function for checksubdir to make it easier
 454  * to also check ZFS subgroups.
 455  * The strictness values mean:
 456  * SA_CHECK_NORMAL == only check newpath against shares that are active
 457  * SA_CHECK_STRICT == check newpath against both active shares and those
 458  *                    stored in the repository
 459  */
 460 static int
 461 checksubdirgroup(sa_group_t group, char *newpath, int strictness)
 462 {
 463         sa_share_t share;
 464         char *path;
 465         int issub = SA_OK;
 466         int subdir;
 467         int parent;
 468 
 469         if (newpath == NULL)
 470                 return (SA_INVALID_PATH);
 471 
 472         for (share = sa_get_share(group, NULL); share != NULL;
 473             share = sa_get_next_share(share)) {
 474                 /*
 475                  * The original behavior of share never checked
 476                  * against the permanent configuration
 477                  * (/etc/dfs/dfstab).  PIT has a number of cases where
 478                  * it depends on this older behavior even though it
 479                  * could be considered incorrect.  We may tighten this
 480                  * up in the future.
 481                  */
 482                 if (strictness == SA_CHECK_NORMAL && !is_shared(share))
 483                         continue;
 484 
 485                 path = sa_get_share_attr(share, "path");
 486                 /*
 487                  * If path is NULL, then a share is in the process of
 488                  * construction or someone has modified the property
 489                  * group inappropriately. It should be
 490                  * ignored. issubdir() comes from the original share
 491                  * implementation and does the difficult part of
 492                  * checking subdirectories.
 493                  */
 494                 if (path == NULL)
 495                         continue;
 496 
 497                 if (strcmp(path, newpath) == 0) {
 498                         issub = SA_INVALID_PATH;
 499                 } else {
 500                         subdir = issubdir(newpath, path);
 501                         parent = issubdir(path, newpath);
 502                         if (subdir || parent) {
 503                                 sa_free_attr_string(path);
 504                                 path = NULL;
 505                                 return (subdir ?
 506                                     SA_PATH_IS_SUBDIR : SA_PATH_IS_PARENTDIR);
 507                         }
 508                 }
 509                 sa_free_attr_string(path);
 510                 path = NULL;
 511         }
 512         return (issub);
 513 }
 514 
 515 /*
 516  * checksubdir(newpath, strictness)
 517  *
 518  * checksubdir determines if the specified path (newpath) is a
 519  * subdirectory of another share. It calls checksubdirgroup() to do
 520  * the complicated work. The strictness parameter determines how
 521  * strict a check to make against the path. The strictness values
 522  * mean: SA_CHECK_NORMAL == only check newpath against shares that are
 523  * active SA_CHECK_STRICT == check newpath against both active shares
 524  * and those * stored in the repository
 525  */
 526 static int
 527 checksubdir(sa_handle_t handle, char *newpath, int strictness)
 528 {
 529         sa_group_t group;
 530         int issub = SA_OK;
 531         char *path = NULL;
 532 
 533         for (group = sa_get_group(handle, NULL);
 534             group != NULL && issub == SA_OK;
 535             group = sa_get_next_group(group)) {
 536                 if (sa_group_is_zfs(group)) {
 537                         sa_group_t subgroup;
 538                         for (subgroup = sa_get_sub_group(group);
 539                             subgroup != NULL && issub == SA_OK;
 540                             subgroup = sa_get_next_group(subgroup))
 541                                 issub = checksubdirgroup(subgroup, newpath,
 542                                     strictness);
 543                 } else {
 544                         issub = checksubdirgroup(group, newpath, strictness);
 545                 }
 546         }
 547         if (path != NULL)
 548                 sa_free_attr_string(path);
 549         return (issub);
 550 }
 551 
 552 /*
 553  * validpath(path, strictness)
 554  * determine if the provided path is valid for a share. It shouldn't
 555  * be a sub-dir of an already shared path or the parent directory of a
 556  * share path.
 557  */
 558 static int
 559 validpath(sa_handle_t handle, char *path, int strictness)
 560 {
 561         int error = SA_OK;
 562         struct stat st;
 563         sa_share_t share;
 564         char *fstype;
 565 
 566         if (*path != '/')
 567                 return (SA_BAD_PATH);
 568 
 569         if (stat(path, &st) < 0) {
 570                 error = SA_NO_SUCH_PATH;
 571         } else {
 572                 share = sa_find_share(handle, path);
 573                 if (share != NULL)
 574                         error = SA_DUPLICATE_NAME;
 575 
 576                 if (error == SA_OK) {
 577                         /*
 578                          * check for special case with file system
 579                          * that might have restrictions.  For now, ZFS
 580                          * is the only case since it has its own idea
 581                          * of how to configure shares. We do this
 582                          * before subdir checking since things like
 583                          * ZFS will do that for us. This should also
 584                          * be done via plugin interface.
 585                          */
 586                         fstype = sa_fstype(path);
 587                         if (fstype != NULL && strcmp(fstype, "zfs") == 0) {
 588                                 if (sa_zfs_is_shared(handle, path))
 589                                         error = SA_INVALID_NAME;
 590                         }
 591                         if (fstype != NULL)
 592                                 sa_free_fstype(fstype);
 593                 }
 594                 if (error == SA_OK)
 595                         error = checksubdir(handle, path, strictness);
 596         }
 597         return (error);
 598 }
 599 
 600 /*
 601  * check to see if group/share is persistent.
 602  *
 603  * "group" can be either an sa_group_t or an sa_share_t. (void *)
 604  * works since both these types are also void *.
 605  * If the share is a ZFS share, mark it as persistent.
 606  */
 607 int
 608 sa_is_persistent(void *group)
 609 {
 610         char *type;
 611         int persist = 1;
 612         sa_group_t grp;
 613 
 614         type = sa_get_group_attr((sa_group_t)group, "type");
 615         if (type != NULL) {
 616                 if (strcmp(type, "transient") == 0)
 617                         persist = 0;
 618                 sa_free_attr_string(type);
 619         }
 620 
 621         grp = (sa_is_share(group)) ? sa_get_parent_group(group) : group;
 622         if (sa_group_is_zfs(grp))
 623                 persist = 1;
 624 
 625         return (persist);
 626 }
 627 
 628 /*
 629  * sa_valid_group_name(name)
 630  *
 631  * check that the "name" contains only valid characters and otherwise
 632  * fits the required naming conventions. Valid names must start with
 633  * an alphabetic and the remainder may consist of only alphanumeric
 634  * plus the '-' and '_' characters. This name limitation comes from
 635  * inherent limitations in SMF.
 636  */
 637 
 638 int
 639 sa_valid_group_name(char *name)
 640 {
 641         int ret = 1;
 642         ssize_t len;
 643 
 644         if (name != NULL && isalpha(*name)) {
 645                 char c;
 646                 len = strlen(name);
 647                 if (len < (scf_max_name_len - sizeof ("group:"))) {
 648                         for (c = *name++; c != '\0' && ret != 0; c = *name++) {
 649                                 if (!isalnum(c) && c != '-' && c != '_')
 650                                         ret = 0;
 651                         }
 652                 } else {
 653                         ret = 0;
 654                 }
 655         } else {
 656                 ret = 0;
 657         }
 658         return (ret);
 659 }
 660 
 661 
 662 /*
 663  * is_zfs_group(group)
 664  *      Determine if the specified group is a ZFS sharenfs group
 665  */
 666 static int
 667 is_zfs_group(sa_group_t group)
 668 {
 669         int ret = 0;
 670         xmlNodePtr parent;
 671         xmlChar *zfs;
 672 
 673         if (strcmp((char *)((xmlNodePtr)group)->name, "share") == 0)
 674                 parent = (xmlNodePtr)sa_get_parent_group(group);
 675         else
 676                 parent = (xmlNodePtr)group;
 677         zfs = xmlGetProp(parent, (xmlChar *)"zfs");
 678         if (zfs != NULL) {
 679                 xmlFree(zfs);
 680                 ret = 1;
 681         }
 682         return (ret);
 683 }
 684 
 685 /*
 686  * sa_get_object_type(object)
 687  *
 688  * This function returns a numeric value representing the object
 689  * type. This allows using simpler checks when doing type specific
 690  * operations.
 691  */
 692 
 693 static int
 694 sa_get_object_type(void *object)
 695 {
 696         xmlNodePtr node = (xmlNodePtr)object;
 697         int type;
 698 
 699         if (xmlStrcmp(node->name, (xmlChar *)"group") == 0)
 700                 type = SA_TYPE_GROUP;
 701         else if (xmlStrcmp(node->name, (xmlChar *)"share") == 0)
 702                 type = SA_TYPE_SHARE;
 703         else if (xmlStrcmp(node->name, (xmlChar *)"resource") == 0)
 704                 type = SA_TYPE_RESOURCE;
 705         else if (xmlStrcmp(node->name, (xmlChar *)"optionset") == 0)
 706                 type = SA_TYPE_OPTIONSET;
 707         else if (xmlStrcmp(node->name, (xmlChar *)"security") == 0)
 708                 type = SA_TYPE_ALTSPACE;
 709         else
 710                 assert(0);
 711         return (type);
 712 }
 713 
 714 /*
 715  * sa_optionset_name(optionset, oname, len, id)
 716  *      return the SMF name for the optionset. If id is not NULL, it
 717  *      will have the GUID value for a share and should be used
 718  *      instead of the keyword "optionset" which is used for
 719  *      groups. If the optionset doesn't have a protocol type
 720  *      associated with it, "default" is used. This shouldn't happen
 721  *      at this point but may be desirable in the future if there are
 722  *      protocol independent properties added. The name is returned in
 723  *      oname.
 724  */
 725 
 726 static int
 727 sa_optionset_name(sa_optionset_t optionset, char *oname, size_t len, char *id)
 728 {
 729         char *proto;
 730         void *parent;
 731         int ptype;
 732 
 733         if (id == NULL)
 734                 id = "optionset";
 735 
 736         parent = sa_get_optionset_parent(optionset);
 737         if (parent != NULL) {
 738                 ptype = sa_get_object_type(parent);
 739                 proto = sa_get_optionset_attr(optionset, "type");
 740                 if (ptype != SA_TYPE_RESOURCE) {
 741                         len = snprintf(oname, len, "%s_%s", id,
 742                             proto ? proto : "default");
 743                 } else {
 744                         char *index;
 745                         index = get_node_attr((void *)parent, "id");
 746                         if (index != NULL) {
 747                                 len = snprintf(oname, len, "%s_%s_%s", id,
 748                                     proto ? proto : "default", index);
 749                                 sa_free_attr_string(index);
 750                         } else {
 751                                 len = 0;
 752                         }
 753                 }
 754 
 755                 if (proto != NULL)
 756                         sa_free_attr_string(proto);
 757         } else {
 758                 len = 0;
 759         }
 760         return (len);
 761 }
 762 
 763 /*
 764  * sa_security_name(optionset, oname, len, id)
 765  *
 766  * return the SMF name for the security. If id is not NULL, it will
 767  * have the GUID value for a share and should be used instead of the
 768  * keyword "optionset" which is used for groups. If the optionset
 769  * doesn't have a protocol type associated with it, "default" is
 770  * used. This shouldn't happen at this point but may be desirable in
 771  * the future if there are protocol independent properties added. The
 772  * name is returned in oname. The security type is also encoded into
 773  * the name. In the future, this wil *be handled a bit differently.
 774  */
 775 
 776 static int
 777 sa_security_name(sa_security_t security, char *oname, size_t len, char *id)
 778 {
 779         char *proto;
 780         char *sectype;
 781 
 782         if (id == NULL)
 783                 id = "optionset";
 784 
 785         proto = sa_get_security_attr(security, "type");
 786         sectype = sa_get_security_attr(security, "sectype");
 787         len = snprintf(oname, len, "%s_%s_%s", id, proto ? proto : "default",
 788             sectype ? sectype : "default");
 789         if (proto != NULL)
 790                 sa_free_attr_string(proto);
 791         if (sectype != NULL)
 792                 sa_free_attr_string(sectype);
 793         return (len);
 794 }
 795 
 796 /*
 797  * verifydefgroupopts(handle)
 798  *
 799  * Make sure a "default" group exists and has default protocols enabled.
 800  */
 801 static void
 802 verifydefgroupopts(sa_handle_t handle)
 803 {
 804         sa_group_t defgrp;
 805         sa_optionset_t opt;
 806 
 807         defgrp = sa_get_group(handle, "default");
 808         if (defgrp != NULL) {
 809                 opt = sa_get_optionset(defgrp, NULL);
 810                 /*
 811                  * NFS is the default for default group
 812                  */
 813                 if (opt == NULL)
 814                         opt = sa_create_optionset(defgrp, "nfs");
 815         }
 816 }
 817 
 818 /*
 819  * sa_init_impl(init_service, arg)
 820  *      Initialize the API
 821  *      find all the shared objects
 822  *      init the tables with all objects
 823  *      read in the current configuration
 824  *
 825  *      arg is a parameter passed in whose meaning is based on the init_service.
 826  *      See libshare.h under API initialization.
 827  */
 828 #define GETPROP(prop)   scf_simple_prop_next_astring(prop)
 829 #define CHECKTSTAMP(st, tval)   stat(SA_LEGACY_DFSTAB, &st) >= 0 && \
 830         tval != TSTAMP(st.st_ctim)
 831 static sa_handle_t
 832 sa_init_impl(int init_service, void *arg)
 833 {
 834         struct stat st;
 835         /* legacy is used for debugging only as far as I can tell */
 836         int legacy = 0;
 837         uint64_t tval = 0;
 838         int lockfd;
 839         sigset_t old;
 840         int updatelegacy = B_FALSE;
 841         scf_simple_prop_t *prop;
 842         sa_handle_impl_t handle;
 843         int err;
 844 
 845         handle = calloc(sizeof (struct sa_handle_impl), 1);
 846 
 847         if (handle != NULL) {
 848                 handle->sa_service = init_service;
 849                 /*
 850                  * Get protocol specific structures, but only if this
 851                  * is the only handle.
 852                  */
 853                 (void) mutex_lock(&sa_global_lock);
 854                 if (sa_global_handles == NULL)
 855                         (void) proto_plugin_init();
 856                 (void) mutex_unlock(&sa_global_lock);
 857                 if (init_service & (SA_INIT_SHARE_API |
 858                     SA_INIT_SHARE_API_SELECTIVE | SA_INIT_ONE_SHARE_FROM_NAME |
 859                     SA_INIT_ONE_SHARE_FROM_HANDLE)) {
 860                         /*
 861                          * initialize access into libzfs. We use this
 862                          * when collecting info about ZFS datasets and
 863                          * shares.
 864                          */
 865                         if (sa_zfs_init(handle) == B_FALSE) {
 866                                 free(handle);
 867                                 (void) mutex_lock(&sa_global_lock);
 868                                 (void) proto_plugin_fini();
 869                                 (void) mutex_unlock(&sa_global_lock);
 870                                 return (NULL);
 871                         }
 872                         /*
 873                          * since we want to use SMF, initialize an svc handle
 874                          * and find out what is there.
 875                          */
 876                         handle->scfhandle = sa_scf_init(handle);
 877                         if (handle->scfhandle != NULL) {
 878                                 /*
 879                                  * Need to lock the extraction of the
 880                                  * configuration if the dfstab file has
 881                                  * changed. Lock everything now and release if
 882                                  * not needed.  Use a file that isn't being
 883                                  * manipulated by other parts of the system in
 884                                  * order to not interfere with locking. Using
 885                                  * dfstab doesn't work.
 886                                  */
 887                                 sablocksigs(&old);
 888                                 lockfd = open(DFS_LOCK_FILE, O_RDWR);
 889                                 if (lockfd >= 0) {
 890                                         errno = 0;
 891                                         (void) lockf(lockfd, F_LOCK, 0);
 892                                         (void) mutex_lock(&sa_dfstab_lock);
 893                                         /*
 894                                          * Check whether we are going to need
 895                                          * to merge any dfstab changes. This
 896                                          * is done by comparing the value of
 897                                          * legacy-timestamp with the current
 898                                          * st_ctim of the file. If they are
 899                                          * different, an update is needed and
 900                                          * the file must remain locked until
 901                                          * the merge is done in order to
 902                                          * prevent multiple startups from
 903                                          * changing the SMF repository at the
 904                                          * same time.  The first to get the
 905                                          * lock will make any changes before
 906                                          * the others can read the repository.
 907                                          */
 908                                         prop = scf_simple_prop_get
 909                                             (handle->scfhandle->handle,
 910                                             (const char *)SA_SVC_FMRI_BASE
 911                                             ":default", "operation",
 912                                             "legacy-timestamp");
 913                                         if (prop != NULL) {
 914                                                 char *i64;
 915                                                 i64 = GETPROP(prop);
 916                                                 if (i64 != NULL)
 917                                                         tval = strtoull(i64,
 918                                                             NULL, 0);
 919                                                 if (CHECKTSTAMP(st, tval))
 920                                                         updatelegacy = B_TRUE;
 921                                                 scf_simple_prop_free(prop);
 922                                         } else {
 923                                                 /*
 924                                                  * We haven't set the
 925                                                  * timestamp before so do it.
 926                                                  */
 927                                                 updatelegacy = B_TRUE;
 928                                         }
 929                                         if (updatelegacy == B_FALSE) {
 930                                                 (void) mutex_unlock(
 931                                                     &sa_dfstab_lock);
 932                                                 (void) lockf(lockfd, F_ULOCK,
 933                                                     0);
 934                                                 (void) close(lockfd);
 935                                         }
 936 
 937                                 }
 938                                 /*
 939                                  * It is essential that the document tree and
 940                                  * the internal list of roots to handles be
 941                                  * setup before anything that might try to
 942                                  * create a new object is called. The document
 943                                  * tree is the combination of handle->doc and
 944                                  * handle->tree. This allows searches,
 945                                  * etc. when all you have is an object in the
 946                                  * tree.
 947                                  */
 948                                 handle->doc = xmlNewDoc((xmlChar *)"1.0");
 949                                 handle->tree = xmlNewNode(NULL,
 950                                     (xmlChar *)"sharecfg");
 951                                 if (handle->doc != NULL &&
 952                                     handle->tree != NULL) {
 953                                         (void) xmlDocSetRootElement(handle->doc,
 954                                             handle->tree);
 955                                         err = add_handle_for_root(handle->tree,
 956                                             handle);
 957                                         if (err == SA_OK)
 958                                                 err = sa_get_config(
 959                                                     handle->scfhandle,
 960                                                     handle->tree, handle);
 961                                 } else {
 962                                         if (handle->doc != NULL)
 963                                                 xmlFreeDoc(handle->doc);
 964                                         if (handle->tree != NULL)
 965                                                 xmlFreeNode(handle->tree);
 966                                         err = SA_NO_MEMORY;
 967                                 }
 968 
 969                                 saunblocksigs(&old);
 970 
 971                                 if (err != SA_OK) {
 972                                         /*
 973                                          * If we couldn't add the tree handle
 974                                          * to the list, then things are going
 975                                          * to fail badly. Might as well undo
 976                                          * everything now and fail the
 977                                          * sa_init().
 978                                          */
 979                                         sa_fini(handle);
 980                                         if (updatelegacy == B_TRUE) {
 981                                                 (void) mutex_unlock(
 982                                                     &sa_dfstab_lock);
 983                                                 (void) lockf(lockfd,
 984                                                     F_ULOCK, 0);
 985                                                 (void) close(lockfd);
 986                                         }
 987                                         return (NULL);
 988                                 }
 989 
 990                                 if (tval == 0) {
 991                                         /*
 992                                          * first time so make sure
 993                                          * default is setup
 994                                          */
 995                                         verifydefgroupopts(handle);
 996                                 }
 997 
 998                                 if (updatelegacy == B_TRUE) {
 999                                         sablocksigs(&old);
1000                                         getlegacyconfig((sa_handle_t)handle,
1001                                             SA_LEGACY_DFSTAB, &handle->tree);
1002                                         if (stat(SA_LEGACY_DFSTAB, &st) >= 0)
1003                                                 set_legacy_timestamp(
1004                                                     handle->tree,
1005                                                     SA_LEGACY_DFSTAB,
1006                                                     TSTAMP(st.st_ctim));
1007                                         saunblocksigs(&old);
1008                                         /*
1009                                          * Safe to unlock now to allow
1010                                          * others to run
1011                                          */
1012                                         (void) mutex_unlock(&sa_dfstab_lock);
1013                                         (void) lockf(lockfd, F_ULOCK, 0);
1014                                         (void) close(lockfd);
1015                                 }
1016                                 /* Get sharetab timestamp */
1017                                 sa_update_sharetab_ts((sa_handle_t)handle);
1018 
1019                                 /* Get lastupdate (transaction) timestamp */
1020                                 prop = scf_simple_prop_get(
1021                                     handle->scfhandle->handle,
1022                                     (const char *)SA_SVC_FMRI_BASE ":default",
1023                                     "state", "lastupdate");
1024                                 if (prop != NULL) {
1025                                         char *str;
1026                                         str =
1027                                             scf_simple_prop_next_astring(prop);
1028                                         if (str != NULL)
1029                                                 handle->tstrans =
1030                                                     strtoull(str, NULL, 0);
1031                                         else
1032                                                 handle->tstrans = 0;
1033                                         scf_simple_prop_free(prop);
1034                                 }
1035                                 /*
1036                                  * In this conditional the library reads from
1037                                  * zfs and /etc/dfs/sharetab to find datasets
1038                                  * that must be shared. The result is a tree of
1039                                  * groups that are stored in the handle for
1040                                  * libshare to utilize later when asked to share
1041                                  * or unshare datasets.
1042                                  */
1043                                 if (init_service &
1044                                     SA_INIT_SHARE_API_SELECTIVE) {
1045                                         char **paths;
1046                                         size_t paths_len, i;
1047 
1048                                         legacy |= sa_get_one_zfs_share(handle,
1049                                             "zfs",
1050                                             (sa_init_selective_arg_t *)arg,
1051                                             &paths, &paths_len);
1052                                         legacy |= get_one_transient(handle,
1053                                             &handle->tree, paths, paths_len);
1054                                         for (i = 0; i < paths_len; ++i) {
1055                                                 free(paths[i]);
1056                                         }
1057                                         free(paths);
1058                                 } else if (init_service &
1059                                     SA_INIT_ONE_SHARE_FROM_NAME) {
1060                                         char path[ZFS_MAXPROPLEN];
1061                                         char *ptr = path;
1062                                         char **ptr_to_path = &ptr;
1063 
1064                                         legacy |=
1065                                             sa_get_zfs_share_for_name(handle,
1066                                             "zfs", (char *)arg, path);
1067                                         legacy |= get_one_transient(handle,
1068                                             &handle->tree, ptr_to_path, 1);
1069                                 } else if (init_service &
1070                                     SA_INIT_ONE_SHARE_FROM_HANDLE) {
1071                                         char path[ZFS_MAXPROPLEN];
1072                                         char *ptr = path;
1073                                         char **ptr_to_path = &ptr;
1074 
1075                                         legacy |=
1076                                             sa_get_zfs_share_for_name(handle,
1077                                             "zfs",
1078                                             zfs_get_name(
1079                                             (zfs_handle_t *)arg),
1080                                             path);
1081                                         legacy |= get_one_transient(handle,
1082                                             &handle->tree, ptr_to_path, 1);
1083                                 } else {
1084                                         legacy |= sa_get_zfs_shares(handle,
1085                                             "zfs");
1086                                         legacy |= gettransients(handle,
1087                                             &handle->tree);
1088                                 }
1089                         }
1090                 }
1091         }
1092         return ((sa_handle_t)handle);
1093 }
1094 
1095 /*
1096  * sa_init exists as a legacy interface, new consumers should use sa_init_arg.
1097  */
1098 sa_handle_t
1099 sa_init(int init_service)
1100 {
1101         return (sa_init_impl(init_service, NULL));
1102 }
1103 
1104 /*
1105  * See libshare.h "API Initialization" section for valid values of init_service
1106  * as well as the appropriate argument type for a given init_service.
1107  */
1108 sa_handle_t
1109 sa_init_arg(int init_service, void *arg)
1110 {
1111         return (sa_init_impl(init_service, arg));
1112 }
1113 
1114 /*
1115  * sa_fini(handle)
1116  *      Uninitialize the API structures including the configuration
1117  *      data structures and ZFS related data.
1118  */
1119 
1120 void
1121 sa_fini(sa_handle_t handle)
1122 {
1123         sa_handle_impl_t impl_handle = (sa_handle_impl_t)handle;
1124 
1125         if (impl_handle != NULL) {
1126                 /*
1127                  * Free the config trees and any other data structures
1128                  * used in the handle.
1129                  */
1130                 if (impl_handle->doc != NULL)
1131                         xmlFreeDoc(impl_handle->doc);
1132 
1133                 /* Remove and free the entry in the global list. */
1134                 remove_handle_for_root(impl_handle->tree);
1135 
1136                 /*
1137                  * If this was the last handle to release, unload the
1138                  * plugins that were loaded. Use a mutex in case
1139                  * another thread is reinitializing.
1140                  */
1141                 (void) mutex_lock(&sa_global_lock);
1142                 if (sa_global_handles == NULL)
1143                         (void) proto_plugin_fini();
1144                 (void) mutex_unlock(&sa_global_lock);
1145 
1146                 sa_scf_fini(impl_handle->scfhandle);
1147                 sa_zfs_fini(impl_handle);
1148 
1149                 /* Make sure we free the handle */
1150                 free(impl_handle);
1151 
1152         }
1153 }
1154 
1155 /*
1156  * sa_service(sa_handle_t handle)
1157  *
1158  * Returns the service for which the handle is currently initialized.
1159  */
1160 int
1161 sa_service(sa_handle_t handle)
1162 {
1163         if (handle == NULL)
1164                 return (0);
1165 
1166         return (((sa_handle_impl_t)handle)->sa_service);
1167 }
1168 
1169 /*
1170  * sa_get_protocols(char **protocol)
1171  *      Get array of protocols that are supported
1172  *      Returns pointer to an allocated and NULL terminated
1173  *      array of strings.  Caller must free.
1174  *      This really should be determined dynamically.
1175  *      If there aren't any defined, return -1.
1176  *      Use free() to return memory.
1177  */
1178 
1179 int
1180 sa_get_protocols(char ***protocols)
1181 {
1182         int numproto = -1;
1183 
1184         if (protocols != NULL) {
1185                 struct sa_proto_plugin *plug;
1186                 for (numproto = 0, plug = sap_proto_list; plug != NULL;
1187                     plug = plug->plugin_next) {
1188                         numproto++;
1189                 }
1190 
1191                 *protocols = calloc(numproto + 1,  sizeof (char *));
1192                 if (*protocols != NULL) {
1193                         int ret = 0;
1194                         for (plug = sap_proto_list; plug != NULL;
1195                             plug = plug->plugin_next) {
1196                                 /* faking for now */
1197                                 (*protocols)[ret++] =
1198                                     plug->plugin_ops->sa_protocol;
1199                         }
1200                 } else {
1201                         numproto = -1;
1202                 }
1203         }
1204         return (numproto);
1205 }
1206 
1207 /*
1208  * find_group_by_name(node, group)
1209  *
1210  * search the XML document subtree specified by node to find the group
1211  * specified by group. Searching subtree allows subgroups to be
1212  * searched for.
1213  */
1214 
1215 static xmlNodePtr
1216 find_group_by_name(xmlNodePtr node, xmlChar *group)
1217 {
1218         xmlChar *name = NULL;
1219 
1220         for (node = node->xmlChildrenNode; node != NULL;
1221             node = node->next) {
1222                 if (xmlStrcmp(node->name, (xmlChar *)"group") == 0) {
1223                         /* if no groupname, return the first found */
1224                         if (group == NULL)
1225                                 break;
1226                         name = xmlGetProp(node, (xmlChar *)"name");
1227                         if (name != NULL && xmlStrcmp(name, group) == 0)
1228                                 break;
1229                         if (name != NULL) {
1230                                 xmlFree(name);
1231                                 name = NULL;
1232                         }
1233                 }
1234         }
1235         if (name != NULL)
1236                 xmlFree(name);
1237         return (node);
1238 }
1239 
1240 /*
1241  * sa_get_group(groupname)
1242  *      Return the "group" specified.  If groupname is NULL,
1243  *      return the first group of the list of groups.
1244  */
1245 sa_group_t
1246 sa_get_group(sa_handle_t handle, char *groupname)
1247 {
1248         xmlNodePtr node = NULL;
1249         char *subgroup = NULL;
1250         char *group = NULL;
1251         sa_handle_impl_t impl_handle = (sa_handle_impl_t)handle;
1252 
1253         if (impl_handle != NULL && impl_handle->tree != NULL) {
1254                 if (groupname != NULL) {
1255                         group = strdup(groupname);
1256                         if (group != NULL) {
1257                                 subgroup = strchr(group, '/');
1258                                 if (subgroup != NULL)
1259                                         *subgroup++ = '\0';
1260                         }
1261                 }
1262                 /*
1263                  * We want to find the, possibly, named group. If
1264                  * group is not NULL, then lookup the name. If it is
1265                  * NULL, we only do the find if groupname is also
1266                  * NULL. This allows lookup of the "first" group in
1267                  * the internal list.
1268                  */
1269                 if (group != NULL || groupname == NULL)
1270                         node = find_group_by_name(impl_handle->tree,
1271                             (xmlChar *)group);
1272 
1273                 /* if a subgroup, find it before returning */
1274                 if (subgroup != NULL && node != NULL)
1275                         node = find_group_by_name(node, (xmlChar *)subgroup);
1276         }
1277         if (node != NULL && (char *)group != NULL)
1278                 (void) sa_get_instance(impl_handle->scfhandle, (char *)group);
1279         if (group != NULL)
1280                 free(group);
1281         return ((sa_group_t)(node));
1282 }
1283 
1284 /*
1285  * sa_get_next_group(group)
1286  *      Return the "next" group after the specified group from
1287  *      the internal group list.  NULL if there are no more.
1288  */
1289 sa_group_t
1290 sa_get_next_group(sa_group_t group)
1291 {
1292         xmlNodePtr ngroup = NULL;
1293         if (group != NULL) {
1294                 for (ngroup = ((xmlNodePtr)group)->next; ngroup != NULL;
1295                     ngroup = ngroup->next) {
1296                         if (xmlStrcmp(ngroup->name, (xmlChar *)"group") == 0)
1297                                 break;
1298                 }
1299         }
1300         return ((sa_group_t)ngroup);
1301 }
1302 
1303 /*
1304  * sa_get_share(group, sharepath)
1305  *      Return the share object for the share specified. The share
1306  *      must be in the specified group.  Return NULL if not found.
1307  */
1308 sa_share_t
1309 sa_get_share(sa_group_t group, char *sharepath)
1310 {
1311         xmlNodePtr node = NULL;
1312         xmlChar *path;
1313 
1314         /*
1315          * For future scalability, this should end up building a cache
1316          * since it will get called regularly by the mountd and info
1317          * services.
1318          */
1319         if (group != NULL) {
1320                 for (node = ((xmlNodePtr)group)->children; node != NULL;
1321                     node = node->next) {
1322                         if (xmlStrcmp(node->name, (xmlChar *)"share") == 0) {
1323                                 if (sharepath == NULL) {
1324                                         break;
1325                                 } else {
1326                                         /* is it the correct share? */
1327                                         path = xmlGetProp(node,
1328                                             (xmlChar *)"path");
1329                                         if (path != NULL &&
1330                                             xmlStrcmp(path,
1331                                             (xmlChar *)sharepath) == 0) {
1332                                                 xmlFree(path);
1333                                                 break;
1334                                         }
1335                                         xmlFree(path);
1336                                 }
1337                         }
1338                 }
1339         }
1340         return ((sa_share_t)node);
1341 }
1342 
1343 /*
1344  * sa_get_next_share(share)
1345  *      Return the next share following the specified share
1346  *      from the internal list of shares. Returns NULL if there
1347  *      are no more shares.  The list is relative to the same
1348  *      group.
1349  */
1350 sa_share_t
1351 sa_get_next_share(sa_share_t share)
1352 {
1353         xmlNodePtr node = NULL;
1354 
1355         if (share != NULL) {
1356                 for (node = ((xmlNodePtr)share)->next; node != NULL;
1357                     node = node->next) {
1358                         if (xmlStrcmp(node->name, (xmlChar *)"share") == 0) {
1359                                 break;
1360                         }
1361                 }
1362         }
1363         return ((sa_share_t)node);
1364 }
1365 
1366 /*
1367  * _sa_get_child_node(node, type)
1368  *
1369  * find the child node of the specified node that has "type". This is
1370  * used to implement several internal functions.
1371  */
1372 
1373 static xmlNodePtr
1374 _sa_get_child_node(xmlNodePtr node, xmlChar *type)
1375 {
1376         xmlNodePtr child;
1377         for (child = node->xmlChildrenNode; child != NULL;
1378             child = child->next)
1379                 if (xmlStrcmp(child->name, type) == 0)
1380                         return (child);
1381         return ((xmlNodePtr)NULL);
1382 }
1383 
1384 /*
1385  *  find_share(group, path)
1386  *
1387  * Search all the shares in the specified group for one that has the
1388  * specified path.
1389  */
1390 
1391 static sa_share_t
1392 find_share(sa_group_t group, char *sharepath)
1393 {
1394         sa_share_t share;
1395         char *path;
1396 
1397         for (share = sa_get_share(group, NULL); share != NULL;
1398             share = sa_get_next_share(share)) {
1399                 path = sa_get_share_attr(share, "path");
1400                 if (path != NULL && strcmp(path, sharepath) == 0) {
1401                         sa_free_attr_string(path);
1402                         break;
1403                 }
1404                 if (path != NULL)
1405                         sa_free_attr_string(path);
1406         }
1407         return (share);
1408 }
1409 
1410 /*
1411  * sa_get_sub_group(group)
1412  *
1413  * Get the first sub-group of group. The sa_get_next_group() function
1414  * can be used to get the rest. This is currently only used for ZFS
1415  * sub-groups but could be used to implement a more general mechanism.
1416  */
1417 
1418 sa_group_t
1419 sa_get_sub_group(sa_group_t group)
1420 {
1421         return ((sa_group_t)_sa_get_child_node((xmlNodePtr)group,
1422             (xmlChar *)"group"));
1423 }
1424 
1425 /*
1426  * sa_find_share(sharepath)
1427  *      Finds a share regardless of group.  In the future, this
1428  *      function should utilize a cache and hash table of some kind.
1429  *      The current assumption is that a path will only be shared
1430  *      once.  In the future, this may change as implementation of
1431  *      resource names comes into being.
1432  */
1433 sa_share_t
1434 sa_find_share(sa_handle_t handle, char *sharepath)
1435 {
1436         sa_group_t group;
1437         sa_group_t zgroup;
1438         sa_share_t share = NULL;
1439         int done = 0;
1440 
1441         for (group = sa_get_group(handle, NULL); group != NULL && !done;
1442             group = sa_get_next_group(group)) {
1443                 if (is_zfs_group(group)) {
1444                         for (zgroup =
1445                             (sa_group_t)_sa_get_child_node((xmlNodePtr)group,
1446                             (xmlChar *)"group");
1447                             zgroup != NULL;
1448                             zgroup = sa_get_next_group(zgroup)) {
1449                                 share = find_share(zgroup, sharepath);
1450                                 if (share != NULL)
1451                                         break;
1452                         }
1453                 } else {
1454                         share = find_share(group, sharepath);
1455                 }
1456                 if (share != NULL)
1457                         break;
1458         }
1459         return (share);
1460 }
1461 
1462 /*
1463  *  sa_check_path(group, path, strictness)
1464  *
1465  * Check that path is a valid path relative to the group.  Currently,
1466  * we are ignoring the group and checking only the NFS rules. Later,
1467  * we may want to use the group to then check against the protocols
1468  * enabled on the group. The strictness values mean:
1469  * SA_CHECK_NORMAL == only check newpath against shares that are active
1470  * SA_CHECK_STRICT == check newpath against both active shares and those
1471  *                    stored in the repository
1472  */
1473 
1474 int
1475 sa_check_path(sa_group_t group, char *path, int strictness)
1476 {
1477         sa_handle_t handle;
1478 
1479         handle = sa_find_group_handle(group);
1480         if (handle == NULL)
1481                 return (SA_BAD_PATH);
1482 
1483         return (validpath(handle, path, strictness));
1484 }
1485 
1486 /*
1487  * mark_excluded_protos(group, share, flags)
1488  *
1489  * Walk through all the protocols enabled for the group and check to
1490  * see if the share has any of them should be in the exclude list
1491  * based on the featureset of the protocol. If there are any, add the
1492  * "exclude" property to the share.
1493  */
1494 static void
1495 mark_excluded_protos(sa_group_t group, xmlNodePtr share, uint64_t flags)
1496 {
1497         sa_optionset_t optionset;
1498         char exclude_list[SA_STRSIZE];
1499         char *sep = "";
1500 
1501         exclude_list[0] = '\0';
1502         for (optionset = sa_get_optionset(group, NULL);
1503             optionset != NULL;
1504             optionset = sa_get_next_optionset(optionset)) {
1505                 char *value;
1506                 uint64_t features;
1507                 value = sa_get_optionset_attr(optionset, "type");
1508                 if (value == NULL)
1509                         continue;
1510                 features = sa_proto_get_featureset(value);
1511                 if (!(features & flags)) {
1512                         (void) strlcat(exclude_list, sep,
1513                             sizeof (exclude_list));
1514                         (void) strlcat(exclude_list, value,
1515                             sizeof (exclude_list));
1516                         sep = ",";
1517                 }
1518                 sa_free_attr_string(value);
1519         }
1520         if (exclude_list[0] != '\0')
1521                 (void) xmlSetProp(share, (xmlChar *)"exclude",
1522                     (xmlChar *)exclude_list);
1523 }
1524 
1525 /*
1526  * get_all_features(group)
1527  *
1528  * Walk through all the protocols on the group and collect all
1529  * possible enabled features. This is the OR of all the featuresets.
1530  */
1531 static uint64_t
1532 get_all_features(sa_group_t group)
1533 {
1534         sa_optionset_t optionset;
1535         uint64_t features = 0;
1536 
1537         for (optionset = sa_get_optionset(group, NULL);
1538             optionset != NULL;
1539             optionset = sa_get_next_optionset(optionset)) {
1540                 char *value;
1541                 value = sa_get_optionset_attr(optionset, "type");
1542                 if (value == NULL)
1543                         continue;
1544                 features |= sa_proto_get_featureset(value);
1545                 sa_free_attr_string(value);
1546         }
1547         return (features);
1548 }
1549 
1550 
1551 /*
1552  * _sa_add_share(group, sharepath, persist, *error, flags)
1553  *
1554  * Common code for all types of add_share. sa_add_share() is the
1555  * public API, we also need to be able to do this when parsing legacy
1556  * files and construction of the internal configuration while
1557  * extracting config info from SMF. "flags" indicates if some
1558  * protocols need relaxed rules while other don't. These values are
1559  * the featureset values defined in libshare.h.
1560  */
1561 
1562 sa_share_t
1563 _sa_add_share(sa_group_t group, char *sharepath, int persist, int *error,
1564     uint64_t flags)
1565 {
1566         xmlNodePtr node = NULL;
1567         int err;
1568 
1569         err  = SA_OK; /* assume success */
1570 
1571         node = xmlNewChild((xmlNodePtr)group, NULL, (xmlChar *)"share", NULL);
1572         if (node == NULL) {
1573                 if (error != NULL)
1574                         *error = SA_NO_MEMORY;
1575                 return (node);
1576         }
1577 
1578         (void) xmlSetProp(node, (xmlChar *)"path", (xmlChar *)sharepath);
1579         (void) xmlSetProp(node, (xmlChar *)"type",
1580             persist ? (xmlChar *)"persist" : (xmlChar *)"transient");
1581         if (flags != 0)
1582                 mark_excluded_protos(group, node, flags);
1583         if (persist != SA_SHARE_TRANSIENT) {
1584                 /*
1585                  * persistent shares come in two flavors: SMF and
1586                  * ZFS. Sort this one out based on target group and
1587                  * path type. Both NFS and SMB are supported. First,
1588                  * check to see if the protocol is enabled on the
1589                  * subgroup and then setup the share appropriately.
1590                  */
1591                 if (sa_group_is_zfs(group) &&
1592                     sa_path_is_zfs(sharepath)) {
1593                         if (sa_get_optionset(group, "nfs") != NULL)
1594                                 err = sa_zfs_set_sharenfs(group, sharepath, 1);
1595                         else if (sa_get_optionset(group, "smb") != NULL)
1596                                 err = sa_zfs_set_sharesmb(group, sharepath, 1);
1597                 } else {
1598                         sa_handle_impl_t impl_handle;
1599                         impl_handle =
1600                             (sa_handle_impl_t)sa_find_group_handle(group);
1601                         if (impl_handle != NULL) {
1602                                 err = sa_commit_share(impl_handle->scfhandle,
1603                                     group, (sa_share_t)node);
1604                         } else {
1605                                 err = SA_SYSTEM_ERR;
1606                         }
1607                 }
1608         }
1609         if (err == SA_NO_PERMISSION && persist & SA_SHARE_PARSER)
1610                 /* called by the dfstab parser so could be a show */
1611                 err = SA_OK;
1612 
1613         if (err != SA_OK) {
1614                 /*
1615                  * we couldn't commit to the repository so undo
1616                  * our internal state to reflect reality.
1617                  */
1618                 xmlUnlinkNode(node);
1619                 xmlFreeNode(node);
1620                 node = NULL;
1621         }
1622 
1623         if (error != NULL)
1624                 *error = err;
1625 
1626         return (node);
1627 }
1628 
1629 /*
1630  * sa_add_share(group, sharepath, persist, *error)
1631  *
1632  *      Add a new share object to the specified group.  The share will
1633  *      have the specified sharepath and will only be constructed if
1634  *      it is a valid path to be shared.  NULL is returned on error
1635  *      and a detailed error value will be returned via the error
1636  *      pointer.
1637  */
1638 sa_share_t
1639 sa_add_share(sa_group_t group, char *sharepath, int persist, int *error)
1640 {
1641         xmlNodePtr node = NULL;
1642         int strictness = SA_CHECK_NORMAL;
1643         sa_handle_t handle;
1644         uint64_t special = 0;
1645         uint64_t features;
1646 
1647         /*
1648          * If the share is to be permanent, use strict checking so a
1649          * bad config doesn't get created. Transient shares only need
1650          * to check against the currently active
1651          * shares. SA_SHARE_PARSER is a modifier used internally to
1652          * indicate that we are being called by the dfstab parser and
1653          * that we need strict checking in all cases. Normally persist
1654          * is in integer value but SA_SHARE_PARSER may be or'd into
1655          * it as an override.
1656          */
1657         if (persist & SA_SHARE_PARSER || persist == SA_SHARE_PERMANENT)
1658                 strictness = SA_CHECK_STRICT;
1659 
1660         handle = sa_find_group_handle(group);
1661 
1662         /*
1663          * need to determine if the share is valid. The rules are:
1664          *      - The path must not already exist
1665          *      - The path must not be a subdir or parent dir of an
1666          *        existing path unless at least one protocol allows it.
1667          * The sub/parent check is done in sa_check_path().
1668          */
1669 
1670         if (sa_find_share(handle, sharepath) == NULL) {
1671                 *error = sa_check_path(group, sharepath, strictness);
1672                 features = get_all_features(group);
1673                 switch (*error) {
1674                 case SA_PATH_IS_SUBDIR:
1675                         if (features & SA_FEATURE_ALLOWSUBDIRS)
1676                                 special |= SA_FEATURE_ALLOWSUBDIRS;
1677                         break;
1678                 case SA_PATH_IS_PARENTDIR:
1679                         if (features & SA_FEATURE_ALLOWPARDIRS)
1680                                 special |= SA_FEATURE_ALLOWPARDIRS;
1681                         break;
1682                 }
1683                 if (*error == SA_OK || special != SA_FEATURE_NONE)
1684                         node = _sa_add_share(group, sharepath, persist,
1685                             error, special);
1686         } else {
1687                 *error = SA_DUPLICATE_NAME;
1688         }
1689 
1690         return ((sa_share_t)node);
1691 }
1692 
1693 /*
1694  * sa_enable_share(share, protocol)
1695  *      Enable the specified share to the specified protocol.
1696  *      If protocol is NULL, then all protocols.
1697  */
1698 int
1699 sa_enable_share(sa_share_t share, char *protocol)
1700 {
1701         char *sharepath;
1702         struct stat st;
1703         int err = SA_OK;
1704         int ret;
1705 
1706         sharepath = sa_get_share_attr(share, "path");
1707         if (sharepath == NULL)
1708                 return (SA_NO_MEMORY);
1709         if (stat(sharepath, &st) < 0) {
1710                 err = SA_NO_SUCH_PATH;
1711         } else {
1712                 /* tell the server about the share */
1713                 if (protocol != NULL) {
1714                         if (excluded_protocol(share, protocol))
1715                                 goto done;
1716 
1717                         /* lookup protocol specific handler */
1718                         err = sa_proto_share(protocol, share);
1719                         if (err == SA_OK)
1720                                 (void) sa_set_share_attr(share,
1721                                     "shared", "true");
1722                 } else {
1723                         /* Tell all protocols about the share */
1724                         sa_group_t group;
1725                         sa_optionset_t optionset;
1726 
1727                         group = sa_get_parent_group(share);
1728 
1729                         for (optionset = sa_get_optionset(group, NULL);
1730                             optionset != NULL;
1731                             optionset = sa_get_next_optionset(optionset)) {
1732                                 char *proto;
1733                                 proto = sa_get_optionset_attr(optionset,
1734                                     "type");
1735                                 if (proto != NULL) {
1736                                         if (!excluded_protocol(share, proto)) {
1737                                                 ret = sa_proto_share(proto,
1738                                                     share);
1739                                                 if (ret != SA_OK)
1740                                                         err = ret;
1741                                         }
1742                                         sa_free_attr_string(proto);
1743                                 }
1744                         }
1745                         (void) sa_set_share_attr(share, "shared", "true");
1746                 }
1747         }
1748 done:
1749         if (sharepath != NULL)
1750                 sa_free_attr_string(sharepath);
1751         return (err);
1752 }
1753 
1754 /*
1755  * sa_disable_share(share, protocol)
1756  *      Disable the specified share to the specified protocol.  If
1757  *      protocol is NULL, then all protocols that are enabled for the
1758  *      share should be disabled.
1759  */
1760 int
1761 sa_disable_share(sa_share_t share, char *protocol)
1762 {
1763         char *path;
1764         int err = SA_OK;
1765         int ret = SA_OK;
1766 
1767         path = sa_get_share_attr(share, "path");
1768 
1769         if (protocol != NULL) {
1770                 ret = sa_proto_unshare(share, protocol, path);
1771         } else {
1772                 /* need to do all protocols */
1773                 sa_group_t group;
1774                 sa_optionset_t optionset;
1775 
1776                 group = sa_get_parent_group(share);
1777 
1778                 /* Tell all protocols about the share */
1779                 for (optionset = sa_get_optionset(group, NULL);
1780                     optionset != NULL;
1781                     optionset = sa_get_next_optionset(optionset)) {
1782                         char *proto;
1783 
1784                         proto = sa_get_optionset_attr(optionset, "type");
1785                         if (proto != NULL) {
1786                                 err = sa_proto_unshare(share, proto, path);
1787                                 if (err != SA_OK)
1788                                         ret = err;
1789                                 sa_free_attr_string(proto);
1790                         }
1791                 }
1792         }
1793         if (ret == SA_OK)
1794                 (void) sa_set_share_attr(share, "shared", NULL);
1795         if (path != NULL)
1796                 sa_free_attr_string(path);
1797         return (ret);
1798 }
1799 
1800 /*
1801  * sa_remove_share(share)
1802  *
1803  * remove the specified share from its containing group.
1804  * Remove from the SMF or ZFS configuration space.
1805  */
1806 
1807 int
1808 sa_remove_share(sa_share_t share)
1809 {
1810         sa_group_t group;
1811         int ret = SA_OK;
1812         char *type;
1813         int transient = 0;
1814         char *groupname;
1815         char *zfs;
1816 
1817         type = sa_get_share_attr(share, "type");
1818         group = sa_get_parent_group(share);
1819         zfs = sa_get_group_attr(group, "zfs");
1820         groupname = sa_get_group_attr(group, "name");
1821         if (type != NULL && strcmp(type, "persist") != 0)
1822                 transient = 1;
1823         if (type != NULL)
1824                 sa_free_attr_string(type);
1825 
1826         /* remove the node from its group then free the memory */
1827 
1828         /*
1829          * need to test if "busy"
1830          */
1831         /* only do SMF action if permanent */
1832         if (!transient || zfs != NULL) {
1833                 /* remove from legacy dfstab as well as possible SMF */
1834                 ret = sa_delete_legacy(share, NULL);
1835                 if (ret == SA_OK) {
1836                         if (!sa_group_is_zfs(group)) {
1837                                 sa_handle_impl_t impl_handle;
1838                                 impl_handle = (sa_handle_impl_t)
1839                                     sa_find_group_handle(group);
1840                                 if (impl_handle != NULL) {
1841                                         ret = sa_delete_share(
1842                                             impl_handle->scfhandle, group,
1843                                             share);
1844                                 } else {
1845                                         ret = SA_SYSTEM_ERR;
1846                                 }
1847                         } else {
1848                                 char *sharepath = sa_get_share_attr(share,
1849                                     "path");
1850                                 if (sharepath != NULL) {
1851                                         ret = sa_zfs_set_sharenfs(group,
1852                                             sharepath, 0);
1853                                         sa_free_attr_string(sharepath);
1854                                 }
1855                         }
1856                 }
1857         }
1858         if (groupname != NULL)
1859                 sa_free_attr_string(groupname);
1860         if (zfs != NULL)
1861                 sa_free_attr_string(zfs);
1862 
1863         xmlUnlinkNode((xmlNodePtr)share);
1864         xmlFreeNode((xmlNodePtr)share);
1865         return (ret);
1866 }
1867 
1868 /*
1869  * sa_move_share(group, share)
1870  *
1871  * move the specified share to the specified group.  Update SMF
1872  * appropriately.
1873  */
1874 
1875 int
1876 sa_move_share(sa_group_t group, sa_share_t share)
1877 {
1878         sa_group_t oldgroup;
1879         int ret = SA_OK;
1880 
1881         /* remove the node from its group then free the memory */
1882 
1883         oldgroup = sa_get_parent_group(share);
1884         if (oldgroup != group) {
1885                 sa_handle_impl_t impl_handle;
1886                 xmlUnlinkNode((xmlNodePtr)share);
1887                 /*
1888                  * now that the share isn't in its old group, add to
1889                  * the new one
1890                  */
1891                 (void) xmlAddChild((xmlNodePtr)group, (xmlNodePtr)share);
1892                 /* need to deal with SMF */
1893                 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
1894                 if (impl_handle != NULL) {
1895                         /*
1896                          * need to remove from old group first and then add to
1897                          * new group. Ideally, we would do the other order but
1898                          * need to avoid having the share in two groups at the
1899                          * same time.
1900                          */
1901                         ret = sa_delete_share(impl_handle->scfhandle, oldgroup,
1902                             share);
1903                         if (ret == SA_OK)
1904                                 ret = sa_commit_share(impl_handle->scfhandle,
1905                                     group, share);
1906                 } else {
1907                         ret = SA_SYSTEM_ERR;
1908                 }
1909         }
1910         return (ret);
1911 }
1912 
1913 /*
1914  * sa_get_parent_group(share)
1915  *
1916  * Return the containing group for the share. If a group was actually
1917  * passed in, we don't want a parent so return NULL.
1918  */
1919 
1920 sa_group_t
1921 sa_get_parent_group(sa_share_t share)
1922 {
1923         xmlNodePtr node = NULL;
1924         if (share != NULL) {
1925                 node = ((xmlNodePtr)share)->parent;
1926                 /*
1927                  * make sure parent is a group and not sharecfg since
1928                  * we may be cheating and passing in a group.
1929                  * Eventually, groups of groups might come into being.
1930                  */
1931                 if (node == NULL ||
1932                     xmlStrcmp(node->name, (xmlChar *)"sharecfg") == 0)
1933                         node = NULL;
1934         }
1935         return ((sa_group_t)node);
1936 }
1937 
1938 /*
1939  * _sa_create_group(impl_handle, groupname)
1940  *
1941  * Create a group in the document. The caller will need to deal with
1942  * configuration store and activation.
1943  */
1944 
1945 sa_group_t
1946 _sa_create_group(sa_handle_impl_t impl_handle, char *groupname)
1947 {
1948         xmlNodePtr node = NULL;
1949 
1950         if (sa_valid_group_name(groupname)) {
1951                 node = xmlNewChild(impl_handle->tree, NULL, (xmlChar *)"group",
1952                     NULL);
1953                 if (node != NULL) {
1954                         (void) xmlSetProp(node, (xmlChar *)"name",
1955                             (xmlChar *)groupname);
1956                         (void) xmlSetProp(node, (xmlChar *)"state",
1957                             (xmlChar *)"enabled");
1958                 }
1959         }
1960         return ((sa_group_t)node);
1961 }
1962 
1963 /*
1964  * _sa_create_zfs_group(group, groupname)
1965  *
1966  * Create a ZFS subgroup under the specified group. This may
1967  * eventually form the basis of general sub-groups, but is currently
1968  * restricted to ZFS.
1969  */
1970 sa_group_t
1971 _sa_create_zfs_group(sa_group_t group, char *groupname)
1972 {
1973         xmlNodePtr node = NULL;
1974 
1975         node = xmlNewChild((xmlNodePtr)group, NULL, (xmlChar *)"group", NULL);
1976         if (node != NULL) {
1977                 (void) xmlSetProp(node, (xmlChar *)"name",
1978                     (xmlChar *)groupname);
1979                 (void) xmlSetProp(node, (xmlChar *)"state",
1980                     (xmlChar *)"enabled");
1981         }
1982 
1983         return ((sa_group_t)node);
1984 }
1985 
1986 /*
1987  * sa_create_group(groupname, *error)
1988  *
1989  * Create a new group with groupname.  Need to validate that it is a
1990  * legal name for SMF and the construct the SMF service instance of
1991  * svc:/network/shares/group to implement the group. All necessary
1992  * operational properties must be added to the group at this point
1993  * (via the SMF transaction model).
1994  */
1995 sa_group_t
1996 sa_create_group(sa_handle_t handle, char *groupname, int *error)
1997 {
1998         xmlNodePtr node = NULL;
1999         sa_group_t group;
2000         int ret;
2001         char rbacstr[SA_STRSIZE];
2002         sa_handle_impl_t impl_handle = (sa_handle_impl_t)handle;
2003 
2004         ret = SA_OK;
2005 
2006         if (impl_handle == NULL || impl_handle->scfhandle == NULL) {
2007                 ret = SA_SYSTEM_ERR;
2008                 goto err;
2009         }
2010 
2011         group = sa_get_group(handle, groupname);
2012         if (group != NULL) {
2013                 ret = SA_DUPLICATE_NAME;
2014         } else {
2015                 if (sa_valid_group_name(groupname)) {
2016                         node = xmlNewChild(impl_handle->tree, NULL,
2017                             (xmlChar *)"group", NULL);
2018                         if (node != NULL) {
2019                                 (void) xmlSetProp(node, (xmlChar *)"name",
2020                                     (xmlChar *)groupname);
2021                                 /* default to the group being enabled */
2022                                 (void) xmlSetProp(node, (xmlChar *)"state",
2023                                     (xmlChar *)"enabled");
2024                                 ret = sa_create_instance(impl_handle->scfhandle,
2025                                     groupname);
2026                                 if (ret == SA_OK) {
2027                                         ret = sa_start_transaction(
2028                                             impl_handle->scfhandle,
2029                                             "operation");
2030                                 }
2031                                 if (ret == SA_OK) {
2032                                         ret = sa_set_property(
2033                                             impl_handle->scfhandle,
2034                                             "state", "enabled");
2035                                         if (ret == SA_OK) {
2036                                                 ret = sa_end_transaction(
2037                                                     impl_handle->scfhandle,
2038                                                     impl_handle);
2039                                         } else {
2040                                                 sa_abort_transaction(
2041                                                     impl_handle->scfhandle);
2042                                         }
2043                                 }
2044                                 if (ret == SA_OK) {
2045                                         /* initialize the RBAC strings */
2046                                         ret = sa_start_transaction(
2047                                             impl_handle->scfhandle,
2048                                             "general");
2049                                         if (ret == SA_OK) {
2050                                                 (void) snprintf(rbacstr,
2051                                                     sizeof (rbacstr), "%s.%s",
2052                                                     SA_RBAC_MANAGE, groupname);
2053                                                 ret = sa_set_property(
2054                                                     impl_handle->scfhandle,
2055                                                     "action_authorization",
2056                                                     rbacstr);
2057                                         }
2058                                         if (ret == SA_OK) {
2059                                                 (void) snprintf(rbacstr,
2060                                                     sizeof (rbacstr), "%s.%s",
2061                                                     SA_RBAC_VALUE, groupname);
2062                                                 ret = sa_set_property(
2063                                                     impl_handle->scfhandle,
2064                                                     "value_authorization",
2065                                                     rbacstr);
2066                                         }
2067                                         if (ret == SA_OK) {
2068                                                 ret = sa_end_transaction(
2069                                                     impl_handle->scfhandle,
2070                                                     impl_handle);
2071                                         } else {
2072                                                 sa_abort_transaction(
2073                                                     impl_handle->scfhandle);
2074                                         }
2075                                 }
2076                                 if (ret != SA_OK) {
2077                                         /*
2078                                          * Couldn't commit the group
2079                                          * so we need to undo
2080                                          * internally.
2081                                          */
2082                                         xmlUnlinkNode(node);
2083                                         xmlFreeNode(node);
2084                                         node = NULL;
2085                                 }
2086                         } else {
2087                                 ret = SA_NO_MEMORY;
2088                         }
2089                 } else {
2090                         ret = SA_INVALID_NAME;
2091                 }
2092         }
2093 err:
2094         if (error != NULL)
2095                 *error = ret;
2096         return ((sa_group_t)node);
2097 }
2098 
2099 /*
2100  * sa_remove_group(group)
2101  *
2102  * Remove the specified group. This deletes from the SMF repository.
2103  * All property groups and properties are removed.
2104  */
2105 
2106 int
2107 sa_remove_group(sa_group_t group)
2108 {
2109         char *name;
2110         int ret = SA_OK;
2111         sa_handle_impl_t impl_handle;
2112 
2113         impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
2114         if (impl_handle != NULL) {
2115                 name = sa_get_group_attr(group, "name");
2116                 if (name != NULL) {
2117                         ret = sa_delete_instance(impl_handle->scfhandle, name);
2118                         sa_free_attr_string(name);
2119                 }
2120                 xmlUnlinkNode((xmlNodePtr)group); /* make sure unlinked */
2121                 xmlFreeNode((xmlNodePtr)group);   /* now it is gone */
2122         } else {
2123                 ret = SA_SYSTEM_ERR;
2124         }
2125         return (ret);
2126 }
2127 
2128 /*
2129  * sa_update_config()
2130  *
2131  * Used to update legacy files that need to be updated in bulk
2132  * Currently, this is a placeholder and will go away in a future
2133  * release.
2134  */
2135 
2136 int
2137 sa_update_config(sa_handle_t handle)
2138 {
2139         /*
2140          * do legacy files first so we can tell when they change.
2141          * This will go away when we start updating individual records
2142          * rather than the whole file.
2143          */
2144         update_legacy_config(handle);
2145         return (SA_OK);
2146 }
2147 
2148 /*
2149  * get_node_attr(node, tag)
2150  *
2151  * Get the specified tag(attribute) if it exists on the node.  This is
2152  * used internally by a number of attribute oriented functions.
2153  */
2154 
2155 static char *
2156 get_node_attr(void *nodehdl, char *tag)
2157 {
2158         xmlNodePtr node = (xmlNodePtr)nodehdl;
2159         xmlChar *name = NULL;
2160 
2161         if (node != NULL)
2162                 name = xmlGetProp(node, (xmlChar *)tag);
2163         return ((char *)name);
2164 }
2165 
2166 /*
2167  * set_node_attr(node, tag)
2168  *
2169  * Set the specified tag(attribute) to the specified value This is
2170  * used internally by a number of attribute oriented functions. It
2171  * doesn't update the repository, only the internal document state.
2172  */
2173 
2174 void
2175 set_node_attr(void *nodehdl, char *tag, char *value)
2176 {
2177         xmlNodePtr node = (xmlNodePtr)nodehdl;
2178         if (node != NULL && tag != NULL) {
2179                 if (value != NULL)
2180                         (void) xmlSetProp(node, (xmlChar *)tag,
2181                             (xmlChar *)value);
2182                 else
2183                         (void) xmlUnsetProp(node, (xmlChar *)tag);
2184         }
2185 }
2186 
2187 /*
2188  * sa_get_group_attr(group, tag)
2189  *
2190  * Get the specied attribute, if defined, for the group.
2191  */
2192 
2193 char *
2194 sa_get_group_attr(sa_group_t group, char *tag)
2195 {
2196         return (get_node_attr((void *)group, tag));
2197 }
2198 
2199 /*
2200  * sa_set_group_attr(group, tag, value)
2201  *
2202  * set the specified tag/attribute on the group using value as its
2203  * value.
2204  *
2205  * This will result in setting the property in the SMF repository as
2206  * well as in the internal document.
2207  */
2208 
2209 int
2210 sa_set_group_attr(sa_group_t group, char *tag, char *value)
2211 {
2212         int ret;
2213         char *groupname;
2214         sa_handle_impl_t impl_handle;
2215 
2216         /*
2217          * ZFS group/subgroup doesn't need the handle so shortcut.
2218          */
2219         if (sa_group_is_zfs(group)) {
2220                 set_node_attr((void *)group, tag, value);
2221                 return (SA_OK);
2222         }
2223 
2224         impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
2225         if (impl_handle != NULL) {
2226                 groupname = sa_get_group_attr(group, "name");
2227                 ret = sa_get_instance(impl_handle->scfhandle, groupname);
2228                 if (ret == SA_OK) {
2229                         set_node_attr((void *)group, tag, value);
2230                         ret = sa_start_transaction(impl_handle->scfhandle,
2231                             "operation");
2232                         if (ret == SA_OK) {
2233                                 ret = sa_set_property(impl_handle->scfhandle,
2234                                     tag, value);
2235                                 if (ret == SA_OK)
2236                                         ret = sa_end_transaction(
2237                                             impl_handle->scfhandle,
2238                                             impl_handle);
2239                                 else
2240                                         sa_abort_transaction(
2241                                             impl_handle->scfhandle);
2242                         }
2243                         if (ret == SA_SYSTEM_ERR)
2244                                 ret = SA_NO_PERMISSION;
2245                 }
2246                 if (groupname != NULL)
2247                         sa_free_attr_string(groupname);
2248         } else {
2249                 ret = SA_SYSTEM_ERR;
2250         }
2251         return (ret);
2252 }
2253 
2254 /*
2255  * sa_get_share_attr(share, tag)
2256  *
2257  * Return the value of the tag/attribute set on the specified
2258  * share. Returns NULL if the tag doesn't exist.
2259  */
2260 
2261 char *
2262 sa_get_share_attr(sa_share_t share, char *tag)
2263 {
2264         return (get_node_attr((void *)share, tag));
2265 }
2266 
2267 /*
2268  * _sa_set_share_description(share, description)
2269  *
2270  * Add a description tag with text contents to the specified share.  A
2271  * separate XML tag is used rather than a property. This can also be
2272  * used with resources.
2273  */
2274 
2275 xmlNodePtr
2276 _sa_set_share_description(void *share, char *content)
2277 {
2278         xmlNodePtr node;
2279         node = xmlNewChild((xmlNodePtr)share, NULL, (xmlChar *)"description",
2280             NULL);
2281         xmlNodeSetContent(node, (xmlChar *)content);
2282         return (node);
2283 }
2284 
2285 /*
2286  * sa_set_share_attr(share, tag, value)
2287  *
2288  * Set the share attribute specified by tag to the specified value. In
2289  * the case of "resource", enforce a no duplicates in a group rule. If
2290  * the share is not transient, commit the changes to the repository
2291  * else just update the share internally.
2292  */
2293 
2294 int
2295 sa_set_share_attr(sa_share_t share, char *tag, char *value)
2296 {
2297         sa_group_t group;
2298         sa_share_t resource;
2299         int ret = SA_OK;
2300 
2301         group = sa_get_parent_group(share);
2302 
2303         /*
2304          * There are some attributes that may have specific
2305          * restrictions on them. Initially, only "resource" has
2306          * special meaning that needs to be checked. Only one instance
2307          * of a resource name may exist within a group.
2308          */
2309 
2310         if (strcmp(tag, "resource") == 0) {
2311                 resource = sa_get_resource(group, value);
2312                 if (resource != share && resource != NULL)
2313                         ret = SA_DUPLICATE_NAME;
2314         }
2315         if (ret == SA_OK) {
2316                 set_node_attr((void *)share, tag, value);
2317                 if (group != NULL) {
2318                         char *type;
2319                         /* we can probably optimize this some */
2320                         type = sa_get_share_attr(share, "type");
2321                         if (type == NULL || strcmp(type, "transient") != 0) {
2322                                 sa_handle_impl_t impl_handle;
2323                                 impl_handle =
2324                                     (sa_handle_impl_t)sa_find_group_handle(
2325                                     group);
2326                                 if (impl_handle != NULL) {
2327                                         ret = sa_commit_share(
2328                                             impl_handle->scfhandle, group,
2329                                             share);
2330                                 } else {
2331                                         ret = SA_SYSTEM_ERR;
2332                                 }
2333                         }
2334                         if (type != NULL)
2335                                 sa_free_attr_string(type);
2336                 }
2337         }
2338         return (ret);
2339 }
2340 
2341 /*
2342  * sa_get_property_attr(prop, tag)
2343  *
2344  * Get the value of the specified property attribute. Standard
2345  * attributes are "type" and "value".
2346  */
2347 
2348 char *
2349 sa_get_property_attr(sa_property_t prop, char *tag)
2350 {
2351         return (get_node_attr((void *)prop, tag));
2352 }
2353 
2354 /*
2355  * sa_get_optionset_attr(prop, tag)
2356  *
2357  * Get the value of the specified property attribute. Standard
2358  * attribute is "type".
2359  */
2360 
2361 char *
2362 sa_get_optionset_attr(sa_property_t optionset, char *tag)
2363 {
2364         return (get_node_attr((void *)optionset, tag));
2365 
2366 }
2367 
2368 /*
2369  * sa_set_optionset_attr(optionset, tag, value)
2370  *
2371  * Set the specified attribute(tag) to the specified value on the
2372  * optionset.
2373  */
2374 
2375 void
2376 sa_set_optionset_attr(sa_group_t optionset, char *tag, char *value)
2377 {
2378         set_node_attr((void *)optionset, tag, value);
2379 }
2380 
2381 /*
2382  * sa_free_attr_string(string)
2383  *
2384  * Free the string that was returned in one of the sa_get_*_attr()
2385  * functions.
2386  */
2387 
2388 void
2389 sa_free_attr_string(char *string)
2390 {
2391         xmlFree((xmlChar *)string);
2392 }
2393 
2394 /*
2395  * sa_get_optionset(group, proto)
2396  *
2397  * Return the optionset, if it exists, that is associated with the
2398  * specified protocol.
2399  */
2400 
2401 sa_optionset_t
2402 sa_get_optionset(void *group, char *proto)
2403 {
2404         xmlNodePtr node;
2405         xmlChar *value = NULL;
2406 
2407         for (node = ((xmlNodePtr)group)->children; node != NULL;
2408             node = node->next) {
2409                 if (xmlStrcmp(node->name, (xmlChar *)"optionset") == 0) {
2410                         value = xmlGetProp(node, (xmlChar *)"type");
2411                         if (proto != NULL) {
2412                                 if (value != NULL &&
2413                                     xmlStrcmp(value, (xmlChar *)proto) == 0) {
2414                                         break;
2415                                 }
2416                                 if (value != NULL) {
2417                                         xmlFree(value);
2418                                         value = NULL;
2419                                 }
2420                         } else {
2421                                 break;
2422                         }
2423                 }
2424         }
2425         if (value != NULL)
2426                 xmlFree(value);
2427         return ((sa_optionset_t)node);
2428 }
2429 
2430 /*
2431  * sa_get_next_optionset(optionset)
2432  *
2433  * Return the next optionset in the group. NULL if this was the last.
2434  */
2435 
2436 sa_optionset_t
2437 sa_get_next_optionset(sa_optionset_t optionset)
2438 {
2439         xmlNodePtr node;
2440 
2441         for (node = ((xmlNodePtr)optionset)->next; node != NULL;
2442             node = node->next) {
2443                 if (xmlStrcmp(node->name, (xmlChar *)"optionset") == 0) {
2444                         break;
2445                 }
2446         }
2447         return ((sa_optionset_t)node);
2448 }
2449 
2450 /*
2451  * sa_get_security(group, sectype, proto)
2452  *
2453  * Return the security optionset. The internal name is a hold over
2454  * from the implementation and will be changed before the API is
2455  * finalized. This is really a named optionset that can be negotiated
2456  * as a group of properties (like NFS security options).
2457  */
2458 
2459 sa_security_t
2460 sa_get_security(sa_group_t group, char *sectype, char *proto)
2461 {
2462         xmlNodePtr node;
2463         xmlChar *value = NULL;
2464 
2465         for (node = ((xmlNodePtr)group)->children; node != NULL;
2466             node = node->next) {
2467                 if (xmlStrcmp(node->name, (xmlChar *)"security") == 0) {
2468                         if (proto != NULL) {
2469                                 value = xmlGetProp(node, (xmlChar *)"type");
2470                                 if (value == NULL ||
2471                                     (value != NULL &&
2472                                     xmlStrcmp(value, (xmlChar *)proto) != 0)) {
2473                                         /* it doesn't match so continue */
2474                                         xmlFree(value);
2475                                         value = NULL;
2476                                         continue;
2477                                 }
2478                         }
2479                         if (value != NULL) {
2480                                 xmlFree(value);
2481                                 value = NULL;
2482                         }
2483                         /* potential match */
2484                         if (sectype != NULL) {
2485                                 value = xmlGetProp(node, (xmlChar *)"sectype");
2486                                 if (value != NULL &&
2487                                     xmlStrcmp(value, (xmlChar *)sectype) == 0) {
2488                                         break;
2489                                 }
2490                         } else {
2491                                 break;
2492                         }
2493                 }
2494                 if (value != NULL) {
2495                         xmlFree(value);
2496                         value = NULL;
2497                 }
2498         }
2499         if (value != NULL)
2500                 xmlFree(value);
2501         return ((sa_security_t)node);
2502 }
2503 
2504 /*
2505  * sa_get_next_security(security)
2506  *
2507  * Get the next security optionset if one exists.
2508  */
2509 
2510 sa_security_t
2511 sa_get_next_security(sa_security_t security)
2512 {
2513         xmlNodePtr node;
2514 
2515         for (node = ((xmlNodePtr)security)->next; node != NULL;
2516             node = node->next) {
2517                 if (xmlStrcmp(node->name, (xmlChar *)"security") == 0) {
2518                         break;
2519                 }
2520         }
2521         return ((sa_security_t)node);
2522 }
2523 
2524 /*
2525  * sa_get_property(optionset, prop)
2526  *
2527  * Get the property object with the name specified in prop from the
2528  * optionset.
2529  */
2530 
2531 sa_property_t
2532 sa_get_property(sa_optionset_t optionset, char *prop)
2533 {
2534         xmlNodePtr node = (xmlNodePtr)optionset;
2535         xmlChar *value = NULL;
2536 
2537         if (optionset == NULL)
2538                 return (NULL);
2539 
2540         for (node = node->children; node != NULL;
2541             node = node->next) {
2542                 if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) {
2543                         if (prop == NULL)
2544                                 break;
2545                         value = xmlGetProp(node, (xmlChar *)"type");
2546                         if (value != NULL &&
2547                             xmlStrcmp(value, (xmlChar *)prop) == 0) {
2548                                 break;
2549                         }
2550                         if (value != NULL) {
2551                                 xmlFree(value);
2552                                 value = NULL;
2553                         }
2554                 }
2555         }
2556         if (value != NULL)
2557                 xmlFree(value);
2558         if (node != NULL && xmlStrcmp(node->name, (xmlChar *)"option") != 0) {
2559                 /*
2560                  * avoid a non option node -- it is possible to be a
2561                  * text node
2562                  */
2563                 node = NULL;
2564         }
2565         return ((sa_property_t)node);
2566 }
2567 
2568 /*
2569  * sa_get_next_property(property)
2570  *
2571  * Get the next property following the specified property. NULL if
2572  * this was the last.
2573  */
2574 
2575 sa_property_t
2576 sa_get_next_property(sa_property_t property)
2577 {
2578         xmlNodePtr node;
2579 
2580         for (node = ((xmlNodePtr)property)->next; node != NULL;
2581             node = node->next) {
2582                 if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) {
2583                         break;
2584                 }
2585         }
2586         return ((sa_property_t)node);
2587 }
2588 
2589 /*
2590  * sa_set_share_description(share, content)
2591  *
2592  * Set the description of share to content.
2593  */
2594 
2595 int
2596 sa_set_share_description(sa_share_t share, char *content)
2597 {
2598         xmlNodePtr node;
2599         sa_group_t group;
2600         int ret = SA_OK;
2601 
2602         for (node = ((xmlNodePtr)share)->children; node != NULL;
2603             node = node->next) {
2604                 if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) {
2605                         break;
2606                 }
2607         }
2608         /* no existing description but want to add */
2609         if (node == NULL && content != NULL) {
2610                 /* add a description */
2611                 node = _sa_set_share_description(share, content);
2612         } else if (node != NULL && content != NULL) {
2613                 /* update a description */
2614                 xmlNodeSetContent(node, (xmlChar *)content);
2615         } else if (node != NULL && content == NULL) {
2616                 /* remove an existing description */
2617                 xmlUnlinkNode(node);
2618                 xmlFreeNode(node);
2619         }
2620         group = sa_get_parent_group(share);
2621         if (group != NULL &&
2622             sa_is_persistent(share) && (!sa_group_is_zfs(group))) {
2623                 sa_handle_impl_t impl_handle;
2624                 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
2625                 if (impl_handle != NULL) {
2626                         ret = sa_commit_share(impl_handle->scfhandle, group,
2627                             share);
2628                 } else {
2629                         ret = SA_SYSTEM_ERR;
2630                 }
2631         }
2632         return (ret);
2633 }
2634 
2635 /*
2636  * fixproblemchars(string)
2637  *
2638  * don't want any newline or tab characters in the text since these
2639  * could break display of data and legacy file formats.
2640  */
2641 static void
2642 fixproblemchars(char *str)
2643 {
2644         int c;
2645         for (c = *str; c != '\0'; c = *++str) {
2646                 if (c == '\t' || c == '\n')
2647                         *str = ' ';
2648                 else if (c == '"')
2649                         *str = '\'';
2650         }
2651 }
2652 
2653 /*
2654  * sa_get_share_description(share)
2655  *
2656  * Return the description text for the specified share if it
2657  * exists. NULL if no description exists.
2658  */
2659 
2660 char *
2661 sa_get_share_description(sa_share_t share)
2662 {
2663         xmlChar *description = NULL;
2664         xmlNodePtr node;
2665 
2666         for (node = ((xmlNodePtr)share)->children; node != NULL;
2667             node = node->next) {
2668                 if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) {
2669                         break;
2670                 }
2671         }
2672         if (node != NULL) {
2673                 description = xmlNodeGetContent(node);
2674                 fixproblemchars((char *)description);
2675         }
2676         return ((char *)description);
2677 }
2678 
2679 /*
2680  * sa_free(share_description(description)
2681  *
2682  * Free the description string.
2683  */
2684 
2685 void
2686 sa_free_share_description(char *description)
2687 {
2688         xmlFree((xmlChar *)description);
2689 }
2690 
2691 /*
2692  * sa_create_optionset(group, proto)
2693  *
2694  * Create an optionset for the specified protocol in the specied
2695  * group. This is manifested as a property group within SMF.
2696  */
2697 
2698 sa_optionset_t
2699 sa_create_optionset(sa_group_t group, char *proto)
2700 {
2701         sa_optionset_t optionset;
2702         sa_group_t parent = group;
2703         sa_share_t share = NULL;
2704         int err = SA_OK;
2705         char *id = NULL;
2706 
2707         optionset = sa_get_optionset(group, proto);
2708         if (optionset != NULL) {
2709                 /* can't have a duplicate protocol */
2710                 optionset = NULL;
2711         } else {
2712                 /*
2713                  * Account for resource names being slightly
2714                  * different.
2715                  */
2716                 if (sa_is_share(group)) {
2717                         /*
2718                          * Transient shares do not have an "id" so not an
2719                          * error to not find one.
2720                          */
2721                         id = sa_get_share_attr((sa_share_t)group, "id");
2722                 } else if (sa_is_resource(group)) {
2723                         share = sa_get_resource_parent(
2724                             (sa_resource_t)group);
2725                         id = sa_get_resource_attr(share, "id");
2726 
2727                         /* id can be NULL if the group is transient (ZFS) */
2728                         if (id == NULL && sa_is_persistent(group))
2729                                 err = SA_NO_MEMORY;
2730                 }
2731                 if (err == SA_NO_MEMORY) {
2732                         /*
2733                          * Couldn't get the id for the share or
2734                          * resource. While this could be a
2735                          * configuration issue, it is most likely an
2736                          * out of memory. In any case, fail the create.
2737                          */
2738                         return (NULL);
2739                 }
2740 
2741                 optionset = (sa_optionset_t)xmlNewChild((xmlNodePtr)group,
2742                     NULL, (xmlChar *)"optionset", NULL);
2743                 /*
2744                  * only put to repository if on a group and we were
2745                  * able to create an optionset.
2746                  */
2747                 if (optionset != NULL) {
2748                         char oname[SA_STRSIZE];
2749                         char *groupname;
2750 
2751                         /*
2752                          * Need to get parent group in all cases, but also get
2753                          * the share if this is a resource.
2754                          */
2755                         if (sa_is_share(group)) {
2756                                 parent = sa_get_parent_group((sa_share_t)group);
2757                         } else if (sa_is_resource(group)) {
2758                                 share = sa_get_resource_parent(
2759                                     (sa_resource_t)group);
2760                                 parent = sa_get_parent_group(share);
2761                         }
2762 
2763                         sa_set_optionset_attr(optionset, "type", proto);
2764 
2765                         (void) sa_optionset_name(optionset, oname,
2766                             sizeof (oname), id);
2767                         groupname = sa_get_group_attr(parent, "name");
2768                         if (groupname != NULL && sa_is_persistent(group)) {
2769                                 sa_handle_impl_t impl_handle;
2770                                 impl_handle =
2771                                     (sa_handle_impl_t)sa_find_group_handle(
2772                                     group);
2773                                 assert(impl_handle != NULL);
2774                                 if (impl_handle != NULL) {
2775                                         (void) sa_get_instance(
2776                                             impl_handle->scfhandle, groupname);
2777                                         (void) sa_create_pgroup(
2778                                             impl_handle->scfhandle, oname);
2779                                 }
2780                         }
2781                         if (groupname != NULL)
2782                                 sa_free_attr_string(groupname);
2783                 }
2784         }
2785 
2786         if (id != NULL)
2787                 sa_free_attr_string(id);
2788         return (optionset);
2789 }
2790 
2791 /*
2792  * sa_get_property_parent(property)
2793  *
2794  * Given a property, return the object it is a property of. This will
2795  * be an optionset of some type.
2796  */
2797 
2798 static sa_optionset_t
2799 sa_get_property_parent(sa_property_t property)
2800 {
2801         xmlNodePtr node = NULL;
2802 
2803         if (property != NULL)
2804                 node = ((xmlNodePtr)property)->parent;
2805         return ((sa_optionset_t)node);
2806 }
2807 
2808 /*
2809  * sa_get_optionset_parent(optionset)
2810  *
2811  * Return the parent of the specified optionset. This could be a group
2812  * or a share.
2813  */
2814 
2815 static sa_group_t
2816 sa_get_optionset_parent(sa_optionset_t optionset)
2817 {
2818         xmlNodePtr node = NULL;
2819 
2820         if (optionset != NULL)
2821                 node = ((xmlNodePtr)optionset)->parent;
2822         return ((sa_group_t)node);
2823 }
2824 
2825 /*
2826  * zfs_needs_update(share)
2827  *
2828  * In order to avoid making multiple updates to a ZFS share when
2829  * setting properties, the share attribute "changed" will be set to
2830  * true when a property is added or modified.  When done adding
2831  * properties, we can then detect that an update is needed.  We then
2832  * clear the state here to detect additional changes.
2833  */
2834 
2835 static int
2836 zfs_needs_update(sa_share_t share)
2837 {
2838         char *attr;
2839         int result = 0;
2840 
2841         attr = sa_get_share_attr(share, "changed");
2842         if (attr != NULL) {
2843                 sa_free_attr_string(attr);
2844                 result = 1;
2845         }
2846         set_node_attr((void *)share, "changed", NULL);
2847         return (result);
2848 }
2849 
2850 /*
2851  * zfs_set_update(share)
2852  *
2853  * Set the changed attribute of the share to true.
2854  */
2855 
2856 static void
2857 zfs_set_update(sa_share_t share)
2858 {
2859         set_node_attr((void *)share, "changed", "true");
2860 }
2861 
2862 /*
2863  * sa_commit_properties(optionset, clear)
2864  *
2865  * Check if SMF or ZFS config and either update or abort the pending
2866  * changes.
2867  */
2868 
2869 int
2870 sa_commit_properties(sa_optionset_t optionset, int clear)
2871 {
2872         sa_group_t group;
2873         sa_group_t parent;
2874         int zfs = 0;
2875         int needsupdate = 0;
2876         int ret = SA_OK;
2877         sa_handle_impl_t impl_handle;
2878 
2879         group = sa_get_optionset_parent(optionset);
2880         if (group != NULL && (sa_is_share(group) || is_zfs_group(group))) {
2881                 /* only update ZFS if on a share */
2882                 parent = sa_get_parent_group(group);
2883                 zfs++;
2884                 if (parent != NULL && is_zfs_group(parent))
2885                         needsupdate = zfs_needs_update(group);
2886                 else
2887                         zfs = 0;
2888         }
2889         if (zfs) {
2890                 if (!clear && needsupdate)
2891                         ret = sa_zfs_update((sa_share_t)group);
2892         } else {
2893                 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
2894                 if (impl_handle != NULL) {
2895                         if (clear) {
2896                                 (void) sa_abort_transaction(
2897                                     impl_handle->scfhandle);
2898                         } else {
2899                                 ret = sa_end_transaction(
2900                                     impl_handle->scfhandle, impl_handle);
2901                         }
2902                 } else {
2903                         ret = SA_SYSTEM_ERR;
2904                 }
2905         }
2906         return (ret);
2907 }
2908 
2909 /*
2910  * sa_destroy_optionset(optionset)
2911  *
2912  * Remove the optionset from its group. Update the repository to
2913  * reflect this change.
2914  */
2915 
2916 int
2917 sa_destroy_optionset(sa_optionset_t optionset)
2918 {
2919         char name[SA_STRSIZE];
2920         int len;
2921         int ret;
2922         char *id = NULL;
2923         sa_group_t group;
2924         int ispersist = 1;
2925 
2926         /* now delete the prop group */
2927         group = sa_get_optionset_parent(optionset);
2928         if (group != NULL) {
2929                 if (sa_is_resource(group)) {
2930                         sa_resource_t resource = group;
2931                         sa_share_t share = sa_get_resource_parent(resource);
2932                         group = sa_get_parent_group(share);
2933                         id = sa_get_share_attr(share, "id");
2934                 } else if (sa_is_share(group)) {
2935                         id = sa_get_share_attr((sa_share_t)group, "id");
2936                 }
2937                 ispersist = sa_is_persistent(group);
2938         }
2939         if (ispersist) {
2940                 sa_handle_impl_t impl_handle;
2941                 len = sa_optionset_name(optionset, name, sizeof (name), id);
2942                 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
2943                 if (impl_handle != NULL) {
2944                         if (len > 0) {
2945                                 ret = sa_delete_pgroup(impl_handle->scfhandle,
2946                                     name);
2947                         }
2948                 } else {
2949                         ret = SA_SYSTEM_ERR;
2950                 }
2951         }
2952         xmlUnlinkNode((xmlNodePtr)optionset);
2953         xmlFreeNode((xmlNodePtr)optionset);
2954         if (id != NULL)
2955                 sa_free_attr_string(id);
2956         return (ret);
2957 }
2958 
2959 /* private to the implementation */
2960 int
2961 _sa_remove_optionset(sa_optionset_t optionset)
2962 {
2963         int ret = SA_OK;
2964 
2965         xmlUnlinkNode((xmlNodePtr)optionset);
2966         xmlFreeNode((xmlNodePtr)optionset);
2967         return (ret);
2968 }
2969 
2970 /*
2971  * sa_create_security(group, sectype, proto)
2972  *
2973  * Create a security optionset (one that has a type name and a
2974  * proto). Security is left over from a pure NFS implementation. The
2975  * naming will change in the future when the API is released.
2976  */
2977 sa_security_t
2978 sa_create_security(sa_group_t group, char *sectype, char *proto)
2979 {
2980         sa_security_t security;
2981         char *id = NULL;
2982         sa_group_t parent;
2983         char *groupname = NULL;
2984 
2985         if (group != NULL && sa_is_share(group)) {
2986                 id = sa_get_share_attr((sa_share_t)group, "id");
2987                 parent = sa_get_parent_group(group);
2988                 if (parent != NULL)
2989                         groupname = sa_get_group_attr(parent, "name");
2990         } else if (group != NULL) {
2991                 groupname = sa_get_group_attr(group, "name");
2992         }
2993 
2994         security = sa_get_security(group, sectype, proto);
2995         if (security != NULL) {
2996                 /* can't have a duplicate security option */
2997                 security = NULL;
2998         } else {
2999                 security = (sa_security_t)xmlNewChild((xmlNodePtr)group,
3000                     NULL, (xmlChar *)"security", NULL);
3001                 if (security != NULL) {
3002                         char oname[SA_STRSIZE];
3003                         sa_set_security_attr(security, "type", proto);
3004 
3005                         sa_set_security_attr(security, "sectype", sectype);
3006                         (void) sa_security_name(security, oname,
3007                             sizeof (oname), id);
3008                         if (groupname != NULL && sa_is_persistent(group)) {
3009                                 sa_handle_impl_t impl_handle;
3010                                 impl_handle =
3011                                     (sa_handle_impl_t)sa_find_group_handle(
3012                                     group);
3013                                 if (impl_handle != NULL) {
3014                                         (void) sa_get_instance(
3015                                             impl_handle->scfhandle, groupname);
3016                                         (void) sa_create_pgroup(
3017                                             impl_handle->scfhandle, oname);
3018                                 }
3019                         }
3020                 }
3021         }
3022         if (id != NULL)
3023                 sa_free_attr_string(id);
3024         if (groupname != NULL)
3025                 sa_free_attr_string(groupname);
3026         return (security);
3027 }
3028 
3029 /*
3030  * sa_destroy_security(security)
3031  *
3032  * Remove the specified optionset from the document and the
3033  * configuration.
3034  */
3035 
3036 int
3037 sa_destroy_security(sa_security_t security)
3038 {
3039         char name[SA_STRSIZE];
3040         int len;
3041         int ret = SA_OK;
3042         char *id = NULL;
3043         sa_group_t group;
3044         int iszfs = 0;
3045         int ispersist = 1;
3046 
3047         group = sa_get_optionset_parent(security);
3048 
3049         if (group != NULL)
3050                 iszfs = sa_group_is_zfs(group);
3051 
3052         if (group != NULL && !iszfs) {
3053                 if (sa_is_share(group))
3054                         ispersist = sa_is_persistent(group);
3055                 id = sa_get_share_attr((sa_share_t)group, "id");
3056         }
3057         if (ispersist) {
3058                 len = sa_security_name(security, name, sizeof (name), id);
3059                 if (!iszfs && len > 0) {
3060                         sa_handle_impl_t impl_handle;
3061                         impl_handle =
3062                             (sa_handle_impl_t)sa_find_group_handle(group);
3063                         if (impl_handle != NULL) {
3064                                 ret = sa_delete_pgroup(impl_handle->scfhandle,
3065                                     name);
3066                         } else {
3067                                 ret = SA_SYSTEM_ERR;
3068                         }
3069                 }
3070         }
3071         xmlUnlinkNode((xmlNodePtr)security);
3072         xmlFreeNode((xmlNodePtr)security);
3073         if (iszfs)
3074                 ret = sa_zfs_update(group);
3075         if (id != NULL)
3076                 sa_free_attr_string(id);
3077         return (ret);
3078 }
3079 
3080 /*
3081  * sa_get_security_attr(optionset, tag)
3082  *
3083  * Return the specified attribute value from the optionset.
3084  */
3085 
3086 char *
3087 sa_get_security_attr(sa_property_t optionset, char *tag)
3088 {
3089         return (get_node_attr((void *)optionset, tag));
3090 
3091 }
3092 
3093 /*
3094  * sa_set_security_attr(optionset, tag, value)
3095  *
3096  * Set the optioset attribute specied by tag to the specified value.
3097  */
3098 
3099 void
3100 sa_set_security_attr(sa_group_t optionset, char *tag, char *value)
3101 {
3102         set_node_attr((void *)optionset, tag, value);
3103 }
3104 
3105 /*
3106  * is_nodetype(node, type)
3107  *
3108  * Check to see if node is of the type specified.
3109  */
3110 
3111 static int
3112 is_nodetype(void *node, char *type)
3113 {
3114         return (strcmp((char *)((xmlNodePtr)node)->name, type) == 0);
3115 }
3116 
3117 /*
3118  * add_or_update()
3119  *
3120  * Add or update a property. Pulled out of sa_set_prop_by_prop for
3121  * readability.
3122  */
3123 static int
3124 add_or_update(scfutilhandle_t *scf_handle, int type, scf_value_t *value,
3125     scf_transaction_entry_t *entry, char *name, char *valstr)
3126 {
3127         int ret = SA_SYSTEM_ERR;
3128 
3129         if (value != NULL) {
3130                 if (type == SA_PROP_OP_ADD)
3131                         ret = scf_transaction_property_new(scf_handle->trans,
3132                             entry, name, SCF_TYPE_ASTRING);
3133                 else
3134                         ret = scf_transaction_property_change(scf_handle->trans,
3135                             entry, name, SCF_TYPE_ASTRING);
3136                 if (ret == 0) {
3137                         ret = scf_value_set_astring(value, valstr);
3138                         if (ret == 0)
3139                                 ret = scf_entry_add_value(entry, value);
3140                         if (ret == 0)
3141                                 return (ret);
3142                         scf_value_destroy(value);
3143                 } else {
3144                         scf_entry_destroy(entry);
3145                 }
3146         }
3147         return (SA_SYSTEM_ERR);
3148 }
3149 
3150 /*
3151  * sa_set_prop_by_prop(optionset, group, prop, type)
3152  *
3153  * Add/remove/update the specified property prop into the optionset or
3154  * share. If a share, sort out which property group based on GUID. In
3155  * all cases, the appropriate transaction is set (or ZFS share is
3156  * marked as needing an update)
3157  */
3158 
3159 static int
3160 sa_set_prop_by_prop(sa_optionset_t optionset, sa_group_t group,
3161     sa_property_t prop, int type)
3162 {
3163         char *name;
3164         char *valstr;
3165         int ret = SA_OK;
3166         scf_transaction_entry_t *entry;
3167         scf_value_t *value;
3168         int opttype; /* 1 == optionset, 0 == security */
3169         char *id = NULL;
3170         int iszfs = 0;
3171         sa_group_t parent = NULL;
3172         sa_share_t share = NULL;
3173         sa_handle_impl_t impl_handle;
3174         scfutilhandle_t  *scf_handle;
3175 
3176         if (!sa_is_persistent(group)) {
3177                 /*
3178                  * if the group/share is not persistent we don't need
3179                  * to do anything here
3180                  */
3181                 return (SA_OK);
3182         }
3183         impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
3184         if (impl_handle == NULL || impl_handle->scfhandle == NULL)
3185                 return (SA_SYSTEM_ERR);
3186         scf_handle = impl_handle->scfhandle;
3187         name = sa_get_property_attr(prop, "type");
3188         valstr = sa_get_property_attr(prop, "value");
3189         entry = scf_entry_create(scf_handle->handle);
3190         opttype = is_nodetype((void *)optionset, "optionset");
3191 
3192         /*
3193          * Check for share vs. resource since they need slightly
3194          * different treatment given the hierarchy.
3195          */
3196         if (valstr != NULL && entry != NULL) {
3197                 if (sa_is_share(group)) {
3198                         parent = sa_get_parent_group(group);
3199                         share = (sa_share_t)group;
3200                         if (parent != NULL)
3201                                 iszfs = is_zfs_group(parent);
3202                 } else if (sa_is_resource(group)) {
3203                         share = sa_get_parent_group(group);
3204                         if (share != NULL)
3205                                 parent = sa_get_parent_group(share);
3206                 } else {
3207                         iszfs = is_zfs_group(group);
3208                 }
3209                 if (!iszfs) {
3210                         if (scf_handle->trans == NULL) {
3211                                 char oname[SA_STRSIZE];
3212                                 char *groupname = NULL;
3213                                 if (share != NULL) {
3214                                         if (parent != NULL)
3215                                                 groupname =
3216                                                     sa_get_group_attr(parent,
3217                                                     "name");
3218                                         id = sa_get_share_attr(
3219                                             (sa_share_t)share, "id");
3220                                 } else {
3221                                         groupname = sa_get_group_attr(group,
3222                                             "name");
3223                                 }
3224                                 if (groupname != NULL) {
3225                                         ret = sa_get_instance(scf_handle,
3226                                             groupname);
3227                                         sa_free_attr_string(groupname);
3228                                 }
3229                                 if (opttype)
3230                                         (void) sa_optionset_name(optionset,
3231                                             oname, sizeof (oname), id);
3232                                 else
3233                                         (void) sa_security_name(optionset,
3234                                             oname, sizeof (oname), id);
3235                                 ret = sa_start_transaction(scf_handle, oname);
3236                                 if (id != NULL)
3237                                         sa_free_attr_string(id);
3238                         }
3239                         if (ret == SA_OK) {
3240                                 switch (type) {
3241                                 case SA_PROP_OP_REMOVE:
3242                                         ret = scf_transaction_property_delete(
3243                                             scf_handle->trans, entry, name);
3244                                         break;
3245                                 case SA_PROP_OP_ADD:
3246                                 case SA_PROP_OP_UPDATE:
3247                                         value = scf_value_create(
3248                                             scf_handle->handle);
3249                                         ret = add_or_update(scf_handle, type,
3250                                             value, entry, name, valstr);
3251                                         break;
3252                                 }
3253                         }
3254                 } else {
3255                         /*
3256                          * ZFS update. The calling function would have updated
3257                          * the internal XML structure. Just need to flag it as
3258                          * changed for ZFS.
3259                          */
3260                         zfs_set_update((sa_share_t)group);
3261                 }
3262         }
3263 
3264         if (name != NULL)
3265                 sa_free_attr_string(name);
3266         if (valstr != NULL)
3267                 sa_free_attr_string(valstr);
3268         else if (entry != NULL)
3269                 scf_entry_destroy(entry);
3270 
3271         if (ret == -1)
3272                 ret = SA_SYSTEM_ERR;
3273 
3274         return (ret);
3275 }
3276 
3277 /*
3278  * sa_create_section(name, value)
3279  *
3280  * Create a new section with the specified name and extra data.
3281  */
3282 
3283 sa_property_t
3284 sa_create_section(char *name, char *extra)
3285 {
3286         xmlNodePtr node;
3287 
3288         node = xmlNewNode(NULL, (xmlChar *)"section");
3289         if (node != NULL) {
3290                 if (name != NULL)
3291                         (void) xmlSetProp(node, (xmlChar *)"name",
3292                             (xmlChar *)name);
3293                 if (extra != NULL)
3294                         (void) xmlSetProp(node, (xmlChar *)"extra",
3295                             (xmlChar *)extra);
3296         }
3297         return ((sa_property_t)node);
3298 }
3299 
3300 void
3301 sa_set_section_attr(sa_property_t sect, char *name, char *value)
3302 {
3303         (void) xmlSetProp(sect, (xmlChar *)name, (xmlChar *)value);
3304 }
3305 
3306 /*
3307  * sa_create_property(section, name, value)
3308  *
3309  * Create a new property with the specified name and value.
3310  */
3311 
3312 sa_property_t
3313 sa_create_property(char *name, char *value)
3314 {
3315         xmlNodePtr node;
3316 
3317         node = xmlNewNode(NULL, (xmlChar *)"option");
3318         if (node != NULL) {
3319                 (void) xmlSetProp(node, (xmlChar *)"type", (xmlChar *)name);
3320                 (void) xmlSetProp(node, (xmlChar *)"value", (xmlChar *)value);
3321         }
3322         return ((sa_property_t)node);
3323 }
3324 
3325 /*
3326  * sa_add_property(object, property)
3327  *
3328  * Add the specified property to the object. Issue the appropriate
3329  * transaction or mark a ZFS object as needing an update.
3330  */
3331 
3332 int
3333 sa_add_property(void *object, sa_property_t property)
3334 {
3335         int ret = SA_OK;
3336         sa_group_t parent;
3337         sa_group_t group;
3338         char *proto;
3339 
3340         if (property != NULL) {
3341                 sa_handle_t handle;
3342                 handle = sa_find_group_handle((sa_group_t)object);
3343                 /* It is legitimate to not find a handle */
3344                 proto = sa_get_optionset_attr(object, "type");
3345                 if ((ret = sa_valid_property(handle, object, proto,
3346                     property)) == SA_OK) {
3347                         property = (sa_property_t)xmlAddChild(
3348                             (xmlNodePtr)object, (xmlNodePtr)property);
3349                 } else {
3350                         if (proto != NULL)
3351                                 sa_free_attr_string(proto);
3352                         return (ret);
3353                 }
3354                 if (proto != NULL)
3355                         sa_free_attr_string(proto);
3356         }
3357 
3358 
3359         parent = sa_get_parent_group(object);
3360         if (!sa_is_persistent(parent))
3361                 return (ret);
3362 
3363         if (sa_is_resource(parent)) {
3364                 /*
3365                  * Resources are children of share.  Need to go up two
3366                  * levels to find the group but the parent needs to be
3367                  * the share at this point in order to get the "id".
3368                  */
3369                 parent = sa_get_parent_group(parent);
3370                 group = sa_get_parent_group(parent);
3371         } else if (sa_is_share(parent)) {
3372                 group = sa_get_parent_group(parent);
3373         } else {
3374                 group = parent;
3375         }
3376 
3377         if (property == NULL) {
3378                 ret = SA_NO_MEMORY;
3379         } else {
3380                 char oname[SA_STRSIZE];
3381 
3382                 if (!is_zfs_group(group)) {
3383                         char *id = NULL;
3384                         sa_handle_impl_t impl_handle;
3385                         scfutilhandle_t  *scf_handle;
3386 
3387                         impl_handle = (sa_handle_impl_t)sa_find_group_handle(
3388                             group);
3389                         if (impl_handle == NULL ||
3390                             impl_handle->scfhandle == NULL)
3391                                 ret = SA_SYSTEM_ERR;
3392                         if (ret == SA_OK) {
3393                                 scf_handle = impl_handle->scfhandle;
3394                                 if (sa_is_share((sa_group_t)parent)) {
3395                                         id = sa_get_share_attr(
3396                                             (sa_share_t)parent, "id");
3397                                 }
3398                                 if (scf_handle->trans == NULL) {
3399                                         if (is_nodetype(object, "optionset")) {
3400                                                 (void) sa_optionset_name(
3401                                                     (sa_optionset_t)object,
3402                                                     oname, sizeof (oname), id);
3403                                         } else {
3404                                                 (void) sa_security_name(
3405                                                     (sa_optionset_t)object,
3406                                                     oname, sizeof (oname), id);
3407                                         }
3408                                         ret = sa_start_transaction(scf_handle,
3409                                             oname);
3410                                 }
3411                                 if (ret == SA_OK) {
3412                                         char *name;
3413                                         char *value;
3414                                         name = sa_get_property_attr(property,
3415                                             "type");
3416                                         value = sa_get_property_attr(property,
3417                                             "value");
3418                                         if (name != NULL && value != NULL) {
3419                                                 if (scf_handle->scf_state ==
3420                                                     SCH_STATE_INIT) {
3421                                                         ret = sa_set_property(
3422                                                             scf_handle, name,
3423                                                             value);
3424                                                 }
3425                                         } else {
3426                                                 ret = SA_CONFIG_ERR;
3427                                         }
3428                                         if (name != NULL)
3429                                                 sa_free_attr_string(
3430                                                     name);
3431                                         if (value != NULL)
3432                                                 sa_free_attr_string(value);
3433                                 }
3434                                 if (id != NULL)
3435                                         sa_free_attr_string(id);
3436                         }
3437                 } else {
3438                         /*
3439                          * ZFS is a special case. We do want
3440                          * to allow editing property/security
3441                          * lists since we can have a better
3442                          * syntax and we also want to keep
3443                          * things consistent when possible.
3444                          *
3445                          * Right now, we defer until the
3446                          * sa_commit_properties so we can get
3447                          * them all at once. We do need to
3448                          * mark the share as "changed"
3449                          */
3450                         zfs_set_update((sa_share_t)parent);
3451                 }
3452         }
3453         return (ret);
3454 }
3455 
3456 /*
3457  * sa_remove_property(property)
3458  *
3459  * Remove the specied property from its containing object. Update the
3460  * repository as appropriate.
3461  */
3462 
3463 int
3464 sa_remove_property(sa_property_t property)
3465 {
3466         int ret = SA_OK;
3467 
3468         if (property != NULL) {
3469                 sa_optionset_t optionset;
3470                 sa_group_t group;
3471                 optionset = sa_get_property_parent(property);
3472                 if (optionset != NULL) {
3473                         group = sa_get_optionset_parent(optionset);
3474                         if (group != NULL) {
3475                                 ret = sa_set_prop_by_prop(optionset, group,
3476                                     property, SA_PROP_OP_REMOVE);
3477                         }
3478                 }
3479                 xmlUnlinkNode((xmlNodePtr)property);
3480                 xmlFreeNode((xmlNodePtr)property);
3481         } else {
3482                 ret = SA_NO_SUCH_PROP;
3483         }
3484         return (ret);
3485 }
3486 
3487 /*
3488  * sa_update_property(property, value)
3489  *
3490  * Update the specified property to the new value.  If value is NULL,
3491  * we currently treat this as a remove.
3492  */
3493 
3494 int
3495 sa_update_property(sa_property_t property, char *value)
3496 {
3497         int ret = SA_OK;
3498         if (value == NULL) {
3499                 return (sa_remove_property(property));
3500         } else {
3501                 sa_optionset_t optionset;
3502                 sa_group_t group;
3503                 set_node_attr((void *)property, "value", value);
3504                 optionset = sa_get_property_parent(property);
3505                 if (optionset != NULL) {
3506                         group = sa_get_optionset_parent(optionset);
3507                         if (group != NULL) {
3508                                 ret = sa_set_prop_by_prop(optionset, group,
3509                                     property, SA_PROP_OP_UPDATE);
3510                         }
3511                 } else {
3512                         ret = SA_NO_SUCH_PROP;
3513                 }
3514         }
3515         return (ret);
3516 }
3517 
3518 /*
3519  * sa_get_protocol_section(propset, prop)
3520  *
3521  * Get the specified protocol specific section. These are global to
3522  * the protocol and not specific to a group or share.
3523  */
3524 
3525 sa_protocol_properties_t
3526 sa_get_protocol_section(sa_protocol_properties_t propset, char *section)
3527 {
3528         xmlNodePtr node = (xmlNodePtr)propset;
3529         xmlChar *value = NULL;
3530         char *proto;
3531 
3532         proto = sa_get_optionset_attr(propset, "type");
3533         if ((sa_proto_get_featureset(proto) & SA_FEATURE_HAS_SECTIONS) == 0) {
3534                 if (proto != NULL)
3535                         sa_free_attr_string(proto);
3536                 return (propset);
3537         }
3538 
3539         for (node = node->children; node != NULL;
3540             node = node->next) {
3541                 if (xmlStrcmp(node->name, (xmlChar *)"section") == 0) {
3542                         if (section == NULL)
3543                                 break;
3544                         value = xmlGetProp(node, (xmlChar *)"name");
3545                         if (value != NULL &&
3546                             xmlStrcasecmp(value, (xmlChar *)section) == 0) {
3547                                 break;
3548                         }
3549                         if (value != NULL) {
3550                                 xmlFree(value);
3551                                 value = NULL;
3552                         }
3553                 }
3554         }
3555         if (value != NULL)
3556                 xmlFree(value);
3557         if (proto != NULL)
3558                 sa_free_attr_string(proto);
3559         if (node != NULL && xmlStrcmp(node->name, (xmlChar *)"section") != 0) {
3560                 /*
3561                  * avoid a non option node -- it is possible to be a
3562                  * text node
3563                  */
3564                 node = NULL;
3565         }
3566         return ((sa_protocol_properties_t)node);
3567 }
3568 
3569 /*
3570  * sa_get_next_protocol_section(prop, find)
3571  *
3572  * Get the next protocol specific section in the list.
3573  */
3574 
3575 sa_property_t
3576 sa_get_next_protocol_section(sa_property_t prop, char *find)
3577 {
3578         xmlNodePtr node;
3579         xmlChar *value = NULL;
3580         char *proto;
3581 
3582         proto = sa_get_optionset_attr(prop, "type");
3583         if ((sa_proto_get_featureset(proto) & SA_FEATURE_HAS_SECTIONS) == 0) {
3584                 if (proto != NULL)
3585                         sa_free_attr_string(proto);
3586                 return ((sa_property_t)NULL);
3587         }
3588 
3589         for (node = ((xmlNodePtr)prop)->next; node != NULL;
3590             node = node->next) {
3591                 if (xmlStrcmp(node->name, (xmlChar *)"section") == 0) {
3592                         if (find == NULL)
3593                                 break;
3594                         value = xmlGetProp(node, (xmlChar *)"name");
3595                         if (value != NULL &&
3596                             xmlStrcasecmp(value, (xmlChar *)find) == 0) {
3597                                 break;
3598                         }
3599                         if (value != NULL) {
3600                                 xmlFree(value);
3601                                 value = NULL;
3602                         }
3603 
3604                 }
3605         }
3606         if (value != NULL)
3607                 xmlFree(value);
3608         if (proto != NULL)
3609                 sa_free_attr_string(proto);
3610         return ((sa_property_t)node);
3611 }
3612 
3613 /*
3614  * sa_get_protocol_property(propset, prop)
3615  *
3616  * Get the specified protocol specific property. These are global to
3617  * the protocol and not specific to a group or share.
3618  */
3619 
3620 sa_property_t
3621 sa_get_protocol_property(sa_protocol_properties_t propset, char *prop)
3622 {
3623         xmlNodePtr node = (xmlNodePtr)propset;
3624         xmlChar *value = NULL;
3625 
3626         if (propset == NULL)
3627                 return (NULL);
3628 
3629         for (node = node->children; node != NULL;
3630             node = node->next) {
3631                 if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) {
3632                         if (prop == NULL)
3633                                 break;
3634                         value = xmlGetProp(node, (xmlChar *)"type");
3635                         if (value != NULL &&
3636                             xmlStrcasecmp(value, (xmlChar *)prop) == 0) {
3637                                 break;
3638                         }
3639                         if (value != NULL) {
3640                                 xmlFree(value);
3641                                 value = NULL;
3642                         }
3643                 }
3644         }
3645         if (value != NULL)
3646                 xmlFree(value);
3647         if (node != NULL && xmlStrcmp(node->name, (xmlChar *)"option") != 0) {
3648                 /*
3649                  * avoid a non option node -- it is possible to be a
3650                  * text node
3651                  */
3652                 node = NULL;
3653         }
3654         return ((sa_property_t)node);
3655 }
3656 
3657 /*
3658  * sa_get_next_protocol_property(prop)
3659  *
3660  * Get the next protocol specific property in the list.
3661  */
3662 
3663 sa_property_t
3664 sa_get_next_protocol_property(sa_property_t prop, char *find)
3665 {
3666         xmlNodePtr node;
3667         xmlChar *value = NULL;
3668 
3669         for (node = ((xmlNodePtr)prop)->next; node != NULL;
3670             node = node->next) {
3671                 if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) {
3672                         if (find == NULL)
3673                                 break;
3674                         value = xmlGetProp(node, (xmlChar *)"type");
3675                         if (value != NULL &&
3676                             xmlStrcasecmp(value, (xmlChar *)find) == 0) {
3677                                 break;
3678                         }
3679                         if (value != NULL) {
3680                                 xmlFree(value);
3681                                 value = NULL;
3682                         }
3683 
3684                 }
3685         }
3686         if (value != NULL)
3687                 xmlFree(value);
3688         return ((sa_property_t)node);
3689 }
3690 
3691 /*
3692  * sa_set_protocol_property(prop, value)
3693  *
3694  * Set the specified property to have the new value.  The protocol
3695  * specific plugin will then be called to update the property.
3696  */
3697 
3698 int
3699 sa_set_protocol_property(sa_property_t prop, char *section, char *value)
3700 {
3701         sa_protocol_properties_t propset;
3702         char *proto;
3703         int ret = SA_INVALID_PROTOCOL;
3704 
3705         propset = ((xmlNodePtr)prop)->parent;
3706         if (propset != NULL) {
3707                 proto = sa_get_optionset_attr(propset, "type");
3708                 if (proto != NULL) {
3709                         if (section != NULL)
3710                                 set_node_attr((xmlNodePtr)prop, "section",
3711                                     section);
3712                         set_node_attr((xmlNodePtr)prop, "value", value);
3713                         ret = sa_proto_set_property(proto, prop);
3714                         sa_free_attr_string(proto);
3715                 }
3716         }
3717         return (ret);
3718 }
3719 
3720 /*
3721  * sa_add_protocol_property(propset, prop)
3722  *
3723  * Add a new property to the protocol specific property set.
3724  */
3725 
3726 int
3727 sa_add_protocol_property(sa_protocol_properties_t propset, sa_property_t prop)
3728 {
3729         xmlNodePtr node;
3730 
3731         /* should check for legitimacy */
3732         node = xmlAddChild((xmlNodePtr)propset, (xmlNodePtr)prop);
3733         if (node != NULL)
3734                 return (SA_OK);
3735         return (SA_NO_MEMORY);
3736 }
3737 
3738 /*
3739  * sa_create_protocol_properties(proto)
3740  *
3741  * Create a protocol specific property set.
3742  */
3743 
3744 sa_protocol_properties_t
3745 sa_create_protocol_properties(char *proto)
3746 {
3747         xmlNodePtr node;
3748 
3749         node = xmlNewNode(NULL, (xmlChar *)"propertyset");
3750         if (node != NULL)
3751                 (void) xmlSetProp(node, (xmlChar *)"type", (xmlChar *)proto);
3752         return (node);
3753 }
3754 
3755 /*
3756  * sa_get_share_resource(share, resource)
3757  *
3758  * Get the named resource from the share, if it exists. If resource is
3759  * NULL, get the first resource.
3760  */
3761 
3762 sa_resource_t
3763 sa_get_share_resource(sa_share_t share, char *resource)
3764 {
3765         xmlNodePtr node = NULL;
3766         xmlChar *name;
3767 
3768         if (share != NULL) {
3769                 for (node = ((xmlNodePtr)share)->children; node != NULL;
3770                     node = node->next) {
3771                         if (xmlStrcmp(node->name, (xmlChar *)"resource") == 0) {
3772                                 if (resource == NULL) {
3773                                         /*
3774                                          * We are looking for the first
3775                                          * resource node and not a names
3776                                          * resource.
3777                                          */
3778                                         break;
3779                                 } else {
3780                                         /* is it the correct share? */
3781                                         name = xmlGetProp(node,
3782                                             (xmlChar *)"name");
3783                                         if (name != NULL &&
3784                                             xmlStrcasecmp(name,
3785                                             (xmlChar *)resource) == 0) {
3786                                                 xmlFree(name);
3787                                                 break;
3788                                         }
3789                                         xmlFree(name);
3790                                 }
3791                         }
3792                 }
3793         }
3794         return ((sa_resource_t)node);
3795 }
3796 
3797 /*
3798  * sa_get_next_resource(resource)
3799  *      Return the next share following the specified share
3800  *      from the internal list of shares. Returns NULL if there
3801  *      are no more shares.  The list is relative to the same
3802  *      group.
3803  */
3804 sa_share_t
3805 sa_get_next_resource(sa_resource_t resource)
3806 {
3807         xmlNodePtr node = NULL;
3808 
3809         if (resource != NULL) {
3810                 for (node = ((xmlNodePtr)resource)->next; node != NULL;
3811                     node = node->next) {
3812                         if (xmlStrcmp(node->name, (xmlChar *)"resource") == 0)
3813                                 break;
3814                 }
3815         }
3816         return ((sa_share_t)node);
3817 }
3818 
3819 /*
3820  * _sa_get_next_resource_index(share)
3821  *
3822  * get the next resource index number (one greater then current largest)
3823  */
3824 
3825 static int
3826 _sa_get_next_resource_index(sa_share_t share)
3827 {
3828         sa_resource_t resource;
3829         int index = 0;
3830         char *id;
3831 
3832         for (resource = sa_get_share_resource(share, NULL);
3833             resource != NULL;
3834             resource = sa_get_next_resource(resource)) {
3835                 id = get_node_attr((void *)resource, "id");
3836                 if (id != NULL) {
3837                         int val;
3838                         val = atoi(id);
3839                         if (val > index)
3840                                 index = val;
3841                         sa_free_attr_string(id);
3842                 }
3843         }
3844         return (index + 1);
3845 }
3846 
3847 
3848 /*
3849  * sa_add_resource(share, resource, persist, &err)
3850  *
3851  * Adds a new resource name associated with share. The resource name
3852  * must be unique in the system and will be case insensitive (eventually).
3853  */
3854 
3855 sa_resource_t
3856 sa_add_resource(sa_share_t share, char *resource, int persist, int *error)
3857 {
3858         xmlNodePtr node;
3859         int err = SA_OK;
3860         sa_resource_t res;
3861         sa_group_t group;
3862         sa_handle_t handle;
3863         char istring[8]; /* just big enough for an integer value */
3864         int index;
3865 
3866         group = sa_get_parent_group(share);
3867         handle = sa_find_group_handle(group);
3868         res = sa_find_resource(handle, resource);
3869         if (res != NULL) {
3870                 err = SA_DUPLICATE_NAME;
3871                 res = NULL;
3872         } else {
3873                 node = xmlNewChild((xmlNodePtr)share, NULL,
3874                     (xmlChar *)"resource", NULL);
3875                 if (node != NULL) {
3876                         (void) xmlSetProp(node, (xmlChar *)"name",
3877                             (xmlChar *)resource);
3878                         (void) xmlSetProp(node, (xmlChar *)"type", persist ?
3879                             (xmlChar *)"persist" : (xmlChar *)"transient");
3880                         if (persist != SA_SHARE_TRANSIENT) {
3881                                 index = _sa_get_next_resource_index(share);
3882                                 (void) snprintf(istring, sizeof (istring), "%d",
3883                                     index);
3884                                 (void) xmlSetProp(node, (xmlChar *)"id",
3885                                     (xmlChar *)istring);
3886 
3887                                 if (!sa_is_persistent((sa_group_t)share))
3888                                         goto done;
3889 
3890                                 if (!sa_group_is_zfs(group)) {
3891                                         /* ZFS doesn't use resource names */
3892                                         sa_handle_impl_t ihandle;
3893 
3894                                         ihandle = (sa_handle_impl_t)
3895                                             sa_find_group_handle(
3896                                             group);
3897                                         if (ihandle != NULL)
3898                                                 err = sa_commit_share(
3899                                                     ihandle->scfhandle, group,
3900                                                     share);
3901                                         else
3902                                                 err = SA_SYSTEM_ERR;
3903                                 } else {
3904                                         err = sa_zfs_update((sa_share_t)group);
3905                                 }
3906                         }
3907                 }
3908         }
3909 done:
3910         if (error != NULL)
3911                 *error = err;
3912         return ((sa_resource_t)node);
3913 }
3914 
3915 /*
3916  * sa_remove_resource(resource)
3917  *
3918  * Remove the resource name from the share (and the system)
3919  */
3920 
3921 int
3922 sa_remove_resource(sa_resource_t resource)
3923 {
3924         sa_share_t share;
3925         sa_group_t group;
3926         char *type;
3927         int ret = SA_OK;
3928         boolean_t transient = B_FALSE;
3929         sa_optionset_t opt;
3930 
3931         share = sa_get_resource_parent(resource);
3932         type = sa_get_share_attr(share, "type");
3933         group = sa_get_parent_group(share);
3934 
3935 
3936         if (type != NULL) {
3937                 if (strcmp(type, "persist") != 0)
3938                         transient = B_TRUE;
3939                 sa_free_attr_string(type);
3940         }
3941 
3942         /* Disable the resource for all protocols. */
3943         (void) sa_disable_resource(resource, NULL);
3944 
3945         /* Remove any optionsets from the resource. */
3946         for (opt = sa_get_optionset(resource, NULL);
3947             opt != NULL;
3948             opt = sa_get_next_optionset(opt))
3949                 (void) sa_destroy_optionset(opt);
3950 
3951         /* Remove from the share */
3952         xmlUnlinkNode((xmlNode *)resource);
3953         xmlFreeNode((xmlNode *)resource);
3954 
3955         /* only do SMF action if permanent and not ZFS */
3956         if (transient)
3957                 return (ret);
3958 
3959         if (!sa_group_is_zfs(group)) {
3960                 sa_handle_impl_t ihandle;
3961                 ihandle = (sa_handle_impl_t)sa_find_group_handle(group);
3962                 if (ihandle != NULL)
3963                         ret = sa_commit_share(ihandle->scfhandle, group, share);
3964                 else
3965                         ret = SA_SYSTEM_ERR;
3966         } else {
3967                 ret = sa_zfs_update((sa_share_t)group);
3968         }
3969 
3970         return (ret);
3971 }
3972 
3973 /*
3974  * proto_rename_resource(handle, group, resource, newname)
3975  *
3976  * Helper function for sa_rename_resource that notifies the protocol
3977  * of a resource name change prior to a config repository update.
3978  */
3979 static int
3980 proto_rename_resource(sa_handle_t handle, sa_group_t group,
3981     sa_resource_t resource, char *newname)
3982 {
3983         sa_optionset_t optionset;
3984         int ret = SA_OK;
3985         int err;
3986 
3987         for (optionset = sa_get_optionset(group, NULL);
3988             optionset != NULL;
3989             optionset = sa_get_next_optionset(optionset)) {
3990                 char *type;
3991                 type = sa_get_optionset_attr(optionset, "type");
3992                 if (type != NULL) {
3993                         err = sa_proto_rename_resource(handle, type, resource,
3994                             newname);
3995                         if (err != SA_OK)
3996                                 ret = err;
3997                         sa_free_attr_string(type);
3998                 }
3999         }
4000         return (ret);
4001 }
4002 
4003 /*
4004  * sa_rename_resource(resource, newname)
4005  *
4006  * Rename the resource to the new name, if it is unique.
4007  */
4008 
4009 int
4010 sa_rename_resource(sa_resource_t resource, char *newname)
4011 {
4012         sa_share_t share;
4013         sa_group_t group = NULL;
4014         sa_resource_t target;
4015         int ret = SA_CONFIG_ERR;
4016         sa_handle_t handle = NULL;
4017 
4018         share = sa_get_resource_parent(resource);
4019         if (share == NULL)
4020                 return (ret);
4021 
4022         group = sa_get_parent_group(share);
4023         if (group == NULL)
4024                 return (ret);
4025 
4026         handle = (sa_handle_impl_t)sa_find_group_handle(group);
4027         if (handle == NULL)
4028                 return (ret);
4029 
4030         target = sa_find_resource(handle, newname);
4031         if (target != NULL) {
4032                 ret = SA_DUPLICATE_NAME;
4033         } else {
4034                 /*
4035                  * Everything appears to be valid at this
4036                  * point. Change the name of the active share and then
4037                  * update the share in the appropriate repository.
4038                  */
4039                 ret = proto_rename_resource(handle, group, resource, newname);
4040                 set_node_attr(resource, "name", newname);
4041 
4042                 if (!sa_is_persistent((sa_group_t)share))
4043                         return (ret);
4044 
4045                 if (!sa_group_is_zfs(group)) {
4046                         sa_handle_impl_t ihandle = (sa_handle_impl_t)handle;
4047                         ret = sa_commit_share(ihandle->scfhandle, group,
4048                             share);
4049                 } else {
4050                         ret = sa_zfs_update((sa_share_t)group);
4051                 }
4052         }
4053         return (ret);
4054 }
4055 
4056 /*
4057  * sa_get_resource_attr(resource, tag)
4058  *
4059  * Get the named attribute of the resource. "name" and "id" are
4060  * currently defined.  NULL if tag not defined.
4061  */
4062 
4063 char *
4064 sa_get_resource_attr(sa_resource_t resource, char *tag)
4065 {
4066         return (get_node_attr((void *)resource, tag));
4067 }
4068 
4069 /*
4070  * sa_set_resource_attr(resource, tag, value)
4071  *
4072  * Get the named attribute of the resource. "name" and "id" are
4073  * currently defined.  NULL if tag not defined. Currently we don't do
4074  * much, but additional checking may be needed in the future.
4075  */
4076 
4077 int
4078 sa_set_resource_attr(sa_resource_t resource, char *tag, char *value)
4079 {
4080         set_node_attr((void *)resource, tag, value);
4081         return (SA_OK);
4082 }
4083 
4084 /*
4085  * sa_get_resource_parent(resource_t)
4086  *
4087  * Returns the share associated with the resource.
4088  */
4089 
4090 sa_share_t
4091 sa_get_resource_parent(sa_resource_t resource)
4092 {
4093         sa_share_t share = NULL;
4094 
4095         if (resource != NULL)
4096                 share = (sa_share_t)((xmlNodePtr)resource)->parent;
4097         return (share);
4098 }
4099 
4100 /*
4101  * find_resource(group, name)
4102  *
4103  * Find the resource within the group.
4104  */
4105 
4106 static sa_resource_t
4107 find_resource(sa_group_t group, char *resname)
4108 {
4109         sa_share_t share;
4110         sa_resource_t resource = NULL;
4111         char *name;
4112 
4113         /* Iterate over all the shares and resources in the group. */
4114         for (share = sa_get_share(group, NULL);
4115             share != NULL && resource == NULL;
4116             share = sa_get_next_share(share)) {
4117                 for (resource = sa_get_share_resource(share, NULL);
4118                     resource != NULL;
4119                     resource = sa_get_next_resource(resource)) {
4120                         name = sa_get_resource_attr(resource, "name");
4121                         if (name != NULL && xmlStrcasecmp((xmlChar*)name,
4122                             (xmlChar*)resname) == 0) {
4123                                 sa_free_attr_string(name);
4124                                 break;
4125                         }
4126                         if (name != NULL) {
4127                                 sa_free_attr_string(name);
4128                         }
4129                 }
4130         }
4131         return (resource);
4132 }
4133 
4134 /*
4135  * sa_find_resource(name)
4136  *
4137  * Find the named resource in the system.
4138  */
4139 
4140 sa_resource_t
4141 sa_find_resource(sa_handle_t handle, char *name)
4142 {
4143         sa_group_t group;
4144         sa_group_t zgroup;
4145         sa_resource_t resource = NULL;
4146 
4147         /*
4148          * Iterate over all groups and zfs subgroups and check for
4149          * resource name in them.
4150          */
4151         for (group = sa_get_group(handle, NULL); group != NULL;
4152             group = sa_get_next_group(group)) {
4153 
4154                 if (is_zfs_group(group)) {
4155                         for (zgroup =
4156                             (sa_group_t)_sa_get_child_node((xmlNodePtr)group,
4157                             (xmlChar *)"group");
4158                             zgroup != NULL && resource == NULL;
4159                             zgroup = sa_get_next_group(zgroup)) {
4160                                 resource = find_resource(zgroup, name);
4161                         }
4162                 } else {
4163                         resource = find_resource(group, name);
4164                 }
4165                 if (resource != NULL)
4166                         break;
4167         }
4168         return (resource);
4169 }
4170 
4171 /*
4172  * sa_get_resource(group, resource)
4173  *
4174  * Search all the shares in the specified group for a share with a
4175  * resource name matching the one specified.
4176  *
4177  * In the future, it may be advantageous to allow group to be NULL and
4178  * search all groups but that isn't needed at present.
4179  */
4180 
4181 sa_resource_t
4182 sa_get_resource(sa_group_t group, char *resource)
4183 {
4184         sa_share_t share = NULL;
4185         sa_resource_t res = NULL;
4186 
4187         if (resource != NULL) {
4188                 for (share = sa_get_share(group, NULL);
4189                     share != NULL && res == NULL;
4190                     share = sa_get_next_share(share)) {
4191                         res = sa_get_share_resource(share, resource);
4192                 }
4193         }
4194         return (res);
4195 }
4196 
4197 /*
4198  * get_protocol_list(optionset, object)
4199  *
4200  * Get the protocol optionset list for the object and add them as
4201  * properties to optionset.
4202  */
4203 static int
4204 get_protocol_list(sa_optionset_t optionset, void *object)
4205 {
4206         sa_property_t prop;
4207         sa_optionset_t opts;
4208         int ret = SA_OK;
4209 
4210         for (opts = sa_get_optionset(object, NULL);
4211             opts != NULL;
4212             opts = sa_get_next_optionset(opts)) {
4213                 char *type;
4214                 type = sa_get_optionset_attr(opts, "type");
4215                 /*
4216                  * It is possible to have a non-protocol optionset. We
4217                  * skip any of those found.
4218                  */
4219                 if (type == NULL)
4220                         continue;
4221                 prop = sa_create_property(type, "true");
4222                 sa_free_attr_string(type);
4223                 if (prop != NULL)
4224                         prop = (sa_property_t)xmlAddChild((xmlNodePtr)optionset,
4225                             (xmlNodePtr)prop);
4226                 /* If prop is NULL, don't bother continuing */
4227                 if (prop == NULL) {
4228                         ret = SA_NO_MEMORY;
4229                         break;
4230                 }
4231         }
4232         return (ret);
4233 }
4234 
4235 /*
4236  * sa_free_protoset(optionset)
4237  *
4238  * Free the protocol property optionset.
4239  */
4240 static void
4241 sa_free_protoset(sa_optionset_t optionset)
4242 {
4243         if (optionset != NULL) {
4244                 xmlUnlinkNode((xmlNodePtr) optionset);
4245                 xmlFreeNode((xmlNodePtr) optionset);
4246         }
4247 }
4248 
4249 /*
4250  * sa_optionset_t sa_get_active_protocols(object)
4251  *
4252  * Return a list of the protocols that are active for the object.
4253  * This is currently an internal helper function, but could be
4254  * made visible if there is enough demand for it.
4255  *
4256  * The function finds the parent group and extracts the protocol
4257  * optionsets creating a new optionset with the protocols as properties.
4258  *
4259  * The caller must free the returned optionset.
4260  */
4261 
4262 static sa_optionset_t
4263 sa_get_active_protocols(void *object)
4264 {
4265         sa_optionset_t options;
4266         sa_share_t share = NULL;
4267         sa_group_t group = NULL;
4268         sa_resource_t resource = NULL;
4269         int ret = SA_OK;
4270 
4271         if (object == NULL)
4272                 return (NULL);
4273         options = (sa_optionset_t)xmlNewNode(NULL, (xmlChar *)"optionset");
4274         if (options == NULL)
4275                 return (NULL);
4276 
4277         /*
4278          * Find the objects up the tree that might have protocols
4279          * enabled on them.
4280          */
4281         if (sa_is_resource(object)) {
4282                 resource = (sa_resource_t)object;
4283                 share = sa_get_resource_parent(resource);
4284                 group = sa_get_parent_group(share);
4285         } else if (sa_is_share(object)) {
4286                 share = (sa_share_t)object;
4287                 group = sa_get_parent_group(share);
4288         } else {
4289                 group = (sa_group_t)group;
4290         }
4291         if (resource != NULL)
4292                 ret = get_protocol_list(options, resource);
4293         if (ret == SA_OK && share != NULL)
4294                 ret = get_protocol_list(options, share);
4295         if (ret == SA_OK && group != NULL)
4296                 ret = get_protocol_list(options, group);
4297 
4298         /*
4299          * If there was an error, we won't have a complete list so
4300          * abandon everything.  The caller will have to deal with the
4301          * issue.
4302          */
4303         if (ret != SA_OK) {
4304                 sa_free_protoset(options);
4305                 options = NULL;
4306         }
4307         return (options);
4308 }
4309 
4310 /*
4311  * sa_enable_resource, protocol)
4312  *      Disable the specified share to the specified protocol.
4313  *      If protocol is NULL, then all protocols.
4314  */
4315 int
4316 sa_enable_resource(sa_resource_t resource, char *protocol)
4317 {
4318         int ret = SA_OK;
4319 
4320         if (protocol != NULL) {
4321                 ret = sa_proto_share_resource(protocol, resource);
4322         } else {
4323                 sa_optionset_t protoset;
4324                 sa_property_t prop;
4325                 char *proto;
4326                 int err;
4327 
4328                 /* need to do all protocols */
4329                 protoset = sa_get_active_protocols(resource);
4330                 if (protoset == NULL)
4331                         return (SA_NO_MEMORY);
4332                 for (prop = sa_get_property(protoset, NULL);
4333                     prop != NULL;
4334                     prop = sa_get_next_property(prop)) {
4335                         proto = sa_get_property_attr(prop, "type");
4336                         if (proto == NULL) {
4337                                 ret = SA_NO_MEMORY;
4338                                 continue;
4339                         }
4340                         err = sa_proto_share_resource(proto, resource);
4341                         if (err != SA_OK)
4342                                 ret = err;
4343                         sa_free_attr_string(proto);
4344                 }
4345                 sa_free_protoset(protoset);
4346         }
4347         if (ret == SA_OK)
4348                 (void) sa_set_resource_attr(resource, "shared", NULL);
4349 
4350         return (ret);
4351 }
4352 
4353 /*
4354  * sa_disable_resource(resource, protocol)
4355  *
4356  *      Disable the specified share for the specified protocol.  If
4357  *      protocol is NULL, then all protocols.  If the underlying
4358  *      protocol doesn't implement disable at the resource level, we
4359  *      disable at the share level.
4360  */
4361 int
4362 sa_disable_resource(sa_resource_t resource, char *protocol)
4363 {
4364         int ret = SA_OK;
4365 
4366         if (protocol != NULL) {
4367                 ret = sa_proto_unshare_resource(protocol, resource);
4368                 if (ret == SA_NOT_IMPLEMENTED) {
4369                         sa_share_t parent;
4370                         /*
4371                          * The protocol doesn't implement unshare
4372                          * resource. That implies that resource names are
4373                          * simple aliases for this protocol so we need to
4374                          * unshare the share.
4375                          */
4376                         parent = sa_get_resource_parent(resource);
4377                         if (parent != NULL)
4378                                 ret = sa_disable_share(parent, protocol);
4379                         else
4380                                 ret = SA_CONFIG_ERR;
4381                 }
4382         } else {
4383                 sa_optionset_t protoset;
4384                 sa_property_t prop;
4385                 char *proto;
4386                 int err;
4387 
4388                 /* need to do all protocols */
4389                 protoset = sa_get_active_protocols(resource);
4390                 if (protoset == NULL)
4391                         return (SA_NO_MEMORY);
4392                 for (prop = sa_get_property(protoset, NULL);
4393                     prop != NULL;
4394                     prop = sa_get_next_property(prop)) {
4395                         proto = sa_get_property_attr(prop, "type");
4396                         if (proto == NULL) {
4397                                 ret = SA_NO_MEMORY;
4398                                 continue;
4399                         }
4400                         err = sa_proto_unshare_resource(proto, resource);
4401                         if (err == SA_NOT_SUPPORTED) {
4402                                 sa_share_t parent;
4403                                 parent = sa_get_resource_parent(resource);
4404                                 if (parent != NULL)
4405                                         err = sa_disable_share(parent, proto);
4406                                 else
4407                                         err = SA_CONFIG_ERR;
4408                         }
4409                         if (err != SA_OK)
4410                                 ret = err;
4411                         sa_free_attr_string(proto);
4412                 }
4413                 sa_free_protoset(protoset);
4414         }
4415         if (ret == SA_OK)
4416                 (void) sa_set_resource_attr(resource, "shared", NULL);
4417 
4418         return (ret);
4419 }
4420 
4421 /*
4422  * sa_set_resource_description(resource, content)
4423  *
4424  * Set the description of share to content.
4425  */
4426 
4427 int
4428 sa_set_resource_description(sa_resource_t resource, char *content)
4429 {
4430         xmlNodePtr node;
4431         sa_group_t group;
4432         sa_share_t share;
4433         int ret = SA_OK;
4434 
4435         for (node = ((xmlNodePtr)resource)->children;
4436             node != NULL;
4437             node = node->next) {
4438                 if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) {
4439                         break;
4440                 }
4441         }
4442 
4443         /* no existing description but want to add */
4444         if (node == NULL && content != NULL) {
4445                 /* add a description */
4446                 node = _sa_set_share_description(resource, content);
4447         } else if (node != NULL && content != NULL) {
4448                 /* update a description */
4449                 xmlNodeSetContent(node, (xmlChar *)content);
4450         } else if (node != NULL && content == NULL) {
4451                 /* remove an existing description */
4452                 xmlUnlinkNode(node);
4453                 xmlFreeNode(node);
4454         }
4455 
4456         share = sa_get_resource_parent(resource);
4457         group = sa_get_parent_group(share);
4458         if (group != NULL &&
4459             sa_is_persistent(share) && (!sa_group_is_zfs(group))) {
4460                 sa_handle_impl_t impl_handle;
4461                 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
4462                 if (impl_handle != NULL)
4463                         ret = sa_commit_share(impl_handle->scfhandle,
4464                             group, share);
4465                 else
4466                         ret = SA_SYSTEM_ERR;
4467         }
4468         return (ret);
4469 }
4470 
4471 /*
4472  * sa_get_resource_description(share)
4473  *
4474  * Return the description text for the specified share if it
4475  * exists. NULL if no description exists.
4476  */
4477 
4478 char *
4479 sa_get_resource_description(sa_resource_t resource)
4480 {
4481         xmlChar *description = NULL;
4482         xmlNodePtr node;
4483 
4484         for (node = ((xmlNodePtr)resource)->children; node != NULL;
4485             node = node->next) {
4486                 if (xmlStrcmp(node->name, (xmlChar *)"description") == 0)
4487                         break;
4488         }
4489         if (node != NULL) {
4490                 description = xmlNodeGetContent(node);
4491                 fixproblemchars((char *)description);
4492         }
4493         return ((char *)description);
4494 }