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