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