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