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