xref: /illumos-gate/usr/src/lib/libshare/common/libsharecore.c (revision 14ea4bb737263733ad80a36b4f73f681c30a6b45)
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  * core library for common functions across all config store types
31  * and file systems to be exported. This includes legacy dfstab/sharetab
32  * parsing. Need to eliminate XML where possible.
33  */
34 
35 #include <stdio.h>
36 #include <string.h>
37 #include <ctype.h>
38 #include <unistd.h>
39 #include <limits.h>
40 #include <errno.h>
41 #include <sys/types.h>
42 #include <sys/stat.h>
43 #include <libxml/parser.h>
44 #include <libxml/tree.h>
45 #include "libshare.h"
46 #include "libshare_impl.h"
47 #include <fcntl.h>
48 #include <sys/stat.h>
49 #include <grp.h>
50 #include <limits.h>
51 #include <sys/param.h>
52 #include <signal.h>
53 #include <libintl.h>
54 
55 #include "sharetab.h"
56 
57 #define	DFSTAB_NOTICE_LINES	5
58 static char *notice[DFSTAB_NOTICE_LINES] =	{
59 	"# Do not modify this file directly.\n",
60 	"# Use the sharemgr(1m) command for all share management\n",
61 	"# This file is reconstructed and only maintained for backward\n",
62 	"# compatibility. Configuration lines could be lost.\n",
63 	"#\n"
64 };
65 
66 #define	STRNCAT(x, y, z)	(xmlChar *)strncat((char *)x, (char *)y, z)
67 
68 /* will be much smaller, but this handles bad syntax in the file */
69 #define	MAXARGSFORSHARE	256
70 
71 /* used internally only */
72 typedef
73 struct sharelist {
74     struct sharelist *next;
75     int   persist;
76     char *path;
77     char *resource;
78     char *fstype;
79     char *options;
80     char *description;
81     char *group;
82     char *origline;
83     int lineno;
84 } xfs_sharelist_t;
85 static void parse_dfstab(char *, xmlNodePtr);
86 extern char *get_token(char *);
87 static void dfs_free_list(xfs_sharelist_t *);
88 /* prototypes */
89 void getlegacyconfig(char *, xmlNodePtr *);
90 extern sa_share_t _sa_add_share(sa_group_t, char *, int, int *);
91 extern xmlNodePtr _sa_get_next_error(xmlNodePtr);
92 extern sa_group_t _sa_create_group(char *);
93 static void outdfstab(FILE *, xfs_sharelist_t *);
94 extern int _sa_remove_optionset(sa_optionset_t);
95 extern int set_node_share(void *, char *, char *);
96 extern void set_node_attr(void *, char *, char *);
97 
98 /*
99  * alloc_sharelist()
100  *
101  * allocator function to return an zfs_sharelist_t
102  */
103 
104 static xfs_sharelist_t *
105 alloc_sharelist()
106 {
107 	xfs_sharelist_t *item;
108 
109 	item = (xfs_sharelist_t *)malloc(sizeof (xfs_sharelist_t));
110 	if (item != NULL)
111 	    (void) memset(item, '\0', sizeof (xfs_sharelist_t));
112 	return (item);
113 }
114 
115 /*
116  * fix_notice(list)
117  *
118  * Look at the beginning of the current /etc/dfs/dfstab file and add
119  * the do not modify notice if it doesn't exist.
120  */
121 
122 static xfs_sharelist_t *
123 fix_notice(xfs_sharelist_t *list)
124 {
125 	xfs_sharelist_t *item, *prev;
126 	int i;
127 
128 	if (list == NULL) {
129 	    /* zero length dfstab */
130 	    list = alloc_sharelist();
131 	    if (list == NULL)
132 		return (NULL);
133 	    list->description = strdup("#\n");
134 	}
135 	if (list->path == NULL && list->description != NULL &&
136 	    strcmp(list->description, notice[0]) != 0) {
137 	    for (prev = NULL, i = 0; i < DFSTAB_NOTICE_LINES; i++) {
138 		item = alloc_sharelist();
139 		if (item != NULL) {
140 		    item->description = strdup(notice[i]);
141 		    if (prev == NULL) {
142 			item->next = list;
143 			prev = item;
144 			list = item;
145 		    } else {
146 			item->next = prev->next;
147 			prev->next = item;
148 			prev = item;
149 		    }
150 		}
151 	    }
152 	}
153 	return (list);
154 }
155 
156 /*
157  * getdfstab(dfs)
158  *
159  * Returns an zfs_sharelist_t list of lines from the dfstab file
160  * pointed to by the FILE pointer dfs. Each entry is parsed and the
161  * original line is also preserved. Used in parsing and updating the
162  * dfstab file.
163  */
164 
165 static xfs_sharelist_t *
166 getdfstab(FILE *dfs)
167 {
168 	char buff[_POSIX_ARG_MAX]; /* reasonable size given syntax of share */
169 	char *bp;
170 	char *token;
171 	char *args[MAXARGSFORSHARE];
172 	int argc;
173 	int c;
174 	static int line = 0;
175 	xfs_sharelist_t *item = NULL, *first = NULL, *last;
176 
177 	if (dfs != NULL) {
178 	    first = NULL;
179 	    line = 0;
180 	    while (fgets(buff, sizeof (buff), dfs) != NULL) {
181 		line++;
182 		bp = buff;
183 		if (buff[0] == '#') {
184 		    item = alloc_sharelist();
185 		    if (item != NULL) {
186 			/* if no path, then comment */
187 			item->lineno = line;
188 			item->description = strdup(buff);
189 			if (first == NULL) {
190 			    first = item;
191 			    last = item;
192 			} else {
193 			    last->next = item;
194 			    last = item;
195 			}
196 		    } else {
197 			break;
198 		    }
199 		    continue;
200 		} else if (buff[0] == '\n') {
201 		    continue;
202 		}
203 		optind = 1;
204 		item = alloc_sharelist();
205 		if (item == NULL) {
206 		    break;
207 		} else if (first == NULL) {
208 		    first = item;
209 		    last = item;
210 		} else {
211 		    last->next = item;
212 		    last = item;
213 		}
214 		item->lineno = line;
215 		item->origline = strdup(buff);
216 		(void) get_token(NULL); /* reset to new pointers */
217 		argc = 0;
218 		while ((token = get_token(bp)) != NULL) {
219 		    if (argc < MAXARGSFORSHARE) {
220 			args[argc++] = token;
221 		    }
222 		}
223 		while ((c = getopt(argc, args, "F:o:d:pg:")) != -1) {
224 		    switch (c) {
225 		    case 'p':
226 			item->persist = 1;
227 			break;
228 		    case 'F':
229 			item->fstype = strdup(optarg);
230 			break;
231 		    case 'o':
232 			item->options = strdup(optarg);
233 			break;
234 		    case 'd':
235 			item->description = strdup(optarg);
236 			break;
237 		    case 'g':
238 			item->group = strdup(optarg);
239 			break;
240 		    default:
241 			break;
242 		    }
243 		}
244 		if (optind < argc) {
245 		    item->path = strdup(args[optind]);
246 		    optind++;
247 		    if (optind < argc) {
248 			char *resource;
249 			char *optgroup;
250 			/* resource and/or groupname */
251 			resource = args[optind];
252 			optgroup = strchr(resource, '@');
253 			if (optgroup != NULL) {
254 			    *optgroup++ = '\0';
255 			}
256 			if (optgroup != NULL)
257 			    item->group = strdup(optgroup);
258 			if (resource != NULL && strlen(resource) > 0)
259 			    item->resource = strdup(resource);
260 		    }
261 		}
262 	    }
263 	    if (item != NULL && item->fstype == NULL)
264 		item->fstype = strdup("nfs"); /* this is the default */
265 	}
266 	first = fix_notice(first);
267 	return (first);
268 }
269 
270 /*
271  * finddfsentry(list, path)
272  *
273  * Look for path in the zfs_sharelist_t list and return the entry if it
274  * exists.
275  */
276 
277 static xfs_sharelist_t *
278 finddfsentry(xfs_sharelist_t *list, char *path)
279 {
280 	xfs_sharelist_t *item;
281 
282 	for (item = list; item != NULL; item = item->next) {
283 	    if (item->path != NULL && strcmp(item->path, path) == 0)
284 		return (item);
285 	}
286 	return (NULL);
287 }
288 
289 /*
290  * remdfsentry(list, path, proto)
291  *
292  * Remove the specified path (with protocol) from the list. This will
293  * remove it from dfstab when the file is rewritten.
294  */
295 
296 static xfs_sharelist_t *
297 remdfsentry(xfs_sharelist_t *list, char *path, char *proto)
298 {
299 	xfs_sharelist_t *item, *prev = NULL;
300 
301 
302 	for (item = prev = list; item != NULL; item = item->next) {
303 	    /* skip comment entry but don't lose it */
304 	    if (item->path == NULL) {
305 		prev = item;
306 		continue;
307 	    }
308 	    /* if proto is NULL, remove all protocols */
309 	    if (proto == NULL || (strcmp(item->path, path) == 0 &&
310 		(item->fstype != NULL && strcmp(item->fstype, proto) == 0)))
311 		break;
312 	    if (item->fstype == NULL &&
313 		(proto == NULL || strcmp(proto, "nfs") == 0))
314 		break;
315 	    prev = item;
316 	}
317 	if (item != NULL) {
318 	    if (item == prev) {
319 		list = item->next; /* this must be the first one */
320 	    } else {
321 		prev->next = item->next;
322 	    }
323 	    item->next = NULL;
324 	    dfs_free_list(item);
325 	}
326 	return (list);
327 }
328 
329 /*
330  * remdfsline(list, line)
331  *
332  * Remove the line specified from the list.
333  */
334 
335 static xfs_sharelist_t *
336 remdfsline(xfs_sharelist_t *list, char *line)
337 {
338 	xfs_sharelist_t *item, *prev = NULL;
339 
340 	for (item = prev = list; item != NULL; item = item->next) {
341 	    /* skip comment entry but don't lose it */
342 	    if (item->path == NULL) {
343 		prev = item;
344 		continue;
345 	    }
346 	    if (strcmp(item->origline, line) == 0) {
347 		break;
348 	    }
349 	    prev = item;
350 	}
351 	if (item != NULL) {
352 	    if (item == prev) {
353 		list = item->next; /* this must be the first one */
354 	    } else {
355 		prev->next = item->next;
356 	    }
357 	    item->next = NULL;
358 	    dfs_free_list(item);
359 	}
360 	return (list);
361 }
362 
363 /*
364  * adddfsentry(list, share, proto)
365  *
366  * Add an entry to the dfstab list for share (relative to proto). This
367  * is used to update dfstab for legacy purposes.
368  */
369 
370 static xfs_sharelist_t *
371 adddfsentry(xfs_sharelist_t *list, sa_share_t share, char *proto)
372 {
373 	xfs_sharelist_t *item, *tmp;
374 	sa_group_t parent;
375 	char *groupname;
376 
377 	item = alloc_sharelist();
378 	if (item != NULL) {
379 	    parent = sa_get_parent_group(share);
380 	    groupname = sa_get_group_attr(parent, "name");
381 	    if (strcmp(groupname, "default") == 0) {
382 		sa_free_attr_string(groupname);
383 		groupname = NULL;
384 	    }
385 	    item->path = sa_get_share_attr(share, "path");
386 	    item->resource = sa_get_share_attr(share, "resource");
387 	    item->group = groupname;
388 	    item->fstype = strdup(proto);
389 	    item->options = sa_proto_legacy_format(proto, share, 1);
390 	    if (item->options != NULL && strlen(item->options) == 0) {
391 		free(item->options);
392 		item->options = NULL;
393 	    }
394 	    item->description = sa_get_share_description(share);
395 	    if (item->description != NULL && strlen(item->description) == 0) {
396 		sa_free_share_description(item->description);
397 		item->description = NULL;
398 	    }
399 	    if (list == NULL) {
400 		list = item;
401 	    } else {
402 		for (tmp = list; tmp->next != NULL; tmp = tmp->next)
403 		    /* do nothing */;
404 		tmp->next = item;
405 	    }
406 	}
407 	return (list);
408 }
409 
410 /*
411  * outdfstab(dfstab, list)
412  *
413  * Output the list to dfstab making sure the file is truncated.
414  * Comments and errors are preserved.
415  */
416 
417 static void
418 outdfstab(FILE *dfstab, xfs_sharelist_t *list)
419 {
420 	xfs_sharelist_t *item;
421 
422 	(void) ftruncate(fileno(dfstab), 0);
423 
424 	for (item = list; item != NULL; item = item->next) {
425 	    if (item->path != NULL) {
426 		if (*item->path == '/')
427 		    (void) fprintf(dfstab, "share %s%s%s%s%s%s%s %s%s%s%s%s\n",
428 			    (item->fstype != NULL) ? "-F " : "",
429 			    (item->fstype != NULL) ? item->fstype : "",
430 			    (item->options != NULL) ? " -o " : "",
431 			    (item->options != NULL) ? item->options : "",
432 			    (item->description != NULL) ? " -d \"" : "",
433 			    (item->description != NULL) ?
434 				item->description : "",
435 			    (item->description != NULL) ? "\"" : "",
436 			    item->path,
437 			    ((item->resource != NULL) ||
438 				(item->group != NULL)) ? " " : "",
439 			    (item->resource != NULL) ? item->resource : "",
440 			    item->group != NULL ? "@" : "",
441 			    item->group != NULL ? item->group : "");
442 		else
443 		    (void) fprintf(dfstab, "%s", item->origline);
444 	    } else {
445 		if (item->description != NULL) {
446 		    (void) fprintf(dfstab, "%s", item->description);
447 		} else {
448 		    (void) fprintf(dfstab, "%s", item->origline);
449 		}
450 	    }
451 	}
452 }
453 
454 /*
455  * open_dfstab(file)
456  *
457  * Open the specified dfstab file. If the owner/group/perms are wrong,
458  * fix them.
459  */
460 
461 static FILE *
462 open_dfstab(char *file)
463 {
464 	struct group *grp;
465 	struct group group;
466 	char *buff;
467 	int grsize;
468 	FILE *dfstab;
469 
470 	dfstab = fopen(file, "r+");
471 	if (dfstab == NULL) {
472 	    dfstab = fopen(file, "w+");
473 	}
474 	if (dfstab != NULL) {
475 		grsize = sysconf(_SC_GETGR_R_SIZE_MAX);
476 		buff = malloc(grsize);
477 		if (buff != NULL)
478 		    grp = getgrnam_r(SA_DEFAULT_FILE_GRP, &group, buff, grsize);
479 		else
480 		    grp = getgrnam(SA_DEFAULT_FILE_GRP); /* take the risk */
481 		(void) fchmod(fileno(dfstab), 0644);
482 		(void) fchown(fileno(dfstab), 0,
483 				grp != NULL ? grp->gr_gid : 3);
484 		if (buff != NULL)
485 		    free(buff);
486 		rewind(dfstab);
487 	}
488 	return (dfstab);
489 }
490 
491 /*
492  * sa_comment_line(line, err)
493  *
494  * Add a comment to the dfstab file with err as a prefix to the
495  * original line.
496  */
497 
498 static void
499 sa_comment_line(char *line, char *err)
500 {
501 	FILE *dfstab;
502 	xfs_sharelist_t *list;
503 	sigset_t old, new;
504 
505 	dfstab = open_dfstab(SA_LEGACY_DFSTAB);
506 	if (dfstab != NULL) {
507 		(void) setvbuf(dfstab, NULL, _IOLBF, BUFSIZ * 8);
508 		(void) sigprocmask(SIG_BLOCK, NULL, &new);
509 		(void) sigaddset(&new, SIGHUP);
510 		(void) sigaddset(&new, SIGINT);
511 		(void) sigaddset(&new, SIGQUIT);
512 		(void) sigaddset(&new, SIGTSTP);
513 		(void) sigprocmask(SIG_SETMASK, &new, &old);
514 		(void) lockf(fileno(dfstab), F_LOCK, 0);
515 		list = getdfstab(dfstab);
516 		rewind(dfstab);
517 		(void) remdfsline(list, line);
518 		outdfstab(dfstab, list);
519 		(void) fprintf(dfstab, "# Error: %s: %s", err, line);
520 		(void) fsync(fileno(dfstab));
521 		(void) lockf(fileno(dfstab), F_ULOCK, 0);
522 		(void) fclose(dfstab);
523 		(void) sigprocmask(SIG_SETMASK, &old, NULL);
524 		if (list != NULL)
525 		    dfs_free_list(list);
526 	}
527 }
528 
529 /*
530  * sa_delete_legacy(share)
531  *
532  * Delete the specified share from the legacy config file.
533  */
534 
535 int
536 sa_delete_legacy(sa_share_t share)
537 {
538 	FILE *dfstab;
539 	int err;
540 	int ret = SA_OK;
541 	xfs_sharelist_t *list;
542 	char *path;
543 	sa_optionset_t optionset;
544 	sa_group_t parent;
545 	sigset_t old, new;
546 
547 	dfstab = open_dfstab(SA_LEGACY_DFSTAB);
548 	if (dfstab != NULL) {
549 		(void) setvbuf(dfstab, NULL, _IOLBF, BUFSIZ * 8);
550 		(void) sigprocmask(SIG_BLOCK, NULL, &new);
551 		(void) sigaddset(&new, SIGHUP);
552 		(void) sigaddset(&new, SIGINT);
553 		(void) sigaddset(&new, SIGQUIT);
554 		(void) sigaddset(&new, SIGTSTP);
555 		(void) sigprocmask(SIG_SETMASK, &new, &old);
556 		path = sa_get_share_attr(share, "path");
557 		parent = sa_get_parent_group(share);
558 		if (parent != NULL) {
559 		    (void) lockf(fileno(dfstab), F_LOCK, 0);
560 		    list = getdfstab(dfstab);
561 		    rewind(dfstab);
562 		    for (optionset = sa_get_optionset(parent, NULL);
563 			optionset != NULL;
564 			optionset = sa_get_next_optionset(optionset)) {
565 			char *proto = sa_get_optionset_attr(optionset, "type");
566 			if (list != NULL && proto != NULL)
567 			    list = remdfsentry(list, path, proto);
568 			if (proto == NULL)
569 			    ret = SA_NO_MEMORY;
570 			/*
571 			 * may want to only do the dfstab if this call
572 			 * returns NOT IMPLEMENTED but it shouldn't
573 			 * hurt.
574 			 */
575 			if (ret == SA_OK) {
576 			    err = sa_proto_delete_legacy(proto, share);
577 			    if (err != SA_NOT_IMPLEMENTED)
578 				ret = err;
579 			}
580 			if (proto != NULL)
581 			    sa_free_attr_string(proto);
582 		    }
583 		    outdfstab(dfstab, list);
584 		    if (list != NULL)
585 			dfs_free_list(list);
586 		    (void) fflush(dfstab);
587 		    (void) lockf(fileno(dfstab), F_ULOCK, 0);
588 		}
589 		(void) fsync(fileno(dfstab));
590 		(void) sigprocmask(SIG_SETMASK, &old, NULL);
591 		(void) fclose(dfstab);
592 		sa_free_attr_string(path);
593 	} else {
594 		if (errno == EACCES || errno == EPERM) {
595 		    ret = SA_NO_PERMISSION;
596 		} else {
597 		    ret = SA_CONFIG_ERR;
598 		}
599 	}
600 	return (ret);
601 }
602 
603 /*
604  * sa_update_legacy(share, proto)
605  *
606  * There is an assumption that dfstab will be the most common form of
607  * legacy configuration file for shares, but not the only one. Because
608  * of that, dfstab handling is done in the main code with calls to
609  * this function and protocol specific calls to deal with formating
610  * options into dfstab/share compatible syntax. Since not everything
611  * will be dfstab, there is a provision for calling a protocol
612  * specific plugin interface that allows the protocol plugin to do its
613  * own legacy files and skip the dfstab update.
614  */
615 
616 int
617 sa_update_legacy(sa_share_t share, char *proto)
618 {
619 	FILE *dfstab;
620 	int ret = SA_OK;
621 	xfs_sharelist_t *list;
622 	char *path;
623 	sigset_t old, new;
624 	char *persist;
625 
626 	ret = sa_proto_update_legacy(proto, share);
627 	if (ret != SA_NOT_IMPLEMENTED)
628 	    return (ret);
629 	/* do the dfstab format */
630 	persist = sa_get_share_attr(share, "type");
631 	/*
632 	 * only update if the share is not transient -- no share type
633 	 * set or the type is not "transient".
634 	 */
635 	if (persist == NULL || strcmp(persist, "transient") != 0) {
636 	    dfstab = open_dfstab(SA_LEGACY_DFSTAB);
637 	    if (dfstab != NULL) {
638 		(void) setvbuf(dfstab, NULL, _IOLBF, BUFSIZ * 8);
639 		(void) sigprocmask(SIG_BLOCK, NULL, &new);
640 		(void) sigaddset(&new, SIGHUP);
641 		(void) sigaddset(&new, SIGINT);
642 		(void) sigaddset(&new, SIGQUIT);
643 		(void) sigaddset(&new, SIGTSTP);
644 		(void) sigprocmask(SIG_SETMASK, &new, &old);
645 		path = sa_get_share_attr(share, "path");
646 		(void) lockf(fileno(dfstab), F_LOCK, 0);
647 		list = getdfstab(dfstab);
648 		rewind(dfstab);
649 		if (list != NULL)
650 		    list = remdfsentry(list, path, proto);
651 		list = adddfsentry(list, share, proto);
652 		outdfstab(dfstab, list);
653 		(void) fflush(dfstab);
654 		(void) lockf(fileno(dfstab), F_ULOCK, 0);
655 		(void) fsync(fileno(dfstab));
656 		(void) sigprocmask(SIG_SETMASK, &old, NULL);
657 		(void) fclose(dfstab);
658 		sa_free_attr_string(path);
659 		if (list != NULL)
660 		    dfs_free_list(list);
661 	    } else {
662 		if (errno == EACCES || errno == EPERM) {
663 		    ret = SA_NO_PERMISSION;
664 		} else {
665 		    ret = SA_CONFIG_ERR;
666 		}
667 	    }
668 	}
669 	if (persist != NULL)
670 	    sa_free_attr_string(persist);
671 	return (ret);
672 }
673 
674 /*
675  * sa_is_security(optname, proto)
676  *
677  * Check to see if optname is a security (named optionset) specific
678  * property for the specified protocol.
679  */
680 
681 int
682 sa_is_security(char *optname, char *proto)
683 {
684 	int ret = 0;
685 	if (proto != NULL)
686 	    ret = sa_proto_security_prop(proto, optname);
687 	return (ret);
688 }
689 
690 /*
691  * add_syntax_comment(root, line, err, todfstab)
692  *
693  * add a comment to the document indicating a syntax error. If
694  * todfstab is set, write it back to the dfstab file as well.
695  */
696 
697 static void
698 add_syntax_comment(xmlNodePtr root, char *line, char *err, int todfstab)
699 {
700 	xmlNodePtr node;
701 
702 	node = xmlNewChild(root, NULL, (xmlChar *)"error", (xmlChar *)line);
703 	if (node != NULL) {
704 	    xmlSetProp(node, (xmlChar *)"type", (xmlChar *)err);
705 	}
706 	if (todfstab)
707 	    sa_comment_line(line, err);
708 }
709 
710 /*
711  * sa_is_share(object)
712  *
713  * returns true of the object is of type "share".
714  */
715 
716 int
717 sa_is_share(void *object)
718 {
719 	if (object != NULL) {
720 	    if (strcmp((char *)((xmlNodePtr)object)->name, "share") == 0)
721 		return (1);
722 	}
723 	return (0);
724 }
725 
726 /*
727  * _sa_remove_property(property)
728  *
729  * remove a property only from the document.
730  */
731 
732 static void
733 _sa_remove_property(sa_property_t property)
734 {
735 	xmlUnlinkNode((xmlNodePtr)property);
736 	xmlFreeNode((xmlNodePtr)property);
737 }
738 
739 /*
740  * sa_parse_legacy_options(group, options, proto)
741  *
742  * In order to support legacy configurations, we allow the protocol
743  * specific plugin to parse legacy syntax options (like those in
744  * /etc/dfs/dfstab). This adds a new optionset to the group (or
745  * share).
746  *
747  * Once the optionset has been created, we then get the derived
748  * optionset of the parent (options from the optionset of the parent
749  * and any parent it might have) and remove those from the created
750  * optionset. This avoids duplication of options.
751  */
752 
753 int
754 sa_parse_legacy_options(sa_group_t group, char *options, char *proto)
755 {
756 	int ret = SA_INVALID_PROTOCOL;
757 	sa_group_t parent;
758 	parent = sa_get_parent_group(group);
759 
760 	if (proto != NULL)
761 	    ret = sa_proto_legacy_opts(proto, group, options);
762 	/*
763 	 * if in a group, remove the inherited options and security
764 	 */
765 	if (ret == SA_OK) {
766 	    if (parent != NULL) {
767 		sa_optionset_t optionset;
768 		sa_property_t popt, prop;
769 		sa_optionset_t localoptions;
770 		/* find parent options to remove from child */
771 		optionset = sa_get_derived_optionset(parent, proto, 1);
772 		localoptions = sa_get_optionset(group, proto);
773 		if (optionset != NULL) {
774 		    for (popt = sa_get_property(optionset, NULL);
775 			popt != NULL;
776 			popt = sa_get_next_property(popt)) {
777 			char *tag;
778 			char *value1;
779 			char *value2;
780 
781 			tag = sa_get_property_attr(popt, "type");
782 			if (tag != NULL) {
783 			    prop = sa_get_property(localoptions, tag);
784 			    if (prop != NULL) {
785 				value1 = sa_get_property_attr(popt, "value");
786 				value2 = sa_get_property_attr(prop, "value");
787 				if (value1 != NULL && value2 != NULL &&
788 				    strcmp(value1, value2) == 0) {
789 				    /* remove the property from the child */
790 				    (void) _sa_remove_property(prop);
791 				}
792 				if (value1 != NULL)
793 				    sa_free_attr_string(value1);
794 				if (value2 != NULL)
795 				    sa_free_attr_string(value2);
796 			    }
797 			    sa_free_attr_string(tag);
798 			}
799 		    }
800 		    prop = sa_get_property(localoptions, NULL);
801 		    if (prop == NULL && sa_is_share(group)) {
802 			/*
803 			 * all properties removed so remove the
804 			 * optionset if it is on a share
805 			 */
806 			(void) _sa_remove_optionset(localoptions);
807 		    }
808 		    sa_free_derived_optionset(optionset);
809 		}
810 		/*
811 		 * need to remove security here. If there are no
812 		 * security options on the local group/share, don't
813 		 * bother since those are the only ones that would be
814 		 * affected.
815 		 */
816 		localoptions = sa_get_all_security_types(group, proto, 0);
817 		if (localoptions != NULL) {
818 		    for (prop = sa_get_property(localoptions, NULL);
819 			prop != NULL; prop = sa_get_next_property(prop)) {
820 			char *tag;
821 			sa_security_t security;
822 			tag = sa_get_property_attr(prop, "type");
823 			if (tag != NULL) {
824 			    security = sa_get_security(group, tag, proto);
825 			    sa_free_attr_string(tag);
826 			    for (popt = sa_get_property(security, NULL);
827 				popt != NULL;
828 				popt = sa_get_next_property(popt)) {
829 				char *value1;
830 				char *value2;
831 
832 				/* remove duplicates from this level */
833 				value1 = sa_get_property_attr(popt, "value");
834 				value2 = sa_get_property_attr(prop, "value");
835 				if (value1 != NULL && value2 != NULL &&
836 				    strcmp(value1, value2) == 0) {
837 				    /* remove the property from the child */
838 				    (void) _sa_remove_property(prop);
839 				}
840 				if (value1 != NULL)
841 				    sa_free_attr_string(value1);
842 				if (value2 != NULL)
843 				    sa_free_attr_string(value2);
844 			    }
845 			}
846 		    }
847 		    (void) sa_destroy_optionset(localoptions);
848 		}
849 	    }
850 	}
851 	return (ret);
852 }
853 
854 /*
855  * dfs_free_list(list)
856  *
857  * Free the data in each list entry of the list as well as freeing the
858  * entries themselves. We need to avoid memory leaks and don't want to
859  * dereference any NULL members.
860  */
861 
862 static void
863 dfs_free_list(xfs_sharelist_t *list)
864 {
865 	xfs_sharelist_t *entry;
866 	for (entry = list; entry != NULL; entry = list) {
867 	    if (entry->path != NULL)
868 		free(entry->path);
869 	    if (entry->resource != NULL)
870 		free(entry->resource);
871 	    if (entry->fstype != NULL)
872 		free(entry->fstype);
873 	    if (entry->options != NULL)
874 		free(entry->options);
875 	    if (entry->description != NULL)
876 		free(entry->description);
877 	    if (entry->origline != NULL)
878 		free(entry->origline);
879 	    if (entry->group != NULL)
880 		free(entry->group);
881 	    list = list->next;
882 	    free(entry);
883 	}
884 }
885 
886 /*
887  * parse_dfstab(dfstab, root)
888  *
889  * Open and read the existing dfstab, parsing each line and adding it
890  * to the internal configuration. Make sure syntax errors, etc are
891  * preserved as comments.
892  */
893 
894 static void
895 parse_dfstab(char *dfstab, xmlNodePtr root)
896 {
897 	sa_share_t share;
898 	sa_group_t group;
899 	sa_group_t sgroup = NULL;
900 	sa_group_t defgroup;
901 	xfs_sharelist_t *head, *list;
902 	int err;
903 	int defined_group;
904 	FILE *dfs;
905 	char *oldprops;
906 
907 	/* read the dfstab format file and fill in the doc tree */
908 
909 	dfs = fopen(dfstab, "r");
910 	if (dfs == NULL) {
911 	    return;
912 	}
913 
914 	defgroup = sa_get_group("default");
915 
916 	for (head = list = getdfstab(dfs);
917 		list != NULL;
918 		list = list->next) {
919 	    share = NULL;
920 	    group = NULL;
921 	    defined_group = 0;
922 	    err = 0;
923 
924 	    if (list->origline == NULL) {
925 		/*
926 		 * Comment line that we will likely skip.
927 		 * If the line has the syntax:
928 		 *	# error: string: string
929 		 * It should be preserved until manually deleted.
930 		 */
931 		if (list->description != NULL &&
932 		    strncmp(list->description, "# Error: ", 9) == 0) {
933 		    char *line;
934 		    char *error;
935 		    char *cmd;
936 		    line = strdup(list->description);
937 		    if (line != NULL) {
938 			error = line + 9;
939 			cmd = strchr(error, ':');
940 			if (cmd != NULL) {
941 			    int len;
942 			    *cmd = '\0';
943 			    cmd += 2;
944 			    len = strlen(cmd);
945 			    cmd[len - 1] = '\0';
946 			    add_syntax_comment(root, cmd, error, 0);
947 			}
948 			free(line);
949 		    }
950 		}
951 		continue;
952 	    }
953 	    if (list->path != NULL && strlen(list->path) > 0 &&
954 		*list->path == '/') {
955 		share = sa_find_share(list->path);
956 		if (share != NULL)
957 		    sgroup = sa_get_parent_group(share);
958 		else
959 		    sgroup = NULL;
960 	    } else {
961 		(void) printf(gettext("No share specified in dfstab: "
962 					"line %d: %s\n"),
963 			list->lineno, list->origline);
964 		add_syntax_comment(root, list->origline,
965 				    gettext("No share specified"),
966 				    1);
967 		continue;
968 	    }
969 	    if (list->group != NULL && strlen(list->group) > 0) {
970 		group = sa_get_group(list->group);
971 		defined_group = 1;
972 	    } else {
973 		group = defgroup;
974 	    }
975 	    if (defined_group && group == NULL) {
976 		(void) printf(gettext("Unknown group used in dfstab: "
977 					"line %d: %s\n"),
978 			list->lineno, list->origline);
979 		add_syntax_comment(root, list->origline,
980 				    gettext("Unknown group specified"), 1);
981 		continue;
982 	    }
983 	    if (group != NULL) {
984 		if (share == NULL) {
985 		    if (!defined_group && group == defgroup) {
986 			/* this is an OK add for legacy */
987 			share = sa_add_share(defgroup, list->path,
988 					SA_SHARE_PERMANENT | SA_SHARE_PARSER,
989 					&err);
990 			if (share != NULL) {
991 			    if (list->description != NULL &&
992 				strlen(list->description) > 0)
993 				(void) sa_set_share_description(share,
994 							    list->description);
995 			    if (list->options != NULL &&
996 				strlen(list->options) > 0) {
997 				(void) sa_parse_legacy_options(share,
998 								list->options,
999 								list->fstype);
1000 			    }
1001 			    if (list->resource != NULL)
1002 				(void) sa_set_share_attr(share, "resource",
1003 						    list->resource);
1004 			} else {
1005 			    (void) printf(gettext("Error in dfstab: "
1006 					    "line %d: %s\n"),
1007 				    list->lineno, list->origline);
1008 			    if (err != SA_BAD_PATH)
1009 				add_syntax_comment(root, list->origline,
1010 						gettext("Syntax"), 1);
1011 			    else
1012 				add_syntax_comment(root, list->origline,
1013 						gettext("Path"), 1);
1014 			    continue;
1015 			}
1016 		    }
1017 		} else {
1018 		    if (group != sgroup) {
1019 			(void) printf(gettext("Attempt to change"
1020 						"configuration in"
1021 						"dfstab: line %d: %s\n"),
1022 				list->lineno, list->origline);
1023 			add_syntax_comment(root, list->origline,
1024 				gettext("Attempt to change configuration"), 1);
1025 			continue;
1026 		    }
1027 		    /* its the same group but could have changed options */
1028 		    oldprops = sa_proto_legacy_format(list->fstype, share, 0);
1029 		    if (oldprops != NULL) {
1030 			if (list->options != NULL &&
1031 				strcmp(oldprops, list->options) != 0) {
1032 			    sa_optionset_t opts;
1033 			    sa_security_t secs;
1034 			    /* possibly different values */
1035 			    opts = sa_get_optionset((sa_group_t)share,
1036 							list->fstype);
1037 			    (void) sa_destroy_optionset(opts);
1038 			    for (secs = sa_get_security((sa_group_t)share,
1039 							NULL, list->fstype);
1040 				secs != NULL;
1041 				secs = sa_get_security((sa_group_t)share,
1042 							NULL, list->fstype)) {
1043 				(void) sa_destroy_security(secs);
1044 			    }
1045 			    (void) sa_parse_legacy_options(share,
1046 							    list->options,
1047 							    list->fstype);
1048 			}
1049 		    }
1050 		}
1051 	    } else {
1052 		/* shouldn't happen */
1053 		err = SA_CONFIG_ERR;
1054 	    }
1055 
1056 	}
1057 	dfs_free_list(head);
1058 }
1059 
1060 /*
1061  * legacy_removes(group, file)
1062  *
1063  * Find any shares that are "missing" from the legacy file. These
1064  * should be removed from the configuration since they are likely from
1065  * a legacy app or the admin modified the dfstab file directly. We
1066  * have to support this even if it is not the recommended way to do
1067  * things.
1068  */
1069 
1070 static void
1071 legacy_removes(sa_group_t group, char *file)
1072 {
1073 	sa_share_t share;
1074 	char *path;
1075 	xfs_sharelist_t *list, *item;
1076 	FILE *dfstab;
1077 
1078 	dfstab = fopen(file, "r");
1079 	if (dfstab != NULL) {
1080 	    list = getdfstab(dfstab);
1081 	    (void) fclose(dfstab);
1082 	    for (share = sa_get_share(group, NULL); share != NULL;
1083 		share = sa_get_next_share(share)) {
1084 		/* now see if the share is in the dfstab file */
1085 		path = sa_get_share_attr(share, "path");
1086 		if (path != NULL) {
1087 		    item = finddfsentry(list, path);
1088 		    if (item == NULL) {
1089 			/* the share was removed this way */
1090 			(void) sa_remove_share(share);
1091 			/* start over since the list was broken */
1092 			share = sa_get_share(group, NULL);
1093 		    }
1094 		    sa_free_attr_string(path);
1095 		}
1096 	    }
1097 	    if (list != NULL)
1098 		dfs_free_list(list);
1099 	}
1100 }
1101 
1102 /*
1103  * getlegacyconfig(path, root)
1104  *
1105  * Parse dfstab and build the legacy configuration. This only gets
1106  * called when a change was detected.
1107  */
1108 
1109 void
1110 getlegacyconfig(char *path, xmlNodePtr *root)
1111 {
1112 	sa_group_t defgroup;
1113 
1114 	if (root != NULL) {
1115 	    if (*root == NULL)
1116 		*root = xmlNewNode(NULL, (xmlChar *)"sharecfg");
1117 	    if (*root != NULL) {
1118 		if (strcmp(path, SA_LEGACY_DFSTAB) == 0) {
1119 			/*
1120 			 * walk the default shares and find anything
1121 			 * missing.  we do this first to make sure it
1122 			 * is cleaned up since there may be legacy
1123 			 * code add/del via dfstab and we need to
1124 			 * cleanup SMF.
1125 			 */
1126 		    defgroup = sa_get_group("default");
1127 		    if (defgroup != NULL) {
1128 			legacy_removes(defgroup, path);
1129 		    }
1130 		    /* parse the dfstab and add anything new */
1131 		    parse_dfstab(path, *root);
1132 		}
1133 	    }
1134 	}
1135 }
1136 
1137 /*
1138  * get_share_list(&err)
1139  *
1140  * Get a linked list of all the shares on the system from
1141  * /etc/dfs/sharetab. This is partially copied from libfsmgt which we
1142  * can't use due to package dependencies.
1143  */
1144 static xfs_sharelist_t *
1145 get_share_list(int *errp)
1146 {
1147 	xfs_sharelist_t	*newp;
1148 	xfs_sharelist_t	*headp;
1149 	xfs_sharelist_t	*tailp;
1150 	FILE		*fp;
1151 
1152 	headp = NULL;
1153 	tailp = NULL;
1154 
1155 	if ((fp = fopen(SHARETAB, "r")) != NULL) {
1156 		struct share	*sharetab_entry;
1157 
1158 		while (getshare(fp, &sharetab_entry) > 0) {
1159 		    newp = alloc_sharelist();
1160 		    if (newp == NULL) {
1161 			goto err;
1162 		    }
1163 
1164 			/*
1165 			 * link into the list here so we don't leak
1166 			 * memory on a failure from strdup().
1167 			 */
1168 		    if (headp == NULL) {
1169 			headp = newp;
1170 			tailp = newp;
1171 		    } else {
1172 			tailp->next = newp;
1173 			tailp = newp;
1174 		    }
1175 
1176 		    newp->path = strdup(sharetab_entry->sh_path);
1177 		    if (newp->path == NULL)
1178 			goto err;
1179 		    newp->resource = strdup(sharetab_entry->sh_res);
1180 		    if (newp->resource == NULL)
1181 			goto err;
1182 		    newp->fstype = strdup(sharetab_entry->sh_fstype);
1183 		    if (newp->fstype == NULL)
1184 			goto err;
1185 		    newp->options = strdup(sharetab_entry->sh_opts);
1186 		    if (newp->options == NULL)
1187 			goto err;
1188 		    newp->description = strdup(sharetab_entry->sh_descr);
1189 		    if (newp->description == NULL)
1190 			goto err;
1191 		}
1192 		(void) fclose(fp);
1193 	} else {
1194 	    *errp = errno;
1195 	}
1196 
1197 	/*
1198 	 * Caller must free the mount list
1199 	 */
1200 	return (headp);
1201 err:
1202 	/*
1203 	 * Out of memory so cleanup and leave.
1204 	 */
1205 	dfs_free_list(headp);
1206 	(void) fclose(fp);
1207 	return (NULL);
1208 }
1209 
1210 /*
1211  * parse_sharetab(void)
1212  *
1213  * Read the /etc/dfs/sharetab file and see which entries don't exist
1214  * in the repository. These shares are marked transient.  We also need
1215  * to see if they are ZFS shares since ZFS bypasses the SMF
1216  * repository.
1217  */
1218 
1219 int
1220 parse_sharetab(void)
1221 {
1222 	xfs_sharelist_t *list, *tmplist;
1223 	int err = 0;
1224 	sa_share_t share;
1225 	sa_group_t group;
1226 	sa_group_t lgroup;
1227 	char *groupname;
1228 	int legacy = 0;
1229 
1230 	list = get_share_list(&err);
1231 	if (list == NULL)
1232 	    return (legacy);
1233 
1234 	lgroup = sa_get_group("default");
1235 
1236 	for (tmplist = list; tmplist != NULL; tmplist = tmplist->next) {
1237 	    group = NULL;
1238 	    share = sa_find_share(tmplist->path);
1239 	    if (share == NULL) {
1240 		/*
1241 		 * this share is transient so needs to be
1242 		 * added. Initially, this will be under
1243 		 * default(legacy) unless it is a ZFS
1244 		 * share. If zfs, we need a zfs group.
1245 		 */
1246 		if (tmplist->resource != NULL &&
1247 		    (groupname = strchr(tmplist->resource, '@')) != NULL) {
1248 		    /* there is a defined group */
1249 		    *groupname++ = '\0';
1250 		    group = sa_get_group(groupname);
1251 		    if (group != NULL) {
1252 			share = _sa_add_share(group, tmplist->path,
1253 						SA_SHARE_TRANSIENT, &err);
1254 		    } else {
1255 			/*
1256 			 * While this case shouldn't occur very often,
1257 			 * it does occur out of a "zfs set
1258 			 * sharenfs=off" when the dataset is also set
1259 			 * to canmount=off. A warning will then cause
1260 			 * the zfs command to abort. Since we add it
1261 			 * to the default list, everything works
1262 			 * properly anyway and the library doesn't
1263 			 * need to give a warning.
1264 			 */
1265 			share = _sa_add_share(lgroup, tmplist->path,
1266 						SA_SHARE_TRANSIENT, &err);
1267 		    }
1268 		} else {
1269 		    if (sa_zfs_is_shared(tmplist->path)) {
1270 			group = sa_get_group("zfs");
1271 			if (group == NULL) {
1272 			    group = sa_create_group("zfs", &err);
1273 			    if (group == NULL && err == SA_NO_PERMISSION) {
1274 				group = _sa_create_group("zfs");
1275 			    }
1276 			    if (group != NULL) {
1277 				(void) sa_create_optionset(group,
1278 							    tmplist->fstype);
1279 				(void) sa_set_group_attr(group, "zfs", "true");
1280 			    }
1281 			}
1282 			if (group != NULL) {
1283 			    share = _sa_add_share(group, tmplist->path,
1284 						    SA_SHARE_TRANSIENT, &err);
1285 			}
1286 		    } else {
1287 			share = _sa_add_share(lgroup, tmplist->path,
1288 						SA_SHARE_TRANSIENT, &err);
1289 		    }
1290 		}
1291 		if (share == NULL)
1292 		    (void) printf(gettext("Problem with transient: %s\n"),
1293 				    sa_errorstr(err));
1294 		if (share != NULL)
1295 		    set_node_attr(share, "shared", "true");
1296 
1297 		if (err == SA_OK) {
1298 		    if (tmplist->options != NULL &&
1299 			strlen(tmplist->options) > 0) {
1300 			(void) sa_parse_legacy_options(share,
1301 							tmplist->options,
1302 							tmplist->fstype);
1303 		    }
1304 		    if (tmplist->resource != NULL &&
1305 			strcmp(tmplist->resource, "-") != 0)
1306 			set_node_attr(share, "resource", tmplist->resource);
1307 		    if (tmplist->description != NULL) {
1308 			xmlNodePtr node;
1309 			node = xmlNewChild((xmlNodePtr)share, NULL,
1310 						(xmlChar *)"description", NULL);
1311 			xmlNodeSetContent(node,
1312 					    (xmlChar *)tmplist->description);
1313 		    }
1314 		    legacy = 1;
1315 		}
1316 	    } else {
1317 		/*
1318 		 * if this is a legacy share, mark as shared so we
1319 		 * only update sharetab appropriately.
1320 		 */
1321 		set_node_attr(share, "shared", "true");
1322 	    }
1323 	}
1324 	dfs_free_list(list);
1325 	return (legacy);
1326 }
1327 
1328 /*
1329  * get the transient shares from the sharetab (or other) file.  since
1330  * these are transient, they only appear in the working file and not
1331  * in a repository.
1332  */
1333 int
1334 gettransients(xmlNodePtr *root)
1335 {
1336 	int legacy = 0;
1337 
1338 	if (root != NULL) {
1339 	    if (*root == NULL)
1340 		*root = xmlNewNode(NULL, (xmlChar *)"sharecfg");
1341 	    if (*root != NULL) {
1342 		legacy = parse_sharetab();
1343 	    }
1344 	}
1345 	return (legacy);
1346 }
1347 
1348 /*
1349  * sa_has_prop(optionset, prop)
1350  *
1351  * Is the specified property a member of the optionset?
1352  */
1353 
1354 int
1355 sa_has_prop(sa_optionset_t optionset, sa_property_t prop)
1356 {
1357 	char *name;
1358 	sa_property_t otherprop;
1359 	int result = 0;
1360 
1361 	if (optionset != NULL) {
1362 	    name = sa_get_property_attr(prop, "type");
1363 	    if (name != NULL) {
1364 		otherprop = sa_get_property(optionset, name);
1365 		if (otherprop != NULL)
1366 		    result = 1;
1367 		sa_free_attr_string(name);
1368 	    }
1369 	}
1370 	return (result);
1371 }
1372 
1373 /*
1374  * Update legacy files
1375  *
1376  * Provides functions to add/remove/modify individual entries
1377  * in dfstab and sharetab
1378  */
1379 
1380 void
1381 update_legacy_config(void)
1382 {
1383 	/*
1384 	 * no longer used -- this is a placeholder in case we need to
1385 	 * add it back later.
1386 	 */
1387 }
1388 
1389 /*
1390  * sa_valid_property(object, proto, property)
1391  *
1392  * check to see if the specified property is valid relative to the
1393  * specified protocol. The protocol plugin is called to do the work.
1394  */
1395 
1396 int
1397 sa_valid_property(void *object, char *proto, sa_property_t property)
1398 {
1399 	int ret = SA_OK;
1400 
1401 	if (proto != NULL && property != NULL) {
1402 	    ret = sa_proto_valid_prop(proto, property, object);
1403 	}
1404 
1405 	return (ret);
1406 }
1407 
1408 /*
1409  * sa_fstype(path)
1410  *
1411  * Given path, return the string representing the path's file system
1412  * type. This is used to discover ZFS shares.
1413  */
1414 
1415 char *
1416 sa_fstype(char *path)
1417 {
1418 	int err;
1419 	struct stat st;
1420 
1421 	err = stat(path, &st);
1422 	if (err < 0) {
1423 	    err = SA_NO_SUCH_PATH;
1424 	} else {
1425 	    err = SA_OK;
1426 	}
1427 	if (err == SA_OK) {
1428 		/* have a valid path at this point */
1429 	    return (strdup(st.st_fstype));
1430 	}
1431 	return (NULL);
1432 }
1433 
1434 void
1435 sa_free_fstype(char *type)
1436 {
1437 	free(type);
1438 }
1439 
1440 /*
1441  * sa_get_derived_optionset(object, proto, hier)
1442  *
1443  *	Work backward to the top of the share object tree and start
1444  *	copying protocol specific optionsets into a newly created
1445  *	optionset that doesn't have a parent (it will be freed
1446  *	later). This provides for the property inheritence model. That
1447  *	is, properties closer to the share take precedence over group
1448  *	level. This also provides for groups of groups in the future.
1449  */
1450 
1451 sa_optionset_t
1452 sa_get_derived_optionset(void *object, char *proto, int hier)
1453 {
1454 	sa_optionset_t newoptionset;
1455 	sa_optionset_t optionset;
1456 	sa_group_t group;
1457 
1458 	if (hier &&
1459 	    (group = sa_get_parent_group((sa_share_t)object)) != NULL) {
1460 	    newoptionset = sa_get_derived_optionset((void *)group, proto, hier);
1461 	} else {
1462 	    newoptionset = (sa_optionset_t)xmlNewNode(NULL,
1463 						    (xmlChar *)"optionset");
1464 	    if (newoptionset != NULL) {
1465 		sa_set_optionset_attr(newoptionset, "type", proto);
1466 	    }
1467 	}
1468 	/* dont' do anything if memory wasn't allocated */
1469 	if (newoptionset == NULL)
1470 	    return (NULL);
1471 
1472 	/* found the top so working back down the stack */
1473 	optionset = sa_get_optionset((sa_optionset_t)object, proto);
1474 	if (optionset != NULL) {
1475 	    sa_property_t prop;
1476 	    /* add optionset to the newoptionset */
1477 	    for (prop = sa_get_property(optionset, NULL);
1478 		prop != NULL; prop = sa_get_next_property(prop)) {
1479 		sa_property_t newprop;
1480 		char *name;
1481 		char *value;
1482 		name = sa_get_property_attr(prop, "type");
1483 		value = sa_get_property_attr(prop, "value");
1484 		if (name != NULL) {
1485 		    newprop = sa_get_property(newoptionset, name);
1486 		    /* replace the value with the new value */
1487 		    if (newprop != NULL) {
1488 			/*
1489 			 * only set if value is non NULL, old value ok
1490 			 * if it is NULL.
1491 			 */
1492 			if (value != NULL)
1493 			    set_node_attr(newprop, "value", value);
1494 		    } else {
1495 			/* an entirely new property */
1496 			if (value != NULL) {
1497 			    newprop = sa_create_property(name, value);
1498 			    if (newprop != NULL) {
1499 				newprop = (sa_property_t)
1500 				    xmlAddChild((xmlNodePtr)newoptionset,
1501 						(xmlNodePtr)newprop);
1502 			    }
1503 			}
1504 		    }
1505 		    sa_free_attr_string(name);
1506 		}
1507 		if (value != NULL)
1508 		    sa_free_attr_string(value);
1509 	    }
1510 	}
1511 	return (newoptionset);
1512 }
1513 
1514 void
1515 sa_free_derived_optionset(sa_optionset_t optionset)
1516 {
1517 	/* while it shouldn't be linked, it doesn't hurt */
1518 	if (optionset != NULL) {
1519 	    xmlUnlinkNode((xmlNodePtr) optionset);
1520 	    xmlFreeNode((xmlNodePtr) optionset);
1521 	}
1522 }
1523 
1524 /*
1525  *  sa_get_all_security_types(object, proto, hier)
1526  *
1527  *	find all the security types set for this object.  This is
1528  *	preliminary to getting a derived security set. The return value is an
1529  *	optionset containg properties which are the sectype values found by
1530  *	walking up the XML document struture. The returned optionset
1531  *	is a derived optionset.
1532  *
1533  *	If hier is 0, only look at object. If non-zero, walk up the tree.
1534  */
1535 sa_optionset_t
1536 sa_get_all_security_types(void *object, char *proto, int hier)
1537 {
1538 	sa_optionset_t options;
1539 	sa_security_t security;
1540 	sa_group_t group;
1541 	sa_property_t prop;
1542 
1543 	options = NULL;
1544 
1545 	if (hier &&
1546 	    (group = sa_get_parent_group((sa_share_t)object)) != NULL) {
1547 	    options = sa_get_all_security_types((void *)group, proto, hier);
1548 	} else {
1549 	    options = (sa_optionset_t)xmlNewNode(NULL,
1550 						    (xmlChar *)"optionset");
1551 	}
1552 	/* hit the top so collect the security types working back */
1553 	if (options != NULL) {
1554 	    for (security = sa_get_security((sa_group_t)object, NULL, NULL);
1555 		security != NULL; security = sa_get_next_security(security)) {
1556 		char *type;
1557 		char *sectype;
1558 
1559 		type = sa_get_security_attr(security, "type");
1560 		if (type != NULL) {
1561 		    if (strcmp(type, proto) != 0) {
1562 			sa_free_attr_string(type);
1563 			continue;
1564 		    }
1565 		    sectype = sa_get_security_attr(security, "sectype");
1566 		    if (sectype != NULL) {
1567 			/*
1568 			 * have a security type, check to see if
1569 			 * already present in optionset and add if it
1570 			 * isn't.
1571 			 */
1572 			if (sa_get_property(options, sectype) == NULL) {
1573 			    prop = sa_create_property(sectype, "true");
1574 			    if (prop != NULL)
1575 				prop = (sa_property_t)
1576 				    xmlAddChild((xmlNodePtr)options,
1577 						(xmlNodePtr)prop);
1578 			}
1579 			sa_free_attr_string(sectype);
1580 		    }
1581 		    sa_free_attr_string(type);
1582 		}
1583 	    }
1584 	}
1585 	return (options);
1586 }
1587 
1588 /*
1589  * sa_get_derived_security(object, sectype, proto, hier)
1590  *
1591  * Get the derived security(named optionset) for the object given the
1592  * sectype and proto. If hier is non-zero, walk up the tree to get all
1593  * properties defined for this object, otherwise just those on the
1594  * object.
1595  */
1596 
1597 sa_security_t
1598 sa_get_derived_security(void *object, char *sectype, char *proto, int hier)
1599 {
1600 	sa_security_t newsecurity;
1601 	sa_security_t security;
1602 	sa_group_t group;
1603 
1604 	if (hier &&
1605 	    (group = sa_get_parent_group((sa_share_t)object)) != NULL) {
1606 	    newsecurity = sa_get_derived_security((void *)group,
1607 						    sectype, proto, hier);
1608 	} else {
1609 	    newsecurity = (sa_security_t)xmlNewNode(NULL,
1610 						    (xmlChar *)"security");
1611 	    if (newsecurity != NULL) {
1612 		sa_set_security_attr(newsecurity, "type", proto);
1613 		sa_set_security_attr(newsecurity, "sectype", sectype);
1614 	    }
1615 	}
1616 	/* dont' do anything if memory wasn't allocated */
1617 	if (newsecurity == NULL)
1618 	    return (NULL);
1619 
1620 	/* found the top so working back down the stack */
1621 	security = sa_get_security((sa_security_t)object, sectype, proto);
1622 	if (security != NULL) {
1623 	    sa_property_t prop;
1624 	    /* add security to the newsecurity */
1625 	    for (prop = sa_get_property(security, NULL);
1626 		prop != NULL; prop = sa_get_next_property(prop)) {
1627 		sa_property_t newprop;
1628 		char *name;
1629 		char *value;
1630 		name = sa_get_property_attr(prop, "type");
1631 		value = sa_get_property_attr(prop, "value");
1632 		if (name != NULL) {
1633 		    newprop = sa_get_property(newsecurity, name);
1634 		    /* replace the value with the new value */
1635 		    if (newprop != NULL) {
1636 			/*
1637 			 * only set if value is non NULL, old value ok
1638 			 * if it is NULL.
1639 			 */
1640 			if (value != NULL)
1641 			    set_node_attr(newprop, name, value);
1642 		    } else {
1643 			/* an entirely new property */
1644 			if (value != NULL) {
1645 			    newprop = sa_create_property(name, value);
1646 			    newprop = (sa_property_t)
1647 				xmlAddChild((xmlNodePtr)newsecurity,
1648 					    (xmlNodePtr)newprop);
1649 			}
1650 		    }
1651 		    sa_free_attr_string(name);
1652 		}
1653 		if (value != NULL)
1654 		    sa_free_attr_string(value);
1655 	    }
1656 	}
1657 	return (newsecurity);
1658 }
1659 
1660 void
1661 sa_free_derived_security(sa_security_t security)
1662 {
1663 	/* while it shouldn't be linked, it doesn't hurt */
1664 	if (security != NULL) {
1665 	    xmlUnlinkNode((xmlNodePtr)security);
1666 	    xmlFreeNode((xmlNodePtr)security);
1667 	}
1668 }
1669 
1670 /*
1671  * sharetab utility functions
1672  *
1673  * makes use of the original sharetab.c from fs.d/nfs/lib
1674  */
1675 
1676 /*
1677  * fillshare(share, proto, sh)
1678  *
1679  * Fill the struct share with values obtained from the share object.
1680  */
1681 static void
1682 fillshare(sa_share_t share, char *proto, struct share *sh)
1683 {
1684 	char *groupname = NULL;
1685 	char *value;
1686 	sa_group_t group;
1687 	char *buff;
1688 	char *zfs;
1689 
1690 	group = sa_get_parent_group(share);
1691 	if (group != NULL) {
1692 	    zfs = sa_get_group_attr(group, "zfs");
1693 	    groupname = sa_get_group_attr(group, "name");
1694 
1695 	    if (groupname != NULL &&
1696 		(strcmp(groupname, "default") == 0 || zfs != NULL)) {
1697 		/*
1698 		 * since the groupname is either "default" or the
1699 		 * group is a ZFS group, we don't want to keep
1700 		 * groupname. We do want it if it is any other type of
1701 		 * group.
1702 		 */
1703 		sa_free_attr_string(groupname);
1704 		groupname = NULL;
1705 	    }
1706 	    if (zfs != NULL)
1707 		sa_free_attr_string(zfs);
1708 	}
1709 
1710 	value = sa_get_share_attr(share, "path");
1711 	if (value != NULL) {
1712 	    sh->sh_path = strdup(value);
1713 	    sa_free_attr_string(value);
1714 	}
1715 
1716 	value = sa_get_share_attr(share, "resource");
1717 	if (value != NULL || groupname != NULL) {
1718 	    int len = 0;
1719 
1720 	    if (value != NULL)
1721 		len += strlen(value);
1722 	    if (groupname != NULL)
1723 		len += strlen(groupname);
1724 	    len += 3; /* worst case */
1725 	    buff = malloc(len);
1726 	    (void) snprintf(buff, len, "%s%s%s",
1727 		    (value != NULL && strlen(value) > 0) ? value : "-",
1728 		    groupname != NULL ? "@" : "",
1729 		    groupname != NULL ? groupname : "");
1730 	    sh->sh_res = buff;
1731 	    if (value != NULL)
1732 		sa_free_attr_string(value);
1733 	    if (groupname != NULL) {
1734 		sa_free_attr_string(groupname);
1735 		groupname = NULL;
1736 	    }
1737 	} else {
1738 	    sh->sh_res = strdup("-");
1739 	}
1740 
1741 	sh->sh_fstype = strdup(proto);
1742 	value = sa_proto_legacy_format(proto, share, 1);
1743 	if (value != NULL) {
1744 	    if (strlen(value) > 0)
1745 		sh->sh_opts = strdup(value);
1746 	    else
1747 		sh->sh_opts = strdup("rw");
1748 	    free(value);
1749 	} else
1750 	    sh->sh_opts = strdup("rw");
1751 
1752 	value = sa_get_share_description(share);
1753 	if (value != NULL) {
1754 	    sh->sh_descr = strdup(value);
1755 	    sa_free_share_description(value);
1756 	} else
1757 	    sh->sh_descr = strdup("");
1758 }
1759 
1760 /*
1761  * emptyshare(sh)
1762  *
1763  * Free the strings in the non-NULL members of sh.
1764  */
1765 
1766 static void
1767 emptyshare(struct share *sh)
1768 {
1769 	if (sh->sh_path != NULL)
1770 	    free(sh->sh_path);
1771 	sh->sh_path = NULL;
1772 	if (sh->sh_res != NULL)
1773 	    free(sh->sh_res);
1774 	sh->sh_res = NULL;
1775 	if (sh->sh_fstype != NULL)
1776 	    free(sh->sh_fstype);
1777 	sh->sh_fstype = NULL;
1778 	if (sh->sh_opts != NULL)
1779 	    free(sh->sh_opts);
1780 	sh->sh_opts = NULL;
1781 	if (sh->sh_descr != NULL)
1782 	    free(sh->sh_descr);
1783 	sh->sh_descr = NULL;
1784 }
1785 
1786 /*
1787  * sa_update_sharetab(share, proto)
1788  *
1789  * Update the sharetab file with info from the specified share.
1790  * This could be an update or add.
1791  */
1792 
1793 int
1794 sa_update_sharetab(sa_share_t share, char *proto)
1795 {
1796 	int ret = SA_OK;
1797 	struct share shtab;
1798 	char *path;
1799 	int logging = 0;
1800 	FILE *sharetab;
1801 	sigset_t old, new;
1802 
1803 	path = sa_get_share_attr(share, "path");
1804 	if (path != NULL) {
1805 	    (void) memset(&shtab, '\0', sizeof (shtab));
1806 	    sharetab = fopen(SA_LEGACY_SHARETAB, "r+");
1807 	    if (sharetab == NULL) {
1808 		sharetab = fopen(SA_LEGACY_SHARETAB, "w+");
1809 	    }
1810 	    if (sharetab != NULL) {
1811 		(void) setvbuf(sharetab, NULL, _IOLBF, BUFSIZ * 8);
1812 		(void) sigprocmask(SIG_BLOCK, NULL, &new);
1813 		(void) sigaddset(&new, SIGHUP);
1814 		(void) sigaddset(&new, SIGINT);
1815 		(void) sigaddset(&new, SIGQUIT);
1816 		(void) sigaddset(&new, SIGTSTP);
1817 		(void) sigprocmask(SIG_SETMASK, &new, &old);
1818 		(void) lockf(fileno(sharetab), F_LOCK, 0);
1819 		(void) remshare(sharetab, path, &logging);
1820 		/* fill in share structure and write it out */
1821 		(void) fillshare(share, proto, &shtab);
1822 		(void) putshare(sharetab, &shtab);
1823 		emptyshare(&shtab);
1824 		(void) fflush(sharetab);
1825 		(void) lockf(fileno(sharetab), F_ULOCK, 0);
1826 		(void) fsync(fileno(sharetab));
1827 		(void) sigprocmask(SIG_SETMASK, &old, NULL);
1828 		(void) fclose(sharetab);
1829 	    } else {
1830 		if (errno == EACCES || errno == EPERM) {
1831 		    ret = SA_NO_PERMISSION;
1832 		} else {
1833 		    ret = SA_CONFIG_ERR;
1834 		}
1835 	    }
1836 	    sa_free_attr_string(path);
1837 	}
1838 	return (ret);
1839 }
1840 
1841 /*
1842  * sa_delete_sharetab(path, proto)
1843  *
1844  * remove the specified share from sharetab.
1845  */
1846 
1847 int
1848 sa_delete_sharetab(char *path, char *proto)
1849 {
1850 	int ret = SA_OK;
1851 	int logging = 0;
1852 	FILE *sharetab;
1853 	sigset_t old, new;
1854 #ifdef lint
1855 	proto = proto;
1856 #endif
1857 
1858 	if (path != NULL) {
1859 	    sharetab = fopen(SA_LEGACY_SHARETAB, "r+");
1860 	    if (sharetab == NULL) {
1861 		sharetab = fopen(SA_LEGACY_SHARETAB, "w+");
1862 	    }
1863 	    if (sharetab != NULL) {
1864 		/* should block keyboard level signals around the lock */
1865 		(void) sigprocmask(SIG_BLOCK, NULL, &new);
1866 		(void) sigaddset(&new, SIGHUP);
1867 		(void) sigaddset(&new, SIGINT);
1868 		(void) sigaddset(&new, SIGQUIT);
1869 		(void) sigaddset(&new, SIGTSTP);
1870 		(void) sigprocmask(SIG_SETMASK, &new, &old);
1871 		(void) lockf(fileno(sharetab), F_LOCK, 0);
1872 		ret = remshare(sharetab, path, &logging);
1873 		(void) fflush(sharetab);
1874 		(void) lockf(fileno(sharetab), F_ULOCK, 0);
1875 		(void) fsync(fileno(sharetab));
1876 		(void) sigprocmask(SIG_SETMASK, &old, NULL);
1877 		(void) fclose(sharetab);
1878 	    } else {
1879 		if (errno == EACCES || errno == EPERM) {
1880 		    ret = SA_NO_PERMISSION;
1881 		} else {
1882 		    ret = SA_CONFIG_ERR;
1883 		}
1884 	    }
1885 	}
1886 	return (ret);
1887 }
1888