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 2014 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_get_protocols(char **protocol)
1157  *      Get array of protocols that are supported
1158  *      Returns pointer to an allocated and NULL terminated
1159  *      array of strings.  Caller must free.
1160  *      This really should be determined dynamically.
1161  *      If there aren't any defined, return -1.
1162  *      Use free() to return memory.
1163  */
1164 
1165 int
1166 sa_get_protocols(char ***protocols)
1167 {
1168         int numproto = -1;
1169 
1170         if (protocols != NULL) {
1171                 struct sa_proto_plugin *plug;
1172                 for (numproto = 0, plug = sap_proto_list; plug != NULL;
1173                     plug = plug->plugin_next) {
1174                         numproto++;
1175                 }
1176 
1177                 *protocols = calloc(numproto + 1,  sizeof (char *));
1178                 if (*protocols != NULL) {
1179                         int ret = 0;
1180                         for (plug = sap_proto_list; plug != NULL;
1181                             plug = plug->plugin_next) {
1182                                 /* faking for now */
1183                                 (*protocols)[ret++] =
1184                                     plug->plugin_ops->sa_protocol;
1185                         }
1186                 } else {
1187                         numproto = -1;
1188                 }
1189         }
1190         return (numproto);
1191 }
1192 
1193 /*
1194  * find_group_by_name(node, group)
1195  *
1196  * search the XML document subtree specified by node to find the group
1197  * specified by group. Searching subtree allows subgroups to be
1198  * searched for.
1199  */
1200 
1201 static xmlNodePtr
1202 find_group_by_name(xmlNodePtr node, xmlChar *group)
1203 {
1204         xmlChar *name = NULL;
1205 
1206         for (node = node->xmlChildrenNode; node != NULL;
1207             node = node->next) {
1208                 if (xmlStrcmp(node->name, (xmlChar *)"group") == 0) {
1209                         /* if no groupname, return the first found */
1210                         if (group == NULL)
1211                                 break;
1212                         name = xmlGetProp(node, (xmlChar *)"name");
1213                         if (name != NULL && xmlStrcmp(name, group) == 0)
1214                                 break;
1215                         if (name != NULL) {
1216                                 xmlFree(name);
1217                                 name = NULL;
1218                         }
1219                 }
1220         }
1221         if (name != NULL)
1222                 xmlFree(name);
1223         return (node);
1224 }
1225 
1226 /*
1227  * sa_get_group(groupname)
1228  *      Return the "group" specified.  If groupname is NULL,
1229  *      return the first group of the list of groups.
1230  */
1231 sa_group_t
1232 sa_get_group(sa_handle_t handle, char *groupname)
1233 {
1234         xmlNodePtr node = NULL;
1235         char *subgroup = NULL;
1236         char *group = NULL;
1237         sa_handle_impl_t impl_handle = (sa_handle_impl_t)handle;
1238 
1239         if (impl_handle != NULL && impl_handle->tree != NULL) {
1240                 if (groupname != NULL) {
1241                         group = strdup(groupname);
1242                         if (group != NULL) {
1243                                 subgroup = strchr(group, '/');
1244                                 if (subgroup != NULL)
1245                                         *subgroup++ = '\0';
1246                         }
1247                 }
1248                 /*
1249                  * We want to find the, possibly, named group. If
1250                  * group is not NULL, then lookup the name. If it is
1251                  * NULL, we only do the find if groupname is also
1252                  * NULL. This allows lookup of the "first" group in
1253                  * the internal list.
1254                  */
1255                 if (group != NULL || groupname == NULL)
1256                         node = find_group_by_name(impl_handle->tree,
1257                             (xmlChar *)group);
1258 
1259                 /* if a subgroup, find it before returning */
1260                 if (subgroup != NULL && node != NULL)
1261                         node = find_group_by_name(node, (xmlChar *)subgroup);
1262         }
1263         if (node != NULL && (char *)group != NULL)
1264                 (void) sa_get_instance(impl_handle->scfhandle, (char *)group);
1265         if (group != NULL)
1266                 free(group);
1267         return ((sa_group_t)(node));
1268 }
1269 
1270 /*
1271  * sa_get_next_group(group)
1272  *      Return the "next" group after the specified group from
1273  *      the internal group list.  NULL if there are no more.
1274  */
1275 sa_group_t
1276 sa_get_next_group(sa_group_t group)
1277 {
1278         xmlNodePtr ngroup = NULL;
1279         if (group != NULL) {
1280                 for (ngroup = ((xmlNodePtr)group)->next; ngroup != NULL;
1281                     ngroup = ngroup->next) {
1282                         if (xmlStrcmp(ngroup->name, (xmlChar *)"group") == 0)
1283                                 break;
1284                 }
1285         }
1286         return ((sa_group_t)ngroup);
1287 }
1288 
1289 /*
1290  * sa_get_share(group, sharepath)
1291  *      Return the share object for the share specified. The share
1292  *      must be in the specified group.  Return NULL if not found.
1293  */
1294 sa_share_t
1295 sa_get_share(sa_group_t group, char *sharepath)
1296 {
1297         xmlNodePtr node = NULL;
1298         xmlChar *path;
1299 
1300         /*
1301          * For future scalability, this should end up building a cache
1302          * since it will get called regularly by the mountd and info
1303          * services.
1304          */
1305         if (group != NULL) {
1306                 for (node = ((xmlNodePtr)group)->children; node != NULL;
1307                     node = node->next) {
1308                         if (xmlStrcmp(node->name, (xmlChar *)"share") == 0) {
1309                                 if (sharepath == NULL) {
1310                                         break;
1311                                 } else {
1312                                         /* is it the correct share? */
1313                                         path = xmlGetProp(node,
1314                                             (xmlChar *)"path");
1315                                         if (path != NULL &&
1316                                             xmlStrcmp(path,
1317                                             (xmlChar *)sharepath) == 0) {
1318                                                 xmlFree(path);
1319                                                 break;
1320                                         }
1321                                         xmlFree(path);
1322                                 }
1323                         }
1324                 }
1325         }
1326         return ((sa_share_t)node);
1327 }
1328 
1329 /*
1330  * sa_get_next_share(share)
1331  *      Return the next share following the specified share
1332  *      from the internal list of shares. Returns NULL if there
1333  *      are no more shares.  The list is relative to the same
1334  *      group.
1335  */
1336 sa_share_t
1337 sa_get_next_share(sa_share_t share)
1338 {
1339         xmlNodePtr node = NULL;
1340 
1341         if (share != NULL) {
1342                 for (node = ((xmlNodePtr)share)->next; node != NULL;
1343                     node = node->next) {
1344                         if (xmlStrcmp(node->name, (xmlChar *)"share") == 0) {
1345                                 break;
1346                         }
1347                 }
1348         }
1349         return ((sa_share_t)node);
1350 }
1351 
1352 /*
1353  * _sa_get_child_node(node, type)
1354  *
1355  * find the child node of the specified node that has "type". This is
1356  * used to implement several internal functions.
1357  */
1358 
1359 static xmlNodePtr
1360 _sa_get_child_node(xmlNodePtr node, xmlChar *type)
1361 {
1362         xmlNodePtr child;
1363         for (child = node->xmlChildrenNode; child != NULL;
1364             child = child->next)
1365                 if (xmlStrcmp(child->name, type) == 0)
1366                         return (child);
1367         return ((xmlNodePtr)NULL);
1368 }
1369 
1370 /*
1371  *  find_share(group, path)
1372  *
1373  * Search all the shares in the specified group for one that has the
1374  * specified path.
1375  */
1376 
1377 static sa_share_t
1378 find_share(sa_group_t group, char *sharepath)
1379 {
1380         sa_share_t share;
1381         char *path;
1382 
1383         for (share = sa_get_share(group, NULL); share != NULL;
1384             share = sa_get_next_share(share)) {
1385                 path = sa_get_share_attr(share, "path");
1386                 if (path != NULL && strcmp(path, sharepath) == 0) {
1387                         sa_free_attr_string(path);
1388                         break;
1389                 }
1390                 if (path != NULL)
1391                         sa_free_attr_string(path);
1392         }
1393         return (share);
1394 }
1395 
1396 /*
1397  * sa_get_sub_group(group)
1398  *
1399  * Get the first sub-group of group. The sa_get_next_group() function
1400  * can be used to get the rest. This is currently only used for ZFS
1401  * sub-groups but could be used to implement a more general mechanism.
1402  */
1403 
1404 sa_group_t
1405 sa_get_sub_group(sa_group_t group)
1406 {
1407         return ((sa_group_t)_sa_get_child_node((xmlNodePtr)group,
1408             (xmlChar *)"group"));
1409 }
1410 
1411 /*
1412  * sa_find_share(sharepath)
1413  *      Finds a share regardless of group.  In the future, this
1414  *      function should utilize a cache and hash table of some kind.
1415  *      The current assumption is that a path will only be shared
1416  *      once.  In the future, this may change as implementation of
1417  *      resource names comes into being.
1418  */
1419 sa_share_t
1420 sa_find_share(sa_handle_t handle, char *sharepath)
1421 {
1422         sa_group_t group;
1423         sa_group_t zgroup;
1424         sa_share_t share = NULL;
1425         int done = 0;
1426 
1427         for (group = sa_get_group(handle, NULL); group != NULL && !done;
1428             group = sa_get_next_group(group)) {
1429                 if (is_zfs_group(group)) {
1430                         for (zgroup =
1431                             (sa_group_t)_sa_get_child_node((xmlNodePtr)group,
1432                             (xmlChar *)"group");
1433                             zgroup != NULL;
1434                             zgroup = sa_get_next_group(zgroup)) {
1435                                 share = find_share(zgroup, sharepath);
1436                                 if (share != NULL)
1437                                         break;
1438                         }
1439                 } else {
1440                         share = find_share(group, sharepath);
1441                 }
1442                 if (share != NULL)
1443                         break;
1444         }
1445         return (share);
1446 }
1447 
1448 /*
1449  *  sa_check_path(group, path, strictness)
1450  *
1451  * Check that path is a valid path relative to the group.  Currently,
1452  * we are ignoring the group and checking only the NFS rules. Later,
1453  * we may want to use the group to then check against the protocols
1454  * enabled on the group. The strictness values mean:
1455  * SA_CHECK_NORMAL == only check newpath against shares that are active
1456  * SA_CHECK_STRICT == check newpath against both active shares and those
1457  *                    stored in the repository
1458  */
1459 
1460 int
1461 sa_check_path(sa_group_t group, char *path, int strictness)
1462 {
1463         sa_handle_t handle;
1464 
1465         handle = sa_find_group_handle(group);
1466         if (handle == NULL)
1467                 return (SA_BAD_PATH);
1468 
1469         return (validpath(handle, path, strictness));
1470 }
1471 
1472 /*
1473  * mark_excluded_protos(group, share, flags)
1474  *
1475  * Walk through all the protocols enabled for the group and check to
1476  * see if the share has any of them should be in the exclude list
1477  * based on the featureset of the protocol. If there are any, add the
1478  * "exclude" property to the share.
1479  */
1480 static void
1481 mark_excluded_protos(sa_group_t group, xmlNodePtr share, uint64_t flags)
1482 {
1483         sa_optionset_t optionset;
1484         char exclude_list[SA_STRSIZE];
1485         char *sep = "";
1486 
1487         exclude_list[0] = '\0';
1488         for (optionset = sa_get_optionset(group, NULL);
1489             optionset != NULL;
1490             optionset = sa_get_next_optionset(optionset)) {
1491                 char *value;
1492                 uint64_t features;
1493                 value = sa_get_optionset_attr(optionset, "type");
1494                 if (value == NULL)
1495                         continue;
1496                 features = sa_proto_get_featureset(value);
1497                 if (!(features & flags)) {
1498                         (void) strlcat(exclude_list, sep,
1499                             sizeof (exclude_list));
1500                         (void) strlcat(exclude_list, value,
1501                             sizeof (exclude_list));
1502                         sep = ",";
1503                 }
1504                 sa_free_attr_string(value);
1505         }
1506         if (exclude_list[0] != '\0')
1507                 (void) xmlSetProp(share, (xmlChar *)"exclude",
1508                     (xmlChar *)exclude_list);
1509 }
1510 
1511 /*
1512  * get_all_features(group)
1513  *
1514  * Walk through all the protocols on the group and collect all
1515  * possible enabled features. This is the OR of all the featuresets.
1516  */
1517 static uint64_t
1518 get_all_features(sa_group_t group)
1519 {
1520         sa_optionset_t optionset;
1521         uint64_t features = 0;
1522 
1523         for (optionset = sa_get_optionset(group, NULL);
1524             optionset != NULL;
1525             optionset = sa_get_next_optionset(optionset)) {
1526                 char *value;
1527                 value = sa_get_optionset_attr(optionset, "type");
1528                 if (value == NULL)
1529                         continue;
1530                 features |= sa_proto_get_featureset(value);
1531                 sa_free_attr_string(value);
1532         }
1533         return (features);
1534 }
1535 
1536 
1537 /*
1538  * _sa_add_share(group, sharepath, persist, *error, flags)
1539  *
1540  * Common code for all types of add_share. sa_add_share() is the
1541  * public API, we also need to be able to do this when parsing legacy
1542  * files and construction of the internal configuration while
1543  * extracting config info from SMF. "flags" indicates if some
1544  * protocols need relaxed rules while other don't. These values are
1545  * the featureset values defined in libshare.h.
1546  */
1547 
1548 sa_share_t
1549 _sa_add_share(sa_group_t group, char *sharepath, int persist, int *error,
1550     uint64_t flags)
1551 {
1552         xmlNodePtr node = NULL;
1553         int err;
1554 
1555         err  = SA_OK; /* assume success */
1556 
1557         node = xmlNewChild((xmlNodePtr)group, NULL, (xmlChar *)"share", NULL);
1558         if (node == NULL) {
1559                 if (error != NULL)
1560                         *error = SA_NO_MEMORY;
1561                 return (node);
1562         }
1563 
1564         (void) xmlSetProp(node, (xmlChar *)"path", (xmlChar *)sharepath);
1565         (void) xmlSetProp(node, (xmlChar *)"type",
1566             persist ? (xmlChar *)"persist" : (xmlChar *)"transient");
1567         if (flags != 0)
1568                 mark_excluded_protos(group, node, flags);
1569         if (persist != SA_SHARE_TRANSIENT) {
1570                 /*
1571                  * persistent shares come in two flavors: SMF and
1572                  * ZFS. Sort this one out based on target group and
1573                  * path type. Both NFS and SMB are supported. First,
1574                  * check to see if the protocol is enabled on the
1575                  * subgroup and then setup the share appropriately.
1576                  */
1577                 if (sa_group_is_zfs(group) &&
1578                     sa_path_is_zfs(sharepath)) {
1579                         if (sa_get_optionset(group, "nfs") != NULL)
1580                                 err = sa_zfs_set_sharenfs(group, sharepath, 1);
1581                         else if (sa_get_optionset(group, "smb") != NULL)
1582                                 err = sa_zfs_set_sharesmb(group, sharepath, 1);
1583                 } else {
1584                         sa_handle_impl_t impl_handle;
1585                         impl_handle =
1586                             (sa_handle_impl_t)sa_find_group_handle(group);
1587                         if (impl_handle != NULL) {
1588                                 err = sa_commit_share(impl_handle->scfhandle,
1589                                     group, (sa_share_t)node);
1590                         } else {
1591                                 err = SA_SYSTEM_ERR;
1592                         }
1593                 }
1594         }
1595         if (err == SA_NO_PERMISSION && persist & SA_SHARE_PARSER)
1596                 /* called by the dfstab parser so could be a show */
1597                 err = SA_OK;
1598 
1599         if (err != SA_OK) {
1600                 /*
1601                  * we couldn't commit to the repository so undo
1602                  * our internal state to reflect reality.
1603                  */
1604                 xmlUnlinkNode(node);
1605                 xmlFreeNode(node);
1606                 node = NULL;
1607         }
1608 
1609         if (error != NULL)
1610                 *error = err;
1611 
1612         return (node);
1613 }
1614 
1615 /*
1616  * sa_add_share(group, sharepath, persist, *error)
1617  *
1618  *      Add a new share object to the specified group.  The share will
1619  *      have the specified sharepath and will only be constructed if
1620  *      it is a valid path to be shared.  NULL is returned on error
1621  *      and a detailed error value will be returned via the error
1622  *      pointer.
1623  */
1624 sa_share_t
1625 sa_add_share(sa_group_t group, char *sharepath, int persist, int *error)
1626 {
1627         xmlNodePtr node = NULL;
1628         int strictness = SA_CHECK_NORMAL;
1629         sa_handle_t handle;
1630         uint64_t special = 0;
1631         uint64_t features;
1632 
1633         /*
1634          * If the share is to be permanent, use strict checking so a
1635          * bad config doesn't get created. Transient shares only need
1636          * to check against the currently active
1637          * shares. SA_SHARE_PARSER is a modifier used internally to
1638          * indicate that we are being called by the dfstab parser and
1639          * that we need strict checking in all cases. Normally persist
1640          * is in integer value but SA_SHARE_PARSER may be or'd into
1641          * it as an override.
1642          */
1643         if (persist & SA_SHARE_PARSER || persist == SA_SHARE_PERMANENT)
1644                 strictness = SA_CHECK_STRICT;
1645 
1646         handle = sa_find_group_handle(group);
1647 
1648         /*
1649          * need to determine if the share is valid. The rules are:
1650          *      - The path must not already exist
1651          *      - The path must not be a subdir or parent dir of an
1652          *        existing path unless at least one protocol allows it.
1653          * The sub/parent check is done in sa_check_path().
1654          */
1655 
1656         if (sa_find_share(handle, sharepath) == NULL) {
1657                 *error = sa_check_path(group, sharepath, strictness);
1658                 features = get_all_features(group);
1659                 switch (*error) {
1660                 case SA_PATH_IS_SUBDIR:
1661                         if (features & SA_FEATURE_ALLOWSUBDIRS)
1662                                 special |= SA_FEATURE_ALLOWSUBDIRS;
1663                         break;
1664                 case SA_PATH_IS_PARENTDIR:
1665                         if (features & SA_FEATURE_ALLOWPARDIRS)
1666                                 special |= SA_FEATURE_ALLOWPARDIRS;
1667                         break;
1668                 }
1669                 if (*error == SA_OK || special != SA_FEATURE_NONE)
1670                         node = _sa_add_share(group, sharepath, persist,
1671                             error, special);
1672         } else {
1673                 *error = SA_DUPLICATE_NAME;
1674         }
1675 
1676         return ((sa_share_t)node);
1677 }
1678 
1679 /*
1680  * sa_enable_share(share, protocol)
1681  *      Enable the specified share to the specified protocol.
1682  *      If protocol is NULL, then all protocols.
1683  */
1684 int
1685 sa_enable_share(sa_share_t share, char *protocol)
1686 {
1687         char *sharepath;
1688         struct stat st;
1689         int err = SA_OK;
1690         int ret;
1691 
1692         sharepath = sa_get_share_attr(share, "path");
1693         if (sharepath == NULL)
1694                 return (SA_NO_MEMORY);
1695         if (stat(sharepath, &st) < 0) {
1696                 err = SA_NO_SUCH_PATH;
1697         } else {
1698                 /* tell the server about the share */
1699                 if (protocol != NULL) {
1700                         if (excluded_protocol(share, protocol))
1701                                 goto done;
1702 
1703                         /* lookup protocol specific handler */
1704                         err = sa_proto_share(protocol, share);
1705                         if (err == SA_OK)
1706                                 (void) sa_set_share_attr(share,
1707                                     "shared", "true");
1708                 } else {
1709                         /* Tell all protocols about the share */
1710                         sa_group_t group;
1711                         sa_optionset_t optionset;
1712 
1713                         group = sa_get_parent_group(share);
1714 
1715                         for (optionset = sa_get_optionset(group, NULL);
1716                             optionset != NULL;
1717                             optionset = sa_get_next_optionset(optionset)) {
1718                                 char *proto;
1719                                 proto = sa_get_optionset_attr(optionset,
1720                                     "type");
1721                                 if (proto != NULL) {
1722                                         if (!excluded_protocol(share, proto)) {
1723                                                 ret = sa_proto_share(proto,
1724                                                     share);
1725                                                 if (ret != SA_OK)
1726                                                         err = ret;
1727                                         }
1728                                         sa_free_attr_string(proto);
1729                                 }
1730                         }
1731                         (void) sa_set_share_attr(share, "shared", "true");
1732                 }
1733         }
1734 done:
1735         if (sharepath != NULL)
1736                 sa_free_attr_string(sharepath);
1737         return (err);
1738 }
1739 
1740 /*
1741  * sa_disable_share(share, protocol)
1742  *      Disable the specified share to the specified protocol.  If
1743  *      protocol is NULL, then all protocols that are enabled for the
1744  *      share should be disabled.
1745  */
1746 int
1747 sa_disable_share(sa_share_t share, char *protocol)
1748 {
1749         char *path;
1750         int err = SA_OK;
1751         int ret = SA_OK;
1752 
1753         path = sa_get_share_attr(share, "path");
1754 
1755         if (protocol != NULL) {
1756                 ret = sa_proto_unshare(share, protocol, path);
1757         } else {
1758                 /* need to do all protocols */
1759                 sa_group_t group;
1760                 sa_optionset_t optionset;
1761 
1762                 group = sa_get_parent_group(share);
1763 
1764                 /* Tell all protocols about the share */
1765                 for (optionset = sa_get_optionset(group, NULL);
1766                     optionset != NULL;
1767                     optionset = sa_get_next_optionset(optionset)) {
1768                         char *proto;
1769 
1770                         proto = sa_get_optionset_attr(optionset, "type");
1771                         if (proto != NULL) {
1772                                 err = sa_proto_unshare(share, proto, path);
1773                                 if (err != SA_OK)
1774                                         ret = err;
1775                                 sa_free_attr_string(proto);
1776                         }
1777                 }
1778         }
1779         if (ret == SA_OK)
1780                 (void) sa_set_share_attr(share, "shared", NULL);
1781         if (path != NULL)
1782                 sa_free_attr_string(path);
1783         return (ret);
1784 }
1785 
1786 /*
1787  * sa_remove_share(share)
1788  *
1789  * remove the specified share from its containing group.
1790  * Remove from the SMF or ZFS configuration space.
1791  */
1792 
1793 int
1794 sa_remove_share(sa_share_t share)
1795 {
1796         sa_group_t group;
1797         int ret = SA_OK;
1798         char *type;
1799         int transient = 0;
1800         char *groupname;
1801         char *zfs;
1802 
1803         type = sa_get_share_attr(share, "type");
1804         group = sa_get_parent_group(share);
1805         zfs = sa_get_group_attr(group, "zfs");
1806         groupname = sa_get_group_attr(group, "name");
1807         if (type != NULL && strcmp(type, "persist") != 0)
1808                 transient = 1;
1809         if (type != NULL)
1810                 sa_free_attr_string(type);
1811 
1812         /* remove the node from its group then free the memory */
1813 
1814         /*
1815          * need to test if "busy"
1816          */
1817         /* only do SMF action if permanent */
1818         if (!transient || zfs != NULL) {
1819                 /* remove from legacy dfstab as well as possible SMF */
1820                 ret = sa_delete_legacy(share, NULL);
1821                 if (ret == SA_OK) {
1822                         if (!sa_group_is_zfs(group)) {
1823                                 sa_handle_impl_t impl_handle;
1824                                 impl_handle = (sa_handle_impl_t)
1825                                     sa_find_group_handle(group);
1826                                 if (impl_handle != NULL) {
1827                                         ret = sa_delete_share(
1828                                             impl_handle->scfhandle, group,
1829                                             share);
1830                                 } else {
1831                                         ret = SA_SYSTEM_ERR;
1832                                 }
1833                         } else {
1834                                 char *sharepath = sa_get_share_attr(share,
1835                                     "path");
1836                                 if (sharepath != NULL) {
1837                                         ret = sa_zfs_set_sharenfs(group,
1838                                             sharepath, 0);
1839                                         sa_free_attr_string(sharepath);
1840                                 }
1841                         }
1842                 }
1843         }
1844         if (groupname != NULL)
1845                 sa_free_attr_string(groupname);
1846         if (zfs != NULL)
1847                 sa_free_attr_string(zfs);
1848 
1849         xmlUnlinkNode((xmlNodePtr)share);
1850         xmlFreeNode((xmlNodePtr)share);
1851         return (ret);
1852 }
1853 
1854 /*
1855  * sa_move_share(group, share)
1856  *
1857  * move the specified share to the specified group.  Update SMF
1858  * appropriately.
1859  */
1860 
1861 int
1862 sa_move_share(sa_group_t group, sa_share_t share)
1863 {
1864         sa_group_t oldgroup;
1865         int ret = SA_OK;
1866 
1867         /* remove the node from its group then free the memory */
1868 
1869         oldgroup = sa_get_parent_group(share);
1870         if (oldgroup != group) {
1871                 sa_handle_impl_t impl_handle;
1872                 xmlUnlinkNode((xmlNodePtr)share);
1873                 /*
1874                  * now that the share isn't in its old group, add to
1875                  * the new one
1876                  */
1877                 (void) xmlAddChild((xmlNodePtr)group, (xmlNodePtr)share);
1878                 /* need to deal with SMF */
1879                 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
1880                 if (impl_handle != NULL) {
1881                         /*
1882                          * need to remove from old group first and then add to
1883                          * new group. Ideally, we would do the other order but
1884                          * need to avoid having the share in two groups at the
1885                          * same time.
1886                          */
1887                         ret = sa_delete_share(impl_handle->scfhandle, oldgroup,
1888                             share);
1889                         if (ret == SA_OK)
1890                                 ret = sa_commit_share(impl_handle->scfhandle,
1891                                     group, share);
1892                 } else {
1893                         ret = SA_SYSTEM_ERR;
1894                 }
1895         }
1896         return (ret);
1897 }
1898 
1899 /*
1900  * sa_get_parent_group(share)
1901  *
1902  * Return the containing group for the share. If a group was actually
1903  * passed in, we don't want a parent so return NULL.
1904  */
1905 
1906 sa_group_t
1907 sa_get_parent_group(sa_share_t share)
1908 {
1909         xmlNodePtr node = NULL;
1910         if (share != NULL) {
1911                 node = ((xmlNodePtr)share)->parent;
1912                 /*
1913                  * make sure parent is a group and not sharecfg since
1914                  * we may be cheating and passing in a group.
1915                  * Eventually, groups of groups might come into being.
1916                  */
1917                 if (node == NULL ||
1918                     xmlStrcmp(node->name, (xmlChar *)"sharecfg") == 0)
1919                         node = NULL;
1920         }
1921         return ((sa_group_t)node);
1922 }
1923 
1924 /*
1925  * _sa_create_group(impl_handle, groupname)
1926  *
1927  * Create a group in the document. The caller will need to deal with
1928  * configuration store and activation.
1929  */
1930 
1931 sa_group_t
1932 _sa_create_group(sa_handle_impl_t impl_handle, char *groupname)
1933 {
1934         xmlNodePtr node = NULL;
1935 
1936         if (sa_valid_group_name(groupname)) {
1937                 node = xmlNewChild(impl_handle->tree, NULL, (xmlChar *)"group",
1938                     NULL);
1939                 if (node != NULL) {
1940                         (void) xmlSetProp(node, (xmlChar *)"name",
1941                             (xmlChar *)groupname);
1942                         (void) xmlSetProp(node, (xmlChar *)"state",
1943                             (xmlChar *)"enabled");
1944                 }
1945         }
1946         return ((sa_group_t)node);
1947 }
1948 
1949 /*
1950  * _sa_create_zfs_group(group, groupname)
1951  *
1952  * Create a ZFS subgroup under the specified group. This may
1953  * eventually form the basis of general sub-groups, but is currently
1954  * restricted to ZFS.
1955  */
1956 sa_group_t
1957 _sa_create_zfs_group(sa_group_t group, char *groupname)
1958 {
1959         xmlNodePtr node = NULL;
1960 
1961         node = xmlNewChild((xmlNodePtr)group, NULL, (xmlChar *)"group", NULL);
1962         if (node != NULL) {
1963                 (void) xmlSetProp(node, (xmlChar *)"name",
1964                     (xmlChar *)groupname);
1965                 (void) xmlSetProp(node, (xmlChar *)"state",
1966                     (xmlChar *)"enabled");
1967         }
1968 
1969         return ((sa_group_t)node);
1970 }
1971 
1972 /*
1973  * sa_create_group(groupname, *error)
1974  *
1975  * Create a new group with groupname.  Need to validate that it is a
1976  * legal name for SMF and the construct the SMF service instance of
1977  * svc:/network/shares/group to implement the group. All necessary
1978  * operational properties must be added to the group at this point
1979  * (via the SMF transaction model).
1980  */
1981 sa_group_t
1982 sa_create_group(sa_handle_t handle, char *groupname, int *error)
1983 {
1984         xmlNodePtr node = NULL;
1985         sa_group_t group;
1986         int ret;
1987         char rbacstr[SA_STRSIZE];
1988         sa_handle_impl_t impl_handle = (sa_handle_impl_t)handle;
1989 
1990         ret = SA_OK;
1991 
1992         if (impl_handle == NULL || impl_handle->scfhandle == NULL) {
1993                 ret = SA_SYSTEM_ERR;
1994                 goto err;
1995         }
1996 
1997         group = sa_get_group(handle, groupname);
1998         if (group != NULL) {
1999                 ret = SA_DUPLICATE_NAME;
2000         } else {
2001                 if (sa_valid_group_name(groupname)) {
2002                         node = xmlNewChild(impl_handle->tree, NULL,
2003                             (xmlChar *)"group", NULL);
2004                         if (node != NULL) {
2005                                 (void) xmlSetProp(node, (xmlChar *)"name",
2006                                     (xmlChar *)groupname);
2007                                 /* default to the group being enabled */
2008                                 (void) xmlSetProp(node, (xmlChar *)"state",
2009                                     (xmlChar *)"enabled");
2010                                 ret = sa_create_instance(impl_handle->scfhandle,
2011                                     groupname);
2012                                 if (ret == SA_OK) {
2013                                         ret = sa_start_transaction(
2014                                             impl_handle->scfhandle,
2015                                             "operation");
2016                                 }
2017                                 if (ret == SA_OK) {
2018                                         ret = sa_set_property(
2019                                             impl_handle->scfhandle,
2020                                             "state", "enabled");
2021                                         if (ret == SA_OK) {
2022                                                 ret = sa_end_transaction(
2023                                                     impl_handle->scfhandle,
2024                                                     impl_handle);
2025                                         } else {
2026                                                 sa_abort_transaction(
2027                                                     impl_handle->scfhandle);
2028                                         }
2029                                 }
2030                                 if (ret == SA_OK) {
2031                                         /* initialize the RBAC strings */
2032                                         ret = sa_start_transaction(
2033                                             impl_handle->scfhandle,
2034                                             "general");
2035                                         if (ret == SA_OK) {
2036                                                 (void) snprintf(rbacstr,
2037                                                     sizeof (rbacstr), "%s.%s",
2038                                                     SA_RBAC_MANAGE, groupname);
2039                                                 ret = sa_set_property(
2040                                                     impl_handle->scfhandle,
2041                                                     "action_authorization",
2042                                                     rbacstr);
2043                                         }
2044                                         if (ret == SA_OK) {
2045                                                 (void) snprintf(rbacstr,
2046                                                     sizeof (rbacstr), "%s.%s",
2047                                                     SA_RBAC_VALUE, groupname);
2048                                                 ret = sa_set_property(
2049                                                     impl_handle->scfhandle,
2050                                                     "value_authorization",
2051                                                     rbacstr);
2052                                         }
2053                                         if (ret == SA_OK) {
2054                                                 ret = sa_end_transaction(
2055                                                     impl_handle->scfhandle,
2056                                                     impl_handle);
2057                                         } else {
2058                                                 sa_abort_transaction(
2059                                                     impl_handle->scfhandle);
2060                                         }
2061                                 }
2062                                 if (ret != SA_OK) {
2063                                         /*
2064                                          * Couldn't commit the group
2065                                          * so we need to undo
2066                                          * internally.
2067                                          */
2068                                         xmlUnlinkNode(node);
2069                                         xmlFreeNode(node);
2070                                         node = NULL;
2071                                 }
2072                         } else {
2073                                 ret = SA_NO_MEMORY;
2074                         }
2075                 } else {
2076                         ret = SA_INVALID_NAME;
2077                 }
2078         }
2079 err:
2080         if (error != NULL)
2081                 *error = ret;
2082         return ((sa_group_t)node);
2083 }
2084 
2085 /*
2086  * sa_remove_group(group)
2087  *
2088  * Remove the specified group. This deletes from the SMF repository.
2089  * All property groups and properties are removed.
2090  */
2091 
2092 int
2093 sa_remove_group(sa_group_t group)
2094 {
2095         char *name;
2096         int ret = SA_OK;
2097         sa_handle_impl_t impl_handle;
2098 
2099         impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
2100         if (impl_handle != NULL) {
2101                 name = sa_get_group_attr(group, "name");
2102                 if (name != NULL) {
2103                         ret = sa_delete_instance(impl_handle->scfhandle, name);
2104                         sa_free_attr_string(name);
2105                 }
2106                 xmlUnlinkNode((xmlNodePtr)group); /* make sure unlinked */
2107                 xmlFreeNode((xmlNodePtr)group);   /* now it is gone */
2108         } else {
2109                 ret = SA_SYSTEM_ERR;
2110         }
2111         return (ret);
2112 }
2113 
2114 /*
2115  * sa_update_config()
2116  *
2117  * Used to update legacy files that need to be updated in bulk
2118  * Currently, this is a placeholder and will go away in a future
2119  * release.
2120  */
2121 
2122 int
2123 sa_update_config(sa_handle_t handle)
2124 {
2125         /*
2126          * do legacy files first so we can tell when they change.
2127          * This will go away when we start updating individual records
2128          * rather than the whole file.
2129          */
2130         update_legacy_config(handle);
2131         return (SA_OK);
2132 }
2133 
2134 /*
2135  * get_node_attr(node, tag)
2136  *
2137  * Get the specified tag(attribute) if it exists on the node.  This is
2138  * used internally by a number of attribute oriented functions.
2139  */
2140 
2141 static char *
2142 get_node_attr(void *nodehdl, char *tag)
2143 {
2144         xmlNodePtr node = (xmlNodePtr)nodehdl;
2145         xmlChar *name = NULL;
2146 
2147         if (node != NULL)
2148                 name = xmlGetProp(node, (xmlChar *)tag);
2149         return ((char *)name);
2150 }
2151 
2152 /*
2153  * set_node_attr(node, tag)
2154  *
2155  * Set the specified tag(attribute) to the specified value This is
2156  * used internally by a number of attribute oriented functions. It
2157  * doesn't update the repository, only the internal document state.
2158  */
2159 
2160 void
2161 set_node_attr(void *nodehdl, char *tag, char *value)
2162 {
2163         xmlNodePtr node = (xmlNodePtr)nodehdl;
2164         if (node != NULL && tag != NULL) {
2165                 if (value != NULL)
2166                         (void) xmlSetProp(node, (xmlChar *)tag,
2167                             (xmlChar *)value);
2168                 else
2169                         (void) xmlUnsetProp(node, (xmlChar *)tag);
2170         }
2171 }
2172 
2173 /*
2174  * sa_get_group_attr(group, tag)
2175  *
2176  * Get the specied attribute, if defined, for the group.
2177  */
2178 
2179 char *
2180 sa_get_group_attr(sa_group_t group, char *tag)
2181 {
2182         return (get_node_attr((void *)group, tag));
2183 }
2184 
2185 /*
2186  * sa_set_group_attr(group, tag, value)
2187  *
2188  * set the specified tag/attribute on the group using value as its
2189  * value.
2190  *
2191  * This will result in setting the property in the SMF repository as
2192  * well as in the internal document.
2193  */
2194 
2195 int
2196 sa_set_group_attr(sa_group_t group, char *tag, char *value)
2197 {
2198         int ret;
2199         char *groupname;
2200         sa_handle_impl_t impl_handle;
2201 
2202         /*
2203          * ZFS group/subgroup doesn't need the handle so shortcut.
2204          */
2205         if (sa_group_is_zfs(group)) {
2206                 set_node_attr((void *)group, tag, value);
2207                 return (SA_OK);
2208         }
2209 
2210         impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
2211         if (impl_handle != NULL) {
2212                 groupname = sa_get_group_attr(group, "name");
2213                 ret = sa_get_instance(impl_handle->scfhandle, groupname);
2214                 if (ret == SA_OK) {
2215                         set_node_attr((void *)group, tag, value);
2216                         ret = sa_start_transaction(impl_handle->scfhandle,
2217                             "operation");
2218                         if (ret == SA_OK) {
2219                                 ret = sa_set_property(impl_handle->scfhandle,
2220                                     tag, value);
2221                                 if (ret == SA_OK)
2222                                         ret = sa_end_transaction(
2223                                             impl_handle->scfhandle,
2224                                             impl_handle);
2225                                 else
2226                                         sa_abort_transaction(
2227                                             impl_handle->scfhandle);
2228                         }
2229                         if (ret == SA_SYSTEM_ERR)
2230                                 ret = SA_NO_PERMISSION;
2231                 }
2232                 if (groupname != NULL)
2233                         sa_free_attr_string(groupname);
2234         } else {
2235                 ret = SA_SYSTEM_ERR;
2236         }
2237         return (ret);
2238 }
2239 
2240 /*
2241  * sa_get_share_attr(share, tag)
2242  *
2243  * Return the value of the tag/attribute set on the specified
2244  * share. Returns NULL if the tag doesn't exist.
2245  */
2246 
2247 char *
2248 sa_get_share_attr(sa_share_t share, char *tag)
2249 {
2250         return (get_node_attr((void *)share, tag));
2251 }
2252 
2253 /*
2254  * _sa_set_share_description(share, description)
2255  *
2256  * Add a description tag with text contents to the specified share.  A
2257  * separate XML tag is used rather than a property. This can also be
2258  * used with resources.
2259  */
2260 
2261 xmlNodePtr
2262 _sa_set_share_description(void *share, char *content)
2263 {
2264         xmlNodePtr node;
2265         node = xmlNewChild((xmlNodePtr)share, NULL, (xmlChar *)"description",
2266             NULL);
2267         xmlNodeSetContent(node, (xmlChar *)content);
2268         return (node);
2269 }
2270 
2271 /*
2272  * sa_set_share_attr(share, tag, value)
2273  *
2274  * Set the share attribute specified by tag to the specified value. In
2275  * the case of "resource", enforce a no duplicates in a group rule. If
2276  * the share is not transient, commit the changes to the repository
2277  * else just update the share internally.
2278  */
2279 
2280 int
2281 sa_set_share_attr(sa_share_t share, char *tag, char *value)
2282 {
2283         sa_group_t group;
2284         sa_share_t resource;
2285         int ret = SA_OK;
2286 
2287         group = sa_get_parent_group(share);
2288 
2289         /*
2290          * There are some attributes that may have specific
2291          * restrictions on them. Initially, only "resource" has
2292          * special meaning that needs to be checked. Only one instance
2293          * of a resource name may exist within a group.
2294          */
2295 
2296         if (strcmp(tag, "resource") == 0) {
2297                 resource = sa_get_resource(group, value);
2298                 if (resource != share && resource != NULL)
2299                         ret = SA_DUPLICATE_NAME;
2300         }
2301         if (ret == SA_OK) {
2302                 set_node_attr((void *)share, tag, value);
2303                 if (group != NULL) {
2304                         char *type;
2305                         /* we can probably optimize this some */
2306                         type = sa_get_share_attr(share, "type");
2307                         if (type == NULL || strcmp(type, "transient") != 0) {
2308                                 sa_handle_impl_t impl_handle;
2309                                 impl_handle =
2310                                     (sa_handle_impl_t)sa_find_group_handle(
2311                                     group);
2312                                 if (impl_handle != NULL) {
2313                                         ret = sa_commit_share(
2314                                             impl_handle->scfhandle, group,
2315                                             share);
2316                                 } else {
2317                                         ret = SA_SYSTEM_ERR;
2318                                 }
2319                         }
2320                         if (type != NULL)
2321                                 sa_free_attr_string(type);
2322                 }
2323         }
2324         return (ret);
2325 }
2326 
2327 /*
2328  * sa_get_property_attr(prop, tag)
2329  *
2330  * Get the value of the specified property attribute. Standard
2331  * attributes are "type" and "value".
2332  */
2333 
2334 char *
2335 sa_get_property_attr(sa_property_t prop, char *tag)
2336 {
2337         return (get_node_attr((void *)prop, tag));
2338 }
2339 
2340 /*
2341  * sa_get_optionset_attr(prop, tag)
2342  *
2343  * Get the value of the specified property attribute. Standard
2344  * attribute is "type".
2345  */
2346 
2347 char *
2348 sa_get_optionset_attr(sa_property_t optionset, char *tag)
2349 {
2350         return (get_node_attr((void *)optionset, tag));
2351 
2352 }
2353 
2354 /*
2355  * sa_set_optionset_attr(optionset, tag, value)
2356  *
2357  * Set the specified attribute(tag) to the specified value on the
2358  * optionset.
2359  */
2360 
2361 void
2362 sa_set_optionset_attr(sa_group_t optionset, char *tag, char *value)
2363 {
2364         set_node_attr((void *)optionset, tag, value);
2365 }
2366 
2367 /*
2368  * sa_free_attr_string(string)
2369  *
2370  * Free the string that was returned in one of the sa_get_*_attr()
2371  * functions.
2372  */
2373 
2374 void
2375 sa_free_attr_string(char *string)
2376 {
2377         xmlFree((xmlChar *)string);
2378 }
2379 
2380 /*
2381  * sa_get_optionset(group, proto)
2382  *
2383  * Return the optionset, if it exists, that is associated with the
2384  * specified protocol.
2385  */
2386 
2387 sa_optionset_t
2388 sa_get_optionset(void *group, char *proto)
2389 {
2390         xmlNodePtr node;
2391         xmlChar *value = NULL;
2392 
2393         for (node = ((xmlNodePtr)group)->children; node != NULL;
2394             node = node->next) {
2395                 if (xmlStrcmp(node->name, (xmlChar *)"optionset") == 0) {
2396                         value = xmlGetProp(node, (xmlChar *)"type");
2397                         if (proto != NULL) {
2398                                 if (value != NULL &&
2399                                     xmlStrcmp(value, (xmlChar *)proto) == 0) {
2400                                         break;
2401                                 }
2402                                 if (value != NULL) {
2403                                         xmlFree(value);
2404                                         value = NULL;
2405                                 }
2406                         } else {
2407                                 break;
2408                         }
2409                 }
2410         }
2411         if (value != NULL)
2412                 xmlFree(value);
2413         return ((sa_optionset_t)node);
2414 }
2415 
2416 /*
2417  * sa_get_next_optionset(optionset)
2418  *
2419  * Return the next optionset in the group. NULL if this was the last.
2420  */
2421 
2422 sa_optionset_t
2423 sa_get_next_optionset(sa_optionset_t optionset)
2424 {
2425         xmlNodePtr node;
2426 
2427         for (node = ((xmlNodePtr)optionset)->next; node != NULL;
2428             node = node->next) {
2429                 if (xmlStrcmp(node->name, (xmlChar *)"optionset") == 0) {
2430                         break;
2431                 }
2432         }
2433         return ((sa_optionset_t)node);
2434 }
2435 
2436 /*
2437  * sa_get_security(group, sectype, proto)
2438  *
2439  * Return the security optionset. The internal name is a hold over
2440  * from the implementation and will be changed before the API is
2441  * finalized. This is really a named optionset that can be negotiated
2442  * as a group of properties (like NFS security options).
2443  */
2444 
2445 sa_security_t
2446 sa_get_security(sa_group_t group, char *sectype, char *proto)
2447 {
2448         xmlNodePtr node;
2449         xmlChar *value = NULL;
2450 
2451         for (node = ((xmlNodePtr)group)->children; node != NULL;
2452             node = node->next) {
2453                 if (xmlStrcmp(node->name, (xmlChar *)"security") == 0) {
2454                         if (proto != NULL) {
2455                                 value = xmlGetProp(node, (xmlChar *)"type");
2456                                 if (value == NULL ||
2457                                     (value != NULL &&
2458                                     xmlStrcmp(value, (xmlChar *)proto) != 0)) {
2459                                         /* it doesn't match so continue */
2460                                         xmlFree(value);
2461                                         value = NULL;
2462                                         continue;
2463                                 }
2464                         }
2465                         if (value != NULL) {
2466                                 xmlFree(value);
2467                                 value = NULL;
2468                         }
2469                         /* potential match */
2470                         if (sectype != NULL) {
2471                                 value = xmlGetProp(node, (xmlChar *)"sectype");
2472                                 if (value != NULL &&
2473                                     xmlStrcmp(value, (xmlChar *)sectype) == 0) {
2474                                         break;
2475                                 }
2476                         } else {
2477                                 break;
2478                         }
2479                 }
2480                 if (value != NULL) {
2481                         xmlFree(value);
2482                         value = NULL;
2483                 }
2484         }
2485         if (value != NULL)
2486                 xmlFree(value);
2487         return ((sa_security_t)node);
2488 }
2489 
2490 /*
2491  * sa_get_next_security(security)
2492  *
2493  * Get the next security optionset if one exists.
2494  */
2495 
2496 sa_security_t
2497 sa_get_next_security(sa_security_t security)
2498 {
2499         xmlNodePtr node;
2500 
2501         for (node = ((xmlNodePtr)security)->next; node != NULL;
2502             node = node->next) {
2503                 if (xmlStrcmp(node->name, (xmlChar *)"security") == 0) {
2504                         break;
2505                 }
2506         }
2507         return ((sa_security_t)node);
2508 }
2509 
2510 /*
2511  * sa_get_property(optionset, prop)
2512  *
2513  * Get the property object with the name specified in prop from the
2514  * optionset.
2515  */
2516 
2517 sa_property_t
2518 sa_get_property(sa_optionset_t optionset, char *prop)
2519 {
2520         xmlNodePtr node = (xmlNodePtr)optionset;
2521         xmlChar *value = NULL;
2522 
2523         if (optionset == NULL)
2524                 return (NULL);
2525 
2526         for (node = node->children; node != NULL;
2527             node = node->next) {
2528                 if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) {
2529                         if (prop == NULL)
2530                                 break;
2531                         value = xmlGetProp(node, (xmlChar *)"type");
2532                         if (value != NULL &&
2533                             xmlStrcmp(value, (xmlChar *)prop) == 0) {
2534                                 break;
2535                         }
2536                         if (value != NULL) {
2537                                 xmlFree(value);
2538                                 value = NULL;
2539                         }
2540                 }
2541         }
2542         if (value != NULL)
2543                 xmlFree(value);
2544         if (node != NULL && xmlStrcmp(node->name, (xmlChar *)"option") != 0) {
2545                 /*
2546                  * avoid a non option node -- it is possible to be a
2547                  * text node
2548                  */
2549                 node = NULL;
2550         }
2551         return ((sa_property_t)node);
2552 }
2553 
2554 /*
2555  * sa_get_next_property(property)
2556  *
2557  * Get the next property following the specified property. NULL if
2558  * this was the last.
2559  */
2560 
2561 sa_property_t
2562 sa_get_next_property(sa_property_t property)
2563 {
2564         xmlNodePtr node;
2565 
2566         for (node = ((xmlNodePtr)property)->next; node != NULL;
2567             node = node->next) {
2568                 if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) {
2569                         break;
2570                 }
2571         }
2572         return ((sa_property_t)node);
2573 }
2574 
2575 /*
2576  * sa_set_share_description(share, content)
2577  *
2578  * Set the description of share to content.
2579  */
2580 
2581 int
2582 sa_set_share_description(sa_share_t share, char *content)
2583 {
2584         xmlNodePtr node;
2585         sa_group_t group;
2586         int ret = SA_OK;
2587 
2588         for (node = ((xmlNodePtr)share)->children; node != NULL;
2589             node = node->next) {
2590                 if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) {
2591                         break;
2592                 }
2593         }
2594         /* no existing description but want to add */
2595         if (node == NULL && content != NULL) {
2596                 /* add a description */
2597                 node = _sa_set_share_description(share, content);
2598         } else if (node != NULL && content != NULL) {
2599                 /* update a description */
2600                 xmlNodeSetContent(node, (xmlChar *)content);
2601         } else if (node != NULL && content == NULL) {
2602                 /* remove an existing description */
2603                 xmlUnlinkNode(node);
2604                 xmlFreeNode(node);
2605         }
2606         group = sa_get_parent_group(share);
2607         if (group != NULL &&
2608             sa_is_persistent(share) && (!sa_group_is_zfs(group))) {
2609                 sa_handle_impl_t impl_handle;
2610                 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
2611                 if (impl_handle != NULL) {
2612                         ret = sa_commit_share(impl_handle->scfhandle, group,
2613                             share);
2614                 } else {
2615                         ret = SA_SYSTEM_ERR;
2616                 }
2617         }
2618         return (ret);
2619 }
2620 
2621 /*
2622  * fixproblemchars(string)
2623  *
2624  * don't want any newline or tab characters in the text since these
2625  * could break display of data and legacy file formats.
2626  */
2627 static void
2628 fixproblemchars(char *str)
2629 {
2630         int c;
2631         for (c = *str; c != '\0'; c = *++str) {
2632                 if (c == '\t' || c == '\n')
2633                         *str = ' ';
2634                 else if (c == '"')
2635                         *str = '\'';
2636         }
2637 }
2638 
2639 /*
2640  * sa_get_share_description(share)
2641  *
2642  * Return the description text for the specified share if it
2643  * exists. NULL if no description exists.
2644  */
2645 
2646 char *
2647 sa_get_share_description(sa_share_t share)
2648 {
2649         xmlChar *description = NULL;
2650         xmlNodePtr node;
2651 
2652         for (node = ((xmlNodePtr)share)->children; node != NULL;
2653             node = node->next) {
2654                 if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) {
2655                         break;
2656                 }
2657         }
2658         if (node != NULL) {
2659                 description = xmlNodeGetContent(node);
2660                 fixproblemchars((char *)description);
2661         }
2662         return ((char *)description);
2663 }
2664 
2665 /*
2666  * sa_free(share_description(description)
2667  *
2668  * Free the description string.
2669  */
2670 
2671 void
2672 sa_free_share_description(char *description)
2673 {
2674         xmlFree((xmlChar *)description);
2675 }
2676 
2677 /*
2678  * sa_create_optionset(group, proto)
2679  *
2680  * Create an optionset for the specified protocol in the specied
2681  * group. This is manifested as a property group within SMF.
2682  */
2683 
2684 sa_optionset_t
2685 sa_create_optionset(sa_group_t group, char *proto)
2686 {
2687         sa_optionset_t optionset;
2688         sa_group_t parent = group;
2689         sa_share_t share = NULL;
2690         int err = SA_OK;
2691         char *id = NULL;
2692 
2693         optionset = sa_get_optionset(group, proto);
2694         if (optionset != NULL) {
2695                 /* can't have a duplicate protocol */
2696                 optionset = NULL;
2697         } else {
2698                 /*
2699                  * Account for resource names being slightly
2700                  * different.
2701                  */
2702                 if (sa_is_share(group)) {
2703                         /*
2704                          * Transient shares do not have an "id" so not an
2705                          * error to not find one.
2706                          */
2707                         id = sa_get_share_attr((sa_share_t)group, "id");
2708                 } else if (sa_is_resource(group)) {
2709                         share = sa_get_resource_parent(
2710                             (sa_resource_t)group);
2711                         id = sa_get_resource_attr(share, "id");
2712 
2713                         /* id can be NULL if the group is transient (ZFS) */
2714                         if (id == NULL && sa_is_persistent(group))
2715                                 err = SA_NO_MEMORY;
2716                 }
2717                 if (err == SA_NO_MEMORY) {
2718                         /*
2719                          * Couldn't get the id for the share or
2720                          * resource. While this could be a
2721                          * configuration issue, it is most likely an
2722                          * out of memory. In any case, fail the create.
2723                          */
2724                         return (NULL);
2725                 }
2726 
2727                 optionset = (sa_optionset_t)xmlNewChild((xmlNodePtr)group,
2728                     NULL, (xmlChar *)"optionset", NULL);
2729                 /*
2730                  * only put to repository if on a group and we were
2731                  * able to create an optionset.
2732                  */
2733                 if (optionset != NULL) {
2734                         char oname[SA_STRSIZE];
2735                         char *groupname;
2736 
2737                         /*
2738                          * Need to get parent group in all cases, but also get
2739                          * the share if this is a resource.
2740                          */
2741                         if (sa_is_share(group)) {
2742                                 parent = sa_get_parent_group((sa_share_t)group);
2743                         } else if (sa_is_resource(group)) {
2744                                 share = sa_get_resource_parent(
2745                                     (sa_resource_t)group);
2746                                 parent = sa_get_parent_group(share);
2747                         }
2748 
2749                         sa_set_optionset_attr(optionset, "type", proto);
2750 
2751                         (void) sa_optionset_name(optionset, oname,
2752                             sizeof (oname), id);
2753                         groupname = sa_get_group_attr(parent, "name");
2754                         if (groupname != NULL && sa_is_persistent(group)) {
2755                                 sa_handle_impl_t impl_handle;
2756                                 impl_handle =
2757                                     (sa_handle_impl_t)sa_find_group_handle(
2758                                     group);
2759                                 assert(impl_handle != NULL);
2760                                 if (impl_handle != NULL) {
2761                                         (void) sa_get_instance(
2762                                             impl_handle->scfhandle, groupname);
2763                                         (void) sa_create_pgroup(
2764                                             impl_handle->scfhandle, oname);
2765                                 }
2766                         }
2767                         if (groupname != NULL)
2768                                 sa_free_attr_string(groupname);
2769                 }
2770         }
2771 
2772         if (id != NULL)
2773                 sa_free_attr_string(id);
2774         return (optionset);
2775 }
2776 
2777 /*
2778  * sa_get_property_parent(property)
2779  *
2780  * Given a property, return the object it is a property of. This will
2781  * be an optionset of some type.
2782  */
2783 
2784 static sa_optionset_t
2785 sa_get_property_parent(sa_property_t property)
2786 {
2787         xmlNodePtr node = NULL;
2788 
2789         if (property != NULL)
2790                 node = ((xmlNodePtr)property)->parent;
2791         return ((sa_optionset_t)node);
2792 }
2793 
2794 /*
2795  * sa_get_optionset_parent(optionset)
2796  *
2797  * Return the parent of the specified optionset. This could be a group
2798  * or a share.
2799  */
2800 
2801 static sa_group_t
2802 sa_get_optionset_parent(sa_optionset_t optionset)
2803 {
2804         xmlNodePtr node = NULL;
2805 
2806         if (optionset != NULL)
2807                 node = ((xmlNodePtr)optionset)->parent;
2808         return ((sa_group_t)node);
2809 }
2810 
2811 /*
2812  * zfs_needs_update(share)
2813  *
2814  * In order to avoid making multiple updates to a ZFS share when
2815  * setting properties, the share attribute "changed" will be set to
2816  * true when a property is added or modified.  When done adding
2817  * properties, we can then detect that an update is needed.  We then
2818  * clear the state here to detect additional changes.
2819  */
2820 
2821 static int
2822 zfs_needs_update(sa_share_t share)
2823 {
2824         char *attr;
2825         int result = 0;
2826 
2827         attr = sa_get_share_attr(share, "changed");
2828         if (attr != NULL) {
2829                 sa_free_attr_string(attr);
2830                 result = 1;
2831         }
2832         set_node_attr((void *)share, "changed", NULL);
2833         return (result);
2834 }
2835 
2836 /*
2837  * zfs_set_update(share)
2838  *
2839  * Set the changed attribute of the share to true.
2840  */
2841 
2842 static void
2843 zfs_set_update(sa_share_t share)
2844 {
2845         set_node_attr((void *)share, "changed", "true");
2846 }
2847 
2848 /*
2849  * sa_commit_properties(optionset, clear)
2850  *
2851  * Check if SMF or ZFS config and either update or abort the pending
2852  * changes.
2853  */
2854 
2855 int
2856 sa_commit_properties(sa_optionset_t optionset, int clear)
2857 {
2858         sa_group_t group;
2859         sa_group_t parent;
2860         int zfs = 0;
2861         int needsupdate = 0;
2862         int ret = SA_OK;
2863         sa_handle_impl_t impl_handle;
2864 
2865         group = sa_get_optionset_parent(optionset);
2866         if (group != NULL && (sa_is_share(group) || is_zfs_group(group))) {
2867                 /* only update ZFS if on a share */
2868                 parent = sa_get_parent_group(group);
2869                 zfs++;
2870                 if (parent != NULL && is_zfs_group(parent))
2871                         needsupdate = zfs_needs_update(group);
2872                 else
2873                         zfs = 0;
2874         }
2875         if (zfs) {
2876                 if (!clear && needsupdate)
2877                         ret = sa_zfs_update((sa_share_t)group);
2878         } else {
2879                 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
2880                 if (impl_handle != NULL) {
2881                         if (clear) {
2882                                 (void) sa_abort_transaction(
2883                                     impl_handle->scfhandle);
2884                         } else {
2885                                 ret = sa_end_transaction(
2886                                     impl_handle->scfhandle, impl_handle);
2887                         }
2888                 } else {
2889                         ret = SA_SYSTEM_ERR;
2890                 }
2891         }
2892         return (ret);
2893 }
2894 
2895 /*
2896  * sa_destroy_optionset(optionset)
2897  *
2898  * Remove the optionset from its group. Update the repository to
2899  * reflect this change.
2900  */
2901 
2902 int
2903 sa_destroy_optionset(sa_optionset_t optionset)
2904 {
2905         char name[SA_STRSIZE];
2906         int len;
2907         int ret;
2908         char *id = NULL;
2909         sa_group_t group;
2910         int ispersist = 1;
2911 
2912         /* now delete the prop group */
2913         group = sa_get_optionset_parent(optionset);
2914         if (group != NULL) {
2915                 if (sa_is_resource(group)) {
2916                         sa_resource_t resource = group;
2917                         sa_share_t share = sa_get_resource_parent(resource);
2918                         group = sa_get_parent_group(share);
2919                         id = sa_get_share_attr(share, "id");
2920                 } else if (sa_is_share(group)) {
2921                         id = sa_get_share_attr((sa_share_t)group, "id");
2922                 }
2923                 ispersist = sa_is_persistent(group);
2924         }
2925         if (ispersist) {
2926                 sa_handle_impl_t impl_handle;
2927                 len = sa_optionset_name(optionset, name, sizeof (name), id);
2928                 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
2929                 if (impl_handle != NULL) {
2930                         if (len > 0) {
2931                                 ret = sa_delete_pgroup(impl_handle->scfhandle,
2932                                     name);
2933                         }
2934                 } else {
2935                         ret = SA_SYSTEM_ERR;
2936                 }
2937         }
2938         xmlUnlinkNode((xmlNodePtr)optionset);
2939         xmlFreeNode((xmlNodePtr)optionset);
2940         if (id != NULL)
2941                 sa_free_attr_string(id);
2942         return (ret);
2943 }
2944 
2945 /* private to the implementation */
2946 int
2947 _sa_remove_optionset(sa_optionset_t optionset)
2948 {
2949         int ret = SA_OK;
2950 
2951         xmlUnlinkNode((xmlNodePtr)optionset);
2952         xmlFreeNode((xmlNodePtr)optionset);
2953         return (ret);
2954 }
2955 
2956 /*
2957  * sa_create_security(group, sectype, proto)
2958  *
2959  * Create a security optionset (one that has a type name and a
2960  * proto). Security is left over from a pure NFS implementation. The
2961  * naming will change in the future when the API is released.
2962  */
2963 sa_security_t
2964 sa_create_security(sa_group_t group, char *sectype, char *proto)
2965 {
2966         sa_security_t security;
2967         char *id = NULL;
2968         sa_group_t parent;
2969         char *groupname = NULL;
2970 
2971         if (group != NULL && sa_is_share(group)) {
2972                 id = sa_get_share_attr((sa_share_t)group, "id");
2973                 parent = sa_get_parent_group(group);
2974                 if (parent != NULL)
2975                         groupname = sa_get_group_attr(parent, "name");
2976         } else if (group != NULL) {
2977                 groupname = sa_get_group_attr(group, "name");
2978         }
2979 
2980         security = sa_get_security(group, sectype, proto);
2981         if (security != NULL) {
2982                 /* can't have a duplicate security option */
2983                 security = NULL;
2984         } else {
2985                 security = (sa_security_t)xmlNewChild((xmlNodePtr)group,
2986                     NULL, (xmlChar *)"security", NULL);
2987                 if (security != NULL) {
2988                         char oname[SA_STRSIZE];
2989                         sa_set_security_attr(security, "type", proto);
2990 
2991                         sa_set_security_attr(security, "sectype", sectype);
2992                         (void) sa_security_name(security, oname,
2993                             sizeof (oname), id);
2994                         if (groupname != NULL && sa_is_persistent(group)) {
2995                                 sa_handle_impl_t impl_handle;
2996                                 impl_handle =
2997                                     (sa_handle_impl_t)sa_find_group_handle(
2998                                     group);
2999                                 if (impl_handle != NULL) {
3000                                         (void) sa_get_instance(
3001                                             impl_handle->scfhandle, groupname);
3002                                         (void) sa_create_pgroup(
3003                                             impl_handle->scfhandle, oname);
3004                                 }
3005                         }
3006                 }
3007         }
3008         if (id != NULL)
3009                 sa_free_attr_string(id);
3010         if (groupname != NULL)
3011                 sa_free_attr_string(groupname);
3012         return (security);
3013 }
3014 
3015 /*
3016  * sa_destroy_security(security)
3017  *
3018  * Remove the specified optionset from the document and the
3019  * configuration.
3020  */
3021 
3022 int
3023 sa_destroy_security(sa_security_t security)
3024 {
3025         char name[SA_STRSIZE];
3026         int len;
3027         int ret = SA_OK;
3028         char *id = NULL;
3029         sa_group_t group;
3030         int iszfs = 0;
3031         int ispersist = 1;
3032 
3033         group = sa_get_optionset_parent(security);
3034 
3035         if (group != NULL)
3036                 iszfs = sa_group_is_zfs(group);
3037 
3038         if (group != NULL && !iszfs) {
3039                 if (sa_is_share(group))
3040                         ispersist = sa_is_persistent(group);
3041                 id = sa_get_share_attr((sa_share_t)group, "id");
3042         }
3043         if (ispersist) {
3044                 len = sa_security_name(security, name, sizeof (name), id);
3045                 if (!iszfs && len > 0) {
3046                         sa_handle_impl_t impl_handle;
3047                         impl_handle =
3048                             (sa_handle_impl_t)sa_find_group_handle(group);
3049                         if (impl_handle != NULL) {
3050                                 ret = sa_delete_pgroup(impl_handle->scfhandle,
3051                                     name);
3052                         } else {
3053                                 ret = SA_SYSTEM_ERR;
3054                         }
3055                 }
3056         }
3057         xmlUnlinkNode((xmlNodePtr)security);
3058         xmlFreeNode((xmlNodePtr)security);
3059         if (iszfs)
3060                 ret = sa_zfs_update(group);
3061         if (id != NULL)
3062                 sa_free_attr_string(id);
3063         return (ret);
3064 }
3065 
3066 /*
3067  * sa_get_security_attr(optionset, tag)
3068  *
3069  * Return the specified attribute value from the optionset.
3070  */
3071 
3072 char *
3073 sa_get_security_attr(sa_property_t optionset, char *tag)
3074 {
3075         return (get_node_attr((void *)optionset, tag));
3076 
3077 }
3078 
3079 /*
3080  * sa_set_security_attr(optionset, tag, value)
3081  *
3082  * Set the optioset attribute specied by tag to the specified value.
3083  */
3084 
3085 void
3086 sa_set_security_attr(sa_group_t optionset, char *tag, char *value)
3087 {
3088         set_node_attr((void *)optionset, tag, value);
3089 }
3090 
3091 /*
3092  * is_nodetype(node, type)
3093  *
3094  * Check to see if node is of the type specified.
3095  */
3096 
3097 static int
3098 is_nodetype(void *node, char *type)
3099 {
3100         return (strcmp((char *)((xmlNodePtr)node)->name, type) == 0);
3101 }
3102 
3103 /*
3104  * add_or_update()
3105  *
3106  * Add or update a property. Pulled out of sa_set_prop_by_prop for
3107  * readability.
3108  */
3109 static int
3110 add_or_update(scfutilhandle_t *scf_handle, int type, scf_value_t *value,
3111     scf_transaction_entry_t *entry, char *name, char *valstr)
3112 {
3113         int ret = SA_SYSTEM_ERR;
3114 
3115         if (value != NULL) {
3116                 if (type == SA_PROP_OP_ADD)
3117                         ret = scf_transaction_property_new(scf_handle->trans,
3118                             entry, name, SCF_TYPE_ASTRING);
3119                 else
3120                         ret = scf_transaction_property_change(scf_handle->trans,
3121                             entry, name, SCF_TYPE_ASTRING);
3122                 if (ret == 0) {
3123                         ret = scf_value_set_astring(value, valstr);
3124                         if (ret == 0)
3125                                 ret = scf_entry_add_value(entry, value);
3126                         if (ret == 0)
3127                                 return (ret);
3128                         scf_value_destroy(value);
3129                 } else {
3130                         scf_entry_destroy(entry);
3131                 }
3132         }
3133         return (SA_SYSTEM_ERR);
3134 }
3135 
3136 /*
3137  * sa_set_prop_by_prop(optionset, group, prop, type)
3138  *
3139  * Add/remove/update the specified property prop into the optionset or
3140  * share. If a share, sort out which property group based on GUID. In
3141  * all cases, the appropriate transaction is set (or ZFS share is
3142  * marked as needing an update)
3143  */
3144 
3145 static int
3146 sa_set_prop_by_prop(sa_optionset_t optionset, sa_group_t group,
3147     sa_property_t prop, int type)
3148 {
3149         char *name;
3150         char *valstr;
3151         int ret = SA_OK;
3152         scf_transaction_entry_t *entry;
3153         scf_value_t *value;
3154         int opttype; /* 1 == optionset, 0 == security */
3155         char *id = NULL;
3156         int iszfs = 0;
3157         sa_group_t parent = NULL;
3158         sa_share_t share = NULL;
3159         sa_handle_impl_t impl_handle;
3160         scfutilhandle_t  *scf_handle;
3161 
3162         if (!sa_is_persistent(group)) {
3163                 /*
3164                  * if the group/share is not persistent we don't need
3165                  * to do anything here
3166                  */
3167                 return (SA_OK);
3168         }
3169         impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
3170         if (impl_handle == NULL || impl_handle->scfhandle == NULL)
3171                 return (SA_SYSTEM_ERR);
3172         scf_handle = impl_handle->scfhandle;
3173         name = sa_get_property_attr(prop, "type");
3174         valstr = sa_get_property_attr(prop, "value");
3175         entry = scf_entry_create(scf_handle->handle);
3176         opttype = is_nodetype((void *)optionset, "optionset");
3177 
3178         /*
3179          * Check for share vs. resource since they need slightly
3180          * different treatment given the hierarchy.
3181          */
3182         if (valstr != NULL && entry != NULL) {
3183                 if (sa_is_share(group)) {
3184                         parent = sa_get_parent_group(group);
3185                         share = (sa_share_t)group;
3186                         if (parent != NULL)
3187                                 iszfs = is_zfs_group(parent);
3188                 } else if (sa_is_resource(group)) {
3189                         share = sa_get_parent_group(group);
3190                         if (share != NULL)
3191                                 parent = sa_get_parent_group(share);
3192                 } else {
3193                         iszfs = is_zfs_group(group);
3194                 }
3195                 if (!iszfs) {
3196                         if (scf_handle->trans == NULL) {
3197                                 char oname[SA_STRSIZE];
3198                                 char *groupname = NULL;
3199                                 if (share != NULL) {
3200                                         if (parent != NULL)
3201                                                 groupname =
3202                                                     sa_get_group_attr(parent,
3203                                                     "name");
3204                                         id = sa_get_share_attr(
3205                                             (sa_share_t)share, "id");
3206                                 } else {
3207                                         groupname = sa_get_group_attr(group,
3208                                             "name");
3209                                 }
3210                                 if (groupname != NULL) {
3211                                         ret = sa_get_instance(scf_handle,
3212                                             groupname);
3213                                         sa_free_attr_string(groupname);
3214                                 }
3215                                 if (opttype)
3216                                         (void) sa_optionset_name(optionset,
3217                                             oname, sizeof (oname), id);
3218                                 else
3219                                         (void) sa_security_name(optionset,
3220                                             oname, sizeof (oname), id);
3221                                 ret = sa_start_transaction(scf_handle, oname);
3222                                 if (id != NULL)
3223                                         sa_free_attr_string(id);
3224                         }
3225                         if (ret == SA_OK) {
3226                                 switch (type) {
3227                                 case SA_PROP_OP_REMOVE:
3228                                         ret = scf_transaction_property_delete(
3229                                             scf_handle->trans, entry, name);
3230                                         break;
3231                                 case SA_PROP_OP_ADD:
3232                                 case SA_PROP_OP_UPDATE:
3233                                         value = scf_value_create(
3234                                             scf_handle->handle);
3235                                         ret = add_or_update(scf_handle, type,
3236                                             value, entry, name, valstr);
3237                                         break;
3238                                 }
3239                         }
3240                 } else {
3241                         /*
3242                          * ZFS update. The calling function would have updated
3243                          * the internal XML structure. Just need to flag it as
3244                          * changed for ZFS.
3245                          */
3246                         zfs_set_update((sa_share_t)group);
3247                 }
3248         }
3249 
3250         if (name != NULL)
3251                 sa_free_attr_string(name);
3252         if (valstr != NULL)
3253                 sa_free_attr_string(valstr);
3254         else if (entry != NULL)
3255                 scf_entry_destroy(entry);
3256 
3257         if (ret == -1)
3258                 ret = SA_SYSTEM_ERR;
3259 
3260         return (ret);
3261 }
3262 
3263 /*
3264  * sa_create_section(name, value)
3265  *
3266  * Create a new section with the specified name and extra data.
3267  */
3268 
3269 sa_property_t
3270 sa_create_section(char *name, char *extra)
3271 {
3272         xmlNodePtr node;
3273 
3274         node = xmlNewNode(NULL, (xmlChar *)"section");
3275         if (node != NULL) {
3276                 if (name != NULL)
3277                         (void) xmlSetProp(node, (xmlChar *)"name",
3278                             (xmlChar *)name);
3279                 if (extra != NULL)
3280                         (void) xmlSetProp(node, (xmlChar *)"extra",
3281                             (xmlChar *)extra);
3282         }
3283         return ((sa_property_t)node);
3284 }
3285 
3286 void
3287 sa_set_section_attr(sa_property_t sect, char *name, char *value)
3288 {
3289         (void) xmlSetProp(sect, (xmlChar *)name, (xmlChar *)value);
3290 }
3291 
3292 /*
3293  * sa_create_property(section, name, value)
3294  *
3295  * Create a new property with the specified name and value.
3296  */
3297 
3298 sa_property_t
3299 sa_create_property(char *name, char *value)
3300 {
3301         xmlNodePtr node;
3302 
3303         node = xmlNewNode(NULL, (xmlChar *)"option");
3304         if (node != NULL) {
3305                 (void) xmlSetProp(node, (xmlChar *)"type", (xmlChar *)name);
3306                 (void) xmlSetProp(node, (xmlChar *)"value", (xmlChar *)value);
3307         }
3308         return ((sa_property_t)node);
3309 }
3310 
3311 /*
3312  * sa_add_property(object, property)
3313  *
3314  * Add the specified property to the object. Issue the appropriate
3315  * transaction or mark a ZFS object as needing an update.
3316  */
3317 
3318 int
3319 sa_add_property(void *object, sa_property_t property)
3320 {
3321         int ret = SA_OK;
3322         sa_group_t parent;
3323         sa_group_t group;
3324         char *proto;
3325 
3326         if (property != NULL) {
3327                 sa_handle_t handle;
3328                 handle = sa_find_group_handle((sa_group_t)object);
3329                 /* It is legitimate to not find a handle */
3330                 proto = sa_get_optionset_attr(object, "type");
3331                 if ((ret = sa_valid_property(handle, object, proto,
3332                     property)) == SA_OK) {
3333                         property = (sa_property_t)xmlAddChild(
3334                             (xmlNodePtr)object, (xmlNodePtr)property);
3335                 } else {
3336                         if (proto != NULL)
3337                                 sa_free_attr_string(proto);
3338                         return (ret);
3339                 }
3340                 if (proto != NULL)
3341                         sa_free_attr_string(proto);
3342         }
3343 
3344 
3345         parent = sa_get_parent_group(object);
3346         if (!sa_is_persistent(parent))
3347                 return (ret);
3348 
3349         if (sa_is_resource(parent)) {
3350                 /*
3351                  * Resources are children of share.  Need to go up two
3352                  * levels to find the group but the parent needs to be
3353                  * the share at this point in order to get the "id".
3354                  */
3355                 parent = sa_get_parent_group(parent);
3356                 group = sa_get_parent_group(parent);
3357         } else if (sa_is_share(parent)) {
3358                 group = sa_get_parent_group(parent);
3359         } else {
3360                 group = parent;
3361         }
3362 
3363         if (property == NULL) {
3364                 ret = SA_NO_MEMORY;
3365         } else {
3366                 char oname[SA_STRSIZE];
3367 
3368                 if (!is_zfs_group(group)) {
3369                         char *id = NULL;
3370                         sa_handle_impl_t impl_handle;
3371                         scfutilhandle_t  *scf_handle;
3372 
3373                         impl_handle = (sa_handle_impl_t)sa_find_group_handle(
3374                             group);
3375                         if (impl_handle == NULL ||
3376                             impl_handle->scfhandle == NULL)
3377                                 ret = SA_SYSTEM_ERR;
3378                         if (ret == SA_OK) {
3379                                 scf_handle = impl_handle->scfhandle;
3380                                 if (sa_is_share((sa_group_t)parent)) {
3381                                         id = sa_get_share_attr(
3382                                             (sa_share_t)parent, "id");
3383                                 }
3384                                 if (scf_handle->trans == NULL) {
3385                                         if (is_nodetype(object, "optionset")) {
3386                                                 (void) sa_optionset_name(
3387                                                     (sa_optionset_t)object,
3388                                                     oname, sizeof (oname), id);
3389                                         } else {
3390                                                 (void) sa_security_name(
3391                                                     (sa_optionset_t)object,
3392                                                     oname, sizeof (oname), id);
3393                                         }
3394                                         ret = sa_start_transaction(scf_handle,
3395                                             oname);
3396                                 }
3397                                 if (ret == SA_OK) {
3398                                         char *name;
3399                                         char *value;
3400                                         name = sa_get_property_attr(property,
3401                                             "type");
3402                                         value = sa_get_property_attr(property,
3403                                             "value");
3404                                         if (name != NULL && value != NULL) {
3405                                                 if (scf_handle->scf_state ==
3406                                                     SCH_STATE_INIT) {
3407                                                         ret = sa_set_property(
3408                                                             scf_handle, name,
3409                                                             value);
3410                                                 }
3411                                         } else {
3412                                                 ret = SA_CONFIG_ERR;
3413                                         }
3414                                         if (name != NULL)
3415                                                 sa_free_attr_string(
3416                                                     name);
3417                                         if (value != NULL)
3418                                                 sa_free_attr_string(value);
3419                                 }
3420                                 if (id != NULL)
3421                                         sa_free_attr_string(id);
3422                         }
3423                 } else {
3424                         /*
3425                          * ZFS is a special case. We do want
3426                          * to allow editing property/security
3427                          * lists since we can have a better
3428                          * syntax and we also want to keep
3429                          * things consistent when possible.
3430                          *
3431                          * Right now, we defer until the
3432                          * sa_commit_properties so we can get
3433                          * them all at once. We do need to
3434                          * mark the share as "changed"
3435                          */
3436                         zfs_set_update((sa_share_t)parent);
3437                 }
3438         }
3439         return (ret);
3440 }
3441 
3442 /*
3443  * sa_remove_property(property)
3444  *
3445  * Remove the specied property from its containing object. Update the
3446  * repository as appropriate.
3447  */
3448 
3449 int
3450 sa_remove_property(sa_property_t property)
3451 {
3452         int ret = SA_OK;
3453 
3454         if (property != NULL) {
3455                 sa_optionset_t optionset;
3456                 sa_group_t group;
3457                 optionset = sa_get_property_parent(property);
3458                 if (optionset != NULL) {
3459                         group = sa_get_optionset_parent(optionset);
3460                         if (group != NULL) {
3461                                 ret = sa_set_prop_by_prop(optionset, group,
3462                                     property, SA_PROP_OP_REMOVE);
3463                         }
3464                 }
3465                 xmlUnlinkNode((xmlNodePtr)property);
3466                 xmlFreeNode((xmlNodePtr)property);
3467         } else {
3468                 ret = SA_NO_SUCH_PROP;
3469         }
3470         return (ret);
3471 }
3472 
3473 /*
3474  * sa_update_property(property, value)
3475  *
3476  * Update the specified property to the new value.  If value is NULL,
3477  * we currently treat this as a remove.
3478  */
3479 
3480 int
3481 sa_update_property(sa_property_t property, char *value)
3482 {
3483         int ret = SA_OK;
3484         if (value == NULL) {
3485                 return (sa_remove_property(property));
3486         } else {
3487                 sa_optionset_t optionset;
3488                 sa_group_t group;
3489                 set_node_attr((void *)property, "value", value);
3490                 optionset = sa_get_property_parent(property);
3491                 if (optionset != NULL) {
3492                         group = sa_get_optionset_parent(optionset);
3493                         if (group != NULL) {
3494                                 ret = sa_set_prop_by_prop(optionset, group,
3495                                     property, SA_PROP_OP_UPDATE);
3496                         }
3497                 } else {
3498                         ret = SA_NO_SUCH_PROP;
3499                 }
3500         }
3501         return (ret);
3502 }
3503 
3504 /*
3505  * sa_get_protocol_section(propset, prop)
3506  *
3507  * Get the specified protocol specific section. These are global to
3508  * the protocol and not specific to a group or share.
3509  */
3510 
3511 sa_protocol_properties_t
3512 sa_get_protocol_section(sa_protocol_properties_t propset, char *section)
3513 {
3514         xmlNodePtr node = (xmlNodePtr)propset;
3515         xmlChar *value = NULL;
3516         char *proto;
3517 
3518         proto = sa_get_optionset_attr(propset, "type");
3519         if ((sa_proto_get_featureset(proto) & SA_FEATURE_HAS_SECTIONS) == 0) {
3520                 if (proto != NULL)
3521                         sa_free_attr_string(proto);
3522                 return (propset);
3523         }
3524 
3525         for (node = node->children; node != NULL;
3526             node = node->next) {
3527                 if (xmlStrcmp(node->name, (xmlChar *)"section") == 0) {
3528                         if (section == NULL)
3529                                 break;
3530                         value = xmlGetProp(node, (xmlChar *)"name");
3531                         if (value != NULL &&
3532                             xmlStrcasecmp(value, (xmlChar *)section) == 0) {
3533                                 break;
3534                         }
3535                         if (value != NULL) {
3536                                 xmlFree(value);
3537                                 value = NULL;
3538                         }
3539                 }
3540         }
3541         if (value != NULL)
3542                 xmlFree(value);
3543         if (proto != NULL)
3544                 sa_free_attr_string(proto);
3545         if (node != NULL && xmlStrcmp(node->name, (xmlChar *)"section") != 0) {
3546                 /*
3547                  * avoid a non option node -- it is possible to be a
3548                  * text node
3549                  */
3550                 node = NULL;
3551         }
3552         return ((sa_protocol_properties_t)node);
3553 }
3554 
3555 /*
3556  * sa_get_next_protocol_section(prop, find)
3557  *
3558  * Get the next protocol specific section in the list.
3559  */
3560 
3561 sa_property_t
3562 sa_get_next_protocol_section(sa_property_t prop, char *find)
3563 {
3564         xmlNodePtr node;
3565         xmlChar *value = NULL;
3566         char *proto;
3567 
3568         proto = sa_get_optionset_attr(prop, "type");
3569         if ((sa_proto_get_featureset(proto) & SA_FEATURE_HAS_SECTIONS) == 0) {
3570                 if (proto != NULL)
3571                         sa_free_attr_string(proto);
3572                 return ((sa_property_t)NULL);
3573         }
3574 
3575         for (node = ((xmlNodePtr)prop)->next; node != NULL;
3576             node = node->next) {
3577                 if (xmlStrcmp(node->name, (xmlChar *)"section") == 0) {
3578                         if (find == NULL)
3579                                 break;
3580                         value = xmlGetProp(node, (xmlChar *)"name");
3581                         if (value != NULL &&
3582                             xmlStrcasecmp(value, (xmlChar *)find) == 0) {
3583                                 break;
3584                         }
3585                         if (value != NULL) {
3586                                 xmlFree(value);
3587                                 value = NULL;
3588                         }
3589 
3590                 }
3591         }
3592         if (value != NULL)
3593                 xmlFree(value);
3594         if (proto != NULL)
3595                 sa_free_attr_string(proto);
3596         return ((sa_property_t)node);
3597 }
3598 
3599 /*
3600  * sa_get_protocol_property(propset, prop)
3601  *
3602  * Get the specified protocol specific property. These are global to
3603  * the protocol and not specific to a group or share.
3604  */
3605 
3606 sa_property_t
3607 sa_get_protocol_property(sa_protocol_properties_t propset, char *prop)
3608 {
3609         xmlNodePtr node = (xmlNodePtr)propset;
3610         xmlChar *value = NULL;
3611 
3612         if (propset == NULL)
3613                 return (NULL);
3614 
3615         for (node = node->children; node != NULL;
3616             node = node->next) {
3617                 if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) {
3618                         if (prop == NULL)
3619                                 break;
3620                         value = xmlGetProp(node, (xmlChar *)"type");
3621                         if (value != NULL &&
3622                             xmlStrcasecmp(value, (xmlChar *)prop) == 0) {
3623                                 break;
3624                         }
3625                         if (value != NULL) {
3626                                 xmlFree(value);
3627                                 value = NULL;
3628                         }
3629                 }
3630         }
3631         if (value != NULL)
3632                 xmlFree(value);
3633         if (node != NULL && xmlStrcmp(node->name, (xmlChar *)"option") != 0) {
3634                 /*
3635                  * avoid a non option node -- it is possible to be a
3636                  * text node
3637                  */
3638                 node = NULL;
3639         }
3640         return ((sa_property_t)node);
3641 }
3642 
3643 /*
3644  * sa_get_next_protocol_property(prop)
3645  *
3646  * Get the next protocol specific property in the list.
3647  */
3648 
3649 sa_property_t
3650 sa_get_next_protocol_property(sa_property_t prop, char *find)
3651 {
3652         xmlNodePtr node;
3653         xmlChar *value = NULL;
3654 
3655         for (node = ((xmlNodePtr)prop)->next; node != NULL;
3656             node = node->next) {
3657                 if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) {
3658                         if (find == NULL)
3659                                 break;
3660                         value = xmlGetProp(node, (xmlChar *)"type");
3661                         if (value != NULL &&
3662                             xmlStrcasecmp(value, (xmlChar *)find) == 0) {
3663                                 break;
3664                         }
3665                         if (value != NULL) {
3666                                 xmlFree(value);
3667                                 value = NULL;
3668                         }
3669 
3670                 }
3671         }
3672         if (value != NULL)
3673                 xmlFree(value);
3674         return ((sa_property_t)node);
3675 }
3676 
3677 /*
3678  * sa_set_protocol_property(prop, value)
3679  *
3680  * Set the specified property to have the new value.  The protocol
3681  * specific plugin will then be called to update the property.
3682  */
3683 
3684 int
3685 sa_set_protocol_property(sa_property_t prop, char *section, char *value)
3686 {
3687         sa_protocol_properties_t propset;
3688         char *proto;
3689         int ret = SA_INVALID_PROTOCOL;
3690 
3691         propset = ((xmlNodePtr)prop)->parent;
3692         if (propset != NULL) {
3693                 proto = sa_get_optionset_attr(propset, "type");
3694                 if (proto != NULL) {
3695                         if (section != NULL)
3696                                 set_node_attr((xmlNodePtr)prop, "section",
3697                                     section);
3698                         set_node_attr((xmlNodePtr)prop, "value", value);
3699                         ret = sa_proto_set_property(proto, prop);
3700                         sa_free_attr_string(proto);
3701                 }
3702         }
3703         return (ret);
3704 }
3705 
3706 /*
3707  * sa_add_protocol_property(propset, prop)
3708  *
3709  * Add a new property to the protocol specific property set.
3710  */
3711 
3712 int
3713 sa_add_protocol_property(sa_protocol_properties_t propset, sa_property_t prop)
3714 {
3715         xmlNodePtr node;
3716 
3717         /* should check for legitimacy */
3718         node = xmlAddChild((xmlNodePtr)propset, (xmlNodePtr)prop);
3719         if (node != NULL)
3720                 return (SA_OK);
3721         return (SA_NO_MEMORY);
3722 }
3723 
3724 /*
3725  * sa_create_protocol_properties(proto)
3726  *
3727  * Create a protocol specific property set.
3728  */
3729 
3730 sa_protocol_properties_t
3731 sa_create_protocol_properties(char *proto)
3732 {
3733         xmlNodePtr node;
3734 
3735         node = xmlNewNode(NULL, (xmlChar *)"propertyset");
3736         if (node != NULL)
3737                 (void) xmlSetProp(node, (xmlChar *)"type", (xmlChar *)proto);
3738         return (node);
3739 }
3740 
3741 /*
3742  * sa_get_share_resource(share, resource)
3743  *
3744  * Get the named resource from the share, if it exists. If resource is
3745  * NULL, get the first resource.
3746  */
3747 
3748 sa_resource_t
3749 sa_get_share_resource(sa_share_t share, char *resource)
3750 {
3751         xmlNodePtr node = NULL;
3752         xmlChar *name;
3753 
3754         if (share != NULL) {
3755                 for (node = ((xmlNodePtr)share)->children; node != NULL;
3756                     node = node->next) {
3757                         if (xmlStrcmp(node->name, (xmlChar *)"resource") == 0) {
3758                                 if (resource == NULL) {
3759                                         /*
3760                                          * We are looking for the first
3761                                          * resource node and not a names
3762                                          * resource.
3763                                          */
3764                                         break;
3765                                 } else {
3766                                         /* is it the correct share? */
3767                                         name = xmlGetProp(node,
3768                                             (xmlChar *)"name");
3769                                         if (name != NULL &&
3770                                             xmlStrcasecmp(name,
3771                                             (xmlChar *)resource) == 0) {
3772                                                 xmlFree(name);
3773                                                 break;
3774                                         }
3775                                         xmlFree(name);
3776                                 }
3777                         }
3778                 }
3779         }
3780         return ((sa_resource_t)node);
3781 }
3782 
3783 /*
3784  * sa_get_next_resource(resource)
3785  *      Return the next share following the specified share
3786  *      from the internal list of shares. Returns NULL if there
3787  *      are no more shares.  The list is relative to the same
3788  *      group.
3789  */
3790 sa_share_t
3791 sa_get_next_resource(sa_resource_t resource)
3792 {
3793         xmlNodePtr node = NULL;
3794 
3795         if (resource != NULL) {
3796                 for (node = ((xmlNodePtr)resource)->next; node != NULL;
3797                     node = node->next) {
3798                         if (xmlStrcmp(node->name, (xmlChar *)"resource") == 0)
3799                                 break;
3800                 }
3801         }
3802         return ((sa_share_t)node);
3803 }
3804 
3805 /*
3806  * _sa_get_next_resource_index(share)
3807  *
3808  * get the next resource index number (one greater then current largest)
3809  */
3810 
3811 static int
3812 _sa_get_next_resource_index(sa_share_t share)
3813 {
3814         sa_resource_t resource;
3815         int index = 0;
3816         char *id;
3817 
3818         for (resource = sa_get_share_resource(share, NULL);
3819             resource != NULL;
3820             resource = sa_get_next_resource(resource)) {
3821                 id = get_node_attr((void *)resource, "id");
3822                 if (id != NULL) {
3823                         int val;
3824                         val = atoi(id);
3825                         if (val > index)
3826                                 index = val;
3827                         sa_free_attr_string(id);
3828                 }
3829         }
3830         return (index + 1);
3831 }
3832 
3833 
3834 /*
3835  * sa_add_resource(share, resource, persist, &err)
3836  *
3837  * Adds a new resource name associated with share. The resource name
3838  * must be unique in the system and will be case insensitive (eventually).
3839  */
3840 
3841 sa_resource_t
3842 sa_add_resource(sa_share_t share, char *resource, int persist, int *error)
3843 {
3844         xmlNodePtr node;
3845         int err = SA_OK;
3846         sa_resource_t res;
3847         sa_group_t group;
3848         sa_handle_t handle;
3849         char istring[8]; /* just big enough for an integer value */
3850         int index;
3851 
3852         group = sa_get_parent_group(share);
3853         handle = sa_find_group_handle(group);
3854         res = sa_find_resource(handle, resource);
3855         if (res != NULL) {
3856                 err = SA_DUPLICATE_NAME;
3857                 res = NULL;
3858         } else {
3859                 node = xmlNewChild((xmlNodePtr)share, NULL,
3860                     (xmlChar *)"resource", NULL);
3861                 if (node != NULL) {
3862                         (void) xmlSetProp(node, (xmlChar *)"name",
3863                             (xmlChar *)resource);
3864                         (void) xmlSetProp(node, (xmlChar *)"type", persist ?
3865                             (xmlChar *)"persist" : (xmlChar *)"transient");
3866                         if (persist != SA_SHARE_TRANSIENT) {
3867                                 index = _sa_get_next_resource_index(share);
3868                                 (void) snprintf(istring, sizeof (istring), "%d",
3869                                     index);
3870                                 (void) xmlSetProp(node, (xmlChar *)"id",
3871                                     (xmlChar *)istring);
3872 
3873                                 if (!sa_is_persistent((sa_group_t)share))
3874                                         goto done;
3875 
3876                                 if (!sa_group_is_zfs(group)) {
3877                                         /* ZFS doesn't use resource names */
3878                                         sa_handle_impl_t ihandle;
3879 
3880                                         ihandle = (sa_handle_impl_t)
3881                                             sa_find_group_handle(
3882                                             group);
3883                                         if (ihandle != NULL)
3884                                                 err = sa_commit_share(
3885                                                     ihandle->scfhandle, group,
3886                                                     share);
3887                                         else
3888                                                 err = SA_SYSTEM_ERR;
3889                                 } else {
3890                                         err = sa_zfs_update((sa_share_t)group);
3891                                 }
3892                         }
3893                 }
3894         }
3895 done:
3896         if (error != NULL)
3897                 *error = err;
3898         return ((sa_resource_t)node);
3899 }
3900 
3901 /*
3902  * sa_remove_resource(resource)
3903  *
3904  * Remove the resource name from the share (and the system)
3905  */
3906 
3907 int
3908 sa_remove_resource(sa_resource_t resource)
3909 {
3910         sa_share_t share;
3911         sa_group_t group;
3912         char *type;
3913         int ret = SA_OK;
3914         boolean_t transient = B_FALSE;
3915         sa_optionset_t opt;
3916 
3917         share = sa_get_resource_parent(resource);
3918         type = sa_get_share_attr(share, "type");
3919         group = sa_get_parent_group(share);
3920 
3921 
3922         if (type != NULL) {
3923                 if (strcmp(type, "persist") != 0)
3924                         transient = B_TRUE;
3925                 sa_free_attr_string(type);
3926         }
3927 
3928         /* Disable the resource for all protocols. */
3929         (void) sa_disable_resource(resource, NULL);
3930 
3931         /* Remove any optionsets from the resource. */
3932         for (opt = sa_get_optionset(resource, NULL);
3933             opt != NULL;
3934             opt = sa_get_next_optionset(opt))
3935                 (void) sa_destroy_optionset(opt);
3936 
3937         /* Remove from the share */
3938         xmlUnlinkNode((xmlNode *)resource);
3939         xmlFreeNode((xmlNode *)resource);
3940 
3941         /* only do SMF action if permanent and not ZFS */
3942         if (transient)
3943                 return (ret);
3944 
3945         if (!sa_group_is_zfs(group)) {
3946                 sa_handle_impl_t ihandle;
3947                 ihandle = (sa_handle_impl_t)sa_find_group_handle(group);
3948                 if (ihandle != NULL)
3949                         ret = sa_commit_share(ihandle->scfhandle, group, share);
3950                 else
3951                         ret = SA_SYSTEM_ERR;
3952         } else {
3953                 ret = sa_zfs_update((sa_share_t)group);
3954         }
3955 
3956         return (ret);
3957 }
3958 
3959 /*
3960  * proto_rename_resource(handle, group, resource, newname)
3961  *
3962  * Helper function for sa_rename_resource that notifies the protocol
3963  * of a resource name change prior to a config repository update.
3964  */
3965 static int
3966 proto_rename_resource(sa_handle_t handle, sa_group_t group,
3967     sa_resource_t resource, char *newname)
3968 {
3969         sa_optionset_t optionset;
3970         int ret = SA_OK;
3971         int err;
3972 
3973         for (optionset = sa_get_optionset(group, NULL);
3974             optionset != NULL;
3975             optionset = sa_get_next_optionset(optionset)) {
3976                 char *type;
3977                 type = sa_get_optionset_attr(optionset, "type");
3978                 if (type != NULL) {
3979                         err = sa_proto_rename_resource(handle, type, resource,
3980                             newname);
3981                         if (err != SA_OK)
3982                                 ret = err;
3983                         sa_free_attr_string(type);
3984                 }
3985         }
3986         return (ret);
3987 }
3988 
3989 /*
3990  * sa_rename_resource(resource, newname)
3991  *
3992  * Rename the resource to the new name, if it is unique.
3993  */
3994 
3995 int
3996 sa_rename_resource(sa_resource_t resource, char *newname)
3997 {
3998         sa_share_t share;
3999         sa_group_t group = NULL;
4000         sa_resource_t target;
4001         int ret = SA_CONFIG_ERR;
4002         sa_handle_t handle = NULL;
4003 
4004         share = sa_get_resource_parent(resource);
4005         if (share == NULL)
4006                 return (ret);
4007 
4008         group = sa_get_parent_group(share);
4009         if (group == NULL)
4010                 return (ret);
4011 
4012         handle = (sa_handle_impl_t)sa_find_group_handle(group);
4013         if (handle == NULL)
4014                 return (ret);
4015 
4016         target = sa_find_resource(handle, newname);
4017         if (target != NULL) {
4018                 ret = SA_DUPLICATE_NAME;
4019         } else {
4020                 /*
4021                  * Everything appears to be valid at this
4022                  * point. Change the name of the active share and then
4023                  * update the share in the appropriate repository.
4024                  */
4025                 ret = proto_rename_resource(handle, group, resource, newname);
4026                 set_node_attr(resource, "name", newname);
4027 
4028                 if (!sa_is_persistent((sa_group_t)share))
4029                         return (ret);
4030 
4031                 if (!sa_group_is_zfs(group)) {
4032                         sa_handle_impl_t ihandle = (sa_handle_impl_t)handle;
4033                         ret = sa_commit_share(ihandle->scfhandle, group,
4034                             share);
4035                 } else {
4036                         ret = sa_zfs_update((sa_share_t)group);
4037                 }
4038         }
4039         return (ret);
4040 }
4041 
4042 /*
4043  * sa_get_resource_attr(resource, tag)
4044  *
4045  * Get the named attribute of the resource. "name" and "id" are
4046  * currently defined.  NULL if tag not defined.
4047  */
4048 
4049 char *
4050 sa_get_resource_attr(sa_resource_t resource, char *tag)
4051 {
4052         return (get_node_attr((void *)resource, tag));
4053 }
4054 
4055 /*
4056  * sa_set_resource_attr(resource, tag, value)
4057  *
4058  * Get the named attribute of the resource. "name" and "id" are
4059  * currently defined.  NULL if tag not defined. Currently we don't do
4060  * much, but additional checking may be needed in the future.
4061  */
4062 
4063 int
4064 sa_set_resource_attr(sa_resource_t resource, char *tag, char *value)
4065 {
4066         set_node_attr((void *)resource, tag, value);
4067         return (SA_OK);
4068 }
4069 
4070 /*
4071  * sa_get_resource_parent(resource_t)
4072  *
4073  * Returns the share associated with the resource.
4074  */
4075 
4076 sa_share_t
4077 sa_get_resource_parent(sa_resource_t resource)
4078 {
4079         sa_share_t share = NULL;
4080 
4081         if (resource != NULL)
4082                 share = (sa_share_t)((xmlNodePtr)resource)->parent;
4083         return (share);
4084 }
4085 
4086 /*
4087  * find_resource(group, name)
4088  *
4089  * Find the resource within the group.
4090  */
4091 
4092 static sa_resource_t
4093 find_resource(sa_group_t group, char *resname)
4094 {
4095         sa_share_t share;
4096         sa_resource_t resource = NULL;
4097         char *name;
4098 
4099         /* Iterate over all the shares and resources in the group. */
4100         for (share = sa_get_share(group, NULL);
4101             share != NULL && resource == NULL;
4102             share = sa_get_next_share(share)) {
4103                 for (resource = sa_get_share_resource(share, NULL);
4104                     resource != NULL;
4105                     resource = sa_get_next_resource(resource)) {
4106                         name = sa_get_resource_attr(resource, "name");
4107                         if (name != NULL && xmlStrcasecmp((xmlChar*)name,
4108                             (xmlChar*)resname) == 0) {
4109                                 sa_free_attr_string(name);
4110                                 break;
4111                         }
4112                         if (name != NULL) {
4113                                 sa_free_attr_string(name);
4114                         }
4115                 }
4116         }
4117         return (resource);
4118 }
4119 
4120 /*
4121  * sa_find_resource(name)
4122  *
4123  * Find the named resource in the system.
4124  */
4125 
4126 sa_resource_t
4127 sa_find_resource(sa_handle_t handle, char *name)
4128 {
4129         sa_group_t group;
4130         sa_group_t zgroup;
4131         sa_resource_t resource = NULL;
4132 
4133         /*
4134          * Iterate over all groups and zfs subgroups and check for
4135          * resource name in them.
4136          */
4137         for (group = sa_get_group(handle, NULL); group != NULL;
4138             group = sa_get_next_group(group)) {
4139 
4140                 if (is_zfs_group(group)) {
4141                         for (zgroup =
4142                             (sa_group_t)_sa_get_child_node((xmlNodePtr)group,
4143                             (xmlChar *)"group");
4144                             zgroup != NULL && resource == NULL;
4145                             zgroup = sa_get_next_group(zgroup)) {
4146                                 resource = find_resource(zgroup, name);
4147                         }
4148                 } else {
4149                         resource = find_resource(group, name);
4150                 }
4151                 if (resource != NULL)
4152                         break;
4153         }
4154         return (resource);
4155 }
4156 
4157 /*
4158  * sa_get_resource(group, resource)
4159  *
4160  * Search all the shares in the specified group for a share with a
4161  * resource name matching the one specified.
4162  *
4163  * In the future, it may be advantageous to allow group to be NULL and
4164  * search all groups but that isn't needed at present.
4165  */
4166 
4167 sa_resource_t
4168 sa_get_resource(sa_group_t group, char *resource)
4169 {
4170         sa_share_t share = NULL;
4171         sa_resource_t res = NULL;
4172 
4173         if (resource != NULL) {
4174                 for (share = sa_get_share(group, NULL);
4175                     share != NULL && res == NULL;
4176                     share = sa_get_next_share(share)) {
4177                         res = sa_get_share_resource(share, resource);
4178                 }
4179         }
4180         return (res);
4181 }
4182 
4183 /*
4184  * get_protocol_list(optionset, object)
4185  *
4186  * Get the protocol optionset list for the object and add them as
4187  * properties to optionset.
4188  */
4189 static int
4190 get_protocol_list(sa_optionset_t optionset, void *object)
4191 {
4192         sa_property_t prop;
4193         sa_optionset_t opts;
4194         int ret = SA_OK;
4195 
4196         for (opts = sa_get_optionset(object, NULL);
4197             opts != NULL;
4198             opts = sa_get_next_optionset(opts)) {
4199                 char *type;
4200                 type = sa_get_optionset_attr(opts, "type");
4201                 /*
4202                  * It is possible to have a non-protocol optionset. We
4203                  * skip any of those found.
4204                  */
4205                 if (type == NULL)
4206                         continue;
4207                 prop = sa_create_property(type, "true");
4208                 sa_free_attr_string(type);
4209                 if (prop != NULL)
4210                         prop = (sa_property_t)xmlAddChild((xmlNodePtr)optionset,
4211                             (xmlNodePtr)prop);
4212                 /* If prop is NULL, don't bother continuing */
4213                 if (prop == NULL) {
4214                         ret = SA_NO_MEMORY;
4215                         break;
4216                 }
4217         }
4218         return (ret);
4219 }
4220 
4221 /*
4222  * sa_free_protoset(optionset)
4223  *
4224  * Free the protocol property optionset.
4225  */
4226 static void
4227 sa_free_protoset(sa_optionset_t optionset)
4228 {
4229         if (optionset != NULL) {
4230                 xmlUnlinkNode((xmlNodePtr) optionset);
4231                 xmlFreeNode((xmlNodePtr) optionset);
4232         }
4233 }
4234 
4235 /*
4236  * sa_optionset_t sa_get_active_protocols(object)
4237  *
4238  * Return a list of the protocols that are active for the object.
4239  * This is currently an internal helper function, but could be
4240  * made visible if there is enough demand for it.
4241  *
4242  * The function finds the parent group and extracts the protocol
4243  * optionsets creating a new optionset with the protocols as properties.
4244  *
4245  * The caller must free the returned optionset.
4246  */
4247 
4248 static sa_optionset_t
4249 sa_get_active_protocols(void *object)
4250 {
4251         sa_optionset_t options;
4252         sa_share_t share = NULL;
4253         sa_group_t group = NULL;
4254         sa_resource_t resource = NULL;
4255         int ret = SA_OK;
4256 
4257         if (object == NULL)
4258                 return (NULL);
4259         options = (sa_optionset_t)xmlNewNode(NULL, (xmlChar *)"optionset");
4260         if (options == NULL)
4261                 return (NULL);
4262 
4263         /*
4264          * Find the objects up the tree that might have protocols
4265          * enabled on them.
4266          */
4267         if (sa_is_resource(object)) {
4268                 resource = (sa_resource_t)object;
4269                 share = sa_get_resource_parent(resource);
4270                 group = sa_get_parent_group(share);
4271         } else if (sa_is_share(object)) {
4272                 share = (sa_share_t)object;
4273                 group = sa_get_parent_group(share);
4274         } else {
4275                 group = (sa_group_t)group;
4276         }
4277         if (resource != NULL)
4278                 ret = get_protocol_list(options, resource);
4279         if (ret == SA_OK && share != NULL)
4280                 ret = get_protocol_list(options, share);
4281         if (ret == SA_OK && group != NULL)
4282                 ret = get_protocol_list(options, group);
4283 
4284         /*
4285          * If there was an error, we won't have a complete list so
4286          * abandon everything.  The caller will have to deal with the
4287          * issue.
4288          */
4289         if (ret != SA_OK) {
4290                 sa_free_protoset(options);
4291                 options = NULL;
4292         }
4293         return (options);
4294 }
4295 
4296 /*
4297  * sa_enable_resource, protocol)
4298  *      Disable the specified share to the specified protocol.
4299  *      If protocol is NULL, then all protocols.
4300  */
4301 int
4302 sa_enable_resource(sa_resource_t resource, char *protocol)
4303 {
4304         int ret = SA_OK;
4305 
4306         if (protocol != NULL) {
4307                 ret = sa_proto_share_resource(protocol, resource);
4308         } else {
4309                 sa_optionset_t protoset;
4310                 sa_property_t prop;
4311                 char *proto;
4312                 int err;
4313 
4314                 /* need to do all protocols */
4315                 protoset = sa_get_active_protocols(resource);
4316                 if (protoset == NULL)
4317                         return (SA_NO_MEMORY);
4318                 for (prop = sa_get_property(protoset, NULL);
4319                     prop != NULL;
4320                     prop = sa_get_next_property(prop)) {
4321                         proto = sa_get_property_attr(prop, "type");
4322                         if (proto == NULL) {
4323                                 ret = SA_NO_MEMORY;
4324                                 continue;
4325                         }
4326                         err = sa_proto_share_resource(proto, resource);
4327                         if (err != SA_OK)
4328                                 ret = err;
4329                         sa_free_attr_string(proto);
4330                 }
4331                 sa_free_protoset(protoset);
4332         }
4333         if (ret == SA_OK)
4334                 (void) sa_set_resource_attr(resource, "shared", NULL);
4335 
4336         return (ret);
4337 }
4338 
4339 /*
4340  * sa_disable_resource(resource, protocol)
4341  *
4342  *      Disable the specified share for the specified protocol.  If
4343  *      protocol is NULL, then all protocols.  If the underlying
4344  *      protocol doesn't implement disable at the resource level, we
4345  *      disable at the share level.
4346  */
4347 int
4348 sa_disable_resource(sa_resource_t resource, char *protocol)
4349 {
4350         int ret = SA_OK;
4351 
4352         if (protocol != NULL) {
4353                 ret = sa_proto_unshare_resource(protocol, resource);
4354                 if (ret == SA_NOT_IMPLEMENTED) {
4355                         sa_share_t parent;
4356                         /*
4357                          * The protocol doesn't implement unshare
4358                          * resource. That implies that resource names are
4359                          * simple aliases for this protocol so we need to
4360                          * unshare the share.
4361                          */
4362                         parent = sa_get_resource_parent(resource);
4363                         if (parent != NULL)
4364                                 ret = sa_disable_share(parent, protocol);
4365                         else
4366                                 ret = SA_CONFIG_ERR;
4367                 }
4368         } else {
4369                 sa_optionset_t protoset;
4370                 sa_property_t prop;
4371                 char *proto;
4372                 int err;
4373 
4374                 /* need to do all protocols */
4375                 protoset = sa_get_active_protocols(resource);
4376                 if (protoset == NULL)
4377                         return (SA_NO_MEMORY);
4378                 for (prop = sa_get_property(protoset, NULL);
4379                     prop != NULL;
4380                     prop = sa_get_next_property(prop)) {
4381                         proto = sa_get_property_attr(prop, "type");
4382                         if (proto == NULL) {
4383                                 ret = SA_NO_MEMORY;
4384                                 continue;
4385                         }
4386                         err = sa_proto_unshare_resource(proto, resource);
4387                         if (err == SA_NOT_SUPPORTED) {
4388                                 sa_share_t parent;
4389                                 parent = sa_get_resource_parent(resource);
4390                                 if (parent != NULL)
4391                                         err = sa_disable_share(parent, proto);
4392                                 else
4393                                         err = SA_CONFIG_ERR;
4394                         }
4395                         if (err != SA_OK)
4396                                 ret = err;
4397                         sa_free_attr_string(proto);
4398                 }
4399                 sa_free_protoset(protoset);
4400         }
4401         if (ret == SA_OK)
4402                 (void) sa_set_resource_attr(resource, "shared", NULL);
4403 
4404         return (ret);
4405 }
4406 
4407 /*
4408  * sa_set_resource_description(resource, content)
4409  *
4410  * Set the description of share to content.
4411  */
4412 
4413 int
4414 sa_set_resource_description(sa_resource_t resource, char *content)
4415 {
4416         xmlNodePtr node;
4417         sa_group_t group;
4418         sa_share_t share;
4419         int ret = SA_OK;
4420 
4421         for (node = ((xmlNodePtr)resource)->children;
4422             node != NULL;
4423             node = node->next) {
4424                 if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) {
4425                         break;
4426                 }
4427         }
4428 
4429         /* no existing description but want to add */
4430         if (node == NULL && content != NULL) {
4431                 /* add a description */
4432                 node = _sa_set_share_description(resource, content);
4433         } else if (node != NULL && content != NULL) {
4434                 /* update a description */
4435                 xmlNodeSetContent(node, (xmlChar *)content);
4436         } else if (node != NULL && content == NULL) {
4437                 /* remove an existing description */
4438                 xmlUnlinkNode(node);
4439                 xmlFreeNode(node);
4440         }
4441 
4442         share = sa_get_resource_parent(resource);
4443         group = sa_get_parent_group(share);
4444         if (group != NULL &&
4445             sa_is_persistent(share) && (!sa_group_is_zfs(group))) {
4446                 sa_handle_impl_t impl_handle;
4447                 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
4448                 if (impl_handle != NULL)
4449                         ret = sa_commit_share(impl_handle->scfhandle,
4450                             group, share);
4451                 else
4452                         ret = SA_SYSTEM_ERR;
4453         }
4454         return (ret);
4455 }
4456 
4457 /*
4458  * sa_get_resource_description(share)
4459  *
4460  * Return the description text for the specified share if it
4461  * exists. NULL if no description exists.
4462  */
4463 
4464 char *
4465 sa_get_resource_description(sa_resource_t resource)
4466 {
4467         xmlChar *description = NULL;
4468         xmlNodePtr node;
4469 
4470         for (node = ((xmlNodePtr)resource)->children; node != NULL;
4471             node = node->next) {
4472                 if (xmlStrcmp(node->name, (xmlChar *)"description") == 0)
4473                         break;
4474         }
4475         if (node != NULL) {
4476                 description = xmlNodeGetContent(node);
4477                 fixproblemchars((char *)description);
4478         }
4479         return ((char *)description);
4480 }