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