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