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