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