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