xref: /illumos-gate/usr/src/lib/libshare/common/libshare.c (revision dfc115332c94a2f62058ac7f2bce7631fbd20b3d)
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 2014 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_get_protocols(char **protocol)
1157  *	Get array of protocols that are supported
1158  *	Returns pointer to an allocated and NULL terminated
1159  *	array of strings.  Caller must free.
1160  *	This really should be determined dynamically.
1161  *	If there aren't any defined, return -1.
1162  *	Use free() to return memory.
1163  */
1164 
1165 int
1166 sa_get_protocols(char ***protocols)
1167 {
1168 	int numproto = -1;
1169 
1170 	if (protocols != NULL) {
1171 		struct sa_proto_plugin *plug;
1172 		for (numproto = 0, plug = sap_proto_list; plug != NULL;
1173 		    plug = plug->plugin_next) {
1174 			numproto++;
1175 		}
1176 
1177 		*protocols = calloc(numproto + 1,  sizeof (char *));
1178 		if (*protocols != NULL) {
1179 			int ret = 0;
1180 			for (plug = sap_proto_list; plug != NULL;
1181 			    plug = plug->plugin_next) {
1182 				/* faking for now */
1183 				(*protocols)[ret++] =
1184 				    plug->plugin_ops->sa_protocol;
1185 			}
1186 		} else {
1187 			numproto = -1;
1188 		}
1189 	}
1190 	return (numproto);
1191 }
1192 
1193 /*
1194  * find_group_by_name(node, group)
1195  *
1196  * search the XML document subtree specified by node to find the group
1197  * specified by group. Searching subtree allows subgroups to be
1198  * searched for.
1199  */
1200 
1201 static xmlNodePtr
1202 find_group_by_name(xmlNodePtr node, xmlChar *group)
1203 {
1204 	xmlChar *name = NULL;
1205 
1206 	for (node = node->xmlChildrenNode; node != NULL;
1207 	    node = node->next) {
1208 		if (xmlStrcmp(node->name, (xmlChar *)"group") == 0) {
1209 			/* if no groupname, return the first found */
1210 			if (group == NULL)
1211 				break;
1212 			name = xmlGetProp(node, (xmlChar *)"name");
1213 			if (name != NULL && xmlStrcmp(name, group) == 0)
1214 				break;
1215 			if (name != NULL) {
1216 				xmlFree(name);
1217 				name = NULL;
1218 			}
1219 		}
1220 	}
1221 	if (name != NULL)
1222 		xmlFree(name);
1223 	return (node);
1224 }
1225 
1226 /*
1227  * sa_get_group(groupname)
1228  *	Return the "group" specified.  If groupname is NULL,
1229  *	return the first group of the list of groups.
1230  */
1231 sa_group_t
1232 sa_get_group(sa_handle_t handle, char *groupname)
1233 {
1234 	xmlNodePtr node = NULL;
1235 	char *subgroup = NULL;
1236 	char *group = NULL;
1237 	sa_handle_impl_t impl_handle = (sa_handle_impl_t)handle;
1238 
1239 	if (impl_handle != NULL && impl_handle->tree != NULL) {
1240 		if (groupname != NULL) {
1241 			group = strdup(groupname);
1242 			if (group != NULL) {
1243 				subgroup = strchr(group, '/');
1244 				if (subgroup != NULL)
1245 					*subgroup++ = '\0';
1246 			}
1247 		}
1248 		/*
1249 		 * We want to find the, possibly, named group. If
1250 		 * group is not NULL, then lookup the name. If it is
1251 		 * NULL, we only do the find if groupname is also
1252 		 * NULL. This allows lookup of the "first" group in
1253 		 * the internal list.
1254 		 */
1255 		if (group != NULL || groupname == NULL)
1256 			node = find_group_by_name(impl_handle->tree,
1257 			    (xmlChar *)group);
1258 
1259 		/* if a subgroup, find it before returning */
1260 		if (subgroup != NULL && node != NULL)
1261 			node = find_group_by_name(node, (xmlChar *)subgroup);
1262 	}
1263 	if (node != NULL && (char *)group != NULL)
1264 		(void) sa_get_instance(impl_handle->scfhandle, (char *)group);
1265 	if (group != NULL)
1266 		free(group);
1267 	return ((sa_group_t)(node));
1268 }
1269 
1270 /*
1271  * sa_get_next_group(group)
1272  *	Return the "next" group after the specified group from
1273  *	the internal group list.  NULL if there are no more.
1274  */
1275 sa_group_t
1276 sa_get_next_group(sa_group_t group)
1277 {
1278 	xmlNodePtr ngroup = NULL;
1279 	if (group != NULL) {
1280 		for (ngroup = ((xmlNodePtr)group)->next; ngroup != NULL;
1281 		    ngroup = ngroup->next) {
1282 			if (xmlStrcmp(ngroup->name, (xmlChar *)"group") == 0)
1283 				break;
1284 		}
1285 	}
1286 	return ((sa_group_t)ngroup);
1287 }
1288 
1289 /*
1290  * sa_get_share(group, sharepath)
1291  *	Return the share object for the share specified. The share
1292  *	must be in the specified group.  Return NULL if not found.
1293  */
1294 sa_share_t
1295 sa_get_share(sa_group_t group, char *sharepath)
1296 {
1297 	xmlNodePtr node = NULL;
1298 	xmlChar *path;
1299 
1300 	/*
1301 	 * For future scalability, this should end up building a cache
1302 	 * since it will get called regularly by the mountd and info
1303 	 * services.
1304 	 */
1305 	if (group != NULL) {
1306 		for (node = ((xmlNodePtr)group)->children; node != NULL;
1307 		    node = node->next) {
1308 			if (xmlStrcmp(node->name, (xmlChar *)"share") == 0) {
1309 				if (sharepath == NULL) {
1310 					break;
1311 				} else {
1312 					/* is it the correct share? */
1313 					path = xmlGetProp(node,
1314 					    (xmlChar *)"path");
1315 					if (path != NULL &&
1316 					    xmlStrcmp(path,
1317 					    (xmlChar *)sharepath) == 0) {
1318 						xmlFree(path);
1319 						break;
1320 					}
1321 					xmlFree(path);
1322 				}
1323 			}
1324 		}
1325 	}
1326 	return ((sa_share_t)node);
1327 }
1328 
1329 /*
1330  * sa_get_next_share(share)
1331  *	Return the next share following the specified share
1332  *	from the internal list of shares. Returns NULL if there
1333  *	are no more shares.  The list is relative to the same
1334  *	group.
1335  */
1336 sa_share_t
1337 sa_get_next_share(sa_share_t share)
1338 {
1339 	xmlNodePtr node = NULL;
1340 
1341 	if (share != NULL) {
1342 		for (node = ((xmlNodePtr)share)->next; node != NULL;
1343 		    node = node->next) {
1344 			if (xmlStrcmp(node->name, (xmlChar *)"share") == 0) {
1345 				break;
1346 			}
1347 		}
1348 	}
1349 	return ((sa_share_t)node);
1350 }
1351 
1352 /*
1353  * _sa_get_child_node(node, type)
1354  *
1355  * find the child node of the specified node that has "type". This is
1356  * used to implement several internal functions.
1357  */
1358 
1359 static xmlNodePtr
1360 _sa_get_child_node(xmlNodePtr node, xmlChar *type)
1361 {
1362 	xmlNodePtr child;
1363 	for (child = node->xmlChildrenNode; child != NULL;
1364 	    child = child->next)
1365 		if (xmlStrcmp(child->name, type) == 0)
1366 			return (child);
1367 	return ((xmlNodePtr)NULL);
1368 }
1369 
1370 /*
1371  *  find_share(group, path)
1372  *
1373  * Search all the shares in the specified group for one that has the
1374  * specified path.
1375  */
1376 
1377 static sa_share_t
1378 find_share(sa_group_t group, char *sharepath)
1379 {
1380 	sa_share_t share;
1381 	char *path;
1382 
1383 	for (share = sa_get_share(group, NULL); share != NULL;
1384 	    share = sa_get_next_share(share)) {
1385 		path = sa_get_share_attr(share, "path");
1386 		if (path != NULL && strcmp(path, sharepath) == 0) {
1387 			sa_free_attr_string(path);
1388 			break;
1389 		}
1390 		if (path != NULL)
1391 			sa_free_attr_string(path);
1392 	}
1393 	return (share);
1394 }
1395 
1396 /*
1397  * sa_get_sub_group(group)
1398  *
1399  * Get the first sub-group of group. The sa_get_next_group() function
1400  * can be used to get the rest. This is currently only used for ZFS
1401  * sub-groups but could be used to implement a more general mechanism.
1402  */
1403 
1404 sa_group_t
1405 sa_get_sub_group(sa_group_t group)
1406 {
1407 	return ((sa_group_t)_sa_get_child_node((xmlNodePtr)group,
1408 	    (xmlChar *)"group"));
1409 }
1410 
1411 /*
1412  * sa_find_share(sharepath)
1413  *	Finds a share regardless of group.  In the future, this
1414  *	function should utilize a cache and hash table of some kind.
1415  *	The current assumption is that a path will only be shared
1416  *	once.  In the future, this may change as implementation of
1417  *	resource names comes into being.
1418  */
1419 sa_share_t
1420 sa_find_share(sa_handle_t handle, char *sharepath)
1421 {
1422 	sa_group_t group;
1423 	sa_group_t zgroup;
1424 	sa_share_t share = NULL;
1425 	int done = 0;
1426 
1427 	for (group = sa_get_group(handle, NULL); group != NULL && !done;
1428 	    group = sa_get_next_group(group)) {
1429 		if (is_zfs_group(group)) {
1430 			for (zgroup =
1431 			    (sa_group_t)_sa_get_child_node((xmlNodePtr)group,
1432 			    (xmlChar *)"group");
1433 			    zgroup != NULL;
1434 			    zgroup = sa_get_next_group(zgroup)) {
1435 				share = find_share(zgroup, sharepath);
1436 				if (share != NULL)
1437 					break;
1438 			}
1439 		} else {
1440 			share = find_share(group, sharepath);
1441 		}
1442 		if (share != NULL)
1443 			break;
1444 	}
1445 	return (share);
1446 }
1447 
1448 /*
1449  *  sa_check_path(group, path, strictness)
1450  *
1451  * Check that path is a valid path relative to the group.  Currently,
1452  * we are ignoring the group and checking only the NFS rules. Later,
1453  * we may want to use the group to then check against the protocols
1454  * enabled on the group. The strictness values mean:
1455  * SA_CHECK_NORMAL == only check newpath against shares that are active
1456  * SA_CHECK_STRICT == check newpath against both active shares and those
1457  *		      stored in the repository
1458  */
1459 
1460 int
1461 sa_check_path(sa_group_t group, char *path, int strictness)
1462 {
1463 	sa_handle_t handle;
1464 
1465 	handle = sa_find_group_handle(group);
1466 	if (handle == NULL)
1467 		return (SA_BAD_PATH);
1468 
1469 	return (validpath(handle, path, strictness));
1470 }
1471 
1472 /*
1473  * mark_excluded_protos(group, share, flags)
1474  *
1475  * Walk through all the protocols enabled for the group and check to
1476  * see if the share has any of them should be in the exclude list
1477  * based on the featureset of the protocol. If there are any, add the
1478  * "exclude" property to the share.
1479  */
1480 static void
1481 mark_excluded_protos(sa_group_t group, xmlNodePtr share, uint64_t flags)
1482 {
1483 	sa_optionset_t optionset;
1484 	char exclude_list[SA_STRSIZE];
1485 	char *sep = "";
1486 
1487 	exclude_list[0] = '\0';
1488 	for (optionset = sa_get_optionset(group, NULL);
1489 	    optionset != NULL;
1490 	    optionset = sa_get_next_optionset(optionset)) {
1491 		char *value;
1492 		uint64_t features;
1493 		value = sa_get_optionset_attr(optionset, "type");
1494 		if (value == NULL)
1495 			continue;
1496 		features = sa_proto_get_featureset(value);
1497 		if (!(features & flags)) {
1498 			(void) strlcat(exclude_list, sep,
1499 			    sizeof (exclude_list));
1500 			(void) strlcat(exclude_list, value,
1501 			    sizeof (exclude_list));
1502 			sep = ",";
1503 		}
1504 		sa_free_attr_string(value);
1505 	}
1506 	if (exclude_list[0] != '\0')
1507 		(void) xmlSetProp(share, (xmlChar *)"exclude",
1508 		    (xmlChar *)exclude_list);
1509 }
1510 
1511 /*
1512  * get_all_features(group)
1513  *
1514  * Walk through all the protocols on the group and collect all
1515  * possible enabled features. This is the OR of all the featuresets.
1516  */
1517 static uint64_t
1518 get_all_features(sa_group_t group)
1519 {
1520 	sa_optionset_t optionset;
1521 	uint64_t features = 0;
1522 
1523 	for (optionset = sa_get_optionset(group, NULL);
1524 	    optionset != NULL;
1525 	    optionset = sa_get_next_optionset(optionset)) {
1526 		char *value;
1527 		value = sa_get_optionset_attr(optionset, "type");
1528 		if (value == NULL)
1529 			continue;
1530 		features |= sa_proto_get_featureset(value);
1531 		sa_free_attr_string(value);
1532 	}
1533 	return (features);
1534 }
1535 
1536 
1537 /*
1538  * _sa_add_share(group, sharepath, persist, *error, flags)
1539  *
1540  * Common code for all types of add_share. sa_add_share() is the
1541  * public API, we also need to be able to do this when parsing legacy
1542  * files and construction of the internal configuration while
1543  * extracting config info from SMF. "flags" indicates if some
1544  * protocols need relaxed rules while other don't. These values are
1545  * the featureset values defined in libshare.h.
1546  */
1547 
1548 sa_share_t
1549 _sa_add_share(sa_group_t group, char *sharepath, int persist, int *error,
1550     uint64_t flags)
1551 {
1552 	xmlNodePtr node = NULL;
1553 	int err;
1554 
1555 	err  = SA_OK; /* assume success */
1556 
1557 	node = xmlNewChild((xmlNodePtr)group, NULL, (xmlChar *)"share", NULL);
1558 	if (node == NULL) {
1559 		if (error != NULL)
1560 			*error = SA_NO_MEMORY;
1561 		return (node);
1562 	}
1563 
1564 	(void) xmlSetProp(node, (xmlChar *)"path", (xmlChar *)sharepath);
1565 	(void) xmlSetProp(node, (xmlChar *)"type",
1566 	    persist ? (xmlChar *)"persist" : (xmlChar *)"transient");
1567 	if (flags != 0)
1568 		mark_excluded_protos(group, node, flags);
1569 	if (persist != SA_SHARE_TRANSIENT) {
1570 		/*
1571 		 * persistent shares come in two flavors: SMF and
1572 		 * ZFS. Sort this one out based on target group and
1573 		 * path type. Both NFS and SMB are supported. First,
1574 		 * check to see if the protocol is enabled on the
1575 		 * subgroup and then setup the share appropriately.
1576 		 */
1577 		if (sa_group_is_zfs(group) &&
1578 		    sa_path_is_zfs(sharepath)) {
1579 			if (sa_get_optionset(group, "nfs") != NULL)
1580 				err = sa_zfs_set_sharenfs(group, sharepath, 1);
1581 			else if (sa_get_optionset(group, "smb") != NULL)
1582 				err = sa_zfs_set_sharesmb(group, sharepath, 1);
1583 		} else {
1584 			sa_handle_impl_t impl_handle;
1585 			impl_handle =
1586 			    (sa_handle_impl_t)sa_find_group_handle(group);
1587 			if (impl_handle != NULL) {
1588 				err = sa_commit_share(impl_handle->scfhandle,
1589 				    group, (sa_share_t)node);
1590 			} else {
1591 				err = SA_SYSTEM_ERR;
1592 			}
1593 		}
1594 	}
1595 	if (err == SA_NO_PERMISSION && persist & SA_SHARE_PARSER)
1596 		/* called by the dfstab parser so could be a show */
1597 		err = SA_OK;
1598 
1599 	if (err != SA_OK) {
1600 		/*
1601 		 * we couldn't commit to the repository so undo
1602 		 * our internal state to reflect reality.
1603 		 */
1604 		xmlUnlinkNode(node);
1605 		xmlFreeNode(node);
1606 		node = NULL;
1607 	}
1608 
1609 	if (error != NULL)
1610 		*error = err;
1611 
1612 	return (node);
1613 }
1614 
1615 /*
1616  * sa_add_share(group, sharepath, persist, *error)
1617  *
1618  *	Add a new share object to the specified group.  The share will
1619  *	have the specified sharepath and will only be constructed if
1620  *	it is a valid path to be shared.  NULL is returned on error
1621  *	and a detailed error value will be returned via the error
1622  *	pointer.
1623  */
1624 sa_share_t
1625 sa_add_share(sa_group_t group, char *sharepath, int persist, int *error)
1626 {
1627 	xmlNodePtr node = NULL;
1628 	int strictness = SA_CHECK_NORMAL;
1629 	sa_handle_t handle;
1630 	uint64_t special = 0;
1631 	uint64_t features;
1632 
1633 	/*
1634 	 * If the share is to be permanent, use strict checking so a
1635 	 * bad config doesn't get created. Transient shares only need
1636 	 * to check against the currently active
1637 	 * shares. SA_SHARE_PARSER is a modifier used internally to
1638 	 * indicate that we are being called by the dfstab parser and
1639 	 * that we need strict checking in all cases. Normally persist
1640 	 * is in integer value but SA_SHARE_PARSER may be or'd into
1641 	 * it as an override.
1642 	 */
1643 	if (persist & SA_SHARE_PARSER || persist == SA_SHARE_PERMANENT)
1644 		strictness = SA_CHECK_STRICT;
1645 
1646 	handle = sa_find_group_handle(group);
1647 
1648 	/*
1649 	 * need to determine if the share is valid. The rules are:
1650 	 *	- The path must not already exist
1651 	 *	- The path must not be a subdir or parent dir of an
1652 	 *	  existing path unless at least one protocol allows it.
1653 	 * The sub/parent check is done in sa_check_path().
1654 	 */
1655 
1656 	if (sa_find_share(handle, sharepath) == NULL) {
1657 		*error = sa_check_path(group, sharepath, strictness);
1658 		features = get_all_features(group);
1659 		switch (*error) {
1660 		case SA_PATH_IS_SUBDIR:
1661 			if (features & SA_FEATURE_ALLOWSUBDIRS)
1662 				special |= SA_FEATURE_ALLOWSUBDIRS;
1663 			break;
1664 		case SA_PATH_IS_PARENTDIR:
1665 			if (features & SA_FEATURE_ALLOWPARDIRS)
1666 				special |= SA_FEATURE_ALLOWPARDIRS;
1667 			break;
1668 		}
1669 		if (*error == SA_OK || special != SA_FEATURE_NONE)
1670 			node = _sa_add_share(group, sharepath, persist,
1671 			    error, special);
1672 	} else {
1673 		*error = SA_DUPLICATE_NAME;
1674 	}
1675 
1676 	return ((sa_share_t)node);
1677 }
1678 
1679 /*
1680  * sa_enable_share(share, protocol)
1681  *	Enable the specified share to the specified protocol.
1682  *	If protocol is NULL, then all protocols.
1683  */
1684 int
1685 sa_enable_share(sa_share_t share, char *protocol)
1686 {
1687 	char *sharepath;
1688 	struct stat st;
1689 	int err = SA_OK;
1690 	int ret;
1691 
1692 	sharepath = sa_get_share_attr(share, "path");
1693 	if (sharepath == NULL)
1694 		return (SA_NO_MEMORY);
1695 	if (stat(sharepath, &st) < 0) {
1696 		err = SA_NO_SUCH_PATH;
1697 	} else {
1698 		/* tell the server about the share */
1699 		if (protocol != NULL) {
1700 			if (excluded_protocol(share, protocol))
1701 				goto done;
1702 
1703 			/* lookup protocol specific handler */
1704 			err = sa_proto_share(protocol, share);
1705 			if (err == SA_OK)
1706 				(void) sa_set_share_attr(share,
1707 				    "shared", "true");
1708 		} else {
1709 			/* Tell all protocols about the share */
1710 			sa_group_t group;
1711 			sa_optionset_t optionset;
1712 
1713 			group = sa_get_parent_group(share);
1714 
1715 			for (optionset = sa_get_optionset(group, NULL);
1716 			    optionset != NULL;
1717 			    optionset = sa_get_next_optionset(optionset)) {
1718 				char *proto;
1719 				proto = sa_get_optionset_attr(optionset,
1720 				    "type");
1721 				if (proto != NULL) {
1722 					if (!excluded_protocol(share, proto)) {
1723 						ret = sa_proto_share(proto,
1724 						    share);
1725 						if (ret != SA_OK)
1726 							err = ret;
1727 					}
1728 					sa_free_attr_string(proto);
1729 				}
1730 			}
1731 			(void) sa_set_share_attr(share, "shared", "true");
1732 		}
1733 	}
1734 done:
1735 	if (sharepath != NULL)
1736 		sa_free_attr_string(sharepath);
1737 	return (err);
1738 }
1739 
1740 /*
1741  * sa_disable_share(share, protocol)
1742  *	Disable the specified share to the specified protocol.  If
1743  *	protocol is NULL, then all protocols that are enabled for the
1744  *	share should be disabled.
1745  */
1746 int
1747 sa_disable_share(sa_share_t share, char *protocol)
1748 {
1749 	char *path;
1750 	int err = SA_OK;
1751 	int ret = SA_OK;
1752 
1753 	path = sa_get_share_attr(share, "path");
1754 
1755 	if (protocol != NULL) {
1756 		ret = sa_proto_unshare(share, protocol, path);
1757 	} else {
1758 		/* need to do all protocols */
1759 		sa_group_t group;
1760 		sa_optionset_t optionset;
1761 
1762 		group = sa_get_parent_group(share);
1763 
1764 		/* Tell all protocols about the share */
1765 		for (optionset = sa_get_optionset(group, NULL);
1766 		    optionset != NULL;
1767 		    optionset = sa_get_next_optionset(optionset)) {
1768 			char *proto;
1769 
1770 			proto = sa_get_optionset_attr(optionset, "type");
1771 			if (proto != NULL) {
1772 				err = sa_proto_unshare(share, proto, path);
1773 				if (err != SA_OK)
1774 					ret = err;
1775 				sa_free_attr_string(proto);
1776 			}
1777 		}
1778 	}
1779 	if (ret == SA_OK)
1780 		(void) sa_set_share_attr(share, "shared", NULL);
1781 	if (path != NULL)
1782 		sa_free_attr_string(path);
1783 	return (ret);
1784 }
1785 
1786 /*
1787  * sa_remove_share(share)
1788  *
1789  * remove the specified share from its containing group.
1790  * Remove from the SMF or ZFS configuration space.
1791  */
1792 
1793 int
1794 sa_remove_share(sa_share_t share)
1795 {
1796 	sa_group_t group;
1797 	int ret = SA_OK;
1798 	char *type;
1799 	int transient = 0;
1800 	char *groupname;
1801 	char *zfs;
1802 
1803 	type = sa_get_share_attr(share, "type");
1804 	group = sa_get_parent_group(share);
1805 	zfs = sa_get_group_attr(group, "zfs");
1806 	groupname = sa_get_group_attr(group, "name");
1807 	if (type != NULL && strcmp(type, "persist") != 0)
1808 		transient = 1;
1809 	if (type != NULL)
1810 		sa_free_attr_string(type);
1811 
1812 	/* remove the node from its group then free the memory */
1813 
1814 	/*
1815 	 * need to test if "busy"
1816 	 */
1817 	/* only do SMF action if permanent */
1818 	if (!transient || zfs != NULL) {
1819 		/* remove from legacy dfstab as well as possible SMF */
1820 		ret = sa_delete_legacy(share, NULL);
1821 		if (ret == SA_OK) {
1822 			if (!sa_group_is_zfs(group)) {
1823 				sa_handle_impl_t impl_handle;
1824 				impl_handle = (sa_handle_impl_t)
1825 				    sa_find_group_handle(group);
1826 				if (impl_handle != NULL) {
1827 					ret = sa_delete_share(
1828 					    impl_handle->scfhandle, group,
1829 					    share);
1830 				} else {
1831 					ret = SA_SYSTEM_ERR;
1832 				}
1833 			} else {
1834 				char *sharepath = sa_get_share_attr(share,
1835 				    "path");
1836 				if (sharepath != NULL) {
1837 					ret = sa_zfs_set_sharenfs(group,
1838 					    sharepath, 0);
1839 					sa_free_attr_string(sharepath);
1840 				}
1841 			}
1842 		}
1843 	}
1844 	if (groupname != NULL)
1845 		sa_free_attr_string(groupname);
1846 	if (zfs != NULL)
1847 		sa_free_attr_string(zfs);
1848 
1849 	xmlUnlinkNode((xmlNodePtr)share);
1850 	xmlFreeNode((xmlNodePtr)share);
1851 	return (ret);
1852 }
1853 
1854 /*
1855  * sa_move_share(group, share)
1856  *
1857  * move the specified share to the specified group.  Update SMF
1858  * appropriately.
1859  */
1860 
1861 int
1862 sa_move_share(sa_group_t group, sa_share_t share)
1863 {
1864 	sa_group_t oldgroup;
1865 	int ret = SA_OK;
1866 
1867 	/* remove the node from its group then free the memory */
1868 
1869 	oldgroup = sa_get_parent_group(share);
1870 	if (oldgroup != group) {
1871 		sa_handle_impl_t impl_handle;
1872 		xmlUnlinkNode((xmlNodePtr)share);
1873 		/*
1874 		 * now that the share isn't in its old group, add to
1875 		 * the new one
1876 		 */
1877 		(void) xmlAddChild((xmlNodePtr)group, (xmlNodePtr)share);
1878 		/* need to deal with SMF */
1879 		impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
1880 		if (impl_handle != NULL) {
1881 			/*
1882 			 * need to remove from old group first and then add to
1883 			 * new group. Ideally, we would do the other order but
1884 			 * need to avoid having the share in two groups at the
1885 			 * same time.
1886 			 */
1887 			ret = sa_delete_share(impl_handle->scfhandle, oldgroup,
1888 			    share);
1889 			if (ret == SA_OK)
1890 				ret = sa_commit_share(impl_handle->scfhandle,
1891 				    group, share);
1892 		} else {
1893 			ret = SA_SYSTEM_ERR;
1894 		}
1895 	}
1896 	return (ret);
1897 }
1898 
1899 /*
1900  * sa_get_parent_group(share)
1901  *
1902  * Return the containing group for the share. If a group was actually
1903  * passed in, we don't want a parent so return NULL.
1904  */
1905 
1906 sa_group_t
1907 sa_get_parent_group(sa_share_t share)
1908 {
1909 	xmlNodePtr node = NULL;
1910 	if (share != NULL) {
1911 		node = ((xmlNodePtr)share)->parent;
1912 		/*
1913 		 * make sure parent is a group and not sharecfg since
1914 		 * we may be cheating and passing in a group.
1915 		 * Eventually, groups of groups might come into being.
1916 		 */
1917 		if (node == NULL ||
1918 		    xmlStrcmp(node->name, (xmlChar *)"sharecfg") == 0)
1919 			node = NULL;
1920 	}
1921 	return ((sa_group_t)node);
1922 }
1923 
1924 /*
1925  * _sa_create_group(impl_handle, groupname)
1926  *
1927  * Create a group in the document. The caller will need to deal with
1928  * configuration store and activation.
1929  */
1930 
1931 sa_group_t
1932 _sa_create_group(sa_handle_impl_t impl_handle, char *groupname)
1933 {
1934 	xmlNodePtr node = NULL;
1935 
1936 	if (sa_valid_group_name(groupname)) {
1937 		node = xmlNewChild(impl_handle->tree, NULL, (xmlChar *)"group",
1938 		    NULL);
1939 		if (node != NULL) {
1940 			(void) xmlSetProp(node, (xmlChar *)"name",
1941 			    (xmlChar *)groupname);
1942 			(void) xmlSetProp(node, (xmlChar *)"state",
1943 			    (xmlChar *)"enabled");
1944 		}
1945 	}
1946 	return ((sa_group_t)node);
1947 }
1948 
1949 /*
1950  * _sa_create_zfs_group(group, groupname)
1951  *
1952  * Create a ZFS subgroup under the specified group. This may
1953  * eventually form the basis of general sub-groups, but is currently
1954  * restricted to ZFS.
1955  */
1956 sa_group_t
1957 _sa_create_zfs_group(sa_group_t group, char *groupname)
1958 {
1959 	xmlNodePtr node = NULL;
1960 
1961 	node = xmlNewChild((xmlNodePtr)group, NULL, (xmlChar *)"group", NULL);
1962 	if (node != NULL) {
1963 		(void) xmlSetProp(node, (xmlChar *)"name",
1964 		    (xmlChar *)groupname);
1965 		(void) xmlSetProp(node, (xmlChar *)"state",
1966 		    (xmlChar *)"enabled");
1967 	}
1968 
1969 	return ((sa_group_t)node);
1970 }
1971 
1972 /*
1973  * sa_create_group(groupname, *error)
1974  *
1975  * Create a new group with groupname.  Need to validate that it is a
1976  * legal name for SMF and the construct the SMF service instance of
1977  * svc:/network/shares/group to implement the group. All necessary
1978  * operational properties must be added to the group at this point
1979  * (via the SMF transaction model).
1980  */
1981 sa_group_t
1982 sa_create_group(sa_handle_t handle, char *groupname, int *error)
1983 {
1984 	xmlNodePtr node = NULL;
1985 	sa_group_t group;
1986 	int ret;
1987 	char rbacstr[SA_STRSIZE];
1988 	sa_handle_impl_t impl_handle = (sa_handle_impl_t)handle;
1989 
1990 	ret = SA_OK;
1991 
1992 	if (impl_handle == NULL || impl_handle->scfhandle == NULL) {
1993 		ret = SA_SYSTEM_ERR;
1994 		goto err;
1995 	}
1996 
1997 	group = sa_get_group(handle, groupname);
1998 	if (group != NULL) {
1999 		ret = SA_DUPLICATE_NAME;
2000 	} else {
2001 		if (sa_valid_group_name(groupname)) {
2002 			node = xmlNewChild(impl_handle->tree, NULL,
2003 			    (xmlChar *)"group", NULL);
2004 			if (node != NULL) {
2005 				(void) xmlSetProp(node, (xmlChar *)"name",
2006 				    (xmlChar *)groupname);
2007 				/* default to the group being enabled */
2008 				(void) xmlSetProp(node, (xmlChar *)"state",
2009 				    (xmlChar *)"enabled");
2010 				ret = sa_create_instance(impl_handle->scfhandle,
2011 				    groupname);
2012 				if (ret == SA_OK) {
2013 					ret = sa_start_transaction(
2014 					    impl_handle->scfhandle,
2015 					    "operation");
2016 				}
2017 				if (ret == SA_OK) {
2018 					ret = sa_set_property(
2019 					    impl_handle->scfhandle,
2020 					    "state", "enabled");
2021 					if (ret == SA_OK) {
2022 						ret = sa_end_transaction(
2023 						    impl_handle->scfhandle,
2024 						    impl_handle);
2025 					} else {
2026 						sa_abort_transaction(
2027 						    impl_handle->scfhandle);
2028 					}
2029 				}
2030 				if (ret == SA_OK) {
2031 					/* initialize the RBAC strings */
2032 					ret = sa_start_transaction(
2033 					    impl_handle->scfhandle,
2034 					    "general");
2035 					if (ret == SA_OK) {
2036 						(void) snprintf(rbacstr,
2037 						    sizeof (rbacstr), "%s.%s",
2038 						    SA_RBAC_MANAGE, groupname);
2039 						ret = sa_set_property(
2040 						    impl_handle->scfhandle,
2041 						    "action_authorization",
2042 						    rbacstr);
2043 					}
2044 					if (ret == SA_OK) {
2045 						(void) snprintf(rbacstr,
2046 						    sizeof (rbacstr), "%s.%s",
2047 						    SA_RBAC_VALUE, groupname);
2048 						ret = sa_set_property(
2049 						    impl_handle->scfhandle,
2050 						    "value_authorization",
2051 						    rbacstr);
2052 					}
2053 					if (ret == SA_OK) {
2054 						ret = sa_end_transaction(
2055 						    impl_handle->scfhandle,
2056 						    impl_handle);
2057 					} else {
2058 						sa_abort_transaction(
2059 						    impl_handle->scfhandle);
2060 					}
2061 				}
2062 				if (ret != SA_OK) {
2063 					/*
2064 					 * Couldn't commit the group
2065 					 * so we need to undo
2066 					 * internally.
2067 					 */
2068 					xmlUnlinkNode(node);
2069 					xmlFreeNode(node);
2070 					node = NULL;
2071 				}
2072 			} else {
2073 				ret = SA_NO_MEMORY;
2074 			}
2075 		} else {
2076 			ret = SA_INVALID_NAME;
2077 		}
2078 	}
2079 err:
2080 	if (error != NULL)
2081 		*error = ret;
2082 	return ((sa_group_t)node);
2083 }
2084 
2085 /*
2086  * sa_remove_group(group)
2087  *
2088  * Remove the specified group. This deletes from the SMF repository.
2089  * All property groups and properties are removed.
2090  */
2091 
2092 int
2093 sa_remove_group(sa_group_t group)
2094 {
2095 	char *name;
2096 	int ret = SA_OK;
2097 	sa_handle_impl_t impl_handle;
2098 
2099 	impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
2100 	if (impl_handle != NULL) {
2101 		name = sa_get_group_attr(group, "name");
2102 		if (name != NULL) {
2103 			ret = sa_delete_instance(impl_handle->scfhandle, name);
2104 			sa_free_attr_string(name);
2105 		}
2106 		xmlUnlinkNode((xmlNodePtr)group); /* make sure unlinked */
2107 		xmlFreeNode((xmlNodePtr)group);   /* now it is gone */
2108 	} else {
2109 		ret = SA_SYSTEM_ERR;
2110 	}
2111 	return (ret);
2112 }
2113 
2114 /*
2115  * sa_update_config()
2116  *
2117  * Used to update legacy files that need to be updated in bulk
2118  * Currently, this is a placeholder and will go away in a future
2119  * release.
2120  */
2121 
2122 int
2123 sa_update_config(sa_handle_t handle)
2124 {
2125 	/*
2126 	 * do legacy files first so we can tell when they change.
2127 	 * This will go away when we start updating individual records
2128 	 * rather than the whole file.
2129 	 */
2130 	update_legacy_config(handle);
2131 	return (SA_OK);
2132 }
2133 
2134 /*
2135  * get_node_attr(node, tag)
2136  *
2137  * Get the specified tag(attribute) if it exists on the node.  This is
2138  * used internally by a number of attribute oriented functions.
2139  */
2140 
2141 static char *
2142 get_node_attr(void *nodehdl, char *tag)
2143 {
2144 	xmlNodePtr node = (xmlNodePtr)nodehdl;
2145 	xmlChar *name = NULL;
2146 
2147 	if (node != NULL)
2148 		name = xmlGetProp(node, (xmlChar *)tag);
2149 	return ((char *)name);
2150 }
2151 
2152 /*
2153  * set_node_attr(node, tag)
2154  *
2155  * Set the specified tag(attribute) to the specified value This is
2156  * used internally by a number of attribute oriented functions. It
2157  * doesn't update the repository, only the internal document state.
2158  */
2159 
2160 void
2161 set_node_attr(void *nodehdl, char *tag, char *value)
2162 {
2163 	xmlNodePtr node = (xmlNodePtr)nodehdl;
2164 	if (node != NULL && tag != NULL) {
2165 		if (value != NULL)
2166 			(void) xmlSetProp(node, (xmlChar *)tag,
2167 			    (xmlChar *)value);
2168 		else
2169 			(void) xmlUnsetProp(node, (xmlChar *)tag);
2170 	}
2171 }
2172 
2173 /*
2174  * sa_get_group_attr(group, tag)
2175  *
2176  * Get the specied attribute, if defined, for the group.
2177  */
2178 
2179 char *
2180 sa_get_group_attr(sa_group_t group, char *tag)
2181 {
2182 	return (get_node_attr((void *)group, tag));
2183 }
2184 
2185 /*
2186  * sa_set_group_attr(group, tag, value)
2187  *
2188  * set the specified tag/attribute on the group using value as its
2189  * value.
2190  *
2191  * This will result in setting the property in the SMF repository as
2192  * well as in the internal document.
2193  */
2194 
2195 int
2196 sa_set_group_attr(sa_group_t group, char *tag, char *value)
2197 {
2198 	int ret;
2199 	char *groupname;
2200 	sa_handle_impl_t impl_handle;
2201 
2202 	/*
2203 	 * ZFS group/subgroup doesn't need the handle so shortcut.
2204 	 */
2205 	if (sa_group_is_zfs(group)) {
2206 		set_node_attr((void *)group, tag, value);
2207 		return (SA_OK);
2208 	}
2209 
2210 	impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
2211 	if (impl_handle != NULL) {
2212 		groupname = sa_get_group_attr(group, "name");
2213 		ret = sa_get_instance(impl_handle->scfhandle, groupname);
2214 		if (ret == SA_OK) {
2215 			set_node_attr((void *)group, tag, value);
2216 			ret = sa_start_transaction(impl_handle->scfhandle,
2217 			    "operation");
2218 			if (ret == SA_OK) {
2219 				ret = sa_set_property(impl_handle->scfhandle,
2220 				    tag, value);
2221 				if (ret == SA_OK)
2222 					ret = sa_end_transaction(
2223 					    impl_handle->scfhandle,
2224 					    impl_handle);
2225 				else
2226 					sa_abort_transaction(
2227 					    impl_handle->scfhandle);
2228 			}
2229 			if (ret == SA_SYSTEM_ERR)
2230 				ret = SA_NO_PERMISSION;
2231 		}
2232 		if (groupname != NULL)
2233 			sa_free_attr_string(groupname);
2234 	} else {
2235 		ret = SA_SYSTEM_ERR;
2236 	}
2237 	return (ret);
2238 }
2239 
2240 /*
2241  * sa_get_share_attr(share, tag)
2242  *
2243  * Return the value of the tag/attribute set on the specified
2244  * share. Returns NULL if the tag doesn't exist.
2245  */
2246 
2247 char *
2248 sa_get_share_attr(sa_share_t share, char *tag)
2249 {
2250 	return (get_node_attr((void *)share, tag));
2251 }
2252 
2253 /*
2254  * _sa_set_share_description(share, description)
2255  *
2256  * Add a description tag with text contents to the specified share.  A
2257  * separate XML tag is used rather than a property. This can also be
2258  * used with resources.
2259  */
2260 
2261 xmlNodePtr
2262 _sa_set_share_description(void *share, char *content)
2263 {
2264 	xmlNodePtr node;
2265 	node = xmlNewChild((xmlNodePtr)share, NULL, (xmlChar *)"description",
2266 	    NULL);
2267 	xmlNodeSetContent(node, (xmlChar *)content);
2268 	return (node);
2269 }
2270 
2271 /*
2272  * sa_set_share_attr(share, tag, value)
2273  *
2274  * Set the share attribute specified by tag to the specified value. In
2275  * the case of "resource", enforce a no duplicates in a group rule. If
2276  * the share is not transient, commit the changes to the repository
2277  * else just update the share internally.
2278  */
2279 
2280 int
2281 sa_set_share_attr(sa_share_t share, char *tag, char *value)
2282 {
2283 	sa_group_t group;
2284 	sa_share_t resource;
2285 	int ret = SA_OK;
2286 
2287 	group = sa_get_parent_group(share);
2288 
2289 	/*
2290 	 * There are some attributes that may have specific
2291 	 * restrictions on them. Initially, only "resource" has
2292 	 * special meaning that needs to be checked. Only one instance
2293 	 * of a resource name may exist within a group.
2294 	 */
2295 
2296 	if (strcmp(tag, "resource") == 0) {
2297 		resource = sa_get_resource(group, value);
2298 		if (resource != share && resource != NULL)
2299 			ret = SA_DUPLICATE_NAME;
2300 	}
2301 	if (ret == SA_OK) {
2302 		set_node_attr((void *)share, tag, value);
2303 		if (group != NULL) {
2304 			char *type;
2305 			/* we can probably optimize this some */
2306 			type = sa_get_share_attr(share, "type");
2307 			if (type == NULL || strcmp(type, "transient") != 0) {
2308 				sa_handle_impl_t impl_handle;
2309 				impl_handle =
2310 				    (sa_handle_impl_t)sa_find_group_handle(
2311 				    group);
2312 				if (impl_handle != NULL) {
2313 					ret = sa_commit_share(
2314 					    impl_handle->scfhandle, group,
2315 					    share);
2316 				} else {
2317 					ret = SA_SYSTEM_ERR;
2318 				}
2319 			}
2320 			if (type != NULL)
2321 				sa_free_attr_string(type);
2322 		}
2323 	}
2324 	return (ret);
2325 }
2326 
2327 /*
2328  * sa_get_property_attr(prop, tag)
2329  *
2330  * Get the value of the specified property attribute. Standard
2331  * attributes are "type" and "value".
2332  */
2333 
2334 char *
2335 sa_get_property_attr(sa_property_t prop, char *tag)
2336 {
2337 	return (get_node_attr((void *)prop, tag));
2338 }
2339 
2340 /*
2341  * sa_get_optionset_attr(prop, tag)
2342  *
2343  * Get the value of the specified property attribute. Standard
2344  * attribute is "type".
2345  */
2346 
2347 char *
2348 sa_get_optionset_attr(sa_property_t optionset, char *tag)
2349 {
2350 	return (get_node_attr((void *)optionset, tag));
2351 
2352 }
2353 
2354 /*
2355  * sa_set_optionset_attr(optionset, tag, value)
2356  *
2357  * Set the specified attribute(tag) to the specified value on the
2358  * optionset.
2359  */
2360 
2361 void
2362 sa_set_optionset_attr(sa_group_t optionset, char *tag, char *value)
2363 {
2364 	set_node_attr((void *)optionset, tag, value);
2365 }
2366 
2367 /*
2368  * sa_free_attr_string(string)
2369  *
2370  * Free the string that was returned in one of the sa_get_*_attr()
2371  * functions.
2372  */
2373 
2374 void
2375 sa_free_attr_string(char *string)
2376 {
2377 	xmlFree((xmlChar *)string);
2378 }
2379 
2380 /*
2381  * sa_get_optionset(group, proto)
2382  *
2383  * Return the optionset, if it exists, that is associated with the
2384  * specified protocol.
2385  */
2386 
2387 sa_optionset_t
2388 sa_get_optionset(void *group, char *proto)
2389 {
2390 	xmlNodePtr node;
2391 	xmlChar *value = NULL;
2392 
2393 	for (node = ((xmlNodePtr)group)->children; node != NULL;
2394 	    node = node->next) {
2395 		if (xmlStrcmp(node->name, (xmlChar *)"optionset") == 0) {
2396 			value = xmlGetProp(node, (xmlChar *)"type");
2397 			if (proto != NULL) {
2398 				if (value != NULL &&
2399 				    xmlStrcmp(value, (xmlChar *)proto) == 0) {
2400 					break;
2401 				}
2402 				if (value != NULL) {
2403 					xmlFree(value);
2404 					value = NULL;
2405 				}
2406 			} else {
2407 				break;
2408 			}
2409 		}
2410 	}
2411 	if (value != NULL)
2412 		xmlFree(value);
2413 	return ((sa_optionset_t)node);
2414 }
2415 
2416 /*
2417  * sa_get_next_optionset(optionset)
2418  *
2419  * Return the next optionset in the group. NULL if this was the last.
2420  */
2421 
2422 sa_optionset_t
2423 sa_get_next_optionset(sa_optionset_t optionset)
2424 {
2425 	xmlNodePtr node;
2426 
2427 	for (node = ((xmlNodePtr)optionset)->next; node != NULL;
2428 	    node = node->next) {
2429 		if (xmlStrcmp(node->name, (xmlChar *)"optionset") == 0) {
2430 			break;
2431 		}
2432 	}
2433 	return ((sa_optionset_t)node);
2434 }
2435 
2436 /*
2437  * sa_get_security(group, sectype, proto)
2438  *
2439  * Return the security optionset. The internal name is a hold over
2440  * from the implementation and will be changed before the API is
2441  * finalized. This is really a named optionset that can be negotiated
2442  * as a group of properties (like NFS security options).
2443  */
2444 
2445 sa_security_t
2446 sa_get_security(sa_group_t group, char *sectype, char *proto)
2447 {
2448 	xmlNodePtr node;
2449 	xmlChar *value = NULL;
2450 
2451 	for (node = ((xmlNodePtr)group)->children; node != NULL;
2452 	    node = node->next) {
2453 		if (xmlStrcmp(node->name, (xmlChar *)"security") == 0) {
2454 			if (proto != NULL) {
2455 				value = xmlGetProp(node, (xmlChar *)"type");
2456 				if (value == NULL ||
2457 				    (value != NULL &&
2458 				    xmlStrcmp(value, (xmlChar *)proto) != 0)) {
2459 					/* it doesn't match so continue */
2460 					xmlFree(value);
2461 					value = NULL;
2462 					continue;
2463 				}
2464 			}
2465 			if (value != NULL) {
2466 				xmlFree(value);
2467 				value = NULL;
2468 			}
2469 			/* potential match */
2470 			if (sectype != NULL) {
2471 				value = xmlGetProp(node, (xmlChar *)"sectype");
2472 				if (value != NULL &&
2473 				    xmlStrcmp(value, (xmlChar *)sectype) == 0) {
2474 					break;
2475 				}
2476 			} else {
2477 				break;
2478 			}
2479 		}
2480 		if (value != NULL) {
2481 			xmlFree(value);
2482 			value = NULL;
2483 		}
2484 	}
2485 	if (value != NULL)
2486 		xmlFree(value);
2487 	return ((sa_security_t)node);
2488 }
2489 
2490 /*
2491  * sa_get_next_security(security)
2492  *
2493  * Get the next security optionset if one exists.
2494  */
2495 
2496 sa_security_t
2497 sa_get_next_security(sa_security_t security)
2498 {
2499 	xmlNodePtr node;
2500 
2501 	for (node = ((xmlNodePtr)security)->next; node != NULL;
2502 	    node = node->next) {
2503 		if (xmlStrcmp(node->name, (xmlChar *)"security") == 0) {
2504 			break;
2505 		}
2506 	}
2507 	return ((sa_security_t)node);
2508 }
2509 
2510 /*
2511  * sa_get_property(optionset, prop)
2512  *
2513  * Get the property object with the name specified in prop from the
2514  * optionset.
2515  */
2516 
2517 sa_property_t
2518 sa_get_property(sa_optionset_t optionset, char *prop)
2519 {
2520 	xmlNodePtr node = (xmlNodePtr)optionset;
2521 	xmlChar *value = NULL;
2522 
2523 	if (optionset == NULL)
2524 		return (NULL);
2525 
2526 	for (node = node->children; node != NULL;
2527 	    node = node->next) {
2528 		if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) {
2529 			if (prop == NULL)
2530 				break;
2531 			value = xmlGetProp(node, (xmlChar *)"type");
2532 			if (value != NULL &&
2533 			    xmlStrcmp(value, (xmlChar *)prop) == 0) {
2534 				break;
2535 			}
2536 			if (value != NULL) {
2537 				xmlFree(value);
2538 				value = NULL;
2539 			}
2540 		}
2541 	}
2542 	if (value != NULL)
2543 		xmlFree(value);
2544 	if (node != NULL && xmlStrcmp(node->name, (xmlChar *)"option") != 0) {
2545 		/*
2546 		 * avoid a non option node -- it is possible to be a
2547 		 * text node
2548 		 */
2549 		node = NULL;
2550 	}
2551 	return ((sa_property_t)node);
2552 }
2553 
2554 /*
2555  * sa_get_next_property(property)
2556  *
2557  * Get the next property following the specified property. NULL if
2558  * this was the last.
2559  */
2560 
2561 sa_property_t
2562 sa_get_next_property(sa_property_t property)
2563 {
2564 	xmlNodePtr node;
2565 
2566 	for (node = ((xmlNodePtr)property)->next; node != NULL;
2567 	    node = node->next) {
2568 		if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) {
2569 			break;
2570 		}
2571 	}
2572 	return ((sa_property_t)node);
2573 }
2574 
2575 /*
2576  * sa_set_share_description(share, content)
2577  *
2578  * Set the description of share to content.
2579  */
2580 
2581 int
2582 sa_set_share_description(sa_share_t share, char *content)
2583 {
2584 	xmlNodePtr node;
2585 	sa_group_t group;
2586 	int ret = SA_OK;
2587 
2588 	for (node = ((xmlNodePtr)share)->children; node != NULL;
2589 	    node = node->next) {
2590 		if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) {
2591 			break;
2592 		}
2593 	}
2594 	/* no existing description but want to add */
2595 	if (node == NULL && content != NULL) {
2596 		/* add a description */
2597 		node = _sa_set_share_description(share, content);
2598 	} else if (node != NULL && content != NULL) {
2599 		/* update a description */
2600 		xmlNodeSetContent(node, (xmlChar *)content);
2601 	} else if (node != NULL && content == NULL) {
2602 		/* remove an existing description */
2603 		xmlUnlinkNode(node);
2604 		xmlFreeNode(node);
2605 	}
2606 	group = sa_get_parent_group(share);
2607 	if (group != NULL &&
2608 	    sa_is_persistent(share) && (!sa_group_is_zfs(group))) {
2609 		sa_handle_impl_t impl_handle;
2610 		impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
2611 		if (impl_handle != NULL) {
2612 			ret = sa_commit_share(impl_handle->scfhandle, group,
2613 			    share);
2614 		} else {
2615 			ret = SA_SYSTEM_ERR;
2616 		}
2617 	}
2618 	return (ret);
2619 }
2620 
2621 /*
2622  * fixproblemchars(string)
2623  *
2624  * don't want any newline or tab characters in the text since these
2625  * could break display of data and legacy file formats.
2626  */
2627 static void
2628 fixproblemchars(char *str)
2629 {
2630 	int c;
2631 	for (c = *str; c != '\0'; c = *++str) {
2632 		if (c == '\t' || c == '\n')
2633 			*str = ' ';
2634 		else if (c == '"')
2635 			*str = '\'';
2636 	}
2637 }
2638 
2639 /*
2640  * sa_get_share_description(share)
2641  *
2642  * Return the description text for the specified share if it
2643  * exists. NULL if no description exists.
2644  */
2645 
2646 char *
2647 sa_get_share_description(sa_share_t share)
2648 {
2649 	xmlChar *description = NULL;
2650 	xmlNodePtr node;
2651 
2652 	for (node = ((xmlNodePtr)share)->children; node != NULL;
2653 	    node = node->next) {
2654 		if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) {
2655 			break;
2656 		}
2657 	}
2658 	if (node != NULL) {
2659 		description = xmlNodeGetContent(node);
2660 		fixproblemchars((char *)description);
2661 	}
2662 	return ((char *)description);
2663 }
2664 
2665 /*
2666  * sa_free(share_description(description)
2667  *
2668  * Free the description string.
2669  */
2670 
2671 void
2672 sa_free_share_description(char *description)
2673 {
2674 	xmlFree((xmlChar *)description);
2675 }
2676 
2677 /*
2678  * sa_create_optionset(group, proto)
2679  *
2680  * Create an optionset for the specified protocol in the specied
2681  * group. This is manifested as a property group within SMF.
2682  */
2683 
2684 sa_optionset_t
2685 sa_create_optionset(sa_group_t group, char *proto)
2686 {
2687 	sa_optionset_t optionset;
2688 	sa_group_t parent = group;
2689 	sa_share_t share = NULL;
2690 	int err = SA_OK;
2691 	char *id = NULL;
2692 
2693 	optionset = sa_get_optionset(group, proto);
2694 	if (optionset != NULL) {
2695 		/* can't have a duplicate protocol */
2696 		optionset = NULL;
2697 	} else {
2698 		/*
2699 		 * Account for resource names being slightly
2700 		 * different.
2701 		 */
2702 		if (sa_is_share(group)) {
2703 			/*
2704 			 * Transient shares do not have an "id" so not an
2705 			 * error to not find one.
2706 			 */
2707 			id = sa_get_share_attr((sa_share_t)group, "id");
2708 		} else if (sa_is_resource(group)) {
2709 			share = sa_get_resource_parent(
2710 			    (sa_resource_t)group);
2711 			id = sa_get_resource_attr(share, "id");
2712 
2713 			/* id can be NULL if the group is transient (ZFS) */
2714 			if (id == NULL && sa_is_persistent(group))
2715 				err = SA_NO_MEMORY;
2716 		}
2717 		if (err == SA_NO_MEMORY) {
2718 			/*
2719 			 * Couldn't get the id for the share or
2720 			 * resource. While this could be a
2721 			 * configuration issue, it is most likely an
2722 			 * out of memory. In any case, fail the create.
2723 			 */
2724 			return (NULL);
2725 		}
2726 
2727 		optionset = (sa_optionset_t)xmlNewChild((xmlNodePtr)group,
2728 		    NULL, (xmlChar *)"optionset", NULL);
2729 		/*
2730 		 * only put to repository if on a group and we were
2731 		 * able to create an optionset.
2732 		 */
2733 		if (optionset != NULL) {
2734 			char oname[SA_STRSIZE];
2735 			char *groupname;
2736 
2737 			/*
2738 			 * Need to get parent group in all cases, but also get
2739 			 * the share if this is a resource.
2740 			 */
2741 			if (sa_is_share(group)) {
2742 				parent = sa_get_parent_group((sa_share_t)group);
2743 			} else if (sa_is_resource(group)) {
2744 				share = sa_get_resource_parent(
2745 				    (sa_resource_t)group);
2746 				parent = sa_get_parent_group(share);
2747 			}
2748 
2749 			sa_set_optionset_attr(optionset, "type", proto);
2750 
2751 			(void) sa_optionset_name(optionset, oname,
2752 			    sizeof (oname), id);
2753 			groupname = sa_get_group_attr(parent, "name");
2754 			if (groupname != NULL && sa_is_persistent(group)) {
2755 				sa_handle_impl_t impl_handle;
2756 				impl_handle =
2757 				    (sa_handle_impl_t)sa_find_group_handle(
2758 				    group);
2759 				assert(impl_handle != NULL);
2760 				if (impl_handle != NULL) {
2761 					(void) sa_get_instance(
2762 					    impl_handle->scfhandle, groupname);
2763 					(void) sa_create_pgroup(
2764 					    impl_handle->scfhandle, oname);
2765 				}
2766 			}
2767 			if (groupname != NULL)
2768 				sa_free_attr_string(groupname);
2769 		}
2770 	}
2771 
2772 	if (id != NULL)
2773 		sa_free_attr_string(id);
2774 	return (optionset);
2775 }
2776 
2777 /*
2778  * sa_get_property_parent(property)
2779  *
2780  * Given a property, return the object it is a property of. This will
2781  * be an optionset of some type.
2782  */
2783 
2784 static sa_optionset_t
2785 sa_get_property_parent(sa_property_t property)
2786 {
2787 	xmlNodePtr node = NULL;
2788 
2789 	if (property != NULL)
2790 		node = ((xmlNodePtr)property)->parent;
2791 	return ((sa_optionset_t)node);
2792 }
2793 
2794 /*
2795  * sa_get_optionset_parent(optionset)
2796  *
2797  * Return the parent of the specified optionset. This could be a group
2798  * or a share.
2799  */
2800 
2801 static sa_group_t
2802 sa_get_optionset_parent(sa_optionset_t optionset)
2803 {
2804 	xmlNodePtr node = NULL;
2805 
2806 	if (optionset != NULL)
2807 		node = ((xmlNodePtr)optionset)->parent;
2808 	return ((sa_group_t)node);
2809 }
2810 
2811 /*
2812  * zfs_needs_update(share)
2813  *
2814  * In order to avoid making multiple updates to a ZFS share when
2815  * setting properties, the share attribute "changed" will be set to
2816  * true when a property is added or modified.  When done adding
2817  * properties, we can then detect that an update is needed.  We then
2818  * clear the state here to detect additional changes.
2819  */
2820 
2821 static int
2822 zfs_needs_update(sa_share_t share)
2823 {
2824 	char *attr;
2825 	int result = 0;
2826 
2827 	attr = sa_get_share_attr(share, "changed");
2828 	if (attr != NULL) {
2829 		sa_free_attr_string(attr);
2830 		result = 1;
2831 	}
2832 	set_node_attr((void *)share, "changed", NULL);
2833 	return (result);
2834 }
2835 
2836 /*
2837  * zfs_set_update(share)
2838  *
2839  * Set the changed attribute of the share to true.
2840  */
2841 
2842 static void
2843 zfs_set_update(sa_share_t share)
2844 {
2845 	set_node_attr((void *)share, "changed", "true");
2846 }
2847 
2848 /*
2849  * sa_commit_properties(optionset, clear)
2850  *
2851  * Check if SMF or ZFS config and either update or abort the pending
2852  * changes.
2853  */
2854 
2855 int
2856 sa_commit_properties(sa_optionset_t optionset, int clear)
2857 {
2858 	sa_group_t group;
2859 	sa_group_t parent;
2860 	int zfs = 0;
2861 	int needsupdate = 0;
2862 	int ret = SA_OK;
2863 	sa_handle_impl_t impl_handle;
2864 
2865 	group = sa_get_optionset_parent(optionset);
2866 	if (group != NULL && (sa_is_share(group) || is_zfs_group(group))) {
2867 		/* only update ZFS if on a share */
2868 		parent = sa_get_parent_group(group);
2869 		zfs++;
2870 		if (parent != NULL && is_zfs_group(parent))
2871 			needsupdate = zfs_needs_update(group);
2872 		else
2873 			zfs = 0;
2874 	}
2875 	if (zfs) {
2876 		if (!clear && needsupdate)
2877 			ret = sa_zfs_update((sa_share_t)group);
2878 	} else {
2879 		impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
2880 		if (impl_handle != NULL) {
2881 			if (clear) {
2882 				(void) sa_abort_transaction(
2883 				    impl_handle->scfhandle);
2884 			} else {
2885 				ret = sa_end_transaction(
2886 				    impl_handle->scfhandle, impl_handle);
2887 			}
2888 		} else {
2889 			ret = SA_SYSTEM_ERR;
2890 		}
2891 	}
2892 	return (ret);
2893 }
2894 
2895 /*
2896  * sa_destroy_optionset(optionset)
2897  *
2898  * Remove the optionset from its group. Update the repository to
2899  * reflect this change.
2900  */
2901 
2902 int
2903 sa_destroy_optionset(sa_optionset_t optionset)
2904 {
2905 	char name[SA_STRSIZE];
2906 	int len;
2907 	int ret;
2908 	char *id = NULL;
2909 	sa_group_t group;
2910 	int ispersist = 1;
2911 
2912 	/* now delete the prop group */
2913 	group = sa_get_optionset_parent(optionset);
2914 	if (group != NULL) {
2915 		if (sa_is_resource(group)) {
2916 			sa_resource_t resource = group;
2917 			sa_share_t share = sa_get_resource_parent(resource);
2918 			group = sa_get_parent_group(share);
2919 			id = sa_get_share_attr(share, "id");
2920 		} else if (sa_is_share(group)) {
2921 			id = sa_get_share_attr((sa_share_t)group, "id");
2922 		}
2923 		ispersist = sa_is_persistent(group);
2924 	}
2925 	if (ispersist) {
2926 		sa_handle_impl_t impl_handle;
2927 		len = sa_optionset_name(optionset, name, sizeof (name), id);
2928 		impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
2929 		if (impl_handle != NULL) {
2930 			if (len > 0) {
2931 				ret = sa_delete_pgroup(impl_handle->scfhandle,
2932 				    name);
2933 			}
2934 		} else {
2935 			ret = SA_SYSTEM_ERR;
2936 		}
2937 	}
2938 	xmlUnlinkNode((xmlNodePtr)optionset);
2939 	xmlFreeNode((xmlNodePtr)optionset);
2940 	if (id != NULL)
2941 		sa_free_attr_string(id);
2942 	return (ret);
2943 }
2944 
2945 /* private to the implementation */
2946 int
2947 _sa_remove_optionset(sa_optionset_t optionset)
2948 {
2949 	int ret = SA_OK;
2950 
2951 	xmlUnlinkNode((xmlNodePtr)optionset);
2952 	xmlFreeNode((xmlNodePtr)optionset);
2953 	return (ret);
2954 }
2955 
2956 /*
2957  * sa_create_security(group, sectype, proto)
2958  *
2959  * Create a security optionset (one that has a type name and a
2960  * proto). Security is left over from a pure NFS implementation. The
2961  * naming will change in the future when the API is released.
2962  */
2963 sa_security_t
2964 sa_create_security(sa_group_t group, char *sectype, char *proto)
2965 {
2966 	sa_security_t security;
2967 	char *id = NULL;
2968 	sa_group_t parent;
2969 	char *groupname = NULL;
2970 
2971 	if (group != NULL && sa_is_share(group)) {
2972 		id = sa_get_share_attr((sa_share_t)group, "id");
2973 		parent = sa_get_parent_group(group);
2974 		if (parent != NULL)
2975 			groupname = sa_get_group_attr(parent, "name");
2976 	} else if (group != NULL) {
2977 		groupname = sa_get_group_attr(group, "name");
2978 	}
2979 
2980 	security = sa_get_security(group, sectype, proto);
2981 	if (security != NULL) {
2982 		/* can't have a duplicate security option */
2983 		security = NULL;
2984 	} else {
2985 		security = (sa_security_t)xmlNewChild((xmlNodePtr)group,
2986 		    NULL, (xmlChar *)"security", NULL);
2987 		if (security != NULL) {
2988 			char oname[SA_STRSIZE];
2989 			sa_set_security_attr(security, "type", proto);
2990 
2991 			sa_set_security_attr(security, "sectype", sectype);
2992 			(void) sa_security_name(security, oname,
2993 			    sizeof (oname), id);
2994 			if (groupname != NULL && sa_is_persistent(group)) {
2995 				sa_handle_impl_t impl_handle;
2996 				impl_handle =
2997 				    (sa_handle_impl_t)sa_find_group_handle(
2998 				    group);
2999 				if (impl_handle != NULL) {
3000 					(void) sa_get_instance(
3001 					    impl_handle->scfhandle, groupname);
3002 					(void) sa_create_pgroup(
3003 					    impl_handle->scfhandle, oname);
3004 				}
3005 			}
3006 		}
3007 	}
3008 	if (id != NULL)
3009 		sa_free_attr_string(id);
3010 	if (groupname != NULL)
3011 		sa_free_attr_string(groupname);
3012 	return (security);
3013 }
3014 
3015 /*
3016  * sa_destroy_security(security)
3017  *
3018  * Remove the specified optionset from the document and the
3019  * configuration.
3020  */
3021 
3022 int
3023 sa_destroy_security(sa_security_t security)
3024 {
3025 	char name[SA_STRSIZE];
3026 	int len;
3027 	int ret = SA_OK;
3028 	char *id = NULL;
3029 	sa_group_t group;
3030 	int iszfs = 0;
3031 	int ispersist = 1;
3032 
3033 	group = sa_get_optionset_parent(security);
3034 
3035 	if (group != NULL)
3036 		iszfs = sa_group_is_zfs(group);
3037 
3038 	if (group != NULL && !iszfs) {
3039 		if (sa_is_share(group))
3040 			ispersist = sa_is_persistent(group);
3041 		id = sa_get_share_attr((sa_share_t)group, "id");
3042 	}
3043 	if (ispersist) {
3044 		len = sa_security_name(security, name, sizeof (name), id);
3045 		if (!iszfs && len > 0) {
3046 			sa_handle_impl_t impl_handle;
3047 			impl_handle =
3048 			    (sa_handle_impl_t)sa_find_group_handle(group);
3049 			if (impl_handle != NULL) {
3050 				ret = sa_delete_pgroup(impl_handle->scfhandle,
3051 				    name);
3052 			} else {
3053 				ret = SA_SYSTEM_ERR;
3054 			}
3055 		}
3056 	}
3057 	xmlUnlinkNode((xmlNodePtr)security);
3058 	xmlFreeNode((xmlNodePtr)security);
3059 	if (iszfs)
3060 		ret = sa_zfs_update(group);
3061 	if (id != NULL)
3062 		sa_free_attr_string(id);
3063 	return (ret);
3064 }
3065 
3066 /*
3067  * sa_get_security_attr(optionset, tag)
3068  *
3069  * Return the specified attribute value from the optionset.
3070  */
3071 
3072 char *
3073 sa_get_security_attr(sa_property_t optionset, char *tag)
3074 {
3075 	return (get_node_attr((void *)optionset, tag));
3076 
3077 }
3078 
3079 /*
3080  * sa_set_security_attr(optionset, tag, value)
3081  *
3082  * Set the optioset attribute specied by tag to the specified value.
3083  */
3084 
3085 void
3086 sa_set_security_attr(sa_group_t optionset, char *tag, char *value)
3087 {
3088 	set_node_attr((void *)optionset, tag, value);
3089 }
3090 
3091 /*
3092  * is_nodetype(node, type)
3093  *
3094  * Check to see if node is of the type specified.
3095  */
3096 
3097 static int
3098 is_nodetype(void *node, char *type)
3099 {
3100 	return (strcmp((char *)((xmlNodePtr)node)->name, type) == 0);
3101 }
3102 
3103 /*
3104  * add_or_update()
3105  *
3106  * Add or update a property. Pulled out of sa_set_prop_by_prop for
3107  * readability.
3108  */
3109 static int
3110 add_or_update(scfutilhandle_t *scf_handle, int type, scf_value_t *value,
3111     scf_transaction_entry_t *entry, char *name, char *valstr)
3112 {
3113 	int ret = SA_SYSTEM_ERR;
3114 
3115 	if (value != NULL) {
3116 		if (type == SA_PROP_OP_ADD)
3117 			ret = scf_transaction_property_new(scf_handle->trans,
3118 			    entry, name, SCF_TYPE_ASTRING);
3119 		else
3120 			ret = scf_transaction_property_change(scf_handle->trans,
3121 			    entry, name, SCF_TYPE_ASTRING);
3122 		if (ret == 0) {
3123 			ret = scf_value_set_astring(value, valstr);
3124 			if (ret == 0)
3125 				ret = scf_entry_add_value(entry, value);
3126 			if (ret == 0)
3127 				return (ret);
3128 			scf_value_destroy(value);
3129 		} else {
3130 			scf_entry_destroy(entry);
3131 		}
3132 	}
3133 	return (SA_SYSTEM_ERR);
3134 }
3135 
3136 /*
3137  * sa_set_prop_by_prop(optionset, group, prop, type)
3138  *
3139  * Add/remove/update the specified property prop into the optionset or
3140  * share. If a share, sort out which property group based on GUID. In
3141  * all cases, the appropriate transaction is set (or ZFS share is
3142  * marked as needing an update)
3143  */
3144 
3145 static int
3146 sa_set_prop_by_prop(sa_optionset_t optionset, sa_group_t group,
3147     sa_property_t prop, int type)
3148 {
3149 	char *name;
3150 	char *valstr;
3151 	int ret = SA_OK;
3152 	scf_transaction_entry_t *entry;
3153 	scf_value_t *value;
3154 	int opttype; /* 1 == optionset, 0 == security */
3155 	char *id = NULL;
3156 	int iszfs = 0;
3157 	sa_group_t parent = NULL;
3158 	sa_share_t share = NULL;
3159 	sa_handle_impl_t impl_handle;
3160 	scfutilhandle_t  *scf_handle;
3161 
3162 	if (!sa_is_persistent(group)) {
3163 		/*
3164 		 * if the group/share is not persistent we don't need
3165 		 * to do anything here
3166 		 */
3167 		return (SA_OK);
3168 	}
3169 	impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
3170 	if (impl_handle == NULL || impl_handle->scfhandle == NULL)
3171 		return (SA_SYSTEM_ERR);
3172 	scf_handle = impl_handle->scfhandle;
3173 	name = sa_get_property_attr(prop, "type");
3174 	valstr = sa_get_property_attr(prop, "value");
3175 	entry = scf_entry_create(scf_handle->handle);
3176 	opttype = is_nodetype((void *)optionset, "optionset");
3177 
3178 	/*
3179 	 * Check for share vs. resource since they need slightly
3180 	 * different treatment given the hierarchy.
3181 	 */
3182 	if (valstr != NULL && entry != NULL) {
3183 		if (sa_is_share(group)) {
3184 			parent = sa_get_parent_group(group);
3185 			share = (sa_share_t)group;
3186 			if (parent != NULL)
3187 				iszfs = is_zfs_group(parent);
3188 		} else if (sa_is_resource(group)) {
3189 			share = sa_get_parent_group(group);
3190 			if (share != NULL)
3191 				parent = sa_get_parent_group(share);
3192 		} else {
3193 			iszfs = is_zfs_group(group);
3194 		}
3195 		if (!iszfs) {
3196 			if (scf_handle->trans == NULL) {
3197 				char oname[SA_STRSIZE];
3198 				char *groupname = NULL;
3199 				if (share != NULL) {
3200 					if (parent != NULL)
3201 						groupname =
3202 						    sa_get_group_attr(parent,
3203 						    "name");
3204 					id = sa_get_share_attr(
3205 					    (sa_share_t)share, "id");
3206 				} else {
3207 					groupname = sa_get_group_attr(group,
3208 					    "name");
3209 				}
3210 				if (groupname != NULL) {
3211 					ret = sa_get_instance(scf_handle,
3212 					    groupname);
3213 					sa_free_attr_string(groupname);
3214 				}
3215 				if (opttype)
3216 					(void) sa_optionset_name(optionset,
3217 					    oname, sizeof (oname), id);
3218 				else
3219 					(void) sa_security_name(optionset,
3220 					    oname, sizeof (oname), id);
3221 				ret = sa_start_transaction(scf_handle, oname);
3222 				if (id != NULL)
3223 					sa_free_attr_string(id);
3224 			}
3225 			if (ret == SA_OK) {
3226 				switch (type) {
3227 				case SA_PROP_OP_REMOVE:
3228 					ret = scf_transaction_property_delete(
3229 					    scf_handle->trans, entry, name);
3230 					break;
3231 				case SA_PROP_OP_ADD:
3232 				case SA_PROP_OP_UPDATE:
3233 					value = scf_value_create(
3234 					    scf_handle->handle);
3235 					ret = add_or_update(scf_handle, type,
3236 					    value, entry, name, valstr);
3237 					break;
3238 				}
3239 			}
3240 		} else {
3241 			/*
3242 			 * ZFS update. The calling function would have updated
3243 			 * the internal XML structure. Just need to flag it as
3244 			 * changed for ZFS.
3245 			 */
3246 			zfs_set_update((sa_share_t)group);
3247 		}
3248 	}
3249 
3250 	if (name != NULL)
3251 		sa_free_attr_string(name);
3252 	if (valstr != NULL)
3253 		sa_free_attr_string(valstr);
3254 	else if (entry != NULL)
3255 		scf_entry_destroy(entry);
3256 
3257 	if (ret == -1)
3258 		ret = SA_SYSTEM_ERR;
3259 
3260 	return (ret);
3261 }
3262 
3263 /*
3264  * sa_create_section(name, value)
3265  *
3266  * Create a new section with the specified name and extra data.
3267  */
3268 
3269 sa_property_t
3270 sa_create_section(char *name, char *extra)
3271 {
3272 	xmlNodePtr node;
3273 
3274 	node = xmlNewNode(NULL, (xmlChar *)"section");
3275 	if (node != NULL) {
3276 		if (name != NULL)
3277 			(void) xmlSetProp(node, (xmlChar *)"name",
3278 			    (xmlChar *)name);
3279 		if (extra != NULL)
3280 			(void) xmlSetProp(node, (xmlChar *)"extra",
3281 			    (xmlChar *)extra);
3282 	}
3283 	return ((sa_property_t)node);
3284 }
3285 
3286 void
3287 sa_set_section_attr(sa_property_t sect, char *name, char *value)
3288 {
3289 	(void) xmlSetProp(sect, (xmlChar *)name, (xmlChar *)value);
3290 }
3291 
3292 /*
3293  * sa_create_property(section, name, value)
3294  *
3295  * Create a new property with the specified name and value.
3296  */
3297 
3298 sa_property_t
3299 sa_create_property(char *name, char *value)
3300 {
3301 	xmlNodePtr node;
3302 
3303 	node = xmlNewNode(NULL, (xmlChar *)"option");
3304 	if (node != NULL) {
3305 		(void) xmlSetProp(node, (xmlChar *)"type", (xmlChar *)name);
3306 		(void) xmlSetProp(node, (xmlChar *)"value", (xmlChar *)value);
3307 	}
3308 	return ((sa_property_t)node);
3309 }
3310 
3311 /*
3312  * sa_add_property(object, property)
3313  *
3314  * Add the specified property to the object. Issue the appropriate
3315  * transaction or mark a ZFS object as needing an update.
3316  */
3317 
3318 int
3319 sa_add_property(void *object, sa_property_t property)
3320 {
3321 	int ret = SA_OK;
3322 	sa_group_t parent;
3323 	sa_group_t group;
3324 	char *proto;
3325 
3326 	if (property != NULL) {
3327 		sa_handle_t handle;
3328 		handle = sa_find_group_handle((sa_group_t)object);
3329 		/* It is legitimate to not find a handle */
3330 		proto = sa_get_optionset_attr(object, "type");
3331 		if ((ret = sa_valid_property(handle, object, proto,
3332 		    property)) == SA_OK) {
3333 			property = (sa_property_t)xmlAddChild(
3334 			    (xmlNodePtr)object, (xmlNodePtr)property);
3335 		} else {
3336 			if (proto != NULL)
3337 				sa_free_attr_string(proto);
3338 			return (ret);
3339 		}
3340 		if (proto != NULL)
3341 			sa_free_attr_string(proto);
3342 	}
3343 
3344 
3345 	parent = sa_get_parent_group(object);
3346 	if (!sa_is_persistent(parent))
3347 		return (ret);
3348 
3349 	if (sa_is_resource(parent)) {
3350 		/*
3351 		 * Resources are children of share.  Need to go up two
3352 		 * levels to find the group but the parent needs to be
3353 		 * the share at this point in order to get the "id".
3354 		 */
3355 		parent = sa_get_parent_group(parent);
3356 		group = sa_get_parent_group(parent);
3357 	} else if (sa_is_share(parent)) {
3358 		group = sa_get_parent_group(parent);
3359 	} else {
3360 		group = parent;
3361 	}
3362 
3363 	if (property == NULL) {
3364 		ret = SA_NO_MEMORY;
3365 	} else {
3366 		char oname[SA_STRSIZE];
3367 
3368 		if (!is_zfs_group(group)) {
3369 			char *id = NULL;
3370 			sa_handle_impl_t impl_handle;
3371 			scfutilhandle_t  *scf_handle;
3372 
3373 			impl_handle = (sa_handle_impl_t)sa_find_group_handle(
3374 			    group);
3375 			if (impl_handle == NULL ||
3376 			    impl_handle->scfhandle == NULL)
3377 				ret = SA_SYSTEM_ERR;
3378 			if (ret == SA_OK) {
3379 				scf_handle = impl_handle->scfhandle;
3380 				if (sa_is_share((sa_group_t)parent)) {
3381 					id = sa_get_share_attr(
3382 					    (sa_share_t)parent, "id");
3383 				}
3384 				if (scf_handle->trans == NULL) {
3385 					if (is_nodetype(object, "optionset")) {
3386 						(void) sa_optionset_name(
3387 						    (sa_optionset_t)object,
3388 						    oname, sizeof (oname), id);
3389 					} else {
3390 						(void) sa_security_name(
3391 						    (sa_optionset_t)object,
3392 						    oname, sizeof (oname), id);
3393 					}
3394 					ret = sa_start_transaction(scf_handle,
3395 					    oname);
3396 				}
3397 				if (ret == SA_OK) {
3398 					char *name;
3399 					char *value;
3400 					name = sa_get_property_attr(property,
3401 					    "type");
3402 					value = sa_get_property_attr(property,
3403 					    "value");
3404 					if (name != NULL && value != NULL) {
3405 						if (scf_handle->scf_state ==
3406 						    SCH_STATE_INIT) {
3407 							ret = sa_set_property(
3408 							    scf_handle, name,
3409 							    value);
3410 						}
3411 					} else {
3412 						ret = SA_CONFIG_ERR;
3413 					}
3414 					if (name != NULL)
3415 						sa_free_attr_string(
3416 						    name);
3417 					if (value != NULL)
3418 						sa_free_attr_string(value);
3419 				}
3420 				if (id != NULL)
3421 					sa_free_attr_string(id);
3422 			}
3423 		} else {
3424 			/*
3425 			 * ZFS is a special case. We do want
3426 			 * to allow editing property/security
3427 			 * lists since we can have a better
3428 			 * syntax and we also want to keep
3429 			 * things consistent when possible.
3430 			 *
3431 			 * Right now, we defer until the
3432 			 * sa_commit_properties so we can get
3433 			 * them all at once. We do need to
3434 			 * mark the share as "changed"
3435 			 */
3436 			zfs_set_update((sa_share_t)parent);
3437 		}
3438 	}
3439 	return (ret);
3440 }
3441 
3442 /*
3443  * sa_remove_property(property)
3444  *
3445  * Remove the specied property from its containing object. Update the
3446  * repository as appropriate.
3447  */
3448 
3449 int
3450 sa_remove_property(sa_property_t property)
3451 {
3452 	int ret = SA_OK;
3453 
3454 	if (property != NULL) {
3455 		sa_optionset_t optionset;
3456 		sa_group_t group;
3457 		optionset = sa_get_property_parent(property);
3458 		if (optionset != NULL) {
3459 			group = sa_get_optionset_parent(optionset);
3460 			if (group != NULL) {
3461 				ret = sa_set_prop_by_prop(optionset, group,
3462 				    property, SA_PROP_OP_REMOVE);
3463 			}
3464 		}
3465 		xmlUnlinkNode((xmlNodePtr)property);
3466 		xmlFreeNode((xmlNodePtr)property);
3467 	} else {
3468 		ret = SA_NO_SUCH_PROP;
3469 	}
3470 	return (ret);
3471 }
3472 
3473 /*
3474  * sa_update_property(property, value)
3475  *
3476  * Update the specified property to the new value.  If value is NULL,
3477  * we currently treat this as a remove.
3478  */
3479 
3480 int
3481 sa_update_property(sa_property_t property, char *value)
3482 {
3483 	int ret = SA_OK;
3484 	if (value == NULL) {
3485 		return (sa_remove_property(property));
3486 	} else {
3487 		sa_optionset_t optionset;
3488 		sa_group_t group;
3489 		set_node_attr((void *)property, "value", value);
3490 		optionset = sa_get_property_parent(property);
3491 		if (optionset != NULL) {
3492 			group = sa_get_optionset_parent(optionset);
3493 			if (group != NULL) {
3494 				ret = sa_set_prop_by_prop(optionset, group,
3495 				    property, SA_PROP_OP_UPDATE);
3496 			}
3497 		} else {
3498 			ret = SA_NO_SUCH_PROP;
3499 		}
3500 	}
3501 	return (ret);
3502 }
3503 
3504 /*
3505  * sa_get_protocol_section(propset, prop)
3506  *
3507  * Get the specified protocol specific section. These are global to
3508  * the protocol and not specific to a group or share.
3509  */
3510 
3511 sa_protocol_properties_t
3512 sa_get_protocol_section(sa_protocol_properties_t propset, char *section)
3513 {
3514 	xmlNodePtr node = (xmlNodePtr)propset;
3515 	xmlChar *value = NULL;
3516 	char *proto;
3517 
3518 	proto = sa_get_optionset_attr(propset, "type");
3519 	if ((sa_proto_get_featureset(proto) & SA_FEATURE_HAS_SECTIONS) == 0) {
3520 		if (proto != NULL)
3521 			sa_free_attr_string(proto);
3522 		return (propset);
3523 	}
3524 
3525 	for (node = node->children; node != NULL;
3526 	    node = node->next) {
3527 		if (xmlStrcmp(node->name, (xmlChar *)"section") == 0) {
3528 			if (section == NULL)
3529 				break;
3530 			value = xmlGetProp(node, (xmlChar *)"name");
3531 			if (value != NULL &&
3532 			    xmlStrcasecmp(value, (xmlChar *)section) == 0) {
3533 				break;
3534 			}
3535 			if (value != NULL) {
3536 				xmlFree(value);
3537 				value = NULL;
3538 			}
3539 		}
3540 	}
3541 	if (value != NULL)
3542 		xmlFree(value);
3543 	if (proto != NULL)
3544 		sa_free_attr_string(proto);
3545 	if (node != NULL && xmlStrcmp(node->name, (xmlChar *)"section") != 0) {
3546 		/*
3547 		 * avoid a non option node -- it is possible to be a
3548 		 * text node
3549 		 */
3550 		node = NULL;
3551 	}
3552 	return ((sa_protocol_properties_t)node);
3553 }
3554 
3555 /*
3556  * sa_get_next_protocol_section(prop, find)
3557  *
3558  * Get the next protocol specific section in the list.
3559  */
3560 
3561 sa_property_t
3562 sa_get_next_protocol_section(sa_property_t prop, char *find)
3563 {
3564 	xmlNodePtr node;
3565 	xmlChar *value = NULL;
3566 	char *proto;
3567 
3568 	proto = sa_get_optionset_attr(prop, "type");
3569 	if ((sa_proto_get_featureset(proto) & SA_FEATURE_HAS_SECTIONS) == 0) {
3570 		if (proto != NULL)
3571 			sa_free_attr_string(proto);
3572 		return ((sa_property_t)NULL);
3573 	}
3574 
3575 	for (node = ((xmlNodePtr)prop)->next; node != NULL;
3576 	    node = node->next) {
3577 		if (xmlStrcmp(node->name, (xmlChar *)"section") == 0) {
3578 			if (find == NULL)
3579 				break;
3580 			value = xmlGetProp(node, (xmlChar *)"name");
3581 			if (value != NULL &&
3582 			    xmlStrcasecmp(value, (xmlChar *)find) == 0) {
3583 				break;
3584 			}
3585 			if (value != NULL) {
3586 				xmlFree(value);
3587 				value = NULL;
3588 			}
3589 
3590 		}
3591 	}
3592 	if (value != NULL)
3593 		xmlFree(value);
3594 	if (proto != NULL)
3595 		sa_free_attr_string(proto);
3596 	return ((sa_property_t)node);
3597 }
3598 
3599 /*
3600  * sa_get_protocol_property(propset, prop)
3601  *
3602  * Get the specified protocol specific property. These are global to
3603  * the protocol and not specific to a group or share.
3604  */
3605 
3606 sa_property_t
3607 sa_get_protocol_property(sa_protocol_properties_t propset, char *prop)
3608 {
3609 	xmlNodePtr node = (xmlNodePtr)propset;
3610 	xmlChar *value = NULL;
3611 
3612 	if (propset == NULL)
3613 		return (NULL);
3614 
3615 	for (node = node->children; node != NULL;
3616 	    node = node->next) {
3617 		if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) {
3618 			if (prop == NULL)
3619 				break;
3620 			value = xmlGetProp(node, (xmlChar *)"type");
3621 			if (value != NULL &&
3622 			    xmlStrcasecmp(value, (xmlChar *)prop) == 0) {
3623 				break;
3624 			}
3625 			if (value != NULL) {
3626 				xmlFree(value);
3627 				value = NULL;
3628 			}
3629 		}
3630 	}
3631 	if (value != NULL)
3632 		xmlFree(value);
3633 	if (node != NULL && xmlStrcmp(node->name, (xmlChar *)"option") != 0) {
3634 		/*
3635 		 * avoid a non option node -- it is possible to be a
3636 		 * text node
3637 		 */
3638 		node = NULL;
3639 	}
3640 	return ((sa_property_t)node);
3641 }
3642 
3643 /*
3644  * sa_get_next_protocol_property(prop)
3645  *
3646  * Get the next protocol specific property in the list.
3647  */
3648 
3649 sa_property_t
3650 sa_get_next_protocol_property(sa_property_t prop, char *find)
3651 {
3652 	xmlNodePtr node;
3653 	xmlChar *value = NULL;
3654 
3655 	for (node = ((xmlNodePtr)prop)->next; node != NULL;
3656 	    node = node->next) {
3657 		if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) {
3658 			if (find == NULL)
3659 				break;
3660 			value = xmlGetProp(node, (xmlChar *)"type");
3661 			if (value != NULL &&
3662 			    xmlStrcasecmp(value, (xmlChar *)find) == 0) {
3663 				break;
3664 			}
3665 			if (value != NULL) {
3666 				xmlFree(value);
3667 				value = NULL;
3668 			}
3669 
3670 		}
3671 	}
3672 	if (value != NULL)
3673 		xmlFree(value);
3674 	return ((sa_property_t)node);
3675 }
3676 
3677 /*
3678  * sa_set_protocol_property(prop, value)
3679  *
3680  * Set the specified property to have the new value.  The protocol
3681  * specific plugin will then be called to update the property.
3682  */
3683 
3684 int
3685 sa_set_protocol_property(sa_property_t prop, char *section, char *value)
3686 {
3687 	sa_protocol_properties_t propset;
3688 	char *proto;
3689 	int ret = SA_INVALID_PROTOCOL;
3690 
3691 	propset = ((xmlNodePtr)prop)->parent;
3692 	if (propset != NULL) {
3693 		proto = sa_get_optionset_attr(propset, "type");
3694 		if (proto != NULL) {
3695 			if (section != NULL)
3696 				set_node_attr((xmlNodePtr)prop, "section",
3697 				    section);
3698 			set_node_attr((xmlNodePtr)prop, "value", value);
3699 			ret = sa_proto_set_property(proto, prop);
3700 			sa_free_attr_string(proto);
3701 		}
3702 	}
3703 	return (ret);
3704 }
3705 
3706 /*
3707  * sa_add_protocol_property(propset, prop)
3708  *
3709  * Add a new property to the protocol specific property set.
3710  */
3711 
3712 int
3713 sa_add_protocol_property(sa_protocol_properties_t propset, sa_property_t prop)
3714 {
3715 	xmlNodePtr node;
3716 
3717 	/* should check for legitimacy */
3718 	node = xmlAddChild((xmlNodePtr)propset, (xmlNodePtr)prop);
3719 	if (node != NULL)
3720 		return (SA_OK);
3721 	return (SA_NO_MEMORY);
3722 }
3723 
3724 /*
3725  * sa_create_protocol_properties(proto)
3726  *
3727  * Create a protocol specific property set.
3728  */
3729 
3730 sa_protocol_properties_t
3731 sa_create_protocol_properties(char *proto)
3732 {
3733 	xmlNodePtr node;
3734 
3735 	node = xmlNewNode(NULL, (xmlChar *)"propertyset");
3736 	if (node != NULL)
3737 		(void) xmlSetProp(node, (xmlChar *)"type", (xmlChar *)proto);
3738 	return (node);
3739 }
3740 
3741 /*
3742  * sa_get_share_resource(share, resource)
3743  *
3744  * Get the named resource from the share, if it exists. If resource is
3745  * NULL, get the first resource.
3746  */
3747 
3748 sa_resource_t
3749 sa_get_share_resource(sa_share_t share, char *resource)
3750 {
3751 	xmlNodePtr node = NULL;
3752 	xmlChar *name;
3753 
3754 	if (share != NULL) {
3755 		for (node = ((xmlNodePtr)share)->children; node != NULL;
3756 		    node = node->next) {
3757 			if (xmlStrcmp(node->name, (xmlChar *)"resource") == 0) {
3758 				if (resource == NULL) {
3759 					/*
3760 					 * We are looking for the first
3761 					 * resource node and not a names
3762 					 * resource.
3763 					 */
3764 					break;
3765 				} else {
3766 					/* is it the correct share? */
3767 					name = xmlGetProp(node,
3768 					    (xmlChar *)"name");
3769 					if (name != NULL &&
3770 					    xmlStrcasecmp(name,
3771 					    (xmlChar *)resource) == 0) {
3772 						xmlFree(name);
3773 						break;
3774 					}
3775 					xmlFree(name);
3776 				}
3777 			}
3778 		}
3779 	}
3780 	return ((sa_resource_t)node);
3781 }
3782 
3783 /*
3784  * sa_get_next_resource(resource)
3785  *	Return the next share following the specified share
3786  *	from the internal list of shares. Returns NULL if there
3787  *	are no more shares.  The list is relative to the same
3788  *	group.
3789  */
3790 sa_share_t
3791 sa_get_next_resource(sa_resource_t resource)
3792 {
3793 	xmlNodePtr node = NULL;
3794 
3795 	if (resource != NULL) {
3796 		for (node = ((xmlNodePtr)resource)->next; node != NULL;
3797 		    node = node->next) {
3798 			if (xmlStrcmp(node->name, (xmlChar *)"resource") == 0)
3799 				break;
3800 		}
3801 	}
3802 	return ((sa_share_t)node);
3803 }
3804 
3805 /*
3806  * _sa_get_next_resource_index(share)
3807  *
3808  * get the next resource index number (one greater then current largest)
3809  */
3810 
3811 static int
3812 _sa_get_next_resource_index(sa_share_t share)
3813 {
3814 	sa_resource_t resource;
3815 	int index = 0;
3816 	char *id;
3817 
3818 	for (resource = sa_get_share_resource(share, NULL);
3819 	    resource != NULL;
3820 	    resource = sa_get_next_resource(resource)) {
3821 		id = get_node_attr((void *)resource, "id");
3822 		if (id != NULL) {
3823 			int val;
3824 			val = atoi(id);
3825 			if (val > index)
3826 				index = val;
3827 			sa_free_attr_string(id);
3828 		}
3829 	}
3830 	return (index + 1);
3831 }
3832 
3833 
3834 /*
3835  * sa_add_resource(share, resource, persist, &err)
3836  *
3837  * Adds a new resource name associated with share. The resource name
3838  * must be unique in the system and will be case insensitive (eventually).
3839  */
3840 
3841 sa_resource_t
3842 sa_add_resource(sa_share_t share, char *resource, int persist, int *error)
3843 {
3844 	xmlNodePtr node;
3845 	int err = SA_OK;
3846 	sa_resource_t res;
3847 	sa_group_t group;
3848 	sa_handle_t handle;
3849 	char istring[8]; /* just big enough for an integer value */
3850 	int index;
3851 
3852 	group = sa_get_parent_group(share);
3853 	handle = sa_find_group_handle(group);
3854 	res = sa_find_resource(handle, resource);
3855 	if (res != NULL) {
3856 		err = SA_DUPLICATE_NAME;
3857 		res = NULL;
3858 	} else {
3859 		node = xmlNewChild((xmlNodePtr)share, NULL,
3860 		    (xmlChar *)"resource", NULL);
3861 		if (node != NULL) {
3862 			(void) xmlSetProp(node, (xmlChar *)"name",
3863 			    (xmlChar *)resource);
3864 			(void) xmlSetProp(node, (xmlChar *)"type", persist ?
3865 			    (xmlChar *)"persist" : (xmlChar *)"transient");
3866 			if (persist != SA_SHARE_TRANSIENT) {
3867 				index = _sa_get_next_resource_index(share);
3868 				(void) snprintf(istring, sizeof (istring), "%d",
3869 				    index);
3870 				(void) xmlSetProp(node, (xmlChar *)"id",
3871 				    (xmlChar *)istring);
3872 
3873 				if (!sa_is_persistent((sa_group_t)share))
3874 					goto done;
3875 
3876 				if (!sa_group_is_zfs(group)) {
3877 					/* ZFS doesn't use resource names */
3878 					sa_handle_impl_t ihandle;
3879 
3880 					ihandle = (sa_handle_impl_t)
3881 					    sa_find_group_handle(
3882 					    group);
3883 					if (ihandle != NULL)
3884 						err = sa_commit_share(
3885 						    ihandle->scfhandle, group,
3886 						    share);
3887 					else
3888 						err = SA_SYSTEM_ERR;
3889 				} else {
3890 					err = sa_zfs_update((sa_share_t)group);
3891 				}
3892 			}
3893 		}
3894 	}
3895 done:
3896 	if (error != NULL)
3897 		*error = err;
3898 	return ((sa_resource_t)node);
3899 }
3900 
3901 /*
3902  * sa_remove_resource(resource)
3903  *
3904  * Remove the resource name from the share (and the system)
3905  */
3906 
3907 int
3908 sa_remove_resource(sa_resource_t resource)
3909 {
3910 	sa_share_t share;
3911 	sa_group_t group;
3912 	char *type;
3913 	int ret = SA_OK;
3914 	boolean_t transient = B_FALSE;
3915 	sa_optionset_t opt;
3916 
3917 	share = sa_get_resource_parent(resource);
3918 	type = sa_get_share_attr(share, "type");
3919 	group = sa_get_parent_group(share);
3920 
3921 
3922 	if (type != NULL) {
3923 		if (strcmp(type, "persist") != 0)
3924 			transient = B_TRUE;
3925 		sa_free_attr_string(type);
3926 	}
3927 
3928 	/* Disable the resource for all protocols. */
3929 	(void) sa_disable_resource(resource, NULL);
3930 
3931 	/* Remove any optionsets from the resource. */
3932 	for (opt = sa_get_optionset(resource, NULL);
3933 	    opt != NULL;
3934 	    opt = sa_get_next_optionset(opt))
3935 		(void) sa_destroy_optionset(opt);
3936 
3937 	/* Remove from the share */
3938 	xmlUnlinkNode((xmlNode *)resource);
3939 	xmlFreeNode((xmlNode *)resource);
3940 
3941 	/* only do SMF action if permanent and not ZFS */
3942 	if (transient)
3943 		return (ret);
3944 
3945 	if (!sa_group_is_zfs(group)) {
3946 		sa_handle_impl_t ihandle;
3947 		ihandle = (sa_handle_impl_t)sa_find_group_handle(group);
3948 		if (ihandle != NULL)
3949 			ret = sa_commit_share(ihandle->scfhandle, group, share);
3950 		else
3951 			ret = SA_SYSTEM_ERR;
3952 	} else {
3953 		ret = sa_zfs_update((sa_share_t)group);
3954 	}
3955 
3956 	return (ret);
3957 }
3958 
3959 /*
3960  * proto_rename_resource(handle, group, resource, newname)
3961  *
3962  * Helper function for sa_rename_resource that notifies the protocol
3963  * of a resource name change prior to a config repository update.
3964  */
3965 static int
3966 proto_rename_resource(sa_handle_t handle, sa_group_t group,
3967     sa_resource_t resource, char *newname)
3968 {
3969 	sa_optionset_t optionset;
3970 	int ret = SA_OK;
3971 	int err;
3972 
3973 	for (optionset = sa_get_optionset(group, NULL);
3974 	    optionset != NULL;
3975 	    optionset = sa_get_next_optionset(optionset)) {
3976 		char *type;
3977 		type = sa_get_optionset_attr(optionset, "type");
3978 		if (type != NULL) {
3979 			err = sa_proto_rename_resource(handle, type, resource,
3980 			    newname);
3981 			if (err != SA_OK)
3982 				ret = err;
3983 			sa_free_attr_string(type);
3984 		}
3985 	}
3986 	return (ret);
3987 }
3988 
3989 /*
3990  * sa_rename_resource(resource, newname)
3991  *
3992  * Rename the resource to the new name, if it is unique.
3993  */
3994 
3995 int
3996 sa_rename_resource(sa_resource_t resource, char *newname)
3997 {
3998 	sa_share_t share;
3999 	sa_group_t group = NULL;
4000 	sa_resource_t target;
4001 	int ret = SA_CONFIG_ERR;
4002 	sa_handle_t handle = NULL;
4003 
4004 	share = sa_get_resource_parent(resource);
4005 	if (share == NULL)
4006 		return (ret);
4007 
4008 	group = sa_get_parent_group(share);
4009 	if (group == NULL)
4010 		return (ret);
4011 
4012 	handle = (sa_handle_impl_t)sa_find_group_handle(group);
4013 	if (handle == NULL)
4014 		return (ret);
4015 
4016 	target = sa_find_resource(handle, newname);
4017 	if (target != NULL) {
4018 		ret = SA_DUPLICATE_NAME;
4019 	} else {
4020 		/*
4021 		 * Everything appears to be valid at this
4022 		 * point. Change the name of the active share and then
4023 		 * update the share in the appropriate repository.
4024 		 */
4025 		ret = proto_rename_resource(handle, group, resource, newname);
4026 		set_node_attr(resource, "name", newname);
4027 
4028 		if (!sa_is_persistent((sa_group_t)share))
4029 			return (ret);
4030 
4031 		if (!sa_group_is_zfs(group)) {
4032 			sa_handle_impl_t ihandle = (sa_handle_impl_t)handle;
4033 			ret = sa_commit_share(ihandle->scfhandle, group,
4034 			    share);
4035 		} else {
4036 			ret = sa_zfs_update((sa_share_t)group);
4037 		}
4038 	}
4039 	return (ret);
4040 }
4041 
4042 /*
4043  * sa_get_resource_attr(resource, tag)
4044  *
4045  * Get the named attribute of the resource. "name" and "id" are
4046  * currently defined.  NULL if tag not defined.
4047  */
4048 
4049 char *
4050 sa_get_resource_attr(sa_resource_t resource, char *tag)
4051 {
4052 	return (get_node_attr((void *)resource, tag));
4053 }
4054 
4055 /*
4056  * sa_set_resource_attr(resource, tag, value)
4057  *
4058  * Get the named attribute of the resource. "name" and "id" are
4059  * currently defined.  NULL if tag not defined. Currently we don't do
4060  * much, but additional checking may be needed in the future.
4061  */
4062 
4063 int
4064 sa_set_resource_attr(sa_resource_t resource, char *tag, char *value)
4065 {
4066 	set_node_attr((void *)resource, tag, value);
4067 	return (SA_OK);
4068 }
4069 
4070 /*
4071  * sa_get_resource_parent(resource_t)
4072  *
4073  * Returns the share associated with the resource.
4074  */
4075 
4076 sa_share_t
4077 sa_get_resource_parent(sa_resource_t resource)
4078 {
4079 	sa_share_t share = NULL;
4080 
4081 	if (resource != NULL)
4082 		share = (sa_share_t)((xmlNodePtr)resource)->parent;
4083 	return (share);
4084 }
4085 
4086 /*
4087  * find_resource(group, name)
4088  *
4089  * Find the resource within the group.
4090  */
4091 
4092 static sa_resource_t
4093 find_resource(sa_group_t group, char *resname)
4094 {
4095 	sa_share_t share;
4096 	sa_resource_t resource = NULL;
4097 	char *name;
4098 
4099 	/* Iterate over all the shares and resources in the group. */
4100 	for (share = sa_get_share(group, NULL);
4101 	    share != NULL && resource == NULL;
4102 	    share = sa_get_next_share(share)) {
4103 		for (resource = sa_get_share_resource(share, NULL);
4104 		    resource != NULL;
4105 		    resource = sa_get_next_resource(resource)) {
4106 			name = sa_get_resource_attr(resource, "name");
4107 			if (name != NULL && xmlStrcasecmp((xmlChar*)name,
4108 			    (xmlChar*)resname) == 0) {
4109 				sa_free_attr_string(name);
4110 				break;
4111 			}
4112 			if (name != NULL) {
4113 				sa_free_attr_string(name);
4114 			}
4115 		}
4116 	}
4117 	return (resource);
4118 }
4119 
4120 /*
4121  * sa_find_resource(name)
4122  *
4123  * Find the named resource in the system.
4124  */
4125 
4126 sa_resource_t
4127 sa_find_resource(sa_handle_t handle, char *name)
4128 {
4129 	sa_group_t group;
4130 	sa_group_t zgroup;
4131 	sa_resource_t resource = NULL;
4132 
4133 	/*
4134 	 * Iterate over all groups and zfs subgroups and check for
4135 	 * resource name in them.
4136 	 */
4137 	for (group = sa_get_group(handle, NULL); group != NULL;
4138 	    group = sa_get_next_group(group)) {
4139 
4140 		if (is_zfs_group(group)) {
4141 			for (zgroup =
4142 			    (sa_group_t)_sa_get_child_node((xmlNodePtr)group,
4143 			    (xmlChar *)"group");
4144 			    zgroup != NULL && resource == NULL;
4145 			    zgroup = sa_get_next_group(zgroup)) {
4146 				resource = find_resource(zgroup, name);
4147 			}
4148 		} else {
4149 			resource = find_resource(group, name);
4150 		}
4151 		if (resource != NULL)
4152 			break;
4153 	}
4154 	return (resource);
4155 }
4156 
4157 /*
4158  * sa_get_resource(group, resource)
4159  *
4160  * Search all the shares in the specified group for a share with a
4161  * resource name matching the one specified.
4162  *
4163  * In the future, it may be advantageous to allow group to be NULL and
4164  * search all groups but that isn't needed at present.
4165  */
4166 
4167 sa_resource_t
4168 sa_get_resource(sa_group_t group, char *resource)
4169 {
4170 	sa_share_t share = NULL;
4171 	sa_resource_t res = NULL;
4172 
4173 	if (resource != NULL) {
4174 		for (share = sa_get_share(group, NULL);
4175 		    share != NULL && res == NULL;
4176 		    share = sa_get_next_share(share)) {
4177 			res = sa_get_share_resource(share, resource);
4178 		}
4179 	}
4180 	return (res);
4181 }
4182 
4183 /*
4184  * get_protocol_list(optionset, object)
4185  *
4186  * Get the protocol optionset list for the object and add them as
4187  * properties to optionset.
4188  */
4189 static int
4190 get_protocol_list(sa_optionset_t optionset, void *object)
4191 {
4192 	sa_property_t prop;
4193 	sa_optionset_t opts;
4194 	int ret = SA_OK;
4195 
4196 	for (opts = sa_get_optionset(object, NULL);
4197 	    opts != NULL;
4198 	    opts = sa_get_next_optionset(opts)) {
4199 		char *type;
4200 		type = sa_get_optionset_attr(opts, "type");
4201 		/*
4202 		 * It is possible to have a non-protocol optionset. We
4203 		 * skip any of those found.
4204 		 */
4205 		if (type == NULL)
4206 			continue;
4207 		prop = sa_create_property(type, "true");
4208 		sa_free_attr_string(type);
4209 		if (prop != NULL)
4210 			prop = (sa_property_t)xmlAddChild((xmlNodePtr)optionset,
4211 			    (xmlNodePtr)prop);
4212 		/* If prop is NULL, don't bother continuing */
4213 		if (prop == NULL) {
4214 			ret = SA_NO_MEMORY;
4215 			break;
4216 		}
4217 	}
4218 	return (ret);
4219 }
4220 
4221 /*
4222  * sa_free_protoset(optionset)
4223  *
4224  * Free the protocol property optionset.
4225  */
4226 static void
4227 sa_free_protoset(sa_optionset_t optionset)
4228 {
4229 	if (optionset != NULL) {
4230 		xmlUnlinkNode((xmlNodePtr) optionset);
4231 		xmlFreeNode((xmlNodePtr) optionset);
4232 	}
4233 }
4234 
4235 /*
4236  * sa_optionset_t sa_get_active_protocols(object)
4237  *
4238  * Return a list of the protocols that are active for the object.
4239  * This is currently an internal helper function, but could be
4240  * made visible if there is enough demand for it.
4241  *
4242  * The function finds the parent group and extracts the protocol
4243  * optionsets creating a new optionset with the protocols as properties.
4244  *
4245  * The caller must free the returned optionset.
4246  */
4247 
4248 static sa_optionset_t
4249 sa_get_active_protocols(void *object)
4250 {
4251 	sa_optionset_t options;
4252 	sa_share_t share = NULL;
4253 	sa_group_t group = NULL;
4254 	sa_resource_t resource = NULL;
4255 	int ret = SA_OK;
4256 
4257 	if (object == NULL)
4258 		return (NULL);
4259 	options = (sa_optionset_t)xmlNewNode(NULL, (xmlChar *)"optionset");
4260 	if (options == NULL)
4261 		return (NULL);
4262 
4263 	/*
4264 	 * Find the objects up the tree that might have protocols
4265 	 * enabled on them.
4266 	 */
4267 	if (sa_is_resource(object)) {
4268 		resource = (sa_resource_t)object;
4269 		share = sa_get_resource_parent(resource);
4270 		group = sa_get_parent_group(share);
4271 	} else if (sa_is_share(object)) {
4272 		share = (sa_share_t)object;
4273 		group = sa_get_parent_group(share);
4274 	} else {
4275 		group = (sa_group_t)group;
4276 	}
4277 	if (resource != NULL)
4278 		ret = get_protocol_list(options, resource);
4279 	if (ret == SA_OK && share != NULL)
4280 		ret = get_protocol_list(options, share);
4281 	if (ret == SA_OK && group != NULL)
4282 		ret = get_protocol_list(options, group);
4283 
4284 	/*
4285 	 * If there was an error, we won't have a complete list so
4286 	 * abandon everything.  The caller will have to deal with the
4287 	 * issue.
4288 	 */
4289 	if (ret != SA_OK) {
4290 		sa_free_protoset(options);
4291 		options = NULL;
4292 	}
4293 	return (options);
4294 }
4295 
4296 /*
4297  * sa_enable_resource, protocol)
4298  *	Disable the specified share to the specified protocol.
4299  *	If protocol is NULL, then all protocols.
4300  */
4301 int
4302 sa_enable_resource(sa_resource_t resource, char *protocol)
4303 {
4304 	int ret = SA_OK;
4305 
4306 	if (protocol != NULL) {
4307 		ret = sa_proto_share_resource(protocol, resource);
4308 	} else {
4309 		sa_optionset_t protoset;
4310 		sa_property_t prop;
4311 		char *proto;
4312 		int err;
4313 
4314 		/* need to do all protocols */
4315 		protoset = sa_get_active_protocols(resource);
4316 		if (protoset == NULL)
4317 			return (SA_NO_MEMORY);
4318 		for (prop = sa_get_property(protoset, NULL);
4319 		    prop != NULL;
4320 		    prop = sa_get_next_property(prop)) {
4321 			proto = sa_get_property_attr(prop, "type");
4322 			if (proto == NULL) {
4323 				ret = SA_NO_MEMORY;
4324 				continue;
4325 			}
4326 			err = sa_proto_share_resource(proto, resource);
4327 			if (err != SA_OK)
4328 				ret = err;
4329 			sa_free_attr_string(proto);
4330 		}
4331 		sa_free_protoset(protoset);
4332 	}
4333 	if (ret == SA_OK)
4334 		(void) sa_set_resource_attr(resource, "shared", NULL);
4335 
4336 	return (ret);
4337 }
4338 
4339 /*
4340  * sa_disable_resource(resource, protocol)
4341  *
4342  *	Disable the specified share for the specified protocol.  If
4343  *	protocol is NULL, then all protocols.  If the underlying
4344  *	protocol doesn't implement disable at the resource level, we
4345  *	disable at the share level.
4346  */
4347 int
4348 sa_disable_resource(sa_resource_t resource, char *protocol)
4349 {
4350 	int ret = SA_OK;
4351 
4352 	if (protocol != NULL) {
4353 		ret = sa_proto_unshare_resource(protocol, resource);
4354 		if (ret == SA_NOT_IMPLEMENTED) {
4355 			sa_share_t parent;
4356 			/*
4357 			 * The protocol doesn't implement unshare
4358 			 * resource. That implies that resource names are
4359 			 * simple aliases for this protocol so we need to
4360 			 * unshare the share.
4361 			 */
4362 			parent = sa_get_resource_parent(resource);
4363 			if (parent != NULL)
4364 				ret = sa_disable_share(parent, protocol);
4365 			else
4366 				ret = SA_CONFIG_ERR;
4367 		}
4368 	} else {
4369 		sa_optionset_t protoset;
4370 		sa_property_t prop;
4371 		char *proto;
4372 		int err;
4373 
4374 		/* need to do all protocols */
4375 		protoset = sa_get_active_protocols(resource);
4376 		if (protoset == NULL)
4377 			return (SA_NO_MEMORY);
4378 		for (prop = sa_get_property(protoset, NULL);
4379 		    prop != NULL;
4380 		    prop = sa_get_next_property(prop)) {
4381 			proto = sa_get_property_attr(prop, "type");
4382 			if (proto == NULL) {
4383 				ret = SA_NO_MEMORY;
4384 				continue;
4385 			}
4386 			err = sa_proto_unshare_resource(proto, resource);
4387 			if (err == SA_NOT_SUPPORTED) {
4388 				sa_share_t parent;
4389 				parent = sa_get_resource_parent(resource);
4390 				if (parent != NULL)
4391 					err = sa_disable_share(parent, proto);
4392 				else
4393 					err = SA_CONFIG_ERR;
4394 			}
4395 			if (err != SA_OK)
4396 				ret = err;
4397 			sa_free_attr_string(proto);
4398 		}
4399 		sa_free_protoset(protoset);
4400 	}
4401 	if (ret == SA_OK)
4402 		(void) sa_set_resource_attr(resource, "shared", NULL);
4403 
4404 	return (ret);
4405 }
4406 
4407 /*
4408  * sa_set_resource_description(resource, content)
4409  *
4410  * Set the description of share to content.
4411  */
4412 
4413 int
4414 sa_set_resource_description(sa_resource_t resource, char *content)
4415 {
4416 	xmlNodePtr node;
4417 	sa_group_t group;
4418 	sa_share_t share;
4419 	int ret = SA_OK;
4420 
4421 	for (node = ((xmlNodePtr)resource)->children;
4422 	    node != NULL;
4423 	    node = node->next) {
4424 		if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) {
4425 			break;
4426 		}
4427 	}
4428 
4429 	/* no existing description but want to add */
4430 	if (node == NULL && content != NULL) {
4431 		/* add a description */
4432 		node = _sa_set_share_description(resource, content);
4433 	} else if (node != NULL && content != NULL) {
4434 		/* update a description */
4435 		xmlNodeSetContent(node, (xmlChar *)content);
4436 	} else if (node != NULL && content == NULL) {
4437 		/* remove an existing description */
4438 		xmlUnlinkNode(node);
4439 		xmlFreeNode(node);
4440 	}
4441 
4442 	share = sa_get_resource_parent(resource);
4443 	group = sa_get_parent_group(share);
4444 	if (group != NULL &&
4445 	    sa_is_persistent(share) && (!sa_group_is_zfs(group))) {
4446 		sa_handle_impl_t impl_handle;
4447 		impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
4448 		if (impl_handle != NULL)
4449 			ret = sa_commit_share(impl_handle->scfhandle,
4450 			    group, share);
4451 		else
4452 			ret = SA_SYSTEM_ERR;
4453 	}
4454 	return (ret);
4455 }
4456 
4457 /*
4458  * sa_get_resource_description(share)
4459  *
4460  * Return the description text for the specified share if it
4461  * exists. NULL if no description exists.
4462  */
4463 
4464 char *
4465 sa_get_resource_description(sa_resource_t resource)
4466 {
4467 	xmlChar *description = NULL;
4468 	xmlNodePtr node;
4469 
4470 	for (node = ((xmlNodePtr)resource)->children; node != NULL;
4471 	    node = node->next) {
4472 		if (xmlStrcmp(node->name, (xmlChar *)"description") == 0)
4473 			break;
4474 	}
4475 	if (node != NULL) {
4476 		description = xmlNodeGetContent(node);
4477 		fixproblemchars((char *)description);
4478 	}
4479 	return ((char *)description);
4480 }
4481