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