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