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