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