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