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