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