13034Sdougm /*
23034Sdougm  * CDDL HEADER START
33034Sdougm  *
43034Sdougm  * The contents of this file are subject to the terms of the
53034Sdougm  * Common Development and Distribution License (the "License").
63034Sdougm  * You may not use this file except in compliance with the License.
73034Sdougm  *
83034Sdougm  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93034Sdougm  * or http://www.opensolaris.org/os/licensing.
103034Sdougm  * See the License for the specific language governing permissions
113034Sdougm  * and limitations under the License.
123034Sdougm  *
133034Sdougm  * When distributing Covered Code, include this CDDL HEADER in each
143034Sdougm  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153034Sdougm  * If applicable, add the following below this CDDL HEADER, with the
163034Sdougm  * fields enclosed by brackets "[]" replaced with your own identifying
173034Sdougm  * information: Portions Copyright [yyyy] [name of copyright owner]
183034Sdougm  *
193034Sdougm  * CDDL HEADER END
203034Sdougm  */
213034Sdougm 
223034Sdougm /*
233348Sdougm  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
243034Sdougm  * Use is subject to license terms.
253034Sdougm  */
263034Sdougm 
273034Sdougm #pragma ident	"%Z%%M%	%I%	%E% SMI"
283034Sdougm 
293034Sdougm /*
303034Sdougm  * Share control API
313034Sdougm  */
323034Sdougm #include <stdio.h>
333034Sdougm #include <string.h>
343034Sdougm #include <ctype.h>
353034Sdougm #include <sys/types.h>
363034Sdougm #include <sys/stat.h>
373663Sdougm #include <fcntl.h>
383034Sdougm #include <unistd.h>
393034Sdougm #include <libxml/parser.h>
403034Sdougm #include <libxml/tree.h>
413034Sdougm #include "libshare.h"
423034Sdougm #include "libshare_impl.h"
433034Sdougm #include <libscf.h>
443034Sdougm #include "scfutil.h"
453034Sdougm #include <ctype.h>
463034Sdougm #include <libintl.h>
473910Sdougm #include <thread.h>
483910Sdougm #include <synch.h>
493034Sdougm 
503034Sdougm #if _NOT_SMF
513034Sdougm #define	CONFIG_FILE	"/var/tmp/share.cfg"
523034Sdougm #define	CONFIG_FILE_TMP	"/var/tmp/share.cfg.tmp"
533034Sdougm #endif
543034Sdougm #define	TSTAMP(tm)	(uint64_t)(((uint64_t)tm.tv_sec << 32) | \
553034Sdougm 					(tm.tv_nsec & 0xffffffff))
563034Sdougm 
573663Sdougm #define	DFS_LOCK_FILE	"/etc/dfs/fstypes"
583663Sdougm 
593034Sdougm /*
603034Sdougm  * internal data structures
613034Sdougm  */
623034Sdougm 
633034Sdougm extern struct sa_proto_plugin *sap_proto_list;
643034Sdougm 
653034Sdougm /* current SMF/SVC repository handle */
663910Sdougm extern void getlegacyconfig(sa_handle_t, char *, xmlNodePtr *);
673910Sdougm extern int gettransients(sa_handle_impl_t, xmlNodePtr *);
683034Sdougm extern int sa_valid_property(void *, char *, sa_property_t);
693034Sdougm extern char *sa_fstype(char *);
703034Sdougm extern int sa_is_share(void *);
713034Sdougm extern ssize_t scf_max_name_len; /* defined in scfutil during initialization */
723034Sdougm extern int sa_group_is_zfs(sa_group_t);
733034Sdougm extern int sa_path_is_zfs(char *);
743034Sdougm extern int sa_zfs_set_sharenfs(sa_group_t, char *, int);
753910Sdougm extern void update_legacy_config(sa_handle_t);
763034Sdougm extern int issubdir(char *, char *);
773910Sdougm extern void sa_zfs_init(sa_handle_impl_t);
783910Sdougm extern void sa_zfs_fini(sa_handle_impl_t);
793663Sdougm extern void sablocksigs(sigset_t *);
803663Sdougm extern void saunblocksigs(sigset_t *);
813034Sdougm 
823910Sdougm /*
833910Sdougm  * Data structures for finding/managing the document root to access
843910Sdougm  * handle mapping. The list isn't expected to grow very large so a
853910Sdougm  * simple list is acceptable. The purpose is to provide a way to start
863910Sdougm  * with a group or share and find the library handle needed for
873910Sdougm  * various operations.
883910Sdougm  */
893910Sdougm mutex_t sa_global_lock;
903910Sdougm struct doc2handle {
913910Sdougm 	struct doc2handle	*next;
923910Sdougm 	xmlNodePtr		root;
933910Sdougm 	sa_handle_impl_t	handle;
943910Sdougm };
953910Sdougm 
963910Sdougm static struct doc2handle *sa_global_handles = NULL;
973034Sdougm 
983034Sdougm /* helper functions */
993034Sdougm 
1003910Sdougm /*
1013910Sdougm  * sa_errorstr(err)
1023910Sdougm  *
1033910Sdougm  * convert an error value to an error string
1043910Sdougm  */
1053910Sdougm 
1063034Sdougm char *
1073034Sdougm sa_errorstr(int err)
1083034Sdougm {
1093034Sdougm 	static char errstr[32];
1103034Sdougm 	char *ret = NULL;
1113034Sdougm 
1123034Sdougm 	switch (err) {
1133034Sdougm 	case SA_OK:
1143407Sdougm 	    ret = dgettext(TEXT_DOMAIN, "ok");
1153034Sdougm 	    break;
1163034Sdougm 	case SA_NO_SUCH_PATH:
1173407Sdougm 	    ret = dgettext(TEXT_DOMAIN, "path doesn't exist");
1183034Sdougm 	    break;
1193034Sdougm 	case SA_NO_MEMORY:
1203407Sdougm 	    ret = dgettext(TEXT_DOMAIN, "no memory");
1213034Sdougm 	    break;
1223034Sdougm 	case SA_DUPLICATE_NAME:
1233407Sdougm 	    ret = dgettext(TEXT_DOMAIN, "name in use");
1243034Sdougm 	    break;
1253034Sdougm 	case SA_BAD_PATH:
1263407Sdougm 	    ret = dgettext(TEXT_DOMAIN, "bad path");
1273034Sdougm 	    break;
1283034Sdougm 	case SA_NO_SUCH_GROUP:
1293407Sdougm 	    ret = dgettext(TEXT_DOMAIN, "no such group");
1303034Sdougm 	    break;
1313034Sdougm 	case SA_CONFIG_ERR:
1323407Sdougm 	    ret = dgettext(TEXT_DOMAIN, "configuration error");
1333034Sdougm 	    break;
1343034Sdougm 	case SA_SYSTEM_ERR:
1353407Sdougm 	    ret = dgettext(TEXT_DOMAIN, "system error");
1363034Sdougm 	    break;
1373034Sdougm 	case SA_SYNTAX_ERR:
1383407Sdougm 	    ret = dgettext(TEXT_DOMAIN, "syntax error");
1393034Sdougm 	    break;
1403034Sdougm 	case SA_NO_PERMISSION:
1413407Sdougm 	    ret = dgettext(TEXT_DOMAIN, "no permission");
1423034Sdougm 	    break;
1433034Sdougm 	case SA_BUSY:
1443407Sdougm 	    ret = dgettext(TEXT_DOMAIN, "busy");
1453034Sdougm 	    break;
1463034Sdougm 	case SA_NO_SUCH_PROP:
1473407Sdougm 	    ret = dgettext(TEXT_DOMAIN, "no such property");
1483034Sdougm 	    break;
1493034Sdougm 	case SA_INVALID_NAME:
1503407Sdougm 	    ret = dgettext(TEXT_DOMAIN, "invalid name");
1513034Sdougm 	    break;
1523034Sdougm 	case SA_INVALID_PROTOCOL:
1533407Sdougm 	    ret = dgettext(TEXT_DOMAIN, "invalid protocol");
1543034Sdougm 	    break;
1553034Sdougm 	case SA_NOT_ALLOWED:
1563407Sdougm 	    ret = dgettext(TEXT_DOMAIN, "operation not allowed");
1573034Sdougm 	    break;
1583034Sdougm 	case SA_BAD_VALUE:
1593407Sdougm 	    ret = dgettext(TEXT_DOMAIN, "bad property value");
1603034Sdougm 	    break;
1613034Sdougm 	case SA_INVALID_SECURITY:
1623407Sdougm 	    ret = dgettext(TEXT_DOMAIN, "invalid security type");
1633034Sdougm 	    break;
1643034Sdougm 	case SA_NO_SUCH_SECURITY:
1653407Sdougm 	    ret = dgettext(TEXT_DOMAIN, "security type not found");
1663034Sdougm 	    break;
1673034Sdougm 	case SA_VALUE_CONFLICT:
1683407Sdougm 	    ret = dgettext(TEXT_DOMAIN, "property value conflict");
1693034Sdougm 	    break;
1703034Sdougm 	case SA_NOT_IMPLEMENTED:
1713407Sdougm 	    ret = dgettext(TEXT_DOMAIN, "not implemented");
1723034Sdougm 	    break;
1733034Sdougm 	case SA_INVALID_PATH:
1743407Sdougm 	    ret = dgettext(TEXT_DOMAIN, "invalid path");
1753034Sdougm 	    break;
1763034Sdougm 	case SA_NOT_SUPPORTED:
1773407Sdougm 	    ret = dgettext(TEXT_DOMAIN, "operation not supported");
1783034Sdougm 	    break;
1793034Sdougm 	case SA_PROP_SHARE_ONLY:
1803407Sdougm 	    ret = dgettext(TEXT_DOMAIN, "property not valid for group");
1813034Sdougm 	    break;
1823034Sdougm 	case SA_NOT_SHARED:
1833407Sdougm 	    ret = dgettext(TEXT_DOMAIN, "not shared");
1843034Sdougm 	    break;
1853034Sdougm 	default:
1863034Sdougm 	    (void) snprintf(errstr, sizeof (errstr),
1873407Sdougm 				dgettext(TEXT_DOMAIN, "unknown %d"), err);
1883034Sdougm 	    ret = errstr;
1893034Sdougm 	}
1903034Sdougm 	return (ret);
1913034Sdougm }
1923034Sdougm 
1933034Sdougm /*
1943910Sdougm  * Document root to active handle mapping functions.  These are only
1953910Sdougm  * used internally. A mutex is used to prevent access while the list
1963910Sdougm  * is changing. In general, the list will be relatively short - one
1973910Sdougm  * item per thread that has called sa_init().
1983910Sdougm  */
1993910Sdougm 
2003910Sdougm sa_handle_impl_t
2013910Sdougm get_handle_for_root(xmlNodePtr root)
2023910Sdougm {
2033910Sdougm 	struct doc2handle *item;
2043910Sdougm 
2053910Sdougm 	(void) mutex_lock(&sa_global_lock);
2063910Sdougm 	for (item = sa_global_handles; item != NULL; item = item->next) {
2073910Sdougm 	    if (item->root == root)
2083910Sdougm 		break;
2093910Sdougm 	}
2103910Sdougm 	(void) mutex_unlock(&sa_global_lock);
2113910Sdougm 	if (item != NULL)
2123910Sdougm 	    return (item->handle);
2133910Sdougm 	return (NULL);
2143910Sdougm }
2153910Sdougm 
2163910Sdougm static int
2173910Sdougm add_handle_for_root(xmlNodePtr root, sa_handle_impl_t handle)
2183910Sdougm {
2193910Sdougm 	struct doc2handle *item;
2203910Sdougm 	int ret = SA_NO_MEMORY;
2213910Sdougm 
2223910Sdougm 	item = (struct doc2handle *)calloc(sizeof (struct doc2handle), 1);
2233910Sdougm 	if (item != NULL) {
2243910Sdougm 	    item->root = root;
2253910Sdougm 	    item->handle = handle;
2263910Sdougm 	    (void) mutex_lock(&sa_global_lock);
2273910Sdougm 	    item->next = sa_global_handles;
2283910Sdougm 	    sa_global_handles = item;
2293910Sdougm 	    (void) mutex_unlock(&sa_global_lock);
2303910Sdougm 	    ret = SA_OK;
2313910Sdougm 	}
2323910Sdougm 	return (ret);
2333910Sdougm }
2343910Sdougm 
2353910Sdougm /*
2363910Sdougm  * remove_handle_for_root(root)
2373910Sdougm  *
2383910Sdougm  * Walks the list of handles and removes the one for this "root" from
2393910Sdougm  * the list. It is up to the caller to free the data.
2403910Sdougm  */
2413910Sdougm 
2423910Sdougm static void
2433910Sdougm remove_handle_for_root(xmlNodePtr root)
2443910Sdougm {
2453910Sdougm 	struct doc2handle *item, *prev;
2463910Sdougm 
2473910Sdougm 	(void) mutex_lock(&sa_global_lock);
2483910Sdougm 	for (prev = NULL, item = sa_global_handles; item != NULL;
2493910Sdougm 		item = item->next) {
2503910Sdougm 	    if (item->root == root) {
2513910Sdougm 		if (prev == NULL) {
2523910Sdougm 		    /* first in the list */
2533910Sdougm 		    sa_global_handles = sa_global_handles->next;
2543910Sdougm 		} else {
2553910Sdougm 		    prev->next = item->next;
2563910Sdougm 		}
2573910Sdougm 		/* Item is out of the list so free the list structure */
2583910Sdougm 		free(item);
2593910Sdougm 		break;
2603910Sdougm 	    }
2613910Sdougm 	    prev = item;
2623910Sdougm 	}
2633910Sdougm 	(void) mutex_unlock(&sa_global_lock);
2643910Sdougm }
2653910Sdougm 
2663910Sdougm /*
2673910Sdougm  * sa_find_group_handle(sa_group_t group)
2683910Sdougm  *
2693910Sdougm  * Find the sa_handle_t for the configuration associated with this
2703910Sdougm  * group.
2713910Sdougm  */
2723910Sdougm sa_handle_t
2733910Sdougm sa_find_group_handle(sa_group_t group)
2743910Sdougm {
2753910Sdougm 	xmlNodePtr node = (xmlNodePtr)group;
2763910Sdougm 	sa_handle_t handle;
2773910Sdougm 
2783910Sdougm 	while (node != NULL) {
2793910Sdougm 	    if (strcmp((char *)(node->name), "sharecfg") == 0) {
2803910Sdougm 		/* have the root so get the handle */
2813910Sdougm 		handle = (sa_handle_t)get_handle_for_root(node);
2823910Sdougm 		return (handle);
2833910Sdougm 	    }
2843910Sdougm 	    node = node->parent;
2853910Sdougm 	}
2863910Sdougm 	return (NULL);
2873910Sdougm }
2883910Sdougm 
2893910Sdougm /*
2903034Sdougm  * set_legacy_timestamp(root, path, timevalue)
2913034Sdougm  *
2923034Sdougm  * add the current timestamp value to the configuration for use in
2933034Sdougm  * determining when to update the legacy files.  For SMF, this
2943034Sdougm  * property is kept in default/operation/legacy_timestamp
2953034Sdougm  */
2963034Sdougm 
2973034Sdougm static void
2983034Sdougm set_legacy_timestamp(xmlNodePtr root, char *path, uint64_t tval)
2993034Sdougm {
3003034Sdougm 	xmlNodePtr node;
3013034Sdougm 	xmlChar *lpath = NULL;
3023910Sdougm 	sa_handle_impl_t handle;
3033910Sdougm 
3043910Sdougm 	/* Have to have a handle or else we weren't initialized. */
3053910Sdougm 	handle = get_handle_for_root(root);
3063910Sdougm 	if (handle == NULL)
3073910Sdougm 	    return;
3083034Sdougm 
3093034Sdougm 	for (node = root->xmlChildrenNode; node != NULL;
3103034Sdougm 		node = node->next) {
3113034Sdougm 	    if (xmlStrcmp(node->name, (xmlChar *)"legacy") == 0) {
3123034Sdougm 		/* a possible legacy node for this path */
3133034Sdougm 		lpath = xmlGetProp(node, (xmlChar *)"path");
3143034Sdougm 		if (lpath != NULL && xmlStrcmp(lpath, (xmlChar *)path) == 0) {
3153034Sdougm 		    xmlFree(lpath);
3163034Sdougm 		    break;
3173034Sdougm 		}
3183034Sdougm 		if (lpath != NULL)
3193034Sdougm 		    xmlFree(lpath);
3203034Sdougm 	    }
3213034Sdougm 	}
3223034Sdougm 	if (node == NULL) {
3233034Sdougm 	    /* need to create the first legacy timestamp node */
3243034Sdougm 	    node = xmlNewChild(root, NULL, (xmlChar *)"legacy", NULL);
3253034Sdougm 	}
3263034Sdougm 	if (node != NULL) {
3273034Sdougm 	    char tstring[32];
3283034Sdougm 	    int ret;
3293034Sdougm 
3303034Sdougm 	    (void) snprintf(tstring, sizeof (tstring), "%lld", tval);
3313034Sdougm 	    xmlSetProp(node, (xmlChar *)"timestamp", (xmlChar *)tstring);
3323034Sdougm 	    xmlSetProp(node, (xmlChar *)"path", (xmlChar *)path);
3333034Sdougm 	    /* now commit to SMF */
3343910Sdougm 	    ret = sa_get_instance(handle->scfhandle, "default");
3353034Sdougm 	    if (ret == SA_OK) {
3363910Sdougm 		ret = sa_start_transaction(handle->scfhandle, "operation");
3373034Sdougm 		if (ret == SA_OK) {
3383910Sdougm 		    ret = sa_set_property(handle->scfhandle, "legacy-timestamp",
3393034Sdougm 					    tstring);
3403034Sdougm 		    if (ret == SA_OK) {
3413910Sdougm 			(void) sa_end_transaction(handle->scfhandle);
3423034Sdougm 		    } else {
3433910Sdougm 			sa_abort_transaction(handle->scfhandle);
3443034Sdougm 		    }
3453034Sdougm 		}
3463034Sdougm 	    }
3473034Sdougm 	}
3483034Sdougm }
3493034Sdougm 
3503034Sdougm /*
3513034Sdougm  * is_shared(share)
3523034Sdougm  *
3533034Sdougm  * determine if the specified share is currently shared or not.
3543034Sdougm  */
3553034Sdougm static int
3563034Sdougm is_shared(sa_share_t share)
3573034Sdougm {
3583034Sdougm 	char *shared;
3593034Sdougm 	int result = 0; /* assume not */
3603034Sdougm 
3613034Sdougm 	shared = sa_get_share_attr(share, "shared");
3623034Sdougm 	if (shared != NULL) {
3633034Sdougm 	    if (strcmp(shared, "true") == 0)
3643034Sdougm 		result = 1;
3653034Sdougm 	    sa_free_attr_string(shared);
3663034Sdougm 	}
3673034Sdougm 	return (result);
3683034Sdougm }
3693034Sdougm 
3703034Sdougm /*
3713663Sdougm  * checksubdirgroup(group, newpath, strictness)
3723348Sdougm  *
3733663Sdougm  * check all the specified newpath against all the paths in the
3743663Sdougm  * group. This is a helper function for checksubdir to make it easier
3753663Sdougm  * to also check ZFS subgroups.
3763663Sdougm  * The strictness values mean:
3773348Sdougm  * SA_CHECK_NORMAL == only check newpath against shares that are active
3783348Sdougm  * SA_CHECK_STRICT == check newpath against both active shares and those
3793348Sdougm  *		      stored in the repository
3803034Sdougm  */
3813034Sdougm static int
3823663Sdougm checksubdirgroup(sa_group_t group, char *newpath, int strictness)
3833034Sdougm {
3843034Sdougm 	sa_share_t share;
3853663Sdougm 	char *path;
3863663Sdougm 	int issub = SA_OK;
3873034Sdougm 
3883663Sdougm 	for (share = sa_get_share(group, NULL); share != NULL;
3893663Sdougm 	    share = sa_get_next_share(share)) {
3903034Sdougm 		/*
3913034Sdougm 		 * The original behavior of share never checked
3923034Sdougm 		 * against the permanent configuration
3933034Sdougm 		 * (/etc/dfs/dfstab).  PIT has a number of cases where
3943034Sdougm 		 * it depends on this older behavior even though it
3953034Sdougm 		 * could be considered incorrect.  We may tighten this
3963034Sdougm 		 * up in the future.
3973034Sdougm 		 */
3983663Sdougm 	    if (strictness == SA_CHECK_NORMAL && !is_shared(share))
3993663Sdougm 		continue;
4003034Sdougm 
4013663Sdougm 	    path = sa_get_share_attr(share, "path");
4023348Sdougm 		/*
4033348Sdougm 		 * If path is NULL, then a share is in the process of
4043348Sdougm 		 * construction or someone has modified the property
4053663Sdougm 		 * group inappropriately. It should be
4063663Sdougm 		 * ignored. issubdir() comes from the original share
4073663Sdougm 		 * implementation and does the difficult part of
4083663Sdougm 		 * checking subdirectories.
4093348Sdougm 		 */
4103663Sdougm 	    if (path == NULL)
4113663Sdougm 		continue;
4123663Sdougm 	    if (newpath != NULL &&
4133663Sdougm 		(strcmp(path, newpath) == 0 || issubdir(newpath, path) ||
4143663Sdougm 		issubdir(path, newpath))) {
4153034Sdougm 		sa_free_attr_string(path);
4163034Sdougm 		path = NULL;
4173663Sdougm 		issub = SA_INVALID_PATH;
4183663Sdougm 		break;
4193663Sdougm 	    }
4203663Sdougm 	    sa_free_attr_string(path);
4213663Sdougm 	    path = NULL;
4223663Sdougm 	}
4233663Sdougm 	return (issub);
4243663Sdougm }
4253663Sdougm 
4263663Sdougm /*
4273663Sdougm  * checksubdir(newpath, strictness)
4283663Sdougm  *
4293663Sdougm  * checksubdir determines if the specified path (newpath) is a
4303663Sdougm  * subdirectory of another share. It calls checksubdirgroup() to do
4313663Sdougm  * the complicated work. The strictness parameter determines how
4323663Sdougm  * strict a check to make against the path. The strictness values
4333663Sdougm  * mean: SA_CHECK_NORMAL == only check newpath against shares that are
4343663Sdougm  * active SA_CHECK_STRICT == check newpath against both active shares
4353663Sdougm  * and those * stored in the repository
4363663Sdougm  */
4373663Sdougm static int
4383910Sdougm checksubdir(sa_handle_t handle, char *newpath, int strictness)
4393663Sdougm {
4403663Sdougm 	sa_group_t group;
4413663Sdougm 	int issub;
4423663Sdougm 	char *path = NULL;
4433663Sdougm 
4443910Sdougm 	for (issub = 0, group = sa_get_group(handle, NULL);
4453663Sdougm 		group != NULL && !issub;
4463663Sdougm 		group = sa_get_next_group(group)) {
4473663Sdougm 	    if (sa_group_is_zfs(group)) {
4483663Sdougm 		sa_group_t subgroup;
4493663Sdougm 		for (subgroup = sa_get_sub_group(group);
4503663Sdougm 		    subgroup != NULL && !issub;
4513663Sdougm 		    subgroup = sa_get_next_group(subgroup))
4523663Sdougm 		    issub = checksubdirgroup(subgroup, newpath, strictness);
4533663Sdougm 	    } else {
4543663Sdougm 		issub = checksubdirgroup(group, newpath, strictness);
4553034Sdougm 	    }
4563034Sdougm 	}
4573034Sdougm 	if (path != NULL)
4583034Sdougm 	    sa_free_attr_string(path);
4593034Sdougm 	return (issub);
4603034Sdougm }
4613034Sdougm 
4623034Sdougm /*
4633348Sdougm  * validpath(path, strictness)
4643034Sdougm  * determine if the provided path is valid for a share. It shouldn't
4653034Sdougm  * be a sub-dir of an already shared path or the parent directory of a
4663034Sdougm  * share path.
4673034Sdougm  */
4683034Sdougm static int
4693910Sdougm validpath(sa_handle_t handle, char *path, int strictness)
4703034Sdougm {
4713034Sdougm 	int error = SA_OK;
4723034Sdougm 	struct stat st;
4733034Sdougm 	sa_share_t share;
4743034Sdougm 	char *fstype;
4753034Sdougm 
4763034Sdougm 	if (*path != '/') {
4773034Sdougm 	    return (SA_BAD_PATH);
4783034Sdougm 	}
4793034Sdougm 	if (stat(path, &st) < 0) {
4803034Sdougm 	    error = SA_NO_SUCH_PATH;
4813034Sdougm 	} else {
4823910Sdougm 	    share = sa_find_share(handle, path);
4833034Sdougm 	    if (share != NULL) {
4843034Sdougm 		error = SA_DUPLICATE_NAME;
4853034Sdougm 	    }
4863034Sdougm 	    if (error == SA_OK) {
4873034Sdougm 		/*
4883034Sdougm 		 * check for special case with file system that might
4893034Sdougm 		 * have restrictions.  For now, ZFS is the only case
4903034Sdougm 		 * since it has its own idea of how to configure
4913034Sdougm 		 * shares. We do this before subdir checking since
4923034Sdougm 		 * things like ZFS will do that for us. This should
4933034Sdougm 		 * also be done via plugin interface.
4943034Sdougm 		 */
4953034Sdougm 		fstype = sa_fstype(path);
4963034Sdougm 		if (fstype != NULL && strcmp(fstype, "zfs") == 0) {
4973910Sdougm 		    if (sa_zfs_is_shared(handle, path))
4983663Sdougm 			error = SA_INVALID_NAME;
4993034Sdougm 		}
5003034Sdougm 		if (fstype != NULL)
5013034Sdougm 		    sa_free_fstype(fstype);
5023034Sdougm 	    }
5033034Sdougm 	    if (error == SA_OK) {
5043910Sdougm 		error = checksubdir(handle, path, strictness);
5053034Sdougm 	    }
5063034Sdougm 	}
5073034Sdougm 	return (error);
5083034Sdougm }
5093034Sdougm 
5103034Sdougm /*
5113034Sdougm  * check to see if group/share is persistent.
5123034Sdougm  */
5133034Sdougm static int
5143034Sdougm is_persistent(sa_group_t group)
5153034Sdougm {
5163034Sdougm 	char *type;
5173034Sdougm 	int persist = 1;
5183034Sdougm 
5193034Sdougm 	type = sa_get_group_attr(group, "type");
5203034Sdougm 	if (type != NULL && strcmp(type, "transient") == 0)
5213034Sdougm 	    persist = 0;
5223034Sdougm 	if (type != NULL)
5233034Sdougm 	    sa_free_attr_string(type);
5243034Sdougm 	return (persist);
5253034Sdougm }
5263034Sdougm 
5273034Sdougm /*
5283034Sdougm  * sa_valid_group_name(name)
5293034Sdougm  *
5303034Sdougm  * check that the "name" contains only valid characters and otherwise
5313034Sdougm  * fits the required naming conventions. Valid names must start with
5323034Sdougm  * an alphabetic and the remainder may consist of only alphanumeric
5333034Sdougm  * plus the '-' and '_' characters. This name limitation comes from
5343034Sdougm  * inherent limitations in SMF.
5353034Sdougm  */
5363034Sdougm 
5373034Sdougm int
5383034Sdougm sa_valid_group_name(char *name)
5393034Sdougm {
5403034Sdougm 	int ret = 1;
5413034Sdougm 	ssize_t len;
5423034Sdougm 
5433034Sdougm 	if (name != NULL && isalpha(*name)) {
5443034Sdougm 	    char c;
5453034Sdougm 	    len = strlen(name);
5463034Sdougm 	    if (len < (scf_max_name_len - sizeof ("group:"))) {
5473034Sdougm 		for (c = *name++; c != '\0' && ret != 0; c = *name++) {
5483034Sdougm 		    if (!isalnum(c) && c != '-' && c != '_')
5493034Sdougm 			ret = 0;
5503034Sdougm 		}
5513034Sdougm 	    } else {
5523034Sdougm 		ret = 0;
5533034Sdougm 	    }
5543034Sdougm 	} else {
5553034Sdougm 	    ret = 0;
5563034Sdougm 	}
5573034Sdougm 	return (ret);
5583034Sdougm }
5593034Sdougm 
5603034Sdougm 
5613034Sdougm /*
5623034Sdougm  * is_zfs_group(group)
5633034Sdougm  *	Determine if the specified group is a ZFS sharenfs group
5643034Sdougm  */
5653034Sdougm static int
5663034Sdougm is_zfs_group(sa_group_t group)
5673034Sdougm {
5683034Sdougm 	int ret = 0;
5693034Sdougm 	xmlNodePtr parent;
5703034Sdougm 	xmlChar *zfs;
5713034Sdougm 
5723034Sdougm 	if (strcmp((char *)((xmlNodePtr)group)->name, "share") == 0) {
5733034Sdougm 	    parent = (xmlNodePtr)sa_get_parent_group(group);
5743034Sdougm 	} else {
5753034Sdougm 	    parent = (xmlNodePtr)group;
5763034Sdougm 	}
5773034Sdougm 	zfs = xmlGetProp(parent, (xmlChar *)"zfs");
5783034Sdougm 	if (zfs != NULL) {
5793034Sdougm 	    xmlFree(zfs);
5803034Sdougm 	    ret = 1;
5813034Sdougm 	}
5823034Sdougm 	return (ret);
5833034Sdougm }
5843034Sdougm 
5853034Sdougm /*
5863034Sdougm  * sa_optionset_name(optionset, oname, len, id)
5873034Sdougm  *	return the SMF name for the optionset. If id is not NULL, it
5883034Sdougm  *	will have the GUID value for a share and should be used
5893034Sdougm  *	instead of the keyword "optionset" which is used for
5903034Sdougm  *	groups. If the optionset doesn't have a protocol type
5913034Sdougm  *	associated with it, "default" is used. This shouldn't happen
5923034Sdougm  *	at this point but may be desirable in the future if there are
5933034Sdougm  *	protocol independent properties added. The name is returned in
5943034Sdougm  *	oname.
5953034Sdougm  */
5963034Sdougm 
5973034Sdougm static int
5983034Sdougm sa_optionset_name(sa_optionset_t optionset, char *oname, size_t len, char *id)
5993034Sdougm {
6003034Sdougm 	char *proto;
6013034Sdougm 
6023034Sdougm 	if (id == NULL)
6033034Sdougm 	    id = "optionset";
6043034Sdougm 
6053034Sdougm 	proto = sa_get_optionset_attr(optionset, "type");
6063034Sdougm 	len = snprintf(oname, len, "%s_%s", id, proto ? proto : "default");
6073034Sdougm 
6083034Sdougm 	if (proto != NULL)
6093034Sdougm 	    sa_free_attr_string(proto);
6103034Sdougm 	return (len);
6113034Sdougm }
6123034Sdougm 
6133034Sdougm /*
6143034Sdougm  * sa_security_name(optionset, oname, len, id)
6153034Sdougm  *
6163034Sdougm  * return the SMF name for the security. If id is not NULL, it will
6173034Sdougm  * have the GUID value for a share and should be used instead of the
6183034Sdougm  * keyword "optionset" which is used for groups. If the optionset
6193034Sdougm  * doesn't have a protocol type associated with it, "default" is
6203034Sdougm  * used. This shouldn't happen at this point but may be desirable in
6213034Sdougm  * the future if there are protocol independent properties added. The
6223034Sdougm  * name is returned in oname. The security type is also encoded into
6233034Sdougm  * the name. In the future, this wil *be handled a bit differently.
6243034Sdougm  */
6253034Sdougm 
6263034Sdougm static int
6273034Sdougm sa_security_name(sa_security_t security, char *oname, size_t len, char *id)
6283034Sdougm {
6293034Sdougm 	char *proto;
6303034Sdougm 	char *sectype;
6313034Sdougm 
6323034Sdougm 	if (id == NULL)
6333034Sdougm 	    id = "optionset";
6343034Sdougm 
6353034Sdougm 	proto = sa_get_security_attr(security, "type");
6363034Sdougm 	sectype = sa_get_security_attr(security, "sectype");
6373034Sdougm 	len = snprintf(oname, len, "%s_%s_%s", id,
6383034Sdougm 			    proto ? proto : "default",
6393034Sdougm 			    sectype ? sectype : "default");
6403034Sdougm 	if (proto != NULL)
6413034Sdougm 	    sa_free_attr_string(proto);
6423034Sdougm 	if (sectype != NULL)
6433034Sdougm 	    sa_free_attr_string(sectype);
6443034Sdougm 	return (len);
6453034Sdougm }
6463034Sdougm 
6473034Sdougm /*
6483348Sdougm  * sa_init(init_service)
6493034Sdougm  *	Initialize the API
6503034Sdougm  *	find all the shared objects
6513034Sdougm  *	init the tables with all objects
6523034Sdougm  *	read in the current configuration
6533034Sdougm  */
6543034Sdougm 
6553910Sdougm sa_handle_t
6563034Sdougm sa_init(int init_service)
6573034Sdougm {
6583034Sdougm 	struct stat st;
6593034Sdougm 	int legacy = 0;
6603034Sdougm 	uint64_t tval = 0;
6613663Sdougm 	int lockfd;
6623663Sdougm 	sigset_t old;
6633663Sdougm 	int updatelegacy = B_FALSE;
6643663Sdougm 	scf_simple_prop_t *prop;
6653910Sdougm 	sa_handle_impl_t handle;
6663910Sdougm 	int err;
6673034Sdougm 
6683910Sdougm 	handle = calloc(sizeof (struct sa_handle_impl), 1);
6693910Sdougm 
6703910Sdougm 	if (handle != NULL) {
6713034Sdougm 	    /* get protocol specific structures */
6723034Sdougm 	    (void) proto_plugin_init();
6733034Sdougm 	    if (init_service & SA_INIT_SHARE_API) {
6743034Sdougm 		/*
6753218Sdougm 		 * initialize access into libzfs. We use this when
6763218Sdougm 		 * collecting info about ZFS datasets and shares.
6773218Sdougm 		 */
6783910Sdougm 		sa_zfs_init(handle);
6793218Sdougm 		/*
6803034Sdougm 		 * since we want to use SMF, initialize an svc handle
6813034Sdougm 		 * and find out what is there.
6823034Sdougm 		 */
6833910Sdougm 		handle->scfhandle = sa_scf_init(handle);
6843910Sdougm 		if (handle->scfhandle != NULL) {
6853663Sdougm 			/*
6863663Sdougm 			 * Need to lock the extraction of the
6873663Sdougm 			 * configuration if the dfstab file has
6883663Sdougm 			 * changed. Lock everything now and release if
6893663Sdougm 			 * not needed.  Use a file that isn't being
6903663Sdougm 			 * manipulated by other parts of the system in
6913663Sdougm 			 * order to not interfere with locking. Using
6923663Sdougm 			 * dfstab doesn't work.
6933663Sdougm 			 */
6943663Sdougm 		    sablocksigs(&old);
6953663Sdougm 		    lockfd = open(DFS_LOCK_FILE, O_RDWR);
6963663Sdougm 		    if (lockfd >= 0) {
6973663Sdougm 			extern int errno;
6983663Sdougm 			errno = 0;
6993663Sdougm 			(void) lockf(lockfd, F_LOCK, 0);
7003663Sdougm 			/*
7013663Sdougm 			 * Check whether we are going to need to merge
7023663Sdougm 			 * any dfstab changes. This is done by
7033663Sdougm 			 * comparing the value of legacy-timestamp
7043663Sdougm 			 * with the current st_ctim of the file. If
7053663Sdougm 			 * they are different, an update is needed and
7063663Sdougm 			 * the file must remain locked until the merge
7073663Sdougm 			 * is done in order to prevent multiple
7083663Sdougm 			 * startups from changing the SMF repository
7093663Sdougm 			 * at the same time.  The first to get the
7103663Sdougm 			 * lock will make any changes before the
7113663Sdougm 			 * others can read the repository.
7123663Sdougm 			 */
7133910Sdougm 			prop = scf_simple_prop_get(handle->scfhandle->handle,
7143663Sdougm 						(const char *)
7153663Sdougm 						    SA_SVC_FMRI_BASE ":default",
7163663Sdougm 						"operation",
7173663Sdougm 						"legacy-timestamp");
7183663Sdougm 			if (prop != NULL) {
7193663Sdougm 			    char *i64;
7203663Sdougm 			    i64 = scf_simple_prop_next_astring(prop);
7213663Sdougm 			    if (i64 != NULL) {
7223663Sdougm 				tval = strtoull(i64, NULL, 0);
7233663Sdougm 			    }
7243663Sdougm 			    if (stat(SA_LEGACY_DFSTAB, &st) >= 0 &&
7253663Sdougm 				tval != TSTAMP(st.st_ctim)) {
7263663Sdougm 				updatelegacy = B_TRUE;
7273663Sdougm 			    }
7283663Sdougm 			} else {
7293663Sdougm 			    /* We haven't set the timestamp before so do it. */
7303663Sdougm 			    updatelegacy = B_TRUE;
7313663Sdougm 			}
7323663Sdougm 		    }
7333663Sdougm 		    if (updatelegacy == B_FALSE) {
7343663Sdougm 			/* Don't need the lock anymore */
7353663Sdougm 			(void) lockf(lockfd, F_ULOCK, 0);
7363663Sdougm 			(void) close(lockfd);
7373663Sdougm 		    }
738*3973Sdougm 
739*3973Sdougm 		/*
740*3973Sdougm 		 * It is essential that the document tree and
741*3973Sdougm 		 * the internal list of roots to handles be
742*3973Sdougm 		 * setup before anything that might try to
743*3973Sdougm 		 * create a new object is called. The document
744*3973Sdougm 		 * tree is the combination of handle->doc and
745*3973Sdougm 		 * handle->tree. This allows searches,
746*3973Sdougm 		 * etc. when all you have is an object in the
747*3973Sdougm 		 * tree.
748*3973Sdougm 		 */
749*3973Sdougm 		    handle->doc = xmlNewDoc((xmlChar *)"1.0");
750*3973Sdougm 		    handle->tree = xmlNewNode(NULL, (xmlChar *)"sharecfg");
751*3973Sdougm 		    if (handle->doc != NULL && handle->tree != NULL) {
752*3973Sdougm 			xmlDocSetRootElement(handle->doc, handle->tree);
753*3973Sdougm 			err = add_handle_for_root(handle->tree, handle);
754*3973Sdougm 			if (err == SA_OK)
755*3973Sdougm 			    err = sa_get_config(handle->scfhandle,
756*3973Sdougm 						    handle->tree, handle);
757*3973Sdougm 		    } else {
758*3973Sdougm 			if (handle->doc != NULL)
759*3973Sdougm 			    xmlFreeDoc(handle->doc);
760*3973Sdougm 			if (handle->tree != NULL)
761*3973Sdougm 			    xmlFreeNode(handle->tree);
762*3973Sdougm 			err = SA_NO_MEMORY;
763*3973Sdougm 		    }
764*3973Sdougm 
7653663Sdougm 		    saunblocksigs(&old);
7663910Sdougm 
7673910Sdougm 		    if (err != SA_OK) {
7683910Sdougm 			/*
769*3973Sdougm 			 * If we couldn't add the tree handle
770*3973Sdougm 			 * to the list, then things are going
771*3973Sdougm 			 * to fail badly. Might as well undo
772*3973Sdougm 			 * everything now and fail the
773*3973Sdougm 			 * sa_init().
7743910Sdougm 			 */
7753910Sdougm 			sa_fini(handle);
7763910Sdougm 			return (NULL);
7773910Sdougm 		    }
7783910Sdougm 
7793034Sdougm 		    if (tval == 0) {
7803034Sdougm 			/* first time so make sure default is setup */
7813034Sdougm 			sa_group_t defgrp;
7823034Sdougm 			sa_optionset_t opt;
7833910Sdougm 			defgrp = sa_get_group(handle, "default");
7843034Sdougm 			if (defgrp != NULL) {
7853034Sdougm 			    opt = sa_get_optionset(defgrp, NULL);
7863034Sdougm 			    if (opt == NULL)
7873034Sdougm 				/* NFS is the default for default */
7883034Sdougm 				opt = sa_create_optionset(defgrp, "nfs");
7893034Sdougm 			}
7903034Sdougm 		    }
791*3973Sdougm 
7923663Sdougm 		    if (updatelegacy == B_TRUE) {
7933663Sdougm 			sablocksigs(&old);
7943910Sdougm 			getlegacyconfig((sa_handle_t)handle,
7953910Sdougm 					    SA_LEGACY_DFSTAB, &handle->tree);
7963034Sdougm 			if (stat(SA_LEGACY_DFSTAB, &st) >= 0)
7973910Sdougm 			    set_legacy_timestamp(handle->tree,
7983034Sdougm 						SA_LEGACY_DFSTAB,
7993034Sdougm 						TSTAMP(st.st_ctim));
8003663Sdougm 			saunblocksigs(&old);
8013663Sdougm 			/* Safe to unlock now to allow others to run */
8023663Sdougm 			(void) lockf(lockfd, F_ULOCK, 0);
8033663Sdougm 			(void) close(lockfd);
8043034Sdougm 		    }
8053910Sdougm 		    legacy |= sa_get_zfs_shares(handle, "zfs");
8063910Sdougm 		    legacy |= gettransients(handle, &handle->tree);
8073034Sdougm 		}
8083034Sdougm 	    }
8093034Sdougm 	}
8103910Sdougm 	return ((sa_handle_t)handle);
8113034Sdougm }
8123034Sdougm 
8133034Sdougm /*
8143910Sdougm  * sa_fini(handle)
8153034Sdougm  *	Uninitialize the API structures including the configuration
8163218Sdougm  *	data structures and ZFS related data.
8173034Sdougm  */
8183034Sdougm 
8193034Sdougm void
8203910Sdougm sa_fini(sa_handle_t handle)
8213034Sdougm {
8223910Sdougm 	sa_handle_impl_t impl_handle = (sa_handle_impl_t)handle;
8233910Sdougm 
8243910Sdougm 	if (impl_handle != NULL) {
8253910Sdougm 		/*
8263910Sdougm 		 * Free the config trees and any other data structures
8273910Sdougm 		 * used in the handle.
8283910Sdougm 		 */
8293910Sdougm 		if (impl_handle->doc != NULL)
8303910Sdougm 			xmlFreeDoc(impl_handle->doc);
8313910Sdougm 		sa_scf_fini(impl_handle->scfhandle);
8323910Sdougm 		sa_zfs_fini(impl_handle);
8333910Sdougm 
8343910Sdougm 		/* Remove and free the entry in the global list. */
8353910Sdougm 		remove_handle_for_root(impl_handle->tree);
8363910Sdougm 
8373910Sdougm 		/* Make sure we free the handle */
8383910Sdougm 		free(impl_handle);
8393910Sdougm 
8403910Sdougm 		/*
8413910Sdougm 		 * If this was the last handle to release, unload the
8423910Sdougm 		 * plugins that were loaded.
8433910Sdougm 		 */
8443910Sdougm 		if (sa_global_handles == NULL)
8453910Sdougm 		    (void) proto_plugin_fini();
8463910Sdougm 
8473034Sdougm 	}
8483034Sdougm }
8493034Sdougm 
8503034Sdougm /*
8513034Sdougm  * sa_get_protocols(char **protocol)
8523034Sdougm  *	Get array of protocols that are supported
8533034Sdougm  *	Returns pointer to an allocated and NULL terminated
8543034Sdougm  *	array of strings.  Caller must free.
8553034Sdougm  *	This really should be determined dynamically.
8563034Sdougm  *	If there aren't any defined, return -1.
8573034Sdougm  *	Use free() to return memory.
8583034Sdougm  */
8593034Sdougm 
8603034Sdougm int
8613034Sdougm sa_get_protocols(char ***protocols)
8623034Sdougm {
8633034Sdougm 	int numproto = -1;
8643034Sdougm 
8653034Sdougm 	if (protocols != NULL) {
8663034Sdougm 	    struct sa_proto_plugin *plug;
8673034Sdougm 	    for (numproto = 0, plug = sap_proto_list; plug != NULL;
8683034Sdougm 		plug = plug->plugin_next) {
8693034Sdougm 		numproto++;
8703034Sdougm 	    }
8713034Sdougm 
8723034Sdougm 	    *protocols = calloc(numproto + 1,  sizeof (char *));
8733034Sdougm 	    if (*protocols != NULL) {
8743034Sdougm 		int ret = 0;
8753034Sdougm 		for (plug = sap_proto_list; plug != NULL;
8763034Sdougm 		    plug = plug->plugin_next) {
8773034Sdougm 		    /* faking for now */
8783034Sdougm 		    (*protocols)[ret++] = plug->plugin_ops->sa_protocol;
8793034Sdougm 		}
8803034Sdougm 	    } else {
8813034Sdougm 		numproto = -1;
8823034Sdougm 	    }
8833034Sdougm 	}
8843034Sdougm 	return (numproto);
8853034Sdougm }
8863034Sdougm 
8873034Sdougm /*
8883034Sdougm  * find_group_by_name(node, group)
8893034Sdougm  *
8903034Sdougm  * search the XML document subtree specified by node to find the group
8913034Sdougm  * specified by group. Searching subtree allows subgroups to be
8923034Sdougm  * searched for.
8933034Sdougm  */
8943034Sdougm 
8953034Sdougm static xmlNodePtr
8963034Sdougm find_group_by_name(xmlNodePtr node, xmlChar *group)
8973034Sdougm {
8983034Sdougm 	xmlChar *name = NULL;
8993034Sdougm 
9003034Sdougm 	for (node = node->xmlChildrenNode; node != NULL;
9013034Sdougm 	    node = node->next) {
9023034Sdougm 	    if (xmlStrcmp(node->name, (xmlChar *)"group") == 0) {
9033034Sdougm 		/* if no groupname, return the first found */
9043034Sdougm 		if (group == NULL)
9053034Sdougm 		    break;
9063034Sdougm 		name = xmlGetProp(node, (xmlChar *)"name");
9073034Sdougm 		if (name != NULL &&
9083034Sdougm 		    xmlStrcmp(name, group) == 0) {
9093034Sdougm 		    break;
9103034Sdougm 		}
9113034Sdougm 		if (name != NULL) {
9123034Sdougm 		    xmlFree(name);
9133034Sdougm 		    name = NULL;
9143034Sdougm 		}
9153034Sdougm 	    }
9163034Sdougm 	}
9173034Sdougm 	if (name != NULL)
9183034Sdougm 	    xmlFree(name);
9193034Sdougm 	return (node);
9203034Sdougm }
9213034Sdougm 
9223034Sdougm /*
9233034Sdougm  * sa_get_group(groupname)
9243034Sdougm  *	Return the "group" specified.  If groupname is NULL,
9253034Sdougm  *	return the first group of the list of groups.
9263034Sdougm  */
9273034Sdougm sa_group_t
9283910Sdougm sa_get_group(sa_handle_t handle, char *groupname)
9293034Sdougm {
9303034Sdougm 	xmlNodePtr node = NULL;
9313034Sdougm 	char *subgroup = NULL;
9323034Sdougm 	char *group = NULL;
9333910Sdougm 	sa_handle_impl_t impl_handle = (sa_handle_impl_t)handle;
9343034Sdougm 
9353910Sdougm 	if (impl_handle != NULL && impl_handle->tree != NULL) {
9363034Sdougm 	    if (groupname != NULL) {
9373034Sdougm 		group = strdup(groupname);
9383034Sdougm 		subgroup = strchr(group, '/');
9393034Sdougm 		if (subgroup != NULL)
9403034Sdougm 		    *subgroup++ = '\0';
9413034Sdougm 	    }
9423910Sdougm 	    node = find_group_by_name(impl_handle->tree, (xmlChar *)group);
9433034Sdougm 	    /* if a subgroup, find it before returning */
9443034Sdougm 	    if (subgroup != NULL && node != NULL) {
9453034Sdougm 		node = find_group_by_name(node, (xmlChar *)subgroup);
9463034Sdougm 	    }
9473034Sdougm 	}
9483034Sdougm 	if (node != NULL && (char *)group != NULL)
9493910Sdougm 	    (void) sa_get_instance(impl_handle->scfhandle, (char *)group);
9503034Sdougm 	if (group != NULL)
9513034Sdougm 	    free(group);
9523034Sdougm 	return ((sa_group_t)(node));
9533034Sdougm }
9543034Sdougm 
9553034Sdougm /*
9563034Sdougm  * sa_get_next_group(group)
9573034Sdougm  *	Return the "next" group after the specified group from
9583034Sdougm  *	the internal group list.  NULL if there are no more.
9593034Sdougm  */
9603034Sdougm sa_group_t
9613034Sdougm sa_get_next_group(sa_group_t group)
9623034Sdougm {
9633034Sdougm 	xmlNodePtr ngroup = NULL;
9643034Sdougm 	if (group != NULL) {
9653034Sdougm 	    for (ngroup = ((xmlNodePtr)group)->next; ngroup != NULL;
9663034Sdougm 		    ngroup = ngroup->next) {
9673034Sdougm 		if (xmlStrcmp(ngroup->name, (xmlChar *)"group") == 0)
9683034Sdougm 		    break;
9693034Sdougm 	    }
9703034Sdougm 	}
9713034Sdougm 	return ((sa_group_t)ngroup);
9723034Sdougm }
9733034Sdougm 
9743034Sdougm /*
9753034Sdougm  * sa_get_share(group, sharepath)
9763034Sdougm  *	Return the share object for the share specified. The share
9773034Sdougm  *	must be in the specified group.  Return NULL if not found.
9783034Sdougm  */
9793034Sdougm sa_share_t
9803034Sdougm sa_get_share(sa_group_t group, char *sharepath)
9813034Sdougm {
9823034Sdougm 	xmlNodePtr node = NULL;
9833034Sdougm 	xmlChar *path;
9843034Sdougm 
9853034Sdougm 	/*
9863034Sdougm 	 * For future scalability, this should end up building a cache
9873034Sdougm 	 * since it will get called regularly by the mountd and info
9883034Sdougm 	 * services.
9893034Sdougm 	 */
9903034Sdougm 	if (group != NULL) {
9913034Sdougm 	    for (node = ((xmlNodePtr)group)->children; node != NULL;
9923034Sdougm 		    node = node->next) {
9933034Sdougm 		if (xmlStrcmp(node->name, (xmlChar *)"share") == 0) {
9943034Sdougm 			if (sharepath == NULL) {
9953034Sdougm 				break;
9963034Sdougm 			} else {
9973034Sdougm 				/* is it the correct share? */
9983034Sdougm 			    path = xmlGetProp(node, (xmlChar *)"path");
9993034Sdougm 			    if (path != NULL &&
10003034Sdougm 				xmlStrcmp(path, (xmlChar *)sharepath) == 0) {
10013034Sdougm 				xmlFree(path);
10023034Sdougm 				break;
10033034Sdougm 			    }
10043034Sdougm 			    xmlFree(path);
10053034Sdougm 			}
10063034Sdougm 		}
10073034Sdougm 	    }
10083034Sdougm 	}
10093034Sdougm 	return ((sa_share_t)node);
10103034Sdougm }
10113034Sdougm 
10123034Sdougm /*
10133034Sdougm  * sa_get_next_share(share)
10143034Sdougm  *	Return the next share following the specified share
10153034Sdougm  *	from the internal list of shares. Returns NULL if there
10163034Sdougm  *	are no more shares.  The list is relative to the same
10173034Sdougm  *	group.
10183034Sdougm  */
10193034Sdougm sa_share_t
10203034Sdougm sa_get_next_share(sa_share_t share)
10213034Sdougm {
10223034Sdougm 	xmlNodePtr node = NULL;
10233034Sdougm 
10243034Sdougm 	if (share != NULL) {
10253034Sdougm 	    for (node = ((xmlNodePtr)share)->next; node != NULL;
10263034Sdougm 		    node = node->next) {
10273034Sdougm 		if (xmlStrcmp(node->name, (xmlChar *)"share") == 0) {
10283034Sdougm 			break;
10293034Sdougm 		}
10303034Sdougm 	    }
10313034Sdougm 	}
10323034Sdougm 	return ((sa_share_t)node);
10333034Sdougm }
10343034Sdougm 
10353034Sdougm /*
10363034Sdougm  * _sa_get_child_node(node, type)
10373034Sdougm  *
10383034Sdougm  * find the child node of the specified node that has "type". This is
10393034Sdougm  * used to implement several internal functions.
10403034Sdougm  */
10413034Sdougm 
10423034Sdougm static xmlNodePtr
10433034Sdougm _sa_get_child_node(xmlNodePtr node, xmlChar *type)
10443034Sdougm {
10453034Sdougm 	xmlNodePtr child;
10463034Sdougm 	for (child = node->xmlChildrenNode; child != NULL;
10473034Sdougm 	    child = child->next)
10483034Sdougm 	    if (xmlStrcmp(child->name, type) == 0)
10493034Sdougm 		return (child);
10503034Sdougm 	return ((xmlNodePtr)NULL);
10513034Sdougm }
10523034Sdougm 
10533034Sdougm /*
10543034Sdougm  *  find_share(group, path)
10553034Sdougm  *
10563034Sdougm  * Search all the shares in the specified group for one that has the
10573034Sdougm  * specified path.
10583034Sdougm  */
10593034Sdougm 
10603034Sdougm static sa_share_t
10613034Sdougm find_share(sa_group_t group, char *sharepath)
10623034Sdougm {
10633034Sdougm 	sa_share_t share;
10643034Sdougm 	char *path;
10653034Sdougm 
10663034Sdougm 	for (share = sa_get_share(group, NULL); share != NULL;
10673034Sdougm 	    share = sa_get_next_share(share)) {
10683034Sdougm 	    path = sa_get_share_attr(share, "path");
10693034Sdougm 	    if (path != NULL && strcmp(path, sharepath) == 0) {
10703034Sdougm 		sa_free_attr_string(path);
10713034Sdougm 		break;
10723034Sdougm 	    }
10733034Sdougm 	    if (path != NULL)
10743034Sdougm 		sa_free_attr_string(path);
10753034Sdougm 	}
10763034Sdougm 	return (share);
10773034Sdougm }
10783034Sdougm 
10793034Sdougm /*
10803034Sdougm  * sa_get_sub_group(group)
10813034Sdougm  *
10823034Sdougm  * Get the first sub-group of group. The sa_get_next_group() function
10833034Sdougm  * can be used to get the rest. This is currently only used for ZFS
10843034Sdougm  * sub-groups but could be used to implement a more general mechanism.
10853034Sdougm  */
10863034Sdougm 
10873034Sdougm sa_group_t
10883034Sdougm sa_get_sub_group(sa_group_t group)
10893034Sdougm {
10903034Sdougm 	return ((sa_group_t)_sa_get_child_node((xmlNodePtr)group,
10913034Sdougm 					    (xmlChar *)"group"));
10923034Sdougm }
10933034Sdougm 
10943034Sdougm /*
10953034Sdougm  * sa_find_share(sharepath)
10963034Sdougm  *	Finds a share regardless of group.  In the future, this
10973034Sdougm  *	function should utilize a cache and hash table of some kind.
10983034Sdougm  *	The current assumption is that a path will only be shared
10993034Sdougm  *	once.  In the future, this may change as implementation of
11003034Sdougm  *	resource names comes into being.
11013034Sdougm  */
11023034Sdougm sa_share_t
11033910Sdougm sa_find_share(sa_handle_t handle, char *sharepath)
11043034Sdougm {
11053034Sdougm 	sa_group_t group;
11063034Sdougm 	sa_group_t zgroup;
11073034Sdougm 	sa_share_t share = NULL;
11083034Sdougm 	int done = 0;
11093034Sdougm 
11103910Sdougm 	for (group = sa_get_group(handle, NULL); group != NULL && !done;
11113034Sdougm 		group = sa_get_next_group(group)) {
11123034Sdougm 	    if (is_zfs_group(group)) {
11133034Sdougm 		for (zgroup = (sa_group_t)_sa_get_child_node((xmlNodePtr)group,
11143034Sdougm 							(xmlChar *)"group");
11153034Sdougm 		    zgroup != NULL; zgroup = sa_get_next_group(zgroup)) {
11163034Sdougm 		    share = find_share(zgroup, sharepath);
11173034Sdougm 		    if (share != NULL)
11183034Sdougm 			break;
11193034Sdougm 		}
11203034Sdougm 	    } else {
11213034Sdougm 		share = find_share(group, sharepath);
11223034Sdougm 	    }
11233034Sdougm 	    if (share != NULL)
11243034Sdougm 		break;
11253034Sdougm 	}
11263034Sdougm 	return (share);
11273034Sdougm }
11283034Sdougm 
11293034Sdougm /*
11303348Sdougm  *  sa_check_path(group, path, strictness)
11313034Sdougm  *
11323034Sdougm  * check that path is a valid path relative to the group.  Currently,
11333034Sdougm  * we are ignoring the group and checking only the NFS rules. Later,
11343034Sdougm  * we may want to use the group to then check against the protocols
11353348Sdougm  * enabled on the group. The strictness values mean:
11363348Sdougm  * SA_CHECK_NORMAL == only check newpath against shares that are active
11373348Sdougm  * SA_CHECK_STRICT == check newpath against both active shares and those
11383348Sdougm  *		      stored in the repository
11393034Sdougm  */
11403034Sdougm 
11413034Sdougm int
11423348Sdougm sa_check_path(sa_group_t group, char *path, int strictness)
11433034Sdougm {
11443910Sdougm 	sa_handle_t handle;
11453910Sdougm 
11463910Sdougm 	handle = sa_find_group_handle(group);
11473910Sdougm 	return (validpath(handle, path, strictness));
11483034Sdougm }
11493034Sdougm 
11503034Sdougm /*
11513034Sdougm  * _sa_add_share(group, sharepath, persist, *error)
11523034Sdougm  *
11533034Sdougm  * common code for all types of add_share. sa_add_share() is the
11543034Sdougm  * public API, we also need to be able to do this when parsing legacy
11553034Sdougm  * files and construction of the internal configuration while
11563034Sdougm  * extracting config info from SMF.
11573034Sdougm  */
11583034Sdougm 
11593034Sdougm sa_share_t
11603034Sdougm _sa_add_share(sa_group_t group, char *sharepath, int persist, int *error)
11613034Sdougm {
11623034Sdougm 	xmlNodePtr node = NULL;
11633034Sdougm 	int err;
11643034Sdougm 
11653034Sdougm 	err  = SA_OK; /* assume success */
11663034Sdougm 
11673034Sdougm 	node = xmlNewChild((xmlNodePtr)group, NULL,
11683034Sdougm 				(xmlChar *)"share", NULL);
11693034Sdougm 	if (node != NULL) {
11703034Sdougm 	    xmlSetProp(node, (xmlChar *)"path", (xmlChar *)sharepath);
11713034Sdougm 	    xmlSetProp(node, (xmlChar *)"type", persist ?
11723034Sdougm 			(xmlChar *)"persist" : (xmlChar *)"transient");
11733034Sdougm 	    if (persist != SA_SHARE_TRANSIENT) {
11743034Sdougm 		/*
11753034Sdougm 		 * persistent shares come in two flavors: SMF and
11763034Sdougm 		 * ZFS. Sort this one out based on target group and
11773034Sdougm 		 * path type. Currently, only NFS is supported in the
11783034Sdougm 		 * ZFS group and it is always on.
11793034Sdougm 		 */
11803034Sdougm 		if (sa_group_is_zfs(group) && sa_path_is_zfs(sharepath)) {
11813034Sdougm 		    err = sa_zfs_set_sharenfs(group, sharepath, 1);
11823034Sdougm 		} else {
11833910Sdougm 		    sa_handle_impl_t impl_handle;
11843910Sdougm 		    impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
11853910Sdougm 		    if (impl_handle != NULL)
11863910Sdougm 			err = sa_commit_share(impl_handle->scfhandle, group,
11873034Sdougm 						(sa_share_t)node);
11883910Sdougm 		    else
11893910Sdougm 			err = SA_SYSTEM_ERR;
11903034Sdougm 		}
11913034Sdougm 	    }
11923034Sdougm 	    if (err == SA_NO_PERMISSION && persist & SA_SHARE_PARSER) {
11933034Sdougm 		/* called by the dfstab parser so could be a show */
11943034Sdougm 		err = SA_OK;
11953034Sdougm 	    }
11963034Sdougm 	    if (err != SA_OK) {
11973034Sdougm 		/*
11983034Sdougm 		 * we couldn't commit to the repository so undo
11993034Sdougm 		 * our internal state to reflect reality.
12003034Sdougm 		 */
12013034Sdougm 		xmlUnlinkNode(node);
12023034Sdougm 		xmlFreeNode(node);
12033034Sdougm 		node = NULL;
12043034Sdougm 	    }
12053034Sdougm 	} else {
12063034Sdougm 	    err = SA_NO_MEMORY;
12073034Sdougm 	}
12083034Sdougm 	if (error != NULL)
12093034Sdougm 	    *error = err;
12103034Sdougm 	return (node);
12113034Sdougm }
12123034Sdougm 
12133034Sdougm /*
12143034Sdougm  * sa_add_share(group, sharepath, persist, *error)
12153034Sdougm  *
12163034Sdougm  *	Add a new share object to the specified group.  The share will
12173034Sdougm  *	have the specified sharepath and will only be constructed if
12183034Sdougm  *	it is a valid path to be shared.  NULL is returned on error
12193034Sdougm  *	and a detailed error value will be returned via the error
12203034Sdougm  *	pointer.
12213034Sdougm  */
12223034Sdougm sa_share_t
12233034Sdougm sa_add_share(sa_group_t group, char *sharepath, int persist, int *error)
12243034Sdougm {
12253034Sdougm 	xmlNodePtr node = NULL;
12263034Sdougm 	sa_share_t dup;
12273348Sdougm 	int strictness = SA_CHECK_NORMAL;
12283910Sdougm 	sa_handle_t handle;
12293348Sdougm 
12303348Sdougm 	/*
12313348Sdougm 	 * If the share is to be permanent, use strict checking so a
12323348Sdougm 	 * bad config doesn't get created. Transient shares only need
12333348Sdougm 	 * to check against the currently active
12343348Sdougm 	 * shares. SA_SHARE_PARSER is a modifier used internally to
12353348Sdougm 	 * indicate that we are being called by the dfstab parser and
12363348Sdougm 	 * that we need strict checking in all cases. Normally persist
12373348Sdougm 	 * is in integer value but SA_SHARE_PARSER may be or'd into
12383348Sdougm 	 * it as an override.
12393348Sdougm 	 */
12403348Sdougm 	if (persist & SA_SHARE_PARSER || persist == SA_SHARE_PERMANENT)
12413348Sdougm 	    strictness = SA_CHECK_STRICT;
12423034Sdougm 
12433910Sdougm 	handle = sa_find_group_handle(group);
12443910Sdougm 
12453910Sdougm 	if ((dup = sa_find_share(handle, sharepath)) == NULL &&
12463348Sdougm 		(*error = sa_check_path(group, sharepath, strictness)) ==
12473348Sdougm 			SA_OK) {
12483034Sdougm 	    node = _sa_add_share(group, sharepath, persist, error);
12493034Sdougm 	}
12503034Sdougm 	if (dup != NULL)
12513034Sdougm 	    *error = SA_DUPLICATE_NAME;
12523034Sdougm 
12533034Sdougm 	return ((sa_share_t)node);
12543034Sdougm }
12553034Sdougm 
12563034Sdougm /*
12573034Sdougm  * sa_enable_share(share, protocol)
12583034Sdougm  *	Enable the specified share to the specified protocol.
12593034Sdougm  *	If protocol is NULL, then all protocols.
12603034Sdougm  */
12613034Sdougm int
12623034Sdougm sa_enable_share(sa_share_t share, char *protocol)
12633034Sdougm {
12643034Sdougm 	char *sharepath;
12653034Sdougm 	struct stat st;
12663034Sdougm 	int err = 0;
12673034Sdougm 
12683034Sdougm 	sharepath = sa_get_share_attr(share, "path");
12693034Sdougm 	if (stat(sharepath, &st) < 0) {
12703034Sdougm 	    err = SA_NO_SUCH_PATH;
12713034Sdougm 	} else {
12723034Sdougm 	    /* tell the server about the share */
12733034Sdougm 	    if (protocol != NULL) {
12743034Sdougm 		/* lookup protocol specific handler */
12753034Sdougm 		err = sa_proto_share(protocol, share);
12763034Sdougm 		if (err == SA_OK)
12773034Sdougm 		    (void) sa_set_share_attr(share, "shared", "true");
12783034Sdougm 	    } else {
12793034Sdougm 		/* tell all protocols */
12803034Sdougm 		err = sa_proto_share("nfs", share); /* only NFS for now */
12813034Sdougm 		(void) sa_set_share_attr(share, "shared", "true");
12823034Sdougm 	    }
12833034Sdougm 	}
12843034Sdougm 	if (sharepath != NULL)
12853034Sdougm 	    sa_free_attr_string(sharepath);
12863034Sdougm 	return (err);
12873034Sdougm }
12883034Sdougm 
12893034Sdougm /*
12903034Sdougm  * sa_disable_share(share, protocol)
12913034Sdougm  *	Disable the specified share to the specified protocol.
12923034Sdougm  *	If protocol is NULL, then all protocols.
12933034Sdougm  */
12943034Sdougm int
12953034Sdougm sa_disable_share(sa_share_t share, char *protocol)
12963034Sdougm {
12973034Sdougm 	char *path;
12983034Sdougm 	char *shared;
12993034Sdougm 	int ret = SA_OK;
13003034Sdougm 
13013034Sdougm 	path = sa_get_share_attr(share, "path");
13023034Sdougm 	shared = sa_get_share_attr(share, "shared");
13033034Sdougm 
13043034Sdougm 	if (protocol != NULL) {
13053034Sdougm 	    ret = sa_proto_unshare(protocol, path);
13063034Sdougm 	} else {
13073034Sdougm 	    /* need to do all protocols */
13083034Sdougm 	    ret = sa_proto_unshare("nfs", path);
13093034Sdougm 	}
13103034Sdougm 	if (ret == SA_OK)
13113034Sdougm 		(void) sa_set_share_attr(share, "shared", NULL);
13123034Sdougm 	if (path != NULL)
13133034Sdougm 	    sa_free_attr_string(path);
13143034Sdougm 	if (shared != NULL)
13153034Sdougm 	    sa_free_attr_string(shared);
13163034Sdougm 	return (ret);
13173034Sdougm }
13183034Sdougm 
13193034Sdougm /*
13203034Sdougm  * sa_remove_share(share)
13213034Sdougm  *
13223034Sdougm  * remove the specified share from its containing group.
13233034Sdougm  * Remove from the SMF or ZFS configuration space.
13243034Sdougm  */
13253034Sdougm 
13263034Sdougm int
13273034Sdougm sa_remove_share(sa_share_t share)
13283034Sdougm {
13293034Sdougm 	sa_group_t group;
13303034Sdougm 	int ret = SA_OK;
13313034Sdougm 	char *type;
13323034Sdougm 	int transient = 0;
13333034Sdougm 	char *groupname;
13343034Sdougm 	char *zfs;
13353034Sdougm 
13363034Sdougm 	type = sa_get_share_attr(share, "type");
13373034Sdougm 	group = sa_get_parent_group(share);
13383034Sdougm 	zfs = sa_get_group_attr(group, "zfs");
13393034Sdougm 	groupname = sa_get_group_attr(group, "name");
13403034Sdougm 	if (type != NULL && strcmp(type, "persist") != 0)
13413034Sdougm 	    transient = 1;
13423034Sdougm 	if (type != NULL)
13433034Sdougm 	    sa_free_attr_string(type);
13443034Sdougm 
13453034Sdougm 	/* remove the node from its group then free the memory */
13463034Sdougm 
13473034Sdougm 	/*
13483034Sdougm 	 * need to test if "busy"
13493034Sdougm 	 */
13503034Sdougm 	/* only do SMF action if permanent */
13513034Sdougm 	if (!transient || zfs != NULL) {
13523034Sdougm 	    /* remove from legacy dfstab as well as possible SMF */
13533034Sdougm 	    ret = sa_delete_legacy(share);
13543034Sdougm 	    if (ret == SA_OK) {
13553034Sdougm 		if (!sa_group_is_zfs(group)) {
13563910Sdougm 		    sa_handle_impl_t impl_handle;
13573910Sdougm 		    impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
13583910Sdougm 		    if (impl_handle != NULL)
13593910Sdougm 			ret = sa_delete_share(impl_handle->scfhandle,
13603910Sdougm 						group, share);
13613910Sdougm 		    else
13623910Sdougm 			ret = SA_SYSTEM_ERR;
13633034Sdougm 		} else {
13643034Sdougm 		    char *sharepath = sa_get_share_attr(share, "path");
13653034Sdougm 		    if (sharepath != NULL) {
13663034Sdougm 			ret = sa_zfs_set_sharenfs(group, sharepath, 0);
13673034Sdougm 			sa_free_attr_string(sharepath);
13683034Sdougm 		    }
13693034Sdougm 		}
13703034Sdougm 	    }
13713034Sdougm 	}
13723034Sdougm 	if (groupname != NULL)
13733034Sdougm 	    sa_free_attr_string(groupname);
13743034Sdougm 	if (zfs != NULL)
13753034Sdougm 	    sa_free_attr_string(zfs);
13763034Sdougm 
13773034Sdougm 	xmlUnlinkNode((xmlNodePtr)share);
13783034Sdougm 	xmlFreeNode((xmlNodePtr)share);
13793034Sdougm 	return (ret);
13803034Sdougm }
13813034Sdougm 
13823034Sdougm /*
13833034Sdougm  * sa_move_share(group, share)
13843034Sdougm  *
13853034Sdougm  * move the specified share to the specified group.  Update SMF
13863034Sdougm  * appropriately.
13873034Sdougm  */
13883034Sdougm 
13893034Sdougm int
13903034Sdougm sa_move_share(sa_group_t group, sa_share_t share)
13913034Sdougm {
13923034Sdougm 	sa_group_t oldgroup;
13933034Sdougm 	int ret = SA_OK;
13943034Sdougm 
13953034Sdougm 	/* remove the node from its group then free the memory */
13963034Sdougm 
13973034Sdougm 	oldgroup = sa_get_parent_group(share);
13983034Sdougm 	if (oldgroup != group) {
13993910Sdougm 	    sa_handle_impl_t impl_handle;
14003034Sdougm 	    xmlUnlinkNode((xmlNodePtr)share);
14013034Sdougm 	    /* now that the share isn't in its old group, add to the new one */
14023034Sdougm 	    xmlAddChild((xmlNodePtr)group, (xmlNodePtr)share);
14033034Sdougm 	    /* need to deal with SMF */
14043910Sdougm 	    impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
14053910Sdougm 	    if (impl_handle != NULL) {
14063034Sdougm 		/*
14073034Sdougm 		 * need to remove from old group first and then add to
14083034Sdougm 		 * new group. Ideally, we would do the other order but
14093034Sdougm 		 * need to avoid having the share in two groups at the
14103034Sdougm 		 * same time.
14113034Sdougm 		 */
14123910Sdougm 		ret = sa_delete_share(impl_handle->scfhandle, oldgroup, share);
14133910Sdougm 		if (ret == SA_OK)
14143910Sdougm 		    ret = sa_commit_share(impl_handle->scfhandle, group, share);
14153910Sdougm 	    } else {
14163910Sdougm 		ret = SA_SYSTEM_ERR;
14173034Sdougm 	    }
14183034Sdougm 	}
14193034Sdougm 	return (ret);
14203034Sdougm }
14213034Sdougm 
14223034Sdougm /*
14233034Sdougm  * sa_get_parent_group(share)
14243034Sdougm  *
14253034Sdougm  * Return the containg group for the share. If a group was actually
14263034Sdougm  * passed in, we don't want a parent so return NULL.
14273034Sdougm  */
14283034Sdougm 
14293034Sdougm sa_group_t
14303034Sdougm sa_get_parent_group(sa_share_t share)
14313034Sdougm {
14323034Sdougm 	xmlNodePtr node = NULL;
14333034Sdougm 	if (share != NULL) {
14343034Sdougm 	    node = ((xmlNodePtr)share)->parent;
14353034Sdougm 		/*
14363034Sdougm 		 * make sure parent is a group and not sharecfg since
14373034Sdougm 		 * we may be cheating and passing in a group.
14383034Sdougm 		 * Eventually, groups of groups might come into being.
14393034Sdougm 		 */
14403034Sdougm 	    if (node == NULL ||
14413034Sdougm 		xmlStrcmp(node->name, (xmlChar *)"sharecfg") == 0)
14423034Sdougm 		node = NULL;
14433034Sdougm 	}
14443034Sdougm 	return ((sa_group_t)node);
14453034Sdougm }
14463034Sdougm 
14473034Sdougm /*
14483910Sdougm  * _sa_create_group(impl_handle, groupname)
14493034Sdougm  *
14503034Sdougm  * Create a group in the document. The caller will need to deal with
14513034Sdougm  * configuration store and activation.
14523034Sdougm  */
14533034Sdougm 
14543034Sdougm sa_group_t
14553910Sdougm _sa_create_group(sa_handle_impl_t impl_handle, char *groupname)
14563034Sdougm {
14573034Sdougm 	xmlNodePtr node = NULL;
14583034Sdougm 
14593034Sdougm 	if (sa_valid_group_name(groupname)) {
14603910Sdougm 	    node = xmlNewChild(impl_handle->tree, NULL,
14613034Sdougm 				(xmlChar *)"group", NULL);
14623034Sdougm 	    if (node != NULL) {
14633034Sdougm 		xmlSetProp(node, (xmlChar *)"name", (xmlChar *)groupname);
14643034Sdougm 		xmlSetProp(node, (xmlChar *)"state", (xmlChar *)"enabled");
14653034Sdougm 	    }
14663034Sdougm 	}
14673034Sdougm 	return ((sa_group_t)node);
14683034Sdougm }
14693034Sdougm 
14703034Sdougm /*
14713034Sdougm  * _sa_create_zfs_group(group, groupname)
14723034Sdougm  *
14733034Sdougm  * Create a ZFS subgroup under the specified group. This may
14743034Sdougm  * eventually form the basis of general sub-groups, but is currently
14753034Sdougm  * restricted to ZFS.
14763034Sdougm  */
14773034Sdougm sa_group_t
14783034Sdougm _sa_create_zfs_group(sa_group_t group, char *groupname)
14793034Sdougm {
14803034Sdougm 	xmlNodePtr node = NULL;
14813034Sdougm 
14823034Sdougm 	node = xmlNewChild((xmlNodePtr)group, NULL,
14833034Sdougm 				(xmlChar *)"group", NULL);
14843034Sdougm 	if (node != NULL) {
14853034Sdougm 		xmlSetProp(node, (xmlChar *)"name", (xmlChar *)groupname);
14863034Sdougm 		xmlSetProp(node, (xmlChar *)"state", (xmlChar *)"enabled");
14873034Sdougm 	}
14883034Sdougm 
14893034Sdougm 	return ((sa_group_t)node);
14903034Sdougm }
14913034Sdougm 
14923034Sdougm /*
14933034Sdougm  * sa_create_group(groupname, *error)
14943034Sdougm  *
14953034Sdougm  * Create a new group with groupname.  Need to validate that it is a
14963034Sdougm  * legal name for SMF and the construct the SMF service instance of
14973034Sdougm  * svc:/network/shares/group to implement the group. All necessary
14983034Sdougm  * operational properties must be added to the group at this point
14993034Sdougm  * (via the SMF transaction model).
15003034Sdougm  */
15013034Sdougm sa_group_t
15023910Sdougm sa_create_group(sa_handle_t handle, char *groupname, int *error)
15033034Sdougm {
15043034Sdougm 	xmlNodePtr node = NULL;
15053034Sdougm 	sa_group_t group;
15063034Sdougm 	int ret;
15073034Sdougm 	char rbacstr[256];
15083910Sdougm 	sa_handle_impl_t impl_handle = (sa_handle_impl_t)handle;
15093034Sdougm 
15103034Sdougm 	ret = SA_OK;
15113034Sdougm 
15123910Sdougm 	if (impl_handle == NULL || impl_handle->scfhandle == NULL) {
15133034Sdougm 	    ret = SA_SYSTEM_ERR;
15143034Sdougm 	    goto err;
15153034Sdougm 	}
15163034Sdougm 
15173910Sdougm 	group = sa_get_group(handle, groupname);
15183034Sdougm 	if (group != NULL) {
15193034Sdougm 	    ret = SA_DUPLICATE_NAME;
15203034Sdougm 	} else {
15213034Sdougm 	    if (sa_valid_group_name(groupname)) {
15223910Sdougm 		node = xmlNewChild(impl_handle->tree, NULL,
15233034Sdougm 				    (xmlChar *)"group", NULL);
15243034Sdougm 		if (node != NULL) {
15253034Sdougm 		    xmlSetProp(node, (xmlChar *)"name", (xmlChar *)groupname);
15263034Sdougm 		    /* default to the group being enabled */
15273034Sdougm 		    xmlSetProp(node, (xmlChar *)"state", (xmlChar *)"enabled");
15283910Sdougm 		    ret = sa_create_instance(impl_handle->scfhandle, groupname);
15293034Sdougm 		    if (ret == SA_OK) {
15303910Sdougm 			ret = sa_start_transaction(impl_handle->scfhandle,
15313910Sdougm 							"operation");
15323034Sdougm 		    }
15333034Sdougm 		    if (ret == SA_OK) {
15343910Sdougm 			ret = sa_set_property(impl_handle->scfhandle,
15353910Sdougm 						"state", "enabled");
15363034Sdougm 			if (ret == SA_OK) {
15373910Sdougm 			    ret = sa_end_transaction(impl_handle->scfhandle);
15383034Sdougm 			} else {
15393910Sdougm 			    sa_abort_transaction(impl_handle->scfhandle);
15403034Sdougm 			}
15413034Sdougm 		    }
15423034Sdougm 		    if (ret == SA_OK) {
15433034Sdougm 			/* initialize the RBAC strings */
15443910Sdougm 			ret = sa_start_transaction(impl_handle->scfhandle,
15453910Sdougm 							"general");
15463034Sdougm 			if (ret == SA_OK) {
15473034Sdougm 			    (void) snprintf(rbacstr, sizeof (rbacstr), "%s.%s",
15483034Sdougm 					SA_RBAC_MANAGE, groupname);
15493910Sdougm 			    ret = sa_set_property(impl_handle->scfhandle,
15503034Sdougm 						    "action_authorization",
15513034Sdougm 						    rbacstr);
15523034Sdougm 			}
15533034Sdougm 			if (ret == SA_OK) {
15543034Sdougm 			    (void) snprintf(rbacstr, sizeof (rbacstr), "%s.%s",
15553034Sdougm 					SA_RBAC_VALUE, groupname);
15563910Sdougm 			    ret = sa_set_property(impl_handle->scfhandle,
15573034Sdougm 						    "value_authorization",
15583034Sdougm 						    rbacstr);
15593034Sdougm 			}
15603034Sdougm 			if (ret == SA_OK) {
15613910Sdougm 			    ret = sa_end_transaction(impl_handle->scfhandle);
15623034Sdougm 			} else {
15633910Sdougm 			    sa_abort_transaction(impl_handle->scfhandle);
15643034Sdougm 			}
15653034Sdougm 		    }
15663034Sdougm 		    if (ret != SA_OK) {
15673034Sdougm 			/*
15683034Sdougm 			 * Couldn't commit the group so we need to
15693034Sdougm 			 * undo internally.
15703034Sdougm 			 */
15713034Sdougm 			xmlUnlinkNode(node);
15723034Sdougm 			xmlFreeNode(node);
15733034Sdougm 			node = NULL;
15743034Sdougm 		    }
15753034Sdougm 		} else {
15763034Sdougm 		    ret = SA_NO_MEMORY;
15773034Sdougm 		}
15783034Sdougm 	    } else {
15793034Sdougm 		ret = SA_INVALID_NAME;
15803034Sdougm 	    }
15813034Sdougm 	}
15823034Sdougm err:
15833034Sdougm 	if (error != NULL)
15843034Sdougm 	    *error = ret;
15853034Sdougm 	return ((sa_group_t)node);
15863034Sdougm }
15873034Sdougm 
15883034Sdougm /*
15893034Sdougm  * sa_remove_group(group)
15903034Sdougm  *
15913034Sdougm  * Remove the specified group. This deletes from the SMF repository.
15923034Sdougm  * All property groups and properties are removed.
15933034Sdougm  */
15943034Sdougm 
15953034Sdougm int
15963034Sdougm sa_remove_group(sa_group_t group)
15973034Sdougm {
15983034Sdougm 	char *name;
15993034Sdougm 	int ret = SA_OK;
16003910Sdougm 	sa_handle_impl_t impl_handle;
16013034Sdougm 
16023910Sdougm 	impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
16033910Sdougm 	if (impl_handle != NULL) {
16043910Sdougm 	    name = sa_get_group_attr(group, "name");
16053910Sdougm 	    if (name != NULL) {
16063910Sdougm 		ret = sa_delete_instance(impl_handle->scfhandle, name);
16073910Sdougm 		sa_free_attr_string(name);
16083910Sdougm 	    }
16093910Sdougm 	    xmlUnlinkNode((xmlNodePtr)group); /* make sure unlinked */
16103910Sdougm 	    xmlFreeNode((xmlNodePtr)group);   /* now it is gone */
16113910Sdougm 	} else {
16123910Sdougm 	    ret = SA_SYSTEM_ERR;
16133034Sdougm 	}
16143034Sdougm 	return (ret);
16153034Sdougm }
16163034Sdougm 
16173034Sdougm /*
16183034Sdougm  * sa_update_config()
16193034Sdougm  *
16203034Sdougm  * Used to update legacy files that need to be updated in bulk
16213034Sdougm  * Currently, this is a placeholder and will go away in a future
16223034Sdougm  * release.
16233034Sdougm  */
16243034Sdougm 
16253034Sdougm int
16263910Sdougm sa_update_config(sa_handle_t handle)
16273034Sdougm {
16283034Sdougm 	/*
16293034Sdougm 	 * do legacy files first so we can tell when they change.
16303034Sdougm 	 * This will go away when we start updating individual records
16313034Sdougm 	 * rather than the whole file.
16323034Sdougm 	 */
16333910Sdougm 	update_legacy_config(handle);
16343034Sdougm 	return (SA_OK);
16353034Sdougm }
16363034Sdougm 
16373034Sdougm /*
16383034Sdougm  * get_node_attr(node, tag)
16393034Sdougm  *
16403034Sdougm  * Get the speficied tag(attribute) if it exists on the node.  This is
16413034Sdougm  * used internally by a number of attribute oriented functions.
16423034Sdougm  */
16433034Sdougm 
16443034Sdougm static char *
16453034Sdougm get_node_attr(void *nodehdl, char *tag)
16463034Sdougm {
16473034Sdougm 	xmlNodePtr node = (xmlNodePtr)nodehdl;
16483034Sdougm 	xmlChar *name = NULL;
16493034Sdougm 
16503034Sdougm 	if (node != NULL) {
16513034Sdougm 		name = xmlGetProp(node, (xmlChar *)tag);
16523034Sdougm 	}
16533034Sdougm 	return ((char *)name);
16543034Sdougm }
16553034Sdougm 
16563034Sdougm /*
16573034Sdougm  * get_node_attr(node, tag)
16583034Sdougm  *
16593034Sdougm  * Set the speficied tag(attribute) to the specified value This is
16603034Sdougm  * used internally by a number of attribute oriented functions. It
16613034Sdougm  * doesn't update the repository, only the internal document state.
16623034Sdougm  */
16633034Sdougm 
16643034Sdougm void
16653034Sdougm set_node_attr(void *nodehdl, char *tag, char *value)
16663034Sdougm {
16673034Sdougm 	xmlNodePtr node = (xmlNodePtr)nodehdl;
16683034Sdougm 	if (node != NULL && tag != NULL) {
16693034Sdougm 		if (value != NULL) {
16703034Sdougm 			xmlSetProp(node, (xmlChar *)tag, (xmlChar *)value);
16713034Sdougm 		} else {
16723034Sdougm 			xmlUnsetProp(node, (xmlChar *)tag);
16733034Sdougm 		}
16743034Sdougm 	}
16753034Sdougm }
16763034Sdougm 
16773034Sdougm /*
16783034Sdougm  * sa_get_group_attr(group, tag)
16793034Sdougm  *
16803034Sdougm  * Get the specied attribute, if defined, for the group.
16813034Sdougm  */
16823034Sdougm 
16833034Sdougm char *
16843034Sdougm sa_get_group_attr(sa_group_t group, char *tag)
16853034Sdougm {
16863034Sdougm 	return (get_node_attr((void *)group, tag));
16873034Sdougm }
16883034Sdougm 
16893034Sdougm /*
16903034Sdougm  * sa_set_group_attr(group, tag, value)
16913034Sdougm  *
16923034Sdougm  * set the specified tag/attribute on the group using value as its
16933034Sdougm  * value.
16943034Sdougm  *
16953034Sdougm  * This will result in setting the property in the SMF repository as
16963034Sdougm  * well as in the internal document.
16973034Sdougm  */
16983034Sdougm 
16993034Sdougm int
17003034Sdougm sa_set_group_attr(sa_group_t group, char *tag, char *value)
17013034Sdougm {
17023034Sdougm 	int ret;
17033034Sdougm 	char *groupname;
17043910Sdougm 	sa_handle_impl_t impl_handle;
17053034Sdougm 
17063910Sdougm 	impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
17073910Sdougm 	if (impl_handle != NULL) {
17083910Sdougm 	    groupname = sa_get_group_attr(group, "name");
17093910Sdougm 	    ret = sa_get_instance(impl_handle->scfhandle, groupname);
17103034Sdougm 	    if (ret == SA_OK) {
17113910Sdougm 		set_node_attr((void *)group, tag, value);
17123910Sdougm 		ret = sa_start_transaction(impl_handle->scfhandle, "operation");
17133910Sdougm 		if (ret == SA_OK) {
17143910Sdougm 		    ret = sa_set_property(impl_handle->scfhandle, tag, value);
17153910Sdougm 		    if (ret == SA_OK)
17163910Sdougm 			(void) sa_end_transaction(impl_handle->scfhandle);
17173910Sdougm 		    else {
17183910Sdougm 			sa_abort_transaction(impl_handle->scfhandle);
17193910Sdougm 		    }
17203034Sdougm 		}
17213034Sdougm 	    }
17223910Sdougm 	    if (groupname != NULL)
17233910Sdougm 		sa_free_attr_string(groupname);
17243910Sdougm 	} else {
17253910Sdougm 	    ret = SA_SYSTEM_ERR;
17263034Sdougm 	}
17273034Sdougm 	return (ret);
17283034Sdougm }
17293034Sdougm 
17303034Sdougm /*
17313034Sdougm  * sa_get_share_attr(share, tag)
17323034Sdougm  *
17333034Sdougm  * Return the value of the tag/attribute set on the specified
17343034Sdougm  * share. Returns NULL if the tag doesn't exist.
17353034Sdougm  */
17363034Sdougm 
17373034Sdougm char *
17383034Sdougm sa_get_share_attr(sa_share_t share, char *tag)
17393034Sdougm {
17403034Sdougm 	return (get_node_attr((void *)share, tag));
17413034Sdougm }
17423034Sdougm 
17433034Sdougm /*
17443034Sdougm  * sa_get_resource(group, resource)
17453034Sdougm  *
17463034Sdougm  * Search all the shares in the speified group for a share with a
17473034Sdougm  * resource name matching the one specified.
17483034Sdougm  *
17493034Sdougm  * In the future, it may be advantageous to allow group to be NULL and
17503034Sdougm  * search all groups but that isn't needed at present.
17513034Sdougm  */
17523034Sdougm 
17533034Sdougm sa_share_t
17543034Sdougm sa_get_resource(sa_group_t group, char *resource)
17553034Sdougm {
17563034Sdougm 	sa_share_t share = NULL;
17573034Sdougm 	char *name = NULL;
17583034Sdougm 
17593034Sdougm 	if (resource != NULL) {
17603034Sdougm 	    for (share = sa_get_share(group, NULL); share != NULL;
17613034Sdougm 		share = sa_get_next_share(share)) {
17623034Sdougm 		name = sa_get_share_attr(share, "resource");
17633034Sdougm 		if (name != NULL) {
17643034Sdougm 		    if (strcmp(name, resource) == 0)
17653034Sdougm 			break;
17663034Sdougm 		    sa_free_attr_string(name);
17673034Sdougm 		    name = NULL;
17683034Sdougm 		}
17693034Sdougm 	    }
17703034Sdougm 	    if (name != NULL)
17713034Sdougm 		sa_free_attr_string(name);
17723034Sdougm 	}
17733034Sdougm 	return ((sa_share_t)share);
17743034Sdougm }
17753034Sdougm 
17763034Sdougm /*
17773034Sdougm  * _sa_set_share_description(share, description)
17783034Sdougm  *
17793034Sdougm  * Add a description tag with text contents to the specified share.
17803034Sdougm  * A separate XML tag is used rather than a property.
17813034Sdougm  */
17823034Sdougm 
17833034Sdougm xmlNodePtr
17843034Sdougm _sa_set_share_description(sa_share_t share, char *content)
17853034Sdougm {
17863034Sdougm 	xmlNodePtr node;
17873034Sdougm 	node = xmlNewChild((xmlNodePtr)share,
17883034Sdougm 			    NULL, (xmlChar *)"description", NULL);
17893034Sdougm 	xmlNodeSetContent(node, (xmlChar *)content);
17903034Sdougm 	return (node);
17913034Sdougm }
17923034Sdougm 
17933034Sdougm /*
17943034Sdougm  * sa_set_share_attr(share, tag, value)
17953034Sdougm  *
17963034Sdougm  * Set the share attribute specified by tag to the specified value. In
17973034Sdougm  * the case of "resource", enforce a no duplicates in a group rule. If
17983034Sdougm  * the share is not transient, commit the changes to the repository
17993034Sdougm  * else just update the share internally.
18003034Sdougm  */
18013034Sdougm 
18023034Sdougm int
18033034Sdougm sa_set_share_attr(sa_share_t share, char *tag, char *value)
18043034Sdougm {
18053034Sdougm 	sa_group_t group;
18063034Sdougm 	sa_share_t resource;
18073034Sdougm 	int ret = SA_OK;
18083034Sdougm 
18093034Sdougm 	group = sa_get_parent_group(share);
18103034Sdougm 
18113034Sdougm 	/*
18123034Sdougm 	 * There are some attributes that may have specific
18133034Sdougm 	 * restrictions on them. Initially, only "resource" has
18143034Sdougm 	 * special meaning that needs to be checked. Only one instance
18153034Sdougm 	 * of a resource name may exist within a group.
18163034Sdougm 	 */
18173034Sdougm 
18183034Sdougm 	if (strcmp(tag, "resource") == 0) {
18193034Sdougm 	    resource = sa_get_resource(group, value);
18203034Sdougm 	    if (resource != share && resource != NULL)
18213034Sdougm 		ret = SA_DUPLICATE_NAME;
18223034Sdougm 	}
18233034Sdougm 	if (ret == SA_OK) {
18243034Sdougm 	    set_node_attr((void *)share, tag, value);
18253034Sdougm 	    if (group != NULL) {
18263034Sdougm 		char *type;
18273034Sdougm 		/* we can probably optimize this some */
18283034Sdougm 		type = sa_get_share_attr(share, "type");
18293910Sdougm 		if (type == NULL || strcmp(type, "transient") != 0) {
18303910Sdougm 		    sa_handle_impl_t impl_handle;
18313910Sdougm 		    impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
18323910Sdougm 		    if (impl_handle != NULL)
18333910Sdougm 			ret = sa_commit_share(impl_handle->scfhandle,
18343910Sdougm 						group, share);
18353910Sdougm 		    else
18363910Sdougm 			ret = SA_SYSTEM_ERR;
18373910Sdougm 		}
18383034Sdougm 		if (type != NULL)
18393034Sdougm 		    sa_free_attr_string(type);
18403034Sdougm 	    }
18413034Sdougm 	}
18423034Sdougm 	return (ret);
18433034Sdougm }
18443034Sdougm 
18453034Sdougm /*
18463034Sdougm  * sa_get_property_attr(prop, tag)
18473034Sdougm  *
18483034Sdougm  * Get the value of the specified property attribute. Standard
18493034Sdougm  * attributes are "type" and "value".
18503034Sdougm  */
18513034Sdougm 
18523034Sdougm char *
18533034Sdougm sa_get_property_attr(sa_property_t prop, char *tag)
18543034Sdougm {
18553034Sdougm 	return (get_node_attr((void *)prop, tag));
18563034Sdougm }
18573034Sdougm 
18583034Sdougm /*
18593034Sdougm  * sa_get_optionset_attr(prop, tag)
18603034Sdougm  *
18613034Sdougm  * Get the value of the specified property attribute. Standard
18623034Sdougm  * attribute is "type".
18633034Sdougm  */
18643034Sdougm 
18653034Sdougm char *
18663034Sdougm sa_get_optionset_attr(sa_property_t optionset, char *tag)
18673034Sdougm {
18683034Sdougm 	return (get_node_attr((void *)optionset, tag));
18693034Sdougm 
18703034Sdougm }
18713034Sdougm 
18723034Sdougm /*
18733034Sdougm  * sa_set_optionset_attr(optionset, tag, value)
18743034Sdougm  *
18753034Sdougm  * Set the specified attribute(tag) to the specified value on the
18763034Sdougm  * optionset.
18773034Sdougm  */
18783034Sdougm 
18793034Sdougm void
18803034Sdougm sa_set_optionset_attr(sa_group_t optionset, char *tag, char *value)
18813034Sdougm {
18823034Sdougm 	set_node_attr((void *)optionset, tag, value);
18833034Sdougm }
18843034Sdougm 
18853034Sdougm /*
18863034Sdougm  * sa_free_attr_string(string)
18873034Sdougm  *
18883034Sdougm  * Free the string that was returned in one of the sa_get_*_attr()
18893034Sdougm  * functions.
18903034Sdougm  */
18913034Sdougm 
18923034Sdougm void
18933034Sdougm sa_free_attr_string(char *string)
18943034Sdougm {
18953034Sdougm 	xmlFree((xmlChar *)string);
18963034Sdougm }
18973034Sdougm 
18983034Sdougm /*
18993034Sdougm  * sa_get_optionset(group, proto)
19003034Sdougm  *
19013034Sdougm  * Return the optionset, if it exists, that is associated with the
19023034Sdougm  * specified protocol.
19033034Sdougm  */
19043034Sdougm 
19053034Sdougm sa_optionset_t
19063034Sdougm sa_get_optionset(void *group, char *proto)
19073034Sdougm {
19083034Sdougm 	xmlNodePtr node;
19093034Sdougm 	xmlChar *value = NULL;
19103034Sdougm 
19113034Sdougm 	for (node = ((xmlNodePtr)group)->children; node != NULL;
19123034Sdougm 		node = node->next) {
19133034Sdougm 		if (xmlStrcmp(node->name, (xmlChar *)"optionset") == 0) {
19143034Sdougm 		    value = xmlGetProp(node, (xmlChar *)"type");
19153034Sdougm 		    if (proto != NULL) {
19163034Sdougm 			if (value != NULL &&
19173034Sdougm 			    xmlStrcmp(value, (xmlChar *)proto) == 0) {
19183034Sdougm 			    break;
19193034Sdougm 			}
19203034Sdougm 			if (value != NULL) {
19213034Sdougm 			    xmlFree(value);
19223034Sdougm 			    value = NULL;
19233034Sdougm 			}
19243034Sdougm 		    } else {
19253034Sdougm 			break;
19263034Sdougm 		    }
19273034Sdougm 		}
19283034Sdougm 	}
19293034Sdougm 	if (value != NULL)
19303034Sdougm 	    xmlFree(value);
19313034Sdougm 	return ((sa_optionset_t)node);
19323034Sdougm }
19333034Sdougm 
19343034Sdougm /*
19353034Sdougm  * sa_get_next_optionset(optionset)
19363034Sdougm  *
19373034Sdougm  * Return the next optionset in the group. NULL if this was the last.
19383034Sdougm  */
19393034Sdougm 
19403034Sdougm sa_optionset_t
19413034Sdougm sa_get_next_optionset(sa_optionset_t optionset)
19423034Sdougm {
19433034Sdougm 	xmlNodePtr node;
19443034Sdougm 
19453034Sdougm 	for (node = ((xmlNodePtr)optionset)->next; node != NULL;
19463034Sdougm 		node = node->next) {
19473034Sdougm 		if (xmlStrcmp(node->name, (xmlChar *)"optionset") == 0) {
19483034Sdougm 			break;
19493034Sdougm 		}
19503034Sdougm 	}
19513034Sdougm 	return ((sa_optionset_t)node);
19523034Sdougm }
19533034Sdougm 
19543034Sdougm /*
19553034Sdougm  * sa_get_security(group, sectype, proto)
19563034Sdougm  *
19573034Sdougm  * Return the security optionset. The internal name is a hold over
19583034Sdougm  * from the implementation and will be changed before the API is
19593034Sdougm  * finalized. This is really a named optionset that can be negotiated
19603034Sdougm  * as a group of properties (like NFS security options).
19613034Sdougm  */
19623034Sdougm 
19633034Sdougm sa_security_t
19643034Sdougm sa_get_security(sa_group_t group, char *sectype, char *proto)
19653034Sdougm {
19663034Sdougm 	xmlNodePtr node;
19673034Sdougm 	xmlChar *value = NULL;
19683034Sdougm 
19693034Sdougm 	for (node = ((xmlNodePtr)group)->children; node != NULL;
19703034Sdougm 		node = node->next) {
19713034Sdougm 	    if (xmlStrcmp(node->name, (xmlChar *)"security") == 0) {
19723034Sdougm 		if (proto != NULL) {
19733034Sdougm 		    value = xmlGetProp(node, (xmlChar *)"type");
19743034Sdougm 		    if (value == NULL ||
19753034Sdougm 			(value != NULL &&
19763034Sdougm 			xmlStrcmp(value, (xmlChar *)proto) != 0)) {
19773034Sdougm 			/* it doesn't match so continue */
19783034Sdougm 			xmlFree(value);
19793034Sdougm 			value = NULL;
19803034Sdougm 			continue;
19813034Sdougm 		    }
19823034Sdougm 		}
19833034Sdougm 		if (value != NULL) {
19843034Sdougm 		    xmlFree(value);
19853034Sdougm 		    value = NULL;
19863034Sdougm 		}
19873034Sdougm 		/* potential match */
19883034Sdougm 		if (sectype != NULL) {
19893034Sdougm 		    value = xmlGetProp(node, (xmlChar *)"sectype");
19903034Sdougm 		    if (value != NULL &&
19913034Sdougm 			xmlStrcmp(value, (xmlChar *)sectype) == 0) {
19923034Sdougm 			break;
19933034Sdougm 		    }
19943034Sdougm 		} else {
19953034Sdougm 		    break;
19963034Sdougm 		}
19973034Sdougm 	    }
19983034Sdougm 	    if (value != NULL) {
19993034Sdougm 		xmlFree(value);
20003034Sdougm 		value = NULL;
20013034Sdougm 	    }
20023034Sdougm 	}
20033034Sdougm 	if (value != NULL)
20043034Sdougm 	    xmlFree(value);
20053034Sdougm 	return ((sa_security_t)node);
20063034Sdougm }
20073034Sdougm 
20083034Sdougm /*
20093034Sdougm  * sa_get_next_security(security)
20103034Sdougm  *
20113034Sdougm  * Get the next security optionset if one exists.
20123034Sdougm  */
20133034Sdougm 
20143034Sdougm sa_security_t
20153034Sdougm sa_get_next_security(sa_security_t security)
20163034Sdougm {
20173034Sdougm 	xmlNodePtr node;
20183034Sdougm 
20193034Sdougm 	for (node = ((xmlNodePtr)security)->next; node != NULL;
20203034Sdougm 		node = node->next) {
20213034Sdougm 		if (xmlStrcmp(node->name, (xmlChar *)"security") == 0) {
20223034Sdougm 			break;
20233034Sdougm 		}
20243034Sdougm 	}
20253034Sdougm 	return ((sa_security_t)node);
20263034Sdougm }
20273034Sdougm 
20283034Sdougm /*
20293034Sdougm  * sa_get_property(optionset, prop)
20303034Sdougm  *
20313034Sdougm  * Get the property object with the name specified in prop from the
20323034Sdougm  * optionset.
20333034Sdougm  */
20343034Sdougm 
20353034Sdougm sa_property_t
20363034Sdougm sa_get_property(sa_optionset_t optionset, char *prop)
20373034Sdougm {
20383034Sdougm 	xmlNodePtr node = (xmlNodePtr)optionset;
20393034Sdougm 	xmlChar *value = NULL;
20403034Sdougm 
20413034Sdougm 	if (optionset == NULL)
20423034Sdougm 	    return (NULL);
20433034Sdougm 
20443034Sdougm 	for (node = node->children; node != NULL;
20453034Sdougm 		node = node->next) {
20463034Sdougm 	    if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) {
20473034Sdougm 		if (prop == NULL)
20483034Sdougm 		    break;
20493034Sdougm 		value = xmlGetProp(node, (xmlChar *)"type");
20503034Sdougm 		if (value != NULL && xmlStrcmp(value, (xmlChar *)prop) == 0) {
20513034Sdougm 		    break;
20523034Sdougm 		}
20533034Sdougm 		if (value != NULL) {
20543034Sdougm 		    xmlFree(value);
20553034Sdougm 		    value = NULL;
20563034Sdougm 		}
20573034Sdougm 	    }
20583034Sdougm 	}
20593034Sdougm 	if (value != NULL)
20603034Sdougm 		xmlFree(value);
20613034Sdougm 	if (node != NULL && xmlStrcmp(node->name, (xmlChar *)"option") != 0) {
20623034Sdougm 	    /* avoid a non option node -- it is possible to be a text node */
20633034Sdougm 	    node = NULL;
20643034Sdougm 	}
20653034Sdougm 	return ((sa_property_t)node);
20663034Sdougm }
20673034Sdougm 
20683034Sdougm /*
20693034Sdougm  * sa_get_next_property(property)
20703034Sdougm  *
20713034Sdougm  * Get the next property following the specified property. NULL if
20723034Sdougm  * this was the last.
20733034Sdougm  */
20743034Sdougm 
20753034Sdougm sa_property_t
20763034Sdougm sa_get_next_property(sa_property_t property)
20773034Sdougm {
20783034Sdougm 	xmlNodePtr node;
20793034Sdougm 
20803034Sdougm 	for (node = ((xmlNodePtr)property)->next; node != NULL;
20813034Sdougm 		node = node->next) {
20823034Sdougm 		if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) {
20833034Sdougm 			break;
20843034Sdougm 		}
20853034Sdougm 	}
20863034Sdougm 	return ((sa_property_t)node);
20873034Sdougm }
20883034Sdougm 
20893034Sdougm /*
20903034Sdougm  * sa_set_share_description(share, content)
20913034Sdougm  *
20923034Sdougm  * Set the description of share to content.
20933034Sdougm  */
20943034Sdougm 
20953034Sdougm int
20963034Sdougm sa_set_share_description(sa_share_t share, char *content)
20973034Sdougm {
20983034Sdougm 	xmlNodePtr node;
20993034Sdougm 	sa_group_t group;
21003034Sdougm 	int ret = SA_OK;
21013034Sdougm 
21023034Sdougm 	for (node = ((xmlNodePtr)share)->children; node != NULL;
21033034Sdougm 		node = node->next) {
21043034Sdougm 		if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) {
21053034Sdougm 			break;
21063034Sdougm 		}
21073034Sdougm 	}
21083034Sdougm 	group = sa_get_parent_group(share);
21093034Sdougm 	/* no existing description but want to add */
21103034Sdougm 	if (node == NULL && content != NULL) {
21113034Sdougm 		/* add a description */
21123034Sdougm 	    node = _sa_set_share_description(share, content);
21133034Sdougm 	} else if (node != NULL && content != NULL) {
21143034Sdougm 		/* update a description */
21153034Sdougm 		xmlNodeSetContent(node, (xmlChar *)content);
21163034Sdougm 	} else if (node != NULL && content == NULL) {
21173034Sdougm 		/* remove an existing description */
21183034Sdougm 		xmlUnlinkNode(node);
21193034Sdougm 		xmlFreeNode(node);
21203034Sdougm 	}
21213910Sdougm 	if (group != NULL && is_persistent((sa_group_t)share)) {
21223910Sdougm 	    sa_handle_impl_t impl_handle;
21233910Sdougm 	    impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
21243910Sdougm 	    if (impl_handle != NULL)
21253910Sdougm 		ret = sa_commit_share(impl_handle->scfhandle, group, share);
21263910Sdougm 	    else
21273910Sdougm 		ret = SA_SYSTEM_ERR;
21283910Sdougm 	}
21293034Sdougm 	return (ret);
21303034Sdougm }
21313034Sdougm 
21323034Sdougm /*
21333034Sdougm  * fixproblemchars(string)
21343034Sdougm  *
21353034Sdougm  * don't want any newline or tab characters in the text since these
21363034Sdougm  * could break display of data and legacy file formats.
21373034Sdougm  */
21383034Sdougm static void
21393034Sdougm fixproblemchars(char *str)
21403034Sdougm {
21413034Sdougm 	int c;
21423034Sdougm 	for (c = *str; c != '\0'; c = *++str) {
21433034Sdougm 	    if (c == '\t' || c == '\n')
21443034Sdougm 		*str = ' ';
21453034Sdougm 	    else if (c == '"')
21463034Sdougm 		*str = '\'';
21473034Sdougm 	}
21483034Sdougm }
21493034Sdougm 
21503034Sdougm /*
21513034Sdougm  * sa_get_share_description(share)
21523034Sdougm  *
21533034Sdougm  * Return the description text for the specified share if it
21543034Sdougm  * exists. NULL if no description exists.
21553034Sdougm  */
21563034Sdougm 
21573034Sdougm char *
21583034Sdougm sa_get_share_description(sa_share_t share)
21593034Sdougm {
21603034Sdougm 	xmlChar *description = NULL;
21613034Sdougm 	xmlNodePtr node;
21623034Sdougm 
21633034Sdougm 	for (node = ((xmlNodePtr)share)->children; node != NULL;
21643034Sdougm 		node = node->next) {
21653034Sdougm 	    if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) {
21663034Sdougm 		break;
21673034Sdougm 	    }
21683034Sdougm 	}
21693034Sdougm 	if (node != NULL) {
21703034Sdougm 	    description = xmlNodeGetContent((xmlNodePtr)share);
21713034Sdougm 	    fixproblemchars((char *)description);
21723034Sdougm 	}
21733034Sdougm 	return ((char *)description);
21743034Sdougm }
21753034Sdougm 
21763034Sdougm /*
21773034Sdougm  * sa_free(share_description(description)
21783034Sdougm  *
21793034Sdougm  * Free the description string.
21803034Sdougm  */
21813034Sdougm 
21823034Sdougm void
21833034Sdougm sa_free_share_description(char *description)
21843034Sdougm {
21853034Sdougm 	xmlFree((xmlChar *)description);
21863034Sdougm }
21873034Sdougm 
21883034Sdougm /*
21893034Sdougm  * sa_create_optionset(group, proto)
21903034Sdougm  *
21913034Sdougm  * Create an optionset for the specified protocol in the specied
21923034Sdougm  * group. This is manifested as a property group within SMF.
21933034Sdougm  */
21943034Sdougm 
21953034Sdougm sa_optionset_t
21963034Sdougm sa_create_optionset(sa_group_t group, char *proto)
21973034Sdougm {
21983034Sdougm 	sa_optionset_t optionset;
21993034Sdougm 	sa_group_t parent = group;
22003034Sdougm 
22013034Sdougm 	optionset = sa_get_optionset(group, proto);
22023034Sdougm 	if (optionset != NULL) {
22033034Sdougm 		/* can't have a duplicate protocol */
22043034Sdougm 	    optionset = NULL;
22053034Sdougm 	} else {
22063034Sdougm 	    optionset = (sa_optionset_t)xmlNewChild((xmlNodePtr)group,
22073034Sdougm 						    NULL,
22083034Sdougm 						    (xmlChar *)"optionset",
22093034Sdougm 						    NULL);
22103034Sdougm 		/*
22113034Sdougm 		 * only put to repository if on a group and we were
22123034Sdougm 		 * able to create an optionset.
22133034Sdougm 		 */
22143034Sdougm 	    if (optionset != NULL) {
22153034Sdougm 		char oname[256];
22163034Sdougm 		char *groupname;
22173034Sdougm 		char *id = NULL;
22183034Sdougm 
22193034Sdougm 		if (sa_is_share(group))
22203034Sdougm 		    parent = sa_get_parent_group((sa_share_t)group);
22213034Sdougm 
22223034Sdougm 		sa_set_optionset_attr(optionset, "type", proto);
22233034Sdougm 
22243034Sdougm 		if (sa_is_share(group)) {
22253034Sdougm 			id = sa_get_share_attr((sa_share_t)group, "id");
22263034Sdougm 		}
22273034Sdougm 		(void) sa_optionset_name(optionset, oname,
22283034Sdougm 					sizeof (oname), id);
22293034Sdougm 		groupname = sa_get_group_attr(parent, "name");
22303034Sdougm 		if (groupname != NULL && is_persistent(group)) {
22313910Sdougm 		    sa_handle_impl_t impl_handle;
22323910Sdougm 		    impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
22333910Sdougm 		    assert(impl_handle != NULL);
22343910Sdougm 		    if (impl_handle != NULL) {
22353910Sdougm 			(void) sa_get_instance(impl_handle->scfhandle,
22363910Sdougm 						groupname);
22373910Sdougm 			(void) sa_create_pgroup(impl_handle->scfhandle, oname);
22383910Sdougm 		    }
22393034Sdougm 		}
22403910Sdougm 		if (groupname != NULL)
22413910Sdougm 		    sa_free_attr_string(groupname);
22423034Sdougm 		if (id != NULL)
22433034Sdougm 		    sa_free_attr_string(id);
22443034Sdougm 	    }
22453034Sdougm 	}
22463034Sdougm 	return (optionset);
22473034Sdougm }
22483034Sdougm 
22493034Sdougm /*
22503034Sdougm  * sa_get_property_parent(property)
22513034Sdougm  *
22523034Sdougm  * Given a property, return the object it is a property of. This will
22533034Sdougm  * be an optionset of some type.
22543034Sdougm  */
22553034Sdougm 
22563034Sdougm static sa_optionset_t
22573034Sdougm sa_get_property_parent(sa_property_t property)
22583034Sdougm {
22593034Sdougm 	xmlNodePtr node = NULL;
22603034Sdougm 
22613034Sdougm 	if (property != NULL) {
22623034Sdougm 	    node = ((xmlNodePtr)property)->parent;
22633034Sdougm 	}
22643034Sdougm 	return ((sa_optionset_t)node);
22653034Sdougm }
22663034Sdougm 
22673034Sdougm /*
22683034Sdougm  * sa_get_optionset_parent(optionset)
22693034Sdougm  *
22703034Sdougm  * Return the parent of the specified optionset. This could be a group
22713034Sdougm  * or a share.
22723034Sdougm  */
22733034Sdougm 
22743034Sdougm static sa_group_t
22753034Sdougm sa_get_optionset_parent(sa_optionset_t optionset)
22763034Sdougm {
22773034Sdougm 	xmlNodePtr node = NULL;
22783034Sdougm 
22793034Sdougm 	if (optionset != NULL) {
22803034Sdougm 	    node = ((xmlNodePtr)optionset)->parent;
22813034Sdougm 	}
22823034Sdougm 	return ((sa_group_t)node);
22833034Sdougm }
22843034Sdougm 
22853034Sdougm /*
22863034Sdougm  * zfs_needs_update(share)
22873034Sdougm  *
22883034Sdougm  * In order to avoid making multiple updates to a ZFS share when
22893034Sdougm  * setting properties, the share attribute "changed" will be set to
22903034Sdougm  * true when a property is added or modifed.  When done adding
22913034Sdougm  * properties, we can then detect that an update is needed.  We then
22923034Sdougm  * clear the state here to detect additional changes.
22933034Sdougm  */
22943034Sdougm 
22953034Sdougm static int
22963034Sdougm zfs_needs_update(sa_share_t share)
22973034Sdougm {
22983034Sdougm 	char *attr;
22993034Sdougm 	int result = 0;
23003034Sdougm 
23013034Sdougm 	attr = sa_get_share_attr(share, "changed");
23023034Sdougm 	if (attr != NULL) {
23033034Sdougm 	    sa_free_attr_string(attr);
23043034Sdougm 		result = 1;
23053034Sdougm 	}
23063034Sdougm 	set_node_attr((void *)share, "changed", NULL);
23073034Sdougm 	return (result);
23083034Sdougm }
23093034Sdougm 
23103034Sdougm /*
23113034Sdougm  * zfs_set_update(share)
23123034Sdougm  *
23133034Sdougm  * Set the changed attribute of the share to true.
23143034Sdougm  */
23153034Sdougm 
23163034Sdougm static void
23173034Sdougm zfs_set_update(sa_share_t share)
23183034Sdougm {
23193034Sdougm 	set_node_attr((void *)share, "changed", "true");
23203034Sdougm }
23213034Sdougm 
23223034Sdougm /*
23233034Sdougm  * sa_commit_properties(optionset, clear)
23243034Sdougm  *
23253034Sdougm  * Check if SMF or ZFS config and either update or abort the pending
23263034Sdougm  * changes.
23273034Sdougm  */
23283034Sdougm 
23293034Sdougm int
23303034Sdougm sa_commit_properties(sa_optionset_t optionset, int clear)
23313034Sdougm {
23323034Sdougm 	sa_group_t group;
23333034Sdougm 	sa_group_t parent;
23343034Sdougm 	int zfs = 0;
23353034Sdougm 	int needsupdate = 0;
23363034Sdougm 	int ret = SA_OK;
23373910Sdougm 	sa_handle_impl_t impl_handle;
23383034Sdougm 
23393034Sdougm 	group = sa_get_optionset_parent(optionset);
23403034Sdougm 	if (group != NULL && (sa_is_share(group) || is_zfs_group(group))) {
23413034Sdougm 	    /* only update ZFS if on a share */
23423034Sdougm 	    parent = sa_get_parent_group(group);
23433034Sdougm 	    zfs++;
23443034Sdougm 	    if (parent != NULL && is_zfs_group(parent)) {
23453034Sdougm 		needsupdate = zfs_needs_update(group);
23463034Sdougm 	    } else {
23473034Sdougm 		zfs = 0;
23483034Sdougm 	    }
23493034Sdougm 	}
23503034Sdougm 	if (zfs) {
23513034Sdougm 	    if (!clear && needsupdate)
23523034Sdougm 		ret = sa_zfs_update((sa_share_t)group);
23533034Sdougm 	} else {
23543910Sdougm 	    impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
23553910Sdougm 	    if (impl_handle != NULL) {
23563910Sdougm 		if (clear)
23573910Sdougm 		    (void) sa_abort_transaction(impl_handle->scfhandle);
23583910Sdougm 		else
23593910Sdougm 		    ret = sa_end_transaction(impl_handle->scfhandle);
23603910Sdougm 	    } else {
23613910Sdougm 		ret = SA_SYSTEM_ERR;
23623910Sdougm 	    }
23633034Sdougm 	}
23643034Sdougm 	return (ret);
23653034Sdougm }
23663034Sdougm 
23673034Sdougm /*
23683034Sdougm  * sa_destroy_optionset(optionset)
23693034Sdougm  *
23703034Sdougm  * Remove the optionset from its group. Update the repostory to
23713034Sdougm  * reflect this change.
23723034Sdougm  */
23733034Sdougm 
23743034Sdougm int
23753034Sdougm sa_destroy_optionset(sa_optionset_t optionset)
23763034Sdougm {
23773034Sdougm 	char name[256];
23783034Sdougm 	int len;
23793034Sdougm 	int ret;
23803034Sdougm 	char *id = NULL;
23813034Sdougm 	sa_group_t group;
23823034Sdougm 	int ispersist = 1;
23833034Sdougm 
23843034Sdougm 	/* now delete the prop group */
23853034Sdougm 	group = sa_get_optionset_parent(optionset);
23863034Sdougm 	if (group != NULL && sa_is_share(group)) {
23873034Sdougm 	    ispersist = is_persistent(group);
23883034Sdougm 	    id = sa_get_share_attr((sa_share_t)group, "id");
23893034Sdougm 	}
23903034Sdougm 	if (ispersist) {
23913910Sdougm 	    sa_handle_impl_t impl_handle;
23923034Sdougm 	    len = sa_optionset_name(optionset, name, sizeof (name), id);
23933910Sdougm 	    impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
23943910Sdougm 	    if (impl_handle != NULL) {
23953910Sdougm 		if (len > 0) {
23963910Sdougm 		    ret = sa_delete_pgroup(impl_handle->scfhandle, name);
23973910Sdougm 		}
23983910Sdougm 	    } else {
23993910Sdougm 		ret = SA_SYSTEM_ERR;
24003034Sdougm 	    }
24013034Sdougm 	}
24023034Sdougm 	xmlUnlinkNode((xmlNodePtr)optionset);
24033034Sdougm 	xmlFreeNode((xmlNodePtr)optionset);
24043034Sdougm 	if (id != NULL)
24053034Sdougm 	    sa_free_attr_string(id);
24063034Sdougm 	return (ret);
24073034Sdougm }
24083034Sdougm 
24093034Sdougm /* private to the implementation */
24103034Sdougm int
24113034Sdougm _sa_remove_optionset(sa_optionset_t optionset)
24123034Sdougm {
24133034Sdougm 	int ret = SA_OK;
24143034Sdougm 
24153034Sdougm 	xmlUnlinkNode((xmlNodePtr)optionset);
24163034Sdougm 	xmlFreeNode((xmlNodePtr)optionset);
24173034Sdougm 	return (ret);
24183034Sdougm }
24193034Sdougm 
24203034Sdougm /*
24213034Sdougm  * sa_create_security(group, sectype, proto)
24223034Sdougm  *
24233034Sdougm  * Create a security optionset (one that has a type name and a
24243034Sdougm  * proto). Security is left over from a pure NFS implementation. The
24253034Sdougm  * naming will change in the future when the API is released.
24263034Sdougm  */
24273034Sdougm sa_security_t
24283034Sdougm sa_create_security(sa_group_t group, char *sectype, char *proto)
24293034Sdougm {
24303034Sdougm 	sa_security_t security;
24313034Sdougm 	char *id = NULL;
24323034Sdougm 	sa_group_t parent;
24333034Sdougm 	char *groupname = NULL;
24343034Sdougm 
24353034Sdougm 	if (group != NULL && sa_is_share(group)) {
24363034Sdougm 	    id = sa_get_share_attr((sa_share_t)group, "id");
24373034Sdougm 	    parent = sa_get_parent_group(group);
24383034Sdougm 	    if (parent != NULL)
24393034Sdougm 		groupname = sa_get_group_attr(parent, "name");
24403034Sdougm 	} else if (group != NULL) {
24413034Sdougm 	    groupname = sa_get_group_attr(group, "name");
24423034Sdougm 	}
24433034Sdougm 
24443034Sdougm 	security = sa_get_security(group, sectype, proto);
24453034Sdougm 	if (security != NULL) {
24463034Sdougm 		/* can't have a duplicate security option */
24473034Sdougm 		security = NULL;
24483034Sdougm 	} else {
24493034Sdougm 		security = (sa_security_t)xmlNewChild((xmlNodePtr)group,
24503034Sdougm 							NULL,
24513034Sdougm 							(xmlChar *)"security",
24523034Sdougm 							NULL);
24533034Sdougm 		if (security != NULL) {
24543034Sdougm 			char oname[256];
24553034Sdougm 			sa_set_security_attr(security, "type", proto);
24563034Sdougm 
24573034Sdougm 			sa_set_security_attr(security, "sectype", sectype);
24583034Sdougm 			(void) sa_security_name(security, oname,
24593034Sdougm 						sizeof (oname), id);
24603034Sdougm 			if (groupname != NULL && is_persistent(group)) {
24613910Sdougm 			    sa_handle_impl_t impl_handle;
24623910Sdougm 			    impl_handle =
24633910Sdougm 				(sa_handle_impl_t)sa_find_group_handle(group);
24643910Sdougm 			    if (impl_handle != NULL) {
24653910Sdougm 				(void) sa_get_instance(impl_handle->scfhandle,
24663910Sdougm 							groupname);
24673910Sdougm 				(void) sa_create_pgroup(impl_handle->scfhandle,
24683910Sdougm 							oname);
24693910Sdougm 			    }
24703034Sdougm 			}
24713034Sdougm 		}
24723034Sdougm 	}
24733034Sdougm 	if (groupname != NULL)
24743034Sdougm 	    sa_free_attr_string(groupname);
24753034Sdougm 	return (security);
24763034Sdougm }
24773034Sdougm 
24783034Sdougm /*
24793034Sdougm  * sa_destroy_security(security)
24803034Sdougm  *
24813034Sdougm  * Remove the specified optionset from the document and the
24823034Sdougm  * configuration.
24833034Sdougm  */
24843034Sdougm 
24853034Sdougm int
24863034Sdougm sa_destroy_security(sa_security_t security)
24873034Sdougm {
24883034Sdougm 	char name[256];
24893034Sdougm 	int len;
24903034Sdougm 	int ret = SA_OK;
24913034Sdougm 	char *id = NULL;
24923034Sdougm 	sa_group_t group;
24933034Sdougm 	int iszfs = 0;
24943034Sdougm 	int ispersist = 1;
24953034Sdougm 
24963034Sdougm 	group = sa_get_optionset_parent(security);
24973034Sdougm 
24983034Sdougm 	if (group != NULL)
24993034Sdougm 	    iszfs = sa_group_is_zfs(group);
25003034Sdougm 
25013034Sdougm 	if (group != NULL && !iszfs) {
25023034Sdougm 	    if (sa_is_share(group))
25033034Sdougm 		ispersist = is_persistent(group);
25043034Sdougm 	    id = sa_get_share_attr((sa_share_t)group, "id");
25053034Sdougm 	}
25063034Sdougm 	if (ispersist) {
25073034Sdougm 	    len = sa_security_name(security, name, sizeof (name), id);
25083034Sdougm 	    if (!iszfs && len > 0) {
25093910Sdougm 		sa_handle_impl_t impl_handle;
25103910Sdougm 		impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
25113910Sdougm 		if (impl_handle != NULL) {
25123910Sdougm 		    ret = sa_delete_pgroup(impl_handle->scfhandle, name);
25133910Sdougm 		} else {
25143910Sdougm 		    ret = SA_SYSTEM_ERR;
25153910Sdougm 		}
25163034Sdougm 	    }
25173034Sdougm 	}
25183034Sdougm 	xmlUnlinkNode((xmlNodePtr)security);
25193034Sdougm 	xmlFreeNode((xmlNodePtr)security);
25203034Sdougm 	if (iszfs) {
25213034Sdougm 	    ret = sa_zfs_update(group);
25223034Sdougm 	}
25233034Sdougm 	if (id != NULL)
25243034Sdougm 	    sa_free_attr_string(id);
25253034Sdougm 	return (ret);
25263034Sdougm }
25273034Sdougm 
25283034Sdougm /*
25293034Sdougm  * sa_get_security_attr(optionset, tag)
25303034Sdougm  *
25313034Sdougm  * Return the specified attribute value from the optionset.
25323034Sdougm  */
25333034Sdougm 
25343034Sdougm char *
25353034Sdougm sa_get_security_attr(sa_property_t optionset, char *tag)
25363034Sdougm {
25373034Sdougm 	return (get_node_attr((void *)optionset, tag));
25383034Sdougm 
25393034Sdougm }
25403034Sdougm 
25413034Sdougm /*
25423034Sdougm  * sa_set_security_attr(optionset, tag, value)
25433034Sdougm  *
25443034Sdougm  * Set the optioset attribute specied by tag to the specified value.
25453034Sdougm  */
25463034Sdougm 
25473034Sdougm void
25483034Sdougm sa_set_security_attr(sa_group_t optionset, char *tag, char *value)
25493034Sdougm {
25503034Sdougm 	set_node_attr((void *)optionset, tag, value);
25513034Sdougm }
25523034Sdougm 
25533034Sdougm /*
25543034Sdougm  * is_nodetype(node, type)
25553034Sdougm  *
25563034Sdougm  * Check to see if node is of the type specified.
25573034Sdougm  */
25583034Sdougm 
25593034Sdougm static int
25603034Sdougm is_nodetype(void *node, char *type)
25613034Sdougm {
25623034Sdougm 	return (strcmp((char *)((xmlNodePtr)node)->name, type) == 0);
25633034Sdougm }
25643034Sdougm 
25653034Sdougm /*
25663034Sdougm  * sa_set_prop_by_prop(optionset, group, prop, type)
25673034Sdougm  *
25683034Sdougm  * Add/remove/update the specified property prop into the optionset or
25693034Sdougm  * share. If a share, sort out which property group based on GUID. In
25703034Sdougm  * all cases, the appropriate transaction is set (or ZFS share is
25713034Sdougm  * marked as needing an update)
25723034Sdougm  */
25733034Sdougm 
25743034Sdougm #define	SA_PROP_OP_REMOVE	1
25753034Sdougm #define	SA_PROP_OP_ADD		2
25763034Sdougm #define	SA_PROP_OP_UPDATE	3
25773034Sdougm static int
25783034Sdougm sa_set_prop_by_prop(sa_optionset_t optionset, sa_group_t group,
25793034Sdougm 			sa_property_t prop, int type)
25803034Sdougm {
25813034Sdougm 	char *name;
25823034Sdougm 	char *valstr;
25833034Sdougm 	int ret = SA_OK;
25843034Sdougm 	scf_transaction_entry_t *entry;
25853034Sdougm 	scf_value_t *value;
25863034Sdougm 	int opttype; /* 1 == optionset, 0 == security */
25873034Sdougm 	char *id = NULL;
25883034Sdougm 	int iszfs = 0;
25893034Sdougm 	int isshare = 0;
25903034Sdougm 	sa_group_t parent = NULL;
25913910Sdougm 	sa_handle_impl_t impl_handle;
25923910Sdougm 	scfutilhandle_t  *scf_handle;
25933034Sdougm 
25943034Sdougm 	if (!is_persistent(group)) {
25953034Sdougm 		/*
25963034Sdougm 		 * if the group/share is not persistent we don't need
25973034Sdougm 		 * to do anything here
25983034Sdougm 		 */
25993034Sdougm 	    return (SA_OK);
26003034Sdougm 	}
26013910Sdougm 	impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
26023910Sdougm 	if (impl_handle == NULL || impl_handle->scfhandle == NULL) {
26033910Sdougm 	    return (SA_SYSTEM_ERR);
26043910Sdougm 	}
26053910Sdougm 	scf_handle = impl_handle->scfhandle;
26063034Sdougm 	name = sa_get_property_attr(prop, "type");
26073034Sdougm 	valstr = sa_get_property_attr(prop, "value");
26083034Sdougm 	entry = scf_entry_create(scf_handle->handle);
26093034Sdougm 	opttype = is_nodetype((void *)optionset, "optionset");
26103034Sdougm 
26113034Sdougm 	if (valstr != NULL && entry != NULL) {
26123034Sdougm 	    if (sa_is_share(group)) {
26133034Sdougm 		isshare = 1;
26143034Sdougm 		parent = sa_get_parent_group(group);
26153034Sdougm 		if (parent != NULL) {
26163034Sdougm 		    iszfs = is_zfs_group(parent);
26173034Sdougm 		}
26183034Sdougm 	    } else {
26193034Sdougm 		iszfs = is_zfs_group(group);
26203034Sdougm 	    }
26213034Sdougm 	    if (!iszfs) {
26223910Sdougm 		    if (scf_handle->trans == NULL) {
26233910Sdougm 			char oname[256];
26243910Sdougm 			char *groupname = NULL;
26253910Sdougm 			if (isshare) {
26263910Sdougm 			    if (parent != NULL) {
26273910Sdougm 				groupname = sa_get_group_attr(parent, "name");
26283910Sdougm 			    }
26293910Sdougm 			    id = sa_get_share_attr((sa_share_t)group, "id");
26303910Sdougm 			} else {
26313910Sdougm 			    groupname = sa_get_group_attr(group, "name");
26323034Sdougm 			}
26333910Sdougm 			if (groupname != NULL) {
26343910Sdougm 			    ret = sa_get_instance(scf_handle, groupname);
26353910Sdougm 			    sa_free_attr_string(groupname);
26363910Sdougm 			}
26373910Sdougm 			if (opttype)
26383910Sdougm 			    (void) sa_optionset_name(optionset, oname,
26393034Sdougm 							sizeof (oname), id);
26403910Sdougm 			else
26413910Sdougm 			    (void) sa_security_name(optionset, oname,
26423034Sdougm 							sizeof (oname), id);
26433910Sdougm 			ret = sa_start_transaction(scf_handle, oname);
26443910Sdougm 		    }
26453910Sdougm 		    if (ret == SA_OK) {
26463910Sdougm 			switch (type) {
26473910Sdougm 			case SA_PROP_OP_REMOVE:
26483910Sdougm 			    ret = scf_transaction_property_delete(
26493910Sdougm 							scf_handle->trans,
26503910Sdougm 							entry, name);
26513910Sdougm 			    break;
26523910Sdougm 			case SA_PROP_OP_ADD:
26533910Sdougm 			case SA_PROP_OP_UPDATE:
26543910Sdougm 			    value = scf_value_create(scf_handle->handle);
26553910Sdougm 			    if (value != NULL) {
26563910Sdougm 				if (type == SA_PROP_OP_ADD)
26573910Sdougm 				    ret = scf_transaction_property_new(
26583910Sdougm 							scf_handle->trans,
26593910Sdougm 							entry,
26603910Sdougm 							name,
26613910Sdougm 							SCF_TYPE_ASTRING);
26623910Sdougm 				else
26633910Sdougm 				    ret = scf_transaction_property_change(
26643910Sdougm 							scf_handle->trans,
26653910Sdougm 							entry,
26663910Sdougm 							name,
26673910Sdougm 							SCF_TYPE_ASTRING);
26683910Sdougm 				if (ret == 0) {
26693910Sdougm 				    ret = scf_value_set_astring(value, valstr);
26703910Sdougm 				    if (ret == 0)
26713910Sdougm 					ret = scf_entry_add_value(entry, value);
26723910Sdougm 				    if (ret != 0) {
26733910Sdougm 					scf_value_destroy(value);
26743910Sdougm 					ret = SA_SYSTEM_ERR;
26753910Sdougm 				    }
26763910Sdougm 				} else {
26773910Sdougm 				    scf_entry_destroy(entry);
26783034Sdougm 				    ret = SA_SYSTEM_ERR;
26793034Sdougm 				}
26803910Sdougm 				break;
26813034Sdougm 			    }
26823034Sdougm 			}
26833034Sdougm 		    }
26843034Sdougm 	    } else {
26853034Sdougm 		/*
26863034Sdougm 		 * ZFS update. The calling function would have updated
26873034Sdougm 		 * the internal XML structure. Just need to flag it as
26883034Sdougm 		 * changed for ZFS.
26893034Sdougm 		 */
26903034Sdougm 		zfs_set_update((sa_share_t)group);
26913034Sdougm 	    }
26923034Sdougm 	}
26933034Sdougm 
26943034Sdougm 	if (name != NULL)
26953034Sdougm 	    sa_free_attr_string(name);
26963034Sdougm 	if (valstr != NULL)
26973034Sdougm 	    sa_free_attr_string(valstr);
26983034Sdougm 	else if (entry != NULL)
26993034Sdougm 	    scf_entry_destroy(entry);
27003034Sdougm 
27013034Sdougm 	if (ret == -1)
27023034Sdougm 	    ret = SA_SYSTEM_ERR;
27033034Sdougm 
27043034Sdougm 	return (ret);
27053034Sdougm }
27063034Sdougm 
27073034Sdougm /*
27083034Sdougm  * sa_create_property(name, value)
27093034Sdougm  *
27103034Sdougm  * Create a new property with the specified name and value.
27113034Sdougm  */
27123034Sdougm 
27133034Sdougm sa_property_t
27143034Sdougm sa_create_property(char *name, char *value)
27153034Sdougm {
27163034Sdougm 	xmlNodePtr node;
27173034Sdougm 
27183034Sdougm 	node = xmlNewNode(NULL, (xmlChar *)"option");
27193034Sdougm 	if (node != NULL) {
27203034Sdougm 		xmlSetProp(node, (xmlChar *)"type", (xmlChar *)name);
27213034Sdougm 		xmlSetProp(node, (xmlChar *)"value", (xmlChar *)value);
27223034Sdougm 	}
27233034Sdougm 	return ((sa_property_t)node);
27243034Sdougm }
27253034Sdougm 
27263034Sdougm /*
27273034Sdougm  * sa_add_property(object, property)
27283034Sdougm  *
27293034Sdougm  * Add the specified property to the object. Issue the appropriate
27303034Sdougm  * transaction or mark a ZFS object as needing an update.
27313034Sdougm  */
27323034Sdougm 
27333034Sdougm int
27343034Sdougm sa_add_property(void *object, sa_property_t property)
27353034Sdougm {
27363034Sdougm 	int ret = SA_OK;
27373034Sdougm 	sa_group_t parent;
27383034Sdougm 	sa_group_t group;
27393034Sdougm 	char *proto;
27403034Sdougm 
27413034Sdougm 	proto = sa_get_optionset_attr(object, "type");
27423034Sdougm 	if (property != NULL) {
27433034Sdougm 	    if ((ret = sa_valid_property(object, proto, property)) == SA_OK) {
27443034Sdougm 		property = (sa_property_t)xmlAddChild((xmlNodePtr)object,
27453034Sdougm 							(xmlNodePtr)property);
27463034Sdougm 	    } else {
27473034Sdougm 		if (proto != NULL)
27483034Sdougm 		    sa_free_attr_string(proto);
27493034Sdougm 		return (ret);
27503034Sdougm 	    }
27513034Sdougm 	}
27523034Sdougm 
27533034Sdougm 	if (proto != NULL)
27543034Sdougm 	    sa_free_attr_string(proto);
27553034Sdougm 
27563034Sdougm 	parent = sa_get_parent_group(object);
27573034Sdougm 	if (!is_persistent(parent)) {
27583034Sdougm 	    return (ret);
27593034Sdougm 	}
27603034Sdougm 
27613034Sdougm 	if (sa_is_share(parent))
27623034Sdougm 	    group = sa_get_parent_group(parent);
27633034Sdougm 	else
27643034Sdougm 	    group = parent;
27653034Sdougm 
27663034Sdougm 	if (property == NULL)
27673034Sdougm 	    ret = SA_NO_MEMORY;
27683034Sdougm 	else {
27693034Sdougm 	    char oname[256];
27703034Sdougm 
27713034Sdougm 	    if (!is_zfs_group(group)) {
27723034Sdougm 		char *id = NULL;
27733910Sdougm 		sa_handle_impl_t impl_handle;
27743910Sdougm 		scfutilhandle_t  *scf_handle;
27753910Sdougm 
27763910Sdougm 		impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
27773910Sdougm 		if (impl_handle == NULL || impl_handle->scfhandle == NULL)
27783910Sdougm 		    ret = SA_SYSTEM_ERR;
27793034Sdougm 		if (ret == SA_OK) {
27803910Sdougm 		    scf_handle = impl_handle->scfhandle;
27813910Sdougm 		    if (sa_is_share((sa_group_t)parent)) {
27823910Sdougm 			id = sa_get_share_attr((sa_share_t)parent, "id");
27833910Sdougm 		    }
27843910Sdougm 		    if (scf_handle->trans == NULL) {
27853910Sdougm 			if (is_nodetype(object, "optionset"))
27863910Sdougm 			    (void) sa_optionset_name((sa_optionset_t)object,
27873910Sdougm 					    oname, sizeof (oname), id);
27883910Sdougm 			else
27893910Sdougm 			    (void) sa_security_name((sa_optionset_t)object,
27903910Sdougm 					    oname, sizeof (oname), id);
27913910Sdougm 			ret = sa_start_transaction(scf_handle, oname);
27923910Sdougm 		    }
27933910Sdougm 		    if (ret == SA_OK) {
27943910Sdougm 			char *name;
27953910Sdougm 			char *value;
27963910Sdougm 			name = sa_get_property_attr(property, "type");
27973910Sdougm 			value = sa_get_property_attr(property, "value");
27983910Sdougm 			if (name != NULL && value != NULL) {
27993910Sdougm 			    if (scf_handle->scf_state == SCH_STATE_INIT)
28003910Sdougm 				ret = sa_set_property(scf_handle, name, value);
28013910Sdougm 			} else
28023910Sdougm 			    ret = SA_CONFIG_ERR;
28033910Sdougm 			if (name != NULL)
28043910Sdougm 			    sa_free_attr_string(name);
28053910Sdougm 			if (value != NULL)
28063910Sdougm 			    sa_free_attr_string(value);
28073910Sdougm 		    }
28083910Sdougm 		    if (id != NULL)
28093910Sdougm 			sa_free_attr_string(id);
28103034Sdougm 		}
28113034Sdougm 	    } else {
28123034Sdougm 		/*
28133034Sdougm 		 * ZFS is a special case. We do want to allow editing
28143034Sdougm 		 * property/security lists since we can have a better
28153034Sdougm 		 * syntax and we also want to keep things consistent
28163034Sdougm 		 * when possible.
28173034Sdougm 		 *
28183034Sdougm 		 * Right now, we defer until the sa_commit_properties
28193034Sdougm 		 * so we can get them all at once. We do need to mark
28203034Sdougm 		 * the share as "changed"
28213034Sdougm 		 */
28223034Sdougm 		zfs_set_update((sa_share_t)parent);
28233034Sdougm 	    }
28243034Sdougm 	}
28253034Sdougm 	return (ret);
28263034Sdougm }
28273034Sdougm 
28283034Sdougm /*
28293034Sdougm  * sa_remove_property(property)
28303034Sdougm  *
28313034Sdougm  * Remove the specied property from its containing object. Update the
28323034Sdougm  * repository as appropriate.
28333034Sdougm  */
28343034Sdougm 
28353034Sdougm int
28363034Sdougm sa_remove_property(sa_property_t property)
28373034Sdougm {
28383034Sdougm 	int ret = SA_OK;
28393034Sdougm 
28403034Sdougm 	if (property != NULL) {
28413034Sdougm 		sa_optionset_t optionset;
28423034Sdougm 		sa_group_t group;
28433034Sdougm 		optionset = sa_get_property_parent(property);
28443034Sdougm 		if (optionset != NULL) {
28453034Sdougm 		    group = sa_get_optionset_parent(optionset);
28463034Sdougm 		    if (group != NULL) {
28473034Sdougm 			ret = sa_set_prop_by_prop(optionset, group, property,
28483034Sdougm 					    SA_PROP_OP_REMOVE);
28493034Sdougm 		    }
28503034Sdougm 		}
28513034Sdougm 		xmlUnlinkNode((xmlNodePtr)property);
28523034Sdougm 		xmlFreeNode((xmlNodePtr)property);
28533034Sdougm 	} else {
28543034Sdougm 	    ret = SA_NO_SUCH_PROP;
28553034Sdougm 	}
28563034Sdougm 	return (ret);
28573034Sdougm }
28583034Sdougm 
28593034Sdougm /*
28603034Sdougm  * sa_update_property(property, value)
28613034Sdougm  *
28623034Sdougm  * Update the specified property to the new value.  If value is NULL,
28633034Sdougm  * we currently treat this as a remove.
28643034Sdougm  */
28653034Sdougm 
28663034Sdougm int
28673034Sdougm sa_update_property(sa_property_t property, char *value)
28683034Sdougm {
28693034Sdougm 	int ret = SA_OK;
28703034Sdougm 	if (value == NULL) {
28713034Sdougm 		return (sa_remove_property(property));
28723034Sdougm 	} else {
28733034Sdougm 		sa_optionset_t optionset;
28743034Sdougm 		sa_group_t group;
28753034Sdougm 		set_node_attr((void *)property, "value", value);
28763034Sdougm 		optionset = sa_get_property_parent(property);
28773034Sdougm 		if (optionset != NULL) {
28783034Sdougm 		    group = sa_get_optionset_parent(optionset);
28793034Sdougm 		    if (group != NULL) {
28803034Sdougm 			ret = sa_set_prop_by_prop(optionset, group, property,
28813034Sdougm 					    SA_PROP_OP_UPDATE);
28823034Sdougm 		    }
28833034Sdougm 		} else {
28843034Sdougm 		    ret = SA_NO_SUCH_PROP;
28853034Sdougm 		}
28863034Sdougm 	}
28873034Sdougm 	return (ret);
28883034Sdougm }
28893034Sdougm 
28903034Sdougm /*
28913034Sdougm  * sa_get_protocol_property(propset, prop)
28923034Sdougm  *
28933034Sdougm  * Get the specified protocol specific property. These are global to
28943034Sdougm  * the protocol and not specific to a group or share.
28953034Sdougm  */
28963034Sdougm 
28973034Sdougm sa_property_t
28983034Sdougm sa_get_protocol_property(sa_protocol_properties_t propset, char *prop)
28993034Sdougm {
29003034Sdougm 	xmlNodePtr node = (xmlNodePtr)propset;
29013034Sdougm 	xmlChar *value = NULL;
29023034Sdougm 
29033034Sdougm 	for (node = node->children; node != NULL;
29043034Sdougm 		node = node->next) {
29053034Sdougm 	    if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) {
29063034Sdougm 		if (prop == NULL)
29073034Sdougm 		    break;
29083034Sdougm 		value = xmlGetProp(node, (xmlChar *)"type");
29093034Sdougm 		if (value != NULL &&
29103034Sdougm 		    xmlStrcasecmp(value, (xmlChar *)prop) == 0) {
29113034Sdougm 		    break;
29123034Sdougm 		}
29133034Sdougm 		if (value != NULL) {
29143034Sdougm 		    xmlFree(value);
29153034Sdougm 		    value = NULL;
29163034Sdougm 		}
29173034Sdougm 	    }
29183034Sdougm 	}
29193034Sdougm 	if (value != NULL)
29203034Sdougm 		xmlFree(value);
29213034Sdougm 	if (node != NULL && xmlStrcmp(node->name, (xmlChar *)"option") != 0) {
29223034Sdougm 	    /* avoid a non option node -- it is possible to be a text node */
29233034Sdougm 	    node = NULL;
29243034Sdougm 	}
29253034Sdougm 	return ((sa_property_t)node);
29263034Sdougm }
29273034Sdougm 
29283034Sdougm /*
29293034Sdougm  * sa_get_next_protocol_property(prop)
29303034Sdougm  *
29313034Sdougm  * Get the next protocol specific property in the list.
29323034Sdougm  */
29333034Sdougm 
29343034Sdougm sa_property_t
29353034Sdougm sa_get_next_protocol_property(sa_property_t prop)
29363034Sdougm {
29373034Sdougm 	xmlNodePtr node;
29383034Sdougm 
29393034Sdougm 	for (node = ((xmlNodePtr)prop)->next; node != NULL;
29403034Sdougm 		node = node->next) {
29413034Sdougm 		if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) {
29423034Sdougm 			break;
29433034Sdougm 		}
29443034Sdougm 	}
29453034Sdougm 	return ((sa_property_t)node);
29463034Sdougm }
29473034Sdougm 
29483034Sdougm /*
29493034Sdougm  * sa_set_protocol_property(prop, value)
29503034Sdougm  *
29513034Sdougm  * Set the specified property to have the new value.  The protocol
29523034Sdougm  * specific plugin will then be called to update the property.
29533034Sdougm  */
29543034Sdougm 
29553034Sdougm int
29563034Sdougm sa_set_protocol_property(sa_property_t prop, char *value)
29573034Sdougm {
29583034Sdougm 	sa_protocol_properties_t propset;
29593034Sdougm 	char *proto;
29603034Sdougm 	int ret = SA_INVALID_PROTOCOL;
29613034Sdougm 
29623034Sdougm 	propset = ((xmlNodePtr)prop)->parent;
29633034Sdougm 	if (propset != NULL) {
29643034Sdougm 	    proto = sa_get_optionset_attr(propset, "type");
29653034Sdougm 	    if (proto != NULL) {
29663034Sdougm 		set_node_attr((xmlNodePtr)prop, "value", value);
29673034Sdougm 		ret = sa_proto_set_property(proto, prop);
29683393Sdougm 		sa_free_attr_string(proto);
29693034Sdougm 	    }
29703034Sdougm 	}
29713034Sdougm 	return (ret);
29723034Sdougm }
29733034Sdougm 
29743034Sdougm /*
29753034Sdougm  * sa_add_protocol_property(propset, prop)
29763034Sdougm  *
29773034Sdougm  * Add a new property to the protocol sepcific property set.
29783034Sdougm  */
29793034Sdougm 
29803034Sdougm int
29813034Sdougm sa_add_protocol_property(sa_protocol_properties_t propset, sa_property_t prop)
29823034Sdougm {
29833034Sdougm 	xmlNodePtr node;
29843034Sdougm 
29853034Sdougm 	/* should check for legitimacy */
29863034Sdougm 	node = xmlAddChild((xmlNodePtr)propset, (xmlNodePtr)prop);
29873034Sdougm 	if (node != NULL)
29883034Sdougm 	    return (SA_OK);
29893034Sdougm 	return (SA_NO_MEMORY);
29903034Sdougm }
29913034Sdougm 
29923034Sdougm /*
29933034Sdougm  * sa_create_protocol_properties(proto)
29943034Sdougm  *
29953034Sdougm  * Create a protocol specifity property set.
29963034Sdougm  */
29973034Sdougm 
29983034Sdougm sa_protocol_properties_t
29993034Sdougm sa_create_protocol_properties(char *proto)
30003034Sdougm {
30013034Sdougm 	xmlNodePtr node;
30023034Sdougm 	node = xmlNewNode(NULL, (xmlChar *)"propertyset");
30033034Sdougm 	if (node != NULL) {
30043034Sdougm 	    xmlSetProp(node, (xmlChar *)"type", (xmlChar *)proto);
30053034Sdougm 	}
30063034Sdougm 	return (node);
30073034Sdougm }
3008