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