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